From 2c718184096017ea106e1b7a6402e96013d327fb Mon Sep 17 00:00:00 2001 From: Peter Klimai Date: Tue, 5 May 2020 22:44:44 +0300 Subject: [PATCH 001/156] Fix matrix shape in RealMatrixTest --- .../kotlin/scientific.kmath.real/RealMatrixTest.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/kmath-for-real/src/commonTest/kotlin/scientific.kmath.real/RealMatrixTest.kt b/kmath-for-real/src/commonTest/kotlin/scientific.kmath.real/RealMatrixTest.kt index 31b8b5252..e72711490 100644 --- a/kmath-for-real/src/commonTest/kotlin/scientific.kmath.real/RealMatrixTest.kt +++ b/kmath-for-real/src/commonTest/kotlin/scientific.kmath.real/RealMatrixTest.kt @@ -126,10 +126,10 @@ class RealMatrixTest { assertEquals(matrix1.appendColumn { it[0] }, matrix2) assertEquals(matrix1.extractColumn(1), col1) assertEquals(matrix1.extractColumns(1..2), cols1to2) - assertEquals(matrix1.sumByColumn(), Matrix.build(4, 1)(3.0, -6.0, 10.0, 4.0)) - assertEquals(matrix1.minByColumn(), Matrix.build(4, 1)(-1.0, -6.0, 3.0, -11.0)) - assertEquals(matrix1.maxByColumn(), Matrix.build(4, 1)(4.0, 0.0, 7.0, 15.0)) - assertEquals(matrix1.averageByColumn(), Matrix.build(4, 1)(1.5, -3.0, 5.0, 2.0)) + assertEquals(matrix1.sumByColumn(), Matrix.build(1, 4)(3.0, -6.0, 10.0, 4.0)) + assertEquals(matrix1.minByColumn(), Matrix.build(1, 4)(-1.0, -6.0, 3.0, -11.0)) + assertEquals(matrix1.maxByColumn(), Matrix.build(1, 4)(4.0, 0.0, 7.0, 15.0)) + assertEquals(matrix1.averageByColumn(), Matrix.build(1, 4)(1.5, -3.0, 5.0, 2.0)) } @Test -- 2.34.1 From 646207e14031927dba9be5cd874d8ba67cc2de37 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Wed, 6 May 2020 10:50:08 +0300 Subject: [PATCH 002/156] Equlity half-fix for NDStructure --- build.gradle.kts | 4 +- examples/build.gradle.kts | 2 +- .../kmath/commons/linear/CMMatrix.kt | 11 ++++ .../scientifik/kmath/operations/BigInt.kt | 16 +++++ .../kmath/structures/BufferedNDElement.kt | 12 ++-- .../scientifik/kmath/structures/Buffers.kt | 2 +- .../kmath/structures/NDStructure.kt | 62 +++++++++---------- .../kmath/structures/Structure2D.kt | 19 ------ .../{BigNumbers.kt => bigNumbers.kt} | 23 ++----- .../kotlin/scientifik/kmath/chains/Chain.kt | 4 ++ .../kmath/structures/LazyNDStructure.kt | 14 +++++ .../scientifik/kmath/linear/RealVector.kt | 48 -------------- .../scientifik/kmath/real/RealVector.kt | 59 ++++++++++++++++++ .../scientifik/kmath/linear/VectorTest.kt | 1 + .../kmath/histogram/RealHistogram.kt | 2 +- .../histogram/MultivariateHistogramTest.kt | 2 +- .../kmath/histogram/UnivariateHistogram.kt | 4 +- .../scientifik.kmath.linear/KomaMatrix.kt | 13 ++++ settings.gradle.kts | 12 ++-- 19 files changed, 172 insertions(+), 138 deletions(-) rename kmath-core/src/jvmMain/kotlin/scientifik/kmath/operations/{BigNumbers.kt => bigNumbers.kt} (56%) delete mode 100644 kmath-for-real/src/commonMain/kotlin/scientifik/kmath/linear/RealVector.kt create mode 100644 kmath-for-real/src/commonMain/kotlin/scientifik/kmath/real/RealVector.kt diff --git a/build.gradle.kts b/build.gradle.kts index 6ab33d31c..34e87fa2a 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,8 +1,8 @@ plugins { - id("scientifik.publish") version "0.4.2" apply false + id("scientifik.publish") apply false } -val kmathVersion by extra("0.1.4-dev-4") +val kmathVersion by extra("0.1.4-dev-5") val bintrayRepo by extra("scientifik") val githubProject by extra("kmath") diff --git a/examples/build.gradle.kts b/examples/build.gradle.kts index 8853b78a5..8ab0dd1eb 100644 --- a/examples/build.gradle.kts +++ b/examples/build.gradle.kts @@ -57,6 +57,6 @@ benchmark { tasks.withType { kotlinOptions { - jvmTarget = Scientifik.JVM_VERSION + jvmTarget = Scientifik.JVM_TARGET.toString() } } \ No newline at end of file diff --git a/kmath-commons/src/main/kotlin/scientifik/kmath/commons/linear/CMMatrix.kt b/kmath-commons/src/main/kotlin/scientifik/kmath/commons/linear/CMMatrix.kt index 72e5fb95a..a17effccc 100644 --- a/kmath-commons/src/main/kotlin/scientifik/kmath/commons/linear/CMMatrix.kt +++ b/kmath-commons/src/main/kotlin/scientifik/kmath/commons/linear/CMMatrix.kt @@ -5,6 +5,7 @@ import org.apache.commons.math3.linear.RealMatrix import org.apache.commons.math3.linear.RealVector import scientifik.kmath.linear.* import scientifik.kmath.structures.Matrix +import scientifik.kmath.structures.NDStructure class CMMatrix(val origin: RealMatrix, features: Set? = null) : FeaturedMatrix { @@ -19,6 +20,16 @@ class CMMatrix(val origin: RealMatrix, features: Set? = null) : CMMatrix(origin, this.features + features) override fun get(i: Int, j: Int): Double = origin.getEntry(i, j) + + override fun equals(other: Any?): Boolean { + return NDStructure.equals(this, other as? NDStructure<*> ?: return false) + } + + override fun hashCode(): Int { + var result = origin.hashCode() + result = 31 * result + features.hashCode() + return result + } } fun Matrix.toCM(): CMMatrix = if (this is CMMatrix) { diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/BigInt.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/BigInt.kt index 1661170d3..5e7d49b1c 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/BigInt.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/BigInt.kt @@ -2,6 +2,7 @@ package scientifik.kmath.operations import scientifik.kmath.operations.BigInt.Companion.BASE import scientifik.kmath.operations.BigInt.Companion.BASE_SIZE +import scientifik.kmath.structures.* import kotlin.math.log2 import kotlin.math.max import kotlin.math.min @@ -482,3 +483,18 @@ fun String.parseBigInteger(): BigInt? { } return res * sign } + +inline fun Buffer.Companion.bigInt(size: Int, initializer: (Int) -> BigInt): Buffer = + boxing(size, initializer) + +inline fun MutableBuffer.Companion.bigInt(size: Int, initializer: (Int) -> BigInt): MutableBuffer = + boxing(size, initializer) + +fun NDAlgebra.Companion.bigInt(vararg shape: Int): BoxingNDRing = + BoxingNDRing(shape, BigIntField, Buffer.Companion::bigInt) + +fun NDElement.Companion.bigInt( + vararg shape: Int, + initializer: BigIntField.(IntArray) -> BigInt +): BufferedNDRingElement = + NDAlgebra.bigInt(*shape).produce(initializer) \ No newline at end of file diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BufferedNDElement.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BufferedNDElement.kt index 04049368a..be80f66bf 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BufferedNDElement.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BufferedNDElement.kt @@ -3,10 +3,10 @@ package scientifik.kmath.structures import scientifik.kmath.operations.* /** - * Base interface for an element with context, containing strides + * Base class for an element with context, containing strides */ -interface BufferedNDElement : NDBuffer, NDElement> { - override val context: BufferedNDAlgebra +abstract class BufferedNDElement : NDBuffer(), NDElement> { + abstract override val context: BufferedNDAlgebra override val strides get() = context.strides @@ -16,7 +16,7 @@ interface BufferedNDElement : NDBuffer, NDElement> { class BufferedNDSpaceElement>( override val context: BufferedNDSpace, override val buffer: Buffer -) : BufferedNDElement, SpaceElement, BufferedNDSpaceElement, BufferedNDSpace> { +) : BufferedNDElement(), SpaceElement, BufferedNDSpaceElement, BufferedNDSpace> { override fun unwrap(): NDBuffer = this @@ -29,7 +29,7 @@ class BufferedNDSpaceElement>( class BufferedNDRingElement>( override val context: BufferedNDRing, override val buffer: Buffer -) : BufferedNDElement, RingElement, BufferedNDRingElement, BufferedNDRing> { +) : BufferedNDElement(), RingElement, BufferedNDRingElement, BufferedNDRing> { override fun unwrap(): NDBuffer = this @@ -42,7 +42,7 @@ class BufferedNDRingElement>( class BufferedNDFieldElement>( override val context: BufferedNDField, override val buffer: Buffer -) : BufferedNDElement, FieldElement, BufferedNDFieldElement, BufferedNDField> { +) : BufferedNDElement(), FieldElement, BufferedNDFieldElement, BufferedNDField> { override fun unwrap(): NDBuffer = this 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 f02fd8dd0..ab8891ab9 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/Buffers.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/Buffers.kt @@ -71,7 +71,7 @@ interface Buffer { fun Buffer.asSequence(): Sequence = Sequence(::iterator) -fun Buffer.asIterable(): Iterable = asSequence().asIterable() +fun Buffer.asIterable(): Iterable = Iterable(::iterator) val Buffer<*>.indices: IntRange get() = IntRange(0, size - 1) 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 808f970c5..25d736b92 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/NDStructure.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/NDStructure.kt @@ -14,15 +14,21 @@ interface NDStructure { fun elements(): Sequence> + override fun equals(other: Any?): Boolean + + override fun hashCode(): Int + companion object { fun equals(st1: NDStructure<*>, st2: NDStructure<*>): Boolean { - return when { - st1 === st2 -> true - st1 is BufferNDStructure<*> && st2 is BufferNDStructure<*> && st1.strides == st2.strides -> st1.buffer.contentEquals( - st2.buffer - ) - else -> st1.elements().all { (index, value) -> value == st2[index] } + if(st1===st2) return true + + // fast comparison of buffers if possible + if(st1 is NDBuffer && st2 is NDBuffer && st1.strides == st2.strides){ + return st1.buffer.contentEquals(st2.buffer) } + + //element by element comparison if it could not be avoided + return st1.elements().all { (index, value) -> value == st2[index] } } /** @@ -177,15 +183,25 @@ class DefaultStrides private constructor(override val shape: IntArray) : Strides } } -interface NDBuffer : NDStructure { - val buffer: Buffer - val strides: Strides +abstract class NDBuffer : NDStructure { + abstract val buffer: Buffer + abstract val strides: Strides 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 elements(): Sequence> = strides.indices().map { it to this[it] } + + override fun equals(other: Any?): Boolean { + return NDStructure.equals(this, other as? NDStructure<*> ?: return false) + } + + override fun hashCode(): Int { + var result = strides.hashCode() + result = 31 * result + buffer.hashCode() + return result + } } /** @@ -194,34 +210,12 @@ interface NDBuffer : NDStructure { class BufferNDStructure( override val strides: Strides, override val buffer: Buffer -) : NDBuffer { - +) : NDBuffer(){ init { if (strides.linearSize != buffer.size) { error("Expected buffer side of ${strides.linearSize}, but found ${buffer.size}") } } - - 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 - other is BufferNDStructure<*> && this.strides == other.strides -> this.buffer.contentEquals(other.buffer) - other is NDStructure<*> -> elements().all { (index, value) -> value == other[index] } - else -> false - } - } - - override fun hashCode(): Int { - var result = strides.hashCode() - result = 31 * result + buffer.hashCode() - return result - } } /** @@ -245,7 +239,7 @@ inline fun NDStructure.mapToBuffer( class MutableBufferNDStructure( override val strides: Strides, override val buffer: MutableBuffer -) : NDBuffer, MutableNDStructure { +) : NDBuffer(), MutableNDStructure { init { if (strides.linearSize != buffer.size) { diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/Structure2D.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/Structure2D.kt index e736f84a0..bfaf8338d 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/Structure2D.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/Structure2D.kt @@ -14,7 +14,6 @@ interface Structure2D : NDStructure { return get(index[0], index[1]) } - val rows: Buffer> get() = VirtualBuffer(rowNum) { i -> VirtualBuffer(colNum) { j -> get(i, j) } @@ -58,22 +57,4 @@ fun NDStructure.as2D(): Structure2D = if (shape.size == 2) { error("Can't create 2d-structure from ${shape.size}d-structure") } -/** - * Represent this 2D structure as 1D if it has exactly one column. Throw error otherwise. - */ -fun Structure2D.as1D() = if (colNum == 1) { - object : Structure1D { - override fun get(index: Int): T = get(index, 0) - - override val shape: IntArray get() = intArrayOf(rowNum) - - override fun elements(): Sequence> = elements() - - override val size: Int get() = rowNum - } -} else { - error("Can't convert matrix with more than one column to vector") -} - - typealias Matrix = Structure2D \ No newline at end of file diff --git a/kmath-core/src/jvmMain/kotlin/scientifik/kmath/operations/BigNumbers.kt b/kmath-core/src/jvmMain/kotlin/scientifik/kmath/operations/bigNumbers.kt similarity index 56% rename from kmath-core/src/jvmMain/kotlin/scientifik/kmath/operations/BigNumbers.kt rename to kmath-core/src/jvmMain/kotlin/scientifik/kmath/operations/bigNumbers.kt index f44d15042..76ca199c5 100644 --- a/kmath-core/src/jvmMain/kotlin/scientifik/kmath/operations/BigNumbers.kt +++ b/kmath-core/src/jvmMain/kotlin/scientifik/kmath/operations/bigNumbers.kt @@ -5,7 +5,7 @@ import java.math.BigDecimal import java.math.BigInteger import java.math.MathContext -object BigIntegerRing : Ring { +object JBigIntegerField : Field { override val zero: BigInteger = BigInteger.ZERO override val one: BigInteger = BigInteger.ONE @@ -14,9 +14,11 @@ object BigIntegerRing : Ring { override fun multiply(a: BigInteger, k: Number): BigInteger = a.multiply(k.toInt().toBigInteger()) override fun multiply(a: BigInteger, b: BigInteger): BigInteger = a.multiply(b) + + override fun divide(a: BigInteger, b: BigInteger): BigInteger = a.div(b) } -class BigDecimalField(val mathContext: MathContext = MathContext.DECIMAL64) : Field { +class JBigDecimalField(val mathContext: MathContext = MathContext.DECIMAL64) : Field { override val zero: BigDecimal = BigDecimal.ZERO override val one: BigDecimal = BigDecimal.ONE @@ -27,19 +29,4 @@ class BigDecimalField(val mathContext: MathContext = MathContext.DECIMAL64) : Fi override fun multiply(a: BigDecimal, b: BigDecimal): BigDecimal = a.multiply(b, mathContext) override fun divide(a: BigDecimal, b: BigDecimal): BigDecimal = a.divide(b, mathContext) -} - -inline fun Buffer.Companion.bigInt(size: Int, initializer: (Int) -> BigInteger): Buffer = - boxing(size, initializer) - -inline fun MutableBuffer.Companion.bigInt(size: Int, initializer: (Int) -> BigInteger): MutableBuffer = - boxing(size, initializer) - -fun NDAlgebra.Companion.bigInt(vararg shape: Int): BoxingNDRing = - BoxingNDRing(shape, BigIntegerRing, Buffer.Companion::bigInt) - -fun NDElement.Companion.bigInt( - vararg shape: Int, - initializer: BigIntegerRing.(IntArray) -> BigInteger -): BufferedNDRingElement = - NDAlgebra.bigInt(*shape).produce(initializer) \ No newline at end of file +} \ No newline at end of file diff --git a/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/Chain.kt b/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/Chain.kt index 1c2872d17..fd68636f9 100644 --- a/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/Chain.kt +++ b/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/Chain.kt @@ -68,6 +68,8 @@ class MarkovChain(private val seed: suspend () -> R, private val ge private var value: R? = null + fun value() = value + override suspend fun next(): R { mutex.withLock { val newValue = gen(value ?: seed()) @@ -97,6 +99,8 @@ class StatefulChain( private var value: R? = null + fun value() = value + override suspend fun next(): R { mutex.withLock { val newValue = state.gen(value ?: state.seed()) diff --git a/kmath-coroutines/src/jvmMain/kotlin/scientifik/kmath/structures/LazyNDStructure.kt b/kmath-coroutines/src/jvmMain/kotlin/scientifik/kmath/structures/LazyNDStructure.kt index 784b7cd10..e65940739 100644 --- a/kmath-coroutines/src/jvmMain/kotlin/scientifik/kmath/structures/LazyNDStructure.kt +++ b/kmath-coroutines/src/jvmMain/kotlin/scientifik/kmath/structures/LazyNDStructure.kt @@ -30,6 +30,20 @@ class LazyNDStructure( } return res.asSequence() } + + override fun equals(other: Any?): Boolean { + return NDStructure.equals(this, other as? NDStructure<*> ?: return false) + } + + override fun hashCode(): Int { + var result = scope.hashCode() + result = 31 * result + shape.contentHashCode() + result = 31 * result + function.hashCode() + result = 31 * result + cache.hashCode() + return result + } + + } fun NDStructure.deferred(index: IntArray) = diff --git a/kmath-for-real/src/commonMain/kotlin/scientifik/kmath/linear/RealVector.kt b/kmath-for-real/src/commonMain/kotlin/scientifik/kmath/linear/RealVector.kt deleted file mode 100644 index d3cf07e79..000000000 --- a/kmath-for-real/src/commonMain/kotlin/scientifik/kmath/linear/RealVector.kt +++ /dev/null @@ -1,48 +0,0 @@ -package scientifik.kmath.linear - -import scientifik.kmath.operations.Field -import scientifik.kmath.operations.Norm -import scientifik.kmath.operations.RealField -import scientifik.kmath.operations.SpaceElement -import scientifik.kmath.structures.Buffer -import scientifik.kmath.structures.DoubleBuffer -import scientifik.kmath.structures.asBuffer -import scientifik.kmath.structures.asSequence - -fun DoubleArray.asVector() = RealVector(this.asBuffer()) -fun List.asVector() = RealVector(this.asBuffer()) - - -object VectorL2Norm : Norm, Double> { - override fun norm(arg: Point): Double = - kotlin.math.sqrt(arg.asSequence().sumByDouble { it.toDouble() }) -} - -inline class RealVector(val point: Point) : - SpaceElement, RealVector, VectorSpace>, Point { - override val context: VectorSpace get() = space(point.size) - - override fun unwrap(): Point = point - - override fun Point.wrap(): RealVector = RealVector(this) - - override val size: Int get() = point.size - - override fun get(index: Int): Double = point[index] - - override fun iterator(): Iterator = point.iterator() - - companion object { - - private val spaceCache = HashMap>() - - inline operator fun invoke(dim:Int, initalizer: (Int)-> Double) = RealVector(DoubleBuffer(dim, initalizer)) - - operator fun invoke(vararg values: Double) = values.asVector() - - fun space(dim: Int) = - spaceCache.getOrPut(dim) { - BufferVectorSpace(dim, RealField) { size, init -> Buffer.real(size, init) } - } - } -} \ No newline at end of file diff --git a/kmath-for-real/src/commonMain/kotlin/scientifik/kmath/real/RealVector.kt b/kmath-for-real/src/commonMain/kotlin/scientifik/kmath/real/RealVector.kt new file mode 100644 index 000000000..ff4c835ed --- /dev/null +++ b/kmath-for-real/src/commonMain/kotlin/scientifik/kmath/real/RealVector.kt @@ -0,0 +1,59 @@ +package scientifik.kmath.real + +import scientifik.kmath.linear.BufferVectorSpace +import scientifik.kmath.linear.Point +import scientifik.kmath.linear.VectorSpace +import scientifik.kmath.operations.Norm +import scientifik.kmath.operations.RealField +import scientifik.kmath.operations.SpaceElement +import scientifik.kmath.structures.Buffer +import scientifik.kmath.structures.DoubleBuffer +import scientifik.kmath.structures.asBuffer +import scientifik.kmath.structures.asIterable +import kotlin.math.sqrt + +fun DoubleArray.asVector() = RealVector(this.asBuffer()) +fun List.asVector() = RealVector(this.asBuffer()) + + +object VectorL2Norm : Norm, Double> { + override fun norm(arg: Point): Double = sqrt(arg.asIterable().sumByDouble { it.toDouble() }) +} + +inline class RealVector(private val point: Point) : + SpaceElement, RealVector, VectorSpace>, Point { + + override val context: VectorSpace + get() = space( + point.size + ) + + override fun unwrap(): Point = point + + override fun Point.wrap(): RealVector = + RealVector(this) + + override val size: Int get() = point.size + + override fun get(index: Int): Double = point[index] + + override fun iterator(): Iterator = point.iterator() + + companion object { + + private val spaceCache = HashMap>() + + inline operator fun invoke(dim: Int, initializer: (Int) -> Double) = + RealVector(DoubleBuffer(dim, initializer)) + + operator fun invoke(vararg values: Double): RealVector = values.asVector() + + fun space(dim: Int): BufferVectorSpace = + spaceCache.getOrPut(dim) { + BufferVectorSpace( + dim, + RealField + ) { size, init -> Buffer.real(size, init) } + } + } +} \ No newline at end of file diff --git a/kmath-for-real/src/commonTest/kotlin/scientifik/kmath/linear/VectorTest.kt b/kmath-for-real/src/commonTest/kotlin/scientifik/kmath/linear/VectorTest.kt index 0e73ee4a5..28e62b066 100644 --- a/kmath-for-real/src/commonTest/kotlin/scientifik/kmath/linear/VectorTest.kt +++ b/kmath-for-real/src/commonTest/kotlin/scientifik/kmath/linear/VectorTest.kt @@ -1,5 +1,6 @@ package scientifik.kmath.linear +import scientifik.kmath.real.RealVector import kotlin.test.Test import kotlin.test.assertEquals diff --git a/kmath-histograms/src/commonMain/kotlin/scientifik/kmath/histogram/RealHistogram.kt b/kmath-histograms/src/commonMain/kotlin/scientifik/kmath/histogram/RealHistogram.kt index 85f078fda..4438f5d60 100644 --- a/kmath-histograms/src/commonMain/kotlin/scientifik/kmath/histogram/RealHistogram.kt +++ b/kmath-histograms/src/commonMain/kotlin/scientifik/kmath/histogram/RealHistogram.kt @@ -1,7 +1,7 @@ package scientifik.kmath.histogram import scientifik.kmath.linear.Point -import scientifik.kmath.linear.asVector +import scientifik.kmath.real.asVector import scientifik.kmath.operations.SpaceOperations import scientifik.kmath.structures.* import kotlin.math.floor diff --git a/kmath-histograms/src/commonTest/kotlin/scietifik/kmath/histogram/MultivariateHistogramTest.kt b/kmath-histograms/src/commonTest/kotlin/scietifik/kmath/histogram/MultivariateHistogramTest.kt index 4dc4dfc74..5edecb5a5 100644 --- a/kmath-histograms/src/commonTest/kotlin/scietifik/kmath/histogram/MultivariateHistogramTest.kt +++ b/kmath-histograms/src/commonTest/kotlin/scietifik/kmath/histogram/MultivariateHistogramTest.kt @@ -3,7 +3,7 @@ package scietifik.kmath.histogram import scientifik.kmath.histogram.RealHistogram import scientifik.kmath.histogram.fill import scientifik.kmath.histogram.put -import scientifik.kmath.linear.RealVector +import scientifik.kmath.real.RealVector import kotlin.random.Random import kotlin.test.Test import kotlin.test.assertEquals diff --git a/kmath-histograms/src/jvmMain/kotlin/scientifik/kmath/histogram/UnivariateHistogram.kt b/kmath-histograms/src/jvmMain/kotlin/scientifik/kmath/histogram/UnivariateHistogram.kt index 90b9aff5e..dcc5ac0eb 100644 --- a/kmath-histograms/src/jvmMain/kotlin/scientifik/kmath/histogram/UnivariateHistogram.kt +++ b/kmath-histograms/src/jvmMain/kotlin/scientifik/kmath/histogram/UnivariateHistogram.kt @@ -1,7 +1,7 @@ package scientifik.kmath.histogram -import scientifik.kmath.linear.RealVector -import scientifik.kmath.linear.asVector +import scientifik.kmath.real.RealVector +import scientifik.kmath.real.asVector import scientifik.kmath.structures.Buffer import java.util.* import kotlin.math.floor diff --git a/kmath-koma/src/commonMain/kotlin/scientifik.kmath.linear/KomaMatrix.kt b/kmath-koma/src/commonMain/kotlin/scientifik.kmath.linear/KomaMatrix.kt index 74681ac48..10deabd73 100644 --- a/kmath-koma/src/commonMain/kotlin/scientifik.kmath.linear/KomaMatrix.kt +++ b/kmath-koma/src/commonMain/kotlin/scientifik.kmath.linear/KomaMatrix.kt @@ -4,6 +4,7 @@ import koma.extensions.fill import koma.matrix.MatrixFactory import scientifik.kmath.operations.Space import scientifik.kmath.structures.Matrix +import scientifik.kmath.structures.NDStructure class KomaMatrixContext( private val factory: MatrixFactory>, @@ -85,6 +86,18 @@ class KomaMatrix(val origin: koma.matrix.Matrix, features: Set ?: return false) + } + + override fun hashCode(): Int { + var result = origin.hashCode() + result = 31 * result + features.hashCode() + return result + } + + } class KomaVector internal constructor(val origin: koma.matrix.Matrix) : Point { diff --git a/settings.gradle.kts b/settings.gradle.kts index a08d5f7ee..57173250b 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,10 +1,12 @@ pluginManagement { + val toolsVersion = "0.5.0" + plugins { - id("scientifik.mpp") version "0.4.1" - id("scientifik.jvm") version "0.4.1" - id("scientifik.atomic") version "0.4.1" - id("scientifik.publish") version "0.4.1" + id("scientifik.mpp") version toolsVersion + id("scientifik.jvm") version toolsVersion + id("scientifik.atomic") version toolsVersion + id("scientifik.publish") version toolsVersion } repositories { @@ -20,7 +22,7 @@ pluginManagement { resolutionStrategy { eachPlugin { when (requested.id.id) { - "scientifik.mpp", "scientifik.jvm", "scientifik.publish" -> useModule("scientifik:gradle-tools:${requested.version}") + "scientifik.mpp", "scientifik.jvm", "scientifik.publish" -> useModule("scientifik:gradle-tools:$toolsVersion") } } } -- 2.34.1 From e5ffb22126d9cc7a7c16a3d4a55c7940978542bd Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Thu, 7 May 2020 09:54:46 +0300 Subject: [PATCH 003/156] For-real refactoring and test fix. Never call equals on buffer --- build.gradle.kts | 2 +- .../scientifik/kmath/linear/BufferMatrix.kt | 13 +- .../scientifik/kmath/linear/LinearAlgebra.kt | 6 - .../scientifik/kmath/linear/MatrixBuilder.kt | 38 ++++- .../scientifik/kmath/structures/Buffers.kt | 31 +--- .../kmath/structures/DoubleBuffer.kt | 34 ++++ .../kmath/structures/NDStructure.kt | 10 +- .../kmath/structures/Structure1D.kt | 2 +- .../scientifik/kmath/linear/MatrixTest.kt | 2 +- .../kmath/real/DoubleMatrixOperations.kt | 146 ---------------- .../scientifik/kmath/real/realBuffer.kt | 8 + .../scientifik/kmath/real/realMatrix.kt | 161 ++++++++++++++++++ .../scientific.kmath.real/RealMatrixTest.kt | 133 ++++++++------- 13 files changed, 331 insertions(+), 255 deletions(-) create mode 100644 kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/DoubleBuffer.kt delete mode 100644 kmath-for-real/src/commonMain/kotlin/scientifik/kmath/real/DoubleMatrixOperations.kt create mode 100644 kmath-for-real/src/commonMain/kotlin/scientifik/kmath/real/realBuffer.kt create mode 100644 kmath-for-real/src/commonMain/kotlin/scientifik/kmath/real/realMatrix.kt diff --git a/build.gradle.kts b/build.gradle.kts index 34e87fa2a..4a7e99d90 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -2,7 +2,7 @@ plugins { id("scientifik.publish") apply false } -val kmathVersion by extra("0.1.4-dev-5") +val kmathVersion by extra("0.1.4-dev-6") val bintrayRepo by extra("scientifik") val githubProject by extra("kmath") diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/BufferMatrix.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/BufferMatrix.kt index 624cd44d4..73b18b810 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/BufferMatrix.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/BufferMatrix.kt @@ -27,7 +27,7 @@ class BufferMatrixContext>( @Suppress("OVERRIDE_BY_INLINE") object RealMatrixContext : GenericMatrixContext { - override val elementContext = RealField + override val elementContext get() = RealField override inline fun produce(rows: Int, columns: Int, initializer: (i: Int, j: Int) -> Double): Matrix { val buffer = DoubleBuffer(rows * columns) { offset -> initializer(offset / columns, offset % columns) } @@ -101,8 +101,15 @@ infix fun BufferMatrix.dot(other: BufferMatrix): BufferMatrix.unsafeArray(): DoubleArray = if (this is DoubleBuffer) { + array + } else { + DoubleArray(size) { get(it) } + } + + val a = this.buffer.unsafeArray() + val b = other.buffer.unsafeArray() for (i in (0 until rowNum)) { for (j in (0 until other.colNum)) { diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/LinearAlgebra.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/LinearAlgebra.kt index 0456ffebb..abd8603f4 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/LinearAlgebra.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/LinearAlgebra.kt @@ -1,12 +1,8 @@ package scientifik.kmath.linear -import scientifik.kmath.operations.Field -import scientifik.kmath.operations.Norm -import scientifik.kmath.operations.RealField import scientifik.kmath.structures.Buffer import scientifik.kmath.structures.Matrix import scientifik.kmath.structures.VirtualBuffer -import scientifik.kmath.structures.asSequence typealias Point = Buffer @@ -19,8 +15,6 @@ interface LinearSolver { fun inverse(a: Matrix): Matrix } -typealias RealMatrix = Matrix - /** * Convert matrix to vector if it is possible */ diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/MatrixBuilder.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/MatrixBuilder.kt index 466dbea6e..516f65bb8 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/MatrixBuilder.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/MatrixBuilder.kt @@ -1,14 +1,46 @@ package scientifik.kmath.linear +import scientifik.kmath.structures.Buffer +import scientifik.kmath.structures.BufferFactory import scientifik.kmath.structures.Structure2D import scientifik.kmath.structures.asBuffer -class MatrixBuilder(val rows: Int, val columns: Int) { - operator fun invoke(vararg elements: T): FeaturedMatrix { +class MatrixBuilder(val rows: Int, val columns: Int) { + operator fun invoke(vararg elements: T): FeaturedMatrix { if (rows * columns != elements.size) error("The number of elements ${elements.size} is not equal $rows * $columns") val buffer = elements.asBuffer() return BufferMatrix(rows, columns, buffer) } + + //TODO add specific matrix builder functions like diagonal, etc } -fun Structure2D.Companion.build(rows: Int, columns: Int): MatrixBuilder = MatrixBuilder(rows, columns) \ No newline at end of file +fun Structure2D.Companion.build(rows: Int, columns: Int): MatrixBuilder = MatrixBuilder(rows, columns) + +fun Structure2D.Companion.row(vararg values: T): FeaturedMatrix { + val buffer = values.asBuffer() + return BufferMatrix(1, values.size, buffer) +} + +inline fun Structure2D.Companion.row( + size: Int, + factory: BufferFactory = Buffer.Companion::auto, + noinline builder: (Int) -> T +): FeaturedMatrix { + val buffer = factory(size, builder) + return BufferMatrix(1, size, buffer) +} + +fun Structure2D.Companion.column(vararg values: T): FeaturedMatrix { + val buffer = values.asBuffer() + return BufferMatrix(values.size, 1, buffer) +} + +inline fun Structure2D.Companion.column( + size: Int, + factory: BufferFactory = Buffer.Companion::auto, + noinline builder: (Int) -> T +): FeaturedMatrix { + val buffer = factory(size, builder) + return BufferMatrix(size, 1, buffer) +} 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 ab8891ab9..613a0d7ca 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/Buffers.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/Buffers.kt @@ -161,36 +161,7 @@ class ArrayBuffer(private val array: Array) : MutableBuffer { override fun copy(): MutableBuffer = ArrayBuffer(array.copyOf()) } -fun Array.asBuffer() = ArrayBuffer(this) - -inline class DoubleBuffer(val array: DoubleArray) : MutableBuffer { - override val size: Int get() = array.size - - override fun get(index: Int): Double = array[index] - - override fun set(index: Int, value: Double) { - array[index] = value - } - - override fun iterator() = array.iterator() - - override fun copy(): MutableBuffer = DoubleBuffer(array.copyOf()) -} - -@Suppress("FunctionName") -inline fun DoubleBuffer(size: Int, init: (Int) -> Double) = DoubleBuffer(DoubleArray(size) { init(it) }) - -/** - * Transform buffer of doubles into array for high performance operations - */ -val Buffer.array: DoubleArray - get() = if (this is DoubleBuffer) { - array - } else { - DoubleArray(size) { get(it) } - } - -fun DoubleArray.asBuffer() = DoubleBuffer(this) +fun Array.asBuffer(): ArrayBuffer = ArrayBuffer(this) inline class ShortBuffer(val array: ShortArray) : MutableBuffer { override val size: Int get() = array.size diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/DoubleBuffer.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/DoubleBuffer.kt new file mode 100644 index 000000000..c0b7f713b --- /dev/null +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/DoubleBuffer.kt @@ -0,0 +1,34 @@ +package scientifik.kmath.structures + +inline class DoubleBuffer(val array: DoubleArray) : MutableBuffer { + override val size: Int get() = array.size + + override fun get(index: Int): Double = array[index] + + override fun set(index: Int, value: Double) { + array[index] = value + } + + override fun iterator() = array.iterator() + + override fun copy(): MutableBuffer = + DoubleBuffer(array.copyOf()) +} + +@Suppress("FunctionName") +inline fun DoubleBuffer(size: Int, init: (Int) -> Double): DoubleBuffer = DoubleBuffer(DoubleArray(size) { init(it) }) + +@Suppress("FunctionName") +fun DoubleBuffer(vararg doubles: Double): DoubleBuffer = DoubleBuffer(doubles) + +/** + * Transform buffer of doubles into array for high performance operations + */ +val MutableBuffer.array: DoubleArray + get() = if (this is DoubleBuffer) { + array + } else { + DoubleArray(size) { get(it) } + } + +fun DoubleArray.asBuffer() = DoubleBuffer(this) \ 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 25d736b92..236a6f366 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/NDStructure.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/NDStructure.kt @@ -20,10 +20,14 @@ interface NDStructure { companion object { fun equals(st1: NDStructure<*>, st2: NDStructure<*>): Boolean { - if(st1===st2) return true + if (st1 === st2) return true // fast comparison of buffers if possible - if(st1 is NDBuffer && st2 is NDBuffer && st1.strides == st2.strides){ + if ( + st1 is NDBuffer && + st2 is NDBuffer && + st1.strides == st2.strides + ) { return st1.buffer.contentEquals(st2.buffer) } @@ -210,7 +214,7 @@ abstract class NDBuffer : NDStructure { class BufferNDStructure( override val strides: Strides, override val buffer: Buffer -) : NDBuffer(){ +) : NDBuffer() { init { if (strides.linearSize != buffer.size) { error("Expected buffer side of ${strides.linearSize}, but found ${buffer.size}") diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/Structure1D.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/Structure1D.kt index df56017a3..9974538ec 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/Structure1D.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/Structure1D.kt @@ -17,7 +17,7 @@ interface Structure1D : NDStructure, Buffer { /** * A 1D wrapper for nd-structure */ -private inline class Structure1DWrapper(val structure: NDStructure) : Structure1D { +private inline class Structure1DWrapper(val structure: NDStructure) : Structure1D{ override val shape: IntArray get() = structure.shape override val size: Int get() = structure.shape[0] 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 7d1209963..dcd510e32 100644 --- a/kmath-core/src/commonTest/kotlin/scientifik/kmath/linear/MatrixTest.kt +++ b/kmath-core/src/commonTest/kotlin/scientifik/kmath/linear/MatrixTest.kt @@ -17,7 +17,7 @@ class MatrixTest { @Test fun testBuilder() { - val matrix = Matrix.build(2, 3)( + val matrix = Matrix.build(2, 3)( 1.0, 0.0, 0.0, 0.0, 1.0, 2.0 ) diff --git a/kmath-for-real/src/commonMain/kotlin/scientifik/kmath/real/DoubleMatrixOperations.kt b/kmath-for-real/src/commonMain/kotlin/scientifik/kmath/real/DoubleMatrixOperations.kt deleted file mode 100644 index 7eeba3031..000000000 --- a/kmath-for-real/src/commonMain/kotlin/scientifik/kmath/real/DoubleMatrixOperations.kt +++ /dev/null @@ -1,146 +0,0 @@ -package scientifik.kmath.real - -import scientifik.kmath.linear.MatrixContext -import scientifik.kmath.linear.RealMatrixContext.elementContext -import scientifik.kmath.linear.VirtualMatrix -import scientifik.kmath.operations.sum -import scientifik.kmath.structures.Buffer -import scientifik.kmath.structures.Matrix -import scientifik.kmath.structures.asSequence -import kotlin.math.pow - -/* - * Functions for convenient "numpy-like" operations with Double matrices. - * - * Initial implementation of these functions is taken from: - * https://github.com/thomasnield/numky/blob/master/src/main/kotlin/org/nield/numky/linear/DoubleOperators.kt - * - */ - -/* - * Functions that help create a real (Double) matrix - */ - -fun realMatrix(rowNum: Int, colNum: Int, initializer: (i: Int, j: Int) -> Double) = - MatrixContext.real.produce(rowNum, colNum, initializer) - -fun Sequence.toMatrix() = toList().let { - MatrixContext.real.produce(it.size, it[0].size) { row, col -> it[row][col] } -} - -fun Matrix.repeatStackVertical(n: Int) = VirtualMatrix(rowNum*n, colNum) { - row, col -> get(if (row == 0) 0 else row % rowNum, col) -} - -/* - * Operations for matrix and real number - */ - -operator fun Matrix.times(double: Double) = MatrixContext.real.produce(rowNum, colNum) { - row, col -> this[row, col] * double -} - -operator fun Matrix.plus(double: Double) = MatrixContext.real.produce(rowNum, colNum) { - row, col -> this[row, col] + double -} - -operator fun Matrix.minus(double: Double) = MatrixContext.real.produce(rowNum, colNum) { - row, col -> this[row, col] - double -} - -operator fun Matrix.div(double: Double) = MatrixContext.real.produce(rowNum, colNum) { - row, col -> this[row, col] / double -} - -operator fun Double.times(matrix: Matrix) = MatrixContext.real.produce(matrix.rowNum, matrix.colNum) { - row, col -> this * matrix[row, col] -} - -operator fun Double.plus(matrix: Matrix) = MatrixContext.real.produce(matrix.rowNum, matrix.colNum) { - row, col -> this + matrix[row, col] -} - -operator fun Double.minus(matrix: Matrix) = MatrixContext.real.produce(matrix.rowNum, matrix.colNum) { - row, col -> this - matrix[row, col] -} - -// TODO: does this operation make sense? Should it be 'this/matrix[row, col]'? -//operator fun Double.div(matrix: Matrix) = MatrixContext.real.produce(matrix.rowNum, matrix.colNum) { -// row, col -> matrix[row, col] / this -//} - -/* - * Per-element (!) square and power operations - */ - -fun Matrix.square() = MatrixContext.real.produce(rowNum, colNum) { - row, col -> this[row, col].pow(2) -} - -fun Matrix.pow(n: Int) = MatrixContext.real.produce(rowNum, colNum) { - i, j -> this[i, j].pow(n) -} - -/* - * Operations on two matrices (per-element!) - */ - -operator fun Matrix.times(other: Matrix) = MatrixContext.real.produce(rowNum, colNum) { - row, col -> this[row, col] * other[row, col] -} - -operator fun Matrix.plus(other: Matrix) = MatrixContext.real.add(this, other) - -operator fun Matrix.minus(other: Matrix) = MatrixContext.real.produce(rowNum, colNum) { - row, col -> this[row,col] - other[row,col] -} - -/* - * Operations on columns - */ - -inline fun Matrix.appendColumn(crossinline mapper: (Buffer) -> Double) = - MatrixContext.real.produce(rowNum,colNum+1) { - row, col -> - if (col < colNum) - this[row, col] - else - mapper(rows[row]) - } - -fun Matrix.extractColumns(columnRange: IntRange) = MatrixContext.real.produce(rowNum, columnRange.count()) { - row, col -> this[row, columnRange.first + col] -} - -fun Matrix.extractColumn(columnIndex: Int) = extractColumns(columnIndex..columnIndex) - -fun Matrix.sumByColumn() = MatrixContext.real.produce(1, colNum) { _, j -> - val column = columns[j] - with(elementContext) { - sum(column.asSequence()) - } -} - -fun Matrix.minByColumn() = MatrixContext.real.produce(1, colNum) { - _, j -> columns[j].asSequence().min() ?: throw Exception("Cannot produce min on empty column") -} - -fun Matrix.maxByColumn() = MatrixContext.real.produce(1, colNum) { - _, j -> columns[j].asSequence().max() ?: throw Exception("Cannot produce min on empty column") -} - -fun Matrix.averageByColumn() = MatrixContext.real.produce(1, colNum) { - _, j -> columns[j].asSequence().average() -} - -/* - * Operations processing all elements - */ - -fun Matrix.sum() = elements().map { (_, value) -> value }.sum() - -fun Matrix.min() = elements().map { (_, value) -> value }.min() - -fun Matrix.max() = elements().map { (_, value) -> value }.max() - -fun Matrix.average() = elements().map { (_, value) -> value }.average() diff --git a/kmath-for-real/src/commonMain/kotlin/scientifik/kmath/real/realBuffer.kt b/kmath-for-real/src/commonMain/kotlin/scientifik/kmath/real/realBuffer.kt new file mode 100644 index 000000000..af3569439 --- /dev/null +++ b/kmath-for-real/src/commonMain/kotlin/scientifik/kmath/real/realBuffer.kt @@ -0,0 +1,8 @@ +package scientifik.kmath.real + +import scientifik.kmath.structures.DoubleBuffer + +/** + * C + */ +fun DoubleBuffer.contentEquals(vararg doubles: Double) = array.contentEquals(doubles) \ No newline at end of file diff --git a/kmath-for-real/src/commonMain/kotlin/scientifik/kmath/real/realMatrix.kt b/kmath-for-real/src/commonMain/kotlin/scientifik/kmath/real/realMatrix.kt new file mode 100644 index 000000000..813d89577 --- /dev/null +++ b/kmath-for-real/src/commonMain/kotlin/scientifik/kmath/real/realMatrix.kt @@ -0,0 +1,161 @@ +package scientifik.kmath.real + +import scientifik.kmath.linear.MatrixContext +import scientifik.kmath.linear.RealMatrixContext.elementContext +import scientifik.kmath.linear.VirtualMatrix +import scientifik.kmath.operations.sum +import scientifik.kmath.structures.Buffer +import scientifik.kmath.structures.DoubleBuffer +import scientifik.kmath.structures.Matrix +import scientifik.kmath.structures.asIterable +import kotlin.math.pow + +/* + * Functions for convenient "numpy-like" operations with Double matrices. + * + * Initial implementation of these functions is taken from: + * https://github.com/thomasnield/numky/blob/master/src/main/kotlin/org/nield/numky/linear/DoubleOperators.kt + * + */ + +/* + * Functions that help create a real (Double) matrix + */ + +typealias RealMatrix = Matrix + +fun realMatrix(rowNum: Int, colNum: Int, initializer: (i: Int, j: Int) -> Double): RealMatrix = + MatrixContext.real.produce(rowNum, colNum, initializer) + +fun Sequence.toMatrix(): RealMatrix = toList().let { + MatrixContext.real.produce(it.size, it[0].size) { row, col -> it[row][col] } +} + +fun Matrix.repeatStackVertical(n: Int): RealMatrix = + VirtualMatrix(rowNum * n, colNum) { row, col -> + get(if (row == 0) 0 else row % rowNum, col) + } + +/* + * Operations for matrix and real number + */ + +operator fun Matrix.times(double: Double): RealMatrix = + MatrixContext.real.produce(rowNum, colNum) { row, col -> + this[row, col] * double + } + +operator fun Matrix.plus(double: Double): RealMatrix = + MatrixContext.real.produce(rowNum, colNum) { row, col -> + this[row, col] + double + } + +operator fun Matrix.minus(double: Double): RealMatrix = + MatrixContext.real.produce(rowNum, colNum) { row, col -> + this[row, col] - double + } + +operator fun Matrix.div(double: Double): RealMatrix = + MatrixContext.real.produce(rowNum, colNum) { row, col -> + this[row, col] / double + } + +operator fun Double.times(matrix: Matrix): RealMatrix = + MatrixContext.real.produce(matrix.rowNum, matrix.colNum) { row, col -> + this * matrix[row, col] + } + +operator fun Double.plus(matrix: Matrix): RealMatrix = + MatrixContext.real.produce(matrix.rowNum, matrix.colNum) { row, col -> + this + matrix[row, col] + } + +operator fun Double.minus(matrix: Matrix): RealMatrix = + MatrixContext.real.produce(matrix.rowNum, matrix.colNum) { row, col -> + this - matrix[row, col] + } + +// TODO: does this operation make sense? Should it be 'this/matrix[row, col]'? +//operator fun Double.div(matrix: Matrix) = MatrixContext.real.produce(matrix.rowNum, matrix.colNum) { +// row, col -> matrix[row, col] / this +//} + +/* + * Per-element (!) square and power operations + */ + +fun Matrix.square(): RealMatrix = MatrixContext.real.produce(rowNum, colNum) { row, col -> + this[row, col].pow(2) +} + +fun Matrix.pow(n: Int): RealMatrix = MatrixContext.real.produce(rowNum, colNum) { i, j -> + this[i, j].pow(n) +} + +/* + * Operations on two matrices (per-element!) + */ + +operator fun Matrix.times(other: Matrix): RealMatrix = + MatrixContext.real.produce(rowNum, colNum) { row, col -> + this[row, col] * other[row, col] + } + +operator fun Matrix.plus(other: Matrix): RealMatrix = + MatrixContext.real.add(this, other) + +operator fun Matrix.minus(other: Matrix): RealMatrix = + MatrixContext.real.produce(rowNum, colNum) { row, col -> + this[row, col] - other[row, col] + } + +/* + * Operations on columns + */ + +inline fun Matrix.appendColumn(crossinline mapper: (Buffer) -> Double) = + MatrixContext.real.produce(rowNum, colNum + 1) { row, col -> + if (col < colNum) + this[row, col] + else + mapper(rows[row]) + } + +fun Matrix.extractColumns(columnRange: IntRange): RealMatrix = + MatrixContext.real.produce(rowNum, columnRange.count()) { row, col -> + this[row, columnRange.first + col] + } + +fun Matrix.extractColumn(columnIndex: Int): RealMatrix = + extractColumns(columnIndex..columnIndex) + +fun Matrix.sumByColumn(): DoubleBuffer = DoubleBuffer(colNum) { j -> + val column = columns[j] + with(elementContext) { + sum(column.asIterable()) + } +} + +fun Matrix.minByColumn(): DoubleBuffer = DoubleBuffer(colNum) { j -> + columns[j].asIterable().min() ?: throw Exception("Cannot produce min on empty column") +} + +fun Matrix.maxByColumn(): DoubleBuffer = DoubleBuffer(colNum) { j -> + columns[j].asIterable().max() ?: throw Exception("Cannot produce min on empty column") +} + +fun Matrix.averageByColumn(): DoubleBuffer = DoubleBuffer(colNum) { j -> + columns[j].asIterable().average() +} + +/* + * Operations processing all elements + */ + +fun Matrix.sum() = elements().map { (_, value) -> value }.sum() + +fun Matrix.min() = elements().map { (_, value) -> value }.min() + +fun Matrix.max() = elements().map { (_, value) -> value }.max() + +fun Matrix.average() = elements().map { (_, value) -> value }.average() diff --git a/kmath-for-real/src/commonTest/kotlin/scientific.kmath.real/RealMatrixTest.kt b/kmath-for-real/src/commonTest/kotlin/scientific.kmath.real/RealMatrixTest.kt index e72711490..8918fb300 100644 --- a/kmath-for-real/src/commonTest/kotlin/scientific.kmath.real/RealMatrixTest.kt +++ b/kmath-for-real/src/commonTest/kotlin/scientific.kmath.real/RealMatrixTest.kt @@ -1,11 +1,12 @@ package scientific.kmath.real -import scientifik.kmath.real.* import scientifik.kmath.linear.VirtualMatrix import scientifik.kmath.linear.build +import scientifik.kmath.real.* import scientifik.kmath.structures.Matrix import kotlin.test.Test import kotlin.test.assertEquals +import kotlin.test.assertTrue class RealMatrixTest { @Test @@ -19,72 +20,72 @@ class RealMatrixTest { fun testSequenceToMatrix() { val m = Sequence { listOf( - DoubleArray(10) { 10.0 }, - DoubleArray(10) { 20.0 }, - DoubleArray(10) { 30.0 }).iterator() + DoubleArray(10) { 10.0 }, + DoubleArray(10) { 20.0 }, + DoubleArray(10) { 30.0 }).iterator() }.toMatrix() assertEquals(m.sum(), 20.0 * 30) } @Test fun testRepeatStackVertical() { - val matrix1 = Matrix.build(2, 3)( - 1.0, 0.0, 0.0, - 0.0, 1.0, 2.0 + val matrix1 = Matrix.build(2, 3)( + 1.0, 0.0, 0.0, + 0.0, 1.0, 2.0 ) - val matrix2 = Matrix.build(6, 3)( - 1.0, 0.0, 0.0, - 0.0, 1.0, 2.0, - 1.0, 0.0, 0.0, - 0.0, 1.0, 2.0, - 1.0, 0.0, 0.0, - 0.0, 1.0, 2.0 + val matrix2 = Matrix.build(6, 3)( + 1.0, 0.0, 0.0, + 0.0, 1.0, 2.0, + 1.0, 0.0, 0.0, + 0.0, 1.0, 2.0, + 1.0, 0.0, 0.0, + 0.0, 1.0, 2.0 ) assertEquals(VirtualMatrix.wrap(matrix2), matrix1.repeatStackVertical(3)) } @Test fun testMatrixAndDouble() { - val matrix1 = Matrix.build(2, 3)( - 1.0, 0.0, 3.0, - 4.0, 6.0, 2.0 + val matrix1 = Matrix.build(2, 3)( + 1.0, 0.0, 3.0, + 4.0, 6.0, 2.0 ) val matrix2 = (matrix1 * 2.5 + 1.0 - 2.0) / 2.0 - val expectedResult = Matrix.build(2, 3)( - 0.75, -0.5, 3.25, - 4.5, 7.0, 2.0 + val expectedResult = Matrix.build(2, 3)( + 0.75, -0.5, 3.25, + 4.5, 7.0, 2.0 ) assertEquals(matrix2, expectedResult) } @Test fun testDoubleAndMatrix() { - val matrix1 = Matrix.build(2, 3)( - 1.0, 0.0, 3.0, - 4.0, 6.0, 2.0 + val matrix1 = Matrix.build(2, 3)( + 1.0, 0.0, 3.0, + 4.0, 6.0, 2.0 ) val matrix2 = 20.0 - (10.0 + (5.0 * matrix1)) //val matrix2 = 10.0 + (5.0 * matrix1) - val expectedResult = Matrix.build(2, 3)( - 5.0, 10.0, -5.0, - -10.0, -20.0, 0.0 + val expectedResult = Matrix.build(2, 3)( + 5.0, 10.0, -5.0, + -10.0, -20.0, 0.0 ) assertEquals(matrix2, expectedResult) } @Test fun testSquareAndPower() { - val matrix1 = Matrix.build(2, 3)( - -1.0, 0.0, 3.0, - 4.0, -6.0, -2.0 + val matrix1 = Matrix.build(2, 3)( + -1.0, 0.0, 3.0, + 4.0, -6.0, -2.0 ) - val matrix2 = Matrix.build(2, 3)( - 1.0, 0.0, 9.0, - 16.0, 36.0, 4.0 + val matrix2 = Matrix.build(2, 3)( + 1.0, 0.0, 9.0, + 16.0, 36.0, 4.0 ) - val matrix3 = Matrix.build(2, 3)( - -1.0, 0.0, 27.0, - 64.0, -216.0, -8.0 + val matrix3 = Matrix.build(2, 3)( + -1.0, 0.0, 27.0, + 64.0, -216.0, -8.0 ) assertEquals(matrix1.square(), matrix2) assertEquals(matrix1.pow(3), matrix3) @@ -92,51 +93,61 @@ class RealMatrixTest { @Test fun testTwoMatrixOperations() { - val matrix1 = Matrix.build(2, 3)( - -1.0, 0.0, 3.0, - 4.0, -6.0, 7.0 + val matrix1 = Matrix.build(2, 3)( + -1.0, 0.0, 3.0, + 4.0, -6.0, 7.0 ) - val matrix2 = Matrix.build(2, 3)( - 1.0, 0.0, 3.0, - 4.0, 6.0, -2.0 + val matrix2 = Matrix.build(2, 3)( + 1.0, 0.0, 3.0, + 4.0, 6.0, -2.0 ) val result = matrix1 * matrix2 + matrix1 - matrix2 - val expectedResult = Matrix.build(2, 3)( - -3.0, 0.0, 9.0, - 16.0, -48.0, -5.0 + val expectedResult = Matrix.build(2, 3)( + -3.0, 0.0, 9.0, + 16.0, -48.0, -5.0 ) assertEquals(result, expectedResult) } @Test fun testColumnOperations() { - val matrix1 = Matrix.build(2, 4)( - -1.0, 0.0, 3.0, 15.0, - 4.0, -6.0, 7.0, -11.0 + val matrix1 = Matrix.build(2, 4)( + -1.0, 0.0, 3.0, 15.0, + 4.0, -6.0, 7.0, -11.0 ) - val matrix2 = Matrix.build(2, 5)( - -1.0, 0.0, 3.0, 15.0, -1.0, - 4.0, -6.0, 7.0, -11.0, 4.0 + val matrix2 = Matrix.build(2, 5)( + -1.0, 0.0, 3.0, 15.0, -1.0, + 4.0, -6.0, 7.0, -11.0, 4.0 ) - val col1 = Matrix.build(2, 1)(0.0, -6.0) - val cols1to2 = Matrix.build(2, 2)( - 0.0, 3.0, - -6.0, 7.0 + val col1 = Matrix.build(2, 1)(0.0, -6.0) + val cols1to2 = Matrix.build(2, 2)( + 0.0, 3.0, + -6.0, 7.0 ) + assertEquals(matrix1.appendColumn { it[0] }, matrix2) assertEquals(matrix1.extractColumn(1), col1) assertEquals(matrix1.extractColumns(1..2), cols1to2) - assertEquals(matrix1.sumByColumn(), Matrix.build(1, 4)(3.0, -6.0, 10.0, 4.0)) - assertEquals(matrix1.minByColumn(), Matrix.build(1, 4)(-1.0, -6.0, 3.0, -11.0)) - assertEquals(matrix1.maxByColumn(), Matrix.build(1, 4)(4.0, 0.0, 7.0, 15.0)) - assertEquals(matrix1.averageByColumn(), Matrix.build(1, 4)(1.5, -3.0, 5.0, 2.0)) + //equals should never be called on buffers + assertTrue { + matrix1.sumByColumn().contentEquals(3.0, -6.0, 10.0, 4.0) + } //assertEquals(matrix1.sumByColumn(), DoubleBuffer(3.0, -6.0, 10.0, 4.0)) + assertTrue { + matrix1.minByColumn().contentEquals(-1.0, -6.0, 3.0, -11.0) + } //assertEquals(matrix1.minByColumn(), DoubleBuffer(-1.0, -6.0, 3.0, -11.0)) + assertTrue { + matrix1.maxByColumn().contentEquals(4.0, 0.0, 7.0, 15.0) + } //assertEquals(matrix1.maxByColumn(), DoubleBuffer(4.0, 0.0, 7.0, 15.0)) + assertTrue { + matrix1.averageByColumn().contentEquals(1.5, -3.0, 5.0, 2.0) + } //assertEquals(matrix1.averageByColumn(), DoubleBuffer(1.5, -3.0, 5.0, 2.0)) } @Test fun testAllElementOperations() { - val matrix1 = Matrix.build(2, 4)( - -1.0, 0.0, 3.0, 15.0, - 4.0, -6.0, 7.0, -11.0 + val matrix1 = Matrix.build(2, 4)( + -1.0, 0.0, 3.0, 15.0, + 4.0, -6.0, 7.0, -11.0 ) assertEquals(matrix1.sum(), 11.0) assertEquals(matrix1.min(), -11.0) -- 2.34.1 From c15f77aceffaa026c589628eddd46b7486561152 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Wed, 13 May 2020 21:54:36 +0300 Subject: [PATCH 004/156] doc for realBuffer --- .../src/commonMain/kotlin/scientifik/kmath/real/realBuffer.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kmath-for-real/src/commonMain/kotlin/scientifik/kmath/real/realBuffer.kt b/kmath-for-real/src/commonMain/kotlin/scientifik/kmath/real/realBuffer.kt index af3569439..d9ee4d90b 100644 --- a/kmath-for-real/src/commonMain/kotlin/scientifik/kmath/real/realBuffer.kt +++ b/kmath-for-real/src/commonMain/kotlin/scientifik/kmath/real/realBuffer.kt @@ -3,6 +3,6 @@ package scientifik.kmath.real import scientifik.kmath.structures.DoubleBuffer /** - * C + * Simplified [DoubleBuffer] to array comparison */ fun DoubleBuffer.contentEquals(vararg doubles: Double) = array.contentEquals(doubles) \ No newline at end of file -- 2.34.1 From 4a26c1080e2b16ab3a6d88675e7604ae2141f775 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Thu, 14 May 2020 20:30:43 +0300 Subject: [PATCH 005/156] A prototype for advanced expressoins --- .../kmath/expressions/Expression.kt | 67 +--------- .../expressions/functionalExpressions.kt | 125 ++++++++++++++++++ .../kmath/expressions/syntaxTree.kt | 31 +++++ .../kmath/expressions/ExpressionFieldTest.kt | 12 +- 4 files changed, 163 insertions(+), 72 deletions(-) create mode 100644 kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/functionalExpressions.kt create mode 100644 kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/syntaxTree.kt 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 aa7407c0a..bc3d70b4f 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/Expression.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/Expression.kt @@ -1,9 +1,5 @@ package scientifik.kmath.expressions -import scientifik.kmath.operations.Field -import scientifik.kmath.operations.Ring -import scientifik.kmath.operations.Space - /** * An elementary function that could be invoked on a map of arguments */ @@ -26,67 +22,6 @@ interface ExpressionContext { * A constant expression which does not depend on arguments */ fun const(value: T): Expression -} -internal class VariableExpression(val name: String, val default: T? = null) : Expression { - override fun invoke(arguments: Map): T = - arguments[name] ?: default ?: error("Parameter not found: $name") -} - -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 { - override fun invoke(arguments: Map): T = context.add(first.invoke(arguments), second.invoke(arguments)) -} - -internal class ProductExpression(val context: Ring, 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: Space, val expr: Expression, val const: Number) : - 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 { - override fun invoke(arguments: Map): T = context.divide(expr.invoke(arguments), second.invoke(arguments)) -} - -open class ExpressionSpace(val space: Space) : Space>, ExpressionContext { - override val zero: Expression = ConstantExpression(space.zero) - - override fun const(value: T): Expression = ConstantExpression(value) - - override fun variable(name: String, default: T?): Expression = VariableExpression(name, default) - - override fun add(a: Expression, b: Expression): Expression = SumExpression(space, a, b) - - override fun multiply(a: Expression, k: Number): Expression = ConstProductExpession(space, a, k) - - - operator fun Expression.plus(arg: T) = this + const(arg) - operator fun Expression.minus(arg: T) = this - const(arg) - - operator fun T.plus(arg: Expression) = arg + this - operator fun T.minus(arg: Expression) = arg - this -} - - -class ExpressionField(val field: Field) : Field>, ExpressionSpace(field) { - override val one: Expression = ConstantExpression(field.one) - 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.times(arg: T) = this * const(arg) - operator fun Expression.div(arg: T) = this / const(arg) - - operator fun T.times(arg: Expression) = arg * this - operator fun T.div(arg: Expression) = arg / this + fun produce(node: SyntaxTreeNode): Expression } \ No newline at end of file diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/functionalExpressions.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/functionalExpressions.kt new file mode 100644 index 000000000..64b3ad72c --- /dev/null +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/functionalExpressions.kt @@ -0,0 +1,125 @@ +package scientifik.kmath.expressions + +import scientifik.kmath.operations.Field +import scientifik.kmath.operations.Ring +import scientifik.kmath.operations.Space + +internal class VariableExpression(val name: String, val default: T? = null) : Expression { + override fun invoke(arguments: Map): T = + arguments[name] ?: default ?: error("Parameter not found: $name") +} + +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 { + override fun invoke(arguments: Map): T = context.add(first.invoke(arguments), second.invoke(arguments)) +} + +internal class ProductExpression(val context: Ring, 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: Space, val expr: Expression, val const: Number) : + 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 { + override fun invoke(arguments: Map): T = context.divide(expr.invoke(arguments), second.invoke(arguments)) +} + +open class FunctionalExpressionSpace( + val space: Space, + one: T +) : Space>, ExpressionContext { + + override val zero: Expression = ConstantExpression(space.zero) + + val one: Expression = ConstantExpression(one) + + override fun const(value: T): Expression = ConstantExpression(value) + + override fun variable(name: String, default: T?): Expression = VariableExpression(name, default) + + override fun add(a: Expression, b: Expression): Expression = SumExpression(space, a, b) + + override fun multiply(a: Expression, k: Number): Expression = ConstProductExpession(space, a, k) + + + operator fun Expression.plus(arg: T) = this + const(arg) + operator fun Expression.minus(arg: T) = this - const(arg) + + operator fun T.plus(arg: Expression) = arg + this + operator fun T.minus(arg: Expression) = arg - this + + fun const(value: Double): Expression = one.times(value) + + open fun produceSingular(value: String): Expression { + val numberValue = value.toDoubleOrNull() + return if (numberValue == null) { + variable(value) + } else { + const(numberValue) + } + } + + open fun produceUnary(operation: String, value: Expression): Expression { + return when (operation) { + UnaryNode.PLUS_OPERATION -> value + UnaryNode.MINUS_OPERATION -> -value + else -> error("Unary operation $operation is not supported by $this") + } + } + + open fun produceBinary(operation: String, left: Expression, right: Expression): Expression { + return when (operation) { + BinaryNode.PLUS_OPERATION -> left + right + BinaryNode.MINUS_OPERATION -> left - right + else -> error("Binary operation $operation is not supported by $this") + } + } + + override fun produce(node: SyntaxTreeNode): Expression { + return when (node) { + is SingularNode -> produceSingular(node.value) + is UnaryNode -> produceUnary(node.operation, produce(node.value)) + is BinaryNode -> produceBinary(node.operation, produce(node.left), produce(node.right)) + } + } +} + +open class FunctionalExpressionField( + val field: Field +) : Field>, FunctionalExpressionSpace(field, field.one) { + 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.times(arg: T) = this * const(arg) + operator fun Expression.div(arg: T) = this / const(arg) + + operator fun T.times(arg: Expression) = arg * this + operator fun T.div(arg: Expression) = arg / this + + override fun produce(node: SyntaxTreeNode): Expression { + //TODO bring together numeric and typed expressions + return super.produce(node) + } + + override fun produceBinary(operation: String, left: Expression, right: Expression): Expression { + return when (operation) { + BinaryNode.TIMES_OPERATION -> left * right + BinaryNode.DIV_OPERATION -> left / right + else -> super.produceBinary(operation, left, right) + } + } +} \ No newline at end of file diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/syntaxTree.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/syntaxTree.kt new file mode 100644 index 000000000..daa17977d --- /dev/null +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/syntaxTree.kt @@ -0,0 +1,31 @@ +package scientifik.kmath.expressions + +sealed class SyntaxTreeNode + +data class SingularNode(val value: String) : SyntaxTreeNode() + +data class UnaryNode(val operation: String, val value: SyntaxTreeNode): SyntaxTreeNode(){ + companion object{ + const val PLUS_OPERATION = "+" + const val MINUS_OPERATION = "-" + const val NOT_OPERATION = "!" + const val ABS_OPERATION = "abs" + const val SIN_OPERATION = "sin" + const val cos_OPERATION = "cos" + //TODO add operations + } +} + +data class BinaryNode(val operation: String, val left: SyntaxTreeNode, val right: SyntaxTreeNode): SyntaxTreeNode(){ + companion object{ + const val PLUS_OPERATION = "+" + const val MINUS_OPERATION = "-" + const val TIMES_OPERATION = "*" + const val DIV_OPERATION = "/" + //TODO add operations + } +} + +//TODO add a function with positional arguments + +//TODO add a function with named arguments \ No newline at end of file diff --git a/kmath-core/src/commonTest/kotlin/scientifik/kmath/expressions/ExpressionFieldTest.kt b/kmath-core/src/commonTest/kotlin/scientifik/kmath/expressions/ExpressionFieldTest.kt index 033b2792f..b933f6a17 100644 --- a/kmath-core/src/commonTest/kotlin/scientifik/kmath/expressions/ExpressionFieldTest.kt +++ b/kmath-core/src/commonTest/kotlin/scientifik/kmath/expressions/ExpressionFieldTest.kt @@ -9,7 +9,7 @@ import kotlin.test.assertEquals class ExpressionFieldTest { @Test fun testExpression() { - val context = ExpressionField(RealField) + val context = FunctionalExpressionField(RealField) val expression = with(context) { val x = variable("x", 2.0) x * x + 2 * x + one @@ -20,7 +20,7 @@ class ExpressionFieldTest { @Test fun testComplex() { - val context = ExpressionField(ComplexField) + val context = FunctionalExpressionField(ComplexField) val expression = with(context) { val x = variable("x", Complex(2.0, 0.0)) x * x + 2 * x + one @@ -31,23 +31,23 @@ class ExpressionFieldTest { @Test fun separateContext() { - fun ExpressionField.expression(): Expression { + fun FunctionalExpressionField.expression(): Expression { val x = variable("x") return x * x + 2 * x + one } - val expression = ExpressionField(RealField).expression() + val expression = FunctionalExpressionField(RealField).expression() assertEquals(expression("x" to 1.0), 4.0) } @Test fun valueExpression() { - val expressionBuilder: ExpressionField.() -> Expression = { + val expressionBuilder: FunctionalExpressionField.() -> Expression = { val x = variable("x") x * x + 2 * x + one } - val expression = ExpressionField(RealField).expressionBuilder() + val expression = FunctionalExpressionField(RealField).expressionBuilder() assertEquals(expression("x" to 1.0), 4.0) } } \ No newline at end of file -- 2.34.1 From 1a869ace0e23659eca398d7a768a54191bc084d7 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Fri, 15 May 2020 16:10:19 +0300 Subject: [PATCH 006/156] Refactored Expression tree API --- .../commons/expressions/DiffExpression.kt | 5 +- .../kmath/expressions/Expression.kt | 92 ++++++++++++++++++- .../{syntaxTree.kt => SyntaxTreeNode.kt} | 14 ++- .../expressions/functionalExpressions.kt | 60 ++---------- 4 files changed, 107 insertions(+), 64 deletions(-) rename kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/{syntaxTree.kt => SyntaxTreeNode.kt} (67%) diff --git a/kmath-commons/src/main/kotlin/scientifik/kmath/commons/expressions/DiffExpression.kt b/kmath-commons/src/main/kotlin/scientifik/kmath/commons/expressions/DiffExpression.kt index d5c038dc4..65e915ef4 100644 --- a/kmath-commons/src/main/kotlin/scientifik/kmath/commons/expressions/DiffExpression.kt +++ b/kmath-commons/src/main/kotlin/scientifik/kmath/commons/expressions/DiffExpression.kt @@ -2,9 +2,8 @@ package scientifik.kmath.commons.expressions import org.apache.commons.math3.analysis.differentiation.DerivativeStructure import scientifik.kmath.expressions.Expression -import scientifik.kmath.expressions.ExpressionContext +import scientifik.kmath.expressions.ExpressionField import scientifik.kmath.operations.ExtendedField -import scientifik.kmath.operations.Field import kotlin.properties.ReadOnlyProperty import kotlin.reflect.KProperty @@ -113,7 +112,7 @@ fun DiffExpression.derivative(name: String) = derivative(name to 1) /** * A context for [DiffExpression] (not to be confused with [DerivativeStructure]) */ -object DiffExpressionContext : ExpressionContext, Field { +object DiffExpressionContext : ExpressionField { override fun variable(name: String, default: Double?) = DiffExpression { variable(name, default?.const()) } 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 bc3d70b4f..94bc81413 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/Expression.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/Expression.kt @@ -1,5 +1,8 @@ package scientifik.kmath.expressions +import scientifik.kmath.operations.Field +import scientifik.kmath.operations.Space + /** * An elementary function that could be invoked on a map of arguments */ @@ -12,16 +15,97 @@ operator fun Expression.invoke(vararg pairs: Pair): T = invoke /** * A context for expression construction */ -interface ExpressionContext { +interface ExpressionContext> { /** * Introduce a variable into expression context */ - fun variable(name: String, default: T? = null): Expression + fun variable(name: String, default: T? = null): E /** * A constant expression which does not depend on arguments */ - fun const(value: T): Expression + fun const(value: T): E + + fun produce(node: SyntaxTreeNode): E +} + +interface ExpressionSpace> : Space, ExpressionContext { + + open fun produceSingular(value: String): E = variable(value) + + open fun produceUnary(operation: String, value: E): E { + return when (operation) { + UnaryNode.PLUS_OPERATION -> value + UnaryNode.MINUS_OPERATION -> -value + else -> error("Unary operation $operation is not supported by $this") + } + } + + open fun produceBinary(operation: String, left: E, right: E): E { + return when (operation) { + BinaryNode.PLUS_OPERATION -> left + right + BinaryNode.MINUS_OPERATION -> left - right + else -> error("Binary operation $operation is not supported by $this") + } + } + + override fun produce(node: SyntaxTreeNode): E { + return when (node) { + is NumberNode -> error("Single number nodes are not supported") + is SingularNode -> produceSingular(node.value) + is UnaryNode -> produceUnary(node.operation, produce(node.value)) + is BinaryNode -> { + when (node.operation) { + BinaryNode.TIMES_OPERATION -> { + if (node.left is NumberNode) { + return produce(node.right) * node.left.value + } else if (node.right is NumberNode) { + return produce(node.left) * node.right.value + } + } + BinaryNode.DIV_OPERATION -> { + if (node.right is NumberNode) { + return produce(node.left) / node.right.value + } + } + } + produceBinary(node.operation, produce(node.left), produce(node.right)) + } + } + } +} + +interface ExpressionField> : Field, ExpressionSpace { + fun const(value: Double): E = one.times(value) + + override fun produce(node: SyntaxTreeNode): E { + if (node is BinaryNode) { + when (node.operation) { + BinaryNode.PLUS_OPERATION -> { + if (node.left is NumberNode) { + return produce(node.right) + one * node.left.value + } else if (node.right is NumberNode) { + return produce(node.left) + one * node.right.value + } + } + BinaryNode.MINUS_OPERATION -> { + if (node.left is NumberNode) { + return one * node.left.value - produce(node.right) + } else if (node.right is NumberNode) { + return produce(node.left) - one * node.right.value + } + } + } + } + return super.produce(node) + } + + override fun produceBinary(operation: String, left: E, right: E): E { + return when (operation) { + BinaryNode.TIMES_OPERATION -> left * right + BinaryNode.DIV_OPERATION -> left / right + else -> super.produceBinary(operation, left, right) + } + } - fun produce(node: SyntaxTreeNode): Expression } \ No newline at end of file diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/syntaxTree.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/SyntaxTreeNode.kt similarity index 67% rename from kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/syntaxTree.kt rename to kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/SyntaxTreeNode.kt index daa17977d..fff3c5bc0 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/syntaxTree.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/SyntaxTreeNode.kt @@ -4,20 +4,24 @@ sealed class SyntaxTreeNode data class SingularNode(val value: String) : SyntaxTreeNode() -data class UnaryNode(val operation: String, val value: SyntaxTreeNode): SyntaxTreeNode(){ - companion object{ +data class NumberNode(val value: Number) : SyntaxTreeNode() + +data class UnaryNode(val operation: String, val value: SyntaxTreeNode) : SyntaxTreeNode() { + companion object { const val PLUS_OPERATION = "+" const val MINUS_OPERATION = "-" const val NOT_OPERATION = "!" const val ABS_OPERATION = "abs" const val SIN_OPERATION = "sin" - const val cos_OPERATION = "cos" + const val COS_OPERATION = "cos" + const val EXP_OPERATION = "exp" + const val LN_OPERATION = "ln" //TODO add operations } } -data class BinaryNode(val operation: String, val left: SyntaxTreeNode, val right: SyntaxTreeNode): SyntaxTreeNode(){ - companion object{ +data class BinaryNode(val operation: String, val left: SyntaxTreeNode, val right: SyntaxTreeNode) : SyntaxTreeNode() { + companion object { const val PLUS_OPERATION = "+" const val MINUS_OPERATION = "-" const val TIMES_OPERATION = "*" diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/functionalExpressions.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/functionalExpressions.kt index 64b3ad72c..ad0acbc4a 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/functionalExpressions.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/functionalExpressions.kt @@ -40,12 +40,10 @@ internal class DivExpession(val context: Field, val expr: Expression, v open class FunctionalExpressionSpace( val space: Space, one: T -) : Space>, ExpressionContext { +) : Space>, ExpressionSpace> { override val zero: Expression = ConstantExpression(space.zero) - val one: Expression = ConstantExpression(one) - override fun const(value: T): Expression = ConstantExpression(value) override fun variable(name: String, default: T?): Expression = VariableExpression(name, default) @@ -60,46 +58,17 @@ open class FunctionalExpressionSpace( operator fun T.plus(arg: Expression) = arg + this operator fun T.minus(arg: Expression) = arg - this - - fun const(value: Double): Expression = one.times(value) - - open fun produceSingular(value: String): Expression { - val numberValue = value.toDoubleOrNull() - return if (numberValue == null) { - variable(value) - } else { - const(numberValue) - } - } - - open fun produceUnary(operation: String, value: Expression): Expression { - return when (operation) { - UnaryNode.PLUS_OPERATION -> value - UnaryNode.MINUS_OPERATION -> -value - else -> error("Unary operation $operation is not supported by $this") - } - } - - open fun produceBinary(operation: String, left: Expression, right: Expression): Expression { - return when (operation) { - BinaryNode.PLUS_OPERATION -> left + right - BinaryNode.MINUS_OPERATION -> left - right - else -> error("Binary operation $operation is not supported by $this") - } - } - - override fun produce(node: SyntaxTreeNode): Expression { - return when (node) { - is SingularNode -> produceSingular(node.value) - is UnaryNode -> produceUnary(node.operation, produce(node.value)) - is BinaryNode -> produceBinary(node.operation, produce(node.left), produce(node.right)) - } - } } open class FunctionalExpressionField( val field: Field -) : Field>, FunctionalExpressionSpace(field, field.one) { +) : ExpressionField>, FunctionalExpressionSpace(field, field.one) { + + override val one: Expression + get() = const(this.field.one) + + override fun const(value: Double): Expression = const(field.run { one*value}) + override fun multiply(a: Expression, b: Expression): Expression = ProductExpression(field, a, b) override fun divide(a: Expression, b: Expression): Expression = DivExpession(field, a, b) @@ -109,17 +78,4 @@ open class FunctionalExpressionField( operator fun T.times(arg: Expression) = arg * this operator fun T.div(arg: Expression) = arg / this - - override fun produce(node: SyntaxTreeNode): Expression { - //TODO bring together numeric and typed expressions - return super.produce(node) - } - - override fun produceBinary(operation: String, left: Expression, right: Expression): Expression { - return when (operation) { - BinaryNode.TIMES_OPERATION -> left * right - BinaryNode.DIV_OPERATION -> left / right - else -> super.produceBinary(operation, left, right) - } - } } \ No newline at end of file -- 2.34.1 From 048a1ceaed926614d5307ae3ca399cba1fcfa951 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Fri, 22 May 2020 21:24:20 +0300 Subject: [PATCH 007/156] Moved probability distributions to commons-rng and to prob module --- build.gradle.kts | 2 +- .../commons/prob/CMRandomGeneratorWrapper.kt | 32 ------ .../kmath/commons/prob/CommonsDistribution.kt | 82 -------------- .../kotlin/scientifik/kmath/chains/Chain.kt | 8 +- .../kotlin/scientifik/memory/Memory.kt | 6 + .../scientifik/memory/DataViewMemory.kt | 22 ++-- .../scientifik/memory/ByteBufferMemory.kt | 24 ++-- kmath-prob/build.gradle.kts | 6 + .../scientifik/kmath/prob/RandomGenerator.kt | 26 ++++- .../kmath/prob/UniformDistribution.kt | 5 +- .../kmath/prob/RandomSourceGenerator.kt | 61 ++++++++++ .../scientifik/kmath/prob/distributions.kt | 107 ++++++++++++++++++ .../kmath/prob/CommonsDistributionsTest.kt | 19 ++++ .../scientifik/kmath/prob/StatisticTest.kt | 2 +- 14 files changed, 261 insertions(+), 141 deletions(-) delete mode 100644 kmath-commons/src/main/kotlin/scientifik/kmath/commons/prob/CMRandomGeneratorWrapper.kt delete mode 100644 kmath-commons/src/main/kotlin/scientifik/kmath/commons/prob/CommonsDistribution.kt create mode 100644 kmath-prob/src/jvmMain/kotlin/scientifik/kmath/prob/RandomSourceGenerator.kt create mode 100644 kmath-prob/src/jvmMain/kotlin/scientifik/kmath/prob/distributions.kt create mode 100644 kmath-prob/src/jvmTest/kotlin/scientifik/kmath/prob/CommonsDistributionsTest.kt diff --git a/build.gradle.kts b/build.gradle.kts index 4a7e99d90..6d102a77a 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -2,7 +2,7 @@ plugins { id("scientifik.publish") apply false } -val kmathVersion by extra("0.1.4-dev-6") +val kmathVersion by extra("0.1.4-dev-7") val bintrayRepo by extra("scientifik") val githubProject by extra("kmath") diff --git a/kmath-commons/src/main/kotlin/scientifik/kmath/commons/prob/CMRandomGeneratorWrapper.kt b/kmath-commons/src/main/kotlin/scientifik/kmath/commons/prob/CMRandomGeneratorWrapper.kt deleted file mode 100644 index 74e035ecb..000000000 --- a/kmath-commons/src/main/kotlin/scientifik/kmath/commons/prob/CMRandomGeneratorWrapper.kt +++ /dev/null @@ -1,32 +0,0 @@ -package scientifik.kmath.commons.prob - -import org.apache.commons.math3.random.JDKRandomGenerator -import scientifik.kmath.prob.RandomGenerator -import org.apache.commons.math3.random.RandomGenerator as CMRandom - -inline class CMRandomGeneratorWrapper(val generator: CMRandom) : RandomGenerator { - override fun nextDouble(): Double = generator.nextDouble() - - override fun nextInt(): Int = generator.nextInt() - - override fun nextLong(): Long = generator.nextLong() - - override fun nextBlock(size: Int): ByteArray = ByteArray(size).apply { generator.nextBytes(this) } - - override fun fork(): RandomGenerator { - TODO("not implemented") //To change body of created functions use File | Settings | File Templates. - } -} - -fun CMRandom.asKmathGenerator(): RandomGenerator = CMRandomGeneratorWrapper(this) - -fun RandomGenerator.asCMGenerator(): CMRandom = - (this as? CMRandomGeneratorWrapper)?.generator ?: TODO("Implement reverse CM wrapper") - -val RandomGenerator.Companion.default: RandomGenerator by lazy { JDKRandomGenerator().asKmathGenerator() } - -fun RandomGenerator.Companion.jdk(seed: Int? = null): RandomGenerator = if (seed == null) { - JDKRandomGenerator() -} else { - JDKRandomGenerator(seed) -}.asKmathGenerator() \ No newline at end of file diff --git a/kmath-commons/src/main/kotlin/scientifik/kmath/commons/prob/CommonsDistribution.kt b/kmath-commons/src/main/kotlin/scientifik/kmath/commons/prob/CommonsDistribution.kt deleted file mode 100644 index 94f8560a4..000000000 --- a/kmath-commons/src/main/kotlin/scientifik/kmath/commons/prob/CommonsDistribution.kt +++ /dev/null @@ -1,82 +0,0 @@ -package scientifik.kmath.commons.prob - -import org.apache.commons.math3.distribution.* -import scientifik.kmath.prob.Distribution -import scientifik.kmath.prob.RandomChain -import scientifik.kmath.prob.RandomGenerator -import scientifik.kmath.prob.UnivariateDistribution -import org.apache.commons.math3.random.RandomGenerator as CMRandom - -class CMRealDistributionWrapper(val builder: (CMRandom?) -> RealDistribution) : UnivariateDistribution { - - private val defaultDistribution by lazy { builder(null) } - - override fun probability(arg: Double): Double = defaultDistribution.probability(arg) - - override fun cumulative(arg: Double): Double = defaultDistribution.cumulativeProbability(arg) - - override fun sample(generator: RandomGenerator): RandomChain { - val distribution = builder(generator.asCMGenerator()) - return RandomChain(generator) { distribution.sample() } - } -} - -class CMIntDistributionWrapper(val builder: (CMRandom?) -> IntegerDistribution) : UnivariateDistribution { - - private val defaultDistribution by lazy { builder(null) } - - override fun probability(arg: Int): Double = defaultDistribution.probability(arg) - - override fun cumulative(arg: Int): Double = defaultDistribution.cumulativeProbability(arg) - - override fun sample(generator: RandomGenerator): RandomChain { - val distribution = builder(generator.asCMGenerator()) - return RandomChain(generator) { distribution.sample() } - } -} - - -fun Distribution.Companion.normal(mean: Double = 0.0, sigma: Double = 1.0): UnivariateDistribution = - CMRealDistributionWrapper { generator -> NormalDistribution(generator, mean, sigma) } - -fun Distribution.Companion.poisson(mean: Double): UnivariateDistribution = CMIntDistributionWrapper { generator -> - PoissonDistribution( - generator, - mean, - PoissonDistribution.DEFAULT_EPSILON, - PoissonDistribution.DEFAULT_MAX_ITERATIONS - ) -} - -fun Distribution.Companion.binomial(trials: Int, p: Double): UnivariateDistribution = - CMIntDistributionWrapper { generator -> - BinomialDistribution(generator, trials, p) - } - -fun Distribution.Companion.student(degreesOfFreedom: Double): UnivariateDistribution = - CMRealDistributionWrapper { generator -> - TDistribution(generator, degreesOfFreedom, TDistribution.DEFAULT_INVERSE_ABSOLUTE_ACCURACY) - } - -fun Distribution.Companion.chi2(degreesOfFreedom: Double): UnivariateDistribution = - CMRealDistributionWrapper { generator -> - ChiSquaredDistribution(generator, degreesOfFreedom) - } - -fun Distribution.Companion.fisher( - numeratorDegreesOfFreedom: Double, - denominatorDegreesOfFreedom: Double -): UnivariateDistribution = - CMRealDistributionWrapper { generator -> - FDistribution(generator, numeratorDegreesOfFreedom, denominatorDegreesOfFreedom) - } - -fun Distribution.Companion.exponential(mean: Double): UnivariateDistribution = - CMRealDistributionWrapper { generator -> - ExponentialDistribution(generator, mean) - } - -fun Distribution.Companion.uniform(a: Double, b: Double): UnivariateDistribution = - CMRealDistributionWrapper { generator -> - UniformRealDistribution(generator, a, b) - } \ No newline at end of file diff --git a/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/Chain.kt b/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/Chain.kt index fd68636f9..85db55480 100644 --- a/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/Chain.kt +++ b/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/Chain.kt @@ -38,9 +38,13 @@ interface Chain: Flow { */ fun fork(): Chain - @InternalCoroutinesApi + @OptIn(InternalCoroutinesApi::class) override suspend fun collect(collector: FlowCollector) { - kotlinx.coroutines.flow.flow { while (true) emit(next()) }.collect(collector) + kotlinx.coroutines.flow.flow { + while (true){ + emit(next()) + } + }.collect(collector) } companion object diff --git a/kmath-memory/src/commonMain/kotlin/scientifik/memory/Memory.kt b/kmath-memory/src/commonMain/kotlin/scientifik/memory/Memory.kt index f9f938dcc..7c6886202 100644 --- a/kmath-memory/src/commonMain/kotlin/scientifik/memory/Memory.kt +++ b/kmath-memory/src/commonMain/kotlin/scientifik/memory/Memory.kt @@ -69,3 +69,9 @@ inline fun Memory.write(block: MemoryWriter.() -> Unit) { * Allocate the most effective platform-specific memory */ expect fun Memory.Companion.allocate(length: Int): Memory + +/** + * Wrap a [Memory] around existing [ByteArray]. This operation is unsafe since the array is not copied + * and could be mutated independently from the resulting [Memory] + */ +expect fun Memory.Companion.wrap(array: ByteArray): Memory diff --git a/kmath-memory/src/jsMain/kotlin/scientifik/memory/DataViewMemory.kt b/kmath-memory/src/jsMain/kotlin/scientifik/memory/DataViewMemory.kt index 843464ab9..59945efb9 100644 --- a/kmath-memory/src/jsMain/kotlin/scientifik/memory/DataViewMemory.kt +++ b/kmath-memory/src/jsMain/kotlin/scientifik/memory/DataViewMemory.kt @@ -2,14 +2,7 @@ package scientifik.memory import org.khronos.webgl.ArrayBuffer import org.khronos.webgl.DataView - -/** - * Allocate the most effective platform-specific memory - */ -actual fun Memory.Companion.allocate(length: Int): Memory { - val buffer = ArrayBuffer(length) - return DataViewMemory(DataView(buffer, 0, length)) -} +import org.khronos.webgl.Int8Array class DataViewMemory(val view: DataView) : Memory { @@ -88,4 +81,17 @@ class DataViewMemory(val view: DataView) : Memory { override fun writer(): MemoryWriter = writer +} + +/** + * Allocate the most effective platform-specific memory + */ +actual fun Memory.Companion.allocate(length: Int): Memory { + val buffer = ArrayBuffer(length) + return DataViewMemory(DataView(buffer, 0, length)) +} + +actual fun Memory.Companion.wrap(array: ByteArray): Memory { + @Suppress("CAST_NEVER_SUCCEEDS") val int8Array = array as Int8Array + return DataViewMemory(DataView(int8Array.buffer, int8Array.byteOffset, int8Array.length)) } \ No newline at end of file diff --git a/kmath-memory/src/jvmMain/kotlin/scientifik/memory/ByteBufferMemory.kt b/kmath-memory/src/jvmMain/kotlin/scientifik/memory/ByteBufferMemory.kt index df2e9847a..9ec2b3a09 100644 --- a/kmath-memory/src/jvmMain/kotlin/scientifik/memory/ByteBufferMemory.kt +++ b/kmath-memory/src/jvmMain/kotlin/scientifik/memory/ByteBufferMemory.kt @@ -7,14 +7,6 @@ import java.nio.file.Path import java.nio.file.StandardOpenOption -/** - * Allocate the most effective platform-specific memory - */ -actual fun Memory.Companion.allocate(length: Int): Memory { - val buffer = ByteBuffer.allocate(length) - return ByteBufferMemory(buffer) -} - private class ByteBufferMemory( val buffer: ByteBuffer, val startOffset: Int = 0, @@ -96,6 +88,22 @@ private class ByteBufferMemory( override fun writer(): MemoryWriter = writer } +/** + * Allocate the most effective platform-specific memory + */ +actual fun Memory.Companion.allocate(length: Int): Memory { + val buffer = ByteBuffer.allocate(length) + return ByteBufferMemory(buffer) +} + +actual fun Memory.Companion.wrap(array: ByteArray): Memory { + val buffer = ByteBuffer.wrap(array) + return ByteBufferMemory(buffer) +} + +fun ByteBuffer.asMemory(startOffset: Int = 0, size: Int = limit()): Memory = + ByteBufferMemory(this, startOffset, size) + /** * Use direct memory-mapped buffer from file to read something and close it afterwards. */ diff --git a/kmath-prob/build.gradle.kts b/kmath-prob/build.gradle.kts index 59b25d340..a69d61b73 100644 --- a/kmath-prob/build.gradle.kts +++ b/kmath-prob/build.gradle.kts @@ -8,4 +8,10 @@ kotlin.sourceSets { api(project(":kmath-coroutines")) } } + jvmMain{ + dependencies{ + api("org.apache.commons:commons-rng-sampling:1.3") + api("org.apache.commons:commons-rng-simple:1.3") + } + } } \ No newline at end of file diff --git a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/RandomGenerator.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/RandomGenerator.kt index 13983c6b2..2a225fe47 100644 --- a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/RandomGenerator.kt +++ b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/RandomGenerator.kt @@ -6,10 +6,16 @@ import kotlin.random.Random * A basic generator */ interface RandomGenerator { + fun nextBoolean(): Boolean + fun nextDouble(): Double fun nextInt(): Int + fun nextInt(until: Int): Int fun nextLong(): Long - fun nextBlock(size: Int): ByteArray + fun nextLong(until: Long): Long + + fun fillBytes(array: ByteArray, fromIndex: Int = 0, toIndex: Int = array.size) + fun nextBytes(size: Int): ByteArray = ByteArray(size).also { fillBytes(it) } /** * Create a new generator which is independent from current generator (operations on new generator do not affect this one @@ -21,21 +27,29 @@ interface RandomGenerator { fun fork(): RandomGenerator companion object { - val default by lazy { DefaultGenerator(Random.nextLong()) } + val default by lazy { DefaultGenerator() } + + fun default(seed: Long) = DefaultGenerator(Random(seed)) } } -class DefaultGenerator(seed: Long?) : RandomGenerator { - private val random = seed?.let { Random(it) } ?: Random +inline class DefaultGenerator(val random: Random = Random) : RandomGenerator { + override fun nextBoolean(): Boolean = random.nextBoolean() override fun nextDouble(): Double = random.nextDouble() override fun nextInt(): Int = random.nextInt() + override fun nextInt(until: Int): Int = random.nextInt(until) override fun nextLong(): Long = random.nextLong() - override fun nextBlock(size: Int): ByteArray = random.nextBytes(size) + override fun nextLong(until: Long): Long = random.nextLong(until) - override fun fork(): RandomGenerator = DefaultGenerator(nextLong()) + override fun fillBytes(array: ByteArray, fromIndex: Int, toIndex: Int) { + random.nextBytes(array, fromIndex, toIndex) + } + override fun nextBytes(size: Int): ByteArray = random.nextBytes(size) + + override fun fork(): RandomGenerator = RandomGenerator.default(random.nextLong()) } \ No newline at end of file diff --git a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/UniformDistribution.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/UniformDistribution.kt index ff6e2a551..9d96bff59 100644 --- a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/UniformDistribution.kt +++ b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/UniformDistribution.kt @@ -28,4 +28,7 @@ class UniformDistribution(val range: ClosedFloatingPointRange) : Univari else -> (arg - range.start) / length } } -} \ No newline at end of file +} + +fun Distribution.Companion.uniform(range: ClosedFloatingPointRange): UniformDistribution = + UniformDistribution(range) \ No newline at end of file diff --git a/kmath-prob/src/jvmMain/kotlin/scientifik/kmath/prob/RandomSourceGenerator.kt b/kmath-prob/src/jvmMain/kotlin/scientifik/kmath/prob/RandomSourceGenerator.kt new file mode 100644 index 000000000..2a97fa4be --- /dev/null +++ b/kmath-prob/src/jvmMain/kotlin/scientifik/kmath/prob/RandomSourceGenerator.kt @@ -0,0 +1,61 @@ +package scientifik.kmath.prob + +import org.apache.commons.rng.UniformRandomProvider +import org.apache.commons.rng.simple.RandomSource + +class RandomSourceGenerator(val source: RandomSource, seed: Long?) : RandomGenerator { + internal val random: UniformRandomProvider = seed?.let { + RandomSource.create(source, seed, null) + } ?: RandomSource.create(source) + + override fun nextBoolean(): Boolean = random.nextBoolean() + + override fun nextDouble(): Double = random.nextDouble() + + override fun nextInt(): Int = random.nextInt() + override fun nextInt(until: Int): Int = random.nextInt(until) + + override fun nextLong(): Long = random.nextLong() + override fun nextLong(until: Long): Long = random.nextLong(until) + + override fun fillBytes(array: ByteArray, fromIndex: Int, toIndex: Int) { + require(toIndex > fromIndex) + random.nextBytes(array, fromIndex, toIndex - fromIndex) + } + + override fun fork(): RandomGenerator = RandomSourceGenerator(source, nextLong()) +} + +inline class RandomGeneratorProvider(val generator: RandomGenerator) : UniformRandomProvider { + override fun nextBoolean(): Boolean = generator.nextBoolean() + + override fun nextFloat(): Float = generator.nextDouble().toFloat() + + override fun nextBytes(bytes: ByteArray) { + generator.fillBytes(bytes) + } + + override fun nextBytes(bytes: ByteArray, start: Int, len: Int) { + generator.fillBytes(bytes, start, start + len) + } + + override fun nextInt(): Int = generator.nextInt() + + override fun nextInt(n: Int): Int = generator.nextInt(n) + + override fun nextDouble(): Double = generator.nextDouble() + + override fun nextLong(): Long = generator.nextLong() + + override fun nextLong(n: Long): Long = generator.nextLong(n) +} + +/** + * Represent this [RandomGenerator] as commons-rng [UniformRandomProvider] preserving and mirroring its current state. + * Getting new value from one of those changes the state of another. + */ +fun RandomGenerator.asUniformRandomProvider(): UniformRandomProvider = if (this is RandomSourceGenerator) { + random +} else { + RandomGeneratorProvider(this) +} diff --git a/kmath-prob/src/jvmMain/kotlin/scientifik/kmath/prob/distributions.kt b/kmath-prob/src/jvmMain/kotlin/scientifik/kmath/prob/distributions.kt new file mode 100644 index 000000000..59588d0ef --- /dev/null +++ b/kmath-prob/src/jvmMain/kotlin/scientifik/kmath/prob/distributions.kt @@ -0,0 +1,107 @@ +package scientifik.kmath.prob + +import org.apache.commons.rng.UniformRandomProvider +import org.apache.commons.rng.sampling.distribution.* +import scientifik.kmath.chains.Chain +import java.util.* +import kotlin.math.PI +import kotlin.math.exp +import kotlin.math.pow +import kotlin.math.sqrt + +abstract class ContinuousSamplerDistribution : Distribution { + + private inner class ContinuousSamplerChain(val generator: RandomGenerator) : Chain { + private val sampler = buildSampler(generator) + + override suspend fun next(): Double = sampler.sample() + + override fun fork(): Chain = ContinuousSamplerChain(generator.fork()) + } + + protected abstract fun buildSampler(generator: RandomGenerator): ContinuousSampler + + override fun sample(generator: RandomGenerator): Chain = ContinuousSamplerChain(generator) +} + +abstract class DiscreteSamplerDistribution : Distribution { + + private inner class ContinuousSamplerChain(val generator: RandomGenerator) : Chain { + private val sampler = buildSampler(generator) + + override suspend fun next(): Int = sampler.sample() + + override fun fork(): Chain = ContinuousSamplerChain(generator.fork()) + } + + protected abstract fun buildSampler(generator: RandomGenerator): DiscreteSampler + + override fun sample(generator: RandomGenerator): Chain = ContinuousSamplerChain(generator) +} + +enum class NormalSamplerMethod { + BoxMuller, + Marsaglia, + Ziggurat +} + +private fun normalSampler(method: NormalSamplerMethod, provider: UniformRandomProvider): NormalizedGaussianSampler = + when (method) { + NormalSamplerMethod.BoxMuller -> BoxMullerNormalizedGaussianSampler(provider) + NormalSamplerMethod.Marsaglia -> MarsagliaNormalizedGaussianSampler(provider) + NormalSamplerMethod.Ziggurat -> ZigguratNormalizedGaussianSampler(provider) + } + +fun Distribution.Companion.normal( + method: NormalSamplerMethod = NormalSamplerMethod.Ziggurat +): Distribution = object : ContinuousSamplerDistribution() { + override fun buildSampler(generator: RandomGenerator): ContinuousSampler { + val provider: UniformRandomProvider = generator.asUniformRandomProvider() + return normalSampler(method, provider) + } + + override fun probability(arg: Double): Double { + return exp(-arg.pow(2) / 2) / sqrt(PI * 2) + } +} + +fun Distribution.Companion.normal( + mean: Double, + sigma: Double, + method: NormalSamplerMethod = NormalSamplerMethod.Ziggurat +): Distribution = object : ContinuousSamplerDistribution() { + private val sigma2 = sigma.pow(2) + private val norm = sigma * sqrt(PI * 2) + + override fun buildSampler(generator: RandomGenerator): ContinuousSampler { + val provider: UniformRandomProvider = generator.asUniformRandomProvider() + val normalizedSampler = normalSampler(method, provider) + return GaussianSampler(normalizedSampler, mean, sigma) + } + + override fun probability(arg: Double): Double { + return exp(-(arg - mean).pow(2) / 2 / sigma2) / norm + } +} + +fun Distribution.Companion.poisson( + lambda: Double +): Distribution = object : DiscreteSamplerDistribution() { + + override fun buildSampler(generator: RandomGenerator): DiscreteSampler { + return PoissonSampler.of(generator.asUniformRandomProvider(), lambda) + } + + private val computedProb: HashMap = hashMapOf(0 to exp(-lambda)) + + override fun probability(arg: Int): Double { + require(arg >= 0) { "The argument must be >= 0" } + return if (arg > 40) { + exp(-(arg - lambda).pow(2) / 2 / lambda) / sqrt(2 * PI * lambda) + } else { + computedProb.getOrPut(arg) { + probability(arg - 1) * lambda / arg + } + } + } +} diff --git a/kmath-prob/src/jvmTest/kotlin/scientifik/kmath/prob/CommonsDistributionsTest.kt b/kmath-prob/src/jvmTest/kotlin/scientifik/kmath/prob/CommonsDistributionsTest.kt new file mode 100644 index 000000000..d6fd629b7 --- /dev/null +++ b/kmath-prob/src/jvmTest/kotlin/scientifik/kmath/prob/CommonsDistributionsTest.kt @@ -0,0 +1,19 @@ +package scientifik.kmath.prob + +import kotlinx.coroutines.flow.take +import kotlinx.coroutines.flow.toList +import kotlinx.coroutines.runBlocking +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.Test + +class CommonsDistributionsTest { + @Test + fun testNormalDistribution(){ + val distribution = Distribution.normal(7.0,2.0) + val generator = RandomGenerator.default(1) + val sample = runBlocking { + distribution.sample(generator).take(1000).toList() + } + Assertions.assertEquals(7.0, sample.average(), 0.1) + } +} \ No newline at end of file diff --git a/kmath-prob/src/jvmTest/kotlin/scientifik/kmath/prob/StatisticTest.kt b/kmath-prob/src/jvmTest/kotlin/scientifik/kmath/prob/StatisticTest.kt index af069810f..2613f71d5 100644 --- a/kmath-prob/src/jvmTest/kotlin/scientifik/kmath/prob/StatisticTest.kt +++ b/kmath-prob/src/jvmTest/kotlin/scientifik/kmath/prob/StatisticTest.kt @@ -9,7 +9,7 @@ import kotlin.test.Test class StatisticTest { //create a random number generator. - val generator = DefaultGenerator(1) + val generator = RandomGenerator.default(1) //Create a stateless chain from generator. val data = generator.chain { nextDouble() } //Convert a chaint to Flow and break it into chunks. -- 2.34.1 From 3f68c0c34e548c8476e6d23da22e026d4bd206b6 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Fri, 22 May 2020 21:28:14 +0300 Subject: [PATCH 008/156] Fix demo for distributions --- examples/build.gradle.kts | 1 + .../kotlin/scientifik/kmath/commons/prob/DistributionDemo.kt | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/examples/build.gradle.kts b/examples/build.gradle.kts index 8ab0dd1eb..2fab47ac0 100644 --- a/examples/build.gradle.kts +++ b/examples/build.gradle.kts @@ -27,6 +27,7 @@ dependencies { implementation(project(":kmath-core")) implementation(project(":kmath-coroutines")) implementation(project(":kmath-commons")) + implementation(project(":kmath-prob")) implementation(project(":kmath-koma")) implementation(project(":kmath-viktor")) implementation(project(":kmath-dimensions")) diff --git a/examples/src/main/kotlin/scientifik/kmath/commons/prob/DistributionDemo.kt b/examples/src/main/kotlin/scientifik/kmath/commons/prob/DistributionDemo.kt index 3c5f53e13..e059415dc 100644 --- a/examples/src/main/kotlin/scientifik/kmath/commons/prob/DistributionDemo.kt +++ b/examples/src/main/kotlin/scientifik/kmath/commons/prob/DistributionDemo.kt @@ -5,10 +5,11 @@ import scientifik.kmath.chains.Chain import scientifik.kmath.chains.collectWithState import scientifik.kmath.prob.Distribution import scientifik.kmath.prob.RandomGenerator +import scientifik.kmath.prob.normal data class AveragingChainState(var num: Int = 0, var value: Double = 0.0) -fun Chain.mean(): Chain = collectWithState(AveragingChainState(),{it.copy()}){ chain-> +fun Chain.mean(): Chain = collectWithState(AveragingChainState(), { it.copy() }) { chain -> val next = chain.next() num++ value += next -- 2.34.1 From 8533a31677b2b78cfa4d22b214cd4490dcfe0204 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sat, 23 May 2020 10:45:02 +0300 Subject: [PATCH 009/156] Optimized blocking chains for primitive numbers generation. --- .../commons/prob/DistributionBenchmark.kt | 71 +++++++++++++++++++ .../kmath/chains/BlockingIntChain.kt | 12 ++++ .../kmath/chains/BlockingRealChain.kt | 12 ++++ .../kotlin/scientifik/kmath/chains/Chain.kt | 1 - .../scientifik/kmath/streaming/BufferFlow.kt | 34 +++++---- .../kmath/prob/RandomSourceGenerator.kt | 8 ++- .../scientifik/kmath/prob/distributions.kt | 26 +++---- .../kmath/prob/CommonsDistributionsTest.kt | 13 +++- 8 files changed, 149 insertions(+), 28 deletions(-) create mode 100644 examples/src/main/kotlin/scientifik/kmath/commons/prob/DistributionBenchmark.kt create mode 100644 kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/BlockingIntChain.kt create mode 100644 kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/BlockingRealChain.kt diff --git a/examples/src/main/kotlin/scientifik/kmath/commons/prob/DistributionBenchmark.kt b/examples/src/main/kotlin/scientifik/kmath/commons/prob/DistributionBenchmark.kt new file mode 100644 index 000000000..b060cddb6 --- /dev/null +++ b/examples/src/main/kotlin/scientifik/kmath/commons/prob/DistributionBenchmark.kt @@ -0,0 +1,71 @@ +package scientifik.kmath.commons.prob + +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.async +import kotlinx.coroutines.runBlocking +import org.apache.commons.rng.sampling.distribution.ZigguratNormalizedGaussianSampler +import org.apache.commons.rng.simple.RandomSource +import scientifik.kmath.chains.BlockingRealChain +import scientifik.kmath.prob.* +import java.time.Duration +import java.time.Instant + + +private suspend fun runChain(): Duration { + val generator = RandomGenerator.fromSource(RandomSource.MT, 123L) + + val normal = Distribution.normal(NormalSamplerMethod.Ziggurat) + val chain = normal.sample(generator) as BlockingRealChain + + val startTime = Instant.now() + var sum = 0.0 + repeat(10000001) { counter -> + + sum += chain.nextDouble() + + if (counter % 100000 == 0) { + val duration = Duration.between(startTime, Instant.now()) + val meanValue = sum / counter + println("Chain sampler completed $counter elements in $duration: $meanValue") + } + } + return Duration.between(startTime, Instant.now()) +} + +private fun runDirect(): Duration { + val provider = RandomSource.create(RandomSource.MT, 123L) + val sampler = ZigguratNormalizedGaussianSampler(provider) + val startTime = Instant.now() + + var sum = 0.0 + repeat(10000001) { counter -> + + sum += sampler.sample() + + if (counter % 100000 == 0) { + val duration = Duration.between(startTime, Instant.now()) + val meanValue = sum / counter + println("Direct sampler completed $counter elements in $duration: $meanValue") + } + } + return Duration.between(startTime, Instant.now()) +} + +/** + * Comparing chain sampling performance with direct sampling performance + */ +fun main() { + runBlocking(Dispatchers.Default) { + val chainJob = async { + runChain() + } + + val directJob = async { + runDirect() + } + + println("Chain: ${chainJob.await()}") + println("Direct: ${directJob.await()}") + } + +} \ No newline at end of file diff --git a/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/BlockingIntChain.kt b/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/BlockingIntChain.kt new file mode 100644 index 000000000..6ec84d5c7 --- /dev/null +++ b/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/BlockingIntChain.kt @@ -0,0 +1,12 @@ +package scientifik.kmath.chains + +/** + * Performance optimized chain for integer values + */ +abstract class BlockingIntChain : Chain { + abstract fun nextInt(): Int + + override suspend fun next(): Int = nextInt() + + fun nextBlock(size: Int): IntArray = IntArray(size) { nextInt() } +} \ No newline at end of file diff --git a/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/BlockingRealChain.kt b/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/BlockingRealChain.kt new file mode 100644 index 000000000..6b69d2734 --- /dev/null +++ b/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/BlockingRealChain.kt @@ -0,0 +1,12 @@ +package scientifik.kmath.chains + +/** + * Performance optimized chain for real values + */ +abstract class BlockingRealChain : Chain { + abstract fun nextDouble(): Double + + override suspend fun next(): Double = nextDouble() + + fun nextBlock(size: Int): DoubleArray = DoubleArray(size) { nextDouble() } +} \ No newline at end of file diff --git a/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/Chain.kt b/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/Chain.kt index 85db55480..5635499e5 100644 --- a/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/Chain.kt +++ b/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/Chain.kt @@ -48,7 +48,6 @@ interface Chain: Flow { } companion object - } diff --git a/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/streaming/BufferFlow.kt b/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/streaming/BufferFlow.kt index cf4d4cc17..bef21a680 100644 --- a/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/streaming/BufferFlow.kt +++ b/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/streaming/BufferFlow.kt @@ -2,9 +2,11 @@ package scientifik.kmath.streaming import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.flow.* +import scientifik.kmath.chains.BlockingRealChain import scientifik.kmath.structures.Buffer import scientifik.kmath.structures.BufferFactory import scientifik.kmath.structures.DoubleBuffer +import scientifik.kmath.structures.asBuffer /** * Create a [Flow] from buffer @@ -45,20 +47,28 @@ fun Flow.chunked(bufferSize: Int, bufferFactory: BufferFactory): Flow< */ fun Flow.chunked(bufferSize: Int): Flow = flow { require(bufferSize > 0) { "Resulting chunk size must be more than zero" } - val array = DoubleArray(bufferSize) - var counter = 0 - this@chunked.collect { element -> - array[counter] = element - counter++ - if (counter == bufferSize) { - val buffer = DoubleBuffer(array) - emit(buffer) - counter = 0 + if (this@chunked is BlockingRealChain) { + //performance optimization for blocking primitive chain + while (true) { + emit(nextBlock(bufferSize).asBuffer()) + } + } else { + val array = DoubleArray(bufferSize) + var counter = 0 + + this@chunked.collect { element -> + array[counter] = element + counter++ + if (counter == bufferSize) { + val buffer = DoubleBuffer(array) + emit(buffer) + counter = 0 + } + } + if (counter > 0) { + emit(DoubleBuffer(counter) { array[it] }) } - } - if (counter > 0) { - emit(DoubleBuffer(counter) { array[it] }) } } diff --git a/kmath-prob/src/jvmMain/kotlin/scientifik/kmath/prob/RandomSourceGenerator.kt b/kmath-prob/src/jvmMain/kotlin/scientifik/kmath/prob/RandomSourceGenerator.kt index 2a97fa4be..f5a73a08b 100644 --- a/kmath-prob/src/jvmMain/kotlin/scientifik/kmath/prob/RandomSourceGenerator.kt +++ b/kmath-prob/src/jvmMain/kotlin/scientifik/kmath/prob/RandomSourceGenerator.kt @@ -5,7 +5,7 @@ import org.apache.commons.rng.simple.RandomSource class RandomSourceGenerator(val source: RandomSource, seed: Long?) : RandomGenerator { internal val random: UniformRandomProvider = seed?.let { - RandomSource.create(source, seed, null) + RandomSource.create(source, seed) } ?: RandomSource.create(source) override fun nextBoolean(): Boolean = random.nextBoolean() @@ -59,3 +59,9 @@ fun RandomGenerator.asUniformRandomProvider(): UniformRandomProvider = if (this } else { RandomGeneratorProvider(this) } + +fun RandomGenerator.Companion.fromSource(source: RandomSource, seed: Long? = null): RandomSourceGenerator = + RandomSourceGenerator(source, seed) + +fun RandomGenerator.Companion.mersenneTwister(seed: Long? = null): RandomSourceGenerator = + fromSource(RandomSource.MT, seed) diff --git a/kmath-prob/src/jvmMain/kotlin/scientifik/kmath/prob/distributions.kt b/kmath-prob/src/jvmMain/kotlin/scientifik/kmath/prob/distributions.kt index 59588d0ef..412454994 100644 --- a/kmath-prob/src/jvmMain/kotlin/scientifik/kmath/prob/distributions.kt +++ b/kmath-prob/src/jvmMain/kotlin/scientifik/kmath/prob/distributions.kt @@ -2,6 +2,8 @@ package scientifik.kmath.prob import org.apache.commons.rng.UniformRandomProvider import org.apache.commons.rng.sampling.distribution.* +import scientifik.kmath.chains.BlockingIntChain +import scientifik.kmath.chains.BlockingRealChain import scientifik.kmath.chains.Chain import java.util.* import kotlin.math.PI @@ -11,32 +13,32 @@ import kotlin.math.sqrt abstract class ContinuousSamplerDistribution : Distribution { - private inner class ContinuousSamplerChain(val generator: RandomGenerator) : Chain { - private val sampler = buildSampler(generator) + private inner class ContinuousSamplerChain(val generator: RandomGenerator) : BlockingRealChain() { + private val sampler = buildCMSampler(generator) - override suspend fun next(): Double = sampler.sample() + override fun nextDouble(): Double = sampler.sample() override fun fork(): Chain = ContinuousSamplerChain(generator.fork()) } - protected abstract fun buildSampler(generator: RandomGenerator): ContinuousSampler + protected abstract fun buildCMSampler(generator: RandomGenerator): ContinuousSampler - override fun sample(generator: RandomGenerator): Chain = ContinuousSamplerChain(generator) + override fun sample(generator: RandomGenerator): BlockingRealChain = ContinuousSamplerChain(generator) } abstract class DiscreteSamplerDistribution : Distribution { - private inner class ContinuousSamplerChain(val generator: RandomGenerator) : Chain { + private inner class ContinuousSamplerChain(val generator: RandomGenerator) : BlockingIntChain() { private val sampler = buildSampler(generator) - override suspend fun next(): Int = sampler.sample() + override fun nextInt(): Int = sampler.sample() override fun fork(): Chain = ContinuousSamplerChain(generator.fork()) } protected abstract fun buildSampler(generator: RandomGenerator): DiscreteSampler - override fun sample(generator: RandomGenerator): Chain = ContinuousSamplerChain(generator) + override fun sample(generator: RandomGenerator): BlockingIntChain = ContinuousSamplerChain(generator) } enum class NormalSamplerMethod { @@ -55,7 +57,7 @@ private fun normalSampler(method: NormalSamplerMethod, provider: UniformRandomPr fun Distribution.Companion.normal( method: NormalSamplerMethod = NormalSamplerMethod.Ziggurat ): Distribution = object : ContinuousSamplerDistribution() { - override fun buildSampler(generator: RandomGenerator): ContinuousSampler { + override fun buildCMSampler(generator: RandomGenerator): ContinuousSampler { val provider: UniformRandomProvider = generator.asUniformRandomProvider() return normalSampler(method, provider) } @@ -69,11 +71,11 @@ fun Distribution.Companion.normal( mean: Double, sigma: Double, method: NormalSamplerMethod = NormalSamplerMethod.Ziggurat -): Distribution = object : ContinuousSamplerDistribution() { +): ContinuousSamplerDistribution = object : ContinuousSamplerDistribution() { private val sigma2 = sigma.pow(2) private val norm = sigma * sqrt(PI * 2) - override fun buildSampler(generator: RandomGenerator): ContinuousSampler { + override fun buildCMSampler(generator: RandomGenerator): ContinuousSampler { val provider: UniformRandomProvider = generator.asUniformRandomProvider() val normalizedSampler = normalSampler(method, provider) return GaussianSampler(normalizedSampler, mean, sigma) @@ -86,7 +88,7 @@ fun Distribution.Companion.normal( fun Distribution.Companion.poisson( lambda: Double -): Distribution = object : DiscreteSamplerDistribution() { +): DiscreteSamplerDistribution = object : DiscreteSamplerDistribution() { override fun buildSampler(generator: RandomGenerator): DiscreteSampler { return PoissonSampler.of(generator.asUniformRandomProvider(), lambda) diff --git a/kmath-prob/src/jvmTest/kotlin/scientifik/kmath/prob/CommonsDistributionsTest.kt b/kmath-prob/src/jvmTest/kotlin/scientifik/kmath/prob/CommonsDistributionsTest.kt index d6fd629b7..7638c695e 100644 --- a/kmath-prob/src/jvmTest/kotlin/scientifik/kmath/prob/CommonsDistributionsTest.kt +++ b/kmath-prob/src/jvmTest/kotlin/scientifik/kmath/prob/CommonsDistributionsTest.kt @@ -8,12 +8,21 @@ import org.junit.jupiter.api.Test class CommonsDistributionsTest { @Test - fun testNormalDistribution(){ - val distribution = Distribution.normal(7.0,2.0) + fun testNormalDistributionSuspend() { + val distribution = Distribution.normal(7.0, 2.0) val generator = RandomGenerator.default(1) val sample = runBlocking { distribution.sample(generator).take(1000).toList() } Assertions.assertEquals(7.0, sample.average(), 0.1) } + + @Test + fun testNormalDistributionBlocking() { + val distribution = Distribution.normal(7.0, 2.0) + val generator = RandomGenerator.default(1) + val sample = distribution.sample(generator).nextBlock(1000) + Assertions.assertEquals(7.0, sample.average(), 0.1) + } + } \ No newline at end of file -- 2.34.1 From 3ea76d56a563175a4a66f3ea68ce7d1e5b6cd157 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Fri, 5 Jun 2020 22:05:16 +0700 Subject: [PATCH 010/156] Implement kmath-asm module stubs --- kmath-asm/build.gradle.kts | 9 + .../kmath/expressions/AsmExpressions.kt | 385 ++++++++++++++++++ .../kmath/expressions/MethodVisitors.kt | 17 + .../scientifik/kmath/expressions/AsmTest.kt | 23 ++ kmath-core/build.gradle.kts | 2 +- .../kmath/expressions/Expression.kt | 12 +- ...xpressions.kt => FunctionalExpressions.kt} | 0 .../{bigNumbers.kt => BigNumbers.kt} | 0 settings.gradle.kts | 3 +- 9 files changed, 443 insertions(+), 8 deletions(-) create mode 100644 kmath-asm/build.gradle.kts create mode 100644 kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressions.kt create mode 100644 kmath-asm/src/main/kotlin/scientifik/kmath/expressions/MethodVisitors.kt create mode 100644 kmath-asm/src/test/kotlin/scientifik/kmath/expressions/AsmTest.kt rename kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/{functionalExpressions.kt => FunctionalExpressions.kt} (100%) rename kmath-core/src/jvmMain/kotlin/scientifik/kmath/operations/{bigNumbers.kt => BigNumbers.kt} (100%) diff --git a/kmath-asm/build.gradle.kts b/kmath-asm/build.gradle.kts new file mode 100644 index 000000000..ab4684cdc --- /dev/null +++ b/kmath-asm/build.gradle.kts @@ -0,0 +1,9 @@ +plugins { + id("scientifik.jvm") +} + +dependencies { + api(project(path = ":kmath-core")) + api("org.ow2.asm:asm:8.0.1") + api("org.ow2.asm:asm-commons:8.0.1") +} diff --git a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressions.kt b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressions.kt new file mode 100644 index 000000000..b9c357671 --- /dev/null +++ b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressions.kt @@ -0,0 +1,385 @@ +package scientifik.kmath.expressions + +import org.objectweb.asm.ClassWriter +import org.objectweb.asm.Label +import org.objectweb.asm.MethodVisitor +import org.objectweb.asm.Opcodes.* +import scientifik.kmath.operations.Algebra +import scientifik.kmath.operations.Field +import scientifik.kmath.operations.Space +import java.io.File + +abstract class AsmCompiled(@JvmField val algebra: Algebra, @JvmField val constants: MutableList) { + abstract fun evaluate(arguments: Map): T +} + +class AsmGenerationContext(classOfT: Class<*>, private val algebra: Algebra, private val className: String) { + private class ClassLoader(parent: java.lang.ClassLoader) : java.lang.ClassLoader(parent) { + internal fun defineClass(name: String?, b: ByteArray): Class<*> = defineClass(name, b, 0, b.size) + } + + private val classLoader: ClassLoader = ClassLoader(javaClass.classLoader) + + @Suppress("PrivatePropertyName") + private val T_ALGEBRA_CLASS: String = algebra.javaClass.name.replace(oldChar = '.', newChar = '/') + + @Suppress("PrivatePropertyName") + private val T_CLASS: String = classOfT.name.replace('.', '/') + private val constants: MutableList = mutableListOf() + private val asmCompiledClassWriter = ClassWriter(0) + private val slashesClassName: String = className.replace(oldChar = '.', newChar = '/') + private val evaluateMethodVisitor: MethodVisitor + private val evaluateThisVar: Int = 0 + private val evaluateArgumentsVar: Int = 1 + private var evaluateL0: Label + private lateinit var evaluateL1: Label + var maxStack: Int = 0 + + init { + asmCompiledClassWriter.visit( + V1_8, + ACC_PUBLIC or ACC_FINAL or ACC_SUPER, + slashesClassName, + "L$ASM_COMPILED_CLASS;", + ASM_COMPILED_CLASS, + arrayOf() + ) + + asmCompiledClassWriter.run { + visitMethod(ACC_PUBLIC, "", "(L$ALGEBRA_CLASS;L$LIST_CLASS;)V", null, null).run { + val thisVar = 0 + val algebraVar = 1 + val constantsVar = 2 + val l0 = Label() + visitLabel(l0) + visitVarInsn(ALOAD, thisVar) + visitVarInsn(ALOAD, algebraVar) + visitVarInsn(ALOAD, constantsVar) + + visitMethodInsn( + INVOKESPECIAL, + ASM_COMPILED_CLASS, + "", + "(L$ALGEBRA_CLASS;L$LIST_CLASS;)V", + false + ) + + val l1 = Label() + visitLabel(l1) + visitInsn(RETURN) + val l2 = Label() + visitLabel(l2) + visitLocalVariable("this", "L$slashesClassName;", null, l0, l2, thisVar) + + visitLocalVariable( + "algebra", + "L$ALGEBRA_CLASS;", + "L$ALGEBRA_CLASS;", + l0, + l2, + algebraVar + ) + + visitLocalVariable("constants", "L$LIST_CLASS;", "L$LIST_CLASS;", l0, l2, constantsVar) + visitMaxs(3, 3) + visitEnd() + } + + evaluateMethodVisitor = visitMethod( + ACC_PUBLIC or ACC_FINAL, + "evaluate", + "(L$MAP_CLASS;)L$T_CLASS;", + "(L$MAP_CLASS;)L$T_CLASS;", + null + ) + + evaluateMethodVisitor.run { + visitCode() + evaluateL0 = Label() + visitLabel(evaluateL0) + } + } + } + + @Suppress("UNCHECKED_CAST") + fun generate(): AsmCompiled { + evaluateMethodVisitor.run { + visitInsn(ARETURN) + evaluateL1 = Label() + visitLabel(evaluateL1) + + visitLocalVariable( + "this", + "L$slashesClassName;", + T_CLASS, + evaluateL0, + evaluateL1, + evaluateThisVar + ) + + visitLocalVariable( + "arguments", + "L$MAP_CLASS;", + "L$MAP_CLASS;", + evaluateL0, + evaluateL1, + evaluateArgumentsVar + ) + + visitMaxs(maxStack + 1, 2) + visitEnd() + } + + asmCompiledClassWriter.visitMethod( + ACC_PUBLIC or ACC_FINAL or ACC_BRIDGE or ACC_SYNTHETIC, + "evaluate", + "(L$MAP_CLASS;)L$OBJECT_CLASS;", + null, + null + ).run { + val thisVar = 0 + visitCode() + val l0 = Label() + visitLabel(l0) + visitVarInsn(ALOAD, 0) + visitVarInsn(ALOAD, 1) + visitMethodInsn(INVOKEVIRTUAL, slashesClassName, "evaluate", "(L$MAP_CLASS;)L$T_CLASS;", false) + visitInsn(ARETURN) + val l1 = Label() + visitLabel(l1) + + visitLocalVariable( + "this", + "L$slashesClassName;", + T_CLASS, + l0, + l1, + thisVar + ) + + visitMaxs(2, 2) + visitEnd() + } + + asmCompiledClassWriter.visitEnd() + + return classLoader + .defineClass(className, asmCompiledClassWriter.toByteArray()) + .constructors + .first() + .newInstance(algebra, constants) as AsmCompiled + } + + fun visitLoadFromConstants(value: T) { + val idx = if (value in constants) constants.indexOf(value) else constants.apply { add(value) }.lastIndex + maxStack++ + + evaluateMethodVisitor.run { + visitLoadThis() + visitFieldInsn(GETFIELD, slashesClassName, "constants", "L$LIST_CLASS;") + visitLdcOrIConstInsn(idx) + visitMethodInsn(INVOKEINTERFACE, LIST_CLASS, "get", "(I)L$OBJECT_CLASS;", true) + visitCastToT() + } + } + + private fun visitLoadThis(): Unit = evaluateMethodVisitor.visitVarInsn(ALOAD, evaluateThisVar) + + fun visitNumberConstant(value: Number): Unit = evaluateMethodVisitor.visitLdcInsn(value) + + fun visitLoadFromVariables(name: String, defaultValue: T? = null) = evaluateMethodVisitor.run { + maxStack++ + visitVarInsn(ALOAD, evaluateArgumentsVar) + + if (defaultValue != null) { + visitLdcInsn(name) + visitLoadFromConstants(defaultValue) + + visitMethodInsn( + INVOKEINTERFACE, + MAP_CLASS, + "getOrDefault", + "(L$OBJECT_CLASS;L$OBJECT_CLASS;)L$OBJECT_CLASS;", + true + ) + + visitCastToT() + return + } + + visitLdcInsn(name) + visitMethodInsn(INVOKEINTERFACE, MAP_CLASS, "get", "(L$OBJECT_CLASS;)L$OBJECT_CLASS;", true) + visitCastToT() + } + + fun visitLoadAlgebra() { + evaluateMethodVisitor.visitVarInsn(ALOAD, evaluateThisVar) + + evaluateMethodVisitor.visitFieldInsn( + GETFIELD, + ASM_COMPILED_CLASS, "algebra", "L$ALGEBRA_CLASS;" + ) + + evaluateMethodVisitor.visitTypeInsn(CHECKCAST, T_ALGEBRA_CLASS) + } + + fun visitInvokeAlgebraOperation(owner: String, method: String, descriptor: String) { + maxStack++ + evaluateMethodVisitor.visitMethodInsn(INVOKEINTERFACE, owner, method, descriptor, true) + visitCastToT() + } + + fun visitCastToT() { + evaluateMethodVisitor.visitTypeInsn(CHECKCAST, T_CLASS) + } + + companion object { + const val ASM_COMPILED_CLASS = "scientifik/kmath/expressions/AsmCompiled" + const val LIST_CLASS = "java/util/List" + const val MAP_CLASS = "java/util/Map" + const val OBJECT_CLASS = "java/lang/Object" + const val ALGEBRA_CLASS = "scientifik/kmath/operations/Algebra" + const val SPACE_CLASS = "scientifik/kmath/operations/Space" + const val SPACE_OPERATIONS_CLASS = "scientifik/kmath/operations/SpaceOperations" + const val FIELD_CLASS = "scientifik/kmath/operations/Field" + const val STRING_CLASS = "java/lang/String" + } +} + +interface AsmExpression { + fun invoke(gen: AsmGenerationContext) +} + +internal class AsmVariableExpression(val name: String, val default: T? = null) : + AsmExpression { + override fun invoke(gen: AsmGenerationContext) { + gen.visitLoadFromVariables(name, default) + } +} + +internal class AsmConstantExpression(val value: T) : + AsmExpression { + override fun invoke(gen: AsmGenerationContext) { + gen.visitLoadFromConstants(value) + } +} + +internal class AsmSumExpression( + val first: AsmExpression, + val second: AsmExpression +) : AsmExpression { + override fun invoke(gen: AsmGenerationContext) { + gen.visitLoadAlgebra() + first.invoke(gen) + second.invoke(gen) + + gen.visitInvokeAlgebraOperation( + owner = AsmGenerationContext.SPACE_OPERATIONS_CLASS, + method = "add", + descriptor = "(L${AsmGenerationContext.OBJECT_CLASS};L${AsmGenerationContext.OBJECT_CLASS};)L${AsmGenerationContext.OBJECT_CLASS};" + ) + } +} + +internal class AsmProductExpression( + val first: AsmExpression, + val second: AsmExpression +) : AsmExpression { + override fun invoke(gen: AsmGenerationContext) { + gen.visitLoadAlgebra() + first.invoke(gen) + second.invoke(gen) + + gen.visitInvokeAlgebraOperation( + owner = AsmGenerationContext.SPACE_CLASS, + method = "times", + descriptor = "(L${AsmGenerationContext.OBJECT_CLASS};L${AsmGenerationContext.OBJECT_CLASS};)L${AsmGenerationContext.OBJECT_CLASS};" + ) + } +} + +internal class AsmConstProductExpression( + val expr: AsmExpression, + val const: Number +) : AsmExpression { + override fun invoke(gen: AsmGenerationContext) { + gen.visitLoadAlgebra() + expr.invoke(gen) + gen.visitNumberConstant(const) + + gen.visitInvokeAlgebraOperation( + owner = AsmGenerationContext.SPACE_CLASS, + method = "multiply", + descriptor = "(L${AsmGenerationContext.OBJECT_CLASS};L${AsmGenerationContext.OBJECT_CLASS};)L${AsmGenerationContext.OBJECT_CLASS};" + ) + } +} + +internal class AsmDivExpression( + val expr: AsmExpression, + val second: AsmExpression +) : AsmExpression { + override fun invoke(gen: AsmGenerationContext) { + gen.visitLoadAlgebra() + expr.invoke(gen) + second.invoke(gen) + + gen.visitInvokeAlgebraOperation( + owner = AsmGenerationContext.FIELD_CLASS, + method = "divide", + descriptor = "(L${AsmGenerationContext.OBJECT_CLASS};L${AsmGenerationContext.OBJECT_CLASS};)L${AsmGenerationContext.OBJECT_CLASS};" + ) + } +} + + +open class AsmFunctionalExpressionSpace( + val space: Space, + one: T +) : Space>, + ExpressionSpace> { + override val zero: AsmExpression = + AsmConstantExpression(space.zero) + + override fun const(value: T): AsmExpression = + AsmConstantExpression(value) + + override fun variable(name: String, default: T?): AsmExpression = + AsmVariableExpression( + name, + default + ) + + override fun add(a: AsmExpression, b: AsmExpression): AsmExpression = + AsmSumExpression(a, b) + + override fun multiply(a: AsmExpression, k: Number): AsmExpression = + AsmConstProductExpression(a, k) + + + operator fun AsmExpression.plus(arg: T) = this + const(arg) + operator fun AsmExpression.minus(arg: T) = this - const(arg) + + operator fun T.plus(arg: AsmExpression) = arg + this + operator fun T.minus(arg: AsmExpression) = arg - this +} + +class AsmFunctionalExpressionField(val field: Field) : ExpressionField>, + AsmFunctionalExpressionSpace(field, field.one) { + override val one: AsmExpression + get() = const(this.field.one) + + override fun const(value: Double): AsmExpression = const(field.run { one * value }) + + override fun multiply(a: AsmExpression, b: AsmExpression): AsmExpression = + AsmProductExpression(a, b) + + override fun divide(a: AsmExpression, b: AsmExpression): AsmExpression = + AsmDivExpression(a, b) + + operator fun AsmExpression.times(arg: T) = this * const(arg) + operator fun AsmExpression.div(arg: T) = this / const(arg) + + operator fun T.times(arg: AsmExpression) = arg * this + operator fun T.div(arg: AsmExpression) = arg / this +} diff --git a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/MethodVisitors.kt b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/MethodVisitors.kt new file mode 100644 index 000000000..fdbc1062e --- /dev/null +++ b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/MethodVisitors.kt @@ -0,0 +1,17 @@ +package scientifik.kmath.expressions + +import org.objectweb.asm.MethodVisitor +import org.objectweb.asm.Opcodes + +fun MethodVisitor.visitLdcOrIConstInsn(value: Int) { + when (value) { + -1 -> visitInsn(Opcodes.ICONST_M1) + 0 -> visitInsn(Opcodes.ICONST_0) + 1 -> visitInsn(Opcodes.ICONST_1) + 2 -> visitInsn(Opcodes.ICONST_2) + 3 -> visitInsn(Opcodes.ICONST_3) + 4 -> visitInsn(Opcodes.ICONST_4) + 5 -> visitInsn(Opcodes.ICONST_5) + else -> visitLdcInsn(value) + } +} diff --git a/kmath-asm/src/test/kotlin/scientifik/kmath/expressions/AsmTest.kt b/kmath-asm/src/test/kotlin/scientifik/kmath/expressions/AsmTest.kt new file mode 100644 index 000000000..e95f8df76 --- /dev/null +++ b/kmath-asm/src/test/kotlin/scientifik/kmath/expressions/AsmTest.kt @@ -0,0 +1,23 @@ +package scientifik.kmath.expressions + +import scientifik.kmath.operations.RealField +import kotlin.test.Test +import kotlin.test.assertEquals + +class AsmTest { + @Test + fun test() { + val expr = AsmSumExpression(AsmConstantExpression(1.0), AsmVariableExpression("x")) + + val gen = AsmGenerationContext( + java.lang.Double::class.java, + RealField, + "MyAsmCompiled" + ) + + expr.invoke(gen) + val compiled = gen.generate() + val value = compiled.evaluate(mapOf("x" to 25.0)) + assertEquals(26.0, value) + } +} diff --git a/kmath-core/build.gradle.kts b/kmath-core/build.gradle.kts index 092f3deb7..18c0cc771 100644 --- a/kmath-core/build.gradle.kts +++ b/kmath-core/build.gradle.kts @@ -8,4 +8,4 @@ kotlin.sourceSets { api(project(":kmath-memory")) } } -} \ 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 94bc81413..7b781e4e1 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/Expression.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/Expression.kt @@ -15,7 +15,7 @@ operator fun Expression.invoke(vararg pairs: Pair): T = invoke /** * A context for expression construction */ -interface ExpressionContext> { +interface ExpressionContext { /** * Introduce a variable into expression context */ @@ -29,11 +29,11 @@ interface ExpressionContext> { fun produce(node: SyntaxTreeNode): E } -interface ExpressionSpace> : Space, ExpressionContext { +interface ExpressionSpace : Space, ExpressionContext { - open fun produceSingular(value: String): E = variable(value) + fun produceSingular(value: String): E = variable(value) - open fun produceUnary(operation: String, value: E): E { + fun produceUnary(operation: String, value: E): E { return when (operation) { UnaryNode.PLUS_OPERATION -> value UnaryNode.MINUS_OPERATION -> -value @@ -41,7 +41,7 @@ interface ExpressionSpace> : Space, ExpressionContext left + right BinaryNode.MINUS_OPERATION -> left - right @@ -75,7 +75,7 @@ interface ExpressionSpace> : Space, ExpressionContext> : Field, ExpressionSpace { +interface ExpressionField : Field, ExpressionSpace { fun const(value: Double): E = one.times(value) override fun produce(node: SyntaxTreeNode): E { diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/functionalExpressions.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressions.kt similarity index 100% rename from kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/functionalExpressions.kt rename to kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressions.kt diff --git a/kmath-core/src/jvmMain/kotlin/scientifik/kmath/operations/bigNumbers.kt b/kmath-core/src/jvmMain/kotlin/scientifik/kmath/operations/BigNumbers.kt similarity index 100% rename from kmath-core/src/jvmMain/kotlin/scientifik/kmath/operations/bigNumbers.kt rename to kmath-core/src/jvmMain/kotlin/scientifik/kmath/operations/BigNumbers.kt diff --git a/settings.gradle.kts b/settings.gradle.kts index 57173250b..fcaa72698 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -44,5 +44,6 @@ include( ":kmath-dimensions", ":kmath-for-real", ":kmath-geometry", - ":examples" + ":examples", + ":kmath-asm" ) -- 2.34.1 From fdd2551c3f8efd6efa76e4d7c09e74ab18a1aa35 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Fri, 5 Jun 2020 22:12:39 +0700 Subject: [PATCH 011/156] Minor refactor --- .../kmath/expressions/AsmExpressions.kt | 28 ++++++++----------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressions.kt b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressions.kt index b9c357671..36607d2b5 100644 --- a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressions.kt +++ b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressions.kt @@ -7,7 +7,6 @@ import org.objectweb.asm.Opcodes.* import scientifik.kmath.operations.Algebra import scientifik.kmath.operations.Field import scientifik.kmath.operations.Space -import java.io.File abstract class AsmCompiled(@JvmField val algebra: Algebra, @JvmField val constants: MutableList) { abstract fun evaluate(arguments: Map): T @@ -223,15 +222,13 @@ class AsmGenerationContext(classOfT: Class<*>, private val algebra: Algebra(classOfT: Class<*>, private val algebra: Algebra { internal class AsmVariableExpression(val name: String, val default: T? = null) : AsmExpression { - override fun invoke(gen: AsmGenerationContext) { - gen.visitLoadFromVariables(name, default) - } + override fun invoke(gen: AsmGenerationContext): Unit = gen.visitLoadFromVariables(name, default) } internal class AsmConstantExpression(val value: T) : AsmExpression { - override fun invoke(gen: AsmGenerationContext) { - gen.visitLoadFromConstants(value) - } + override fun invoke(gen: AsmGenerationContext): Unit = gen.visitLoadFromConstants(value) } internal class AsmSumExpression( @@ -273,7 +267,7 @@ internal class AsmSumExpression( first.invoke(gen) second.invoke(gen) - gen.visitInvokeAlgebraOperation( + gen.visitAlgebraOperation( owner = AsmGenerationContext.SPACE_OPERATIONS_CLASS, method = "add", descriptor = "(L${AsmGenerationContext.OBJECT_CLASS};L${AsmGenerationContext.OBJECT_CLASS};)L${AsmGenerationContext.OBJECT_CLASS};" @@ -290,8 +284,8 @@ internal class AsmProductExpression( first.invoke(gen) second.invoke(gen) - gen.visitInvokeAlgebraOperation( - owner = AsmGenerationContext.SPACE_CLASS, + gen.visitAlgebraOperation( + owner = AsmGenerationContext.SPACE_OPERATIONS_CLASS, method = "times", descriptor = "(L${AsmGenerationContext.OBJECT_CLASS};L${AsmGenerationContext.OBJECT_CLASS};)L${AsmGenerationContext.OBJECT_CLASS};" ) @@ -307,7 +301,7 @@ internal class AsmConstProductExpression( expr.invoke(gen) gen.visitNumberConstant(const) - gen.visitInvokeAlgebraOperation( + gen.visitAlgebraOperation( owner = AsmGenerationContext.SPACE_CLASS, method = "multiply", descriptor = "(L${AsmGenerationContext.OBJECT_CLASS};L${AsmGenerationContext.OBJECT_CLASS};)L${AsmGenerationContext.OBJECT_CLASS};" @@ -324,8 +318,8 @@ internal class AsmDivExpression( expr.invoke(gen) second.invoke(gen) - gen.visitInvokeAlgebraOperation( - owner = AsmGenerationContext.FIELD_CLASS, + gen.visitAlgebraOperation( + owner = AsmGenerationContext.FIELD_OPERATIONS_CLASS, method = "divide", descriptor = "(L${AsmGenerationContext.OBJECT_CLASS};L${AsmGenerationContext.OBJECT_CLASS};)L${AsmGenerationContext.OBJECT_CLASS};" ) -- 2.34.1 From 557142c2bab38bffe63de44e4f6dee79d3324291 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Fri, 5 Jun 2020 23:02:16 +0700 Subject: [PATCH 012/156] Add more tests, fix constant product and product operations impl. --- .../kmath/expressions/AsmExpressions.kt | 25 ++++--- .../kmath/expressions/MethodVisitors.kt | 33 ++++++--- .../scientifik/kmath/expressions/AsmTest.kt | 71 +++++++++++++++---- 3 files changed, 97 insertions(+), 32 deletions(-) diff --git a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressions.kt b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressions.kt index 36607d2b5..9a7ad69f2 100644 --- a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressions.kt +++ b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressions.kt @@ -7,6 +7,7 @@ import org.objectweb.asm.Opcodes.* import scientifik.kmath.operations.Algebra import scientifik.kmath.operations.Field import scientifik.kmath.operations.Space +import java.io.File abstract class AsmCompiled(@JvmField val algebra: Algebra, @JvmField val constants: MutableList) { abstract fun evaluate(arguments: Map): T @@ -184,10 +185,13 @@ class AsmGenerationContext(classOfT: Class<*>, private val algebra: Algebra(classOfT: Class<*>, private val algebra: Algebra(classOfT: Class<*>, private val algebra: Algebra( second.invoke(gen) gen.visitAlgebraOperation( - owner = AsmGenerationContext.SPACE_OPERATIONS_CLASS, - method = "times", + owner = AsmGenerationContext.RING_OPERATIONS_CLASS, + method = "multiply", descriptor = "(L${AsmGenerationContext.OBJECT_CLASS};L${AsmGenerationContext.OBJECT_CLASS};)L${AsmGenerationContext.OBJECT_CLASS};" ) } @@ -298,13 +302,13 @@ internal class AsmConstProductExpression( ) : AsmExpression { override fun invoke(gen: AsmGenerationContext) { gen.visitLoadAlgebra() - expr.invoke(gen) gen.visitNumberConstant(const) + expr.invoke(gen) gen.visitAlgebraOperation( - owner = AsmGenerationContext.SPACE_CLASS, + owner = AsmGenerationContext.SPACE_OPERATIONS_CLASS, method = "multiply", - descriptor = "(L${AsmGenerationContext.OBJECT_CLASS};L${AsmGenerationContext.OBJECT_CLASS};)L${AsmGenerationContext.OBJECT_CLASS};" + descriptor = "(L${AsmGenerationContext.OBJECT_CLASS};L${AsmGenerationContext.NUMBER_CLASS};)L${AsmGenerationContext.OBJECT_CLASS};" ) } } @@ -326,7 +330,6 @@ internal class AsmDivExpression( } } - open class AsmFunctionalExpressionSpace( val space: Space, one: T diff --git a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/MethodVisitors.kt b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/MethodVisitors.kt index fdbc1062e..5b9a8afe8 100644 --- a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/MethodVisitors.kt +++ b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/MethodVisitors.kt @@ -1,17 +1,34 @@ package scientifik.kmath.expressions import org.objectweb.asm.MethodVisitor -import org.objectweb.asm.Opcodes +import org.objectweb.asm.Opcodes.* fun MethodVisitor.visitLdcOrIConstInsn(value: Int) { when (value) { - -1 -> visitInsn(Opcodes.ICONST_M1) - 0 -> visitInsn(Opcodes.ICONST_0) - 1 -> visitInsn(Opcodes.ICONST_1) - 2 -> visitInsn(Opcodes.ICONST_2) - 3 -> visitInsn(Opcodes.ICONST_3) - 4 -> visitInsn(Opcodes.ICONST_4) - 5 -> visitInsn(Opcodes.ICONST_5) + -1 -> visitInsn(ICONST_M1) + 0 -> visitInsn(ICONST_0) + 1 -> visitInsn(ICONST_1) + 2 -> visitInsn(ICONST_2) + 3 -> visitInsn(ICONST_3) + 4 -> visitInsn(ICONST_4) + 5 -> visitInsn(ICONST_5) else -> visitLdcInsn(value) } } + +private val signatureLetters = mapOf( + java.lang.Byte::class.java to "B", + java.lang.Short::class.java to "S", + java.lang.Integer::class.java to "I", + java.lang.Long::class.java to "J", + java.lang.Float::class.java to "F", + java.lang.Double::class.java to "D", + java.lang.Short::class.java to "S" +) + +fun MethodVisitor.visitBoxedNumberConstant(number: Number) { + val clazz = number.javaClass + val c = clazz.name.replace('.', '/') + visitLdcInsn(number) + visitMethodInsn(INVOKESTATIC, c, "valueOf", "(${signatureLetters[clazz]})L${c};", false) +} diff --git a/kmath-asm/src/test/kotlin/scientifik/kmath/expressions/AsmTest.kt b/kmath-asm/src/test/kotlin/scientifik/kmath/expressions/AsmTest.kt index e95f8df76..402307050 100644 --- a/kmath-asm/src/test/kotlin/scientifik/kmath/expressions/AsmTest.kt +++ b/kmath-asm/src/test/kotlin/scientifik/kmath/expressions/AsmTest.kt @@ -1,23 +1,68 @@ package scientifik.kmath.expressions +import scientifik.kmath.operations.Algebra import scientifik.kmath.operations.RealField import kotlin.test.Test import kotlin.test.assertEquals class AsmTest { - @Test - fun test() { - val expr = AsmSumExpression(AsmConstantExpression(1.0), AsmVariableExpression("x")) - - val gen = AsmGenerationContext( - java.lang.Double::class.java, - RealField, - "MyAsmCompiled" + private fun testExpressionValue( + expectedValue: T, + expr: AsmExpression, + arguments: Map, + algebra: Algebra, + clazz: Class<*> + ) { + assertEquals( + expectedValue, AsmGenerationContext( + clazz, + algebra, + "TestAsmCompiled" + ).also(expr::invoke).generate().evaluate(arguments) ) - - expr.invoke(gen) - val compiled = gen.generate() - val value = compiled.evaluate(mapOf("x" to 25.0)) - assertEquals(26.0, value) } + + @Suppress("UNCHECKED_CAST") + private fun testDoubleExpressionValue( + expectedValue: Double, + expr: AsmExpression, + arguments: Map, + algebra: Algebra = RealField, + clazz: Class = java.lang.Double::class.java as Class + ) = testExpressionValue(expectedValue, expr, arguments, algebra, clazz) + + @Test + fun testSum() = testDoubleExpressionValue( + 25.0, + AsmSumExpression(AsmConstantExpression(1.0), AsmVariableExpression("x")), + mapOf("x" to 24.0) + ) + + @Test + fun testConst() = testDoubleExpressionValue( + 123.0, + AsmConstantExpression(123.0), + mapOf() + ) + + @Test + fun testDiv() = testDoubleExpressionValue( + 0.5, + AsmDivExpression(AsmConstantExpression(1.0), AsmConstantExpression(2.0)), + mapOf() + ) + + @Test + fun testProduct() = testDoubleExpressionValue( + 25.0, + AsmProductExpression(AsmVariableExpression("x"), AsmVariableExpression("x")), + mapOf("x" to 5.0) + ) + + @Test + fun testCProduct() = testDoubleExpressionValue( + 25.0, + AsmConstProductExpression(AsmVariableExpression("x"), 5.0), + mapOf("x" to 5.0) + ) } -- 2.34.1 From d6d9351c9c15bf8780a1bc16953d98b37d9650cb Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Fri, 5 Jun 2020 23:04:04 +0700 Subject: [PATCH 013/156] Add more tests --- .../kotlin/scientifik/kmath/expressions/AsmTest.kt | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/kmath-asm/src/test/kotlin/scientifik/kmath/expressions/AsmTest.kt b/kmath-asm/src/test/kotlin/scientifik/kmath/expressions/AsmTest.kt index 402307050..98aa3706b 100644 --- a/kmath-asm/src/test/kotlin/scientifik/kmath/expressions/AsmTest.kt +++ b/kmath-asm/src/test/kotlin/scientifik/kmath/expressions/AsmTest.kt @@ -65,4 +65,18 @@ class AsmTest { AsmConstProductExpression(AsmVariableExpression("x"), 5.0), mapOf("x" to 5.0) ) + + @Test + fun testVar() = testDoubleExpressionValue( + 10000.0, + AsmVariableExpression("x"), + mapOf("x" to 10000.0) + ) + + @Test + fun testVarWithDefault() = testDoubleExpressionValue( + 10000.0, + AsmVariableExpression("x", 10000.0), + mapOf() + ) } -- 2.34.1 From 723bd84417e168f067d59d84ab20330b9efa8423 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Sat, 6 Jun 2020 21:06:15 +0700 Subject: [PATCH 014/156] Remove unused import --- .../main/kotlin/scientifik/kmath/expressions/AsmExpressions.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressions.kt b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressions.kt index 9a7ad69f2..f4951081b 100644 --- a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressions.kt +++ b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressions.kt @@ -7,7 +7,6 @@ import org.objectweb.asm.Opcodes.* import scientifik.kmath.operations.Algebra import scientifik.kmath.operations.Field import scientifik.kmath.operations.Space -import java.io.File abstract class AsmCompiled(@JvmField val algebra: Algebra, @JvmField val constants: MutableList) { abstract fun evaluate(arguments: Map): T -- 2.34.1 From 30b32c15159118f1a93d1f2d1f466711c656c6ee Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Sat, 6 Jun 2020 21:45:15 +0700 Subject: [PATCH 015/156] Move initialization code to separate method to make AsmGenerationContext restartable --- .../kmath/expressions/AsmExpressions.kt | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressions.kt b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressions.kt index f4951081b..876e7abe2 100644 --- a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressions.kt +++ b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressions.kt @@ -24,17 +24,26 @@ class AsmGenerationContext(classOfT: Class<*>, private val algebra: Algebra = mutableListOf() - private val asmCompiledClassWriter = ClassWriter(0) + private val slashesClassName: String = className.replace(oldChar = '.', newChar = '/') - private val evaluateMethodVisitor: MethodVisitor private val evaluateThisVar: Int = 0 private val evaluateArgumentsVar: Int = 1 - private var evaluateL0: Label + private var maxStack: Int = 0 + private lateinit var constants: MutableList + private lateinit var asmCompiledClassWriter: ClassWriter + private lateinit var evaluateMethodVisitor: MethodVisitor + private lateinit var evaluateL0: Label private lateinit var evaluateL1: Label - var maxStack: Int = 0 init { + start() + } + + fun start() { + constants = mutableListOf() + asmCompiledClassWriter = ClassWriter(0) + maxStack = 0 + asmCompiledClassWriter.visit( V1_8, ACC_PUBLIC or ACC_FINAL or ACC_SUPER, -- 2.34.1 From 41094e63da244314b07bb7043d519600b018d9dc Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Sat, 6 Jun 2020 21:48:52 +0700 Subject: [PATCH 016/156] Optimize number constants placing to economize contant pool places --- .../kmath/expressions/MethodVisitors.kt | 42 +++++++++++++------ 1 file changed, 30 insertions(+), 12 deletions(-) diff --git a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/MethodVisitors.kt b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/MethodVisitors.kt index 5b9a8afe8..dc5d83ea5 100644 --- a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/MethodVisitors.kt +++ b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/MethodVisitors.kt @@ -3,17 +3,28 @@ package scientifik.kmath.expressions import org.objectweb.asm.MethodVisitor import org.objectweb.asm.Opcodes.* -fun MethodVisitor.visitLdcOrIConstInsn(value: Int) { - when (value) { - -1 -> visitInsn(ICONST_M1) - 0 -> visitInsn(ICONST_0) - 1 -> visitInsn(ICONST_1) - 2 -> visitInsn(ICONST_2) - 3 -> visitInsn(ICONST_3) - 4 -> visitInsn(ICONST_4) - 5 -> visitInsn(ICONST_5) - else -> visitLdcInsn(value) - } +fun MethodVisitor.visitLdcOrIConstInsn(value: Int) = when (value) { + -1 -> visitInsn(ICONST_M1) + 0 -> visitInsn(ICONST_0) + 1 -> visitInsn(ICONST_1) + 2 -> visitInsn(ICONST_2) + 3 -> visitInsn(ICONST_3) + 4 -> visitInsn(ICONST_4) + 5 -> visitInsn(ICONST_5) + else -> visitLdcInsn(value) +} + +fun MethodVisitor.visitLdcOrDConstInsn(value: Double) = when (value) { + 0.0 -> visitInsn(DCONST_0) + 1.0 -> visitInsn(DCONST_1) + else -> visitLdcInsn(value) +} + +fun MethodVisitor.visitLdcOrFConstInsn(value: Float) = when (value) { + 0f -> visitInsn(FCONST_0) + 1f -> visitInsn(FCONST_1) + 2f -> visitInsn(FCONST_2) + else -> visitLdcInsn(value) } private val signatureLetters = mapOf( @@ -29,6 +40,13 @@ private val signatureLetters = mapOf( fun MethodVisitor.visitBoxedNumberConstant(number: Number) { val clazz = number.javaClass val c = clazz.name.replace('.', '/') - visitLdcInsn(number) + + when (number) { + is Int -> visitLdcOrIConstInsn(number) + is Double -> visitLdcOrDConstInsn(number) + is Float -> visitLdcOrFConstInsn(number) + else -> visitLdcInsn(number) + } + visitMethodInsn(INVOKESTATIC, c, "valueOf", "(${signatureLetters[clazz]})L${c};", false) } -- 2.34.1 From cdb24ea8e2c798850e09c22f12c44cd03e16e2f0 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Sat, 6 Jun 2020 21:51:22 +0700 Subject: [PATCH 017/156] Remove duplicate Short key from signatureLetters map --- .../main/kotlin/scientifik/kmath/expressions/MethodVisitors.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/MethodVisitors.kt b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/MethodVisitors.kt index dc5d83ea5..7ca488846 100644 --- a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/MethodVisitors.kt +++ b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/MethodVisitors.kt @@ -33,8 +33,7 @@ private val signatureLetters = mapOf( java.lang.Integer::class.java to "I", java.lang.Long::class.java to "J", java.lang.Float::class.java to "F", - java.lang.Double::class.java to "D", - java.lang.Short::class.java to "S" + java.lang.Double::class.java to "D" ) fun MethodVisitor.visitBoxedNumberConstant(number: Number) { -- 2.34.1 From fb74e74b01b780cc7d389140ad61cb06d3d383f6 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Sat, 6 Jun 2020 22:05:25 +0700 Subject: [PATCH 018/156] Remove constant number allocation hack and support uncommon Number implementations to be available in constants --- .../kmath/expressions/AsmExpressions.kt | 36 ++++++++++++++++--- .../kmath/expressions/MethodVisitors.kt | 23 ------------ 2 files changed, 32 insertions(+), 27 deletions(-) diff --git a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressions.kt b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressions.kt index 876e7abe2..1b44903cd 100644 --- a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressions.kt +++ b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressions.kt @@ -8,7 +8,7 @@ import scientifik.kmath.operations.Algebra import scientifik.kmath.operations.Field import scientifik.kmath.operations.Space -abstract class AsmCompiled(@JvmField val algebra: Algebra, @JvmField val constants: MutableList) { +abstract class AsmCompiled(@JvmField val algebra: Algebra, @JvmField val constants: MutableList) { abstract fun evaluate(arguments: Map): T } @@ -29,7 +29,7 @@ class AsmGenerationContext(classOfT: Class<*>, private val algebra: Algebra + private lateinit var constants: MutableList private lateinit var asmCompiledClassWriter: ClassWriter private lateinit var evaluateMethodVisitor: MethodVisitor private lateinit var evaluateL0: Label @@ -178,7 +178,9 @@ class AsmGenerationContext(classOfT: Class<*>, private val algebra: Algebra } - fun visitLoadFromConstants(value: T) { + fun visitLoadFromConstants(value: T) = visitLoadAnyFromConstants(value as Any) + + fun visitLoadAnyFromConstants(value: Any) { val idx = if (value in constants) constants.indexOf(value) else constants.apply { add(value) }.lastIndex maxStack++ @@ -195,7 +197,24 @@ class AsmGenerationContext(classOfT: Class<*>, private val algebra: Algebra evaluateMethodVisitor.visitLdcOrIConstInsn(value) + is Double -> evaluateMethodVisitor.visitLdcOrDConstInsn(value) + is Float -> evaluateMethodVisitor.visitLdcOrFConstInsn(value) + else -> evaluateMethodVisitor.visitLdcInsn(value) + } + + evaluateMethodVisitor.visitMethodInsn(INVOKESTATIC, c, "valueOf", "($sigLetter)L${c};", false) + return + } + + visitLoadAnyFromConstants(value) } fun visitLoadFromVariables(name: String, defaultValue: T? = null) = evaluateMethodVisitor.run { @@ -243,6 +262,15 @@ class AsmGenerationContext(classOfT: Class<*>, private val algebra: Algebra visitInsn(FCONST_2) else -> visitLdcInsn(value) } - -private val signatureLetters = mapOf( - java.lang.Byte::class.java to "B", - java.lang.Short::class.java to "S", - java.lang.Integer::class.java to "I", - java.lang.Long::class.java to "J", - java.lang.Float::class.java to "F", - java.lang.Double::class.java to "D" -) - -fun MethodVisitor.visitBoxedNumberConstant(number: Number) { - val clazz = number.javaClass - val c = clazz.name.replace('.', '/') - - when (number) { - is Int -> visitLdcOrIConstInsn(number) - is Double -> visitLdcOrDConstInsn(number) - is Float -> visitLdcOrFConstInsn(number) - else -> visitLdcInsn(number) - } - - visitMethodInsn(INVOKESTATIC, c, "valueOf", "(${signatureLetters[clazz]})L${c};", false) -} -- 2.34.1 From 6686144538268343e47856d39b26fc8a42d17e01 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Sat, 6 Jun 2020 22:09:18 +0700 Subject: [PATCH 019/156] Add type casts for constants --- .../kotlin/scientifik/kmath/expressions/AsmExpressions.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressions.kt b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressions.kt index 1b44903cd..8060674e5 100644 --- a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressions.kt +++ b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressions.kt @@ -178,9 +178,9 @@ class AsmGenerationContext(classOfT: Class<*>, private val algebra: Algebra } - fun visitLoadFromConstants(value: T) = visitLoadAnyFromConstants(value as Any) + fun visitLoadFromConstants(value: T) = visitLoadAnyFromConstants(value as Any, T_CLASS) - fun visitLoadAnyFromConstants(value: Any) { + fun visitLoadAnyFromConstants(value: Any, type: String) { val idx = if (value in constants) constants.indexOf(value) else constants.apply { add(value) }.lastIndex maxStack++ @@ -189,7 +189,7 @@ class AsmGenerationContext(classOfT: Class<*>, private val algebra: Algebra(classOfT: Class<*>, private val algebra: Algebra Date: Sun, 7 Jun 2020 15:57:23 +0700 Subject: [PATCH 020/156] Add more test for ASM const product --- .../scientifik/kmath/expressions/AsmTest.kt | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/kmath-asm/src/test/kotlin/scientifik/kmath/expressions/AsmTest.kt b/kmath-asm/src/test/kotlin/scientifik/kmath/expressions/AsmTest.kt index 98aa3706b..9ab6f26c9 100644 --- a/kmath-asm/src/test/kotlin/scientifik/kmath/expressions/AsmTest.kt +++ b/kmath-asm/src/test/kotlin/scientifik/kmath/expressions/AsmTest.kt @@ -66,6 +66,30 @@ class AsmTest { mapOf("x" to 5.0) ) + @Test + fun testCProductWithOtherTypeNumber() = testDoubleExpressionValue( + 25.0, + AsmConstProductExpression(AsmVariableExpression("x"), 5f), + mapOf("x" to 5.0) + ) + + object CustomZero : Number() { + override fun toByte(): Byte = 0 + override fun toChar(): Char = 0.toChar() + override fun toDouble(): Double = 0.0 + override fun toFloat(): Float = 0f + override fun toInt(): Int = 0 + override fun toLong(): Long = 0L + override fun toShort(): Short = 0 + } + + @Test + fun testCProductWithCustomTypeNumber() = testDoubleExpressionValue( + 0.0, + AsmConstProductExpression(AsmVariableExpression("x"), CustomZero), + mapOf("x" to 5.0) + ) + @Test fun testVar() = testDoubleExpressionValue( 10000.0, -- 2.34.1 From 6ac0297530010799e2ede800f93a7a50e8c891b0 Mon Sep 17 00:00:00 2001 From: Commander Tvis Date: Sun, 7 Jun 2020 19:04:39 +0700 Subject: [PATCH 021/156] Move asm dependency to implementation configuration; rename many ASM API classes, make AsmCompiledExpression implement functional Expression, fix typos, encapsulate AsmGenerationContext --- kmath-asm/build.gradle.kts | 4 +- .../kmath/expressions/AsmExpressions.kt | 199 +++++++++--------- .../scientifik/kmath/expressions/AsmTest.kt | 25 +-- .../kmath/expressions/Expression.kt | 2 +- .../expressions/FunctionalExpressions.kt | 46 ++-- 5 files changed, 129 insertions(+), 147 deletions(-) diff --git a/kmath-asm/build.gradle.kts b/kmath-asm/build.gradle.kts index ab4684cdc..4104349f8 100644 --- a/kmath-asm/build.gradle.kts +++ b/kmath-asm/build.gradle.kts @@ -4,6 +4,6 @@ plugins { dependencies { api(project(path = ":kmath-core")) - api("org.ow2.asm:asm:8.0.1") - api("org.ow2.asm:asm-commons:8.0.1") + implementation("org.ow2.asm:asm:8.0.1") + implementation("org.ow2.asm:asm-commons:8.0.1") } diff --git a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressions.kt b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressions.kt index 8060674e5..f196efffe 100644 --- a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressions.kt +++ b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressions.kt @@ -8,11 +8,30 @@ import scientifik.kmath.operations.Algebra import scientifik.kmath.operations.Field import scientifik.kmath.operations.Space -abstract class AsmCompiled(@JvmField val algebra: Algebra, @JvmField val constants: MutableList) { - abstract fun evaluate(arguments: Map): T +abstract class AsmCompiledExpression internal constructor( + @JvmField val algebra: Algebra, + @JvmField val constants: MutableList +) : Expression { + abstract override fun invoke(arguments: Map): T } -class AsmGenerationContext(classOfT: Class<*>, private val algebra: Algebra, private val className: String) { +internal fun buildName(expression: AsmExpression<*>, collision: Int = 0): String { + val name = "scientifik.kmath.expressions.generated.AsmCompiledExpression_${expression.hashCode()}_$collision" + + try { + Class.forName(name) + } catch (ignored: ClassNotFoundException) { + return name + } + + return buildName(expression, collision + 1) +} + +class AsmGenerationContext( + classOfT: Class<*>, + private val algebra: Algebra, + private val className: String +) { private class ClassLoader(parent: java.lang.ClassLoader) : java.lang.ClassLoader(parent) { internal fun defineClass(name: String?, b: ByteArray): Class<*> = defineClass(name, b, 0, b.size) } @@ -26,30 +45,23 @@ class AsmGenerationContext(classOfT: Class<*>, private val algebra: Algebra - private lateinit var asmCompiledClassWriter: ClassWriter - private lateinit var evaluateMethodVisitor: MethodVisitor - private lateinit var evaluateL0: Label - private lateinit var evaluateL1: Label + private val constants: MutableList = mutableListOf() + private val asmCompiledClassWriter: ClassWriter = ClassWriter(0) + private val invokeMethodVisitor: MethodVisitor + private val invokeL0: Label + private lateinit var invokeL1: Label + private var generatedInstance: AsmCompiledExpression? = null init { - start() - } - - fun start() { - constants = mutableListOf() - asmCompiledClassWriter = ClassWriter(0) - maxStack = 0 - asmCompiledClassWriter.visit( V1_8, ACC_PUBLIC or ACC_FINAL or ACC_SUPER, slashesClassName, - "L$ASM_COMPILED_CLASS;", - ASM_COMPILED_CLASS, + "L$ASM_COMPILED_EXPRESSION_CLASS;", + ASM_COMPILED_EXPRESSION_CLASS, arrayOf() ) @@ -66,7 +78,7 @@ class AsmGenerationContext(classOfT: Class<*>, private val algebra: Algebra", "(L$ALGEBRA_CLASS;L$LIST_CLASS;)V", false @@ -93,45 +105,48 @@ class AsmGenerationContext(classOfT: Class<*>, private val algebra: Algebra;)L$T_CLASS;", null ) - evaluateMethodVisitor.run { + invokeMethodVisitor.run { visitCode() - evaluateL0 = Label() - visitLabel(evaluateL0) + invokeL0 = Label() + visitLabel(invokeL0) } } } + @PublishedApi @Suppress("UNCHECKED_CAST") - fun generate(): AsmCompiled { - evaluateMethodVisitor.run { + internal fun generate(): AsmCompiledExpression { + generatedInstance?.let { return it } + + invokeMethodVisitor.run { visitInsn(ARETURN) - evaluateL1 = Label() - visitLabel(evaluateL1) + invokeL1 = Label() + visitLabel(invokeL1) visitLocalVariable( "this", "L$slashesClassName;", T_CLASS, - evaluateL0, - evaluateL1, - evaluateThisVar + invokeL0, + invokeL1, + invokeThisVar ) visitLocalVariable( "arguments", "L$MAP_CLASS;", "L$MAP_CLASS;", - evaluateL0, - evaluateL1, - evaluateArgumentsVar + invokeL0, + invokeL1, + invokeArgumentsVar ) visitMaxs(maxStack + 1, 2) @@ -140,7 +155,7 @@ class AsmGenerationContext(classOfT: Class<*>, private val algebra: Algebra(classOfT: Class<*>, private val algebra: Algebra(classOfT: Class<*>, private val algebra: Algebra + .newInstance(algebra, constants) as AsmCompiledExpression + + generatedInstance = new + return new } - fun visitLoadFromConstants(value: T) = visitLoadAnyFromConstants(value as Any, T_CLASS) + internal fun visitLoadFromConstants(value: T) = visitLoadAnyFromConstants(value as Any, T_CLASS) - fun visitLoadAnyFromConstants(value: Any, type: String) { + private fun visitLoadAnyFromConstants(value: Any, type: String) { val idx = if (value in constants) constants.indexOf(value) else constants.apply { add(value) }.lastIndex maxStack++ - evaluateMethodVisitor.run { + invokeMethodVisitor.run { visitLoadThis() visitFieldInsn(GETFIELD, slashesClassName, "constants", "L$LIST_CLASS;") visitLdcOrIConstInsn(idx) visitMethodInsn(INVOKEINTERFACE, LIST_CLASS, "get", "(I)L$OBJECT_CLASS;", true) - evaluateMethodVisitor.visitTypeInsn(CHECKCAST, type) + invokeMethodVisitor.visitTypeInsn(CHECKCAST, type) } } - private fun visitLoadThis(): Unit = evaluateMethodVisitor.visitVarInsn(ALOAD, evaluateThisVar) + private fun visitLoadThis(): Unit = invokeMethodVisitor.visitVarInsn(ALOAD, invokeThisVar) - fun visitNumberConstant(value: Number) { + internal fun visitNumberConstant(value: Number) { maxStack++ val clazz = value.javaClass val c = clazz.name.replace('.', '/') @@ -204,22 +222,22 @@ class AsmGenerationContext(classOfT: Class<*>, private val algebra: Algebra evaluateMethodVisitor.visitLdcOrIConstInsn(value) - is Double -> evaluateMethodVisitor.visitLdcOrDConstInsn(value) - is Float -> evaluateMethodVisitor.visitLdcOrFConstInsn(value) - else -> evaluateMethodVisitor.visitLdcInsn(value) + is Int -> invokeMethodVisitor.visitLdcOrIConstInsn(value) + is Double -> invokeMethodVisitor.visitLdcOrDConstInsn(value) + is Float -> invokeMethodVisitor.visitLdcOrFConstInsn(value) + else -> invokeMethodVisitor.visitLdcInsn(value) } - evaluateMethodVisitor.visitMethodInsn(INVOKESTATIC, c, "valueOf", "($sigLetter)L${c};", false) + invokeMethodVisitor.visitMethodInsn(INVOKESTATIC, c, "valueOf", "($sigLetter)L${c};", false) return } visitLoadAnyFromConstants(value, c) } - fun visitLoadFromVariables(name: String, defaultValue: T? = null) = evaluateMethodVisitor.run { + internal fun visitLoadFromVariables(name: String, defaultValue: T? = null) = invokeMethodVisitor.run { maxStack += 2 - visitVarInsn(ALOAD, evaluateArgumentsVar) + visitVarInsn(ALOAD, invokeArgumentsVar) if (defaultValue != null) { visitLdcInsn(name) @@ -242,26 +260,26 @@ class AsmGenerationContext(classOfT: Class<*>, private val algebra: Algebra(classOfT: Class<*>, private val algebra: Algebra( } } -open class AsmFunctionalExpressionSpace( - val space: Space, - one: T -) : Space>, +open class AsmExpressionSpace(space: Space) : Space>, ExpressionSpace> { - override val zero: AsmExpression = - AsmConstantExpression(space.zero) - - override fun const(value: T): AsmExpression = - AsmConstantExpression(value) - - override fun variable(name: String, default: T?): AsmExpression = - AsmVariableExpression( - name, - default - ) - - override fun add(a: AsmExpression, b: AsmExpression): AsmExpression = - AsmSumExpression(a, b) - - override fun multiply(a: AsmExpression, k: Number): AsmExpression = - AsmConstProductExpression(a, k) - - + override val zero: AsmExpression = AsmConstantExpression(space.zero) + override fun const(value: T): AsmExpression = AsmConstantExpression(value) + override fun variable(name: String, default: T?): AsmExpression = AsmVariableExpression(name, default) + override fun add(a: AsmExpression, b: AsmExpression): AsmExpression = AsmSumExpression(a, b) + override fun multiply(a: AsmExpression, k: Number): AsmExpression = AsmConstProductExpression(a, k) operator fun AsmExpression.plus(arg: T) = this + const(arg) operator fun AsmExpression.minus(arg: T) = this - const(arg) - operator fun T.plus(arg: AsmExpression) = arg + this operator fun T.minus(arg: AsmExpression) = arg - this } -class AsmFunctionalExpressionField(val field: Field) : ExpressionField>, - AsmFunctionalExpressionSpace(field, field.one) { +class AsmExpressionField(private val field: Field) : ExpressionField>, + AsmExpressionSpace(field) { override val one: AsmExpression get() = const(this.field.one) - override fun const(value: Double): AsmExpression = const(field.run { one * value }) + override fun number(value: Number): AsmExpression = const(field.run { one * value }) override fun multiply(a: AsmExpression, b: AsmExpression): AsmExpression = AsmProductExpression(a, b) @@ -412,7 +412,6 @@ class AsmFunctionalExpressionField(val field: Field) : ExpressionField.times(arg: T) = this * const(arg) operator fun AsmExpression.div(arg: T) = this / const(arg) - operator fun T.times(arg: AsmExpression) = arg * this operator fun T.div(arg: AsmExpression) = arg / this } diff --git a/kmath-asm/src/test/kotlin/scientifik/kmath/expressions/AsmTest.kt b/kmath-asm/src/test/kotlin/scientifik/kmath/expressions/AsmTest.kt index 9ab6f26c9..875080616 100644 --- a/kmath-asm/src/test/kotlin/scientifik/kmath/expressions/AsmTest.kt +++ b/kmath-asm/src/test/kotlin/scientifik/kmath/expressions/AsmTest.kt @@ -12,15 +12,12 @@ class AsmTest { arguments: Map, algebra: Algebra, clazz: Class<*> - ) { - assertEquals( - expectedValue, AsmGenerationContext( - clazz, - algebra, - "TestAsmCompiled" - ).also(expr::invoke).generate().evaluate(arguments) - ) - } + ): Unit = assertEquals( + expectedValue, AsmGenerationContext(clazz, algebra, "TestAsmCompiled") + .also(expr::invoke) + .generate() + .invoke(arguments) + ) @Suppress("UNCHECKED_CAST") private fun testDoubleExpressionValue( @@ -29,7 +26,7 @@ class AsmTest { arguments: Map, algebra: Algebra = RealField, clazz: Class = java.lang.Double::class.java as Class - ) = testExpressionValue(expectedValue, expr, arguments, algebra, clazz) + ): Unit = testExpressionValue(expectedValue, expr, arguments, algebra, clazz) @Test fun testSum() = testDoubleExpressionValue( @@ -39,28 +36,28 @@ class AsmTest { ) @Test - fun testConst() = testDoubleExpressionValue( + fun testConst(): Unit = testDoubleExpressionValue( 123.0, AsmConstantExpression(123.0), mapOf() ) @Test - fun testDiv() = testDoubleExpressionValue( + fun testDiv(): Unit = testDoubleExpressionValue( 0.5, AsmDivExpression(AsmConstantExpression(1.0), AsmConstantExpression(2.0)), mapOf() ) @Test - fun testProduct() = testDoubleExpressionValue( + fun testProduct(): Unit = testDoubleExpressionValue( 25.0, AsmProductExpression(AsmVariableExpression("x"), AsmVariableExpression("x")), mapOf("x" to 5.0) ) @Test - fun testCProduct() = testDoubleExpressionValue( + fun testCProduct(): Unit = testDoubleExpressionValue( 25.0, AsmConstProductExpression(AsmVariableExpression("x"), 5.0), mapOf("x" to 5.0) 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 7b781e4e1..dae069879 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/Expression.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/Expression.kt @@ -76,7 +76,7 @@ interface ExpressionSpace : Space, ExpressionContext { } interface ExpressionField : Field, ExpressionSpace { - fun const(value: Double): E = one.times(value) + fun number(value: Number): E = one * value override fun produce(node: SyntaxTreeNode): E { if (node is BinaryNode) { diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressions.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressions.kt index ad0acbc4a..dbc2eac1e 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressions.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressions.kt @@ -27,55 +27,41 @@ internal class ProductExpression(val context: Ring, val first: Expression< context.multiply(first.invoke(arguments), second.invoke(arguments)) } -internal class ConstProductExpession(val context: Space, val expr: Expression, val const: Number) : +internal class ConstProductExpression(val context: Space, val expr: Expression, val const: Number) : 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) : +internal class DivExpression(val context: Field, val expr: Expression, val second: Expression) : Expression { override fun invoke(arguments: Map): T = context.divide(expr.invoke(arguments), second.invoke(arguments)) } -open class FunctionalExpressionSpace( - val space: Space, - one: T -) : Space>, ExpressionSpace> { - +open class FunctionalExpressionSpace(val space: Space) : Space>, ExpressionSpace> { override val zero: Expression = ConstantExpression(space.zero) override fun const(value: T): Expression = ConstantExpression(value) - override fun variable(name: String, default: T?): Expression = VariableExpression(name, default) - override fun add(a: Expression, b: Expression): Expression = SumExpression(space, a, b) - - override fun multiply(a: Expression, k: Number): Expression = ConstProductExpession(space, a, k) - - - operator fun Expression.plus(arg: T) = this + const(arg) - operator fun Expression.minus(arg: T) = this - const(arg) - - operator fun T.plus(arg: Expression) = arg + this - operator fun T.minus(arg: Expression) = arg - this + override fun multiply(a: Expression, k: Number): Expression = ConstProductExpression(space, a, k) + operator fun Expression.plus(arg: T): Expression = this + const(arg) + operator fun Expression.minus(arg: T): Expression = this - const(arg) + operator fun T.plus(arg: Expression): Expression = arg + this + operator fun T.minus(arg: Expression): Expression = arg - this } open class FunctionalExpressionField( val field: Field -) : ExpressionField>, FunctionalExpressionSpace(field, field.one) { +) : ExpressionField>, FunctionalExpressionSpace(field) { override val one: Expression get() = const(this.field.one) - override fun const(value: Double): Expression = const(field.run { one*value}) - + override fun number(value: Number): Expression = const(field.run { one * value }) 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.times(arg: T) = this * const(arg) - operator fun Expression.div(arg: T) = this / const(arg) - - operator fun T.times(arg: Expression) = arg * this - operator fun T.div(arg: Expression) = arg / this -} \ No newline at end of file + override fun divide(a: Expression, b: Expression): Expression = DivExpression(field, a, b) + operator fun Expression.times(arg: T): Expression = this * const(arg) + operator fun Expression.div(arg: T): Expression = this / const(arg) + operator fun T.times(arg: Expression): Expression = arg * this + operator fun T.div(arg: Expression): Expression = arg / this +} -- 2.34.1 From 013030951ee8cd0c8319c5504d07704a91a90901 Mon Sep 17 00:00:00 2001 From: Commander Tvis Date: Sun, 7 Jun 2020 19:23:39 +0700 Subject: [PATCH 022/156] Make AsmCompiledExpression fields private, add builder functions not to expose AsmGenerationContext to public API, refactor code --- .../kmath/expressions/AsmExpressionSpaces.kt | 31 ++ .../kmath/expressions/AsmExpressions.kt | 329 +----------------- .../kmath/expressions/AsmGenerationContext.kt | 283 +++++++++++++++ .../scientifik/kmath/expressions/Builders.kt | 38 ++ .../scientifik/kmath/expressions/AsmTest.kt | 8 +- 5 files changed, 358 insertions(+), 331 deletions(-) create mode 100644 kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressionSpaces.kt create mode 100644 kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmGenerationContext.kt create mode 100644 kmath-asm/src/main/kotlin/scientifik/kmath/expressions/Builders.kt diff --git a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressionSpaces.kt b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressionSpaces.kt new file mode 100644 index 000000000..5c93fd729 --- /dev/null +++ b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressionSpaces.kt @@ -0,0 +1,31 @@ +package scientifik.kmath.expressions + +import scientifik.kmath.operations.Field +import scientifik.kmath.operations.Space + +open class AsmExpressionSpace(space: Space) : Space>, + ExpressionSpace> { + override val zero: AsmExpression = AsmConstantExpression(space.zero) + override fun const(value: T): AsmExpression = AsmConstantExpression(value) + override fun variable(name: String, default: T?): AsmExpression = AsmVariableExpression(name, default) + override fun add(a: AsmExpression, b: AsmExpression): AsmExpression = AsmSumExpression(a, b) + override fun multiply(a: AsmExpression, k: Number): AsmExpression = AsmConstProductExpression(a, k) + operator fun AsmExpression.plus(arg: T): AsmExpression = this + const(arg) + operator fun AsmExpression.minus(arg: T): AsmExpression = this - const(arg) + operator fun T.plus(arg: AsmExpression): AsmExpression = arg + this + operator fun T.minus(arg: AsmExpression): AsmExpression = arg - this +} + +class AsmExpressionField(private val field: Field) : ExpressionField>, + AsmExpressionSpace(field) { + override val one: AsmExpression + get() = const(this.field.one) + + override fun number(value: Number): AsmExpression = const(field.run { one * value }) + override fun multiply(a: AsmExpression, b: AsmExpression): AsmExpression = AsmProductExpression(a, b) + override fun divide(a: AsmExpression, b: AsmExpression): AsmExpression = AsmDivExpression(a, b) + operator fun AsmExpression.times(arg: T): AsmExpression = this * const(arg) + operator fun AsmExpression.div(arg: T): AsmExpression = this / const(arg) + operator fun T.times(arg: AsmExpression): AsmExpression = arg * this + operator fun T.div(arg: AsmExpression): AsmExpression = arg / this +} diff --git a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressions.kt b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressions.kt index f196efffe..8708a33e1 100644 --- a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressions.kt +++ b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressions.kt @@ -1,307 +1,14 @@ package scientifik.kmath.expressions -import org.objectweb.asm.ClassWriter -import org.objectweb.asm.Label -import org.objectweb.asm.MethodVisitor -import org.objectweb.asm.Opcodes.* import scientifik.kmath.operations.Algebra -import scientifik.kmath.operations.Field -import scientifik.kmath.operations.Space abstract class AsmCompiledExpression internal constructor( - @JvmField val algebra: Algebra, - @JvmField val constants: MutableList + @JvmField private val algebra: Algebra, + @JvmField private val constants: MutableList ) : Expression { abstract override fun invoke(arguments: Map): T } -internal fun buildName(expression: AsmExpression<*>, collision: Int = 0): String { - val name = "scientifik.kmath.expressions.generated.AsmCompiledExpression_${expression.hashCode()}_$collision" - - try { - Class.forName(name) - } catch (ignored: ClassNotFoundException) { - return name - } - - return buildName(expression, collision + 1) -} - -class AsmGenerationContext( - classOfT: Class<*>, - private val algebra: Algebra, - private val className: String -) { - private class ClassLoader(parent: java.lang.ClassLoader) : java.lang.ClassLoader(parent) { - internal fun defineClass(name: String?, b: ByteArray): Class<*> = defineClass(name, b, 0, b.size) - } - - private val classLoader: ClassLoader = ClassLoader(javaClass.classLoader) - - @Suppress("PrivatePropertyName") - private val T_ALGEBRA_CLASS: String = algebra.javaClass.name.replace(oldChar = '.', newChar = '/') - - @Suppress("PrivatePropertyName") - private val T_CLASS: String = classOfT.name.replace('.', '/') - - private val slashesClassName: String = className.replace(oldChar = '.', newChar = '/') - private val invokeThisVar: Int = 0 - private val invokeArgumentsVar: Int = 1 - private var maxStack: Int = 0 - private val constants: MutableList = mutableListOf() - private val asmCompiledClassWriter: ClassWriter = ClassWriter(0) - private val invokeMethodVisitor: MethodVisitor - private val invokeL0: Label - private lateinit var invokeL1: Label - private var generatedInstance: AsmCompiledExpression? = null - - init { - asmCompiledClassWriter.visit( - V1_8, - ACC_PUBLIC or ACC_FINAL or ACC_SUPER, - slashesClassName, - "L$ASM_COMPILED_EXPRESSION_CLASS;", - ASM_COMPILED_EXPRESSION_CLASS, - arrayOf() - ) - - asmCompiledClassWriter.run { - visitMethod(ACC_PUBLIC, "", "(L$ALGEBRA_CLASS;L$LIST_CLASS;)V", null, null).run { - val thisVar = 0 - val algebraVar = 1 - val constantsVar = 2 - val l0 = Label() - visitLabel(l0) - visitVarInsn(ALOAD, thisVar) - visitVarInsn(ALOAD, algebraVar) - visitVarInsn(ALOAD, constantsVar) - - visitMethodInsn( - INVOKESPECIAL, - ASM_COMPILED_EXPRESSION_CLASS, - "", - "(L$ALGEBRA_CLASS;L$LIST_CLASS;)V", - false - ) - - val l1 = Label() - visitLabel(l1) - visitInsn(RETURN) - val l2 = Label() - visitLabel(l2) - visitLocalVariable("this", "L$slashesClassName;", null, l0, l2, thisVar) - - visitLocalVariable( - "algebra", - "L$ALGEBRA_CLASS;", - "L$ALGEBRA_CLASS;", - l0, - l2, - algebraVar - ) - - visitLocalVariable("constants", "L$LIST_CLASS;", "L$LIST_CLASS;", l0, l2, constantsVar) - visitMaxs(3, 3) - visitEnd() - } - - invokeMethodVisitor = visitMethod( - ACC_PUBLIC or ACC_FINAL, - "invoke", - "(L$MAP_CLASS;)L$T_CLASS;", - "(L$MAP_CLASS;)L$T_CLASS;", - null - ) - - invokeMethodVisitor.run { - visitCode() - invokeL0 = Label() - visitLabel(invokeL0) - } - } - } - - @PublishedApi - @Suppress("UNCHECKED_CAST") - internal fun generate(): AsmCompiledExpression { - generatedInstance?.let { return it } - - invokeMethodVisitor.run { - visitInsn(ARETURN) - invokeL1 = Label() - visitLabel(invokeL1) - - visitLocalVariable( - "this", - "L$slashesClassName;", - T_CLASS, - invokeL0, - invokeL1, - invokeThisVar - ) - - visitLocalVariable( - "arguments", - "L$MAP_CLASS;", - "L$MAP_CLASS;", - invokeL0, - invokeL1, - invokeArgumentsVar - ) - - visitMaxs(maxStack + 1, 2) - visitEnd() - } - - asmCompiledClassWriter.visitMethod( - ACC_PUBLIC or ACC_FINAL or ACC_BRIDGE or ACC_SYNTHETIC, - "invoke", - "(L$MAP_CLASS;)L$OBJECT_CLASS;", - null, - null - ).run { - val thisVar = 0 - visitCode() - val l0 = Label() - visitLabel(l0) - visitVarInsn(ALOAD, 0) - visitVarInsn(ALOAD, 1) - visitMethodInsn(INVOKEVIRTUAL, slashesClassName, "invoke", "(L$MAP_CLASS;)L$T_CLASS;", false) - visitInsn(ARETURN) - val l1 = Label() - visitLabel(l1) - - visitLocalVariable( - "this", - "L$slashesClassName;", - T_CLASS, - l0, - l1, - thisVar - ) - - visitMaxs(2, 2) - visitEnd() - } - - asmCompiledClassWriter.visitEnd() - - val new = classLoader - .defineClass(className, asmCompiledClassWriter.toByteArray()) - .constructors - .first() - .newInstance(algebra, constants) as AsmCompiledExpression - - generatedInstance = new - return new - } - - internal fun visitLoadFromConstants(value: T) = visitLoadAnyFromConstants(value as Any, T_CLASS) - - private fun visitLoadAnyFromConstants(value: Any, type: String) { - val idx = if (value in constants) constants.indexOf(value) else constants.apply { add(value) }.lastIndex - maxStack++ - - invokeMethodVisitor.run { - visitLoadThis() - visitFieldInsn(GETFIELD, slashesClassName, "constants", "L$LIST_CLASS;") - visitLdcOrIConstInsn(idx) - visitMethodInsn(INVOKEINTERFACE, LIST_CLASS, "get", "(I)L$OBJECT_CLASS;", true) - invokeMethodVisitor.visitTypeInsn(CHECKCAST, type) - } - } - - private fun visitLoadThis(): Unit = invokeMethodVisitor.visitVarInsn(ALOAD, invokeThisVar) - - internal fun visitNumberConstant(value: Number) { - maxStack++ - val clazz = value.javaClass - val c = clazz.name.replace('.', '/') - - val sigLetter = SIGNATURE_LETTERS[clazz] - - if (sigLetter != null) { - when (value) { - is Int -> invokeMethodVisitor.visitLdcOrIConstInsn(value) - is Double -> invokeMethodVisitor.visitLdcOrDConstInsn(value) - is Float -> invokeMethodVisitor.visitLdcOrFConstInsn(value) - else -> invokeMethodVisitor.visitLdcInsn(value) - } - - invokeMethodVisitor.visitMethodInsn(INVOKESTATIC, c, "valueOf", "($sigLetter)L${c};", false) - return - } - - visitLoadAnyFromConstants(value, c) - } - - internal fun visitLoadFromVariables(name: String, defaultValue: T? = null) = invokeMethodVisitor.run { - maxStack += 2 - visitVarInsn(ALOAD, invokeArgumentsVar) - - if (defaultValue != null) { - visitLdcInsn(name) - visitLoadFromConstants(defaultValue) - - visitMethodInsn( - INVOKEINTERFACE, - MAP_CLASS, - "getOrDefault", - "(L$OBJECT_CLASS;L$OBJECT_CLASS;)L$OBJECT_CLASS;", - true - ) - - visitCastToT() - return - } - - visitLdcInsn(name) - visitMethodInsn(INVOKEINTERFACE, MAP_CLASS, "get", "(L$OBJECT_CLASS;)L$OBJECT_CLASS;", true) - visitCastToT() - } - - internal fun visitLoadAlgebra() { - invokeMethodVisitor.visitVarInsn(ALOAD, invokeThisVar) - - invokeMethodVisitor.visitFieldInsn( - GETFIELD, - ASM_COMPILED_EXPRESSION_CLASS, "algebra", "L$ALGEBRA_CLASS;" - ) - - invokeMethodVisitor.visitTypeInsn(CHECKCAST, T_ALGEBRA_CLASS) - } - - internal fun visitAlgebraOperation(owner: String, method: String, descriptor: String) { - maxStack++ - invokeMethodVisitor.visitMethodInsn(INVOKEINTERFACE, owner, method, descriptor, true) - visitCastToT() - } - - private fun visitCastToT(): Unit = invokeMethodVisitor.visitTypeInsn(CHECKCAST, T_CLASS) - - internal companion object { - private val SIGNATURE_LETTERS = mapOf( - java.lang.Byte::class.java to "B", - java.lang.Short::class.java to "S", - java.lang.Integer::class.java to "I", - java.lang.Long::class.java to "J", - java.lang.Float::class.java to "F", - java.lang.Double::class.java to "D" - ) - - internal const val ASM_COMPILED_EXPRESSION_CLASS = "scientifik/kmath/expressions/AsmCompiledExpression" - internal const val LIST_CLASS = "java/util/List" - internal const val MAP_CLASS = "java/util/Map" - internal const val OBJECT_CLASS = "java/lang/Object" - internal const val ALGEBRA_CLASS = "scientifik/kmath/operations/Algebra" - internal const val SPACE_OPERATIONS_CLASS = "scientifik/kmath/operations/SpaceOperations" - internal const val STRING_CLASS = "java/lang/String" - internal const val FIELD_OPERATIONS_CLASS = "scientifik/kmath/operations/FieldOperations" - internal const val RING_OPERATIONS_CLASS = "scientifik/kmath/operations/RingOperations" - internal const val NUMBER_CLASS = "java/lang/Number" - } -} - interface AsmExpression { fun invoke(gen: AsmGenerationContext) } @@ -383,35 +90,3 @@ internal class AsmDivExpression( ) } } - -open class AsmExpressionSpace(space: Space) : Space>, - ExpressionSpace> { - override val zero: AsmExpression = AsmConstantExpression(space.zero) - override fun const(value: T): AsmExpression = AsmConstantExpression(value) - override fun variable(name: String, default: T?): AsmExpression = AsmVariableExpression(name, default) - override fun add(a: AsmExpression, b: AsmExpression): AsmExpression = AsmSumExpression(a, b) - override fun multiply(a: AsmExpression, k: Number): AsmExpression = AsmConstProductExpression(a, k) - operator fun AsmExpression.plus(arg: T) = this + const(arg) - operator fun AsmExpression.minus(arg: T) = this - const(arg) - operator fun T.plus(arg: AsmExpression) = arg + this - operator fun T.minus(arg: AsmExpression) = arg - this -} - -class AsmExpressionField(private val field: Field) : ExpressionField>, - AsmExpressionSpace(field) { - override val one: AsmExpression - get() = const(this.field.one) - - override fun number(value: Number): AsmExpression = const(field.run { one * value }) - - override fun multiply(a: AsmExpression, b: AsmExpression): AsmExpression = - AsmProductExpression(a, b) - - override fun divide(a: AsmExpression, b: AsmExpression): AsmExpression = - AsmDivExpression(a, b) - - operator fun AsmExpression.times(arg: T) = this * const(arg) - operator fun AsmExpression.div(arg: T) = this / const(arg) - operator fun T.times(arg: AsmExpression) = arg * this - operator fun T.div(arg: AsmExpression) = arg / this -} diff --git a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmGenerationContext.kt b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmGenerationContext.kt new file mode 100644 index 000000000..cedd5c0fd --- /dev/null +++ b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmGenerationContext.kt @@ -0,0 +1,283 @@ +package scientifik.kmath.expressions + +import org.objectweb.asm.ClassWriter +import org.objectweb.asm.Label +import org.objectweb.asm.MethodVisitor +import org.objectweb.asm.Opcodes +import scientifik.kmath.operations.Algebra + +class AsmGenerationContext( + classOfT: Class<*>, + private val algebra: Algebra, + private val className: String +) { + private class ClassLoader(parent: java.lang.ClassLoader) : java.lang.ClassLoader(parent) { + internal fun defineClass(name: String?, b: ByteArray): Class<*> = defineClass(name, b, 0, b.size) + } + + private val classLoader: ClassLoader = ClassLoader(javaClass.classLoader) + + @Suppress("PrivatePropertyName") + private val T_ALGEBRA_CLASS: String = algebra.javaClass.name.replace(oldChar = '.', newChar = '/') + + @Suppress("PrivatePropertyName") + private val T_CLASS: String = classOfT.name.replace('.', '/') + + private val slashesClassName: String = className.replace(oldChar = '.', newChar = '/') + private val invokeThisVar: Int = 0 + private val invokeArgumentsVar: Int = 1 + private var maxStack: Int = 0 + private val constants: MutableList = mutableListOf() + private val asmCompiledClassWriter: ClassWriter = + ClassWriter(0) + private val invokeMethodVisitor: MethodVisitor + private val invokeL0: Label + private lateinit var invokeL1: Label + private var generatedInstance: AsmCompiledExpression? = null + + init { + asmCompiledClassWriter.visit( + Opcodes.V1_8, + Opcodes.ACC_PUBLIC or Opcodes.ACC_FINAL or Opcodes.ACC_SUPER, + slashesClassName, + "L$ASM_COMPILED_EXPRESSION_CLASS;", + ASM_COMPILED_EXPRESSION_CLASS, + arrayOf() + ) + + asmCompiledClassWriter.run { + visitMethod(Opcodes.ACC_PUBLIC, "", "(L$ALGEBRA_CLASS;L$LIST_CLASS;)V", null, null).run { + val thisVar = 0 + val algebraVar = 1 + val constantsVar = 2 + val l0 = Label() + visitLabel(l0) + visitVarInsn(Opcodes.ALOAD, thisVar) + visitVarInsn(Opcodes.ALOAD, algebraVar) + visitVarInsn(Opcodes.ALOAD, constantsVar) + + visitMethodInsn( + Opcodes.INVOKESPECIAL, + ASM_COMPILED_EXPRESSION_CLASS, + "", + "(L$ALGEBRA_CLASS;L$LIST_CLASS;)V", + false + ) + + val l1 = Label() + visitLabel(l1) + visitInsn(Opcodes.RETURN) + val l2 = Label() + visitLabel(l2) + visitLocalVariable("this", "L$slashesClassName;", null, l0, l2, thisVar) + + visitLocalVariable( + "algebra", + "L$ALGEBRA_CLASS;", + "L$ALGEBRA_CLASS;", + l0, + l2, + algebraVar + ) + + visitLocalVariable("constants", "L$LIST_CLASS;", "L$LIST_CLASS;", l0, l2, constantsVar) + visitMaxs(3, 3) + visitEnd() + } + + invokeMethodVisitor = visitMethod( + Opcodes.ACC_PUBLIC or Opcodes.ACC_FINAL, + "invoke", + "(L$MAP_CLASS;)L$T_CLASS;", + "(L$MAP_CLASS;)L$T_CLASS;", + null + ) + + invokeMethodVisitor.run { + visitCode() + invokeL0 = Label() + visitLabel(invokeL0) + } + } + } + + @PublishedApi + @Suppress("UNCHECKED_CAST") + internal fun generate(): AsmCompiledExpression { + generatedInstance?.let { return it } + + invokeMethodVisitor.run { + visitInsn(Opcodes.ARETURN) + invokeL1 = Label() + visitLabel(invokeL1) + + visitLocalVariable( + "this", + "L$slashesClassName;", + T_CLASS, + invokeL0, + invokeL1, + invokeThisVar + ) + + visitLocalVariable( + "arguments", + "L$MAP_CLASS;", + "L$MAP_CLASS;", + invokeL0, + invokeL1, + invokeArgumentsVar + ) + + visitMaxs(maxStack + 1, 2) + visitEnd() + } + + asmCompiledClassWriter.visitMethod( + Opcodes.ACC_PUBLIC or Opcodes.ACC_FINAL or Opcodes.ACC_BRIDGE or Opcodes.ACC_SYNTHETIC, + "invoke", + "(L$MAP_CLASS;)L$OBJECT_CLASS;", + null, + null + ).run { + val thisVar = 0 + visitCode() + val l0 = Label() + visitLabel(l0) + visitVarInsn(Opcodes.ALOAD, 0) + visitVarInsn(Opcodes.ALOAD, 1) + visitMethodInsn(Opcodes.INVOKEVIRTUAL, slashesClassName, "invoke", "(L$MAP_CLASS;)L$T_CLASS;", false) + visitInsn(Opcodes.ARETURN) + val l1 = Label() + visitLabel(l1) + + visitLocalVariable( + "this", + "L$slashesClassName;", + T_CLASS, + l0, + l1, + thisVar + ) + + visitMaxs(2, 2) + visitEnd() + } + + asmCompiledClassWriter.visitEnd() + + val new = classLoader + .defineClass(className, asmCompiledClassWriter.toByteArray()) + .constructors + .first() + .newInstance(algebra, constants) as AsmCompiledExpression + + generatedInstance = new + return new + } + + internal fun visitLoadFromConstants(value: T) = visitLoadAnyFromConstants(value as Any, T_CLASS) + + private fun visitLoadAnyFromConstants(value: Any, type: String) { + val idx = if (value in constants) constants.indexOf(value) else constants.apply { add(value) }.lastIndex + maxStack++ + + invokeMethodVisitor.run { + visitLoadThis() + visitFieldInsn(Opcodes.GETFIELD, slashesClassName, "constants", "L$LIST_CLASS;") + visitLdcOrIConstInsn(idx) + visitMethodInsn(Opcodes.INVOKEINTERFACE, LIST_CLASS, "get", "(I)L$OBJECT_CLASS;", true) + invokeMethodVisitor.visitTypeInsn(Opcodes.CHECKCAST, type) + } + } + + private fun visitLoadThis(): Unit = invokeMethodVisitor.visitVarInsn(Opcodes.ALOAD, invokeThisVar) + + internal fun visitNumberConstant(value: Number) { + maxStack++ + val clazz = value.javaClass + val c = clazz.name.replace('.', '/') + + val sigLetter = SIGNATURE_LETTERS[clazz] + + if (sigLetter != null) { + when (value) { + is Int -> invokeMethodVisitor.visitLdcOrIConstInsn(value) + is Double -> invokeMethodVisitor.visitLdcOrDConstInsn(value) + is Float -> invokeMethodVisitor.visitLdcOrFConstInsn(value) + else -> invokeMethodVisitor.visitLdcInsn(value) + } + + invokeMethodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, c, "valueOf", "($sigLetter)L${c};", false) + return + } + + visitLoadAnyFromConstants(value, c) + } + + internal fun visitLoadFromVariables(name: String, defaultValue: T? = null) = invokeMethodVisitor.run { + maxStack += 2 + visitVarInsn(Opcodes.ALOAD, invokeArgumentsVar) + + if (defaultValue != null) { + visitLdcInsn(name) + visitLoadFromConstants(defaultValue) + + visitMethodInsn( + Opcodes.INVOKEINTERFACE, + MAP_CLASS, + "getOrDefault", + "(L$OBJECT_CLASS;L$OBJECT_CLASS;)L$OBJECT_CLASS;", + true + ) + + visitCastToT() + return + } + + visitLdcInsn(name) + visitMethodInsn(Opcodes.INVOKEINTERFACE, MAP_CLASS, "get", "(L$OBJECT_CLASS;)L$OBJECT_CLASS;", true) + visitCastToT() + } + + internal fun visitLoadAlgebra() { + invokeMethodVisitor.visitVarInsn(Opcodes.ALOAD, invokeThisVar) + + invokeMethodVisitor.visitFieldInsn( + Opcodes.GETFIELD, + ASM_COMPILED_EXPRESSION_CLASS, "algebra", "L$ALGEBRA_CLASS;" + ) + + invokeMethodVisitor.visitTypeInsn(Opcodes.CHECKCAST, T_ALGEBRA_CLASS) + } + + internal fun visitAlgebraOperation(owner: String, method: String, descriptor: String) { + maxStack++ + invokeMethodVisitor.visitMethodInsn(Opcodes.INVOKEINTERFACE, owner, method, descriptor, true) + visitCastToT() + } + + private fun visitCastToT(): Unit = invokeMethodVisitor.visitTypeInsn(Opcodes.CHECKCAST, T_CLASS) + + internal companion object { + private val SIGNATURE_LETTERS = mapOf( + java.lang.Byte::class.java to "B", + java.lang.Short::class.java to "S", + java.lang.Integer::class.java to "I", + java.lang.Long::class.java to "J", + java.lang.Float::class.java to "F", + java.lang.Double::class.java to "D" + ) + + internal const val ASM_COMPILED_EXPRESSION_CLASS = "scientifik/kmath/expressions/AsmCompiledExpression" + internal const val LIST_CLASS = "java/util/List" + internal const val MAP_CLASS = "java/util/Map" + internal const val OBJECT_CLASS = "java/lang/Object" + internal const val ALGEBRA_CLASS = "scientifik/kmath/operations/Algebra" + internal const val SPACE_OPERATIONS_CLASS = "scientifik/kmath/operations/SpaceOperations" + internal const val STRING_CLASS = "java/lang/String" + internal const val FIELD_OPERATIONS_CLASS = "scientifik/kmath/operations/FieldOperations" + internal const val RING_OPERATIONS_CLASS = "scientifik/kmath/operations/RingOperations" + internal const val NUMBER_CLASS = "java/lang/Number" + } +} diff --git a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/Builders.kt b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/Builders.kt new file mode 100644 index 000000000..bdbab2b42 --- /dev/null +++ b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/Builders.kt @@ -0,0 +1,38 @@ +package scientifik.kmath.expressions + +import scientifik.kmath.operations.Field +import scientifik.kmath.operations.Space + +@PublishedApi +internal fun buildName(expression: AsmExpression<*>, collision: Int = 0): String { + val name = "scientifik.kmath.expressions.generated.AsmCompiledExpression_${expression.hashCode()}_$collision" + + try { + Class.forName(name) + } catch (ignored: ClassNotFoundException) { + return name + } + + return buildName(expression, collision + 1) +} + + +inline fun asmSpace( + algebra: Space, + block: AsmExpressionSpace.() -> AsmExpression +): Expression { + val expression = AsmExpressionSpace(algebra).block() + val ctx = AsmGenerationContext(T::class.java, algebra, buildName(expression)) + expression.invoke(ctx) + return ctx.generate() +} + +inline fun asmField( + algebra: Field, + block: AsmExpressionField.() -> AsmExpression +): Expression { + val expression = AsmExpressionField(algebra).block() + val ctx = AsmGenerationContext(T::class.java, algebra, buildName(expression)) + expression.invoke(ctx) + return ctx.generate() +} diff --git a/kmath-asm/src/test/kotlin/scientifik/kmath/expressions/AsmTest.kt b/kmath-asm/src/test/kotlin/scientifik/kmath/expressions/AsmTest.kt index 875080616..4370029f6 100644 --- a/kmath-asm/src/test/kotlin/scientifik/kmath/expressions/AsmTest.kt +++ b/kmath-asm/src/test/kotlin/scientifik/kmath/expressions/AsmTest.kt @@ -64,7 +64,7 @@ class AsmTest { ) @Test - fun testCProductWithOtherTypeNumber() = testDoubleExpressionValue( + fun testCProductWithOtherTypeNumber(): Unit = testDoubleExpressionValue( 25.0, AsmConstProductExpression(AsmVariableExpression("x"), 5f), mapOf("x" to 5.0) @@ -81,21 +81,21 @@ class AsmTest { } @Test - fun testCProductWithCustomTypeNumber() = testDoubleExpressionValue( + fun testCProductWithCustomTypeNumber(): Unit = testDoubleExpressionValue( 0.0, AsmConstProductExpression(AsmVariableExpression("x"), CustomZero), mapOf("x" to 5.0) ) @Test - fun testVar() = testDoubleExpressionValue( + fun testVar(): Unit = testDoubleExpressionValue( 10000.0, AsmVariableExpression("x"), mapOf("x" to 10000.0) ) @Test - fun testVarWithDefault() = testDoubleExpressionValue( + fun testVarWithDefault(): Unit = testDoubleExpressionValue( 10000.0, AsmVariableExpression("x", 10000.0), mapOf() -- 2.34.1 From b7d1fe256023acf0b03d48fb77a55c5119b453e8 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Mon, 8 Jun 2020 14:38:37 +0700 Subject: [PATCH 023/156] Implement recursive constants evaluation, improve builders --- .../kmath/expressions/AsmExpressionSpaces.kt | 13 +++-- .../kmath/expressions/AsmExpressions.kt | 49 +++++++++++++++---- .../scientifik/kmath/expressions/Builders.kt | 20 ++++---- .../kmath/expressions/Optimization.kt | 6 +++ 4 files changed, 63 insertions(+), 25 deletions(-) create mode 100644 kmath-asm/src/main/kotlin/scientifik/kmath/expressions/Optimization.kt diff --git a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressionSpaces.kt b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressionSpaces.kt index 5c93fd729..da788372b 100644 --- a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressionSpaces.kt +++ b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressionSpaces.kt @@ -3,13 +3,13 @@ package scientifik.kmath.expressions import scientifik.kmath.operations.Field import scientifik.kmath.operations.Space -open class AsmExpressionSpace(space: Space) : Space>, +open class AsmExpressionSpace(private val space: Space) : Space>, ExpressionSpace> { override val zero: AsmExpression = AsmConstantExpression(space.zero) override fun const(value: T): AsmExpression = AsmConstantExpression(value) override fun variable(name: String, default: T?): AsmExpression = AsmVariableExpression(name, default) - override fun add(a: AsmExpression, b: AsmExpression): AsmExpression = AsmSumExpression(a, b) - override fun multiply(a: AsmExpression, k: Number): AsmExpression = AsmConstProductExpression(a, k) + override fun add(a: AsmExpression, b: AsmExpression): AsmExpression = AsmSumExpression(space, a, b) + override fun multiply(a: AsmExpression, k: Number): AsmExpression = AsmConstProductExpression(space, a, k) operator fun AsmExpression.plus(arg: T): AsmExpression = this + const(arg) operator fun AsmExpression.minus(arg: T): AsmExpression = this - const(arg) operator fun T.plus(arg: AsmExpression): AsmExpression = arg + this @@ -22,8 +22,11 @@ class AsmExpressionField(private val field: Field) : ExpressionField = const(field.run { one * value }) - override fun multiply(a: AsmExpression, b: AsmExpression): AsmExpression = AsmProductExpression(a, b) - override fun divide(a: AsmExpression, b: AsmExpression): AsmExpression = AsmDivExpression(a, b) + + override fun multiply(a: AsmExpression, b: AsmExpression): AsmExpression = + AsmProductExpression(field, a, b) + + override fun divide(a: AsmExpression, b: AsmExpression): AsmExpression = AsmDivExpression(field, a, b) operator fun AsmExpression.times(arg: T): AsmExpression = this * const(arg) operator fun AsmExpression.div(arg: T): AsmExpression = this / const(arg) operator fun T.times(arg: AsmExpression): AsmExpression = arg * this diff --git a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressions.kt b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressions.kt index 8708a33e1..cc861d363 100644 --- a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressions.kt +++ b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressions.kt @@ -1,6 +1,6 @@ package scientifik.kmath.expressions -import scientifik.kmath.operations.Algebra +import scientifik.kmath.operations.* abstract class AsmCompiledExpression internal constructor( @JvmField private val algebra: Algebra, @@ -10,6 +10,7 @@ abstract class AsmCompiledExpression internal constructor( } interface AsmExpression { + fun tryEvaluate(): T? = null fun invoke(gen: AsmGenerationContext) } @@ -20,13 +21,22 @@ internal class AsmVariableExpression(val name: String, val default: T? = null internal class AsmConstantExpression(val value: T) : AsmExpression { + override fun tryEvaluate(): T = value override fun invoke(gen: AsmGenerationContext): Unit = gen.visitLoadFromConstants(value) } internal class AsmSumExpression( - val first: AsmExpression, - val second: AsmExpression + private val algebra: SpaceOperations, + first: AsmExpression, + second: AsmExpression ) : AsmExpression { + private val first: AsmExpression = first.optimize() + private val second: AsmExpression = second.optimize() + + override fun tryEvaluate(): T? = algebra { + (first.tryEvaluate() ?: return@algebra null) + (second.tryEvaluate() ?: return@algebra null) + } + override fun invoke(gen: AsmGenerationContext) { gen.visitLoadAlgebra() first.invoke(gen) @@ -41,9 +51,17 @@ internal class AsmSumExpression( } internal class AsmProductExpression( - val first: AsmExpression, - val second: AsmExpression + private val algebra: RingOperations, + first: AsmExpression, + second: AsmExpression ) : AsmExpression { + private val first: AsmExpression = first.optimize() + private val second: AsmExpression = second.optimize() + + override fun tryEvaluate(): T? = algebra { + (first.tryEvaluate() ?: return@algebra null) * (second.tryEvaluate() ?: return@algebra null) + } + override fun invoke(gen: AsmGenerationContext) { gen.visitLoadAlgebra() first.invoke(gen) @@ -58,9 +76,14 @@ internal class AsmProductExpression( } internal class AsmConstProductExpression( - val expr: AsmExpression, - val const: Number + private val algebra: SpaceOperations, + expr: AsmExpression, + private val const: Number ) : AsmExpression { + private val expr: AsmExpression = expr.optimize() + + override fun tryEvaluate(): T? = algebra { (expr.tryEvaluate() ?: return@algebra null) * const } + override fun invoke(gen: AsmGenerationContext) { gen.visitLoadAlgebra() gen.visitNumberConstant(const) @@ -75,9 +98,17 @@ internal class AsmConstProductExpression( } internal class AsmDivExpression( - val expr: AsmExpression, - val second: AsmExpression + private val algebra: FieldOperations, + expr: AsmExpression, + second: AsmExpression ) : AsmExpression { + private val expr: AsmExpression = expr.optimize() + private val second: AsmExpression = second.optimize() + + override fun tryEvaluate(): T? = algebra { + (expr.tryEvaluate() ?: return@algebra null) / (second.tryEvaluate() ?: return@algebra null) + } + override fun invoke(gen: AsmGenerationContext) { gen.visitLoadAlgebra() expr.invoke(gen) diff --git a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/Builders.kt b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/Builders.kt index bdbab2b42..f8b4afd5b 100644 --- a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/Builders.kt +++ b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/Builders.kt @@ -1,5 +1,6 @@ package scientifik.kmath.expressions +import scientifik.kmath.operations.Algebra import scientifik.kmath.operations.Field import scientifik.kmath.operations.Space @@ -17,22 +18,19 @@ internal fun buildName(expression: AsmExpression<*>, collision: Int = 0): String } -inline fun asmSpace( - algebra: Space, - block: AsmExpressionSpace.() -> AsmExpression -): Expression { - val expression = AsmExpressionSpace(algebra).block() +inline fun asm(i: I, algebra: Algebra, block: I.() -> AsmExpression): Expression { + val expression = i.block().optimize() val ctx = AsmGenerationContext(T::class.java, algebra, buildName(expression)) expression.invoke(ctx) return ctx.generate() } +inline fun asmSpace( + algebra: Space, + block: AsmExpressionSpace.() -> AsmExpression +): Expression = asm(AsmExpressionSpace(algebra), algebra, block) + inline fun asmField( algebra: Field, block: AsmExpressionField.() -> AsmExpression -): Expression { - val expression = AsmExpressionField(algebra).block() - val ctx = AsmGenerationContext(T::class.java, algebra, buildName(expression)) - expression.invoke(ctx) - return ctx.generate() -} +): Expression = asm(AsmExpressionField(algebra), algebra, block) diff --git a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/Optimization.kt b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/Optimization.kt new file mode 100644 index 000000000..bb7d0476d --- /dev/null +++ b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/Optimization.kt @@ -0,0 +1,6 @@ +package scientifik.kmath.expressions + +fun AsmExpression.optimize(): AsmExpression { + val a = tryEvaluate() + return if (a == null) this else AsmConstantExpression(a) +} -- 2.34.1 From c576e46020b9a10ce2b6d3769032cc57e6522bbc Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Mon, 8 Jun 2020 14:46:00 +0700 Subject: [PATCH 024/156] Minor refactor --- .../kotlin/scientifik/kmath/expressions/AsmExpressionSpaces.kt | 3 ++- .../scientifik/kmath/expressions/FunctionalExpressions.kt | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressionSpaces.kt b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressionSpaces.kt index da788372b..8147bcd5c 100644 --- a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressionSpaces.kt +++ b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressionSpaces.kt @@ -2,6 +2,7 @@ package scientifik.kmath.expressions import scientifik.kmath.operations.Field import scientifik.kmath.operations.Space +import scientifik.kmath.operations.invoke open class AsmExpressionSpace(private val space: Space) : Space>, ExpressionSpace> { @@ -21,7 +22,7 @@ class AsmExpressionField(private val field: Field) : ExpressionField get() = const(this.field.one) - override fun number(value: Number): AsmExpression = const(field.run { one * value }) + override fun number(value: Number): AsmExpression = const(field { one * value }) override fun multiply(a: AsmExpression, b: AsmExpression): AsmExpression = AsmProductExpression(field, a, b) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressions.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressions.kt index dbc2eac1e..0304a665f 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressions.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressions.kt @@ -3,6 +3,7 @@ package scientifik.kmath.expressions import scientifik.kmath.operations.Field import scientifik.kmath.operations.Ring import scientifik.kmath.operations.Space +import scientifik.kmath.operations.invoke internal class VariableExpression(val name: String, val default: T? = null) : Expression { override fun invoke(arguments: Map): T = @@ -57,7 +58,7 @@ open class FunctionalExpressionField( override val one: Expression get() = const(this.field.one) - override fun number(value: Number): Expression = const(field.run { one * value }) + override fun number(value: Number): Expression = const(field { one * value }) override fun multiply(a: Expression, b: Expression): Expression = ProductExpression(field, a, b) override fun divide(a: Expression, b: Expression): Expression = DivExpression(field, a, b) operator fun Expression.times(arg: T): Expression = this * const(arg) -- 2.34.1 From 8f1cf0179a95345c3d9e0bd5323eb0b320f4f9dc Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Mon, 8 Jun 2020 14:48:44 +0700 Subject: [PATCH 025/156] Minor refactor --- .../scientifik/kmath/expressions/FunctionalExpressions.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressions.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressions.kt index 0304a665f..7b5a93465 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressions.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressions.kt @@ -51,9 +51,9 @@ open class FunctionalExpressionSpace(val space: Space) : Space): Expression = arg - this } -open class FunctionalExpressionField( - val field: Field -) : ExpressionField>, FunctionalExpressionSpace(field) { +open class FunctionalExpressionField(val field: Field) : + ExpressionField>, + FunctionalExpressionSpace(field) { override val one: Expression get() = const(this.field.one) -- 2.34.1 From 5d5ddd46d43aad77cd74f981890e5b3a227263f7 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Mon, 8 Jun 2020 14:53:14 +0700 Subject: [PATCH 026/156] Upgrade Gradle --- gradle/wrapper/gradle-wrapper.jar | Bin 58694 -> 58910 bytes gradle/wrapper/gradle-wrapper.properties | 2 +- gradlew | 2 ++ gradlew.bat | 1 + 4 files changed, 4 insertions(+), 1 deletion(-) diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 490fda8577df6c95960ba7077c43220e5bb2c0d9..62d4c053550b91381bbd28b1afc82d634bf73a8a 100644 GIT binary patch delta 6447 zcmY*dbyQSczlH%shY+L(kQ}C6ise@?c@F%#`dE9xT=qM=Dm?$VxD1hrECD1a#01Q8o zMyT3}z+1K>hPE%4doH=x5X;^NP(OFD5GByp;5FQ^bpzkBa(;eudMu7Iyv$DE+N=>p z{3Y5_BP>F3)tXW*Styc(Ji3jnK-giGA_&42fsbZ@#+e+ly3w0VmLC;LA)h1UY(ChA zfwqQ?-@}@S93F|exOv;Se;P|SrYvEG(8q&|ltqvQHO9KgCSwM!Y+#d5eIRq$Mi`pU__N$FTxW@KAWIw= zayY6@9EyxG<_tr&{Wi87m5*mf=u&=;eL1gf{Mt)q8Drick8CcxzLW>cG~TbW)|$*D zYMc|5eZNNzt7O_C1LqgaI`Z0B+2#;3yO;E7N4oMY@~7$4;MRonU+Ca z#*cD!7$u9pZ|3f!-_6rpN}XhAWd`1qiR{e*1CJK1dvBsjUyY@BuT|;EAz}*0uSwT_ zq(g0jXTAK4wsQ>kIKEfRQZw^GIKNRZmV)b;c*Kpc?IvNuq{+eCM4%IBoRUk!JeJ4IVH!pLl+5gQn^$0Fw(WROw~SclOYWbMmvR+x&lYa zrU`5lck*s2zl;n6HEa_E|Btu!_BeeF8T=~0Z-pdJsKtN8nr88*8loznbI`@@8U-bc zCE&MaHH#?LN;6&wU%>->{X&6n*c6ECkP#Bn{lafo9KW+AKK>>f)YfzG#t`XCsl$WX zeS|50l&G{J6yrdD0#njv3|C}K(~azN%1+c#*-JXtZd=Rs-zr)f{Mneaqpgewz^3OM5FaDaH3?RpqMyL}=5sFu_zcDc^E~=$H zp`mutZ0ahrf32c`6ROBh&lI`>vuFJE*(NVpjr~^d53MZ0v$G#mHqBLpZ_=3?pNjHc zq`Dn6xbc32BSg`U@YE?)%%`LvRRWt@NnS4GSj=p><<_-c6l`myAJE0fSp^QbBfdS( zl>BC`;EiMtvPQ^FVSL|sjTc(?b%8Qt@%U>rt&`4_cYT+R`OvMomf#104S~4%y%G=i zSF$4cuIxlIe>@1E=sfXhVt@RqP-*grJnW~;iWiY{&Bqh7J|{vYQ!^1x4cnyGk6Wb9 zO0~}ejH&5@bEj&`2?Wl*cf=IV=$oa9rzh+#gN?j{IY z{cFM?b1*TT+&S2rOIFFvy{`FvX}_js+9rw1O*1ySv8Q}r2b0@*h|1Di0R1v* zVt4yRX`%ac3xeH;(y!FJ1wfX0u z(vEffdladd}+qfb##M5s|vX#V+!&>>0;o_Le@c)+7jDwJJ(9>+3CRkGH z##M)o)xY%3-ifK*iFpo7NiBT`wYVc=lYIZtKF{pxNfod2V)Ml&<=??l)7w5)Glopn z8#scqBz@^rE2-5aVDT_~Q#A7m4S6@B{QM6c_oY?)xk>z8r!qnbkvnqHoIRTMZijQ5 zv*ir-hjrA??C7S({;peDbjO+Kk0=tpoYQr7VQMJ*cR43?@CVMwg=}e<87k-T@wQ2`}bwe-}AAk?H=&0Yz~Zbk~bc>EP@tV zZ}M>Z2br)mwHOaQS1^~;AVlgQS(~eqTT3cQ)Jq8?bKk~$>tZSLgMW6sF{Os2*%OD^ z#@G{w=c@536Pgy5n{C*b?yf@Kd`v9zOG*56432l!^U3K)m1;qIzM*3ZS)XJnJ4THC z^e*Y&BQ)hyIA?LzXpqWK1`GN&gr?{?;qw?0wZ2-3WO3HI;)oY4YL?q5>F9QpzV?jw z%Ae1D+te?r(`vL~!tzayt@-830@#ZS)-KyoF0$s!Vw0Vud%!J!?moY0m8#gODn9F+ zY?TnBMwOKomVz60?|&V3HO!Z!cH+<9qbk>I-tT86n9=X9g`Zr=G+ zeJZH~&WtV__tcQ~B#c3;HnlwX+UoXIT>zqV;hho> zm(S|vbkcOsiPJd5fwJn%e%@Z(YNs#TqQ-MTQNPf9zDS)^#q=x)hn0wzK&7Tn_|BdK zx}|&Y!FqT|pVs!!ayLJ%C$M2LMR|s6aQ%QUi>oqMG=a-^oPaKfKR>DyX9dBV*%R!+ z%FvBF>KN67w@!4Lj7{*vhaGWkP344{vG@LFna%+6y+SB#;an8bz1SAoZg)%>it7$I$^*bWXoT6hbhk;!C7 z5tAKrT@VO5N!8a8G3=U4NL5yNqYdEsc2}2^o5ctj;Hrf0Dk~jL|srk z+XuB%H@ROKFqLw>LUu0bqRXw}B*R!OLo6|5*Q4|0dPlcG;>@4(_wZ})Yf&doH+L*RE=D|Z6RxTU#a|+qO_A4p z2U{|br!ER>QqRY>(awtH6L-S8zx$EeC$o;?KH-zEE{_f%M55>lLD!d9KbLpEyv&z3 zOD}@>1Exq4C9v6urtETRrtB>6m;qqJfh)6o@&+S>@D45s~ccePF=|y`U z-f~hKH|y8x$ovl1NJi3Qqom;ERzIG#^&!~fFQcyl0+H+;`yV@UyA|P*R^h1K*<8h{ zZqjSxw79HGC?HMzs;UY)%J2b0gXnQ=OY;dHMi3-zr7BZ6SnFxTu8VCoySbgs>l^A8 zmN&kvh~36=TRu2B!zInA7+dp6$aaef-&PgtbENZDyV(2Qh!`{>wDfZGw=1SFg*E{+ z#RVlY)C{0iP0+Q52$nQXhK{cVx<)i;=tyb=4mRyl7vX}F8Q%QL>_d6O7MM}r2)$$y+>m{$P8lbYz;fZ z3QWqj-`0^M+YpnVm!KE9$7?qn-uiDEF=*G=DW84fhX*c2c78!Mp!igEq_TE#1gLe8 zl$ro$nqM(yq&C?t-G#o9^eY1)Q9PX&YrAtOX|lboS9pTS>3XVy+T*%QF@Dx%R! zi~z%gEL!?kG{Q%?*cWYwt#5W}g>qQ?$$RX%E0(03W7ZERFNIOjpM5e?6J0JAro(i1 zsQeyE7G{}iSZNnP(n4FwvEp+ztGzd?jYx+(7Mk46X^c!>`oO7{i_yo>FV+t|SvS!} zBkOPHlUb!OPh1Y-8duD(b2u@P=5b8soW*+wnMY4Q8Eq!-L)~5b=n{68|ISew8k>Nt zjw!awOP?W8P1$OO`+#?*f{M(%*J)%E_^tKqR(nv#swuRijXecgwQacnz4TE8 z=2-p0u+VG&&^ePGuUHKIgI+h>XY*ZqAI5N*4Wc%8CXbXf57?Mpl#k^M=OHx26*X=b z@XIHOwsp{@XZ?Foo*@>FnvH!0EQsZ*BR?l&zm|TjE+bDiqA$Y2SY>Copx~1PHa4js z_!C`yon1&oi{Kr00~T|`DcYfvr^uu*F03OLS>^N@6Zi4VhFx(|WVY7whxD`RzX@{a zbt^j09cW#7p^J^3)}YLkrHR`G;mbL@W6__7SC=}Xh$OzjG!>tu=ubtG%LthmSDE)Y zfp>6T8@qS6C@y(<;eHyUqHzM9+%$!LWjRr*z1Qw1s?bAYrK7*KD*C^qP{W=T31H#9%+CXSZ;mJdIE6lN%IxBUk0hr5P})$QDM>4>ow%muHv z-zVTS+rI9+PV|%56*~qa^GKRWwz;dLtoUR%*1M}RGh$LcGlrHaAh-`>BW&!A6mvv( zo}57{BhH+Bqiza~XoxEIpXk_BGR8GzhcQwT4ND>~ahppmV*4SGve=@GE0zZGn}Z_l zMJ~Bi7prl4W<5m=nXZVtIYs=mwv2O*-UXG(Y9#Tfu8=c%NzSja+#d#gJ}FZhj)shN zMhx$^a#S-Ji`_niAxIQ^8YN)tqqJ!k5S_*BUFNY4F-4u9`G(W0v9;O*=f94+)C?7x zvYptQhDL9z*Ef*V5;DWma#Kwl4duDaGW=wP;`7wCjpnvd1`SO#b!fM0%!1J-u}iOT zS`t%%#@E|EzErxcRQ`fYJ)?gm)spx4eAd0@1P(T8Pr4n}5d$L~0>gytVD-^eF2bLx zW3i^+7-f{_=5Zq77xY&vCpL~@OTUZ`^myD;mRijH9fO>_Qdw^gurX%)NhZcgCIxgN z4yJcYrgaS}O8U(X^mwaTnrkxmt*ni+Cdmv>X$)_K4fl)^GtOUWQ~h>K$_^s;h!1Dw z*q&qAD_pNCM3lb9=U3Af`-?xuwb62P12trTb=MXKaYoNRHZPDJv9*`Aw)QF0Tb@g}XFL;| zdJF}(@e5r%*LCQBK*U(pdQRDeKE!)FF+}k{9Fz>A6zUP@OV+3DhvOQ zm{2a0QrQ^kn~?Df`@q(xA(yDoo!~Q+;;_*@_h(a`J~*mJkCa@npgsiRZAQ#pqSOZK z!muT4MNvG*<^MYIQN0h-W#UtDprj`i7Xxq=bTN{>rHH}V?ZdT~kd!O-X zt5JI4SH&YHnn(%JNKh$z*YZsO#t%LLA680?$^5V~dE8Pl^cPrXu++@2D?!)`KkPkM zE{Jaq+MNaAl)!{f!@ID?j@Fh)p!zU~?G%ODNge-447;DM8a%=PGRAB#D&LD5-=atG zY9Y3SF$2Xq8v`e8Rvmy3(wxGi--=L0eqRV6KFsU+waZV(WuPT00CKK)a--{eLpmBy zcXLs^*FtPQfeF;&p!YXTs3p9?U8Q0nzxqE+bM#Y7^_TmK zsw$bo4WCokyvS6N_0(KUJ2!8X|5~{<8pDd7rDt;^sCOx&=RxoN<`o-B}EwumojPl2bzq!x}k%%W5t9nTM1xeXi zQv;z_icyd<$#$rBJk9nk)8!h|c`$y~+NUVUGMRKk0aIBHQxP%YPu#d}ntgv1C_my; zpbt9K?YSK7jR%!jIUz+E3dnfbRMkv&7^h$B&oh5Ae2U{ka*7&~Z|XGk#69p1c_G1FC{&L1hn#)ZCmqpbHXC6uk;Obwn7kSJKaZ`H?u#%dz%W!fJP&`<51T`RomXjQ_%* zZ6iKVWhSW(o;7GYUuAwQxLzZTMt^H4@rorBp`tprXq9xsaKz)V<&_~zzsbGC#J2xC zQqiFYS<^~7D^Pcs?HzZm78=|`Ql?|`KIZR%#&qOMAEpStCrEMl8R0iZLR|#8%!;8p z0VGG*J(7WAxG~ij`ISsxDD--ge}1Dh3vAj>!wtQtec=#YCHNFKz$`Il6fa~c`rYYD z(xqyH;ETfFb?fK!?^*s3`))*65xs|5*^u3Snz(6t59|0kESGze=0W7f>LL{K_sC3& z*ardr??S+*s+p>{8sni`20|xZQ#^D^AQTjp`=*)izGeFN$qoSHK6K7(lg#A*T_gM( zK|#q5@BmyU)j&wqjB*=s29ufgV)YL%VJRV>@1p)anJxE7WkARdZ36Lb~f2b6Q zlm7uK{1gU}2|U1INlYN^Cl9Dh;{WL3PjQf^)PE=rpfSw?($jsQrq#T^it69uKY15Tb~K=hm} zh{fw3iUZN>cmUlz1T^;!pw6KHjOL|4uKo}3i|5k^cjn$5g+E9&YZL(c0t7^Yyr*;k z{39mNJB|kkA^-oNpr8j6hJ*m~3oM}A&ow%Xk22_5P%a?j<^aqv(ILmiH2Q>4Owl^89`~3rMHp zp3(w1Yh0kR@38~4fWByT)-r6kJki5KxqsSQ->5QD8+n7Lblrq&rqbQu<4GcZbwU*DehL0!uF< zAtALa2-nN~-E+^Kf9CT%^Pcydcg~!dGjHY)VIP{X+Mk5X+Z1~yNkl;K;}!vd91tr< z3$)!P0ZK`15GdXAY=~6eS|ICMe*_|EtvP9boO{_-?eWIq(~Zo-^Ni?kUPq%Frv%84 zr)oV1Do+g^<-_H;g&&6jZW30jA}03FK{ok6%fnpg;T?i6z?Ni4>j&X84{fZopFMV_ zPgq3;2ochOBOr>*RYtVW6qFYa2RCa+Rij=CocWr`A#j^WVQcy=l`bl)`?rJh=2@6e z5{>%T3cj@IohTK=HSz{HCTuU>e9Jdy(opO40;jY>4CYhDaoW$2zlNl%@5(Qiu=y0y zcPW^JHHod;>lqb~jFKYaMy2xYMtqcZ)tr_RM@6k9lIwWE8QrU-RZ^X=V;RcRRkvfh zd1>Ux5k>B6Zog!6HSDMhQY$F;vke(i*FG4;(;LQ}mHEaN8B^s8K(WGkdBU85Nh-nw z3TtcD!M5Wr+_o`vA0(6W&{4w4+nrWDTW1^{ z`epH{pQuSybd8I*sYD3SJ~2ag z)Yl_lSuF&Mbw4X`D?Zu`D`om|Xx`05WdlZ9t=JoV-6wy-R)lz9Vmu3c>A*fG30~0(?uQ5FkJ%zGK6$qDU~&hJ-V3Gc6s?!hhw*e)&1k)r=FnmzLWcywDn{+ksed*I9(B{*s3K(%lJ)U)|9X0a^E2 z?>RlLCvy+s4faLC0}D1!+cYzr%>h-s0|&9TBc1a9Zj|0mYS(5 zrQ~xRl7za1>q_E^{8c1q74LqFM-}HUQKs z-HX=BqDsXVjC!$_)l0!SF$o_V=RXM+z&V&q6#jU#AuF*Ji7|_5#Z1IhRaGYUxFADf zpXVNXi^mIuN^VZCEy?r%N`o=v9TuU`3mG^fHWsJ7ia5E@h3U;R^8nN0<6mS@yNZ|*5X zjEnxhb4H)?Mxy|QSTBrESL0adG6`arE$lH-Quq8IpQfLyXQ6-~q4$o-rhCpAt($tI zaQa-ZZM^S!;$?}%kABf#XFUWGO|RZjOJYN?9`~l2FNCPG(y>&9>G2l#+5fWW;j7y+ zQId*;#2h|q8>}2c^sysZFYgKl&gLAc8b;;_h%M^v5(yp^hO`DU#mFTN zZo|S}wZuF&o_J(DA!5AX>d=y}Iw7%z*yBr$?F*l*`ncP=hjAJ8zx2t%b$OWhk#*>L zp`+b!2vJ%5!5Pm;TXyhUy>17398}g9$AA1ssrPvPv44N`QtuuEE{>Jfe<@nFgB5?k zeEE{>t*#8BJh%#1a}!~{TtS;f#A-UQO!fR1zuQA~$WHb8_sW<`I zOQt1l>b3%|CE-m#+H%q)ASiMAt&ke3SnvD{cC0Ff;U-w5o;8ioQdl~qkLfEQ-TaIu~%rf%rG#UXd z#FXb(La?+7@`V^U+FMI3**T4yDFF#ZXU;?IM6Bw#p@kx86Xq&q-1cybR(211`S}V* znO%<4o*ixUE0Pbh+Yz&y$*tl-EYXj4#@j5-Wj6CQ7slhaV>Bq)HZf-lb{<_}t>aYl z&=`I3F_+?^Q~lAB&dSS|O^qS%5er4X>)d^YqM{p>F_t3F+O*!(aZ;%_yJJ}DE$sT^ zD?V+F1o)k|;MJA7`df*pD~TA{i+^wLEi5h3gr(29e5~cw@g{21H}^GSsQD@#%k03a zK9?s{0JjBaTq z%7|3eul{k|8$TQf8qMtCiY(ub>dVMH!d3$^aEg9r8e~r>3sXIyah&#Of9~35eqFVQ>knQg8ZBr~gYpRT*COY|4$vZssNa2NxUeYfsm!1qND_;I$wR~eah0d%+M7?x^JA+$)Ce~Rg zeqN7OxBK8sNnuySGL7AXp>`pLB^Uz@)H+Fq#6*xz^WQ%C8FYh2c}ibM$objs+y-d? zrX=r$2HB8GQAT(a-w^I+Es60?fl37;e}5$RjTuFMKXp%mne_VmrD+=0@u#&VHEO>T z0+aDh{lgzr?z>~c5JWEZg`onQ5xvC~Pg`I34~`FcnLIpC<-1wExH5^!-;y8S-GaK$ zqV%<$D)?4;qGGHu8a=-ztvXSqxh#zCt;e8A_h?gwd4CR;I%At`%CO^gi0;$9($Z`nsRqjuU6#in|WCc2vnFl7_u}-ps18Z*4Id%R4g&)zX z=u-}T0Ym3Y-i-H&S?xF}yw?AdonDV+mwfb*odRY)h;UL3);X$Jjcc$Zn&D^A3CtT} z(yDV3RddXi$VJUPVhedH^S0)1&)Bbgt@+Paok?^h;$k*W0Cbh`vG2mpVU2}c99a5HuH!aSi! z`nGbfL^TymSO0$QBNCccZm*uW{Nh09Z~MGCeOOU2RMqHJ-N&DuF-2n_ObxbNZG*JV zbI(4ArNKZ@CUt-@eo_k@7Mxy(MarP*DVP^#5Z;ZCqEYjzxIeI@q|R4zFEvIRGSVU% z$duRe?0xKK+(*?VWjN^l{Is8>%$ zZ+M=HCS<3MQ`&8i7~}*7hNPrD|Jpj|yihO~({IdOBM?%{!ygU%^BJyBmS%6`!UkVo zL^v<&C;4Th7tx1l!)WXNrYFSMljXe=FPsxEl#gW6l0I%9R?<>^G5~ze5H_V;gf+ny zkoSHZ-~~LeKBBjvGOTE0$zT3w3P}2At4ce)1Y^c=mw9(lJ+3FzO|?53ToOlD?jbsQ z5vy<+b*YLnYm1m9*uo+Hv$3$6AsTswxYOo$!QDU1@_I;r+|0PE$m%;+gL_=h`{M0G z<%5f$DRD1rkyN$KcaWOd?Z>Vcr0Itq->o9Q2%tOr{?NT>&{g$V>kWg|J-0^vg*>mq zXDCk~jYn^7od`Ep|5+kxII7RTuS?Tx=nETO{85~G=6slBjlci%kz`5LkHx;b8HlZh zw*1dWnq*D}N{}lP?*^3Sl#PuDO{Q#n_};J|DU39cPe7s2pX@nCXO~n(FReYqJ3s!S zxpR+QJYxy(_V`@?XTfn8#(w-Z6!{lnk#x%5?42|OsX85_8tK`R_Ov3I#G8T%~|m5^dSLk z=E+zY@@x=EdFQ?R+(^!|Odf9!syD1W>9@W&hWlp@K0RyhEXqPgul#0a-Iymp?(Z8+ zedpt^fW(v;4&6%_BXA z4ML%iVq3UBLjtrypnLM(5fbb$$>*yu%nuPX34Rq^>h*W~m(1Af3XeCtwBOBnb(dcg z+c1f(KCz$tT8{k$O(PYvpV-y?HCzAn)o{Gqea*A+gt|&S*q!p*I7C$ro)~UpMuq~z zD|2*bHB0PErq1`Q`F1;cdmrI%ATwI3T;F3jc(Op`_q zG9GZ(b!$5`zCYFbU0gY*arcOL7%Z11HI8N< zcq<&EOTU~%Z3Q#_Ew?K+2p9%*Mv-*1Nf&fk%@LxhKX;1l5O|Iu>j}ovw{mq96>@dX zRyxG|0z=J$nFIqD!E-Q&?67!glaAo1mOtCUh7{Ar?dWVzC&DU-cGcQD zdZs=K!wc!qJbJ4aoRX@L zBRa?Q9N7R5#0tl=(2)H*61@~nW?QcNN)aonJBtDj!>d+B8l-Vjc1vu()AGLsOg;z= z3z>Lgn+88SWz5<$r*2$j5F6$glpX51lvo`8iT|m8vPVVVa|jx z&hfX2>kf%tAM?<=>xP+`#7lZs61$5|7J_%%!KyPj!t#T}j$H#+@?leTQwL&WsN$BN zuXS}6RGLD|V8HiN%M-zT^@+Hmns8IP+?%IVh@_upzIr!I+-a7r=-%NBXw*Op0`LK3 zG5fdG`C@Axy?d+8VQLq(qkUTD+FNVrN5Q|J6R&jh2Lv)Ole+5pGloEZZQ79>m7YGM zSPJ1GRDQtW?r9jb{g**e3Mr>PHrRWagZ|ku4kjL;JOdL~Id05kc*CA+ui@= zieS-e>hskR-1I9Sx7b4i6p>2LP#vgtG6;8vGL>E3$NPQ$J2r~XGQDNg;Sw=& zC}lz+3@Sq%I2q-97R&9|8Ij2^?^DGQK_oiqZS2$!-rzVqn=~d~TS{n&I+svxt4dWO zT?K0)JEx>9E7saW8h!5+MmAkC`g~v*@ z6VKn0>eZdon>BH(O$mACnxk3D?vSlCFFnvZ#+&hUs)Wr!aP{<@|oc^G>bJk59^xhmz!RA%|K_$o)V`D@gVs>@bSmXVID_PQXp znfja8U01+t3V!o{8ZKi~G@#q$KrAH-Ks3$G{Qo}H|N1ijJMsgZDgOmM1O$Fi0>0CX zpbAzXhYbP@PV;~=*nn7eQGjoT2b9nGFNg-PpHT$a@?7JL7I&pmkmclS7#Y#zRYg_`D0h47O z&|%88tXNh8{Yk$@@*HA-B9r#tDkY$>!U#Ie`j1TupjRn@;(ykyyld-zJ{@qm!UG~I zxR#ZxV8CEi5JXV?ANc~bS9*;MYtkTvifc5iynmg!XpIr%SN*R#E?|3&2QVs~N02d=N!1;GdfNGr)gc$|K#-y*M=Ra9B4#cmk-naoQuS*cWnE3C4 F{|nTN-B$nr diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index a4b442974..622ab64a3 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.3-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index 2fe81a7d9..fbd7c5158 100755 --- a/gradlew +++ b/gradlew @@ -82,6 +82,7 @@ esac CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then @@ -129,6 +130,7 @@ fi if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then APP_HOME=`cygpath --path --mixed "$APP_HOME"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` # We build the pattern for arguments to be converted via cygpath diff --git a/gradlew.bat b/gradlew.bat index 62bd9b9cc..5093609d5 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -84,6 +84,7 @@ set CMD_LINE_ARGS=%* set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + @rem Execute Gradle "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% -- 2.34.1 From 774b1123f729123e94461aee1ddc9517ba8d10df Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Mon, 8 Jun 2020 12:07:27 +0300 Subject: [PATCH 027/156] Minor change in grid builders --- .../kmath/linear/LUPDecomposition.kt | 6 +-- .../kotlin/scientifik/kmath/misc/Grids.kt | 44 ++++++++++++------- 2 files changed, 30 insertions(+), 20 deletions(-) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/LUPDecomposition.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/LUPDecomposition.kt index 87e0ef027..75d7da169 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/LUPDecomposition.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/LUPDecomposition.kt @@ -128,14 +128,14 @@ fun , F : Field> GenericMatrixContext.lup( luRow[col] = sum // maintain best permutation choice - if (abs(sum) > largest) { - largest = abs(sum) + if (this@lup.abs(sum) > largest) { + largest = this@lup.abs(sum) max = row } } // Singularity check - if (checkSingular(abs(lu[max, col]))) { + if (checkSingular(this@lup.abs(lu[max, col]))) { error("The matrix is singular") } diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/misc/Grids.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/misc/Grids.kt index 90ce5da68..f040fb8d4 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/misc/Grids.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/misc/Grids.kt @@ -1,5 +1,7 @@ package scientifik.kmath.misc +import kotlin.math.abs + /** * Convert double range to sequence. * @@ -8,28 +10,36 @@ package scientifik.kmath.misc * * If step is negative, the same goes from upper boundary downwards */ -fun ClosedFloatingPointRange.toSequence(step: Double): Sequence = - when { - step == 0.0 -> error("Zero step in double progression") - step > 0 -> sequence { - var current = start - while (current <= endInclusive) { - yield(current) - current += step - } - } - else -> sequence { - var current = endInclusive - while (current >= start) { - yield(current) - current += step - } - } +fun ClosedFloatingPointRange.toSequenceWithStep(step: Double): Sequence = when { + step == 0.0 -> error("Zero step in double progression") + step > 0 -> sequence { + var current = start + while (current <= endInclusive) { + yield(current) + current += step } + } + else -> sequence { + var current = endInclusive + while (current >= start) { + yield(current) + current += step + } + } +} + +/** + * Convert double range to sequence with the fixed number of points + */ +fun ClosedFloatingPointRange.toSequenceWithPoints(numPoints: Int): Sequence { + require(numPoints > 1) { "The number of points should be more than 2" } + return toSequenceWithStep(abs(endInclusive - start) / (numPoints - 1)) +} /** * Convert double range to array of evenly spaced doubles, where the size of array equals [numPoints] */ +@Deprecated("Replace by 'toSequenceWithPoints'") fun ClosedFloatingPointRange.toGrid(numPoints: Int): DoubleArray { if (numPoints < 2) error("Can't create generic grid with less than two points") return DoubleArray(numPoints) { i -> start + (endInclusive - start) / (numPoints - 1) * i } -- 2.34.1 From 65370f93fb0f425d9c9ed0bc6bb5cbd9a049e47b Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Mon, 8 Jun 2020 23:18:08 +0700 Subject: [PATCH 028/156] Make algebra and constants protected, fix tests --- .../scientifik/kmath/expressions/AsmExpressions.kt | 4 ++-- .../kotlin/scientifik/kmath/expressions/AsmTest.kt | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressions.kt b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressions.kt index cc861d363..fc7788589 100644 --- a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressions.kt +++ b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressions.kt @@ -3,8 +3,8 @@ package scientifik.kmath.expressions import scientifik.kmath.operations.* abstract class AsmCompiledExpression internal constructor( - @JvmField private val algebra: Algebra, - @JvmField private val constants: MutableList + @JvmField protected val algebra: Algebra, + @JvmField protected val constants: MutableList ) : Expression { abstract override fun invoke(arguments: Map): T } diff --git a/kmath-asm/src/test/kotlin/scientifik/kmath/expressions/AsmTest.kt b/kmath-asm/src/test/kotlin/scientifik/kmath/expressions/AsmTest.kt index 4370029f6..437fd158f 100644 --- a/kmath-asm/src/test/kotlin/scientifik/kmath/expressions/AsmTest.kt +++ b/kmath-asm/src/test/kotlin/scientifik/kmath/expressions/AsmTest.kt @@ -31,7 +31,7 @@ class AsmTest { @Test fun testSum() = testDoubleExpressionValue( 25.0, - AsmSumExpression(AsmConstantExpression(1.0), AsmVariableExpression("x")), + AsmSumExpression(RealField, AsmConstantExpression(1.0), AsmVariableExpression("x")), mapOf("x" to 24.0) ) @@ -45,28 +45,28 @@ class AsmTest { @Test fun testDiv(): Unit = testDoubleExpressionValue( 0.5, - AsmDivExpression(AsmConstantExpression(1.0), AsmConstantExpression(2.0)), + AsmDivExpression(RealField, AsmConstantExpression(1.0), AsmConstantExpression(2.0)), mapOf() ) @Test fun testProduct(): Unit = testDoubleExpressionValue( 25.0, - AsmProductExpression(AsmVariableExpression("x"), AsmVariableExpression("x")), + AsmProductExpression(RealField,AsmVariableExpression("x"), AsmVariableExpression("x")), mapOf("x" to 5.0) ) @Test fun testCProduct(): Unit = testDoubleExpressionValue( 25.0, - AsmConstProductExpression(AsmVariableExpression("x"), 5.0), + AsmConstProductExpression(RealField,AsmVariableExpression("x"), 5.0), mapOf("x" to 5.0) ) @Test fun testCProductWithOtherTypeNumber(): Unit = testDoubleExpressionValue( 25.0, - AsmConstProductExpression(AsmVariableExpression("x"), 5f), + AsmConstProductExpression(RealField,AsmVariableExpression("x"), 5f), mapOf("x" to 5.0) ) @@ -83,7 +83,7 @@ class AsmTest { @Test fun testCProductWithCustomTypeNumber(): Unit = testDoubleExpressionValue( 0.0, - AsmConstProductExpression(AsmVariableExpression("x"), CustomZero), + AsmConstProductExpression(RealField,AsmVariableExpression("x"), CustomZero), mapOf("x" to 5.0) ) -- 2.34.1 From 2855ad29a409885aad5decd89e4391330d3820c3 Mon Sep 17 00:00:00 2001 From: darksnake Date: Tue, 9 Jun 2020 11:14:47 +0300 Subject: [PATCH 029/156] Remove unused argument in functionalExpressions.kt --- .../scientifik/kmath/expressions/functionalExpressions.kt | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/functionalExpressions.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/functionalExpressions.kt index ad0acbc4a..90694b04a 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/functionalExpressions.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/functionalExpressions.kt @@ -38,8 +38,7 @@ internal class DivExpession(val context: Field, val expr: Expression, v } open class FunctionalExpressionSpace( - val space: Space, - one: T + val space: Space ) : Space>, ExpressionSpace> { override val zero: Expression = ConstantExpression(space.zero) @@ -62,7 +61,7 @@ open class FunctionalExpressionSpace( open class FunctionalExpressionField( val field: Field -) : ExpressionField>, FunctionalExpressionSpace(field, field.one) { +) : ExpressionField>, FunctionalExpressionSpace(field) { override val one: Expression get() = const(this.field.one) -- 2.34.1 From 5dc07febe344baff46c1a512622f8fb085a67a7f Mon Sep 17 00:00:00 2001 From: darksnake Date: Tue, 9 Jun 2020 13:07:40 +0300 Subject: [PATCH 030/156] Expression simplification --- .../commons/expressions/DiffExpression.kt | 5 +- .../kmath/expressions/Expression.kt | 91 ++----------------- .../kmath/expressions/SyntaxTreeNode.kt | 7 -- .../expressions/functionalExpressions.kt | 6 +- .../scientifik/kmath/operations/Algebra.kt | 45 ++++++++- .../kmath/linear/VectorSpaceTest.kt | 0 6 files changed, 56 insertions(+), 98 deletions(-) create mode 100644 kmath-core/src/commonTest/kotlin/scientifik/kmath/linear/VectorSpaceTest.kt diff --git a/kmath-commons/src/main/kotlin/scientifik/kmath/commons/expressions/DiffExpression.kt b/kmath-commons/src/main/kotlin/scientifik/kmath/commons/expressions/DiffExpression.kt index 65e915ef4..8c19395d3 100644 --- a/kmath-commons/src/main/kotlin/scientifik/kmath/commons/expressions/DiffExpression.kt +++ b/kmath-commons/src/main/kotlin/scientifik/kmath/commons/expressions/DiffExpression.kt @@ -2,8 +2,9 @@ package scientifik.kmath.commons.expressions import org.apache.commons.math3.analysis.differentiation.DerivativeStructure import scientifik.kmath.expressions.Expression -import scientifik.kmath.expressions.ExpressionField +import scientifik.kmath.expressions.ExpressionContext import scientifik.kmath.operations.ExtendedField +import scientifik.kmath.operations.Field import kotlin.properties.ReadOnlyProperty import kotlin.reflect.KProperty @@ -112,7 +113,7 @@ fun DiffExpression.derivative(name: String) = derivative(name to 1) /** * A context for [DiffExpression] (not to be confused with [DerivativeStructure]) */ -object DiffExpressionContext : ExpressionField { +object DiffExpressionContext : ExpressionContext, Field { override fun variable(name: String, default: Double?) = DiffExpression { variable(name, default?.const()) } 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 94bc81413..73745f78f 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/Expression.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/Expression.kt @@ -1,7 +1,6 @@ package scientifik.kmath.expressions -import scientifik.kmath.operations.Field -import scientifik.kmath.operations.Space +import scientifik.kmath.operations.Algebra /** * An elementary function that could be invoked on a map of arguments @@ -15,7 +14,7 @@ operator fun Expression.invoke(vararg pairs: Pair): T = invoke /** * A context for expression construction */ -interface ExpressionContext> { +interface ExpressionContext : Algebra { /** * Introduce a variable into expression context */ @@ -25,87 +24,13 @@ interface ExpressionContext> { * A constant expression which does not depend on arguments */ fun const(value: T): E - - fun produce(node: SyntaxTreeNode): E } -interface ExpressionSpace> : Space, ExpressionContext { - - open fun produceSingular(value: String): E = variable(value) - - open fun produceUnary(operation: String, value: E): E { - return when (operation) { - UnaryNode.PLUS_OPERATION -> value - UnaryNode.MINUS_OPERATION -> -value - else -> error("Unary operation $operation is not supported by $this") - } +fun ExpressionContext.produce(node: SyntaxTreeNode): E { + return when (node) { + is NumberNode -> error("Single number nodes are not supported") + is SingularNode -> variable(node.value) + is UnaryNode -> unaryOperation(node.operation, produce(node.value)) + is BinaryNode -> binaryOperation(node.operation, produce(node.left), produce(node.right)) } - - open fun produceBinary(operation: String, left: E, right: E): E { - return when (operation) { - BinaryNode.PLUS_OPERATION -> left + right - BinaryNode.MINUS_OPERATION -> left - right - else -> error("Binary operation $operation is not supported by $this") - } - } - - override fun produce(node: SyntaxTreeNode): E { - return when (node) { - is NumberNode -> error("Single number nodes are not supported") - is SingularNode -> produceSingular(node.value) - is UnaryNode -> produceUnary(node.operation, produce(node.value)) - is BinaryNode -> { - when (node.operation) { - BinaryNode.TIMES_OPERATION -> { - if (node.left is NumberNode) { - return produce(node.right) * node.left.value - } else if (node.right is NumberNode) { - return produce(node.left) * node.right.value - } - } - BinaryNode.DIV_OPERATION -> { - if (node.right is NumberNode) { - return produce(node.left) / node.right.value - } - } - } - produceBinary(node.operation, produce(node.left), produce(node.right)) - } - } - } -} - -interface ExpressionField> : Field, ExpressionSpace { - fun const(value: Double): E = one.times(value) - - override fun produce(node: SyntaxTreeNode): E { - if (node is BinaryNode) { - when (node.operation) { - BinaryNode.PLUS_OPERATION -> { - if (node.left is NumberNode) { - return produce(node.right) + one * node.left.value - } else if (node.right is NumberNode) { - return produce(node.left) + one * node.right.value - } - } - BinaryNode.MINUS_OPERATION -> { - if (node.left is NumberNode) { - return one * node.left.value - produce(node.right) - } else if (node.right is NumberNode) { - return produce(node.left) - one * node.right.value - } - } - } - } - return super.produce(node) - } - - override fun produceBinary(operation: String, left: E, right: E): E { - return when (operation) { - BinaryNode.TIMES_OPERATION -> left * right - BinaryNode.DIV_OPERATION -> left / right - else -> super.produceBinary(operation, left, right) - } - } - } \ No newline at end of file diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/SyntaxTreeNode.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/SyntaxTreeNode.kt index fff3c5bc0..b4038f680 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/SyntaxTreeNode.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/SyntaxTreeNode.kt @@ -8,9 +8,6 @@ data class NumberNode(val value: Number) : SyntaxTreeNode() data class UnaryNode(val operation: String, val value: SyntaxTreeNode) : SyntaxTreeNode() { companion object { - const val PLUS_OPERATION = "+" - const val MINUS_OPERATION = "-" - const val NOT_OPERATION = "!" const val ABS_OPERATION = "abs" const val SIN_OPERATION = "sin" const val COS_OPERATION = "cos" @@ -22,10 +19,6 @@ data class UnaryNode(val operation: String, val value: SyntaxTreeNode) : SyntaxT data class BinaryNode(val operation: String, val left: SyntaxTreeNode, val right: SyntaxTreeNode) : SyntaxTreeNode() { companion object { - const val PLUS_OPERATION = "+" - const val MINUS_OPERATION = "-" - const val TIMES_OPERATION = "*" - const val DIV_OPERATION = "/" //TODO add operations } } diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/functionalExpressions.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/functionalExpressions.kt index 90694b04a..4f38e3a71 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/functionalExpressions.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/functionalExpressions.kt @@ -39,7 +39,7 @@ internal class DivExpession(val context: Field, val expr: Expression, v open class FunctionalExpressionSpace( val space: Space -) : Space>, ExpressionSpace> { +) : Space>, ExpressionContext> { override val zero: Expression = ConstantExpression(space.zero) @@ -61,12 +61,12 @@ open class FunctionalExpressionSpace( open class FunctionalExpressionField( val field: Field -) : ExpressionField>, FunctionalExpressionSpace(field) { +) : Field>, ExpressionContext>, FunctionalExpressionSpace(field) { override val one: Expression get() = const(this.field.one) - override fun const(value: Double): Expression = const(field.run { one*value}) + fun const(value: Double): Expression = const(field.run { one*value}) override fun multiply(a: Expression, b: Expression): Expression = ProductExpression(field, a, b) 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 485185526..b3282e5f3 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Algebra.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Algebra.kt @@ -6,9 +6,12 @@ annotation class KMathContext /** * Marker interface for any algebra */ -interface Algebra +interface Algebra { + fun unaryOperation(operation: String, arg: T): T + fun binaryOperation(operation: String, left: T, right: T): T +} -inline operator fun , R> T.invoke(block: T.() -> R): R = run(block) +inline operator fun , R> A.invoke(block: A.() -> R): R = run(block) /** * Space-like operations without neutral element @@ -24,7 +27,7 @@ interface SpaceOperations : Algebra { */ fun multiply(a: T, k: Number): T - //Operation to be performed in this context + //Operation to be performed in this context. Could be moved to extensions in case of KEEP-176 operator fun T.unaryMinus(): T = multiply(this, -1.0) operator fun T.plus(b: T): T = add(this, b) @@ -32,6 +35,24 @@ interface SpaceOperations : Algebra { 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 + + override fun unaryOperation(operation: String, arg: T): T = when (operation) { + PLUS_OPERATION -> arg + MINUS_OPERATION -> -arg + else -> error("Unary operation $operation not defined in $this") + } + + override fun binaryOperation(operation: String, left: T, right: T): T = when (operation) { + PLUS_OPERATION -> add(left, right) + MINUS_OPERATION -> left - right + else -> error("Binary operation $operation not defined in $this") + } + + companion object { + const val PLUS_OPERATION = "+" + const val MINUS_OPERATION = "-" + const val NOT_OPERATION = "!" + } } @@ -60,6 +81,15 @@ interface RingOperations : SpaceOperations { fun multiply(a: T, b: T): T operator fun T.times(b: T): T = multiply(this, b) + + override fun binaryOperation(operation: String, left: T, right: T): T = when (operation) { + TIMES_OPERATION -> multiply(left, right) + else -> super.binaryOperation(operation, left, right) + } + + companion object{ + const val TIMES_OPERATION = "*" + } } /** @@ -85,6 +115,15 @@ interface FieldOperations : RingOperations { fun divide(a: T, b: T): T operator fun T.div(b: T): T = divide(this, b) + + override fun binaryOperation(operation: String, left: T, right: T): T = when (operation) { + DIV_OPERATION -> divide(left, right) + else -> super.binaryOperation(operation, left, right) + } + + companion object{ + const val DIV_OPERATION = "/" + } } /** diff --git a/kmath-core/src/commonTest/kotlin/scientifik/kmath/linear/VectorSpaceTest.kt b/kmath-core/src/commonTest/kotlin/scientifik/kmath/linear/VectorSpaceTest.kt new file mode 100644 index 000000000..e69de29bb -- 2.34.1 From 33a519c10b10a39d99aad40c210354f6e4909d40 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Tue, 9 Jun 2020 22:22:15 +0700 Subject: [PATCH 031/156] Apply new interfaces structure to ASM Expression Field/Space --- .../kmath/expressions/AsmExpressionSpaces.kt | 11 +++++++---- .../kmath/expressions/FunctionalExpressions.kt | 6 +++--- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressionSpaces.kt b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressionSpaces.kt index 8147bcd5c..38ed00605 100644 --- a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressionSpaces.kt +++ b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressionSpaces.kt @@ -4,8 +4,9 @@ import scientifik.kmath.operations.Field import scientifik.kmath.operations.Space import scientifik.kmath.operations.invoke -open class AsmExpressionSpace(private val space: Space) : Space>, - ExpressionSpace> { +open class AsmExpressionSpace(private val space: Space) : + Space>, + ExpressionContext> { override val zero: AsmExpression = AsmConstantExpression(space.zero) override fun const(value: T): AsmExpression = AsmConstantExpression(value) override fun variable(name: String, default: T?): AsmExpression = AsmVariableExpression(name, default) @@ -17,12 +18,14 @@ open class AsmExpressionSpace(private val space: Space) : Space): AsmExpression = arg - this } -class AsmExpressionField(private val field: Field) : ExpressionField>, +class AsmExpressionField(private val field: Field) : + ExpressionContext>, + Field>, AsmExpressionSpace(field) { override val one: AsmExpression get() = const(this.field.one) - override fun number(value: Number): AsmExpression = const(field { one * value }) + fun number(value: Number): AsmExpression = const(field { one * value }) override fun multiply(a: AsmExpression, b: AsmExpression): AsmExpression = AsmProductExpression(field, a, b) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressions.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressions.kt index 4f38e3a71..97002f664 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressions.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressions.kt @@ -39,7 +39,7 @@ internal class DivExpession(val context: Field, val expr: Expression, v open class FunctionalExpressionSpace( val space: Space -) : Space>, ExpressionContext> { +) : Space>, ExpressionContext> { override val zero: Expression = ConstantExpression(space.zero) @@ -61,12 +61,12 @@ open class FunctionalExpressionSpace( open class FunctionalExpressionField( val field: Field -) : Field>, ExpressionContext>, FunctionalExpressionSpace(field) { +) : Field>, ExpressionContext>, FunctionalExpressionSpace(field) { override val one: Expression get() = const(this.field.one) - fun const(value: Double): Expression = const(field.run { one*value}) + fun number(value: Number): Expression = const(field.run { one * value }) override fun multiply(a: Expression, b: Expression): Expression = ProductExpression(field, a, b) -- 2.34.1 From 1b6a0a13d8308504d7df6b8673b9c321961d518f Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Wed, 10 Jun 2020 00:44:56 +0700 Subject: [PATCH 032/156] Completely rework Expression API to expose direct unaryOperation and binaryOperation, improve ASM API accordingly --- .../kmath/expressions/AsmExpressionSpaces.kt | 38 ------ .../kmath/expressions/AsmExpressions.kt | 123 ------------------ .../kmath/expressions/Optimization.kt | 6 - .../expressions/asm/AsmExpressionSpaces.kt | 74 +++++++++++ .../kmath/expressions/asm/AsmExpressions.kt | 106 +++++++++++++++ .../{ => asm}/AsmGenerationContext.kt | 24 ++-- .../kmath/expressions/{ => asm}/Builders.kt | 14 +- .../expressions/{ => asm}/MethodVisitors.kt | 2 +- .../kmath/expressions/asm/Optimization.kt | 9 ++ .../scientifik/kmath/expressions/AsmTest.kt | 97 +++----------- .../expressions/FunctionalExpressions.kt | 114 +++++++++------- 11 files changed, 301 insertions(+), 306 deletions(-) delete mode 100644 kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressionSpaces.kt delete mode 100644 kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressions.kt delete mode 100644 kmath-asm/src/main/kotlin/scientifik/kmath/expressions/Optimization.kt create mode 100644 kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/AsmExpressionSpaces.kt create mode 100644 kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/AsmExpressions.kt rename kmath-asm/src/main/kotlin/scientifik/kmath/expressions/{ => asm}/AsmGenerationContext.kt (92%) rename kmath-asm/src/main/kotlin/scientifik/kmath/expressions/{ => asm}/Builders.kt (69%) rename kmath-asm/src/main/kotlin/scientifik/kmath/expressions/{ => asm}/MethodVisitors.kt (94%) create mode 100644 kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/Optimization.kt diff --git a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressionSpaces.kt b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressionSpaces.kt deleted file mode 100644 index 38ed00605..000000000 --- a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressionSpaces.kt +++ /dev/null @@ -1,38 +0,0 @@ -package scientifik.kmath.expressions - -import scientifik.kmath.operations.Field -import scientifik.kmath.operations.Space -import scientifik.kmath.operations.invoke - -open class AsmExpressionSpace(private val space: Space) : - Space>, - ExpressionContext> { - override val zero: AsmExpression = AsmConstantExpression(space.zero) - override fun const(value: T): AsmExpression = AsmConstantExpression(value) - override fun variable(name: String, default: T?): AsmExpression = AsmVariableExpression(name, default) - override fun add(a: AsmExpression, b: AsmExpression): AsmExpression = AsmSumExpression(space, a, b) - override fun multiply(a: AsmExpression, k: Number): AsmExpression = AsmConstProductExpression(space, a, k) - operator fun AsmExpression.plus(arg: T): AsmExpression = this + const(arg) - operator fun AsmExpression.minus(arg: T): AsmExpression = this - const(arg) - operator fun T.plus(arg: AsmExpression): AsmExpression = arg + this - operator fun T.minus(arg: AsmExpression): AsmExpression = arg - this -} - -class AsmExpressionField(private val field: Field) : - ExpressionContext>, - Field>, - AsmExpressionSpace(field) { - override val one: AsmExpression - get() = const(this.field.one) - - fun number(value: Number): AsmExpression = const(field { one * value }) - - override fun multiply(a: AsmExpression, b: AsmExpression): AsmExpression = - AsmProductExpression(field, a, b) - - override fun divide(a: AsmExpression, b: AsmExpression): AsmExpression = AsmDivExpression(field, a, b) - operator fun AsmExpression.times(arg: T): AsmExpression = this * const(arg) - operator fun AsmExpression.div(arg: T): AsmExpression = this / const(arg) - operator fun T.times(arg: AsmExpression): AsmExpression = arg * this - operator fun T.div(arg: AsmExpression): AsmExpression = arg / this -} diff --git a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressions.kt b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressions.kt deleted file mode 100644 index fc7788589..000000000 --- a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmExpressions.kt +++ /dev/null @@ -1,123 +0,0 @@ -package scientifik.kmath.expressions - -import scientifik.kmath.operations.* - -abstract class AsmCompiledExpression internal constructor( - @JvmField protected val algebra: Algebra, - @JvmField protected val constants: MutableList -) : Expression { - abstract override fun invoke(arguments: Map): T -} - -interface AsmExpression { - fun tryEvaluate(): T? = null - fun invoke(gen: AsmGenerationContext) -} - -internal class AsmVariableExpression(val name: String, val default: T? = null) : - AsmExpression { - override fun invoke(gen: AsmGenerationContext): Unit = gen.visitLoadFromVariables(name, default) -} - -internal class AsmConstantExpression(val value: T) : - AsmExpression { - override fun tryEvaluate(): T = value - override fun invoke(gen: AsmGenerationContext): Unit = gen.visitLoadFromConstants(value) -} - -internal class AsmSumExpression( - private val algebra: SpaceOperations, - first: AsmExpression, - second: AsmExpression -) : AsmExpression { - private val first: AsmExpression = first.optimize() - private val second: AsmExpression = second.optimize() - - override fun tryEvaluate(): T? = algebra { - (first.tryEvaluate() ?: return@algebra null) + (second.tryEvaluate() ?: return@algebra null) - } - - override fun invoke(gen: AsmGenerationContext) { - gen.visitLoadAlgebra() - first.invoke(gen) - second.invoke(gen) - - gen.visitAlgebraOperation( - owner = AsmGenerationContext.SPACE_OPERATIONS_CLASS, - method = "add", - descriptor = "(L${AsmGenerationContext.OBJECT_CLASS};L${AsmGenerationContext.OBJECT_CLASS};)L${AsmGenerationContext.OBJECT_CLASS};" - ) - } -} - -internal class AsmProductExpression( - private val algebra: RingOperations, - first: AsmExpression, - second: AsmExpression -) : AsmExpression { - private val first: AsmExpression = first.optimize() - private val second: AsmExpression = second.optimize() - - override fun tryEvaluate(): T? = algebra { - (first.tryEvaluate() ?: return@algebra null) * (second.tryEvaluate() ?: return@algebra null) - } - - override fun invoke(gen: AsmGenerationContext) { - gen.visitLoadAlgebra() - first.invoke(gen) - second.invoke(gen) - - gen.visitAlgebraOperation( - owner = AsmGenerationContext.RING_OPERATIONS_CLASS, - method = "multiply", - descriptor = "(L${AsmGenerationContext.OBJECT_CLASS};L${AsmGenerationContext.OBJECT_CLASS};)L${AsmGenerationContext.OBJECT_CLASS};" - ) - } -} - -internal class AsmConstProductExpression( - private val algebra: SpaceOperations, - expr: AsmExpression, - private val const: Number -) : AsmExpression { - private val expr: AsmExpression = expr.optimize() - - override fun tryEvaluate(): T? = algebra { (expr.tryEvaluate() ?: return@algebra null) * const } - - override fun invoke(gen: AsmGenerationContext) { - gen.visitLoadAlgebra() - gen.visitNumberConstant(const) - expr.invoke(gen) - - gen.visitAlgebraOperation( - owner = AsmGenerationContext.SPACE_OPERATIONS_CLASS, - method = "multiply", - descriptor = "(L${AsmGenerationContext.OBJECT_CLASS};L${AsmGenerationContext.NUMBER_CLASS};)L${AsmGenerationContext.OBJECT_CLASS};" - ) - } -} - -internal class AsmDivExpression( - private val algebra: FieldOperations, - expr: AsmExpression, - second: AsmExpression -) : AsmExpression { - private val expr: AsmExpression = expr.optimize() - private val second: AsmExpression = second.optimize() - - override fun tryEvaluate(): T? = algebra { - (expr.tryEvaluate() ?: return@algebra null) / (second.tryEvaluate() ?: return@algebra null) - } - - override fun invoke(gen: AsmGenerationContext) { - gen.visitLoadAlgebra() - expr.invoke(gen) - second.invoke(gen) - - gen.visitAlgebraOperation( - owner = AsmGenerationContext.FIELD_OPERATIONS_CLASS, - method = "divide", - descriptor = "(L${AsmGenerationContext.OBJECT_CLASS};L${AsmGenerationContext.OBJECT_CLASS};)L${AsmGenerationContext.OBJECT_CLASS};" - ) - } -} diff --git a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/Optimization.kt b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/Optimization.kt deleted file mode 100644 index bb7d0476d..000000000 --- a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/Optimization.kt +++ /dev/null @@ -1,6 +0,0 @@ -package scientifik.kmath.expressions - -fun AsmExpression.optimize(): AsmExpression { - val a = tryEvaluate() - return if (a == null) this else AsmConstantExpression(a) -} diff --git a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/AsmExpressionSpaces.kt b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/AsmExpressionSpaces.kt new file mode 100644 index 000000000..17b6dc023 --- /dev/null +++ b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/AsmExpressionSpaces.kt @@ -0,0 +1,74 @@ +package scientifik.kmath.expressions.asm + +import scientifik.kmath.expressions.ExpressionContext +import scientifik.kmath.operations.* + +open class AsmExpressionAlgebra(val algebra: Algebra) : + Algebra>, + ExpressionContext> { + override fun unaryOperation(operation: String, arg: AsmExpression): AsmExpression = + AsmUnaryOperation(algebra, operation, arg) + + override fun binaryOperation(operation: String, left: AsmExpression, right: AsmExpression): AsmExpression = + AsmBinaryOperation(algebra, operation, left, right) + + override fun const(value: T): AsmExpression = AsmConstantExpression(value) + override fun variable(name: String, default: T?): AsmExpression = AsmVariableExpression(name, default) +} + +open class AsmExpressionSpace( + val space: Space +) : AsmExpressionAlgebra(space), Space> { + override fun unaryOperation(operation: String, arg: AsmExpression): AsmExpression = + AsmUnaryOperation(algebra, operation, arg) + + override fun binaryOperation(operation: String, left: AsmExpression, right: AsmExpression): AsmExpression = + AsmBinaryOperation(algebra, operation, left, right) + + override val zero: AsmExpression = AsmConstantExpression(space.zero) + + override fun add(a: AsmExpression, b: AsmExpression): AsmExpression = + AsmBinaryOperation(space, SpaceOperations.PLUS_OPERATION, a, b) + + override fun multiply(a: AsmExpression, k: Number): AsmExpression = AsmConstProductExpression(space, a, k) + operator fun AsmExpression.plus(arg: T) = this + const(arg) + operator fun AsmExpression.minus(arg: T) = this - const(arg) + operator fun T.plus(arg: AsmExpression) = arg + this + operator fun T.minus(arg: AsmExpression) = arg - this +} + +open class AsmExpressionRing(private val ring: Ring) : AsmExpressionSpace(ring), Ring> { + override val one: AsmExpression + get() = const(this.ring.one) + + override fun unaryOperation(operation: String, arg: AsmExpression): AsmExpression = + AsmUnaryOperation(algebra, operation, arg) + + override fun binaryOperation(operation: String, left: AsmExpression, right: AsmExpression): AsmExpression = + AsmBinaryOperation(algebra, operation, left, right) + + fun number(value: Number): AsmExpression = const(ring { one * value }) + + override fun multiply(a: AsmExpression, b: AsmExpression): AsmExpression = + AsmBinaryOperation(space, RingOperations.TIMES_OPERATION, a, b) + + operator fun AsmExpression.times(arg: T) = this * const(arg) + operator fun T.times(arg: AsmExpression) = arg * this +} + +open class AsmExpressionField(private val field: Field) : + AsmExpressionRing(field), + Field> { + + override fun unaryOperation(operation: String, arg: AsmExpression): AsmExpression = + AsmUnaryOperation(algebra, operation, arg) + + override fun binaryOperation(operation: String, left: AsmExpression, right: AsmExpression): AsmExpression = + AsmBinaryOperation(algebra, operation, left, right) + + override fun divide(a: AsmExpression, b: AsmExpression): AsmExpression = + AsmBinaryOperation(space, FieldOperations.DIV_OPERATION, a, b) + + operator fun AsmExpression.div(arg: T) = this / const(arg) + operator fun T.div(arg: AsmExpression) = arg / this +} diff --git a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/AsmExpressions.kt b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/AsmExpressions.kt new file mode 100644 index 000000000..d7d655d6e --- /dev/null +++ b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/AsmExpressions.kt @@ -0,0 +1,106 @@ +package scientifik.kmath.expressions.asm + +import scientifik.kmath.expressions.Expression +import scientifik.kmath.operations.Algebra +import scientifik.kmath.operations.Space +import scientifik.kmath.operations.invoke + +interface AsmExpression { + fun tryEvaluate(): T? = null + fun invoke(gen: AsmGenerationContext) +} + +internal class AsmUnaryOperation(private val context: Algebra, private val name: String, expr: AsmExpression) : + AsmExpression { + private val expr: AsmExpression = expr.optimize() + + override fun tryEvaluate(): T? = context { + unaryOperation( + name, + expr.tryEvaluate() ?: return@context null + ) + } + + override fun invoke(gen: AsmGenerationContext) { + gen.visitLoadAlgebra() + gen.visitStringConstant(name) + expr.invoke(gen) + + gen.visitAlgebraOperation( + owner = AsmGenerationContext.ALGEBRA_CLASS, + method = "unaryOperation", + descriptor = "(L${AsmGenerationContext.STRING_CLASS};" + + "L${AsmGenerationContext.OBJECT_CLASS};)" + + "L${AsmGenerationContext.OBJECT_CLASS};" + ) + } +} + +internal class AsmBinaryOperation( + private val context: Algebra, + private val name: String, + first: AsmExpression, + second: AsmExpression +) : AsmExpression { + private val first: AsmExpression = first.optimize() + private val second: AsmExpression = second.optimize() + + override fun tryEvaluate(): T? = context { + binaryOperation( + name, + first.tryEvaluate() ?: return@context null, + second.tryEvaluate() ?: return@context null + ) + } + + override fun invoke(gen: AsmGenerationContext) { + gen.visitLoadAlgebra() + gen.visitStringConstant(name) + first.invoke(gen) + second.invoke(gen) + + gen.visitAlgebraOperation( + owner = AsmGenerationContext.ALGEBRA_CLASS, + method = "binaryOperation", + descriptor = "(L${AsmGenerationContext.STRING_CLASS};" + + "L${AsmGenerationContext.OBJECT_CLASS};" + + "L${AsmGenerationContext.OBJECT_CLASS};)" + + "L${AsmGenerationContext.OBJECT_CLASS};" + ) + } +} + +internal class AsmVariableExpression(private val name: String, private val default: T? = null) : AsmExpression { + override fun invoke(gen: AsmGenerationContext): Unit = gen.visitLoadFromVariables(name, default) +} + +internal class AsmConstantExpression(private val value: T) : AsmExpression { + override fun tryEvaluate(): T = value + override fun invoke(gen: AsmGenerationContext): Unit = gen.visitLoadFromConstants(value) +} + +internal class AsmConstProductExpression(private val context: Space, expr: AsmExpression, private val const: Number) : + AsmExpression { + private val expr: AsmExpression = expr.optimize() + + override fun tryEvaluate(): T? = context { (expr.tryEvaluate() ?: return@context null) * const } + + override fun invoke(gen: AsmGenerationContext) { + gen.visitLoadAlgebra() + gen.visitNumberConstant(const) + expr.invoke(gen) + + gen.visitAlgebraOperation( + owner = AsmGenerationContext.SPACE_OPERATIONS_CLASS, + method = "multiply", + descriptor = "(L${AsmGenerationContext.OBJECT_CLASS};L${AsmGenerationContext.NUMBER_CLASS};)L${AsmGenerationContext.OBJECT_CLASS};" + ) + } +} + +internal abstract class FunctionalCompiledExpression internal constructor( + @JvmField protected val algebra: Algebra, + @JvmField protected val constants: MutableList +) : Expression { + abstract override fun invoke(arguments: Map): T +} diff --git a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmGenerationContext.kt b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/AsmGenerationContext.kt similarity index 92% rename from kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmGenerationContext.kt rename to kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/AsmGenerationContext.kt index cedd5c0fd..3e47da5e3 100644 --- a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/AsmGenerationContext.kt +++ b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/AsmGenerationContext.kt @@ -1,4 +1,4 @@ -package scientifik.kmath.expressions +package scientifik.kmath.expressions.asm import org.objectweb.asm.ClassWriter import org.objectweb.asm.Label @@ -33,15 +33,15 @@ class AsmGenerationContext( private val invokeMethodVisitor: MethodVisitor private val invokeL0: Label private lateinit var invokeL1: Label - private var generatedInstance: AsmCompiledExpression? = null + private var generatedInstance: FunctionalCompiledExpression? = null init { asmCompiledClassWriter.visit( Opcodes.V1_8, Opcodes.ACC_PUBLIC or Opcodes.ACC_FINAL or Opcodes.ACC_SUPER, slashesClassName, - "L$ASM_COMPILED_EXPRESSION_CLASS;", - ASM_COMPILED_EXPRESSION_CLASS, + "L$FUNCTIONAL_COMPILED_EXPRESSION_CLASS;", + FUNCTIONAL_COMPILED_EXPRESSION_CLASS, arrayOf() ) @@ -58,7 +58,7 @@ class AsmGenerationContext( visitMethodInsn( Opcodes.INVOKESPECIAL, - ASM_COMPILED_EXPRESSION_CLASS, + FUNCTIONAL_COMPILED_EXPRESSION_CLASS, "", "(L$ALGEBRA_CLASS;L$LIST_CLASS;)V", false @@ -103,7 +103,7 @@ class AsmGenerationContext( @PublishedApi @Suppress("UNCHECKED_CAST") - internal fun generate(): AsmCompiledExpression { + internal fun generate(): FunctionalCompiledExpression { generatedInstance?.let { return it } invokeMethodVisitor.run { @@ -170,7 +170,7 @@ class AsmGenerationContext( .defineClass(className, asmCompiledClassWriter.toByteArray()) .constructors .first() - .newInstance(algebra, constants) as AsmCompiledExpression + .newInstance(algebra, constants) as FunctionalCompiledExpression generatedInstance = new return new @@ -245,7 +245,7 @@ class AsmGenerationContext( invokeMethodVisitor.visitFieldInsn( Opcodes.GETFIELD, - ASM_COMPILED_EXPRESSION_CLASS, "algebra", "L$ALGEBRA_CLASS;" + FUNCTIONAL_COMPILED_EXPRESSION_CLASS, "algebra", "L$ALGEBRA_CLASS;" ) invokeMethodVisitor.visitTypeInsn(Opcodes.CHECKCAST, T_ALGEBRA_CLASS) @@ -259,6 +259,10 @@ class AsmGenerationContext( private fun visitCastToT(): Unit = invokeMethodVisitor.visitTypeInsn(Opcodes.CHECKCAST, T_CLASS) + internal fun visitStringConstant(string: String) { + invokeMethodVisitor.visitLdcInsn(string) + } + internal companion object { private val SIGNATURE_LETTERS = mapOf( java.lang.Byte::class.java to "B", @@ -269,15 +273,13 @@ class AsmGenerationContext( java.lang.Double::class.java to "D" ) - internal const val ASM_COMPILED_EXPRESSION_CLASS = "scientifik/kmath/expressions/AsmCompiledExpression" + internal const val FUNCTIONAL_COMPILED_EXPRESSION_CLASS = "scientifik/kmath/expressions/asm/FunctionalCompiledExpression" internal const val LIST_CLASS = "java/util/List" internal const val MAP_CLASS = "java/util/Map" internal const val OBJECT_CLASS = "java/lang/Object" internal const val ALGEBRA_CLASS = "scientifik/kmath/operations/Algebra" internal const val SPACE_OPERATIONS_CLASS = "scientifik/kmath/operations/SpaceOperations" internal const val STRING_CLASS = "java/lang/String" - internal const val FIELD_OPERATIONS_CLASS = "scientifik/kmath/operations/FieldOperations" - internal const val RING_OPERATIONS_CLASS = "scientifik/kmath/operations/RingOperations" internal const val NUMBER_CLASS = "java/lang/Number" } } diff --git a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/Builders.kt b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/Builders.kt similarity index 69% rename from kmath-asm/src/main/kotlin/scientifik/kmath/expressions/Builders.kt rename to kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/Builders.kt index f8b4afd5b..d55555567 100644 --- a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/Builders.kt +++ b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/Builders.kt @@ -1,7 +1,9 @@ -package scientifik.kmath.expressions +package scientifik.kmath.expressions.asm +import scientifik.kmath.expressions.Expression import scientifik.kmath.operations.Algebra import scientifik.kmath.operations.Field +import scientifik.kmath.operations.Ring import scientifik.kmath.operations.Space @PublishedApi @@ -25,11 +27,21 @@ inline fun asm(i: I, algebra: Algebra, block: I.() -> AsmExpre return ctx.generate() } +inline fun asmAlgebra( + algebra: Algebra, + block: AsmExpressionAlgebra.() -> AsmExpression +): Expression = asm(AsmExpressionAlgebra(algebra), algebra, block) + inline fun asmSpace( algebra: Space, block: AsmExpressionSpace.() -> AsmExpression ): Expression = asm(AsmExpressionSpace(algebra), algebra, block) +inline fun asmRing( + algebra: Ring, + block: AsmExpressionRing.() -> AsmExpression +): Expression = asm(AsmExpressionRing(algebra), algebra, block) + inline fun asmField( algebra: Field, block: AsmExpressionField.() -> AsmExpression diff --git a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/MethodVisitors.kt b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/MethodVisitors.kt similarity index 94% rename from kmath-asm/src/main/kotlin/scientifik/kmath/expressions/MethodVisitors.kt rename to kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/MethodVisitors.kt index 9cdb0672f..9f697ab6d 100644 --- a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/MethodVisitors.kt +++ b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/MethodVisitors.kt @@ -1,4 +1,4 @@ -package scientifik.kmath.expressions +package scientifik.kmath.expressions.asm import org.objectweb.asm.MethodVisitor import org.objectweb.asm.Opcodes.* diff --git a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/Optimization.kt b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/Optimization.kt new file mode 100644 index 000000000..db57a690c --- /dev/null +++ b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/Optimization.kt @@ -0,0 +1,9 @@ +package scientifik.kmath.expressions.asm + +import scientifik.kmath.expressions.asm.AsmConstantExpression +import scientifik.kmath.expressions.asm.AsmExpression + +fun AsmExpression.optimize(): AsmExpression { + val a = tryEvaluate() + return if (a == null) this else AsmConstantExpression(a) +} diff --git a/kmath-asm/src/test/kotlin/scientifik/kmath/expressions/AsmTest.kt b/kmath-asm/src/test/kotlin/scientifik/kmath/expressions/AsmTest.kt index 437fd158f..55c240cef 100644 --- a/kmath-asm/src/test/kotlin/scientifik/kmath/expressions/AsmTest.kt +++ b/kmath-asm/src/test/kotlin/scientifik/kmath/expressions/AsmTest.kt @@ -1,103 +1,40 @@ package scientifik.kmath.expressions -import scientifik.kmath.operations.Algebra +import scientifik.kmath.expressions.asm.AsmExpression +import scientifik.kmath.expressions.asm.AsmExpressionField +import scientifik.kmath.expressions.asm.asmField import scientifik.kmath.operations.RealField import kotlin.test.Test import kotlin.test.assertEquals class AsmTest { - private fun testExpressionValue( - expectedValue: T, - expr: AsmExpression, - arguments: Map, - algebra: Algebra, - clazz: Class<*> - ): Unit = assertEquals( - expectedValue, AsmGenerationContext(clazz, algebra, "TestAsmCompiled") - .also(expr::invoke) - .generate() - .invoke(arguments) - ) - - @Suppress("UNCHECKED_CAST") - private fun testDoubleExpressionValue( - expectedValue: Double, - expr: AsmExpression, - arguments: Map, - algebra: Algebra = RealField, - clazz: Class = java.lang.Double::class.java as Class - ): Unit = testExpressionValue(expectedValue, expr, arguments, algebra, clazz) + private fun testDoubleExpression( + expected: Double?, + arguments: Map = emptyMap(), + block: AsmExpressionField.() -> AsmExpression + ): Unit = assertEquals(expected = expected, actual = asmField(RealField, block)(arguments)) @Test - fun testSum() = testDoubleExpressionValue( - 25.0, - AsmSumExpression(RealField, AsmConstantExpression(1.0), AsmVariableExpression("x")), - mapOf("x" to 24.0) - ) + fun testConstantsSum() = testDoubleExpression(16.0) { const(8.0) + 8.0 } @Test - fun testConst(): Unit = testDoubleExpressionValue( - 123.0, - AsmConstantExpression(123.0), - mapOf() - ) + fun testVarsSum() = testDoubleExpression(1000.0, mapOf("x" to 500.0)) { variable("x") + 500.0 } @Test - fun testDiv(): Unit = testDoubleExpressionValue( - 0.5, - AsmDivExpression(RealField, AsmConstantExpression(1.0), AsmConstantExpression(2.0)), - mapOf() - ) + fun testProduct() = testDoubleExpression(24.0) { const(4.0) * const(6.0) } @Test - fun testProduct(): Unit = testDoubleExpressionValue( - 25.0, - AsmProductExpression(RealField,AsmVariableExpression("x"), AsmVariableExpression("x")), - mapOf("x" to 5.0) - ) + fun testConstantProduct() = testDoubleExpression(984.0) { const(8.0) * 123 } @Test - fun testCProduct(): Unit = testDoubleExpressionValue( - 25.0, - AsmConstProductExpression(RealField,AsmVariableExpression("x"), 5.0), - mapOf("x" to 5.0) - ) + fun testSubtraction() = testDoubleExpression(2.0) { const(4.0) - 2.0 } @Test - fun testCProductWithOtherTypeNumber(): Unit = testDoubleExpressionValue( - 25.0, - AsmConstProductExpression(RealField,AsmVariableExpression("x"), 5f), - mapOf("x" to 5.0) - ) - - object CustomZero : Number() { - override fun toByte(): Byte = 0 - override fun toChar(): Char = 0.toChar() - override fun toDouble(): Double = 0.0 - override fun toFloat(): Float = 0f - override fun toInt(): Int = 0 - override fun toLong(): Long = 0L - override fun toShort(): Short = 0 - } + fun testDivision() = testDoubleExpression(64.0) { const(128.0) / 2 } @Test - fun testCProductWithCustomTypeNumber(): Unit = testDoubleExpressionValue( - 0.0, - AsmConstProductExpression(RealField,AsmVariableExpression("x"), CustomZero), - mapOf("x" to 5.0) - ) + fun testDirectCall() = testDoubleExpression(4096.0) { binaryOperation("*", const(64.0), const(64.0)) } - @Test - fun testVar(): Unit = testDoubleExpressionValue( - 10000.0, - AsmVariableExpression("x"), - mapOf("x" to 10000.0) - ) - - @Test - fun testVarWithDefault(): Unit = testDoubleExpressionValue( - 10000.0, - AsmVariableExpression("x", 10000.0), - mapOf() - ) +// @Test +// fun testSine() = testDoubleExpression(0.0) { unaryOperation("sin", const(PI)) } } diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressions.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressions.kt index 97002f664..b36ea8b52 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressions.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressions.kt @@ -1,80 +1,102 @@ package scientifik.kmath.expressions -import scientifik.kmath.operations.Field -import scientifik.kmath.operations.Ring -import scientifik.kmath.operations.Space +import scientifik.kmath.operations.* -internal class VariableExpression(val name: String, val default: T? = null) : Expression { +internal class FunctionalUnaryOperation(val context: Algebra, val name: String, val expr: Expression) : + Expression { + override fun invoke(arguments: Map): T = context.unaryOperation(name, expr.invoke(arguments)) +} + +internal class FunctionalBinaryOperation( + val context: Algebra, + val name: String, + val first: Expression, + val second: Expression +) : Expression { + override fun invoke(arguments: Map): T = + context.binaryOperation(name, first.invoke(arguments), second.invoke(arguments)) +} + +internal class FunctionalVariableExpression(val name: String, val default: T? = null) : Expression { override fun invoke(arguments: Map): T = arguments[name] ?: default ?: error("Parameter not found: $name") } -internal class ConstantExpression(val value: T) : Expression { +internal class FunctionalConstantExpression(val value: T) : Expression { override fun invoke(arguments: Map): T = value } -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: Ring, 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: Space, val expr: Expression, val const: Number) : +internal class FunctionalConstProductExpression(val context: Space, val expr: Expression, val const: Number) : 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 { - override fun invoke(arguments: Map): T = context.divide(expr.invoke(arguments), second.invoke(arguments)) +open class FunctionalExpressionAlgebra(val algebra: Algebra) : + Algebra>, + ExpressionContext> { + override fun unaryOperation(operation: String, arg: Expression): Expression = + FunctionalUnaryOperation(algebra, operation, arg) + + override fun binaryOperation(operation: String, left: Expression, right: Expression): Expression = + FunctionalBinaryOperation(algebra, operation, left, right) + + override fun const(value: T): Expression = FunctionalConstantExpression(value) + override fun variable(name: String, default: T?): Expression = FunctionalVariableExpression(name, default) } -open class FunctionalExpressionSpace( - val space: Space -) : Space>, ExpressionContext> { +open class FunctionalExpressionSpace(val space: Space) : + FunctionalExpressionAlgebra(space), + Space> { + override fun unaryOperation(operation: String, arg: Expression): Expression = + FunctionalUnaryOperation(algebra, operation, arg) - override val zero: Expression = ConstantExpression(space.zero) + override fun binaryOperation(operation: String, left: Expression, right: Expression): Expression = + FunctionalBinaryOperation(algebra, operation, left, right) - override fun const(value: T): Expression = ConstantExpression(value) - - override fun variable(name: String, default: T?): Expression = VariableExpression(name, default) - - override fun add(a: Expression, b: Expression): Expression = SumExpression(space, a, b) - - override fun multiply(a: Expression, k: Number): Expression = ConstProductExpession(space, a, k) + override val zero: Expression = FunctionalConstantExpression(space.zero) + override fun add(a: Expression, b: Expression): Expression = + FunctionalBinaryOperation(space, SpaceOperations.PLUS_OPERATION, a, b) + override fun multiply(a: Expression, k: Number): Expression = FunctionalConstProductExpression(space, a, k) operator fun Expression.plus(arg: T) = this + const(arg) operator fun Expression.minus(arg: T) = this - const(arg) - operator fun T.plus(arg: Expression) = arg + this operator fun T.minus(arg: Expression) = arg - this } -open class FunctionalExpressionField( - val field: Field -) : Field>, ExpressionContext>, FunctionalExpressionSpace(field) { - +open class FunctionalExpressionRing(val ring: Ring) : FunctionalExpressionSpace(ring), Ring> { override val one: Expression - get() = const(this.field.one) + get() = const(this.ring.one) - fun number(value: Number): Expression = const(field.run { one * value }) + override fun unaryOperation(operation: String, arg: Expression): Expression = + FunctionalUnaryOperation(algebra, operation, arg) - override fun multiply(a: Expression, b: Expression): Expression = ProductExpression(field, a, b) + override fun binaryOperation(operation: String, left: Expression, right: Expression): Expression = + FunctionalBinaryOperation(algebra, operation, left, right) - override fun divide(a: Expression, b: Expression): Expression = DivExpession(field, a, b) + fun number(value: Number): Expression = const(ring { one * value }) + + override fun multiply(a: Expression, b: Expression): Expression = + FunctionalBinaryOperation(space, RingOperations.TIMES_OPERATION, a, b) operator fun Expression.times(arg: T) = this * const(arg) - operator fun Expression.div(arg: T) = this / const(arg) - operator fun T.times(arg: Expression) = arg * this +} + +open class FunctionalExpressionField(val field: Field) : + FunctionalExpressionRing(field), + Field> { + + override fun unaryOperation(operation: String, arg: Expression): Expression = + FunctionalUnaryOperation(algebra, operation, arg) + + override fun binaryOperation(operation: String, left: Expression, right: Expression): Expression = + FunctionalBinaryOperation(algebra, operation, left, right) + + override fun divide(a: Expression, b: Expression): Expression = + FunctionalBinaryOperation(space, FieldOperations.DIV_OPERATION, a, b) + + operator fun Expression.div(arg: T) = this / const(arg) operator fun T.div(arg: Expression) = arg / this -} \ No newline at end of file +} -- 2.34.1 From 2e5c13aea99b8ecc82c867ca859353df80ea194c Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Wed, 10 Jun 2020 02:05:13 +0700 Subject: [PATCH 033/156] Improve support of string-identified operations API, rework trigonometric operations algebra part: introduce inverse trigonometric operations, rename tg to tan --- .../commons/expressions/DiffExpression.kt | 7 +- .../scientifik/kmath/operations/Complex.kt | 8 +- .../kmath/operations/NumberAlgebra.kt | 12 +- .../kmath/operations/OptionalOperations.kt | 34 +++-- .../kmath/structures/ComplexNDField.kt | 7 ++ .../kmath/structures/ExtendedNDField.kt | 2 +- .../kmath/structures/RealBufferField.kt | 117 ++++++++++++------ .../kmath/structures/RealNDField.kt | 7 ++ 8 files changed, 140 insertions(+), 54 deletions(-) diff --git a/kmath-commons/src/main/kotlin/scientifik/kmath/commons/expressions/DiffExpression.kt b/kmath-commons/src/main/kotlin/scientifik/kmath/commons/expressions/DiffExpression.kt index 8c19395d3..88d32378e 100644 --- a/kmath-commons/src/main/kotlin/scientifik/kmath/commons/expressions/DiffExpression.kt +++ b/kmath-commons/src/main/kotlin/scientifik/kmath/commons/expressions/DiffExpression.kt @@ -59,8 +59,10 @@ class DerivativeStructureField( override fun divide(a: DerivativeStructure, b: DerivativeStructure): DerivativeStructure = a.divide(b) override fun sin(arg: DerivativeStructure): DerivativeStructure = arg.sin() - override fun cos(arg: DerivativeStructure): DerivativeStructure = arg.cos() + override fun asin(arg: DerivativeStructure): DerivativeStructure = arg.asin() + override fun acos(arg: DerivativeStructure): DerivativeStructure = arg.acos() + override fun atan(arg: DerivativeStructure): DerivativeStructure = arg.atan() override fun power(arg: DerivativeStructure, pow: Number): DerivativeStructure = when (pow) { is Double -> arg.pow(pow) @@ -136,6 +138,3 @@ object DiffExpressionContext : ExpressionContext, Field< override fun divide(a: DiffExpression, b: DiffExpression) = DiffExpression { a.function(this) / b.function(this) } } - - - 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 6c529f55e..ea9425bef 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Complex.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Complex.kt @@ -8,6 +8,8 @@ import scientifik.memory.MemorySpec import scientifik.memory.MemoryWriter import kotlin.math.* +private val PI_DIV_2 = Complex(PI / 2, 0) + /** * A field for complex numbers */ @@ -30,9 +32,11 @@ object ComplexField : ExtendedFieldOperations, Field { return Complex((a.re * b.re + a.im * b.im) / norm, (a.re * b.im - a.im * b.re) / norm) } - override fun sin(arg: Complex): Complex = i / 2 * (exp(-i * arg) - exp(i * arg)) - + override fun sin(arg: Complex): Complex = i * (exp(-i * arg) - exp(i * arg)) / 2 override fun cos(arg: Complex): Complex = (exp(-i * arg) + exp(i * arg)) / 2 + override fun asin(arg: Complex): Complex = -i * ln(sqrt(one - arg pow 2) + i * arg) + override fun acos(arg: Complex): Complex = PI_DIV_2 + i * ln(sqrt(one - arg pow 2) + i * arg) + override fun atan(arg: Complex): Complex = i * (ln(one - i * arg) - ln(one + i * arg)) / 2 override fun power(arg: Complex, pow: Number): Complex = arg.r.pow(pow.toDouble()) * (cos(pow.toDouble() * arg.theta) + i * sin(pow.toDouble() * arg.theta)) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/NumberAlgebra.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/NumberAlgebra.kt index 9639e4c28..3d942beac 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/NumberAlgebra.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/NumberAlgebra.kt @@ -8,7 +8,7 @@ import kotlin.math.pow as kpow */ interface ExtendedFieldOperations : FieldOperations, - TrigonometricOperations, + InverseTrigonometricOperations, PowerOperations, ExponentialOperations @@ -44,6 +44,10 @@ object RealField : ExtendedField, Norm { override inline fun sin(arg: Double) = kotlin.math.sin(arg) override inline fun cos(arg: Double) = kotlin.math.cos(arg) + override inline fun tan(arg: Double): Double = kotlin.math.tan(arg) + override inline fun acos(arg: Double): Double = kotlin.math.acos(arg) + override inline fun asin(arg: Double): Double = kotlin.math.asin(arg) + override inline fun atan(arg: Double): Double = kotlin.math.atan(arg) override inline fun power(arg: Double, pow: Number) = arg.kpow(pow.toDouble()) @@ -75,6 +79,10 @@ object FloatField : ExtendedField, Norm { override inline fun sin(arg: Float) = kotlin.math.sin(arg) override inline fun cos(arg: Float) = kotlin.math.cos(arg) + override inline fun tan(arg: Float) = kotlin.math.tan(arg) + override inline fun acos(arg: Float) = kotlin.math.acos(arg) + override inline fun asin(arg: Float) = kotlin.math.asin(arg) + override inline fun atan(arg: Float) = kotlin.math.atan(arg) override inline fun power(arg: Float, pow: Number) = arg.pow(pow.toFloat()) @@ -180,4 +188,4 @@ object LongRing : Ring, Norm { override inline fun Long.minus(b: Long) = (this - b) override inline fun Long.times(b: Long) = (this * b) -} \ No newline at end of file +} 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 bd83932e7..bbd7a110e 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/OptionalOperations.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/OptionalOperations.kt @@ -13,16 +13,33 @@ package scientifik.kmath.operations interface TrigonometricOperations : FieldOperations { fun sin(arg: T): T fun cos(arg: T): T + fun tan(arg: T): T = sin(arg) / cos(arg) - fun tg(arg: T): T = sin(arg) / cos(arg) + companion object { + const val SIN_OPERATION = "sin" + const val COS_OPERATION = "cos" + const val TAN_OPERATION = "tan" + } +} - fun ctg(arg: T): T = cos(arg) / sin(arg) +interface InverseTrigonometricOperations : TrigonometricOperations { + fun asin(arg: T): T + fun acos(arg: T): T + fun atan(arg: T): T + + companion object { + const val ASIN_OPERATION = "asin" + const val ACOS_OPERATION = "acos" + const val ATAN_OPERATION = "atan" + } } 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 >> tan(arg: T): T = arg.context.tan(arg) +fun >> asin(arg: T): T = arg.context.asin(arg) +fun >> acos(arg: T): T = arg.context.acos(arg) +fun >> atan(arg: T): T = arg.context.atan(arg) /* Power and roots */ @@ -32,8 +49,11 @@ fun >> ctg(arg: T): T = arg.conte interface PowerOperations : Algebra { fun power(arg: T, pow: Number): T fun sqrt(arg: T) = power(arg, 0.5) - infix fun T.pow(pow: Number) = power(this, pow) + + companion object { + const val SQRT_OPERATION = "sqrt" + } } infix fun >> T.pow(power: Double): T = context.power(this, power) @@ -42,7 +62,7 @@ fun >> sqr(arg: T): T = arg pow 2.0 /* Exponential */ -interface ExponentialOperations: Algebra { +interface ExponentialOperations : Algebra { fun exp(arg: T): T fun ln(arg: T): T } @@ -54,4 +74,4 @@ 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) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ComplexNDField.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ComplexNDField.kt index a79366a99..c7e672c28 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ComplexNDField.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ComplexNDField.kt @@ -79,6 +79,13 @@ class ComplexNDField(override val shape: IntArray) : override fun cos(arg: NDBuffer) = map(arg) { cos(it) } + override fun tan(arg: NDBuffer): NDBuffer = map(arg) { tan(it) } + + override fun asin(arg: NDBuffer): NDBuffer = map(arg) { asin(it) } + + override fun acos(arg: NDBuffer): NDBuffer = map(arg) {acos(it)} + + override fun atan(arg: NDBuffer): NDBuffer = map(arg) {atan(it)} } 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 3437644ff..c986ff011 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ExtendedNDField.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ExtendedNDField.kt @@ -4,7 +4,7 @@ import scientifik.kmath.operations.* interface ExtendedNDField> : NDField, - TrigonometricOperations, + InverseTrigonometricOperations, PowerOperations, ExponentialOperations where F : ExtendedFieldOperations, F : Field diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/RealBufferField.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/RealBufferField.kt index 88c8c29db..2fb6d15d4 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/RealBufferField.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/RealBufferField.kt @@ -9,8 +9,10 @@ import kotlin.math.* * A simple field over linear buffers of [Double] */ object RealBufferFieldOperations : ExtendedFieldOperations> { + override fun add(a: Buffer, b: Buffer): DoubleBuffer { require(b.size == a.size) { "The size of the first buffer ${a.size} should be the same as for second one: ${b.size} " } + return if (a is DoubleBuffer && b is DoubleBuffer) { val aArray = a.array val bArray = b.array @@ -22,6 +24,7 @@ object RealBufferFieldOperations : ExtendedFieldOperations> { override fun multiply(a: Buffer, k: Number): DoubleBuffer { val kValue = k.toDouble() + return if (a is DoubleBuffer) { val aArray = a.array DoubleBuffer(DoubleArray(a.size) { aArray[it] * kValue }) @@ -32,6 +35,7 @@ object RealBufferFieldOperations : ExtendedFieldOperations> { override fun multiply(a: Buffer, b: Buffer): DoubleBuffer { require(b.size == a.size) { "The size of the first buffer ${a.size} should be the same as for second one: ${b.size} " } + return if (a is DoubleBuffer && b is DoubleBuffer) { val aArray = a.array val bArray = b.array @@ -43,6 +47,7 @@ object RealBufferFieldOperations : ExtendedFieldOperations> { override fun divide(a: Buffer, b: Buffer): DoubleBuffer { require(b.size == a.size) { "The size of the first buffer ${a.size} should be the same as for second one: ${b.size} " } + return if (a is DoubleBuffer && b is DoubleBuffer) { val aArray = a.array val bArray = b.array @@ -52,49 +57,67 @@ object RealBufferFieldOperations : ExtendedFieldOperations> { } } - override fun sin(arg: Buffer): DoubleBuffer { - return if (arg is DoubleBuffer) { - val array = arg.array - DoubleBuffer(DoubleArray(arg.size) { sin(array[it]) }) - } else { - DoubleBuffer(DoubleArray(arg.size) { sin(arg[it]) }) - } + override fun sin(arg: Buffer): DoubleBuffer = if (arg is DoubleBuffer) { + val array = arg.array + DoubleBuffer(DoubleArray(arg.size) { sin(array[it]) }) + } else { + DoubleBuffer(DoubleArray(arg.size) { sin(arg[it]) }) } - override fun cos(arg: Buffer): DoubleBuffer { - return if (arg is DoubleBuffer) { - val array = arg.array - DoubleBuffer(DoubleArray(arg.size) { cos(array[it]) }) - } else { - DoubleBuffer(DoubleArray(arg.size) { cos(arg[it]) }) - } + override fun cos(arg: Buffer): DoubleBuffer = if (arg is DoubleBuffer) { + val array = arg.array + DoubleBuffer(DoubleArray(arg.size) { cos(array[it]) }) + } else { + DoubleBuffer(DoubleArray(arg.size) { cos(arg[it]) }) } - override fun power(arg: Buffer, pow: Number): DoubleBuffer { - return if (arg is DoubleBuffer) { - val array = arg.array - DoubleBuffer(DoubleArray(arg.size) { array[it].pow(pow.toDouble()) }) - } else { - DoubleBuffer(DoubleArray(arg.size) { arg[it].pow(pow.toDouble()) }) - } + override fun tan(arg: Buffer): DoubleBuffer = if (arg is DoubleBuffer) { + val array = arg.array + DoubleBuffer(DoubleArray(arg.size) { tan(array[it]) }) + } else { + DoubleBuffer(DoubleArray(arg.size) { tan(arg[it]) }) } - override fun exp(arg: Buffer): DoubleBuffer { - return if (arg is DoubleBuffer) { - val array = arg.array - DoubleBuffer(DoubleArray(arg.size) { exp(array[it]) }) - } else { - DoubleBuffer(DoubleArray(arg.size) { exp(arg[it]) }) - } + override fun asin(arg: Buffer): DoubleBuffer = if (arg is DoubleBuffer) { + val array = arg.array + DoubleBuffer(DoubleArray(arg.size) { asin(array[it]) }) + } else { + DoubleBuffer(DoubleArray(arg.size) { asin(arg[it]) }) } - override fun ln(arg: Buffer): DoubleBuffer { - return if (arg is DoubleBuffer) { - val array = arg.array - DoubleBuffer(DoubleArray(arg.size) { ln(array[it]) }) - } else { - DoubleBuffer(DoubleArray(arg.size) { ln(arg[it]) }) - } + override fun acos(arg: Buffer): DoubleBuffer = if (arg is DoubleBuffer) { + val array = arg.array + DoubleBuffer(DoubleArray(arg.size) { acos(array[it]) }) + } else { + DoubleBuffer(DoubleArray(arg.size) { acos(arg[it]) }) + } + + override fun atan(arg: Buffer): DoubleBuffer = if (arg is DoubleBuffer) { + val array = arg.array + DoubleBuffer(DoubleArray(arg.size) { atan(array[it]) }) + } else { + DoubleBuffer(DoubleArray(arg.size) { atan(arg[it]) }) + } + + override fun power(arg: Buffer, pow: Number): DoubleBuffer = if (arg is DoubleBuffer) { + val array = arg.array + DoubleBuffer(DoubleArray(arg.size) { array[it].pow(pow.toDouble()) }) + } else { + DoubleBuffer(DoubleArray(arg.size) { arg[it].pow(pow.toDouble()) }) + } + + override fun exp(arg: Buffer): DoubleBuffer = if (arg is DoubleBuffer) { + val array = arg.array + DoubleBuffer(DoubleArray(arg.size) { exp(array[it]) }) + } else { + DoubleBuffer(DoubleArray(arg.size) { exp(arg[it]) }) + } + + override fun ln(arg: Buffer): DoubleBuffer = if (arg is DoubleBuffer) { + val array = arg.array + DoubleBuffer(DoubleArray(arg.size) { ln(array[it]) }) + } else { + DoubleBuffer(DoubleArray(arg.size) { ln(arg[it]) }) } } @@ -119,7 +142,6 @@ class RealBufferField(val size: Int) : ExtendedField> { return RealBufferFieldOperations.multiply(a, b) } - override fun divide(a: Buffer, b: Buffer): DoubleBuffer { require(a.size == size) { "The buffer size ${a.size} does not match context size $size" } return RealBufferFieldOperations.divide(a, b) @@ -135,6 +157,26 @@ class RealBufferField(val size: Int) : ExtendedField> { return RealBufferFieldOperations.cos(arg) } + override fun tan(arg: Buffer): DoubleBuffer { + require(arg.size == size) { "The buffer size ${arg.size} does not match context size $size" } + return RealBufferFieldOperations.tan(arg) + } + + override fun asin(arg: Buffer): DoubleBuffer { + require(arg.size == size) { "The buffer size ${arg.size} does not match context size $size" } + return RealBufferFieldOperations.asin(arg) + } + + override fun acos(arg: Buffer): DoubleBuffer { + require(arg.size == size) { "The buffer size ${arg.size} does not match context size $size" } + return RealBufferFieldOperations.acos(arg) + } + + override fun atan(arg: Buffer): DoubleBuffer { + require(arg.size == size) { "The buffer size ${arg.size} does not match context size $size" } + return RealBufferFieldOperations.atan(arg) + } + override fun power(arg: Buffer, pow: Number): DoubleBuffer { require(arg.size == size) { "The buffer size ${arg.size} does not match context size $size" } return RealBufferFieldOperations.power(arg, pow) @@ -149,5 +191,4 @@ class RealBufferField(val size: Int) : ExtendedField> { require(arg.size == size) { "The buffer size ${arg.size} does not match context size $size" } return RealBufferFieldOperations.ln(arg) } - -} \ No newline at end of file +} 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 8c1bd4239..22b33aa4d 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/RealNDField.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/RealNDField.kt @@ -74,6 +74,13 @@ class RealNDField(override val shape: IntArray) : override fun cos(arg: NDBuffer) = map(arg) { cos(it) } + override fun tan(arg: NDBuffer): NDBuffer = map(arg) { tan(it) } + + override fun asin(arg: NDBuffer): NDBuffer = map(arg) { asin(it) } + + override fun acos(arg: NDBuffer): NDBuffer = map(arg) { acos(it) } + + override fun atan(arg: NDBuffer): NDBuffer = map(arg) { atan(it) } } -- 2.34.1 From a0453da4b34706cd7853f49cd4b7de6fa69aa179 Mon Sep 17 00:00:00 2001 From: Commander Tvis Date: Wed, 10 Jun 2020 08:57:17 +0700 Subject: [PATCH 034/156] Refactor, replace constants List with Array, create specification of named functions --- kmath-asm/build.gradle.kts | 1 + .../expressions/asm/AsmExpressionSpaces.kt | 16 ++--- .../kmath/expressions/asm/AsmExpressions.kt | 59 +++++++++++++++---- .../expressions/asm/AsmGenerationContext.kt | 17 +++--- .../kmath/expressions/asm/Builders.kt | 12 ++-- .../kmath/expressions/asm/Optimization.kt | 6 +- .../kmath/expressions/Expression.kt | 14 ++--- .../expressions/FunctionalExpressions.kt | 16 ++--- 8 files changed, 89 insertions(+), 52 deletions(-) diff --git a/kmath-asm/build.gradle.kts b/kmath-asm/build.gradle.kts index 4104349f8..3b004eb8e 100644 --- a/kmath-asm/build.gradle.kts +++ b/kmath-asm/build.gradle.kts @@ -6,4 +6,5 @@ dependencies { api(project(path = ":kmath-core")) implementation("org.ow2.asm:asm:8.0.1") implementation("org.ow2.asm:asm-commons:8.0.1") + implementation(kotlin("reflect")) } diff --git a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/AsmExpressionSpaces.kt b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/AsmExpressionSpaces.kt index 17b6dc023..24260b871 100644 --- a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/AsmExpressionSpaces.kt +++ b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/AsmExpressionSpaces.kt @@ -31,10 +31,10 @@ open class AsmExpressionSpace( AsmBinaryOperation(space, SpaceOperations.PLUS_OPERATION, a, b) override fun multiply(a: AsmExpression, k: Number): AsmExpression = AsmConstProductExpression(space, a, k) - operator fun AsmExpression.plus(arg: T) = this + const(arg) - operator fun AsmExpression.minus(arg: T) = this - const(arg) - operator fun T.plus(arg: AsmExpression) = arg + this - operator fun T.minus(arg: AsmExpression) = arg - this + operator fun AsmExpression.plus(arg: T): AsmExpression = this + const(arg) + operator fun AsmExpression.minus(arg: T): AsmExpression = this - const(arg) + operator fun T.plus(arg: AsmExpression): AsmExpression = arg + this + operator fun T.minus(arg: AsmExpression): AsmExpression = arg - this } open class AsmExpressionRing(private val ring: Ring) : AsmExpressionSpace(ring), Ring> { @@ -52,8 +52,8 @@ open class AsmExpressionRing(private val ring: Ring) : AsmExpressionSpace< override fun multiply(a: AsmExpression, b: AsmExpression): AsmExpression = AsmBinaryOperation(space, RingOperations.TIMES_OPERATION, a, b) - operator fun AsmExpression.times(arg: T) = this * const(arg) - operator fun T.times(arg: AsmExpression) = arg * this + operator fun AsmExpression.times(arg: T): AsmExpression = this * const(arg) + operator fun T.times(arg: AsmExpression): AsmExpression = arg * this } open class AsmExpressionField(private val field: Field) : @@ -69,6 +69,6 @@ open class AsmExpressionField(private val field: Field) : override fun divide(a: AsmExpression, b: AsmExpression): AsmExpression = AsmBinaryOperation(space, FieldOperations.DIV_OPERATION, a, b) - operator fun AsmExpression.div(arg: T) = this / const(arg) - operator fun T.div(arg: AsmExpression) = arg / this + operator fun AsmExpression.div(arg: T): AsmExpression = this / const(arg) + operator fun T.div(arg: AsmExpression): AsmExpression = arg / this } diff --git a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/AsmExpressions.kt b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/AsmExpressions.kt index d7d655d6e..662e1279e 100644 --- a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/AsmExpressions.kt +++ b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/AsmExpressions.kt @@ -4,28 +4,55 @@ import scientifik.kmath.expressions.Expression import scientifik.kmath.operations.Algebra import scientifik.kmath.operations.Space import scientifik.kmath.operations.invoke +import kotlin.reflect.full.memberFunctions +import kotlin.reflect.jvm.jvmName interface AsmExpression { fun tryEvaluate(): T? = null fun invoke(gen: AsmGenerationContext) } +internal fun hasSpecific(context: Algebra, name: String, arity: Int): Boolean { + context::class.memberFunctions.find { it.name == name && it.parameters.size == arity } + ?: return false + + return true +} + +internal fun AsmGenerationContext.tryInvokeSpecific(context: Algebra, name: String, arity: Int): Boolean { + context::class.memberFunctions.find { it.name == name && it.parameters.size == arity } + ?: return false + + val owner = context::class.jvmName.replace('.', '/') + + val sig = buildString { + append('(') + repeat(arity) { append("L${AsmGenerationContext.OBJECT_CLASS};") } + append(')') + append("L${AsmGenerationContext.OBJECT_CLASS};") + } + + visitAlgebraOperation(owner = owner, method = name, descriptor = sig) + + return true +} + internal class AsmUnaryOperation(private val context: Algebra, private val name: String, expr: AsmExpression) : AsmExpression { private val expr: AsmExpression = expr.optimize() - - override fun tryEvaluate(): T? = context { - unaryOperation( - name, - expr.tryEvaluate() ?: return@context null - ) - } + override fun tryEvaluate(): T? = context { unaryOperation(name, expr.tryEvaluate() ?: return@context null) } override fun invoke(gen: AsmGenerationContext) { gen.visitLoadAlgebra() - gen.visitStringConstant(name) + + if (!hasSpecific(context, name, 1)) + gen.visitStringConstant(name) + expr.invoke(gen) + if (gen.tryInvokeSpecific(context, name, 1)) + return + gen.visitAlgebraOperation( owner = AsmGenerationContext.ALGEBRA_CLASS, method = "unaryOperation", @@ -55,10 +82,16 @@ internal class AsmBinaryOperation( override fun invoke(gen: AsmGenerationContext) { gen.visitLoadAlgebra() - gen.visitStringConstant(name) + + if (!hasSpecific(context, name, 1)) + gen.visitStringConstant(name) + first.invoke(gen) second.invoke(gen) + if (gen.tryInvokeSpecific(context, name, 1)) + return + gen.visitAlgebraOperation( owner = AsmGenerationContext.ALGEBRA_CLASS, method = "binaryOperation", @@ -79,7 +112,11 @@ internal class AsmConstantExpression(private val value: T) : AsmExpression override fun invoke(gen: AsmGenerationContext): Unit = gen.visitLoadFromConstants(value) } -internal class AsmConstProductExpression(private val context: Space, expr: AsmExpression, private val const: Number) : +internal class AsmConstProductExpression( + private val context: Space, + expr: AsmExpression, + private val const: Number +) : AsmExpression { private val expr: AsmExpression = expr.optimize() @@ -100,7 +137,7 @@ internal class AsmConstProductExpression(private val context: Space, expr: internal abstract class FunctionalCompiledExpression internal constructor( @JvmField protected val algebra: Algebra, - @JvmField protected val constants: MutableList + @JvmField protected val constants: Array ) : Expression { abstract override fun invoke(arguments: Map): T } diff --git a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/AsmGenerationContext.kt b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/AsmGenerationContext.kt index 3e47da5e3..0375b7fc4 100644 --- a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/AsmGenerationContext.kt +++ b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/AsmGenerationContext.kt @@ -46,7 +46,7 @@ class AsmGenerationContext( ) asmCompiledClassWriter.run { - visitMethod(Opcodes.ACC_PUBLIC, "", "(L$ALGEBRA_CLASS;L$LIST_CLASS;)V", null, null).run { + visitMethod(Opcodes.ACC_PUBLIC, "", "(L$ALGEBRA_CLASS;[L$OBJECT_CLASS;)V", null, null).run { val thisVar = 0 val algebraVar = 1 val constantsVar = 2 @@ -60,7 +60,7 @@ class AsmGenerationContext( Opcodes.INVOKESPECIAL, FUNCTIONAL_COMPILED_EXPRESSION_CLASS, "", - "(L$ALGEBRA_CLASS;L$LIST_CLASS;)V", + "(L$ALGEBRA_CLASS;[L$OBJECT_CLASS;)V", false ) @@ -80,7 +80,7 @@ class AsmGenerationContext( algebraVar ) - visitLocalVariable("constants", "L$LIST_CLASS;", "L$LIST_CLASS;", l0, l2, constantsVar) + visitLocalVariable("constants", "[L$OBJECT_CLASS;", null, l0, l2, constantsVar) visitMaxs(3, 3) visitEnd() } @@ -170,7 +170,7 @@ class AsmGenerationContext( .defineClass(className, asmCompiledClassWriter.toByteArray()) .constructors .first() - .newInstance(algebra, constants) as FunctionalCompiledExpression + .newInstance(algebra, constants.toTypedArray()) as FunctionalCompiledExpression generatedInstance = new return new @@ -184,9 +184,9 @@ class AsmGenerationContext( invokeMethodVisitor.run { visitLoadThis() - visitFieldInsn(Opcodes.GETFIELD, slashesClassName, "constants", "L$LIST_CLASS;") + visitFieldInsn(Opcodes.GETFIELD, slashesClassName, "constants", "[L$OBJECT_CLASS;") visitLdcOrIConstInsn(idx) - visitMethodInsn(Opcodes.INVOKEINTERFACE, LIST_CLASS, "get", "(I)L$OBJECT_CLASS;", true) + visitInsn(Opcodes.AALOAD) invokeMethodVisitor.visitTypeInsn(Opcodes.CHECKCAST, type) } } @@ -273,8 +273,9 @@ class AsmGenerationContext( java.lang.Double::class.java to "D" ) - internal const val FUNCTIONAL_COMPILED_EXPRESSION_CLASS = "scientifik/kmath/expressions/asm/FunctionalCompiledExpression" - internal const val LIST_CLASS = "java/util/List" + internal const val FUNCTIONAL_COMPILED_EXPRESSION_CLASS = + "scientifik/kmath/expressions/asm/FunctionalCompiledExpression" + internal const val MAP_CLASS = "java/util/Map" internal const val OBJECT_CLASS = "java/lang/Object" internal const val ALGEBRA_CLASS = "scientifik/kmath/operations/Algebra" diff --git a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/Builders.kt b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/Builders.kt index d55555567..663bd55ba 100644 --- a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/Builders.kt +++ b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/Builders.kt @@ -1,10 +1,8 @@ package scientifik.kmath.expressions.asm import scientifik.kmath.expressions.Expression -import scientifik.kmath.operations.Algebra -import scientifik.kmath.operations.Field -import scientifik.kmath.operations.Ring -import scientifik.kmath.operations.Space +import scientifik.kmath.expressions.ExpressionContext +import scientifik.kmath.operations.* @PublishedApi internal fun buildName(expression: AsmExpression<*>, collision: Int = 0): String { @@ -20,7 +18,11 @@ internal fun buildName(expression: AsmExpression<*>, collision: Int = 0): String } -inline fun asm(i: I, algebra: Algebra, block: I.() -> AsmExpression): Expression { +inline fun >> asm( + i: E, + algebra: Algebra, + block: E.() -> AsmExpression +): Expression { val expression = i.block().optimize() val ctx = AsmGenerationContext(T::class.java, algebra, buildName(expression)) expression.invoke(ctx) diff --git a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/Optimization.kt b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/Optimization.kt index db57a690c..bcd182dc3 100644 --- a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/Optimization.kt +++ b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/Optimization.kt @@ -1,9 +1,7 @@ package scientifik.kmath.expressions.asm -import scientifik.kmath.expressions.asm.AsmConstantExpression -import scientifik.kmath.expressions.asm.AsmExpression - -fun AsmExpression.optimize(): AsmExpression { +@PublishedApi +internal fun AsmExpression.optimize(): AsmExpression { val a = tryEvaluate() return if (a == null) this else AsmConstantExpression(a) } 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 73745f78f..8d8bd3a7a 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/Expression.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/Expression.kt @@ -26,11 +26,9 @@ interface ExpressionContext : Algebra { fun const(value: T): E } -fun ExpressionContext.produce(node: SyntaxTreeNode): E { - return when (node) { - is NumberNode -> error("Single number nodes are not supported") - is SingularNode -> variable(node.value) - is UnaryNode -> unaryOperation(node.operation, produce(node.value)) - is BinaryNode -> binaryOperation(node.operation, produce(node.left), produce(node.right)) - } -} \ No newline at end of file +fun ExpressionContext.produce(node: SyntaxTreeNode): E = when (node) { + is NumberNode -> error("Single number nodes are not supported") + is SingularNode -> variable(node.value) + is UnaryNode -> unaryOperation(node.operation, produce(node.value)) + is BinaryNode -> binaryOperation(node.operation, produce(node.left), produce(node.right)) +} diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressions.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressions.kt index b36ea8b52..d20a5dbbf 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressions.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressions.kt @@ -59,10 +59,10 @@ open class FunctionalExpressionSpace(val space: Space) : FunctionalBinaryOperation(space, SpaceOperations.PLUS_OPERATION, a, b) override fun multiply(a: Expression, k: Number): Expression = FunctionalConstProductExpression(space, a, k) - operator fun Expression.plus(arg: T) = this + const(arg) - operator fun Expression.minus(arg: T) = this - const(arg) - operator fun T.plus(arg: Expression) = arg + this - operator fun T.minus(arg: Expression) = arg - this + operator fun Expression.plus(arg: T): Expression = this + const(arg) + operator fun Expression.minus(arg: T): Expression = this - const(arg) + operator fun T.plus(arg: Expression): Expression = arg + this + operator fun T.minus(arg: Expression): Expression = arg - this } open class FunctionalExpressionRing(val ring: Ring) : FunctionalExpressionSpace(ring), Ring> { @@ -80,8 +80,8 @@ open class FunctionalExpressionRing(val ring: Ring) : FunctionalExpression override fun multiply(a: Expression, b: Expression): Expression = FunctionalBinaryOperation(space, RingOperations.TIMES_OPERATION, a, b) - operator fun Expression.times(arg: T) = this * const(arg) - operator fun T.times(arg: Expression) = arg * this + operator fun Expression.times(arg: T): Expression = this * const(arg) + operator fun T.times(arg: Expression): Expression = arg * this } open class FunctionalExpressionField(val field: Field) : @@ -97,6 +97,6 @@ open class FunctionalExpressionField(val field: Field) : override fun divide(a: Expression, b: Expression): Expression = FunctionalBinaryOperation(space, FieldOperations.DIV_OPERATION, a, b) - operator fun Expression.div(arg: T) = this / const(arg) - operator fun T.div(arg: Expression) = arg / this + operator fun Expression.div(arg: T): Expression = this / const(arg) + operator fun T.div(arg: Expression): Expression = arg / this } -- 2.34.1 From 927aa589ad4277d8a95aea2c402128a8183e59e7 Mon Sep 17 00:00:00 2001 From: Commander Tvis Date: Thu, 11 Jun 2020 08:49:38 +0700 Subject: [PATCH 035/156] Add missing override modifiers --- .../scientifik/kmath/expressions/asm/AsmExpressionSpaces.kt | 2 +- .../scientifik/kmath/expressions/FunctionalExpressions.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/AsmExpressionSpaces.kt b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/AsmExpressionSpaces.kt index 24260b871..78f6ee351 100644 --- a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/AsmExpressionSpaces.kt +++ b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/AsmExpressionSpaces.kt @@ -47,7 +47,7 @@ open class AsmExpressionRing(private val ring: Ring) : AsmExpressionSpace< override fun binaryOperation(operation: String, left: AsmExpression, right: AsmExpression): AsmExpression = AsmBinaryOperation(algebra, operation, left, right) - fun number(value: Number): AsmExpression = const(ring { one * value }) + override fun number(value: Number): AsmExpression = const(ring { one * value }) override fun multiply(a: AsmExpression, b: AsmExpression): AsmExpression = AsmBinaryOperation(space, RingOperations.TIMES_OPERATION, a, b) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressions.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressions.kt index d20a5dbbf..ce9b4756c 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressions.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressions.kt @@ -75,7 +75,7 @@ open class FunctionalExpressionRing(val ring: Ring) : FunctionalExpression override fun binaryOperation(operation: String, left: Expression, right: Expression): Expression = FunctionalBinaryOperation(algebra, operation, left, right) - fun number(value: Number): Expression = const(ring { one * value }) + override fun number(value: Number): Expression = const(ring { one * value }) override fun multiply(a: Expression, b: Expression): Expression = FunctionalBinaryOperation(space, RingOperations.TIMES_OPERATION, a, b) -- 2.34.1 From e6f97c532b645d019fd76dfe7fec99fac5d55899 Mon Sep 17 00:00:00 2001 From: Commander Tvis Date: Thu, 11 Jun 2020 08:50:37 +0700 Subject: [PATCH 036/156] Minor refactor: replace space property with field --- .../scientifik/kmath/expressions/asm/AsmExpressionSpaces.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/AsmExpressionSpaces.kt b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/AsmExpressionSpaces.kt index 78f6ee351..232710d6c 100644 --- a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/AsmExpressionSpaces.kt +++ b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/AsmExpressionSpaces.kt @@ -67,7 +67,7 @@ open class AsmExpressionField(private val field: Field) : AsmBinaryOperation(algebra, operation, left, right) override fun divide(a: AsmExpression, b: AsmExpression): AsmExpression = - AsmBinaryOperation(space, FieldOperations.DIV_OPERATION, a, b) + AsmBinaryOperation(field, FieldOperations.DIV_OPERATION, a, b) operator fun AsmExpression.div(arg: T): AsmExpression = this / const(arg) operator fun T.div(arg: AsmExpression): AsmExpression = arg / this -- 2.34.1 From 89fae390134b1c5ff27c4864b9ef094356d49036 Mon Sep 17 00:00:00 2001 From: Commander Tvis Date: Thu, 11 Jun 2020 09:27:23 +0700 Subject: [PATCH 037/156] Improve tests --- .../expressions/asm/AsmGenerationContext.kt | 3 ++- .../scientifik/kmath/expressions/AsmTest.kt | 23 +++++++++++-------- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/AsmGenerationContext.kt b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/AsmGenerationContext.kt index 0375b7fc4..92663ba8f 100644 --- a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/AsmGenerationContext.kt +++ b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/AsmGenerationContext.kt @@ -215,7 +215,7 @@ class AsmGenerationContext( visitLoadAnyFromConstants(value, c) } - internal fun visitLoadFromVariables(name: String, defaultValue: T? = null) = invokeMethodVisitor.run { + internal fun visitLoadFromVariables(name: String, defaultValue: T? = null): Unit = invokeMethodVisitor.run { maxStack += 2 visitVarInsn(Opcodes.ALOAD, invokeArgumentsVar) @@ -241,6 +241,7 @@ class AsmGenerationContext( } internal fun visitLoadAlgebra() { + maxStack++ invokeMethodVisitor.visitVarInsn(Opcodes.ALOAD, invokeThisVar) invokeMethodVisitor.visitFieldInsn( diff --git a/kmath-asm/src/test/kotlin/scientifik/kmath/expressions/AsmTest.kt b/kmath-asm/src/test/kotlin/scientifik/kmath/expressions/AsmTest.kt index 55c240cef..93c52faac 100644 --- a/kmath-asm/src/test/kotlin/scientifik/kmath/expressions/AsmTest.kt +++ b/kmath-asm/src/test/kotlin/scientifik/kmath/expressions/AsmTest.kt @@ -3,6 +3,8 @@ package scientifik.kmath.expressions import scientifik.kmath.expressions.asm.AsmExpression import scientifik.kmath.expressions.asm.AsmExpressionField import scientifik.kmath.expressions.asm.asmField +import scientifik.kmath.expressions.asm.asmRing +import scientifik.kmath.operations.IntRing import scientifik.kmath.operations.RealField import kotlin.test.Test import kotlin.test.assertEquals @@ -15,26 +17,29 @@ class AsmTest { ): Unit = assertEquals(expected = expected, actual = asmField(RealField, block)(arguments)) @Test - fun testConstantsSum() = testDoubleExpression(16.0) { const(8.0) + 8.0 } + fun testConstantsSum(): Unit = testDoubleExpression(16.0) { const(8.0) + 8.0 } @Test - fun testVarsSum() = testDoubleExpression(1000.0, mapOf("x" to 500.0)) { variable("x") + 500.0 } + fun testVarsSum(): Unit = testDoubleExpression(1000.0, mapOf("x" to 500.0)) { variable("x") + 500.0 } @Test - fun testProduct() = testDoubleExpression(24.0) { const(4.0) * const(6.0) } + fun testProduct(): Unit = testDoubleExpression(24.0) { const(4.0) * const(6.0) } @Test - fun testConstantProduct() = testDoubleExpression(984.0) { const(8.0) * 123 } + fun testConstantProduct(): Unit = testDoubleExpression(984.0) { const(8.0) * 123 } @Test - fun testSubtraction() = testDoubleExpression(2.0) { const(4.0) - 2.0 } + fun testVarsConstantProductVar(): Unit = testDoubleExpression(984.0, mapOf("x" to 8.0)) { variable("x") * 123 } @Test - fun testDivision() = testDoubleExpression(64.0) { const(128.0) / 2 } + fun testSubtraction(): Unit = testDoubleExpression(2.0) { const(4.0) - 2.0 } @Test - fun testDirectCall() = testDoubleExpression(4096.0) { binaryOperation("*", const(64.0), const(64.0)) } + fun testDivision(): Unit = testDoubleExpression(64.0) { const(128.0) / 2 } -// @Test -// fun testSine() = testDoubleExpression(0.0) { unaryOperation("sin", const(PI)) } + @Test + fun testDirectUnaryCall(): Unit = testDoubleExpression(64.0) { unaryOperation("+", const(64.0)) } + + @Test + fun testDirectBinaryCall(): Unit = testDoubleExpression(4096.0) { binaryOperation("*", const(64.0), const(64.0)) } } -- 2.34.1 From ef67d82d84fcb38e5c4b6be2db28d7d3e1aebcfb Mon Sep 17 00:00:00 2001 From: Commander Tvis Date: Thu, 11 Jun 2020 10:21:13 +0700 Subject: [PATCH 038/156] Repair multiplication for Int, Long, Short, Byte Ring --- .../scientifik/kmath/operations/NumberAlgebra.kt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/NumberAlgebra.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/NumberAlgebra.kt index 9639e4c28..2b6a92f14 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/NumberAlgebra.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/NumberAlgebra.kt @@ -102,7 +102,7 @@ object IntRing : Ring, Norm { override val zero: Int = 0 override inline fun add(a: Int, b: Int) = a + b override inline fun multiply(a: Int, b: Int) = a * b - override inline fun multiply(a: Int, k: Number) = (k * a) + override inline fun multiply(a: Int, k: Number) = k.toInt() * a override val one: Int = 1 override inline fun norm(arg: Int) = abs(arg) @@ -124,7 +124,7 @@ object ShortRing : Ring, Norm { override val zero: Short = 0 override inline fun add(a: Short, b: Short) = (a + b).toShort() override inline fun multiply(a: Short, b: Short) = (a * b).toShort() - override inline fun multiply(a: Short, k: Number) = (a * k) + override inline fun multiply(a: Short, k: Number) = (a * k.toShort()).toShort() override val one: Short = 1 override fun norm(arg: Short): Short = if (arg > 0) arg else (-arg).toShort() @@ -146,7 +146,7 @@ object ByteRing : Ring, Norm { override val zero: Byte = 0 override inline fun add(a: Byte, b: Byte) = (a + b).toByte() override inline fun multiply(a: Byte, b: Byte) = (a * b).toByte() - override inline fun multiply(a: Byte, k: Number) = (a * k) + override inline fun multiply(a: Byte, k: Number) = (a * k.toByte()).toByte() override val one: Byte = 1 override fun norm(arg: Byte): Byte = if (arg > 0) arg else (-arg).toByte() @@ -168,7 +168,7 @@ object LongRing : Ring, Norm { override val zero: Long = 0 override inline fun add(a: Long, b: Long) = (a + b) override inline fun multiply(a: Long, b: Long) = (a * b) - override inline fun multiply(a: Long, k: Number) = (a * k) + override inline fun multiply(a: Long, k: Number) = a * k.toLong() override val one: Long = 1 override fun norm(arg: Long): Long = abs(arg) @@ -180,4 +180,4 @@ object LongRing : Ring, Norm { override inline fun Long.minus(b: Long) = (this - b) override inline fun Long.times(b: Long) = (this * b) -} \ No newline at end of file +} -- 2.34.1 From 0507bfcc24ada2bdc52ad5ceadaafd4d7f0d0ccb Mon Sep 17 00:00:00 2001 From: darksnake Date: Thu, 11 Jun 2020 09:46:42 +0300 Subject: [PATCH 039/156] Expression simplification --- .../kmath/expressions/Expression.kt | 9 ----- .../kmath/expressions/SyntaxTreeNode.kt | 32 ++++++++++++++--- .../scientifik/kmath/operations/Algebra.kt | 36 ++++++++++++++++--- 3 files changed, 60 insertions(+), 17 deletions(-) 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 73745f78f..aba7357e8 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/Expression.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/Expression.kt @@ -24,13 +24,4 @@ interface ExpressionContext : Algebra { * A constant expression which does not depend on arguments */ fun const(value: T): E -} - -fun ExpressionContext.produce(node: SyntaxTreeNode): E { - return when (node) { - is NumberNode -> error("Single number nodes are not supported") - is SingularNode -> variable(node.value) - is UnaryNode -> unaryOperation(node.operation, produce(node.value)) - is BinaryNode -> binaryOperation(node.operation, produce(node.left), produce(node.right)) - } } \ No newline at end of file diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/SyntaxTreeNode.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/SyntaxTreeNode.kt index b4038f680..e56165aad 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/SyntaxTreeNode.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/SyntaxTreeNode.kt @@ -1,11 +1,25 @@ package scientifik.kmath.expressions +import scientifik.kmath.operations.NumericAlgebra + +/** + * A syntax tree node for mathematical expressions + */ sealed class SyntaxTreeNode +/** + * A node containing unparsed string + */ data class SingularNode(val value: String) : SyntaxTreeNode() +/** + * A node containing a number + */ data class NumberNode(val value: Number) : SyntaxTreeNode() +/** + * A node containing an unary operation + */ data class UnaryNode(val operation: String, val value: SyntaxTreeNode) : SyntaxTreeNode() { companion object { const val ABS_OPERATION = "abs" @@ -17,12 +31,22 @@ data class UnaryNode(val operation: String, val value: SyntaxTreeNode) : SyntaxT } } +/** + * A node containing binary operation + */ data class BinaryNode(val operation: String, val left: SyntaxTreeNode, val right: SyntaxTreeNode) : SyntaxTreeNode() { - companion object { - //TODO add operations - } + companion object } //TODO add a function with positional arguments -//TODO add a function with named arguments \ No newline at end of file +//TODO add a function with named arguments + +fun NumericAlgebra.compile(node: SyntaxTreeNode): T{ + return when (node) { + is NumberNode -> number(node.value) + is SingularNode -> raw(node.value) + is UnaryNode -> unaryOperation(node.operation, compile(node.value)) + is BinaryNode -> binaryOperation(node.operation, compile(node.left), compile(node.right)) + } +} \ 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 b3282e5f3..c9fbc1c8b 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Algebra.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Algebra.kt @@ -7,10 +7,35 @@ annotation class KMathContext * Marker interface for any algebra */ interface Algebra { + /** + * Wrap raw string or variable + */ + fun raw(value: String): T = error("Wrapping of '$value' is not supported in $this") + + /** + * Dynamic call of unary operation with name [operation] on [arg] + */ fun unaryOperation(operation: String, arg: T): T + + /** + * Dynamic call of binary operation [operation] on [left] and [right] + */ fun binaryOperation(operation: String, left: T, right: T): T } +/** + * An algebra with numeric representation of members + */ +interface NumericAlgebra : Algebra { + /** + * Wrap a number + */ + fun number(value: Number): T +} + +/** + * Call a block with an [Algebra] as receiver + */ inline operator fun , R> A.invoke(block: A.() -> R): R = run(block) /** @@ -87,7 +112,7 @@ interface RingOperations : SpaceOperations { else -> super.binaryOperation(operation, left, right) } - companion object{ + companion object { const val TIMES_OPERATION = "*" } } @@ -95,13 +120,16 @@ interface RingOperations : SpaceOperations { /** * The same as {@link Space} but with additional multiplication operation */ -interface Ring : Space, RingOperations { +interface Ring : Space, RingOperations, NumericAlgebra { /** * neutral operation for multiplication */ val one: T -// operator fun T.plus(b: Number) = this.plus(b * one) + override fun number(value: Number): T = one * value.toDouble() + + // those operators are blocked by type conflict in RealField + // 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) @@ -121,7 +149,7 @@ interface FieldOperations : RingOperations { else -> super.binaryOperation(operation, left, right) } - companion object{ + companion object { const val DIV_OPERATION = "/" } } -- 2.34.1 From f46615d3bc3e036bd33d59912ae8e3a82f6e1b9b Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Fri, 12 Jun 2020 08:43:47 +0300 Subject: [PATCH 040/156] Left and right-side operations in Algebra --- .../kmath/expressions/MathSyntaxTree.kt | 61 +++++++++++++++++++ .../kmath/expressions/SyntaxTreeNode.kt | 52 ---------------- .../scientifik/kmath/operations/Algebra.kt | 16 ++++- .../kmath/operations/NumberAlgebra.kt | 28 ++++++++- .../kmath/operations/OptionalOperations.kt | 26 +++++--- 5 files changed, 120 insertions(+), 63 deletions(-) create mode 100644 kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/MathSyntaxTree.kt delete mode 100644 kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/SyntaxTreeNode.kt diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/MathSyntaxTree.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/MathSyntaxTree.kt new file mode 100644 index 000000000..fbc055f80 --- /dev/null +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/MathSyntaxTree.kt @@ -0,0 +1,61 @@ +package scientifik.kmath.expressions + +import scientifik.kmath.operations.NumericAlgebra +import scientifik.kmath.operations.RealField + +/** + * A syntax tree node for mathematical expressions + */ +sealed class MathSyntaxTree + +/** + * A node containing unparsed string + */ +data class SingularNode(val value: String) : MathSyntaxTree() + +/** + * A node containing a number + */ +data class NumberNode(val value: Number) : MathSyntaxTree() + +/** + * A node containing an unary operation + */ +data class UnaryNode(val operation: String, val value: MathSyntaxTree) : MathSyntaxTree() { + companion object { + const val ABS_OPERATION = "abs" + //TODO add operations + } +} + +/** + * A node containing binary operation + */ +data class BinaryNode(val operation: String, val left: MathSyntaxTree, val right: MathSyntaxTree) : MathSyntaxTree() { + companion object +} + +//TODO add a function with positional arguments + +//TODO add a function with named arguments + +fun NumericAlgebra.compile(node: MathSyntaxTree): T { + return when (node) { + is NumberNode -> number(node.value) + is SingularNode -> raw(node.value) + is UnaryNode -> unaryOperation(node.operation, compile(node.value)) + is BinaryNode -> when { + node.left is NumberNode && node.right is NumberNode -> { + val number = RealField.binaryOperation( + node.operation, + node.left.value.toDouble(), + node.right.value.toDouble() + ) + number(number) + } + node.left is NumberNode -> leftSideNumberOperation(node.operation, node.left.value, compile(node.right)) + node.right is NumberNode -> rightSideNumberOperation(node.operation, compile(node.left), node.right.value) + else -> binaryOperation(node.operation, compile(node.left), compile(node.right)) + } + } +} \ No newline at end of file diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/SyntaxTreeNode.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/SyntaxTreeNode.kt deleted file mode 100644 index e56165aad..000000000 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/SyntaxTreeNode.kt +++ /dev/null @@ -1,52 +0,0 @@ -package scientifik.kmath.expressions - -import scientifik.kmath.operations.NumericAlgebra - -/** - * A syntax tree node for mathematical expressions - */ -sealed class SyntaxTreeNode - -/** - * A node containing unparsed string - */ -data class SingularNode(val value: String) : SyntaxTreeNode() - -/** - * A node containing a number - */ -data class NumberNode(val value: Number) : SyntaxTreeNode() - -/** - * A node containing an unary operation - */ -data class UnaryNode(val operation: String, val value: SyntaxTreeNode) : SyntaxTreeNode() { - companion object { - const val ABS_OPERATION = "abs" - const val SIN_OPERATION = "sin" - const val COS_OPERATION = "cos" - const val EXP_OPERATION = "exp" - const val LN_OPERATION = "ln" - //TODO add operations - } -} - -/** - * A node containing binary operation - */ -data class BinaryNode(val operation: String, val left: SyntaxTreeNode, val right: SyntaxTreeNode) : SyntaxTreeNode() { - companion object -} - -//TODO add a function with positional arguments - -//TODO add a function with named arguments - -fun NumericAlgebra.compile(node: SyntaxTreeNode): T{ - return when (node) { - is NumberNode -> number(node.value) - is SingularNode -> raw(node.value) - is UnaryNode -> unaryOperation(node.operation, compile(node.value)) - is BinaryNode -> binaryOperation(node.operation, compile(node.left), compile(node.right)) - } -} \ 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 c9fbc1c8b..166287ec7 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Algebra.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Algebra.kt @@ -31,6 +31,12 @@ interface NumericAlgebra : Algebra { * Wrap a number */ fun number(value: Number): T + + fun leftSideNumberOperation(operation: String, left: Number, right: T): T = + binaryOperation(operation, number(left), right) + + fun rightSideNumberOperation(operation: String, left: T, right: Number): T = + leftSideNumberOperation(operation, right, left) } /** @@ -128,8 +134,14 @@ interface Ring : Space, RingOperations, NumericAlgebra { override fun number(value: Number): T = one * value.toDouble() - // those operators are blocked by type conflict in RealField - // operator fun T.plus(b: Number) = this.plus(b * one) + override fun leftSideNumberOperation(operation: String, left: Number, right: T): T = when (operation) { + RingOperations.TIMES_OPERATION -> left * right + else -> super.leftSideNumberOperation(operation, left, right) + } + + //TODO those operators are blocked by type conflict in RealField + +// 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) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/NumberAlgebra.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/NumberAlgebra.kt index 9639e4c28..e844d404e 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/NumberAlgebra.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/NumberAlgebra.kt @@ -10,9 +10,30 @@ interface ExtendedFieldOperations : FieldOperations, TrigonometricOperations, PowerOperations, - ExponentialOperations + ExponentialOperations { -interface ExtendedField : ExtendedFieldOperations, Field + override fun tan(arg: T): T = sin(arg) / cos(arg) + + override fun unaryOperation(operation: String, arg: T): T = when (operation) { + TrigonometricOperations.COS_OPERATION -> cos(arg) + TrigonometricOperations.SIN_OPERATION -> sin(arg) + PowerOperations.SQRT_OPERATION -> sqrt(arg) + ExponentialOperations.EXP_OPERATION -> exp(arg) + ExponentialOperations.LN_OPERATION -> ln(arg) + else -> super.unaryOperation(operation, arg) + } + +} + +interface ExtendedField : ExtendedFieldOperations, Field { + override fun rightSideNumberOperation(operation: String, left: T, right: Number): T { + return when (operation) { + PowerOperations.POW_OPERATION -> power(left, right) + else -> super.rightSideNumberOperation(operation, left, right) + } + + } +} /** * Real field element wrapping double. @@ -44,6 +65,7 @@ object RealField : ExtendedField, Norm { override inline fun sin(arg: Double) = kotlin.math.sin(arg) override inline fun cos(arg: Double) = kotlin.math.cos(arg) + override inline fun tan(arg: Double) = kotlin.math.tan(arg) override inline fun power(arg: Double, pow: Number) = arg.kpow(pow.toDouble()) @@ -76,6 +98,8 @@ object FloatField : ExtendedField, Norm { override inline fun sin(arg: Float) = kotlin.math.sin(arg) override inline fun cos(arg: Float) = kotlin.math.cos(arg) + override inline fun tan(arg: Float): Float = kotlin.math.tan(arg) + override inline fun power(arg: Float, pow: Number) = arg.pow(pow.toFloat()) override inline fun exp(arg: Float) = kotlin.math.exp(arg) 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 bd83932e7..1b58b7254 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/OptionalOperations.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/OptionalOperations.kt @@ -10,30 +10,37 @@ package scientifik.kmath.operations * It also allows to override behavior for optional operations * */ -interface TrigonometricOperations : FieldOperations { +interface TrigonometricOperations { fun sin(arg: T): T fun cos(arg: T): T - fun tg(arg: T): T = sin(arg) / cos(arg) + fun tan(arg: T): T - fun ctg(arg: T): T = cos(arg) / sin(arg) + companion object { + const val SIN_OPERATION = "sin" + const val COS_OPERATION = "cos" + } } 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 >> tan(arg: T): T = arg.context.tan(arg) /* Power and roots */ /** * A context extension to include power operations like square roots, etc */ -interface PowerOperations : Algebra { +interface PowerOperations { fun power(arg: T, pow: Number): T fun sqrt(arg: T) = power(arg, 0.5) infix fun T.pow(pow: Number) = power(this, pow) + + companion object { + const val POW_OPERATION = "pow" + const val SQRT_OPERATION = "sqrt" + } } infix fun >> T.pow(power: Double): T = context.power(this, power) @@ -42,9 +49,14 @@ fun >> sqr(arg: T): T = arg pow 2.0 /* Exponential */ -interface ExponentialOperations: Algebra { +interface ExponentialOperations { fun exp(arg: T): T fun ln(arg: T): T + + companion object { + const val EXP_OPERATION = "exp" + const val LN_OPERATION = "ln" + } } fun >> exp(arg: T): T = arg.context.exp(arg) -- 2.34.1 From 5e92d85c4676c27c45fbba43d58a12569a80a0a7 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Fri, 12 Jun 2020 10:40:59 +0300 Subject: [PATCH 041/156] Separate ast module --- kmath-ast/build.gradle.kts | 16 +++++++++++++ .../scientifik/kmath/ast}/MathSyntaxTree.kt | 2 +- .../kotlin/scientifik/kmath/ast/asm.kt | 23 +++++++++++++++++++ .../commons/expressions/DiffExpression.kt | 4 ++-- .../kmath/expressions/Expression.kt | 2 +- .../expressions/functionalExpressions.kt | 13 +++++++---- .../scientifik/kmath/operations/bigNumbers.kt | 7 +++++- settings.gradle.kts | 1 + 8 files changed, 58 insertions(+), 10 deletions(-) create mode 100644 kmath-ast/build.gradle.kts rename {kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions => kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast}/MathSyntaxTree.kt (98%) create mode 100644 kmath-ast/src/jvmMain/kotlin/scientifik/kmath/ast/asm.kt diff --git a/kmath-ast/build.gradle.kts b/kmath-ast/build.gradle.kts new file mode 100644 index 000000000..f3d37bf65 --- /dev/null +++ b/kmath-ast/build.gradle.kts @@ -0,0 +1,16 @@ +plugins { + id("scientifik.mpp") +} + +repositories{ + maven("https://dl.bintray.com/hotkeytlt/maven") +} + +kotlin.sourceSets { + commonMain { + dependencies { + api(project(":kmath-core")) + implementation("com.github.h0tk3y.betterParse:better-parse-multiplatform:0.4.0-alpha3") + } + } +} \ No newline at end of file diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/MathSyntaxTree.kt b/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MathSyntaxTree.kt similarity index 98% rename from kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/MathSyntaxTree.kt rename to kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MathSyntaxTree.kt index fbc055f80..b43ef705d 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/MathSyntaxTree.kt +++ b/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MathSyntaxTree.kt @@ -1,4 +1,4 @@ -package scientifik.kmath.expressions +package scientifik.kmath.ast import scientifik.kmath.operations.NumericAlgebra import scientifik.kmath.operations.RealField diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/ast/asm.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/ast/asm.kt new file mode 100644 index 000000000..6c4325859 --- /dev/null +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/ast/asm.kt @@ -0,0 +1,23 @@ +package scientifik.kmath.ast + +import scientifik.kmath.expressions.Expression +import scientifik.kmath.operations.Algebra +import scientifik.kmath.operations.NumericAlgebra + +//TODO stubs for asm generation + +interface AsmExpression + +interface AsmExpressionAlgebra> : NumericAlgebra> { + val algebra: A +} + +fun AsmExpression.compile(): Expression = TODO() + +//TODO add converter for functional expressions + +inline fun > A.asm( + block: AsmExpressionAlgebra.() -> AsmExpression +): Expression = TODO() + +inline fun > A.asm(ast: MathSyntaxTree): Expression = asm { compile(ast) } \ No newline at end of file diff --git a/kmath-commons/src/main/kotlin/scientifik/kmath/commons/expressions/DiffExpression.kt b/kmath-commons/src/main/kotlin/scientifik/kmath/commons/expressions/DiffExpression.kt index 8c19395d3..f6f61e08a 100644 --- a/kmath-commons/src/main/kotlin/scientifik/kmath/commons/expressions/DiffExpression.kt +++ b/kmath-commons/src/main/kotlin/scientifik/kmath/commons/expressions/DiffExpression.kt @@ -2,7 +2,7 @@ package scientifik.kmath.commons.expressions import org.apache.commons.math3.analysis.differentiation.DerivativeStructure import scientifik.kmath.expressions.Expression -import scientifik.kmath.expressions.ExpressionContext +import scientifik.kmath.expressions.ExpressionAlgebra import scientifik.kmath.operations.ExtendedField import scientifik.kmath.operations.Field import kotlin.properties.ReadOnlyProperty @@ -113,7 +113,7 @@ fun DiffExpression.derivative(name: String) = derivative(name to 1) /** * A context for [DiffExpression] (not to be confused with [DerivativeStructure]) */ -object DiffExpressionContext : ExpressionContext, Field { +object DiffExpressionAlgebra : ExpressionAlgebra, Field { override fun variable(name: String, default: Double?) = DiffExpression { variable(name, default?.const()) } 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 aba7357e8..df5c981f5 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/Expression.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/Expression.kt @@ -14,7 +14,7 @@ operator fun Expression.invoke(vararg pairs: Pair): T = invoke /** * A context for expression construction */ -interface ExpressionContext : Algebra { +interface ExpressionAlgebra : Algebra { /** * Introduce a variable into expression context */ diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/functionalExpressions.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/functionalExpressions.kt index 4f38e3a71..c3861d256 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/functionalExpressions.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/functionalExpressions.kt @@ -21,8 +21,11 @@ internal class SumExpression( override fun invoke(arguments: Map): T = context.add(first.invoke(arguments), second.invoke(arguments)) } -internal class ProductExpression(val context: Ring, val first: Expression, val second: Expression) : - Expression { +internal class ProductExpression( + val context: Ring, + val first: Expression, + val second: Expression +) : Expression { override fun invoke(arguments: Map): T = context.multiply(first.invoke(arguments), second.invoke(arguments)) } @@ -39,7 +42,7 @@ internal class DivExpession(val context: Field, val expr: Expression, v open class FunctionalExpressionSpace( val space: Space -) : Space>, ExpressionContext> { +) : Space>, ExpressionAlgebra> { override val zero: Expression = ConstantExpression(space.zero) @@ -61,12 +64,12 @@ open class FunctionalExpressionSpace( open class FunctionalExpressionField( val field: Field -) : Field>, ExpressionContext>, FunctionalExpressionSpace(field) { +) : Field>, ExpressionAlgebra>, FunctionalExpressionSpace(field) { override val one: Expression get() = const(this.field.one) - fun const(value: Double): Expression = const(field.run { one*value}) + fun const(value: Double): Expression = const(field.run { one * value }) override fun multiply(a: Expression, b: Expression): Expression = ProductExpression(field, a, b) diff --git a/kmath-core/src/jvmMain/kotlin/scientifik/kmath/operations/bigNumbers.kt b/kmath-core/src/jvmMain/kotlin/scientifik/kmath/operations/bigNumbers.kt index 76ca199c5..e6f09c040 100644 --- a/kmath-core/src/jvmMain/kotlin/scientifik/kmath/operations/bigNumbers.kt +++ b/kmath-core/src/jvmMain/kotlin/scientifik/kmath/operations/bigNumbers.kt @@ -1,10 +1,12 @@ package scientifik.kmath.operations -import scientifik.kmath.structures.* import java.math.BigDecimal import java.math.BigInteger import java.math.MathContext +/** + * A field wrapper for Java [BigInteger] + */ object JBigIntegerField : Field { override val zero: BigInteger = BigInteger.ZERO override val one: BigInteger = BigInteger.ONE @@ -18,6 +20,9 @@ object JBigIntegerField : Field { override fun divide(a: BigInteger, b: BigInteger): BigInteger = a.div(b) } +/** + * A Field wrapper for Java [BigDecimal] + */ class JBigDecimalField(val mathContext: MathContext = MathContext.DECIMAL64) : Field { override val zero: BigDecimal = BigDecimal.ZERO override val one: BigDecimal = BigDecimal.ONE diff --git a/settings.gradle.kts b/settings.gradle.kts index 57173250b..465ecfca8 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -44,5 +44,6 @@ include( ":kmath-dimensions", ":kmath-for-real", ":kmath-geometry", + ":kmath-ast", ":examples" ) -- 2.34.1 From 047af8c172278cdf077e3e9db5f86aff3387f515 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Fri, 12 Jun 2020 11:11:13 +0300 Subject: [PATCH 042/156] Fix ND extendend fields --- kmath-ast/build.gradle.kts | 2 +- .../kotlin/scientifik/kmath/operations/Complex.kt | 2 +- .../scientifik/kmath/structures/ExtendedNDField.kt | 9 ++------- 3 files changed, 4 insertions(+), 9 deletions(-) diff --git a/kmath-ast/build.gradle.kts b/kmath-ast/build.gradle.kts index f3d37bf65..f17809381 100644 --- a/kmath-ast/build.gradle.kts +++ b/kmath-ast/build.gradle.kts @@ -10,7 +10,7 @@ kotlin.sourceSets { commonMain { dependencies { api(project(":kmath-core")) - implementation("com.github.h0tk3y.betterParse:better-parse-multiplatform:0.4.0-alpha3") + implementation("com.github.h0tk3y.betterParse:better-parse-multiplatform:0.4.0-alpha-3") } } } \ 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 6c529f55e..e33e4078e 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Complex.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Complex.kt @@ -11,7 +11,7 @@ import kotlin.math.* /** * A field for complex numbers */ -object ComplexField : ExtendedFieldOperations, Field { +object ComplexField : ExtendedField { override val zero: Complex = Complex(0.0, 0.0) override val one: Complex = Complex(1.0, 0.0) 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 3437644ff..776cff880 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ExtendedNDField.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ExtendedNDField.kt @@ -1,13 +1,8 @@ package scientifik.kmath.structures -import scientifik.kmath.operations.* +import scientifik.kmath.operations.ExtendedField -interface ExtendedNDField> : - NDField, - TrigonometricOperations, - PowerOperations, - ExponentialOperations - where F : ExtendedFieldOperations, F : Field +interface ExtendedNDField, N : NDStructure> : NDField, ExtendedField ///** -- 2.34.1 From 39907a1da239d0eba4f7affbb3438ddcfeb89b1f Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Fri, 12 Jun 2020 19:50:28 +0700 Subject: [PATCH 043/156] Add name adapting of *, +, / --- .../kmath/expressions/asm/AsmExpressions.kt | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/AsmExpressions.kt b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/AsmExpressions.kt index 662e1279e..70f1fc28e 100644 --- a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/AsmExpressions.kt +++ b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/AsmExpressions.kt @@ -12,15 +12,21 @@ interface AsmExpression { fun invoke(gen: AsmGenerationContext) } +internal val methodNameAdapters = mapOf("+" to "add", "*" to "multiply", "/" to "divide") + internal fun hasSpecific(context: Algebra, name: String, arity: Int): Boolean { - context::class.memberFunctions.find { it.name == name && it.parameters.size == arity } + val aName = methodNameAdapters[name] ?: name + + context::class.memberFunctions.find { it.name == aName && it.parameters.size == arity } ?: return false return true } internal fun AsmGenerationContext.tryInvokeSpecific(context: Algebra, name: String, arity: Int): Boolean { - context::class.memberFunctions.find { it.name == name && it.parameters.size == arity } + val aName = methodNameAdapters[name] ?: name + + context::class.memberFunctions.find { it.name == aName && it.parameters.size == arity } ?: return false val owner = context::class.jvmName.replace('.', '/') @@ -32,7 +38,7 @@ internal fun AsmGenerationContext.tryInvokeSpecific(context: Algebra, append("L${AsmGenerationContext.OBJECT_CLASS};") } - visitAlgebraOperation(owner = owner, method = name, descriptor = sig) + visitAlgebraOperation(owner = owner, method = aName, descriptor = sig) return true } -- 2.34.1 From 2751cee9267b95a7370cba9e0565f56c3b29d7cf Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Fri, 12 Jun 2020 16:56:58 +0300 Subject: [PATCH 044/156] MST expression --- kmath-ast/build.gradle.kts | 5 ++ .../kotlin/scientifik/kmath/ast/MST.kt | 62 +++++++++++++++++++ .../scientifik/kmath/ast/MSTExpression.kt | 19 ++++++ .../scientifik/kmath/ast/MathSyntaxTree.kt | 61 ------------------ .../kotlin/scientifik/kmath/ast/asm.kt | 2 +- .../kotlin/scientifik/kmath/ast/parser.kt | 56 +++++++++++++++++ .../kotlin/scietifik/kmath/ast/ParserTest.kt | 17 +++++ .../kmath/expressions/Expression.kt | 6 ++ .../expressions/functionalExpressions.kt | 17 ++++- .../scientifik/kmath/operations/Complex.kt | 6 ++ 10 files changed, 186 insertions(+), 65 deletions(-) create mode 100644 kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MST.kt create mode 100644 kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MSTExpression.kt delete mode 100644 kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MathSyntaxTree.kt create mode 100644 kmath-ast/src/jvmMain/kotlin/scientifik/kmath/ast/parser.kt create mode 100644 kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/ParserTest.kt diff --git a/kmath-ast/build.gradle.kts b/kmath-ast/build.gradle.kts index f17809381..88540e7b8 100644 --- a/kmath-ast/build.gradle.kts +++ b/kmath-ast/build.gradle.kts @@ -13,4 +13,9 @@ kotlin.sourceSets { implementation("com.github.h0tk3y.betterParse:better-parse-multiplatform:0.4.0-alpha-3") } } + jvmMain{ + dependencies{ + implementation("com.github.h0tk3y.betterParse:better-parse-jvm:0.4.0-alpha-3") + } + } } \ No newline at end of file diff --git a/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MST.kt b/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MST.kt new file mode 100644 index 000000000..eba2c3343 --- /dev/null +++ b/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MST.kt @@ -0,0 +1,62 @@ +package scientifik.kmath.ast + +import scientifik.kmath.operations.NumericAlgebra +import scientifik.kmath.operations.RealField + +/** + * A Mathematical Syntax Tree node for mathematical expressions + */ +sealed class MST { + + /** + * A node containing unparsed string + */ + data class Singular(val value: String) : MST() + + /** + * A node containing a number + */ + data class Numeric(val value: Number) : MST() + + /** + * A node containing an unary operation + */ + data class Unary(val operation: String, val value: MST) : MST() { + companion object { + const val ABS_OPERATION = "abs" + //TODO add operations + } + } + + /** + * A node containing binary operation + */ + data class Binary(val operation: String, val left: MST, val right: MST) : MST() { + companion object + } +} + +//TODO add a function with positional arguments + +//TODO add a function with named arguments + +fun NumericAlgebra.evaluate(node: MST): T { + return when (node) { + is MST.Numeric -> number(node.value) + is MST.Singular -> raw(node.value) + is MST.Unary -> unaryOperation(node.operation, evaluate(node.value)) + is MST.Binary -> when { + node.left is MST.Numeric && node.right is MST.Numeric -> { + val number = RealField.binaryOperation( + node.operation, + node.left.value.toDouble(), + node.right.value.toDouble() + ) + number(number) + } + node.left is MST.Numeric -> leftSideNumberOperation(node.operation, node.left.value, evaluate(node.right)) + node.right is MST.Numeric -> rightSideNumberOperation(node.operation, evaluate(node.left), node.right.value) + else -> binaryOperation(node.operation, evaluate(node.left), evaluate(node.right)) + } + } +} \ No newline at end of file diff --git a/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MSTExpression.kt b/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MSTExpression.kt new file mode 100644 index 000000000..c0f124a35 --- /dev/null +++ b/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MSTExpression.kt @@ -0,0 +1,19 @@ +package scientifik.kmath.ast + +import scientifik.kmath.expressions.Expression +import scientifik.kmath.operations.NumericAlgebra + +/** + * The expression evaluates MST on-flight + */ +class MSTExpression(val algebra: NumericAlgebra, val mst: MST) : Expression { + + /** + * Substitute algebra raw value + */ + private inner class InnerAlgebra(val arguments: Map) : NumericAlgebra by algebra { + override fun raw(value: String): T = arguments[value] ?: super.raw(value) + } + + override fun invoke(arguments: Map): T = InnerAlgebra(arguments).evaluate(mst) +} \ No newline at end of file diff --git a/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MathSyntaxTree.kt b/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MathSyntaxTree.kt deleted file mode 100644 index b43ef705d..000000000 --- a/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MathSyntaxTree.kt +++ /dev/null @@ -1,61 +0,0 @@ -package scientifik.kmath.ast - -import scientifik.kmath.operations.NumericAlgebra -import scientifik.kmath.operations.RealField - -/** - * A syntax tree node for mathematical expressions - */ -sealed class MathSyntaxTree - -/** - * A node containing unparsed string - */ -data class SingularNode(val value: String) : MathSyntaxTree() - -/** - * A node containing a number - */ -data class NumberNode(val value: Number) : MathSyntaxTree() - -/** - * A node containing an unary operation - */ -data class UnaryNode(val operation: String, val value: MathSyntaxTree) : MathSyntaxTree() { - companion object { - const val ABS_OPERATION = "abs" - //TODO add operations - } -} - -/** - * A node containing binary operation - */ -data class BinaryNode(val operation: String, val left: MathSyntaxTree, val right: MathSyntaxTree) : MathSyntaxTree() { - companion object -} - -//TODO add a function with positional arguments - -//TODO add a function with named arguments - -fun NumericAlgebra.compile(node: MathSyntaxTree): T { - return when (node) { - is NumberNode -> number(node.value) - is SingularNode -> raw(node.value) - is UnaryNode -> unaryOperation(node.operation, compile(node.value)) - is BinaryNode -> when { - node.left is NumberNode && node.right is NumberNode -> { - val number = RealField.binaryOperation( - node.operation, - node.left.value.toDouble(), - node.right.value.toDouble() - ) - number(number) - } - node.left is NumberNode -> leftSideNumberOperation(node.operation, node.left.value, compile(node.right)) - node.right is NumberNode -> rightSideNumberOperation(node.operation, compile(node.left), node.right.value) - else -> binaryOperation(node.operation, compile(node.left), compile(node.right)) - } - } -} \ No newline at end of file diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/ast/asm.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/ast/asm.kt index 6c4325859..c01648fb0 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/ast/asm.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/ast/asm.kt @@ -20,4 +20,4 @@ inline fun > A.asm( block: AsmExpressionAlgebra.() -> AsmExpression ): Expression = TODO() -inline fun > A.asm(ast: MathSyntaxTree): Expression = asm { compile(ast) } \ No newline at end of file +inline fun > A.asm(ast: MST): Expression = asm { evaluate(ast) } \ No newline at end of file diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/ast/parser.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/ast/parser.kt new file mode 100644 index 000000000..dcab1c972 --- /dev/null +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/ast/parser.kt @@ -0,0 +1,56 @@ +package scientifik.kmath.ast + +import com.github.h0tk3y.betterParse.combinators.* +import com.github.h0tk3y.betterParse.grammar.Grammar +import com.github.h0tk3y.betterParse.grammar.parseToEnd +import com.github.h0tk3y.betterParse.grammar.parser +import com.github.h0tk3y.betterParse.grammar.tryParseToEnd +import com.github.h0tk3y.betterParse.parser.ParseResult +import com.github.h0tk3y.betterParse.parser.Parser +import scientifik.kmath.operations.FieldOperations +import scientifik.kmath.operations.PowerOperations +import scientifik.kmath.operations.RingOperations +import scientifik.kmath.operations.SpaceOperations + +private object ArithmeticsEvaluator : Grammar() { + val num by token("-?[\\d.]+(?:[eE]-?\\d+)?") + val lpar by token("\\(") + val rpar by token("\\)") + val mul by token("\\*") + val pow by token("\\^") + val div by token("/") + val minus by token("-") + val plus by token("\\+") + val ws by token("\\s+", ignore = true) + + val number: Parser by num use { MST.Numeric(text.toDouble()) } + + val term: Parser by number or + (skip(minus) and parser(this::term) map { MST.Unary(SpaceOperations.MINUS_OPERATION, it) }) or + (skip(lpar) and parser(this::rootParser) and skip(rpar)) + + val powChain by leftAssociative(term, pow) { a, _, b -> + MST.Binary(PowerOperations.POW_OPERATION, a, b) + } + + val divMulChain: Parser by leftAssociative(powChain, div or mul use { type }) { a, op, b -> + if (op == div) { + MST.Binary(FieldOperations.DIV_OPERATION, a, b) + } else { + MST.Binary(RingOperations.TIMES_OPERATION, a, b) + } + } + + val subSumChain: Parser by leftAssociative(divMulChain, plus or minus use { type }) { a, op, b -> + if (op == plus) { + MST.Binary(SpaceOperations.PLUS_OPERATION, a, b) + } else { + MST.Binary(SpaceOperations.MINUS_OPERATION, a, b) + } + } + + override val rootParser: Parser by subSumChain +} + +fun String.tryParseMath(): ParseResult = ArithmeticsEvaluator.tryParseToEnd(this) +fun String.parseMath(): MST = ArithmeticsEvaluator.parseToEnd(this) \ No newline at end of file diff --git a/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/ParserTest.kt b/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/ParserTest.kt new file mode 100644 index 000000000..6849d24b8 --- /dev/null +++ b/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/ParserTest.kt @@ -0,0 +1,17 @@ +package scietifik.kmath.ast + +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Test +import scientifik.kmath.ast.evaluate +import scientifik.kmath.ast.parseMath +import scientifik.kmath.operations.Complex +import scientifik.kmath.operations.ComplexField + +internal class ParserTest{ + @Test + fun parsedExpression(){ + val mst = "2+2*(2+2)".parseMath() + val res = ComplexField.evaluate(mst) + assertEquals(Complex(10.0,0.0), res) + } +} \ 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 df5c981f5..eaf3cd1d7 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/Expression.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/Expression.kt @@ -7,6 +7,12 @@ import scientifik.kmath.operations.Algebra */ interface Expression { operator fun invoke(arguments: Map): T + + companion object { + operator fun invoke(block: (Map) -> T): Expression = object : Expression { + override fun invoke(arguments: Map): T = block(arguments) + } + } } operator fun Expression.invoke(vararg pairs: Pair): T = invoke(mapOf(*pairs)) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/functionalExpressions.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/functionalExpressions.kt index c3861d256..6d7676c25 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/functionalExpressions.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/functionalExpressions.kt @@ -66,8 +66,7 @@ open class FunctionalExpressionField( val field: Field ) : Field>, ExpressionAlgebra>, FunctionalExpressionSpace(field) { - override val one: Expression - get() = const(this.field.one) + override val one: Expression = ConstantExpression(this.field.one) fun const(value: Double): Expression = const(field.run { one * value }) @@ -80,4 +79,16 @@ open class FunctionalExpressionField( operator fun T.times(arg: Expression) = arg * this operator fun T.div(arg: Expression) = arg / this -} \ No newline at end of file +} + +/** + * Create a functional expression on this [Space] + */ +fun Space.buildExpression(block: FunctionalExpressionSpace.() -> Expression): Expression = + FunctionalExpressionSpace(this).run(block) + +/** + * Create a functional expression on this [Field] + */ +fun Field.buildExpression(block: FunctionalExpressionField.() -> Expression): Expression = + FunctionalExpressionField(this).run(block) \ 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 e33e4078e..01aef824d 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Complex.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Complex.kt @@ -50,6 +50,12 @@ object ComplexField : ExtendedField { operator fun Complex.minus(d: Double) = add(this, -d.toComplex()) operator fun Double.times(c: Complex) = Complex(c.re * this, c.im * this) + + override fun raw(value: String): Complex = if (value == "i") { + i + } else { + super.raw(value) + } } /** -- 2.34.1 From 09641a5c9c8e4a6af739d984e4a0c634c702f934 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Fri, 12 Jun 2020 16:59:36 +0300 Subject: [PATCH 045/156] Documentation --- .../commonMain/kotlin/scientifik/kmath/ast/MSTExpression.kt | 2 +- kmath-ast/src/jvmMain/kotlin/scientifik/kmath/ast/parser.kt | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MSTExpression.kt b/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MSTExpression.kt index c0f124a35..3ee454b2a 100644 --- a/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MSTExpression.kt +++ b/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MSTExpression.kt @@ -4,7 +4,7 @@ import scientifik.kmath.expressions.Expression import scientifik.kmath.operations.NumericAlgebra /** - * The expression evaluates MST on-flight + * The expression evaluates MST on-flight. Should be much faster than functional expression, but slower than ASM-generated expressions. */ class MSTExpression(val algebra: NumericAlgebra, val mst: MST) : Expression { diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/ast/parser.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/ast/parser.kt index dcab1c972..cec61a8ff 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/ast/parser.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/ast/parser.kt @@ -12,6 +12,9 @@ import scientifik.kmath.operations.PowerOperations import scientifik.kmath.operations.RingOperations import scientifik.kmath.operations.SpaceOperations +/** + * TODO move to common + */ private object ArithmeticsEvaluator : Grammar() { val num by token("-?[\\d.]+(?:[eE]-?\\d+)?") val lpar by token("\\(") -- 2.34.1 From b902f3980a7021a5bdbb0ddc36e45e545d750a95 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Fri, 12 Jun 2020 22:10:56 +0700 Subject: [PATCH 046/156] Fix issues found after merge --- .../expressions/asm/AsmExpressionSpaces.kt | 4 +-- .../kmath/expressions/asm/Builders.kt | 12 ++++---- .../scientifik/kmath/expressions/AsmTest.kt | 6 ++-- .../scientifik/kmath/expressions/Builders.kt | 30 +++++++++++++++++++ .../expressions/FunctionalExpressions.kt | 2 +- settings.gradle.kts | 1 + 6 files changed, 42 insertions(+), 13 deletions(-) create mode 100644 kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/Builders.kt diff --git a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/AsmExpressionSpaces.kt b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/AsmExpressionSpaces.kt index 232710d6c..0f8f685eb 100644 --- a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/AsmExpressionSpaces.kt +++ b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/AsmExpressionSpaces.kt @@ -1,11 +1,11 @@ package scientifik.kmath.expressions.asm -import scientifik.kmath.expressions.ExpressionContext +import scientifik.kmath.expressions.ExpressionAlgebra import scientifik.kmath.operations.* open class AsmExpressionAlgebra(val algebra: Algebra) : Algebra>, - ExpressionContext> { + ExpressionAlgebra> { override fun unaryOperation(operation: String, arg: AsmExpression): AsmExpression = AsmUnaryOperation(algebra, operation, arg) diff --git a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/Builders.kt b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/Builders.kt index 663bd55ba..c56adb767 100644 --- a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/Builders.kt +++ b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/Builders.kt @@ -1,7 +1,7 @@ package scientifik.kmath.expressions.asm import scientifik.kmath.expressions.Expression -import scientifik.kmath.expressions.ExpressionContext +import scientifik.kmath.expressions.ExpressionAlgebra import scientifik.kmath.operations.* @PublishedApi @@ -18,7 +18,7 @@ internal fun buildName(expression: AsmExpression<*>, collision: Int = 0): String } -inline fun >> asm( +inline fun >> asm( i: E, algebra: Algebra, block: E.() -> AsmExpression @@ -29,22 +29,22 @@ inline fun >> asm( return ctx.generate() } -inline fun asmAlgebra( +inline fun buildAsmAlgebra( algebra: Algebra, block: AsmExpressionAlgebra.() -> AsmExpression ): Expression = asm(AsmExpressionAlgebra(algebra), algebra, block) -inline fun asmSpace( +inline fun buildAsmSpace( algebra: Space, block: AsmExpressionSpace.() -> AsmExpression ): Expression = asm(AsmExpressionSpace(algebra), algebra, block) -inline fun asmRing( +inline fun buildAsmRing( algebra: Ring, block: AsmExpressionRing.() -> AsmExpression ): Expression = asm(AsmExpressionRing(algebra), algebra, block) -inline fun asmField( +inline fun buildAsmField( algebra: Field, block: AsmExpressionField.() -> AsmExpression ): Expression = asm(AsmExpressionField(algebra), algebra, block) diff --git a/kmath-asm/src/test/kotlin/scientifik/kmath/expressions/AsmTest.kt b/kmath-asm/src/test/kotlin/scientifik/kmath/expressions/AsmTest.kt index 93c52faac..034ba09c8 100644 --- a/kmath-asm/src/test/kotlin/scientifik/kmath/expressions/AsmTest.kt +++ b/kmath-asm/src/test/kotlin/scientifik/kmath/expressions/AsmTest.kt @@ -2,9 +2,7 @@ package scientifik.kmath.expressions import scientifik.kmath.expressions.asm.AsmExpression import scientifik.kmath.expressions.asm.AsmExpressionField -import scientifik.kmath.expressions.asm.asmField -import scientifik.kmath.expressions.asm.asmRing -import scientifik.kmath.operations.IntRing +import scientifik.kmath.expressions.asm.buildAsmField import scientifik.kmath.operations.RealField import kotlin.test.Test import kotlin.test.assertEquals @@ -14,7 +12,7 @@ class AsmTest { expected: Double?, arguments: Map = emptyMap(), block: AsmExpressionField.() -> AsmExpression - ): Unit = assertEquals(expected = expected, actual = asmField(RealField, block)(arguments)) + ): Unit = assertEquals(expected = expected, actual = buildAsmField(RealField, block)(arguments)) @Test fun testConstantsSum(): Unit = testDoubleExpression(16.0) { const(8.0) + 8.0 } diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/Builders.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/Builders.kt new file mode 100644 index 000000000..b2e4d8a3d --- /dev/null +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/Builders.kt @@ -0,0 +1,30 @@ +package scientifik.kmath.expressions + +import scientifik.kmath.operations.Algebra +import scientifik.kmath.operations.Field +import scientifik.kmath.operations.Ring +import scientifik.kmath.operations.Space + +/** + * Create a functional expression on this [Algebra] + */ +fun Algebra.buildExpression(block: FunctionalExpressionAlgebra.() -> Expression): Expression = + FunctionalExpressionAlgebra(this).run(block) + +/** + * Create a functional expression on this [Space] + */ +fun Space.buildExpression(block: FunctionalExpressionSpace.() -> Expression): Expression = + FunctionalExpressionSpace(this).run(block) + +/** + * Create a functional expression on this [Ring] + */ +fun Ring.buildExpression(block: FunctionalExpressionRing.() -> Expression): Expression = + FunctionalExpressionRing(this).run(block) + +/** + * Create a functional expression on this [Field] + */ +fun Field.buildExpression(block: FunctionalExpressionField.() -> Expression): Expression = + FunctionalExpressionField(this).run(block) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressions.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressions.kt index ce9b4756c..cbbdb99f5 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressions.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressions.kt @@ -33,7 +33,7 @@ internal class FunctionalConstProductExpression(val context: Space, val ex open class FunctionalExpressionAlgebra(val algebra: Algebra) : Algebra>, - ExpressionContext> { + ExpressionAlgebra> { override fun unaryOperation(operation: String, arg: Expression): Expression = FunctionalUnaryOperation(algebra, operation, arg) diff --git a/settings.gradle.kts b/settings.gradle.kts index 465ecfca8..2b2633060 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -45,5 +45,6 @@ include( ":kmath-for-real", ":kmath-geometry", ":kmath-ast", + ":kmath-asm", ":examples" ) -- 2.34.1 From 3ec1f7b5f15ada3527902c448f0e9517213a4354 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Sat, 13 Jun 2020 02:26:12 +0700 Subject: [PATCH 047/156] Merge kmath-asm and kmath-ast modules, make all the ExpressionAlgebras concise and consistent, implement new-styled builders both for ASM and F. expressions --- kmath-asm/build.gradle.kts | 10 -- .../expressions/asm/AsmExpressionSpaces.kt | 74 --------------- .../kmath/expressions/asm/Builders.kt | 50 ---------- .../scientifik/kmath/expressions/AsmTest.kt | 43 --------- kmath-ast/build.gradle.kts | 28 ++++-- .../scientifik/kmath}/asm/AsmExpressions.kt | 92 +++++++++++++++++-- .../kmath}/asm/AsmGenerationContext.kt | 10 +- .../kotlin/scientifik/kmath/asm/Builders.kt | 53 +++++++++++ .../scientifik/kmath}/asm/MethodVisitors.kt | 2 +- .../scientifik/kmath}/asm/Optimization.kt | 2 +- .../kmath/ast/{parser.kt => Parser.kt} | 0 .../kotlin/scientifik/kmath/ast/asm.kt | 23 ----- .../kotlin/scietifik/kmath/ast/AsmTest.kt | 18 ++++ .../kotlin/scietifik/kmath/ast/ParserTest.kt | 10 +- .../scientifik/kmath/expressions/Builders.kt | 13 +-- .../expressions/FunctionalExpressions.kt | 51 +++++----- settings.gradle.kts | 1 - 17 files changed, 220 insertions(+), 260 deletions(-) delete mode 100644 kmath-asm/build.gradle.kts delete mode 100644 kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/AsmExpressionSpaces.kt delete mode 100644 kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/Builders.kt delete mode 100644 kmath-asm/src/test/kotlin/scientifik/kmath/expressions/AsmTest.kt rename {kmath-asm/src/main/kotlin/scientifik/kmath/expressions => kmath-ast/src/jvmMain/kotlin/scientifik/kmath}/asm/AsmExpressions.kt (52%) rename {kmath-asm/src/main/kotlin/scientifik/kmath/expressions => kmath-ast/src/jvmMain/kotlin/scientifik/kmath}/asm/AsmGenerationContext.kt (96%) create mode 100644 kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/Builders.kt rename {kmath-asm/src/main/kotlin/scientifik/kmath/expressions => kmath-ast/src/jvmMain/kotlin/scientifik/kmath}/asm/MethodVisitors.kt (94%) rename {kmath-asm/src/main/kotlin/scientifik/kmath/expressions => kmath-ast/src/jvmMain/kotlin/scientifik/kmath}/asm/Optimization.kt (80%) rename kmath-ast/src/jvmMain/kotlin/scientifik/kmath/ast/{parser.kt => Parser.kt} (100%) delete mode 100644 kmath-ast/src/jvmMain/kotlin/scientifik/kmath/ast/asm.kt create mode 100644 kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/AsmTest.kt diff --git a/kmath-asm/build.gradle.kts b/kmath-asm/build.gradle.kts deleted file mode 100644 index 3b004eb8e..000000000 --- a/kmath-asm/build.gradle.kts +++ /dev/null @@ -1,10 +0,0 @@ -plugins { - id("scientifik.jvm") -} - -dependencies { - api(project(path = ":kmath-core")) - implementation("org.ow2.asm:asm:8.0.1") - implementation("org.ow2.asm:asm-commons:8.0.1") - implementation(kotlin("reflect")) -} diff --git a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/AsmExpressionSpaces.kt b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/AsmExpressionSpaces.kt deleted file mode 100644 index 0f8f685eb..000000000 --- a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/AsmExpressionSpaces.kt +++ /dev/null @@ -1,74 +0,0 @@ -package scientifik.kmath.expressions.asm - -import scientifik.kmath.expressions.ExpressionAlgebra -import scientifik.kmath.operations.* - -open class AsmExpressionAlgebra(val algebra: Algebra) : - Algebra>, - ExpressionAlgebra> { - override fun unaryOperation(operation: String, arg: AsmExpression): AsmExpression = - AsmUnaryOperation(algebra, operation, arg) - - override fun binaryOperation(operation: String, left: AsmExpression, right: AsmExpression): AsmExpression = - AsmBinaryOperation(algebra, operation, left, right) - - override fun const(value: T): AsmExpression = AsmConstantExpression(value) - override fun variable(name: String, default: T?): AsmExpression = AsmVariableExpression(name, default) -} - -open class AsmExpressionSpace( - val space: Space -) : AsmExpressionAlgebra(space), Space> { - override fun unaryOperation(operation: String, arg: AsmExpression): AsmExpression = - AsmUnaryOperation(algebra, operation, arg) - - override fun binaryOperation(operation: String, left: AsmExpression, right: AsmExpression): AsmExpression = - AsmBinaryOperation(algebra, operation, left, right) - - override val zero: AsmExpression = AsmConstantExpression(space.zero) - - override fun add(a: AsmExpression, b: AsmExpression): AsmExpression = - AsmBinaryOperation(space, SpaceOperations.PLUS_OPERATION, a, b) - - override fun multiply(a: AsmExpression, k: Number): AsmExpression = AsmConstProductExpression(space, a, k) - operator fun AsmExpression.plus(arg: T): AsmExpression = this + const(arg) - operator fun AsmExpression.minus(arg: T): AsmExpression = this - const(arg) - operator fun T.plus(arg: AsmExpression): AsmExpression = arg + this - operator fun T.minus(arg: AsmExpression): AsmExpression = arg - this -} - -open class AsmExpressionRing(private val ring: Ring) : AsmExpressionSpace(ring), Ring> { - override val one: AsmExpression - get() = const(this.ring.one) - - override fun unaryOperation(operation: String, arg: AsmExpression): AsmExpression = - AsmUnaryOperation(algebra, operation, arg) - - override fun binaryOperation(operation: String, left: AsmExpression, right: AsmExpression): AsmExpression = - AsmBinaryOperation(algebra, operation, left, right) - - override fun number(value: Number): AsmExpression = const(ring { one * value }) - - override fun multiply(a: AsmExpression, b: AsmExpression): AsmExpression = - AsmBinaryOperation(space, RingOperations.TIMES_OPERATION, a, b) - - operator fun AsmExpression.times(arg: T): AsmExpression = this * const(arg) - operator fun T.times(arg: AsmExpression): AsmExpression = arg * this -} - -open class AsmExpressionField(private val field: Field) : - AsmExpressionRing(field), - Field> { - - override fun unaryOperation(operation: String, arg: AsmExpression): AsmExpression = - AsmUnaryOperation(algebra, operation, arg) - - override fun binaryOperation(operation: String, left: AsmExpression, right: AsmExpression): AsmExpression = - AsmBinaryOperation(algebra, operation, left, right) - - override fun divide(a: AsmExpression, b: AsmExpression): AsmExpression = - AsmBinaryOperation(field, FieldOperations.DIV_OPERATION, a, b) - - operator fun AsmExpression.div(arg: T): AsmExpression = this / const(arg) - operator fun T.div(arg: AsmExpression): AsmExpression = arg / this -} diff --git a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/Builders.kt b/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/Builders.kt deleted file mode 100644 index c56adb767..000000000 --- a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/Builders.kt +++ /dev/null @@ -1,50 +0,0 @@ -package scientifik.kmath.expressions.asm - -import scientifik.kmath.expressions.Expression -import scientifik.kmath.expressions.ExpressionAlgebra -import scientifik.kmath.operations.* - -@PublishedApi -internal fun buildName(expression: AsmExpression<*>, collision: Int = 0): String { - val name = "scientifik.kmath.expressions.generated.AsmCompiledExpression_${expression.hashCode()}_$collision" - - try { - Class.forName(name) - } catch (ignored: ClassNotFoundException) { - return name - } - - return buildName(expression, collision + 1) -} - - -inline fun >> asm( - i: E, - algebra: Algebra, - block: E.() -> AsmExpression -): Expression { - val expression = i.block().optimize() - val ctx = AsmGenerationContext(T::class.java, algebra, buildName(expression)) - expression.invoke(ctx) - return ctx.generate() -} - -inline fun buildAsmAlgebra( - algebra: Algebra, - block: AsmExpressionAlgebra.() -> AsmExpression -): Expression = asm(AsmExpressionAlgebra(algebra), algebra, block) - -inline fun buildAsmSpace( - algebra: Space, - block: AsmExpressionSpace.() -> AsmExpression -): Expression = asm(AsmExpressionSpace(algebra), algebra, block) - -inline fun buildAsmRing( - algebra: Ring, - block: AsmExpressionRing.() -> AsmExpression -): Expression = asm(AsmExpressionRing(algebra), algebra, block) - -inline fun buildAsmField( - algebra: Field, - block: AsmExpressionField.() -> AsmExpression -): Expression = asm(AsmExpressionField(algebra), algebra, block) diff --git a/kmath-asm/src/test/kotlin/scientifik/kmath/expressions/AsmTest.kt b/kmath-asm/src/test/kotlin/scientifik/kmath/expressions/AsmTest.kt deleted file mode 100644 index 034ba09c8..000000000 --- a/kmath-asm/src/test/kotlin/scientifik/kmath/expressions/AsmTest.kt +++ /dev/null @@ -1,43 +0,0 @@ -package scientifik.kmath.expressions - -import scientifik.kmath.expressions.asm.AsmExpression -import scientifik.kmath.expressions.asm.AsmExpressionField -import scientifik.kmath.expressions.asm.buildAsmField -import scientifik.kmath.operations.RealField -import kotlin.test.Test -import kotlin.test.assertEquals - -class AsmTest { - private fun testDoubleExpression( - expected: Double?, - arguments: Map = emptyMap(), - block: AsmExpressionField.() -> AsmExpression - ): Unit = assertEquals(expected = expected, actual = buildAsmField(RealField, block)(arguments)) - - @Test - fun testConstantsSum(): Unit = testDoubleExpression(16.0) { const(8.0) + 8.0 } - - @Test - fun testVarsSum(): Unit = testDoubleExpression(1000.0, mapOf("x" to 500.0)) { variable("x") + 500.0 } - - @Test - fun testProduct(): Unit = testDoubleExpression(24.0) { const(4.0) * const(6.0) } - - @Test - fun testConstantProduct(): Unit = testDoubleExpression(984.0) { const(8.0) * 123 } - - @Test - fun testVarsConstantProductVar(): Unit = testDoubleExpression(984.0, mapOf("x" to 8.0)) { variable("x") * 123 } - - @Test - fun testSubtraction(): Unit = testDoubleExpression(2.0) { const(4.0) - 2.0 } - - @Test - fun testDivision(): Unit = testDoubleExpression(64.0) { const(128.0) / 2 } - - @Test - fun testDirectUnaryCall(): Unit = testDoubleExpression(64.0) { unaryOperation("+", const(64.0)) } - - @Test - fun testDirectBinaryCall(): Unit = testDoubleExpression(4096.0) { binaryOperation("*", const(64.0), const(64.0)) } -} diff --git a/kmath-ast/build.gradle.kts b/kmath-ast/build.gradle.kts index 88540e7b8..deb6a28ee 100644 --- a/kmath-ast/build.gradle.kts +++ b/kmath-ast/build.gradle.kts @@ -1,10 +1,5 @@ -plugins { - id("scientifik.mpp") -} - -repositories{ - maven("https://dl.bintray.com/hotkeytlt/maven") -} +plugins { id("scientifik.mpp") } +repositories { maven("https://dl.bintray.com/hotkeytlt/maven") } kotlin.sourceSets { commonMain { @@ -13,9 +8,22 @@ kotlin.sourceSets { implementation("com.github.h0tk3y.betterParse:better-parse-multiplatform:0.4.0-alpha-3") } } - jvmMain{ - dependencies{ + + jvmMain { + dependencies { implementation("com.github.h0tk3y.betterParse:better-parse-jvm:0.4.0-alpha-3") + implementation("org.ow2.asm:asm:8.0.1") + implementation("org.ow2.asm:asm-commons:8.0.1") + implementation(kotlin("reflect")) } } -} \ No newline at end of file + + jvmTest { + dependencies { + implementation(kotlin("test")) + implementation(kotlin("test-junit5")) + } + } +} + +tasks.withType { useJUnitPlatform() } diff --git a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/AsmExpressions.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmExpressions.kt similarity index 52% rename from kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/AsmExpressions.kt rename to kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmExpressions.kt index 70f1fc28e..75b5f26d0 100644 --- a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/AsmExpressions.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmExpressions.kt @@ -1,9 +1,8 @@ -package scientifik.kmath.expressions.asm +package scientifik.kmath.asm import scientifik.kmath.expressions.Expression -import scientifik.kmath.operations.Algebra -import scientifik.kmath.operations.Space -import scientifik.kmath.operations.invoke +import scientifik.kmath.expressions.ExpressionAlgebra +import scientifik.kmath.operations.* import kotlin.reflect.full.memberFunctions import kotlin.reflect.jvm.jvmName @@ -109,11 +108,13 @@ internal class AsmBinaryOperation( } } -internal class AsmVariableExpression(private val name: String, private val default: T? = null) : AsmExpression { +internal class AsmVariableExpression(private val name: String, private val default: T? = null) : + AsmExpression { override fun invoke(gen: AsmGenerationContext): Unit = gen.visitLoadFromVariables(name, default) } -internal class AsmConstantExpression(private val value: T) : AsmExpression { +internal class AsmConstantExpression(private val value: T) : + AsmExpression { override fun tryEvaluate(): T = value override fun invoke(gen: AsmGenerationContext): Unit = gen.visitLoadFromConstants(value) } @@ -141,9 +142,88 @@ internal class AsmConstProductExpression( } } +internal class AsmNumberExpression(private val context: NumericAlgebra, private val value: Number) : AsmExpression { + override fun tryEvaluate(): T? = context.number(value) + + override fun invoke(gen: AsmGenerationContext): Unit = gen.visitNumberConstant(value) +} + internal abstract class FunctionalCompiledExpression internal constructor( @JvmField protected val algebra: Algebra, @JvmField protected val constants: Array ) : Expression { abstract override fun invoke(arguments: Map): T } + +interface AsmExpressionAlgebra> : NumericAlgebra>, + ExpressionAlgebra> { + val algebra: A + override fun binaryOperation(operation: String, left: AsmExpression, right: AsmExpression): AsmExpression = + AsmBinaryOperation(algebra, operation, left, right) + + override fun unaryOperation(operation: String, arg: AsmExpression): AsmExpression = + AsmUnaryOperation(algebra, operation, arg) + + override fun number(value: Number): AsmExpression = AsmNumberExpression(algebra, value) + override fun const(value: T): AsmExpression = AsmConstantExpression(value) + override fun variable(name: String, default: T?): AsmExpression = AsmVariableExpression(name, default) +} + +open class AsmExpressionSpace(override val algebra: A) : AsmExpressionAlgebra, + Space> where A : Space, A : NumericAlgebra { + override val zero: AsmExpression + get() = const(algebra.zero) + + override fun binaryOperation(operation: String, left: AsmExpression, right: AsmExpression): AsmExpression = + AsmBinaryOperation(algebra, operation, left, right) + + override fun unaryOperation(operation: String, arg: AsmExpression): AsmExpression = + AsmUnaryOperation(algebra, operation, arg) + + override fun add(a: AsmExpression, b: AsmExpression): AsmExpression = + AsmBinaryOperation(algebra, SpaceOperations.PLUS_OPERATION, a, b) + + override fun multiply(a: AsmExpression, k: Number): AsmExpression = AsmConstProductExpression(algebra, a, k) + operator fun AsmExpression.plus(arg: T): AsmExpression = this + const(arg) + operator fun AsmExpression.minus(arg: T): AsmExpression = this - const(arg) + operator fun T.plus(arg: AsmExpression): AsmExpression = arg + this + operator fun T.minus(arg: AsmExpression): AsmExpression = arg - this +} + +open class AsmExpressionRing(override val algebra: A) : AsmExpressionSpace(algebra), + Ring> where A : Ring, A : NumericAlgebra { + override val one: AsmExpression + get() = const(algebra.one) + + override fun unaryOperation(operation: String, arg: AsmExpression): AsmExpression = + AsmUnaryOperation(algebra, operation, arg) + + override fun binaryOperation(operation: String, left: AsmExpression, right: AsmExpression): AsmExpression = + AsmBinaryOperation(algebra, operation, left, right) + + override fun number(value: Number): AsmExpression = const(algebra { one * value }) + + override fun multiply(a: AsmExpression, b: AsmExpression): AsmExpression = + AsmBinaryOperation(algebra, RingOperations.TIMES_OPERATION, a, b) + + operator fun AsmExpression.times(arg: T): AsmExpression = this * const(arg) + operator fun T.times(arg: AsmExpression): AsmExpression = arg * this +} + +open class AsmExpressionField(override val algebra: A) : + AsmExpressionRing(algebra), + Field> where A : Field, A : NumericAlgebra { + + override fun unaryOperation(operation: String, arg: AsmExpression): AsmExpression = + AsmUnaryOperation(algebra, operation, arg) + + override fun binaryOperation(operation: String, left: AsmExpression, right: AsmExpression): AsmExpression = + AsmBinaryOperation(algebra, operation, left, right) + + override fun divide(a: AsmExpression, b: AsmExpression): AsmExpression = + AsmBinaryOperation(algebra, FieldOperations.DIV_OPERATION, a, b) + + operator fun AsmExpression.div(arg: T): AsmExpression = this / const(arg) + operator fun T.div(arg: AsmExpression): AsmExpression = arg / this +} + diff --git a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/AsmGenerationContext.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmGenerationContext.kt similarity index 96% rename from kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/AsmGenerationContext.kt rename to kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmGenerationContext.kt index 92663ba8f..d9dba746d 100644 --- a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/AsmGenerationContext.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmGenerationContext.kt @@ -1,4 +1,4 @@ -package scientifik.kmath.expressions.asm +package scientifik.kmath.asm import org.objectweb.asm.ClassWriter import org.objectweb.asm.Label @@ -15,7 +15,8 @@ class AsmGenerationContext( internal fun defineClass(name: String?, b: ByteArray): Class<*> = defineClass(name, b, 0, b.size) } - private val classLoader: ClassLoader = ClassLoader(javaClass.classLoader) + private val classLoader: ClassLoader = + ClassLoader(javaClass.classLoader) @Suppress("PrivatePropertyName") private val T_ALGEBRA_CLASS: String = algebra.javaClass.name.replace(oldChar = '.', newChar = '/') @@ -236,7 +237,8 @@ class AsmGenerationContext( } visitLdcInsn(name) - visitMethodInsn(Opcodes.INVOKEINTERFACE, MAP_CLASS, "get", "(L$OBJECT_CLASS;)L$OBJECT_CLASS;", true) + visitMethodInsn(Opcodes.INVOKEINTERFACE, + MAP_CLASS, "get", "(L$OBJECT_CLASS;)L$OBJECT_CLASS;", true) visitCastToT() } @@ -275,7 +277,7 @@ class AsmGenerationContext( ) internal const val FUNCTIONAL_COMPILED_EXPRESSION_CLASS = - "scientifik/kmath/expressions/asm/FunctionalCompiledExpression" + "scientifik/kmath/asm/FunctionalCompiledExpression" internal const val MAP_CLASS = "java/util/Map" internal const val OBJECT_CLASS = "java/lang/Object" diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/Builders.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/Builders.kt new file mode 100644 index 000000000..67870e07e --- /dev/null +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/Builders.kt @@ -0,0 +1,53 @@ +package scientifik.kmath.asm + +import scientifik.kmath.ast.MST +import scientifik.kmath.ast.evaluate +import scientifik.kmath.expressions.Expression +import scientifik.kmath.operations.* + +@PublishedApi +internal fun buildName(expression: AsmExpression<*>, collision: Int = 0): String { + val name = "scientifik.kmath.expressions.generated.AsmCompiledExpression_${expression.hashCode()}_$collision" + + try { + Class.forName(name) + } catch (ignored: ClassNotFoundException) { + return name + } + + return buildName(expression, collision + 1) +} + +inline fun AsmExpression.compile(algebra: Algebra): Expression { + val ctx = AsmGenerationContext(T::class.java, algebra, buildName(this)) + invoke(ctx) + return ctx.generate() +} + +inline fun , E : AsmExpressionAlgebra> A.asm( + expressionAlgebra: E, + block: E.() -> AsmExpression +): Expression = expressionAlgebra.block().compile(expressionAlgebra.algebra) + +inline fun , E : AsmExpressionAlgebra> A.asm( + expressionAlgebra: E, + ast: MST +): Expression = asm(expressionAlgebra) { evaluate(ast) } + +inline fun A.asmSpace(block: AsmExpressionSpace.() -> AsmExpression): Expression where A : NumericAlgebra, A : Space = + AsmExpressionSpace(this).let { it.block().compile(it.algebra) } + +inline fun A.asmSpace(ast: MST): Expression where A : NumericAlgebra, A : Space = + asmSpace { evaluate(ast) } + +inline fun A.asmRing(block: AsmExpressionRing.() -> AsmExpression): Expression where A : NumericAlgebra, A : Ring = + AsmExpressionRing(this).let { it.block().compile(it.algebra) } + +inline fun A.asmRing(ast: MST): Expression where A : NumericAlgebra, A : Ring = + asmRing { evaluate(ast) } + +inline fun A.asmField(block: AsmExpressionField.() -> AsmExpression): Expression where A : NumericAlgebra, A : Field = + AsmExpressionField(this).let { it.block().compile(it.algebra) } + +inline fun A.asmField(ast: MST): Expression where A : NumericAlgebra, A : Field = + asmRing { evaluate(ast) } diff --git a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/MethodVisitors.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/MethodVisitors.kt similarity index 94% rename from kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/MethodVisitors.kt rename to kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/MethodVisitors.kt index 9f697ab6d..42565b6ba 100644 --- a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/MethodVisitors.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/MethodVisitors.kt @@ -1,4 +1,4 @@ -package scientifik.kmath.expressions.asm +package scientifik.kmath.asm import org.objectweb.asm.MethodVisitor import org.objectweb.asm.Opcodes.* diff --git a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/Optimization.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/Optimization.kt similarity index 80% rename from kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/Optimization.kt rename to kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/Optimization.kt index bcd182dc3..e821bd206 100644 --- a/kmath-asm/src/main/kotlin/scientifik/kmath/expressions/asm/Optimization.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/Optimization.kt @@ -1,4 +1,4 @@ -package scientifik.kmath.expressions.asm +package scientifik.kmath.asm @PublishedApi internal fun AsmExpression.optimize(): AsmExpression { diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/ast/parser.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/ast/Parser.kt similarity index 100% rename from kmath-ast/src/jvmMain/kotlin/scientifik/kmath/ast/parser.kt rename to kmath-ast/src/jvmMain/kotlin/scientifik/kmath/ast/Parser.kt diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/ast/asm.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/ast/asm.kt deleted file mode 100644 index c01648fb0..000000000 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/ast/asm.kt +++ /dev/null @@ -1,23 +0,0 @@ -package scientifik.kmath.ast - -import scientifik.kmath.expressions.Expression -import scientifik.kmath.operations.Algebra -import scientifik.kmath.operations.NumericAlgebra - -//TODO stubs for asm generation - -interface AsmExpression - -interface AsmExpressionAlgebra> : NumericAlgebra> { - val algebra: A -} - -fun AsmExpression.compile(): Expression = TODO() - -//TODO add converter for functional expressions - -inline fun > A.asm( - block: AsmExpressionAlgebra.() -> AsmExpression -): Expression = TODO() - -inline fun > A.asm(ast: MST): Expression = asm { evaluate(ast) } \ No newline at end of file diff --git a/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/AsmTest.kt b/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/AsmTest.kt new file mode 100644 index 000000000..73058face --- /dev/null +++ b/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/AsmTest.kt @@ -0,0 +1,18 @@ +package scietifik.kmath.ast + +import org.junit.jupiter.api.Test +import scientifik.kmath.asm.asmField +import scientifik.kmath.ast.parseMath +import scientifik.kmath.expressions.invoke +import scientifik.kmath.operations.Complex +import scientifik.kmath.operations.ComplexField +import kotlin.test.assertEquals + +class AsmTest { + @Test + fun parsedExpression() { + val mst = "2+2*(2+2)".parseMath() + val res = ComplexField.asmField(mst)() + assertEquals(Complex(10.0, 0.0), res) + } +} diff --git a/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/ParserTest.kt b/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/ParserTest.kt index 6849d24b8..ddac07786 100644 --- a/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/ParserTest.kt +++ b/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/ParserTest.kt @@ -1,17 +1,17 @@ package scietifik.kmath.ast -import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Test import scientifik.kmath.ast.evaluate import scientifik.kmath.ast.parseMath import scientifik.kmath.operations.Complex import scientifik.kmath.operations.ComplexField +import kotlin.test.assertEquals -internal class ParserTest{ +internal class ParserTest { @Test - fun parsedExpression(){ + fun parsedExpression() { val mst = "2+2*(2+2)".parseMath() val res = ComplexField.evaluate(mst) - assertEquals(Complex(10.0,0.0), res) + assertEquals(Complex(10.0, 0.0), res) } -} \ No newline at end of file +} diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/Builders.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/Builders.kt index b2e4d8a3d..cdd6f695b 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/Builders.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/Builders.kt @@ -1,30 +1,23 @@ package scientifik.kmath.expressions -import scientifik.kmath.operations.Algebra import scientifik.kmath.operations.Field import scientifik.kmath.operations.Ring import scientifik.kmath.operations.Space -/** - * Create a functional expression on this [Algebra] - */ -fun Algebra.buildExpression(block: FunctionalExpressionAlgebra.() -> Expression): Expression = - FunctionalExpressionAlgebra(this).run(block) - /** * Create a functional expression on this [Space] */ -fun Space.buildExpression(block: FunctionalExpressionSpace.() -> Expression): Expression = +fun Space.buildExpression(block: FunctionalExpressionSpace>.() -> Expression): Expression = FunctionalExpressionSpace(this).run(block) /** * Create a functional expression on this [Ring] */ -fun Ring.buildExpression(block: FunctionalExpressionRing.() -> Expression): Expression = +fun Ring.buildExpression(block: FunctionalExpressionRing>.() -> Expression): Expression = FunctionalExpressionRing(this).run(block) /** * Create a functional expression on this [Field] */ -fun Field.buildExpression(block: FunctionalExpressionField.() -> Expression): Expression = +fun Field.buildExpression(block: FunctionalExpressionField>.() -> Expression): Expression = FunctionalExpressionField(this).run(block) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressions.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressions.kt index cbbdb99f5..88d8c87b7 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressions.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressions.kt @@ -26,48 +26,55 @@ internal class FunctionalConstantExpression(val value: T) : Expression { override fun invoke(arguments: Map): T = value } -internal class FunctionalConstProductExpression(val context: Space, val expr: Expression, val const: Number) : +internal class FunctionalConstProductExpression( + val context: Space, + val expr: Expression, + val const: Number +) : Expression { override fun invoke(arguments: Map): T = context.multiply(expr.invoke(arguments), const) } -open class FunctionalExpressionAlgebra(val algebra: Algebra) : - Algebra>, - ExpressionAlgebra> { - override fun unaryOperation(operation: String, arg: Expression): Expression = - FunctionalUnaryOperation(algebra, operation, arg) +interface FunctionalExpressionAlgebra> : ExpressionAlgebra> { + val algebra: A override fun binaryOperation(operation: String, left: Expression, right: Expression): Expression = FunctionalBinaryOperation(algebra, operation, left, right) + override fun unaryOperation(operation: String, arg: Expression): Expression = + FunctionalUnaryOperation(algebra, operation, arg) + override fun const(value: T): Expression = FunctionalConstantExpression(value) override fun variable(name: String, default: T?): Expression = FunctionalVariableExpression(name, default) } -open class FunctionalExpressionSpace(val space: Space) : - FunctionalExpressionAlgebra(space), - Space> { - override fun unaryOperation(operation: String, arg: Expression): Expression = - FunctionalUnaryOperation(algebra, operation, arg) +open class FunctionalExpressionSpace(override val algebra: A) : FunctionalExpressionAlgebra, + Space> where A : Space { + override val zero: Expression + get() = const(algebra.zero) override fun binaryOperation(operation: String, left: Expression, right: Expression): Expression = FunctionalBinaryOperation(algebra, operation, left, right) - override val zero: Expression = FunctionalConstantExpression(space.zero) + override fun unaryOperation(operation: String, arg: Expression): Expression = + FunctionalUnaryOperation(algebra, operation, arg) override fun add(a: Expression, b: Expression): Expression = - FunctionalBinaryOperation(space, SpaceOperations.PLUS_OPERATION, a, b) + FunctionalBinaryOperation(algebra, SpaceOperations.PLUS_OPERATION, a, b) + + override fun multiply(a: Expression, k: Number): Expression = + FunctionalConstProductExpression(algebra, a, k) - override fun multiply(a: Expression, k: Number): Expression = FunctionalConstProductExpression(space, a, k) operator fun Expression.plus(arg: T): Expression = this + const(arg) operator fun Expression.minus(arg: T): Expression = this - const(arg) operator fun T.plus(arg: Expression): Expression = arg + this operator fun T.minus(arg: Expression): Expression = arg - this } -open class FunctionalExpressionRing(val ring: Ring) : FunctionalExpressionSpace(ring), Ring> { +open class FunctionalExpressionRing(override val algebra: A) : FunctionalExpressionSpace(algebra), + Ring> where A : Ring, A : NumericAlgebra { override val one: Expression - get() = const(this.ring.one) + get() = const(algebra.one) override fun unaryOperation(operation: String, arg: Expression): Expression = FunctionalUnaryOperation(algebra, operation, arg) @@ -75,18 +82,18 @@ open class FunctionalExpressionRing(val ring: Ring) : FunctionalExpression override fun binaryOperation(operation: String, left: Expression, right: Expression): Expression = FunctionalBinaryOperation(algebra, operation, left, right) - override fun number(value: Number): Expression = const(ring { one * value }) + override fun number(value: Number): Expression = const(algebra { one * value }) override fun multiply(a: Expression, b: Expression): Expression = - FunctionalBinaryOperation(space, RingOperations.TIMES_OPERATION, a, b) + FunctionalBinaryOperation(algebra, RingOperations.TIMES_OPERATION, a, b) operator fun Expression.times(arg: T): Expression = this * const(arg) operator fun T.times(arg: Expression): Expression = arg * this } -open class FunctionalExpressionField(val field: Field) : - FunctionalExpressionRing(field), - Field> { +open class FunctionalExpressionField(override val algebra: A) : + FunctionalExpressionRing(algebra), + Field> where A : Field, A : NumericAlgebra { override fun unaryOperation(operation: String, arg: Expression): Expression = FunctionalUnaryOperation(algebra, operation, arg) @@ -95,7 +102,7 @@ open class FunctionalExpressionField(val field: Field) : FunctionalBinaryOperation(algebra, operation, left, right) override fun divide(a: Expression, b: Expression): Expression = - FunctionalBinaryOperation(space, FieldOperations.DIV_OPERATION, a, b) + FunctionalBinaryOperation(algebra, FieldOperations.DIV_OPERATION, a, b) operator fun Expression.div(arg: T): Expression = this / const(arg) operator fun T.div(arg: Expression): Expression = arg / this diff --git a/settings.gradle.kts b/settings.gradle.kts index 2b2633060..465ecfca8 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -45,6 +45,5 @@ include( ":kmath-for-real", ":kmath-geometry", ":kmath-ast", - ":kmath-asm", ":examples" ) -- 2.34.1 From 36ad1fcf589e0f8e9b4701f463360d7d66c80c41 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Sat, 13 Jun 2020 15:44:54 +0700 Subject: [PATCH 048/156] Minor refactor and document --- .../scientifik/kmath/asm/AsmExpressions.kt | 101 +++++++++++++----- .../kmath/asm/AsmGenerationContext.kt | 12 ++- .../expressions/FunctionalExpressions.kt | 84 ++++++++++----- 3 files changed, 141 insertions(+), 56 deletions(-) diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmExpressions.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmExpressions.kt index 75b5f26d0..ef579278a 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmExpressions.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmExpressions.kt @@ -142,7 +142,8 @@ internal class AsmConstProductExpression( } } -internal class AsmNumberExpression(private val context: NumericAlgebra, private val value: Number) : AsmExpression { +internal class AsmNumberExpression(private val context: NumericAlgebra, private val value: Number) : + AsmExpression { override fun tryEvaluate(): T? = context.number(value) override fun invoke(gen: AsmGenerationContext): Unit = gen.visitNumberConstant(value) @@ -155,75 +156,121 @@ internal abstract class FunctionalCompiledExpression internal constructor( abstract override fun invoke(arguments: Map): T } +/** + * A context class for [AsmExpression] construction. + */ interface AsmExpressionAlgebra> : NumericAlgebra>, ExpressionAlgebra> { + /** + * The algebra to provide for AsmExpressions built. + */ val algebra: A + + /** + * Builds an AsmExpression to wrap a number. + */ + override fun number(value: Number): AsmExpression = AsmNumberExpression(algebra, value) + + /** + * Builds an AsmExpression of constant expression which does not depend on arguments. + */ + override fun const(value: T): AsmExpression = AsmConstantExpression(value) + + /** + * Builds an AsmExpression to access a variable. + */ + override fun variable(name: String, default: T?): AsmExpression = AsmVariableExpression(name, default) + + /** + * Builds an AsmExpression of dynamic call of binary operation [operation] on [left] and [right]. + */ override fun binaryOperation(operation: String, left: AsmExpression, right: AsmExpression): AsmExpression = AsmBinaryOperation(algebra, operation, left, right) + /** + * Builds an AsmExpression of dynamic call of unary operation with name [operation] on [arg]. + */ override fun unaryOperation(operation: String, arg: AsmExpression): AsmExpression = AsmUnaryOperation(algebra, operation, arg) - - override fun number(value: Number): AsmExpression = AsmNumberExpression(algebra, value) - override fun const(value: T): AsmExpression = AsmConstantExpression(value) - override fun variable(name: String, default: T?): AsmExpression = AsmVariableExpression(name, default) } +/** + * A context class for [AsmExpression] construction for [Space] algebras. + */ open class AsmExpressionSpace(override val algebra: A) : AsmExpressionAlgebra, Space> where A : Space, A : NumericAlgebra { override val zero: AsmExpression get() = const(algebra.zero) - override fun binaryOperation(operation: String, left: AsmExpression, right: AsmExpression): AsmExpression = - AsmBinaryOperation(algebra, operation, left, right) - - override fun unaryOperation(operation: String, arg: AsmExpression): AsmExpression = - AsmUnaryOperation(algebra, operation, arg) - + /** + * Builds an AsmExpression of addition of two another expressions. + */ override fun add(a: AsmExpression, b: AsmExpression): AsmExpression = AsmBinaryOperation(algebra, SpaceOperations.PLUS_OPERATION, a, b) + /** + * Builds an AsmExpression of multiplication of expression by number. + */ override fun multiply(a: AsmExpression, k: Number): AsmExpression = AsmConstProductExpression(algebra, a, k) + operator fun AsmExpression.plus(arg: T): AsmExpression = this + const(arg) operator fun AsmExpression.minus(arg: T): AsmExpression = this - const(arg) operator fun T.plus(arg: AsmExpression): AsmExpression = arg + this operator fun T.minus(arg: AsmExpression): AsmExpression = arg - this + + override fun unaryOperation(operation: String, arg: AsmExpression): AsmExpression = + super.unaryOperation(operation, arg) + + override fun binaryOperation(operation: String, left: AsmExpression, right: AsmExpression): AsmExpression = + super.binaryOperation(operation, left, right) } +/** + * A context class for [AsmExpression] construction for [Ring] algebras. + */ open class AsmExpressionRing(override val algebra: A) : AsmExpressionSpace(algebra), Ring> where A : Ring, A : NumericAlgebra { override val one: AsmExpression get() = const(algebra.one) - override fun unaryOperation(operation: String, arg: AsmExpression): AsmExpression = - AsmUnaryOperation(algebra, operation, arg) - - override fun binaryOperation(operation: String, left: AsmExpression, right: AsmExpression): AsmExpression = - AsmBinaryOperation(algebra, operation, left, right) - - override fun number(value: Number): AsmExpression = const(algebra { one * value }) - + /** + * Builds an AsmExpression of multiplication of two expressions. + */ override fun multiply(a: AsmExpression, b: AsmExpression): AsmExpression = AsmBinaryOperation(algebra, RingOperations.TIMES_OPERATION, a, b) operator fun AsmExpression.times(arg: T): AsmExpression = this * const(arg) operator fun T.times(arg: AsmExpression): AsmExpression = arg * this + + override fun unaryOperation(operation: String, arg: AsmExpression): AsmExpression = + super.unaryOperation(operation, arg) + + override fun binaryOperation(operation: String, left: AsmExpression, right: AsmExpression): AsmExpression = + super.binaryOperation(operation, left, right) + + override fun number(value: Number): AsmExpression = super.number(value) } +/** + * A context class for [AsmExpression] construction for [Field] algebras. + */ open class AsmExpressionField(override val algebra: A) : AsmExpressionRing(algebra), Field> where A : Field, A : NumericAlgebra { - - override fun unaryOperation(operation: String, arg: AsmExpression): AsmExpression = - AsmUnaryOperation(algebra, operation, arg) - - override fun binaryOperation(operation: String, left: AsmExpression, right: AsmExpression): AsmExpression = - AsmBinaryOperation(algebra, operation, left, right) - + /** + * Builds an AsmExpression of division an expression by another one. + */ override fun divide(a: AsmExpression, b: AsmExpression): AsmExpression = AsmBinaryOperation(algebra, FieldOperations.DIV_OPERATION, a, b) operator fun AsmExpression.div(arg: T): AsmExpression = this / const(arg) operator fun T.div(arg: AsmExpression): AsmExpression = arg / this -} + override fun unaryOperation(operation: String, arg: AsmExpression): AsmExpression = + super.unaryOperation(operation, arg) + + override fun binaryOperation(operation: String, left: AsmExpression, right: AsmExpression): AsmExpression = + super.binaryOperation(operation, left, right) + + override fun number(value: Number): AsmExpression = super.number(value) +} diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmGenerationContext.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmGenerationContext.kt index d9dba746d..69a2b6c85 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmGenerationContext.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmGenerationContext.kt @@ -6,6 +6,15 @@ import org.objectweb.asm.MethodVisitor import org.objectweb.asm.Opcodes import scientifik.kmath.operations.Algebra +/** + * AsmGenerationContext is a structure that abstracts building a class that unwraps [AsmExpression] to plain Java + * expression. This class uses [ClassLoader] for loading the generated class, then it is able to instantiate the new + * class. + * + * @param T the type of AsmExpression to unwrap. + * @param algebra the algebra the applied AsmExpressions use. + * @param className the unique class name of new loaded class. + */ class AsmGenerationContext( classOfT: Class<*>, private val algebra: Algebra, @@ -29,8 +38,7 @@ class AsmGenerationContext( private val invokeArgumentsVar: Int = 1 private var maxStack: Int = 0 private val constants: MutableList = mutableListOf() - private val asmCompiledClassWriter: ClassWriter = - ClassWriter(0) + private val asmCompiledClassWriter: ClassWriter = ClassWriter(0) private val invokeMethodVisitor: MethodVisitor private val invokeL0: Label private lateinit var invokeL1: Label diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressions.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressions.kt index 88d8c87b7..c3f36a814 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressions.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressions.kt @@ -28,40 +28,61 @@ internal class FunctionalConstantExpression(val value: T) : Expression { internal class FunctionalConstProductExpression( val context: Space, - val expr: Expression, + private val expr: Expression, val const: Number -) : - Expression { +) : Expression { override fun invoke(arguments: Map): T = context.multiply(expr.invoke(arguments), const) } +/** + * A context class for [Expression] construction. + */ interface FunctionalExpressionAlgebra> : ExpressionAlgebra> { + /** + * The algebra to provide for Expressions built. + */ val algebra: A + /** + * Builds an Expression of constant expression which does not depend on arguments. + */ + override fun const(value: T): Expression = FunctionalConstantExpression(value) + + /** + * Builds an Expression to access a variable. + */ + override fun variable(name: String, default: T?): Expression = FunctionalVariableExpression(name, default) + + /** + * Builds an Expression of dynamic call of binary operation [operation] on [left] and [right]. + */ override fun binaryOperation(operation: String, left: Expression, right: Expression): Expression = FunctionalBinaryOperation(algebra, operation, left, right) + /** + * Builds an Expression of dynamic call of unary operation with name [operation] on [arg]. + */ override fun unaryOperation(operation: String, arg: Expression): Expression = FunctionalUnaryOperation(algebra, operation, arg) - - override fun const(value: T): Expression = FunctionalConstantExpression(value) - override fun variable(name: String, default: T?): Expression = FunctionalVariableExpression(name, default) } +/** + * A context class for [Expression] construction for [Space] algebras. + */ open class FunctionalExpressionSpace(override val algebra: A) : FunctionalExpressionAlgebra, Space> where A : Space { override val zero: Expression get() = const(algebra.zero) - override fun binaryOperation(operation: String, left: Expression, right: Expression): Expression = - FunctionalBinaryOperation(algebra, operation, left, right) - - override fun unaryOperation(operation: String, arg: Expression): Expression = - FunctionalUnaryOperation(algebra, operation, arg) - + /** + * Builds an Expression of addition of two another expressions. + */ override fun add(a: Expression, b: Expression): Expression = FunctionalBinaryOperation(algebra, SpaceOperations.PLUS_OPERATION, a, b) + /** + * Builds an Expression of multiplication of expression by number. + */ override fun multiply(a: Expression, k: Number): Expression = FunctionalConstProductExpression(algebra, a, k) @@ -69,6 +90,12 @@ open class FunctionalExpressionSpace(override val algebra: A) : Functional operator fun Expression.minus(arg: T): Expression = this - const(arg) operator fun T.plus(arg: Expression): Expression = arg + this operator fun T.minus(arg: Expression): Expression = arg - this + + override fun unaryOperation(operation: String, arg: Expression): Expression = + super.unaryOperation(operation, arg) + + override fun binaryOperation(operation: String, left: Expression, right: Expression): Expression = + super.binaryOperation(operation, left, right) } open class FunctionalExpressionRing(override val algebra: A) : FunctionalExpressionSpace(algebra), @@ -76,34 +103,37 @@ open class FunctionalExpressionRing(override val algebra: A) : FunctionalE override val one: Expression get() = const(algebra.one) - override fun unaryOperation(operation: String, arg: Expression): Expression = - FunctionalUnaryOperation(algebra, operation, arg) - - override fun binaryOperation(operation: String, left: Expression, right: Expression): Expression = - FunctionalBinaryOperation(algebra, operation, left, right) - - override fun number(value: Number): Expression = const(algebra { one * value }) - + /** + * Builds an Expression of multiplication of two expressions. + */ override fun multiply(a: Expression, b: Expression): Expression = FunctionalBinaryOperation(algebra, RingOperations.TIMES_OPERATION, a, b) operator fun Expression.times(arg: T): Expression = this * const(arg) operator fun T.times(arg: Expression): Expression = arg * this + + override fun unaryOperation(operation: String, arg: Expression): Expression = + super.unaryOperation(operation, arg) + + override fun binaryOperation(operation: String, left: Expression, right: Expression): Expression = + super.binaryOperation(operation, left, right) } open class FunctionalExpressionField(override val algebra: A) : FunctionalExpressionRing(algebra), Field> where A : Field, A : NumericAlgebra { - - override fun unaryOperation(operation: String, arg: Expression): Expression = - FunctionalUnaryOperation(algebra, operation, arg) - - override fun binaryOperation(operation: String, left: Expression, right: Expression): Expression = - FunctionalBinaryOperation(algebra, operation, left, right) - + /** + * Builds an Expression of division an expression by another one. + */ override fun divide(a: Expression, b: Expression): Expression = FunctionalBinaryOperation(algebra, FieldOperations.DIV_OPERATION, a, b) operator fun Expression.div(arg: T): Expression = this / const(arg) operator fun T.div(arg: Expression): Expression = arg / this + + override fun unaryOperation(operation: String, arg: Expression): Expression = + super.unaryOperation(operation, arg) + + override fun binaryOperation(operation: String, left: Expression, right: Expression): Expression = + super.binaryOperation(operation, left, right) } -- 2.34.1 From fec8c7f9d19118f38188d804fd8248aeb8fbafe8 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Sat, 13 Jun 2020 15:50:21 +0700 Subject: [PATCH 049/156] Minor refactor and encapsulation --- .../jvmMain/kotlin/scientifik/kmath/asm/AsmExpressions.kt | 6 ++++-- .../scientifik/kmath/expressions/FunctionalExpressions.kt | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmExpressions.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmExpressions.kt index ef579278a..9e6efb66b 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmExpressions.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmExpressions.kt @@ -11,7 +11,7 @@ interface AsmExpression { fun invoke(gen: AsmGenerationContext) } -internal val methodNameAdapters = mapOf("+" to "add", "*" to "multiply", "/" to "divide") +private val methodNameAdapters: Map = mapOf("+" to "add", "*" to "multiply", "/" to "divide") internal fun hasSpecific(context: Algebra, name: String, arity: Int): Boolean { val aName = methodNameAdapters[name] ?: name @@ -137,7 +137,9 @@ internal class AsmConstProductExpression( gen.visitAlgebraOperation( owner = AsmGenerationContext.SPACE_OPERATIONS_CLASS, method = "multiply", - descriptor = "(L${AsmGenerationContext.OBJECT_CLASS};L${AsmGenerationContext.NUMBER_CLASS};)L${AsmGenerationContext.OBJECT_CLASS};" + descriptor = "(L${AsmGenerationContext.OBJECT_CLASS};" + + "L${AsmGenerationContext.NUMBER_CLASS};)" + + "L${AsmGenerationContext.OBJECT_CLASS};" ) } } diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressions.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressions.kt index c3f36a814..a441a80c0 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressions.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressions.kt @@ -2,7 +2,7 @@ package scientifik.kmath.expressions import scientifik.kmath.operations.* -internal class FunctionalUnaryOperation(val context: Algebra, val name: String, val expr: Expression) : +internal class FunctionalUnaryOperation(val context: Algebra, val name: String, private val expr: Expression) : Expression { override fun invoke(arguments: Map): T = context.unaryOperation(name, expr.invoke(arguments)) } -- 2.34.1 From 866ae47239f93eea7625e65a94799ce7245f03c4 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sat, 13 Jun 2020 11:51:33 +0300 Subject: [PATCH 050/156] replace `raw` by `symbol` in algebra --- kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MST.kt | 2 +- .../commonMain/kotlin/scientifik/kmath/ast/MSTExpression.kt | 2 +- .../commonMain/kotlin/scientifik/kmath/operations/Algebra.kt | 5 ++++- .../commonMain/kotlin/scientifik/kmath/operations/Complex.kt | 4 ++-- 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MST.kt b/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MST.kt index eba2c3343..3375ecdf3 100644 --- a/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MST.kt +++ b/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MST.kt @@ -43,7 +43,7 @@ sealed class MST { fun NumericAlgebra.evaluate(node: MST): T { return when (node) { is MST.Numeric -> number(node.value) - is MST.Singular -> raw(node.value) + is MST.Singular -> symbol(node.value) is MST.Unary -> unaryOperation(node.operation, evaluate(node.value)) is MST.Binary -> when { node.left is MST.Numeric && node.right is MST.Numeric -> { diff --git a/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MSTExpression.kt b/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MSTExpression.kt index 3ee454b2a..dbd5238e3 100644 --- a/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MSTExpression.kt +++ b/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MSTExpression.kt @@ -12,7 +12,7 @@ class MSTExpression(val algebra: NumericAlgebra, val mst: MST) : Expressio * Substitute algebra raw value */ private inner class InnerAlgebra(val arguments: Map) : NumericAlgebra by algebra { - override fun raw(value: String): T = arguments[value] ?: super.raw(value) + override fun symbol(value: String): T = arguments[value] ?: super.symbol(value) } override fun invoke(arguments: Map): T = InnerAlgebra(arguments).evaluate(mst) 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 166287ec7..8ed3f329e 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Algebra.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Algebra.kt @@ -10,7 +10,10 @@ interface Algebra { /** * Wrap raw string or variable */ - fun raw(value: String): T = error("Wrapping of '$value' is not supported in $this") + fun symbol(value: String): T = error("Wrapping of '$value' is not supported in $this") + + @Deprecated("Symbol is more concise",replaceWith = ReplaceWith("symbol")) + fun raw(value: String): T = symbol(value) /** * Dynamic call of unary operation with name [operation] on [arg] 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 01aef824d..6ce1d929b 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Complex.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Complex.kt @@ -51,10 +51,10 @@ object ComplexField : ExtendedField { operator fun Double.times(c: Complex) = Complex(c.re * this, c.im * this) - override fun raw(value: String): Complex = if (value == "i") { + override fun symbol(value: String): Complex = if (value == "i") { i } else { - super.raw(value) + super.symbol(value) } } -- 2.34.1 From 1582fde0915332681884d6c50f5bf1f516ff5c6a Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Sat, 13 Jun 2020 15:51:42 +0700 Subject: [PATCH 051/156] Replace JUnit @Test with kotlin-test @Test --- kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/AsmTest.kt | 2 +- kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/ParserTest.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/AsmTest.kt b/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/AsmTest.kt index 73058face..c92524b5d 100644 --- a/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/AsmTest.kt +++ b/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/AsmTest.kt @@ -1,12 +1,12 @@ package scietifik.kmath.ast -import org.junit.jupiter.api.Test import scientifik.kmath.asm.asmField import scientifik.kmath.ast.parseMath import scientifik.kmath.expressions.invoke import scientifik.kmath.operations.Complex import scientifik.kmath.operations.ComplexField import kotlin.test.assertEquals +import kotlin.test.Test class AsmTest { @Test diff --git a/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/ParserTest.kt b/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/ParserTest.kt index ddac07786..06546302e 100644 --- a/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/ParserTest.kt +++ b/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/ParserTest.kt @@ -1,11 +1,11 @@ package scietifik.kmath.ast -import org.junit.jupiter.api.Test import scientifik.kmath.ast.evaluate import scientifik.kmath.ast.parseMath import scientifik.kmath.operations.Complex import scientifik.kmath.operations.ComplexField import kotlin.test.assertEquals +import kotlin.test.Test internal class ParserTest { @Test -- 2.34.1 From 834d1e1397e76943adab584c46a7526c47073da3 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Sat, 13 Jun 2020 15:53:25 +0700 Subject: [PATCH 052/156] Move specific optimization functions to Optimization --- .../scientifik/kmath/asm/AsmExpressions.kt | 31 ---------------- .../scientifik/kmath/asm/Optimization.kt | 35 +++++++++++++++++++ 2 files changed, 35 insertions(+), 31 deletions(-) diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmExpressions.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmExpressions.kt index 9e6efb66b..c4039da81 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmExpressions.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmExpressions.kt @@ -11,37 +11,6 @@ interface AsmExpression { fun invoke(gen: AsmGenerationContext) } -private val methodNameAdapters: Map = mapOf("+" to "add", "*" to "multiply", "/" to "divide") - -internal fun hasSpecific(context: Algebra, name: String, arity: Int): Boolean { - val aName = methodNameAdapters[name] ?: name - - context::class.memberFunctions.find { it.name == aName && it.parameters.size == arity } - ?: return false - - return true -} - -internal fun AsmGenerationContext.tryInvokeSpecific(context: Algebra, name: String, arity: Int): Boolean { - val aName = methodNameAdapters[name] ?: name - - context::class.memberFunctions.find { it.name == aName && it.parameters.size == arity } - ?: return false - - val owner = context::class.jvmName.replace('.', '/') - - val sig = buildString { - append('(') - repeat(arity) { append("L${AsmGenerationContext.OBJECT_CLASS};") } - append(')') - append("L${AsmGenerationContext.OBJECT_CLASS};") - } - - visitAlgebraOperation(owner = owner, method = aName, descriptor = sig) - - return true -} - internal class AsmUnaryOperation(private val context: Algebra, private val name: String, expr: AsmExpression) : AsmExpression { private val expr: AsmExpression = expr.optimize() diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/Optimization.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/Optimization.kt index e821bd206..4e094fdd8 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/Optimization.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/Optimization.kt @@ -1,5 +1,40 @@ package scientifik.kmath.asm +import scientifik.kmath.operations.Algebra +import kotlin.reflect.full.memberFunctions +import kotlin.reflect.jvm.jvmName + +private val methodNameAdapters: Map = mapOf("+" to "add", "*" to "multiply", "/" to "divide") + +internal fun hasSpecific(context: Algebra, name: String, arity: Int): Boolean { + val aName = methodNameAdapters[name] ?: name + + context::class.memberFunctions.find { it.name == aName && it.parameters.size == arity } + ?: return false + + return true +} + +internal fun AsmGenerationContext.tryInvokeSpecific(context: Algebra, name: String, arity: Int): Boolean { + val aName = methodNameAdapters[name] ?: name + + context::class.memberFunctions.find { it.name == aName && it.parameters.size == arity } + ?: return false + + val owner = context::class.jvmName.replace('.', '/') + + val sig = buildString { + append('(') + repeat(arity) { append("L${AsmGenerationContext.OBJECT_CLASS};") } + append(')') + append("L${AsmGenerationContext.OBJECT_CLASS};") + } + + visitAlgebraOperation(owner = owner, method = aName, descriptor = sig) + + return true +} + @PublishedApi internal fun AsmExpression.optimize(): AsmExpression { val a = tryEvaluate() -- 2.34.1 From 223d238c435f6e7b34d5ae778ec35b64603f0e81 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Sat, 13 Jun 2020 15:53:54 +0700 Subject: [PATCH 053/156] Encapsulate MethodVisitor extensions --- .../jvmMain/kotlin/scientifik/kmath/asm/MethodVisitors.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/MethodVisitors.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/MethodVisitors.kt index 42565b6ba..aec5a123d 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/MethodVisitors.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/MethodVisitors.kt @@ -3,7 +3,7 @@ package scientifik.kmath.asm import org.objectweb.asm.MethodVisitor import org.objectweb.asm.Opcodes.* -fun MethodVisitor.visitLdcOrIConstInsn(value: Int) = when (value) { +internal fun MethodVisitor.visitLdcOrIConstInsn(value: Int) = when (value) { -1 -> visitInsn(ICONST_M1) 0 -> visitInsn(ICONST_0) 1 -> visitInsn(ICONST_1) @@ -14,13 +14,13 @@ fun MethodVisitor.visitLdcOrIConstInsn(value: Int) = when (value) { else -> visitLdcInsn(value) } -fun MethodVisitor.visitLdcOrDConstInsn(value: Double) = when (value) { +internal fun MethodVisitor.visitLdcOrDConstInsn(value: Double) = when (value) { 0.0 -> visitInsn(DCONST_0) 1.0 -> visitInsn(DCONST_1) else -> visitLdcInsn(value) } -fun MethodVisitor.visitLdcOrFConstInsn(value: Float) = when (value) { +internal fun MethodVisitor.visitLdcOrFConstInsn(value: Float) = when (value) { 0f -> visitInsn(FCONST_0) 1f -> visitInsn(FCONST_1) 2f -> visitInsn(FCONST_2) -- 2.34.1 From e65d1e43cf64a27a04bc20b3245814be372008b9 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Sat, 13 Jun 2020 16:16:57 +0700 Subject: [PATCH 054/156] Write tests --- .../kmath/ast/asm/TestAsmAlgebras.kt | 75 +++++++++++++++++++ .../kmath/ast/asm/TestAsmExpressions.kt | 21 ++++++ 2 files changed, 96 insertions(+) create mode 100644 kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/asm/TestAsmAlgebras.kt create mode 100644 kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/asm/TestAsmExpressions.kt diff --git a/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/asm/TestAsmAlgebras.kt b/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/asm/TestAsmAlgebras.kt new file mode 100644 index 000000000..07f895c40 --- /dev/null +++ b/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/asm/TestAsmAlgebras.kt @@ -0,0 +1,75 @@ +package scietifik.kmath.ast.asm + +import scientifik.kmath.asm.asmField +import scientifik.kmath.asm.asmRing +import scientifik.kmath.asm.asmSpace +import scientifik.kmath.expressions.invoke +import scientifik.kmath.operations.ByteRing +import scientifik.kmath.operations.RealField +import kotlin.test.Test +import kotlin.test.assertEquals + +class TestAsmAlgebras { + @Test + fun space() { + val res = ByteRing.asmSpace { + binaryOperation( + "+", + + unaryOperation( + "+", + 3.toByte() - (2.toByte() + (multiply( + add(const(1), const(1)), + 2 + ) + 1.toByte()) * 3.toByte() - 1.toByte()) + ), + + number(1) + ) + variable("x") + zero + }("x" to 2.toByte()) + + assertEquals(16, res) + } + + @Test + fun ring() { + val res = ByteRing.asmRing { + binaryOperation( + "+", + + unaryOperation( + "+", + (3.toByte() - (2.toByte() + (multiply( + add(const(1), const(1)), + 2 + ) + 1.toByte()))) * 3.0 - 1.toByte() + ), + + number(1) + ) * const(2) + }() + + assertEquals(24, res) + } + + @Test + fun field() { + val res = RealField.asmField { + divide(binaryOperation( + "+", + + unaryOperation( + "+", + (3.0 - (2.0 + (multiply( + add(const(1.0), const(1.0)), + 2 + ) + 1.0))) * 3 - 1.0 + ), + + number(1) + ) / 2, const(2.0)) * one + }() + + assertEquals(3.0, res) + } +} diff --git a/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/asm/TestAsmExpressions.kt b/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/asm/TestAsmExpressions.kt new file mode 100644 index 000000000..2839b8a25 --- /dev/null +++ b/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/asm/TestAsmExpressions.kt @@ -0,0 +1,21 @@ +package scietifik.kmath.ast.asm + +import scientifik.kmath.asm.asmField +import scientifik.kmath.expressions.invoke +import scientifik.kmath.operations.RealField +import kotlin.test.Test +import kotlin.test.assertEquals + +class TestAsmExpressions { + @Test + fun testUnaryOperationInvocation() { + val res = RealField.asmField { unaryOperation("+", variable("x")) }("x" to 2.0) + assertEquals(2.0, res) + } + + @Test + fun testConstProductInvocation() { + val res = RealField.asmField { variable("x") * 2 }("x" to 2.0) + assertEquals(4.0, res) + } +} -- 2.34.1 From f9835979ea22069b0f3f94ec436b665ecb508554 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Sat, 13 Jun 2020 16:48:45 +0700 Subject: [PATCH 055/156] Fix specification bug --- .../scientifik/kmath/asm/AsmExpressions.kt | 4 ++-- .../kmath/asm/AsmGenerationContext.kt | 17 +++++++++++++---- .../kotlin/scientifik/kmath/asm/Optimization.kt | 17 +++++++++++------ 3 files changed, 26 insertions(+), 12 deletions(-) diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmExpressions.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmExpressions.kt index c4039da81..83f3b7754 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmExpressions.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmExpressions.kt @@ -57,13 +57,13 @@ internal class AsmBinaryOperation( override fun invoke(gen: AsmGenerationContext) { gen.visitLoadAlgebra() - if (!hasSpecific(context, name, 1)) + if (!hasSpecific(context, name, 2)) gen.visitStringConstant(name) first.invoke(gen) second.invoke(gen) - if (gen.tryInvokeSpecific(context, name, 1)) + if (gen.tryInvokeSpecific(context, name, 2)) return gen.visitAlgebraOperation( diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmGenerationContext.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmGenerationContext.kt index 69a2b6c85..c10e56ba8 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmGenerationContext.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmGenerationContext.kt @@ -4,6 +4,7 @@ import org.objectweb.asm.ClassWriter import org.objectweb.asm.Label import org.objectweb.asm.MethodVisitor import org.objectweb.asm.Opcodes +import scientifik.kmath.asm.AsmGenerationContext.ClassLoader import scientifik.kmath.operations.Algebra /** @@ -245,8 +246,10 @@ class AsmGenerationContext( } visitLdcInsn(name) - visitMethodInsn(Opcodes.INVOKEINTERFACE, - MAP_CLASS, "get", "(L$OBJECT_CLASS;)L$OBJECT_CLASS;", true) + visitMethodInsn( + Opcodes.INVOKEINTERFACE, + MAP_CLASS, "get", "(L$OBJECT_CLASS;)L$OBJECT_CLASS;", true + ) visitCastToT() } @@ -262,9 +265,15 @@ class AsmGenerationContext( invokeMethodVisitor.visitTypeInsn(Opcodes.CHECKCAST, T_ALGEBRA_CLASS) } - internal fun visitAlgebraOperation(owner: String, method: String, descriptor: String) { + internal fun visitAlgebraOperation( + owner: String, + method: String, + descriptor: String, + opcode: Int = Opcodes.INVOKEINTERFACE, + isInterface: Boolean = true + ) { maxStack++ - invokeMethodVisitor.visitMethodInsn(Opcodes.INVOKEINTERFACE, owner, method, descriptor, true) + invokeMethodVisitor.visitMethodInsn(opcode, owner, method, descriptor, isInterface) visitCastToT() } diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/Optimization.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/Optimization.kt index 4e094fdd8..3198fa8ec 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/Optimization.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/Optimization.kt @@ -1,15 +1,14 @@ package scientifik.kmath.asm +import org.objectweb.asm.Opcodes import scientifik.kmath.operations.Algebra -import kotlin.reflect.full.memberFunctions -import kotlin.reflect.jvm.jvmName private val methodNameAdapters: Map = mapOf("+" to "add", "*" to "multiply", "/" to "divide") internal fun hasSpecific(context: Algebra, name: String, arity: Int): Boolean { val aName = methodNameAdapters[name] ?: name - context::class.memberFunctions.find { it.name == aName && it.parameters.size == arity } + context::class.java.methods.find { it.name == aName && it.parameters.size == arity } ?: return false return true @@ -18,10 +17,10 @@ internal fun hasSpecific(context: Algebra, name: String, arity: Int): Boo internal fun AsmGenerationContext.tryInvokeSpecific(context: Algebra, name: String, arity: Int): Boolean { val aName = methodNameAdapters[name] ?: name - context::class.memberFunctions.find { it.name == aName && it.parameters.size == arity } + context::class.java.methods.find { it.name == aName && it.parameters.size == arity } ?: return false - val owner = context::class.jvmName.replace('.', '/') + val owner = context::class.java.name.replace('.', '/') val sig = buildString { append('(') @@ -30,7 +29,13 @@ internal fun AsmGenerationContext.tryInvokeSpecific(context: Algebra, append("L${AsmGenerationContext.OBJECT_CLASS};") } - visitAlgebraOperation(owner = owner, method = aName, descriptor = sig) + visitAlgebraOperation( + owner = owner, + method = aName, + descriptor = sig, + opcode = Opcodes.INVOKEVIRTUAL, + isInterface = false + ) return true } -- 2.34.1 From e91f6470d3d6fd4d21574b4006977ae7b4be9b2e Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Sat, 13 Jun 2020 17:07:22 +0700 Subject: [PATCH 056/156] Implement constants inlining --- .../kmath/asm/AsmGenerationContext.kt | 33 ++++++++++++------- .../scientifik/kmath/asm/Optimization.kt | 1 + 2 files changed, 23 insertions(+), 11 deletions(-) diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmGenerationContext.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmGenerationContext.kt index c10e56ba8..cc7874e37 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmGenerationContext.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmGenerationContext.kt @@ -17,7 +17,7 @@ import scientifik.kmath.operations.Algebra * @param className the unique class name of new loaded class. */ class AsmGenerationContext( - classOfT: Class<*>, + private val classOfT: Class<*>, private val algebra: Algebra, private val className: String ) { @@ -186,7 +186,15 @@ class AsmGenerationContext( return new } - internal fun visitLoadFromConstants(value: T) = visitLoadAnyFromConstants(value as Any, T_CLASS) + internal fun visitLoadFromConstants(value: T) { + if (classOfT in INLINABLE_NUMBERS) { + visitNumberConstant(value as Number) + visitCastToT() + return + } + + visitLoadAnyFromConstants(value as Any, T_CLASS) + } private fun visitLoadAnyFromConstants(value: Any, type: String) { val idx = if (value in constants) constants.indexOf(value) else constants.apply { add(value) }.lastIndex @@ -207,7 +215,6 @@ class AsmGenerationContext( maxStack++ val clazz = value.javaClass val c = clazz.name.replace('.', '/') - val sigLetter = SIGNATURE_LETTERS[clazz] if (sigLetter != null) { @@ -284,14 +291,18 @@ class AsmGenerationContext( } internal companion object { - private val SIGNATURE_LETTERS = mapOf( - java.lang.Byte::class.java to "B", - java.lang.Short::class.java to "S", - java.lang.Integer::class.java to "I", - java.lang.Long::class.java to "J", - java.lang.Float::class.java to "F", - java.lang.Double::class.java to "D" - ) + private val SIGNATURE_LETTERS by lazy { + mapOf( + java.lang.Byte::class.java to "B", + java.lang.Short::class.java to "S", + java.lang.Integer::class.java to "I", + java.lang.Long::class.java to "J", + java.lang.Float::class.java to "F", + java.lang.Double::class.java to "D" + ) + } + + private val INLINABLE_NUMBERS by lazy { SIGNATURE_LETTERS.keys } internal const val FUNCTIONAL_COMPILED_EXPRESSION_CLASS = "scientifik/kmath/asm/FunctionalCompiledExpression" diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/Optimization.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/Optimization.kt index 3198fa8ec..51048c77c 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/Optimization.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/Optimization.kt @@ -2,6 +2,7 @@ package scientifik.kmath.asm import org.objectweb.asm.Opcodes import scientifik.kmath.operations.Algebra +import scientifik.kmath.operations.ByteRing private val methodNameAdapters: Map = mapOf("+" to "add", "*" to "multiply", "/" to "divide") -- 2.34.1 From 0950580b85ca02a72c13863efc72aae2367e3ab3 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sat, 13 Jun 2020 18:26:18 +0300 Subject: [PATCH 057/156] Moe better-parse to common. Watch for https://github.com/h0tk3y/better-parse/issues/27 --- kmath-ast/build.gradle.kts | 6 ++++++ .../kotlin/scientifik/kmath/ast/parser.kt | 0 2 files changed, 6 insertions(+) rename kmath-ast/src/{jvmMain => commonMain}/kotlin/scientifik/kmath/ast/parser.kt (100%) diff --git a/kmath-ast/build.gradle.kts b/kmath-ast/build.gradle.kts index 88540e7b8..65a1ac12c 100644 --- a/kmath-ast/build.gradle.kts +++ b/kmath-ast/build.gradle.kts @@ -11,6 +11,7 @@ kotlin.sourceSets { dependencies { api(project(":kmath-core")) implementation("com.github.h0tk3y.betterParse:better-parse-multiplatform:0.4.0-alpha-3") + implementation("com.github.h0tk3y.betterParse:better-parse-multiplatform-metadata:0.4.0-alpha-3") } } jvmMain{ @@ -18,4 +19,9 @@ kotlin.sourceSets { implementation("com.github.h0tk3y.betterParse:better-parse-jvm:0.4.0-alpha-3") } } + jsMain{ + dependencies{ + implementation("com.github.h0tk3y.betterParse:better-parse-js:0.4.0-alpha-3") + } + } } \ No newline at end of file diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/ast/parser.kt b/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/parser.kt similarity index 100% rename from kmath-ast/src/jvmMain/kotlin/scientifik/kmath/ast/parser.kt rename to kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/parser.kt -- 2.34.1 From 48b688b6b1a6888d1812a36ab5c2bca12042f8d2 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Sun, 14 Jun 2020 00:06:12 +0700 Subject: [PATCH 058/156] Fix minor problems occured after merge --- .../scientifik/kmath/operations/NumberAlgebra.kt | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/NumberAlgebra.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/NumberAlgebra.kt index 830b1496d..a1b845ccc 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/NumberAlgebra.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/NumberAlgebra.kt @@ -20,20 +20,18 @@ interface ExtendedFieldOperations : PowerOperations.SQRT_OPERATION -> sqrt(arg) ExponentialOperations.EXP_OPERATION -> exp(arg) ExponentialOperations.LN_OPERATION -> ln(arg) - else -> super.unaryOperation(operation, arg) + else -> super.unaryOperation(operation, arg) } } interface ExtendedField : ExtendedFieldOperations, Field { - override fun rightSideNumberOperation(operation: String, left: T, right: Number): T { - return when (operation) { - PowerOperations.POW_OPERATION -> power(left, right) - else -> super.rightSideNumberOperation(operation, left, right) - } - + override fun rightSideNumberOperation(operation: String, left: T, right: Number): T = when (operation) { + PowerOperations.POW_OPERATION -> power(left, right) + else -> super.rightSideNumberOperation(operation, left, right) } } + /** * Real field element wrapping double. * -- 2.34.1 From af410dde70af10e7531809dd18862d5eaca8a204 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Sun, 14 Jun 2020 00:18:40 +0700 Subject: [PATCH 059/156] Apply the suggested changes --- .../kmath/asm/{Builders.kt => AsmBuilders.kt} | 5 ++- .../scientifik/kmath/asm/AsmExpressions.kt | 43 +++++++++++++------ .../kmath/asm/AsmGenerationContext.kt | 5 ++- .../asm/{ => internal}/MethodVisitors.kt | 2 +- .../kmath/asm/{ => internal}/Optimization.kt | 6 ++- 5 files changed, 42 insertions(+), 19 deletions(-) rename kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/{Builders.kt => AsmBuilders.kt} (94%) rename kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/{ => internal}/MethodVisitors.kt (95%) rename kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/{ => internal}/Optimization.kt (88%) diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/Builders.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmBuilders.kt similarity index 94% rename from kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/Builders.kt rename to kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmBuilders.kt index 67870e07e..14456a426 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/Builders.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmBuilders.kt @@ -18,9 +18,10 @@ internal fun buildName(expression: AsmExpression<*>, collision: Int = 0): String return buildName(expression, collision + 1) } -inline fun AsmExpression.compile(algebra: Algebra): Expression { +@PublishedApi +internal inline fun AsmExpression.compile(algebra: Algebra): Expression { val ctx = AsmGenerationContext(T::class.java, algebra, buildName(this)) - invoke(ctx) + compile(ctx) return ctx.generate() } diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmExpressions.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmExpressions.kt index 83f3b7754..cd1815f7c 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmExpressions.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmExpressions.kt @@ -1,14 +1,31 @@ package scientifik.kmath.asm +import scientifik.kmath.asm.internal.hasSpecific +import scientifik.kmath.asm.internal.optimize +import scientifik.kmath.asm.internal.tryInvokeSpecific import scientifik.kmath.expressions.Expression import scientifik.kmath.expressions.ExpressionAlgebra import scientifik.kmath.operations.* -import kotlin.reflect.full.memberFunctions -import kotlin.reflect.jvm.jvmName +/** + * A function declaration that could be compiled to [AsmGenerationContext]. + * + * @param T the type the stored function returns. + */ interface AsmExpression { + /** + * Tries to evaluate this function without its variables. This method is intended for optimization. + * + * @return `null` if the function depends on its variables, the value if the function is a constant. + */ fun tryEvaluate(): T? = null - fun invoke(gen: AsmGenerationContext) + + /** + * Compiles this declaration. + * + * @param gen the target [AsmGenerationContext]. + */ + fun compile(gen: AsmGenerationContext) } internal class AsmUnaryOperation(private val context: Algebra, private val name: String, expr: AsmExpression) : @@ -16,13 +33,13 @@ internal class AsmUnaryOperation(private val context: Algebra, private val private val expr: AsmExpression = expr.optimize() override fun tryEvaluate(): T? = context { unaryOperation(name, expr.tryEvaluate() ?: return@context null) } - override fun invoke(gen: AsmGenerationContext) { + override fun compile(gen: AsmGenerationContext) { gen.visitLoadAlgebra() if (!hasSpecific(context, name, 1)) gen.visitStringConstant(name) - expr.invoke(gen) + expr.compile(gen) if (gen.tryInvokeSpecific(context, name, 1)) return @@ -54,14 +71,14 @@ internal class AsmBinaryOperation( ) } - override fun invoke(gen: AsmGenerationContext) { + override fun compile(gen: AsmGenerationContext) { gen.visitLoadAlgebra() if (!hasSpecific(context, name, 2)) gen.visitStringConstant(name) - first.invoke(gen) - second.invoke(gen) + first.compile(gen) + second.compile(gen) if (gen.tryInvokeSpecific(context, name, 2)) return @@ -79,13 +96,13 @@ internal class AsmBinaryOperation( internal class AsmVariableExpression(private val name: String, private val default: T? = null) : AsmExpression { - override fun invoke(gen: AsmGenerationContext): Unit = gen.visitLoadFromVariables(name, default) + override fun compile(gen: AsmGenerationContext): Unit = gen.visitLoadFromVariables(name, default) } internal class AsmConstantExpression(private val value: T) : AsmExpression { override fun tryEvaluate(): T = value - override fun invoke(gen: AsmGenerationContext): Unit = gen.visitLoadFromConstants(value) + override fun compile(gen: AsmGenerationContext): Unit = gen.visitLoadFromConstants(value) } internal class AsmConstProductExpression( @@ -98,10 +115,10 @@ internal class AsmConstProductExpression( override fun tryEvaluate(): T? = context { (expr.tryEvaluate() ?: return@context null) * const } - override fun invoke(gen: AsmGenerationContext) { + override fun compile(gen: AsmGenerationContext) { gen.visitLoadAlgebra() gen.visitNumberConstant(const) - expr.invoke(gen) + expr.compile(gen) gen.visitAlgebraOperation( owner = AsmGenerationContext.SPACE_OPERATIONS_CLASS, @@ -117,7 +134,7 @@ internal class AsmNumberExpression(private val context: NumericAlgebra, pr AsmExpression { override fun tryEvaluate(): T? = context.number(value) - override fun invoke(gen: AsmGenerationContext): Unit = gen.visitNumberConstant(value) + override fun compile(gen: AsmGenerationContext): Unit = gen.visitNumberConstant(value) } internal abstract class FunctionalCompiledExpression internal constructor( diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmGenerationContext.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmGenerationContext.kt index cc7874e37..be8da3eff 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmGenerationContext.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmGenerationContext.kt @@ -5,6 +5,9 @@ import org.objectweb.asm.Label import org.objectweb.asm.MethodVisitor import org.objectweb.asm.Opcodes import scientifik.kmath.asm.AsmGenerationContext.ClassLoader +import scientifik.kmath.asm.internal.visitLdcOrDConstInsn +import scientifik.kmath.asm.internal.visitLdcOrFConstInsn +import scientifik.kmath.asm.internal.visitLdcOrIConstInsn import scientifik.kmath.operations.Algebra /** @@ -16,7 +19,7 @@ import scientifik.kmath.operations.Algebra * @param algebra the algebra the applied AsmExpressions use. * @param className the unique class name of new loaded class. */ -class AsmGenerationContext( +class AsmGenerationContext @PublishedApi internal constructor( private val classOfT: Class<*>, private val algebra: Algebra, private val className: String diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/MethodVisitors.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/MethodVisitors.kt similarity index 95% rename from kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/MethodVisitors.kt rename to kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/MethodVisitors.kt index aec5a123d..356de4765 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/MethodVisitors.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/MethodVisitors.kt @@ -1,4 +1,4 @@ -package scientifik.kmath.asm +package scientifik.kmath.asm.internal import org.objectweb.asm.MethodVisitor import org.objectweb.asm.Opcodes.* diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/Optimization.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/Optimization.kt similarity index 88% rename from kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/Optimization.kt rename to kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/Optimization.kt index 51048c77c..cd41ff07c 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/Optimization.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/Optimization.kt @@ -1,8 +1,10 @@ -package scientifik.kmath.asm +package scientifik.kmath.asm.internal import org.objectweb.asm.Opcodes +import scientifik.kmath.asm.AsmConstantExpression +import scientifik.kmath.asm.AsmExpression +import scientifik.kmath.asm.AsmGenerationContext import scientifik.kmath.operations.Algebra -import scientifik.kmath.operations.ByteRing private val methodNameAdapters: Map = mapOf("+" to "add", "*" to "multiply", "/" to "divide") -- 2.34.1 From 28cecde05bb16f35db04f31bd2585538e563eed1 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Sun, 14 Jun 2020 00:24:10 +0700 Subject: [PATCH 060/156] Fix compilation problems found after merge --- kmath-ast/build.gradle.kts | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/kmath-ast/build.gradle.kts b/kmath-ast/build.gradle.kts index 65a1ac12c..9764fccc3 100644 --- a/kmath-ast/build.gradle.kts +++ b/kmath-ast/build.gradle.kts @@ -2,7 +2,7 @@ plugins { id("scientifik.mpp") } -repositories{ +repositories { maven("https://dl.bintray.com/hotkeytlt/maven") } @@ -14,13 +14,18 @@ kotlin.sourceSets { implementation("com.github.h0tk3y.betterParse:better-parse-multiplatform-metadata:0.4.0-alpha-3") } } - jvmMain{ - dependencies{ + + jvmMain { + dependencies { implementation("com.github.h0tk3y.betterParse:better-parse-jvm:0.4.0-alpha-3") + implementation("org.ow2.asm:asm:8.0.1") + implementation("org.ow2.asm:asm-commons:8.0.1") + implementation(kotlin("reflect")) } } - jsMain{ - dependencies{ + + jsMain { + dependencies { implementation("com.github.h0tk3y.betterParse:better-parse-js:0.4.0-alpha-3") } } -- 2.34.1 From d3d348620acb55feea6d915eca4523789594bba3 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Sun, 14 Jun 2020 00:30:39 +0700 Subject: [PATCH 061/156] Rename AsmExpression to AsmNode, encapsulate AsmGenerationContext, make AsmNode (ex-AsmExpression) an abstract class instead of interface --- .../scientifik/kmath/asm/AsmBuilders.kt | 16 +-- .../scientifik/kmath/asm/AsmExpressions.kt | 109 +++++++++--------- .../{ => internal}/AsmGenerationContext.kt | 13 +-- .../kmath/asm/internal/Optimization.kt | 5 +- 4 files changed, 72 insertions(+), 71 deletions(-) rename kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/{ => internal}/AsmGenerationContext.kt (96%) diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmBuilders.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmBuilders.kt index 14456a426..69e49f8b6 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmBuilders.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmBuilders.kt @@ -1,12 +1,13 @@ package scientifik.kmath.asm +import scientifik.kmath.asm.internal.AsmGenerationContext import scientifik.kmath.ast.MST import scientifik.kmath.ast.evaluate import scientifik.kmath.expressions.Expression import scientifik.kmath.operations.* @PublishedApi -internal fun buildName(expression: AsmExpression<*>, collision: Int = 0): String { +internal fun buildName(expression: AsmNode<*>, collision: Int = 0): String { val name = "scientifik.kmath.expressions.generated.AsmCompiledExpression_${expression.hashCode()}_$collision" try { @@ -19,15 +20,16 @@ internal fun buildName(expression: AsmExpression<*>, collision: Int = 0): String } @PublishedApi -internal inline fun AsmExpression.compile(algebra: Algebra): Expression { - val ctx = AsmGenerationContext(T::class.java, algebra, buildName(this)) +internal inline fun AsmNode.compile(algebra: Algebra): Expression { + val ctx = + AsmGenerationContext(T::class.java, algebra, buildName(this)) compile(ctx) return ctx.generate() } inline fun , E : AsmExpressionAlgebra> A.asm( expressionAlgebra: E, - block: E.() -> AsmExpression + block: E.() -> AsmNode ): Expression = expressionAlgebra.block().compile(expressionAlgebra.algebra) inline fun , E : AsmExpressionAlgebra> A.asm( @@ -35,19 +37,19 @@ inline fun , E : AsmExpressionAlgebra> A. ast: MST ): Expression = asm(expressionAlgebra) { evaluate(ast) } -inline fun A.asmSpace(block: AsmExpressionSpace.() -> AsmExpression): Expression where A : NumericAlgebra, A : Space = +inline fun A.asmSpace(block: AsmExpressionSpace.() -> AsmNode): Expression where A : NumericAlgebra, A : Space = AsmExpressionSpace(this).let { it.block().compile(it.algebra) } inline fun A.asmSpace(ast: MST): Expression where A : NumericAlgebra, A : Space = asmSpace { evaluate(ast) } -inline fun A.asmRing(block: AsmExpressionRing.() -> AsmExpression): Expression where A : NumericAlgebra, A : Ring = +inline fun A.asmRing(block: AsmExpressionRing.() -> AsmNode): Expression where A : NumericAlgebra, A : Ring = AsmExpressionRing(this).let { it.block().compile(it.algebra) } inline fun A.asmRing(ast: MST): Expression where A : NumericAlgebra, A : Ring = asmRing { evaluate(ast) } -inline fun A.asmField(block: AsmExpressionField.() -> AsmExpression): Expression where A : NumericAlgebra, A : Field = +inline fun A.asmField(block: AsmExpressionField.() -> AsmNode): Expression where A : NumericAlgebra, A : Field = AsmExpressionField(this).let { it.block().compile(it.algebra) } inline fun A.asmField(ast: MST): Expression where A : NumericAlgebra, A : Field = diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmExpressions.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmExpressions.kt index cd1815f7c..8f16a2710 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmExpressions.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmExpressions.kt @@ -1,5 +1,6 @@ package scientifik.kmath.asm +import scientifik.kmath.asm.internal.AsmGenerationContext import scientifik.kmath.asm.internal.hasSpecific import scientifik.kmath.asm.internal.optimize import scientifik.kmath.asm.internal.tryInvokeSpecific @@ -12,25 +13,26 @@ import scientifik.kmath.operations.* * * @param T the type the stored function returns. */ -interface AsmExpression { +abstract class AsmNode internal constructor() { /** * Tries to evaluate this function without its variables. This method is intended for optimization. * * @return `null` if the function depends on its variables, the value if the function is a constant. */ - fun tryEvaluate(): T? = null + internal open fun tryEvaluate(): T? = null /** * Compiles this declaration. * * @param gen the target [AsmGenerationContext]. */ - fun compile(gen: AsmGenerationContext) + @PublishedApi + internal abstract fun compile(gen: AsmGenerationContext) } -internal class AsmUnaryOperation(private val context: Algebra, private val name: String, expr: AsmExpression) : - AsmExpression { - private val expr: AsmExpression = expr.optimize() +internal class AsmUnaryOperation(private val context: Algebra, private val name: String, expr: AsmNode) : + AsmNode() { + private val expr: AsmNode = expr.optimize() override fun tryEvaluate(): T? = context { unaryOperation(name, expr.tryEvaluate() ?: return@context null) } override fun compile(gen: AsmGenerationContext) { @@ -57,11 +59,11 @@ internal class AsmUnaryOperation(private val context: Algebra, private val internal class AsmBinaryOperation( private val context: Algebra, private val name: String, - first: AsmExpression, - second: AsmExpression -) : AsmExpression { - private val first: AsmExpression = first.optimize() - private val second: AsmExpression = second.optimize() + first: AsmNode, + second: AsmNode +) : AsmNode() { + private val first: AsmNode = first.optimize() + private val second: AsmNode = second.optimize() override fun tryEvaluate(): T? = context { binaryOperation( @@ -95,23 +97,22 @@ internal class AsmBinaryOperation( } internal class AsmVariableExpression(private val name: String, private val default: T? = null) : - AsmExpression { + AsmNode() { override fun compile(gen: AsmGenerationContext): Unit = gen.visitLoadFromVariables(name, default) } internal class AsmConstantExpression(private val value: T) : - AsmExpression { + AsmNode() { override fun tryEvaluate(): T = value override fun compile(gen: AsmGenerationContext): Unit = gen.visitLoadFromConstants(value) } internal class AsmConstProductExpression( private val context: Space, - expr: AsmExpression, + expr: AsmNode, private val const: Number -) : - AsmExpression { - private val expr: AsmExpression = expr.optimize() +) : AsmNode() { + private val expr: AsmNode = expr.optimize() override fun tryEvaluate(): T? = context { (expr.tryEvaluate() ?: return@context null) * const } @@ -131,7 +132,7 @@ internal class AsmConstProductExpression( } internal class AsmNumberExpression(private val context: NumericAlgebra, private val value: Number) : - AsmExpression { + AsmNode() { override fun tryEvaluate(): T? = context.number(value) override fun compile(gen: AsmGenerationContext): Unit = gen.visitNumberConstant(value) @@ -145,10 +146,10 @@ internal abstract class FunctionalCompiledExpression internal constructor( } /** - * A context class for [AsmExpression] construction. + * A context class for [AsmNode] construction. */ -interface AsmExpressionAlgebra> : NumericAlgebra>, - ExpressionAlgebra> { +interface AsmExpressionAlgebra> : NumericAlgebra>, + ExpressionAlgebra> { /** * The algebra to provide for AsmExpressions built. */ @@ -157,108 +158,108 @@ interface AsmExpressionAlgebra> : NumericAlgebra = AsmNumberExpression(algebra, value) + override fun number(value: Number): AsmNode = AsmNumberExpression(algebra, value) /** * Builds an AsmExpression of constant expression which does not depend on arguments. */ - override fun const(value: T): AsmExpression = AsmConstantExpression(value) + override fun const(value: T): AsmNode = AsmConstantExpression(value) /** * Builds an AsmExpression to access a variable. */ - override fun variable(name: String, default: T?): AsmExpression = AsmVariableExpression(name, default) + override fun variable(name: String, default: T?): AsmNode = AsmVariableExpression(name, default) /** * Builds an AsmExpression of dynamic call of binary operation [operation] on [left] and [right]. */ - override fun binaryOperation(operation: String, left: AsmExpression, right: AsmExpression): AsmExpression = + override fun binaryOperation(operation: String, left: AsmNode, right: AsmNode): AsmNode = AsmBinaryOperation(algebra, operation, left, right) /** * Builds an AsmExpression of dynamic call of unary operation with name [operation] on [arg]. */ - override fun unaryOperation(operation: String, arg: AsmExpression): AsmExpression = + override fun unaryOperation(operation: String, arg: AsmNode): AsmNode = AsmUnaryOperation(algebra, operation, arg) } /** - * A context class for [AsmExpression] construction for [Space] algebras. + * A context class for [AsmNode] construction for [Space] algebras. */ open class AsmExpressionSpace(override val algebra: A) : AsmExpressionAlgebra, - Space> where A : Space, A : NumericAlgebra { - override val zero: AsmExpression + Space> where A : Space, A : NumericAlgebra { + override val zero: AsmNode get() = const(algebra.zero) /** * Builds an AsmExpression of addition of two another expressions. */ - override fun add(a: AsmExpression, b: AsmExpression): AsmExpression = + override fun add(a: AsmNode, b: AsmNode): AsmNode = AsmBinaryOperation(algebra, SpaceOperations.PLUS_OPERATION, a, b) /** * Builds an AsmExpression of multiplication of expression by number. */ - override fun multiply(a: AsmExpression, k: Number): AsmExpression = AsmConstProductExpression(algebra, a, k) + override fun multiply(a: AsmNode, k: Number): AsmNode = AsmConstProductExpression(algebra, a, k) - operator fun AsmExpression.plus(arg: T): AsmExpression = this + const(arg) - operator fun AsmExpression.minus(arg: T): AsmExpression = this - const(arg) - operator fun T.plus(arg: AsmExpression): AsmExpression = arg + this - operator fun T.minus(arg: AsmExpression): AsmExpression = arg - this + operator fun AsmNode.plus(arg: T): AsmNode = this + const(arg) + operator fun AsmNode.minus(arg: T): AsmNode = this - const(arg) + operator fun T.plus(arg: AsmNode): AsmNode = arg + this + operator fun T.minus(arg: AsmNode): AsmNode = arg - this - override fun unaryOperation(operation: String, arg: AsmExpression): AsmExpression = + override fun unaryOperation(operation: String, arg: AsmNode): AsmNode = super.unaryOperation(operation, arg) - override fun binaryOperation(operation: String, left: AsmExpression, right: AsmExpression): AsmExpression = + override fun binaryOperation(operation: String, left: AsmNode, right: AsmNode): AsmNode = super.binaryOperation(operation, left, right) } /** - * A context class for [AsmExpression] construction for [Ring] algebras. + * A context class for [AsmNode] construction for [Ring] algebras. */ open class AsmExpressionRing(override val algebra: A) : AsmExpressionSpace(algebra), - Ring> where A : Ring, A : NumericAlgebra { - override val one: AsmExpression + Ring> where A : Ring, A : NumericAlgebra { + override val one: AsmNode get() = const(algebra.one) /** * Builds an AsmExpression of multiplication of two expressions. */ - override fun multiply(a: AsmExpression, b: AsmExpression): AsmExpression = + override fun multiply(a: AsmNode, b: AsmNode): AsmNode = AsmBinaryOperation(algebra, RingOperations.TIMES_OPERATION, a, b) - operator fun AsmExpression.times(arg: T): AsmExpression = this * const(arg) - operator fun T.times(arg: AsmExpression): AsmExpression = arg * this + operator fun AsmNode.times(arg: T): AsmNode = this * const(arg) + operator fun T.times(arg: AsmNode): AsmNode = arg * this - override fun unaryOperation(operation: String, arg: AsmExpression): AsmExpression = + override fun unaryOperation(operation: String, arg: AsmNode): AsmNode = super.unaryOperation(operation, arg) - override fun binaryOperation(operation: String, left: AsmExpression, right: AsmExpression): AsmExpression = + override fun binaryOperation(operation: String, left: AsmNode, right: AsmNode): AsmNode = super.binaryOperation(operation, left, right) - override fun number(value: Number): AsmExpression = super.number(value) + override fun number(value: Number): AsmNode = super.number(value) } /** - * A context class for [AsmExpression] construction for [Field] algebras. + * A context class for [AsmNode] construction for [Field] algebras. */ open class AsmExpressionField(override val algebra: A) : AsmExpressionRing(algebra), - Field> where A : Field, A : NumericAlgebra { + Field> where A : Field, A : NumericAlgebra { /** * Builds an AsmExpression of division an expression by another one. */ - override fun divide(a: AsmExpression, b: AsmExpression): AsmExpression = + override fun divide(a: AsmNode, b: AsmNode): AsmNode = AsmBinaryOperation(algebra, FieldOperations.DIV_OPERATION, a, b) - operator fun AsmExpression.div(arg: T): AsmExpression = this / const(arg) - operator fun T.div(arg: AsmExpression): AsmExpression = arg / this + operator fun AsmNode.div(arg: T): AsmNode = this / const(arg) + operator fun T.div(arg: AsmNode): AsmNode = arg / this - override fun unaryOperation(operation: String, arg: AsmExpression): AsmExpression = + override fun unaryOperation(operation: String, arg: AsmNode): AsmNode = super.unaryOperation(operation, arg) - override fun binaryOperation(operation: String, left: AsmExpression, right: AsmExpression): AsmExpression = + override fun binaryOperation(operation: String, left: AsmNode, right: AsmNode): AsmNode = super.binaryOperation(operation, left, right) - override fun number(value: Number): AsmExpression = super.number(value) + override fun number(value: Number): AsmNode = super.number(value) } diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmGenerationContext.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmGenerationContext.kt similarity index 96% rename from kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmGenerationContext.kt rename to kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmGenerationContext.kt index be8da3eff..fa0f06579 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmGenerationContext.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmGenerationContext.kt @@ -1,17 +1,15 @@ -package scientifik.kmath.asm +package scientifik.kmath.asm.internal import org.objectweb.asm.ClassWriter import org.objectweb.asm.Label import org.objectweb.asm.MethodVisitor import org.objectweb.asm.Opcodes -import scientifik.kmath.asm.AsmGenerationContext.ClassLoader -import scientifik.kmath.asm.internal.visitLdcOrDConstInsn -import scientifik.kmath.asm.internal.visitLdcOrFConstInsn -import scientifik.kmath.asm.internal.visitLdcOrIConstInsn +import scientifik.kmath.asm.FunctionalCompiledExpression +import scientifik.kmath.asm.internal.AsmGenerationContext.ClassLoader import scientifik.kmath.operations.Algebra /** - * AsmGenerationContext is a structure that abstracts building a class that unwraps [AsmExpression] to plain Java + * AsmGenerationContext is a structure that abstracts building a class that unwraps [AsmNode] to plain Java * expression. This class uses [ClassLoader] for loading the generated class, then it is able to instantiate the new * class. * @@ -19,7 +17,8 @@ import scientifik.kmath.operations.Algebra * @param algebra the algebra the applied AsmExpressions use. * @param className the unique class name of new loaded class. */ -class AsmGenerationContext @PublishedApi internal constructor( +@PublishedApi +internal class AsmGenerationContext @PublishedApi internal constructor( private val classOfT: Class<*>, private val algebra: Algebra, private val className: String diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/Optimization.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/Optimization.kt index cd41ff07c..815f292ce 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/Optimization.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/Optimization.kt @@ -2,8 +2,7 @@ package scientifik.kmath.asm.internal import org.objectweb.asm.Opcodes import scientifik.kmath.asm.AsmConstantExpression -import scientifik.kmath.asm.AsmExpression -import scientifik.kmath.asm.AsmGenerationContext +import scientifik.kmath.asm.AsmNode import scientifik.kmath.operations.Algebra private val methodNameAdapters: Map = mapOf("+" to "add", "*" to "multiply", "/" to "divide") @@ -44,7 +43,7 @@ internal fun AsmGenerationContext.tryInvokeSpecific(context: Algebra, } @PublishedApi -internal fun AsmExpression.optimize(): AsmExpression { +internal fun AsmNode.optimize(): AsmNode { val a = tryEvaluate() return if (a == null) this else AsmConstantExpression(a) } -- 2.34.1 From 318c3972fc1840b7518eb4491e1b045034191f90 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sat, 13 Jun 2020 20:51:37 +0300 Subject: [PATCH 062/156] Local code style --- docs/codestyle.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 docs/codestyle.md diff --git a/docs/codestyle.md b/docs/codestyle.md new file mode 100644 index 000000000..53789f7b2 --- /dev/null +++ b/docs/codestyle.md @@ -0,0 +1,22 @@ +# Local coding conventions + +Kmath and other `scientifik` projects use general [kotlin code conventions](https://kotlinlang.org/docs/reference/coding-conventions.html), but with a number of small changes and clarifications. + +## Utility class names +File name should coincide with a name of one of the classes contained in the file or start with small letter and describe its contents. + +The code convention [here](https://kotlinlang.org/docs/reference/coding-conventions.html#source-file-names) says that file names should start with capital letter even if file does not contain classes. Yet starting utility classes and aggregators with a small letter seems to be a good way to clearly visually separate those files. + +This convention could be changed in future in a non-breaking way. + +## Private variable names +Private variable names could start with underscore `_` in case the private mutable variable is shadowed by the public read-only value with the same meaning. + +Code convention do not permit underscores in names, but is is sometimes useful to "underscore" the fact that public and private versions define the same entity. It is allowed only for private variables. + +This convention could be changed in future in a non-breaking way. + +## Functions and properties one-liners +Use one-liners when they occupy single code window line both for functions and properties with getters like `val b: String get() = "fff"`. The same should be done with multiline expressions when they could be cleanly separated. + +There is not general consensus whenever use `fun a() = {}` or `fun a(){return}`. Yet from reader perspective one-lines seem to better show that the property or function is easily calculated. \ No newline at end of file -- 2.34.1 From 635aac5f30ce84516db3d3bcc1d4a065572a728c Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Sun, 14 Jun 2020 22:58:09 +0700 Subject: [PATCH 063/156] Refactor ex-AsmGenerationContext, introduce many bytecode utility functions to make its code readable, update compile method --- .../scientifik/kmath/asm/AsmBuilders.kt | 10 +- .../scientifik/kmath/asm/AsmExpressions.kt | 64 ++-- .../asm/internal/AsmGenerationContext.kt | 319 ------------------ .../kmath/asm/internal/AsmGenerator.kt | 311 +++++++++++++++++ .../kmath/asm/internal/ClassWriters.kt | 15 + .../kmath/asm/internal/MethodVisitors.kt | 32 +- .../kmath/asm/internal/Optimization.kt | 8 +- 7 files changed, 394 insertions(+), 365 deletions(-) delete mode 100644 kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmGenerationContext.kt create mode 100644 kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmGenerator.kt create mode 100644 kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/ClassWriters.kt diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmBuilders.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmBuilders.kt index 69e49f8b6..223278756 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmBuilders.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmBuilders.kt @@ -1,6 +1,6 @@ package scientifik.kmath.asm -import scientifik.kmath.asm.internal.AsmGenerationContext +import scientifik.kmath.asm.internal.AsmGenerator import scientifik.kmath.ast.MST import scientifik.kmath.ast.evaluate import scientifik.kmath.expressions.Expression @@ -20,12 +20,8 @@ internal fun buildName(expression: AsmNode<*>, collision: Int = 0): String { } @PublishedApi -internal inline fun AsmNode.compile(algebra: Algebra): Expression { - val ctx = - AsmGenerationContext(T::class.java, algebra, buildName(this)) - compile(ctx) - return ctx.generate() -} +internal inline fun AsmNode.compile(algebra: Algebra): Expression = + AsmGenerator(T::class.java, algebra, buildName(this), this).getInstance() inline fun , E : AsmExpressionAlgebra> A.asm( expressionAlgebra: E, diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmExpressions.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmExpressions.kt index 8f16a2710..c1f71612f 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmExpressions.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmExpressions.kt @@ -1,6 +1,6 @@ package scientifik.kmath.asm -import scientifik.kmath.asm.internal.AsmGenerationContext +import scientifik.kmath.asm.internal.AsmGenerator import scientifik.kmath.asm.internal.hasSpecific import scientifik.kmath.asm.internal.optimize import scientifik.kmath.asm.internal.tryInvokeSpecific @@ -9,7 +9,7 @@ import scientifik.kmath.expressions.ExpressionAlgebra import scientifik.kmath.operations.* /** - * A function declaration that could be compiled to [AsmGenerationContext]. + * A function declaration that could be compiled to [AsmGenerator]. * * @param T the type the stored function returns. */ @@ -24,10 +24,10 @@ abstract class AsmNode internal constructor() { /** * Compiles this declaration. * - * @param gen the target [AsmGenerationContext]. + * @param gen the target [AsmGenerator]. */ @PublishedApi - internal abstract fun compile(gen: AsmGenerationContext) + internal abstract fun compile(gen: AsmGenerator) } internal class AsmUnaryOperation(private val context: Algebra, private val name: String, expr: AsmNode) : @@ -35,23 +35,23 @@ internal class AsmUnaryOperation(private val context: Algebra, private val private val expr: AsmNode = expr.optimize() override fun tryEvaluate(): T? = context { unaryOperation(name, expr.tryEvaluate() ?: return@context null) } - override fun compile(gen: AsmGenerationContext) { - gen.visitLoadAlgebra() + override fun compile(gen: AsmGenerator) { + gen.loadAlgebra() if (!hasSpecific(context, name, 1)) - gen.visitStringConstant(name) + gen.loadStringConstant(name) expr.compile(gen) if (gen.tryInvokeSpecific(context, name, 1)) return - gen.visitAlgebraOperation( - owner = AsmGenerationContext.ALGEBRA_CLASS, + gen.invokeAlgebraOperation( + owner = AsmGenerator.ALGEBRA_CLASS, method = "unaryOperation", - descriptor = "(L${AsmGenerationContext.STRING_CLASS};" + - "L${AsmGenerationContext.OBJECT_CLASS};)" + - "L${AsmGenerationContext.OBJECT_CLASS};" + descriptor = "(L${AsmGenerator.STRING_CLASS};" + + "L${AsmGenerator.OBJECT_CLASS};)" + + "L${AsmGenerator.OBJECT_CLASS};" ) } } @@ -73,11 +73,11 @@ internal class AsmBinaryOperation( ) } - override fun compile(gen: AsmGenerationContext) { - gen.visitLoadAlgebra() + override fun compile(gen: AsmGenerator) { + gen.loadAlgebra() if (!hasSpecific(context, name, 2)) - gen.visitStringConstant(name) + gen.loadStringConstant(name) first.compile(gen) second.compile(gen) @@ -85,26 +85,26 @@ internal class AsmBinaryOperation( if (gen.tryInvokeSpecific(context, name, 2)) return - gen.visitAlgebraOperation( - owner = AsmGenerationContext.ALGEBRA_CLASS, + gen.invokeAlgebraOperation( + owner = AsmGenerator.ALGEBRA_CLASS, method = "binaryOperation", - descriptor = "(L${AsmGenerationContext.STRING_CLASS};" + - "L${AsmGenerationContext.OBJECT_CLASS};" + - "L${AsmGenerationContext.OBJECT_CLASS};)" + - "L${AsmGenerationContext.OBJECT_CLASS};" + descriptor = "(L${AsmGenerator.STRING_CLASS};" + + "L${AsmGenerator.OBJECT_CLASS};" + + "L${AsmGenerator.OBJECT_CLASS};)" + + "L${AsmGenerator.OBJECT_CLASS};" ) } } internal class AsmVariableExpression(private val name: String, private val default: T? = null) : AsmNode() { - override fun compile(gen: AsmGenerationContext): Unit = gen.visitLoadFromVariables(name, default) + override fun compile(gen: AsmGenerator): Unit = gen.loadFromVariables(name, default) } internal class AsmConstantExpression(private val value: T) : AsmNode() { override fun tryEvaluate(): T = value - override fun compile(gen: AsmGenerationContext): Unit = gen.visitLoadFromConstants(value) + override fun compile(gen: AsmGenerator): Unit = gen.loadTConstant(value) } internal class AsmConstProductExpression( @@ -116,17 +116,17 @@ internal class AsmConstProductExpression( override fun tryEvaluate(): T? = context { (expr.tryEvaluate() ?: return@context null) * const } - override fun compile(gen: AsmGenerationContext) { - gen.visitLoadAlgebra() - gen.visitNumberConstant(const) + override fun compile(gen: AsmGenerator) { + gen.loadAlgebra() + gen.loadNumberConstant(const) expr.compile(gen) - gen.visitAlgebraOperation( - owner = AsmGenerationContext.SPACE_OPERATIONS_CLASS, + gen.invokeAlgebraOperation( + owner = AsmGenerator.SPACE_OPERATIONS_CLASS, method = "multiply", - descriptor = "(L${AsmGenerationContext.OBJECT_CLASS};" + - "L${AsmGenerationContext.NUMBER_CLASS};)" + - "L${AsmGenerationContext.OBJECT_CLASS};" + descriptor = "(L${AsmGenerator.OBJECT_CLASS};" + + "L${AsmGenerator.NUMBER_CLASS};)" + + "L${AsmGenerator.OBJECT_CLASS};" ) } } @@ -135,7 +135,7 @@ internal class AsmNumberExpression(private val context: NumericAlgebra, pr AsmNode() { override fun tryEvaluate(): T? = context.number(value) - override fun compile(gen: AsmGenerationContext): Unit = gen.visitNumberConstant(value) + override fun compile(gen: AsmGenerator): Unit = gen.loadNumberConstant(value) } internal abstract class FunctionalCompiledExpression internal constructor( diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmGenerationContext.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmGenerationContext.kt deleted file mode 100644 index fa0f06579..000000000 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmGenerationContext.kt +++ /dev/null @@ -1,319 +0,0 @@ -package scientifik.kmath.asm.internal - -import org.objectweb.asm.ClassWriter -import org.objectweb.asm.Label -import org.objectweb.asm.MethodVisitor -import org.objectweb.asm.Opcodes -import scientifik.kmath.asm.FunctionalCompiledExpression -import scientifik.kmath.asm.internal.AsmGenerationContext.ClassLoader -import scientifik.kmath.operations.Algebra - -/** - * AsmGenerationContext is a structure that abstracts building a class that unwraps [AsmNode] to plain Java - * expression. This class uses [ClassLoader] for loading the generated class, then it is able to instantiate the new - * class. - * - * @param T the type of AsmExpression to unwrap. - * @param algebra the algebra the applied AsmExpressions use. - * @param className the unique class name of new loaded class. - */ -@PublishedApi -internal class AsmGenerationContext @PublishedApi internal constructor( - private val classOfT: Class<*>, - private val algebra: Algebra, - private val className: String -) { - private class ClassLoader(parent: java.lang.ClassLoader) : java.lang.ClassLoader(parent) { - internal fun defineClass(name: String?, b: ByteArray): Class<*> = defineClass(name, b, 0, b.size) - } - - private val classLoader: ClassLoader = - ClassLoader(javaClass.classLoader) - - @Suppress("PrivatePropertyName") - private val T_ALGEBRA_CLASS: String = algebra.javaClass.name.replace(oldChar = '.', newChar = '/') - - @Suppress("PrivatePropertyName") - private val T_CLASS: String = classOfT.name.replace('.', '/') - - private val slashesClassName: String = className.replace(oldChar = '.', newChar = '/') - private val invokeThisVar: Int = 0 - private val invokeArgumentsVar: Int = 1 - private var maxStack: Int = 0 - private val constants: MutableList = mutableListOf() - private val asmCompiledClassWriter: ClassWriter = ClassWriter(0) - private val invokeMethodVisitor: MethodVisitor - private val invokeL0: Label - private lateinit var invokeL1: Label - private var generatedInstance: FunctionalCompiledExpression? = null - - init { - asmCompiledClassWriter.visit( - Opcodes.V1_8, - Opcodes.ACC_PUBLIC or Opcodes.ACC_FINAL or Opcodes.ACC_SUPER, - slashesClassName, - "L$FUNCTIONAL_COMPILED_EXPRESSION_CLASS;", - FUNCTIONAL_COMPILED_EXPRESSION_CLASS, - arrayOf() - ) - - asmCompiledClassWriter.run { - visitMethod(Opcodes.ACC_PUBLIC, "", "(L$ALGEBRA_CLASS;[L$OBJECT_CLASS;)V", null, null).run { - val thisVar = 0 - val algebraVar = 1 - val constantsVar = 2 - val l0 = Label() - visitLabel(l0) - visitVarInsn(Opcodes.ALOAD, thisVar) - visitVarInsn(Opcodes.ALOAD, algebraVar) - visitVarInsn(Opcodes.ALOAD, constantsVar) - - visitMethodInsn( - Opcodes.INVOKESPECIAL, - FUNCTIONAL_COMPILED_EXPRESSION_CLASS, - "", - "(L$ALGEBRA_CLASS;[L$OBJECT_CLASS;)V", - false - ) - - val l1 = Label() - visitLabel(l1) - visitInsn(Opcodes.RETURN) - val l2 = Label() - visitLabel(l2) - visitLocalVariable("this", "L$slashesClassName;", null, l0, l2, thisVar) - - visitLocalVariable( - "algebra", - "L$ALGEBRA_CLASS;", - "L$ALGEBRA_CLASS;", - l0, - l2, - algebraVar - ) - - visitLocalVariable("constants", "[L$OBJECT_CLASS;", null, l0, l2, constantsVar) - visitMaxs(3, 3) - visitEnd() - } - - invokeMethodVisitor = visitMethod( - Opcodes.ACC_PUBLIC or Opcodes.ACC_FINAL, - "invoke", - "(L$MAP_CLASS;)L$T_CLASS;", - "(L$MAP_CLASS;)L$T_CLASS;", - null - ) - - invokeMethodVisitor.run { - visitCode() - invokeL0 = Label() - visitLabel(invokeL0) - } - } - } - - @PublishedApi - @Suppress("UNCHECKED_CAST") - internal fun generate(): FunctionalCompiledExpression { - generatedInstance?.let { return it } - - invokeMethodVisitor.run { - visitInsn(Opcodes.ARETURN) - invokeL1 = Label() - visitLabel(invokeL1) - - visitLocalVariable( - "this", - "L$slashesClassName;", - T_CLASS, - invokeL0, - invokeL1, - invokeThisVar - ) - - visitLocalVariable( - "arguments", - "L$MAP_CLASS;", - "L$MAP_CLASS;", - invokeL0, - invokeL1, - invokeArgumentsVar - ) - - visitMaxs(maxStack + 1, 2) - visitEnd() - } - - asmCompiledClassWriter.visitMethod( - Opcodes.ACC_PUBLIC or Opcodes.ACC_FINAL or Opcodes.ACC_BRIDGE or Opcodes.ACC_SYNTHETIC, - "invoke", - "(L$MAP_CLASS;)L$OBJECT_CLASS;", - null, - null - ).run { - val thisVar = 0 - visitCode() - val l0 = Label() - visitLabel(l0) - visitVarInsn(Opcodes.ALOAD, 0) - visitVarInsn(Opcodes.ALOAD, 1) - visitMethodInsn(Opcodes.INVOKEVIRTUAL, slashesClassName, "invoke", "(L$MAP_CLASS;)L$T_CLASS;", false) - visitInsn(Opcodes.ARETURN) - val l1 = Label() - visitLabel(l1) - - visitLocalVariable( - "this", - "L$slashesClassName;", - T_CLASS, - l0, - l1, - thisVar - ) - - visitMaxs(2, 2) - visitEnd() - } - - asmCompiledClassWriter.visitEnd() - - val new = classLoader - .defineClass(className, asmCompiledClassWriter.toByteArray()) - .constructors - .first() - .newInstance(algebra, constants.toTypedArray()) as FunctionalCompiledExpression - - generatedInstance = new - return new - } - - internal fun visitLoadFromConstants(value: T) { - if (classOfT in INLINABLE_NUMBERS) { - visitNumberConstant(value as Number) - visitCastToT() - return - } - - visitLoadAnyFromConstants(value as Any, T_CLASS) - } - - private fun visitLoadAnyFromConstants(value: Any, type: String) { - val idx = if (value in constants) constants.indexOf(value) else constants.apply { add(value) }.lastIndex - maxStack++ - - invokeMethodVisitor.run { - visitLoadThis() - visitFieldInsn(Opcodes.GETFIELD, slashesClassName, "constants", "[L$OBJECT_CLASS;") - visitLdcOrIConstInsn(idx) - visitInsn(Opcodes.AALOAD) - invokeMethodVisitor.visitTypeInsn(Opcodes.CHECKCAST, type) - } - } - - private fun visitLoadThis(): Unit = invokeMethodVisitor.visitVarInsn(Opcodes.ALOAD, invokeThisVar) - - internal fun visitNumberConstant(value: Number) { - maxStack++ - val clazz = value.javaClass - val c = clazz.name.replace('.', '/') - val sigLetter = SIGNATURE_LETTERS[clazz] - - if (sigLetter != null) { - when (value) { - is Int -> invokeMethodVisitor.visitLdcOrIConstInsn(value) - is Double -> invokeMethodVisitor.visitLdcOrDConstInsn(value) - is Float -> invokeMethodVisitor.visitLdcOrFConstInsn(value) - else -> invokeMethodVisitor.visitLdcInsn(value) - } - - invokeMethodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, c, "valueOf", "($sigLetter)L${c};", false) - return - } - - visitLoadAnyFromConstants(value, c) - } - - internal fun visitLoadFromVariables(name: String, defaultValue: T? = null): Unit = invokeMethodVisitor.run { - maxStack += 2 - visitVarInsn(Opcodes.ALOAD, invokeArgumentsVar) - - if (defaultValue != null) { - visitLdcInsn(name) - visitLoadFromConstants(defaultValue) - - visitMethodInsn( - Opcodes.INVOKEINTERFACE, - MAP_CLASS, - "getOrDefault", - "(L$OBJECT_CLASS;L$OBJECT_CLASS;)L$OBJECT_CLASS;", - true - ) - - visitCastToT() - return - } - - visitLdcInsn(name) - visitMethodInsn( - Opcodes.INVOKEINTERFACE, - MAP_CLASS, "get", "(L$OBJECT_CLASS;)L$OBJECT_CLASS;", true - ) - visitCastToT() - } - - internal fun visitLoadAlgebra() { - maxStack++ - invokeMethodVisitor.visitVarInsn(Opcodes.ALOAD, invokeThisVar) - - invokeMethodVisitor.visitFieldInsn( - Opcodes.GETFIELD, - FUNCTIONAL_COMPILED_EXPRESSION_CLASS, "algebra", "L$ALGEBRA_CLASS;" - ) - - invokeMethodVisitor.visitTypeInsn(Opcodes.CHECKCAST, T_ALGEBRA_CLASS) - } - - internal fun visitAlgebraOperation( - owner: String, - method: String, - descriptor: String, - opcode: Int = Opcodes.INVOKEINTERFACE, - isInterface: Boolean = true - ) { - maxStack++ - invokeMethodVisitor.visitMethodInsn(opcode, owner, method, descriptor, isInterface) - visitCastToT() - } - - private fun visitCastToT(): Unit = invokeMethodVisitor.visitTypeInsn(Opcodes.CHECKCAST, T_CLASS) - - internal fun visitStringConstant(string: String) { - invokeMethodVisitor.visitLdcInsn(string) - } - - internal companion object { - private val SIGNATURE_LETTERS by lazy { - mapOf( - java.lang.Byte::class.java to "B", - java.lang.Short::class.java to "S", - java.lang.Integer::class.java to "I", - java.lang.Long::class.java to "J", - java.lang.Float::class.java to "F", - java.lang.Double::class.java to "D" - ) - } - - private val INLINABLE_NUMBERS by lazy { SIGNATURE_LETTERS.keys } - - internal const val FUNCTIONAL_COMPILED_EXPRESSION_CLASS = - "scientifik/kmath/asm/FunctionalCompiledExpression" - - internal const val MAP_CLASS = "java/util/Map" - internal const val OBJECT_CLASS = "java/lang/Object" - internal const val ALGEBRA_CLASS = "scientifik/kmath/operations/Algebra" - internal const val SPACE_OPERATIONS_CLASS = "scientifik/kmath/operations/SpaceOperations" - internal const val STRING_CLASS = "java/lang/String" - internal const val NUMBER_CLASS = "java/lang/Number" - } -} diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmGenerator.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmGenerator.kt new file mode 100644 index 000000000..f636fc289 --- /dev/null +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmGenerator.kt @@ -0,0 +1,311 @@ +package scientifik.kmath.asm.internal + +import org.objectweb.asm.ClassWriter +import org.objectweb.asm.Label +import org.objectweb.asm.MethodVisitor +import org.objectweb.asm.Opcodes +import scientifik.kmath.asm.AsmNode +import scientifik.kmath.asm.FunctionalCompiledExpression +import scientifik.kmath.asm.internal.AsmGenerator.ClassLoader +import scientifik.kmath.operations.Algebra + +/** + * ASM Generator is a structure that abstracts building a class that unwraps [AsmNode] to plain Java expression. + * This class uses [ClassLoader] for loading the generated class, then it is able to instantiate the new class. + * + * @param T the type of AsmExpression to unwrap. + * @param algebra the algebra the applied AsmExpressions use. + * @param className the unique class name of new loaded class. + */ +@Suppress("UNCHECKED_CAST") +@PublishedApi +internal class AsmGenerator @PublishedApi internal constructor( + private val classOfT: Class<*>, + private val algebra: Algebra, + private val className: String, + private val root: AsmNode +) { + private class ClassLoader(parent: java.lang.ClassLoader) : java.lang.ClassLoader(parent) { + internal fun defineClass(name: String?, b: ByteArray): Class<*> = defineClass(name, b, 0, b.size) + } + + private val classLoader: ClassLoader = + ClassLoader(javaClass.classLoader) + + @Suppress("PrivatePropertyName") + private val T_ALGEBRA_CLASS: String = algebra.javaClass.name.replace(oldChar = '.', newChar = '/') + + @Suppress("PrivatePropertyName") + private val T_CLASS: String = classOfT.name.replace('.', '/') + + private val slashesClassName: String = className.replace(oldChar = '.', newChar = '/') + private val invokeThisVar: Int = 0 + private val invokeArgumentsVar: Int = 1 + private val constants: MutableList = mutableListOf() + private lateinit var invokeMethodVisitor: MethodVisitor + private var generatedInstance: FunctionalCompiledExpression? = null + + fun getInstance(): FunctionalCompiledExpression { + generatedInstance?.let { return it } + + val classWriter = ClassWriter(ClassWriter.COMPUTE_FRAMES) { + visit( + Opcodes.V1_8, + Opcodes.ACC_PUBLIC or Opcodes.ACC_FINAL or Opcodes.ACC_SUPER, + slashesClassName, + "L$FUNCTIONAL_COMPILED_EXPRESSION_CLASS;", + FUNCTIONAL_COMPILED_EXPRESSION_CLASS, + arrayOf() + ) + + visitMethod( + access = Opcodes.ACC_PUBLIC, + name = "", + descriptor = "(L$ALGEBRA_CLASS;[L$OBJECT_CLASS;)V", + signature = null, + exceptions = null + ) { + val thisVar = 0 + val algebraVar = 1 + val constantsVar = 2 + val l0 = Label() + visitLabel(l0) + visitLoadObjectVar(thisVar) + visitLoadObjectVar(algebraVar) + visitLoadObjectVar(constantsVar) + + visitInvokeSpecial( + FUNCTIONAL_COMPILED_EXPRESSION_CLASS, + "", + "(L$ALGEBRA_CLASS;[L$OBJECT_CLASS;)V" + ) + + val l1 = Label() + visitLabel(l1) + visitReturn() + val l2 = Label() + visitLabel(l2) + visitLocalVariable("this", "L$slashesClassName;", null, l0, l2, thisVar) + + visitLocalVariable( + "algebra", + "L$ALGEBRA_CLASS;", + "L$ALGEBRA_CLASS;", + l0, + l2, + algebraVar + ) + + visitLocalVariable("constants", "[L$OBJECT_CLASS;", null, l0, l2, constantsVar) + visitMaxs(0, 3) + visitEnd() + } + + invokeMethodVisitor = visitMethod( + access = Opcodes.ACC_PUBLIC or Opcodes.ACC_FINAL, + name = "invoke", + descriptor = "(L$MAP_CLASS;)L$T_CLASS;", + signature = "(L$MAP_CLASS;)L$T_CLASS;", + exceptions = null + ) {} + + invokeMethodVisitor.run { + visitCode() + val l0 = Label() + visitLabel(l0) + root.compile(this@AsmGenerator) + visitReturnObject() + val l1 = Label() + visitLabel(l1) + + visitLocalVariable( + "this", + "L$slashesClassName;", + T_CLASS, + l0, + l1, + invokeThisVar + ) + + visitLocalVariable( + "arguments", + "L$MAP_CLASS;", + "L$MAP_CLASS;", + l0, + l1, + invokeArgumentsVar + ) + + visitMaxs(0, 2) + visitEnd() + } + + visitMethod( + access = Opcodes.ACC_PUBLIC or Opcodes.ACC_FINAL or Opcodes.ACC_BRIDGE or Opcodes.ACC_SYNTHETIC, + name = "invoke", + descriptor = "(L$MAP_CLASS;)L$OBJECT_CLASS;", + signature = null, + exceptions = null + ) { + val thisVar = 0 + val argumentsVar = 1 + visitCode() + val l0 = Label() + visitLabel(l0) + visitLoadObjectVar(thisVar) + visitLoadObjectVar(argumentsVar) + visitInvokeVirtual(owner = slashesClassName, name = "invoke", descriptor = "(L$MAP_CLASS;)L$T_CLASS;") + visitReturnObject() + val l1 = Label() + visitLabel(l1) + + visitLocalVariable( + "this", + "L$slashesClassName;", + T_CLASS, + l0, + l1, + thisVar + ) + + visitMaxs(0, 2) + visitEnd() + } + + visitEnd() + } + + val new = classLoader + .defineClass(className, classWriter.toByteArray()) + .constructors + .first() + .newInstance(algebra, constants.toTypedArray()) as FunctionalCompiledExpression + + generatedInstance = new + return new + } + + internal fun loadTConstant(value: T) { + if (classOfT in INLINABLE_NUMBERS) { + loadNumberConstant(value as Number) + invokeMethodVisitor.visitCheckCast(T_CLASS) + return + } + + loadConstant(value as Any, T_CLASS) + } + + private fun loadConstant(value: Any, type: String) { + val idx = if (value in constants) constants.indexOf(value) else constants.apply { add(value) }.lastIndex + + invokeMethodVisitor.run { + loadThis() + visitGetField(owner = slashesClassName, name = "constants", descriptor = "[L$OBJECT_CLASS;") + visitLdcOrIntConstant(idx) + visitGetObjectArrayElement() + invokeMethodVisitor.visitCheckCast(type) + } + } + + private fun loadThis(): Unit = invokeMethodVisitor.visitLoadObjectVar(invokeThisVar) + + internal fun loadNumberConstant(value: Number) { + val clazz = value.javaClass + val c = clazz.name.replace('.', '/') + val sigLetter = SIGNATURE_LETTERS[clazz] + + if (sigLetter != null) { + when (value) { + is Int -> invokeMethodVisitor.visitLdcOrIntConstant(value) + is Double -> invokeMethodVisitor.visitLdcOrDoubleConstant(value) + is Float -> invokeMethodVisitor.visitLdcOrFloatConstant(value) + else -> invokeMethodVisitor.visitLdcInsn(value) + } + + invokeMethodVisitor.visitInvokeStatic(c, "valueOf", "($sigLetter)L${c};") + return + } + + loadConstant(value, c) + } + + internal fun loadFromVariables(name: String, defaultValue: T? = null): Unit = invokeMethodVisitor.run { + visitLoadObjectVar(invokeArgumentsVar) + + if (defaultValue != null) { + visitLdcInsn(name) + loadTConstant(defaultValue) + + visitInvokeInterface( + owner = MAP_CLASS, + name = "getOrDefault", + descriptor = "(L$OBJECT_CLASS;L$OBJECT_CLASS;)L$OBJECT_CLASS;" + ) + + invokeMethodVisitor.visitCheckCast(T_CLASS) + return + } + + visitLdcInsn(name) + + visitInvokeInterface( + owner = MAP_CLASS, + name = "get", + descriptor = "(L$OBJECT_CLASS;)L$OBJECT_CLASS;" + ) + + invokeMethodVisitor.visitCheckCast(T_CLASS) + } + + internal fun loadAlgebra() { + loadThis() + + invokeMethodVisitor.visitGetField( + owner = FUNCTIONAL_COMPILED_EXPRESSION_CLASS, + name = "algebra", + descriptor = "L$ALGEBRA_CLASS;" + ) + + invokeMethodVisitor.visitCheckCast(T_ALGEBRA_CLASS) + } + + internal fun invokeAlgebraOperation( + owner: String, + method: String, + descriptor: String, + opcode: Int = Opcodes.INVOKEINTERFACE, + isInterface: Boolean = true + ) { + invokeMethodVisitor.visitMethodInsn(opcode, owner, method, descriptor, isInterface) + invokeMethodVisitor.visitCheckCast(T_CLASS) + } + + internal fun loadStringConstant(string: String) { + invokeMethodVisitor.visitLdcInsn(string) + } + + internal companion object { + private val SIGNATURE_LETTERS: Map, String> by lazy { + mapOf( + java.lang.Byte::class.java to "B", + java.lang.Short::class.java to "S", + java.lang.Integer::class.java to "I", + java.lang.Long::class.java to "J", + java.lang.Float::class.java to "F", + java.lang.Double::class.java to "D" + ) + } + + private val INLINABLE_NUMBERS: Set> by lazy { SIGNATURE_LETTERS.keys } + + internal const val FUNCTIONAL_COMPILED_EXPRESSION_CLASS = + "scientifik/kmath/asm/FunctionalCompiledExpression" + + internal const val MAP_CLASS = "java/util/Map" + internal const val OBJECT_CLASS = "java/lang/Object" + internal const val ALGEBRA_CLASS = "scientifik/kmath/operations/Algebra" + internal const val SPACE_OPERATIONS_CLASS = "scientifik/kmath/operations/SpaceOperations" + internal const val STRING_CLASS = "java/lang/String" + internal const val NUMBER_CLASS = "java/lang/Number" + } +} diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/ClassWriters.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/ClassWriters.kt new file mode 100644 index 000000000..84f736ee7 --- /dev/null +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/ClassWriters.kt @@ -0,0 +1,15 @@ +package scientifik.kmath.asm.internal + +import org.objectweb.asm.ClassWriter +import org.objectweb.asm.MethodVisitor + +inline fun ClassWriter(flags: Int, block: ClassWriter.() -> Unit): ClassWriter = ClassWriter(flags).apply(block) + +inline fun ClassWriter.visitMethod( + access: Int, + name: String, + descriptor: String, + signature: String?, + exceptions: Array?, + block: MethodVisitor.() -> Unit +): MethodVisitor = visitMethod(access, name, descriptor, signature, exceptions).apply(block) diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/MethodVisitors.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/MethodVisitors.kt index 356de4765..d29d786fc 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/MethodVisitors.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/MethodVisitors.kt @@ -3,7 +3,7 @@ package scientifik.kmath.asm.internal import org.objectweb.asm.MethodVisitor import org.objectweb.asm.Opcodes.* -internal fun MethodVisitor.visitLdcOrIConstInsn(value: Int) = when (value) { +internal fun MethodVisitor.visitLdcOrIntConstant(value: Int): Unit = when (value) { -1 -> visitInsn(ICONST_M1) 0 -> visitInsn(ICONST_0) 1 -> visitInsn(ICONST_1) @@ -14,15 +14,41 @@ internal fun MethodVisitor.visitLdcOrIConstInsn(value: Int) = when (value) { else -> visitLdcInsn(value) } -internal fun MethodVisitor.visitLdcOrDConstInsn(value: Double) = when (value) { +internal fun MethodVisitor.visitLdcOrDoubleConstant(value: Double): Unit = when (value) { 0.0 -> visitInsn(DCONST_0) 1.0 -> visitInsn(DCONST_1) else -> visitLdcInsn(value) } -internal fun MethodVisitor.visitLdcOrFConstInsn(value: Float) = when (value) { +internal fun MethodVisitor.visitLdcOrFloatConstant(value: Float): Unit = when (value) { 0f -> visitInsn(FCONST_0) 1f -> visitInsn(FCONST_1) 2f -> visitInsn(FCONST_2) else -> visitLdcInsn(value) } + +internal fun MethodVisitor.visitInvokeInterface(owner: String, name: String, descriptor: String): Unit = + visitMethodInsn(INVOKEINTERFACE, owner, name, descriptor, true) + +internal fun MethodVisitor.visitInvokeVirtual(owner: String, name: String, descriptor: String): Unit = + visitMethodInsn(INVOKEVIRTUAL, owner, name, descriptor, false) + +internal fun MethodVisitor.visitInvokeStatic(owner: String, name: String, descriptor: String): Unit = + visitMethodInsn(INVOKESTATIC, owner, name, descriptor, false) + +internal fun MethodVisitor.visitInvokeSpecial(owner: String, name: String, descriptor: String): Unit = + visitMethodInsn(INVOKESPECIAL, owner, name, descriptor, false) + +internal fun MethodVisitor.visitCheckCast(type: String): Unit = visitTypeInsn(CHECKCAST, type) + +internal fun MethodVisitor.visitGetField(owner: String, name: String, descriptor: String): Unit = + visitFieldInsn(GETFIELD, owner, name, descriptor) + +internal fun MethodVisitor.visitLoadObjectVar(`var`: Int) { + visitVarInsn(ALOAD, `var`) +} + +internal fun MethodVisitor.visitGetObjectArrayElement(): Unit = visitInsn(AALOAD) + +internal fun MethodVisitor.visitReturn(): Unit = visitInsn(RETURN) +internal fun MethodVisitor.visitReturnObject(): Unit = visitInsn(ARETURN) diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/Optimization.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/Optimization.kt index 815f292ce..3b37f81e5 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/Optimization.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/Optimization.kt @@ -16,7 +16,7 @@ internal fun hasSpecific(context: Algebra, name: String, arity: Int): Boo return true } -internal fun AsmGenerationContext.tryInvokeSpecific(context: Algebra, name: String, arity: Int): Boolean { +internal fun AsmGenerator.tryInvokeSpecific(context: Algebra, name: String, arity: Int): Boolean { val aName = methodNameAdapters[name] ?: name context::class.java.methods.find { it.name == aName && it.parameters.size == arity } @@ -26,12 +26,12 @@ internal fun AsmGenerationContext.tryInvokeSpecific(context: Algebra, val sig = buildString { append('(') - repeat(arity) { append("L${AsmGenerationContext.OBJECT_CLASS};") } + repeat(arity) { append("L${AsmGenerator.OBJECT_CLASS};") } append(')') - append("L${AsmGenerationContext.OBJECT_CLASS};") + append("L${AsmGenerator.OBJECT_CLASS};") } - visitAlgebraOperation( + invokeAlgebraOperation( owner = owner, method = aName, descriptor = sig, -- 2.34.1 From 4e28ad7d4e91eb26196907d5f28c618211f71a31 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Sun, 14 Jun 2020 23:00:26 +0700 Subject: [PATCH 064/156] Minor refactor --- .../scientifik/kmath/asm/internal/AsmGenerator.kt | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmGenerator.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmGenerator.kt index f636fc289..d2568eb53 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmGenerator.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmGenerator.kt @@ -17,7 +17,6 @@ import scientifik.kmath.operations.Algebra * @param algebra the algebra the applied AsmExpressions use. * @param className the unique class name of new loaded class. */ -@Suppress("UNCHECKED_CAST") @PublishedApi internal class AsmGenerator @PublishedApi internal constructor( private val classOfT: Class<*>, @@ -45,6 +44,7 @@ internal class AsmGenerator @PublishedApi internal constructor( private lateinit var invokeMethodVisitor: MethodVisitor private var generatedInstance: FunctionalCompiledExpression? = null + @Suppress("UNCHECKED_CAST") fun getInstance(): FunctionalCompiledExpression { generatedInstance?.let { return it } @@ -101,15 +101,14 @@ internal class AsmGenerator @PublishedApi internal constructor( visitEnd() } - invokeMethodVisitor = visitMethod( + visitMethod( access = Opcodes.ACC_PUBLIC or Opcodes.ACC_FINAL, name = "invoke", descriptor = "(L$MAP_CLASS;)L$T_CLASS;", signature = "(L$MAP_CLASS;)L$T_CLASS;", exceptions = null - ) {} - - invokeMethodVisitor.run { + ) { + invokeMethodVisitor = this visitCode() val l0 = Label() visitLabel(l0) @@ -121,7 +120,7 @@ internal class AsmGenerator @PublishedApi internal constructor( visitLocalVariable( "this", "L$slashesClassName;", - T_CLASS, + null, l0, l1, invokeThisVar @@ -280,9 +279,7 @@ internal class AsmGenerator @PublishedApi internal constructor( invokeMethodVisitor.visitCheckCast(T_CLASS) } - internal fun loadStringConstant(string: String) { - invokeMethodVisitor.visitLdcInsn(string) - } + internal fun loadStringConstant(string: String): Unit = invokeMethodVisitor.visitLdcInsn(string) internal companion object { private val SIGNATURE_LETTERS: Map, String> by lazy { -- 2.34.1 From a8fa38549758b5602118c85d23ffdf061fbce4ba Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Sun, 14 Jun 2020 23:01:34 +0700 Subject: [PATCH 065/156] Rename loadFromVariables to loadVariable --- .../src/jvmMain/kotlin/scientifik/kmath/asm/AsmExpressions.kt | 2 +- .../kotlin/scientifik/kmath/asm/internal/AsmGenerator.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmExpressions.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmExpressions.kt index c1f71612f..72a53d899 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmExpressions.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmExpressions.kt @@ -98,7 +98,7 @@ internal class AsmBinaryOperation( internal class AsmVariableExpression(private val name: String, private val default: T? = null) : AsmNode() { - override fun compile(gen: AsmGenerator): Unit = gen.loadFromVariables(name, default) + override fun compile(gen: AsmGenerator): Unit = gen.loadVariable(name, default) } internal class AsmConstantExpression(private val value: T) : diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmGenerator.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmGenerator.kt index d2568eb53..0c6975cd9 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmGenerator.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmGenerator.kt @@ -228,7 +228,7 @@ internal class AsmGenerator @PublishedApi internal constructor( loadConstant(value, c) } - internal fun loadFromVariables(name: String, defaultValue: T? = null): Unit = invokeMethodVisitor.run { + internal fun loadVariable(name: String, defaultValue: T? = null): Unit = invokeMethodVisitor.run { visitLoadObjectVar(invokeArgumentsVar) if (defaultValue != null) { -- 2.34.1 From a7302f49fffdadf91e4255fa3f65e476d51af96d Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Sun, 14 Jun 2020 23:13:26 +0700 Subject: [PATCH 066/156] Convert to expression body --- .../kotlin/scientifik/kmath/asm/internal/MethodVisitors.kt | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/MethodVisitors.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/MethodVisitors.kt index d29d786fc..70fb3bd44 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/MethodVisitors.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/MethodVisitors.kt @@ -44,9 +44,7 @@ internal fun MethodVisitor.visitCheckCast(type: String): Unit = visitTypeInsn(CH internal fun MethodVisitor.visitGetField(owner: String, name: String, descriptor: String): Unit = visitFieldInsn(GETFIELD, owner, name, descriptor) -internal fun MethodVisitor.visitLoadObjectVar(`var`: Int) { - visitVarInsn(ALOAD, `var`) -} +internal fun MethodVisitor.visitLoadObjectVar(`var`: Int): Unit = visitVarInsn(ALOAD, `var`) internal fun MethodVisitor.visitGetObjectArrayElement(): Unit = visitInsn(AALOAD) -- 2.34.1 From 3434dde1d1eeec094f03a150f55a73ac81fa5f59 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Mon, 15 Jun 2020 11:02:13 +0300 Subject: [PATCH 067/156] ASM API simplification --- kmath-ast/README.md | 4 + .../kotlin/scientifik/kmath/ast/MST.kt | 4 +- .../scientifik/kmath/asm/AsmBuilders.kt | 56 ---- .../scientifik/kmath/asm/AsmExpressions.kt | 249 ++++++++++-------- .../kotlin/scientifik/kmath/asm/asm.kt | 47 ++++ ...{AsmGenerationContext.kt => AsmBuilder.kt} | 66 +++-- .../{MethodVisitors.kt => methodVisitors.kt} | 0 .../{Optimization.kt => optimization.kt} | 13 +- .../kmath/{ast => }/asm/TestAsmAlgebras.kt | 2 +- .../kmath/{ast => }/asm/TestAsmExpressions.kt | 2 +- ...ions.kt => FunctionalExpressionAlgebra.kt} | 0 .../kmath/expressions/ExpressionFieldTest.kt | 4 +- 12 files changed, 231 insertions(+), 216 deletions(-) create mode 100644 kmath-ast/README.md delete mode 100644 kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmBuilders.kt create mode 100644 kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/asm.kt rename kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/{AsmGenerationContext.kt => AsmBuilder.kt} (82%) rename kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/{MethodVisitors.kt => methodVisitors.kt} (100%) rename kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/{Optimization.kt => optimization.kt} (72%) rename kmath-ast/src/jvmTest/kotlin/scietifik/kmath/{ast => }/asm/TestAsmAlgebras.kt (98%) rename kmath-ast/src/jvmTest/kotlin/scietifik/kmath/{ast => }/asm/TestAsmExpressions.kt (94%) rename kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/{FunctionalExpressions.kt => FunctionalExpressionAlgebra.kt} (100%) diff --git a/kmath-ast/README.md b/kmath-ast/README.md new file mode 100644 index 000000000..f9dbd4663 --- /dev/null +++ b/kmath-ast/README.md @@ -0,0 +1,4 @@ +# AST based expression representation and operations + +## Dynamic expression code generation +Contributed by [Iaroslav Postovalov](https://github.com/CommanderTvis). \ No newline at end of file diff --git a/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MST.kt b/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MST.kt index 3375ecdf3..900b9297a 100644 --- a/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MST.kt +++ b/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MST.kt @@ -11,7 +11,7 @@ sealed class MST { /** * A node containing unparsed string */ - data class Singular(val value: String) : MST() + data class Symbolic(val value: String) : MST() /** * A node containing a number @@ -43,7 +43,7 @@ sealed class MST { fun NumericAlgebra.evaluate(node: MST): T { return when (node) { is MST.Numeric -> number(node.value) - is MST.Singular -> symbol(node.value) + is MST.Symbolic -> symbol(node.value) is MST.Unary -> unaryOperation(node.operation, evaluate(node.value)) is MST.Binary -> when { node.left is MST.Numeric && node.right is MST.Numeric -> { diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmBuilders.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmBuilders.kt deleted file mode 100644 index 69e49f8b6..000000000 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmBuilders.kt +++ /dev/null @@ -1,56 +0,0 @@ -package scientifik.kmath.asm - -import scientifik.kmath.asm.internal.AsmGenerationContext -import scientifik.kmath.ast.MST -import scientifik.kmath.ast.evaluate -import scientifik.kmath.expressions.Expression -import scientifik.kmath.operations.* - -@PublishedApi -internal fun buildName(expression: AsmNode<*>, collision: Int = 0): String { - val name = "scientifik.kmath.expressions.generated.AsmCompiledExpression_${expression.hashCode()}_$collision" - - try { - Class.forName(name) - } catch (ignored: ClassNotFoundException) { - return name - } - - return buildName(expression, collision + 1) -} - -@PublishedApi -internal inline fun AsmNode.compile(algebra: Algebra): Expression { - val ctx = - AsmGenerationContext(T::class.java, algebra, buildName(this)) - compile(ctx) - return ctx.generate() -} - -inline fun , E : AsmExpressionAlgebra> A.asm( - expressionAlgebra: E, - block: E.() -> AsmNode -): Expression = expressionAlgebra.block().compile(expressionAlgebra.algebra) - -inline fun , E : AsmExpressionAlgebra> A.asm( - expressionAlgebra: E, - ast: MST -): Expression = asm(expressionAlgebra) { evaluate(ast) } - -inline fun A.asmSpace(block: AsmExpressionSpace.() -> AsmNode): Expression where A : NumericAlgebra, A : Space = - AsmExpressionSpace(this).let { it.block().compile(it.algebra) } - -inline fun A.asmSpace(ast: MST): Expression where A : NumericAlgebra, A : Space = - asmSpace { evaluate(ast) } - -inline fun A.asmRing(block: AsmExpressionRing.() -> AsmNode): Expression where A : NumericAlgebra, A : Ring = - AsmExpressionRing(this).let { it.block().compile(it.algebra) } - -inline fun A.asmRing(ast: MST): Expression where A : NumericAlgebra, A : Ring = - asmRing { evaluate(ast) } - -inline fun A.asmField(block: AsmExpressionField.() -> AsmNode): Expression where A : NumericAlgebra, A : Field = - AsmExpressionField(this).let { it.block().compile(it.algebra) } - -inline fun A.asmField(ast: MST): Expression where A : NumericAlgebra, A : Field = - asmRing { evaluate(ast) } diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmExpressions.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmExpressions.kt index 8f16a2710..a8b3ff976 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmExpressions.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmExpressions.kt @@ -1,19 +1,24 @@ package scientifik.kmath.asm -import scientifik.kmath.asm.internal.AsmGenerationContext +import scientifik.kmath.asm.internal.AsmBuilder import scientifik.kmath.asm.internal.hasSpecific import scientifik.kmath.asm.internal.optimize import scientifik.kmath.asm.internal.tryInvokeSpecific import scientifik.kmath.expressions.Expression import scientifik.kmath.expressions.ExpressionAlgebra import scientifik.kmath.operations.* +import kotlin.reflect.KClass /** - * A function declaration that could be compiled to [AsmGenerationContext]. + * A function declaration that could be compiled to [AsmBuilder]. * * @param T the type the stored function returns. */ -abstract class AsmNode internal constructor() { +sealed class AsmExpression: Expression { + abstract val type: KClass + + abstract val algebra: Algebra + /** * Tries to evaluate this function without its variables. This method is intended for optimization. * @@ -24,118 +29,144 @@ abstract class AsmNode internal constructor() { /** * Compiles this declaration. * - * @param gen the target [AsmGenerationContext]. + * @param gen the target [AsmBuilder]. */ - @PublishedApi - internal abstract fun compile(gen: AsmGenerationContext) + internal abstract fun appendTo(gen: AsmBuilder) + + /** + * Compile and cache the expression + */ + private val compiledExpression by lazy{ + val builder = AsmBuilder(type.java, algebra, buildName(this)) + this.appendTo(builder) + builder.generate() + } + + override fun invoke(arguments: Map): T = compiledExpression.invoke(arguments) } -internal class AsmUnaryOperation(private val context: Algebra, private val name: String, expr: AsmNode) : - AsmNode() { - private val expr: AsmNode = expr.optimize() - override fun tryEvaluate(): T? = context { unaryOperation(name, expr.tryEvaluate() ?: return@context null) } +internal class AsmUnaryOperation( + override val type: KClass, + override val algebra: Algebra, + private val name: String, + expr: AsmExpression +) : AsmExpression() { + private val expr: AsmExpression = expr.optimize() + override fun tryEvaluate(): T? = algebra { unaryOperation(name, expr.tryEvaluate() ?: return@algebra null) } - override fun compile(gen: AsmGenerationContext) { + override fun appendTo(gen: AsmBuilder) { gen.visitLoadAlgebra() - if (!hasSpecific(context, name, 1)) + if (!hasSpecific(algebra, name, 1)) gen.visitStringConstant(name) - expr.compile(gen) + expr.appendTo(gen) - if (gen.tryInvokeSpecific(context, name, 1)) + if (gen.tryInvokeSpecific(algebra, name, 1)) return gen.visitAlgebraOperation( - owner = AsmGenerationContext.ALGEBRA_CLASS, + owner = AsmBuilder.ALGEBRA_CLASS, method = "unaryOperation", - descriptor = "(L${AsmGenerationContext.STRING_CLASS};" + - "L${AsmGenerationContext.OBJECT_CLASS};)" + - "L${AsmGenerationContext.OBJECT_CLASS};" + descriptor = "(L${AsmBuilder.STRING_CLASS};" + + "L${AsmBuilder.OBJECT_CLASS};)" + + "L${AsmBuilder.OBJECT_CLASS};" ) } } -internal class AsmBinaryOperation( - private val context: Algebra, +internal class AsmBinaryOperation( + override val type: KClass, + override val algebra: Algebra, private val name: String, - first: AsmNode, - second: AsmNode -) : AsmNode() { - private val first: AsmNode = first.optimize() - private val second: AsmNode = second.optimize() + first: AsmExpression, + second: AsmExpression +) : AsmExpression() { + private val first: AsmExpression = first.optimize() + private val second: AsmExpression = second.optimize() - override fun tryEvaluate(): T? = context { + override fun tryEvaluate(): T? = algebra { binaryOperation( name, - first.tryEvaluate() ?: return@context null, - second.tryEvaluate() ?: return@context null + first.tryEvaluate() ?: return@algebra null, + second.tryEvaluate() ?: return@algebra null ) } - override fun compile(gen: AsmGenerationContext) { + override fun appendTo(gen: AsmBuilder) { gen.visitLoadAlgebra() - if (!hasSpecific(context, name, 2)) + if (!hasSpecific(algebra, name, 2)) gen.visitStringConstant(name) - first.compile(gen) - second.compile(gen) + first.appendTo(gen) + second.appendTo(gen) - if (gen.tryInvokeSpecific(context, name, 2)) + if (gen.tryInvokeSpecific(algebra, name, 2)) return gen.visitAlgebraOperation( - owner = AsmGenerationContext.ALGEBRA_CLASS, + owner = AsmBuilder.ALGEBRA_CLASS, method = "binaryOperation", - descriptor = "(L${AsmGenerationContext.STRING_CLASS};" + - "L${AsmGenerationContext.OBJECT_CLASS};" + - "L${AsmGenerationContext.OBJECT_CLASS};)" + - "L${AsmGenerationContext.OBJECT_CLASS};" + descriptor = "(L${AsmBuilder.STRING_CLASS};" + + "L${AsmBuilder.OBJECT_CLASS};" + + "L${AsmBuilder.OBJECT_CLASS};)" + + "L${AsmBuilder.OBJECT_CLASS};" ) } } -internal class AsmVariableExpression(private val name: String, private val default: T? = null) : - AsmNode() { - override fun compile(gen: AsmGenerationContext): Unit = gen.visitLoadFromVariables(name, default) +internal class AsmVariableExpression( + override val type: KClass, + override val algebra: Algebra, + private val name: String, + private val default: T? = null +) : AsmExpression() { + override fun appendTo(gen: AsmBuilder): Unit = gen.visitLoadFromVariables(name, default) } -internal class AsmConstantExpression(private val value: T) : - AsmNode() { +internal class AsmConstantExpression( + override val type: KClass, + override val algebra: Algebra, + private val value: T +) : AsmExpression() { override fun tryEvaluate(): T = value - override fun compile(gen: AsmGenerationContext): Unit = gen.visitLoadFromConstants(value) + override fun appendTo(gen: AsmBuilder): Unit = gen.visitLoadFromConstants(value) } -internal class AsmConstProductExpression( - private val context: Space, - expr: AsmNode, +internal class AsmConstProductExpression( + override val type: KClass, + override val algebra: Space, + expr: AsmExpression, private val const: Number -) : AsmNode() { - private val expr: AsmNode = expr.optimize() +) : AsmExpression() { + private val expr: AsmExpression = expr.optimize() - override fun tryEvaluate(): T? = context { (expr.tryEvaluate() ?: return@context null) * const } + override fun tryEvaluate(): T? = algebra { (expr.tryEvaluate() ?: return@algebra null) * const } - override fun compile(gen: AsmGenerationContext) { + override fun appendTo(gen: AsmBuilder) { gen.visitLoadAlgebra() gen.visitNumberConstant(const) - expr.compile(gen) + expr.appendTo(gen) gen.visitAlgebraOperation( - owner = AsmGenerationContext.SPACE_OPERATIONS_CLASS, + owner = AsmBuilder.SPACE_OPERATIONS_CLASS, method = "multiply", - descriptor = "(L${AsmGenerationContext.OBJECT_CLASS};" + - "L${AsmGenerationContext.NUMBER_CLASS};)" + - "L${AsmGenerationContext.OBJECT_CLASS};" + descriptor = "(L${AsmBuilder.OBJECT_CLASS};" + + "L${AsmBuilder.NUMBER_CLASS};)" + + "L${AsmBuilder.OBJECT_CLASS};" ) } } -internal class AsmNumberExpression(private val context: NumericAlgebra, private val value: Number) : - AsmNode() { - override fun tryEvaluate(): T? = context.number(value) +internal class AsmNumberExpression( + override val type: KClass, + override val algebra: NumericAlgebra, + private val value: Number +) : AsmExpression() { + override fun tryEvaluate(): T? = algebra.number(value) - override fun compile(gen: AsmGenerationContext): Unit = gen.visitNumberConstant(value) + override fun appendTo(gen: AsmBuilder): Unit = gen.visitNumberConstant(value) } internal abstract class FunctionalCompiledExpression internal constructor( @@ -146,120 +177,116 @@ internal abstract class FunctionalCompiledExpression internal constructor( } /** - * A context class for [AsmNode] construction. + * A context class for [AsmExpression] construction. + * + * @param algebra The algebra to provide for AsmExpressions built. */ -interface AsmExpressionAlgebra> : NumericAlgebra>, - ExpressionAlgebra> { - /** - * The algebra to provide for AsmExpressions built. - */ - val algebra: A +open class AsmExpressionAlgebra>(val type: KClass, val algebra: A) : + NumericAlgebra>, ExpressionAlgebra> { /** * Builds an AsmExpression to wrap a number. */ - override fun number(value: Number): AsmNode = AsmNumberExpression(algebra, value) + override fun number(value: Number): AsmExpression = AsmNumberExpression(type, algebra, value) /** * Builds an AsmExpression of constant expression which does not depend on arguments. */ - override fun const(value: T): AsmNode = AsmConstantExpression(value) + override fun const(value: T): AsmExpression = AsmConstantExpression(type, algebra, value) /** * Builds an AsmExpression to access a variable. */ - override fun variable(name: String, default: T?): AsmNode = AsmVariableExpression(name, default) + override fun variable(name: String, default: T?): AsmExpression = AsmVariableExpression(type, algebra, name, default) /** * Builds an AsmExpression of dynamic call of binary operation [operation] on [left] and [right]. */ - override fun binaryOperation(operation: String, left: AsmNode, right: AsmNode): AsmNode = - AsmBinaryOperation(algebra, operation, left, right) + override fun binaryOperation(operation: String, left: AsmExpression, right: AsmExpression): AsmExpression = + AsmBinaryOperation(type, algebra, operation, left, right) /** * Builds an AsmExpression of dynamic call of unary operation with name [operation] on [arg]. */ - override fun unaryOperation(operation: String, arg: AsmNode): AsmNode = - AsmUnaryOperation(algebra, operation, arg) + override fun unaryOperation(operation: String, arg: AsmExpression): AsmExpression = + AsmUnaryOperation(type, algebra, operation, arg) } /** - * A context class for [AsmNode] construction for [Space] algebras. + * A context class for [AsmExpression] construction for [Space] algebras. */ -open class AsmExpressionSpace(override val algebra: A) : AsmExpressionAlgebra, - Space> where A : Space, A : NumericAlgebra { - override val zero: AsmNode - get() = const(algebra.zero) +open class AsmExpressionSpace(type: KClass, algebra: A) : AsmExpressionAlgebra(type, algebra), + Space> where A : Space, A : NumericAlgebra { + override val zero: AsmExpression get() = const(algebra.zero) /** * Builds an AsmExpression of addition of two another expressions. */ - override fun add(a: AsmNode, b: AsmNode): AsmNode = - AsmBinaryOperation(algebra, SpaceOperations.PLUS_OPERATION, a, b) + override fun add(a: AsmExpression, b: AsmExpression): AsmExpression = + AsmBinaryOperation(type, algebra, SpaceOperations.PLUS_OPERATION, a, b) /** * Builds an AsmExpression of multiplication of expression by number. */ - override fun multiply(a: AsmNode, k: Number): AsmNode = AsmConstProductExpression(algebra, a, k) + override fun multiply(a: AsmExpression, k: Number): AsmExpression = AsmConstProductExpression(type, algebra, a, k) - operator fun AsmNode.plus(arg: T): AsmNode = this + const(arg) - operator fun AsmNode.minus(arg: T): AsmNode = this - const(arg) - operator fun T.plus(arg: AsmNode): AsmNode = arg + this - operator fun T.minus(arg: AsmNode): AsmNode = arg - this + operator fun AsmExpression.plus(arg: T): AsmExpression = this + const(arg) + operator fun AsmExpression.minus(arg: T): AsmExpression = this - const(arg) + operator fun T.plus(arg: AsmExpression): AsmExpression = arg + this + operator fun T.minus(arg: AsmExpression): AsmExpression = arg - this - override fun unaryOperation(operation: String, arg: AsmNode): AsmNode = + override fun unaryOperation(operation: String, arg: AsmExpression): AsmExpression = super.unaryOperation(operation, arg) - override fun binaryOperation(operation: String, left: AsmNode, right: AsmNode): AsmNode = + override fun binaryOperation(operation: String, left: AsmExpression, right: AsmExpression): AsmExpression = super.binaryOperation(operation, left, right) } /** - * A context class for [AsmNode] construction for [Ring] algebras. + * A context class for [AsmExpression] construction for [Ring] algebras. */ -open class AsmExpressionRing(override val algebra: A) : AsmExpressionSpace(algebra), - Ring> where A : Ring, A : NumericAlgebra { - override val one: AsmNode - get() = const(algebra.one) +open class AsmExpressionRing(type: KClass, algebra: A) : AsmExpressionSpace(type, algebra), + Ring> where A : Ring, A : NumericAlgebra { + override val one: AsmExpression get() = const(algebra.one) /** * Builds an AsmExpression of multiplication of two expressions. */ - override fun multiply(a: AsmNode, b: AsmNode): AsmNode = - AsmBinaryOperation(algebra, RingOperations.TIMES_OPERATION, a, b) + override fun multiply(a: AsmExpression, b: AsmExpression): AsmExpression = + AsmBinaryOperation(type, algebra, RingOperations.TIMES_OPERATION, a, b) - operator fun AsmNode.times(arg: T): AsmNode = this * const(arg) - operator fun T.times(arg: AsmNode): AsmNode = arg * this + operator fun AsmExpression.times(arg: T): AsmExpression = this * const(arg) + operator fun T.times(arg: AsmExpression): AsmExpression = arg * this - override fun unaryOperation(operation: String, arg: AsmNode): AsmNode = + override fun unaryOperation(operation: String, arg: AsmExpression): AsmExpression = super.unaryOperation(operation, arg) - override fun binaryOperation(operation: String, left: AsmNode, right: AsmNode): AsmNode = + override fun binaryOperation(operation: String, left: AsmExpression, right: AsmExpression): AsmExpression = super.binaryOperation(operation, left, right) - override fun number(value: Number): AsmNode = super.number(value) + override fun number(value: Number): AsmExpression = super.number(value) } /** - * A context class for [AsmNode] construction for [Field] algebras. + * A context class for [AsmExpression] construction for [Field] algebras. */ -open class AsmExpressionField(override val algebra: A) : - AsmExpressionRing(algebra), - Field> where A : Field, A : NumericAlgebra { +open class AsmExpressionField(type: KClass, algebra: A) : + AsmExpressionRing(type, algebra), + Field> where A : Field, A : NumericAlgebra { /** * Builds an AsmExpression of division an expression by another one. */ - override fun divide(a: AsmNode, b: AsmNode): AsmNode = - AsmBinaryOperation(algebra, FieldOperations.DIV_OPERATION, a, b) + override fun divide(a: AsmExpression, b: AsmExpression): AsmExpression = + AsmBinaryOperation(type, algebra, FieldOperations.DIV_OPERATION, a, b) - operator fun AsmNode.div(arg: T): AsmNode = this / const(arg) - operator fun T.div(arg: AsmNode): AsmNode = arg / this + operator fun AsmExpression.div(arg: T): AsmExpression = this / const(arg) + operator fun T.div(arg: AsmExpression): AsmExpression = arg / this - override fun unaryOperation(operation: String, arg: AsmNode): AsmNode = + override fun unaryOperation(operation: String, arg: AsmExpression): AsmExpression = super.unaryOperation(operation, arg) - override fun binaryOperation(operation: String, left: AsmNode, right: AsmNode): AsmNode = + override fun binaryOperation(operation: String, left: AsmExpression, right: AsmExpression): AsmExpression = super.binaryOperation(operation, left, right) - override fun number(value: Number): AsmNode = super.number(value) + override fun number(value: Number): AsmExpression = super.number(value) } diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/asm.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/asm.kt new file mode 100644 index 000000000..cc3e36e94 --- /dev/null +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/asm.kt @@ -0,0 +1,47 @@ +package scientifik.kmath.asm + +import scientifik.kmath.ast.MST +import scientifik.kmath.ast.evaluate +import scientifik.kmath.expressions.Expression +import scientifik.kmath.operations.Field +import scientifik.kmath.operations.NumericAlgebra +import scientifik.kmath.operations.Ring +import scientifik.kmath.operations.Space + +internal fun buildName(expression: AsmExpression<*>, collision: Int = 0): String { + val name = "scientifik.kmath.expressions.generated.AsmCompiledExpression_${expression.hashCode()}_$collision" + + try { + Class.forName(name) + } catch (ignored: ClassNotFoundException) { + return name + } + + return buildName(expression, collision + 1) +} + +inline fun , E : AsmExpressionAlgebra> A.asm( + expressionAlgebra: E, + block: E.() -> AsmExpression +): Expression = expressionAlgebra.block() + +inline fun NumericAlgebra.asm(ast: MST): Expression = + AsmExpressionAlgebra(T::class, this).evaluate(ast) + +inline fun A.asmSpace(block: AsmExpressionSpace.() -> AsmExpression): Expression where A : NumericAlgebra, A : Space = + AsmExpressionSpace(T::class, this).block() + +inline fun A.asmSpace(ast: MST): Expression where A : NumericAlgebra, A : Space = + asmSpace { evaluate(ast) } + +inline fun A.asmRing(block: AsmExpressionRing.() -> AsmExpression): Expression where A : NumericAlgebra, A : Ring = + AsmExpressionRing(T::class, this).block() + +inline fun A.asmRing(ast: MST): Expression where A : NumericAlgebra, A : Ring = + asmRing { evaluate(ast) } + +inline fun A.asmField(block: AsmExpressionField.() -> AsmExpression): Expression where A : NumericAlgebra, A : Field = + AsmExpressionField(T::class, this).block() + +inline fun A.asmField(ast: MST): Expression where A : NumericAlgebra, A : Field = + asmRing { evaluate(ast) } diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmGenerationContext.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt similarity index 82% rename from kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmGenerationContext.kt rename to kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt index fa0f06579..eefde66e4 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmGenerationContext.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt @@ -5,30 +5,29 @@ import org.objectweb.asm.Label import org.objectweb.asm.MethodVisitor import org.objectweb.asm.Opcodes import scientifik.kmath.asm.FunctionalCompiledExpression -import scientifik.kmath.asm.internal.AsmGenerationContext.ClassLoader +import scientifik.kmath.asm.internal.AsmBuilder.AsmClassLoader import scientifik.kmath.operations.Algebra /** * AsmGenerationContext is a structure that abstracts building a class that unwraps [AsmNode] to plain Java - * expression. This class uses [ClassLoader] for loading the generated class, then it is able to instantiate the new + * expression. This class uses [AsmClassLoader] for loading the generated class, then it is able to instantiate the new * class. * * @param T the type of AsmExpression to unwrap. * @param algebra the algebra the applied AsmExpressions use. * @param className the unique class name of new loaded class. */ -@PublishedApi -internal class AsmGenerationContext @PublishedApi internal constructor( +internal class AsmBuilder( private val classOfT: Class<*>, private val algebra: Algebra, private val className: String ) { - private class ClassLoader(parent: java.lang.ClassLoader) : java.lang.ClassLoader(parent) { + private class AsmClassLoader(parent: ClassLoader) : ClassLoader(parent) { internal fun defineClass(name: String?, b: ByteArray): Class<*> = defineClass(name, b, 0, b.size) } - private val classLoader: ClassLoader = - ClassLoader(javaClass.classLoader) + private val classLoader: AsmClassLoader = + AsmClassLoader(javaClass.classLoader) @Suppress("PrivatePropertyName") private val T_ALGEBRA_CLASS: String = algebra.javaClass.name.replace(oldChar = '.', newChar = '/') @@ -113,9 +112,8 @@ internal class AsmGenerationContext @PublishedApi internal constructor( } } - @PublishedApi @Suppress("UNCHECKED_CAST") - internal fun generate(): FunctionalCompiledExpression { + fun generate(): FunctionalCompiledExpression { generatedInstance?.let { return it } invokeMethodVisitor.run { @@ -188,7 +186,7 @@ internal class AsmGenerationContext @PublishedApi internal constructor( return new } - internal fun visitLoadFromConstants(value: T) { + fun visitLoadFromConstants(value: T) { if (classOfT in INLINABLE_NUMBERS) { visitNumberConstant(value as Number) visitCastToT() @@ -213,7 +211,7 @@ internal class AsmGenerationContext @PublishedApi internal constructor( private fun visitLoadThis(): Unit = invokeMethodVisitor.visitVarInsn(Opcodes.ALOAD, invokeThisVar) - internal fun visitNumberConstant(value: Number) { + fun visitNumberConstant(value: Number) { maxStack++ val clazz = value.javaClass val c = clazz.name.replace('.', '/') @@ -234,7 +232,7 @@ internal class AsmGenerationContext @PublishedApi internal constructor( visitLoadAnyFromConstants(value, c) } - internal fun visitLoadFromVariables(name: String, defaultValue: T? = null): Unit = invokeMethodVisitor.run { + fun visitLoadFromVariables(name: String, defaultValue: T? = null): Unit = invokeMethodVisitor.run { maxStack += 2 visitVarInsn(Opcodes.ALOAD, invokeArgumentsVar) @@ -262,7 +260,7 @@ internal class AsmGenerationContext @PublishedApi internal constructor( visitCastToT() } - internal fun visitLoadAlgebra() { + fun visitLoadAlgebra() { maxStack++ invokeMethodVisitor.visitVarInsn(Opcodes.ALOAD, invokeThisVar) @@ -274,7 +272,7 @@ internal class AsmGenerationContext @PublishedApi internal constructor( invokeMethodVisitor.visitTypeInsn(Opcodes.CHECKCAST, T_ALGEBRA_CLASS) } - internal fun visitAlgebraOperation( + fun visitAlgebraOperation( owner: String, method: String, descriptor: String, @@ -288,32 +286,28 @@ internal class AsmGenerationContext @PublishedApi internal constructor( private fun visitCastToT(): Unit = invokeMethodVisitor.visitTypeInsn(Opcodes.CHECKCAST, T_CLASS) - internal fun visitStringConstant(string: String) { + fun visitStringConstant(string: String) { invokeMethodVisitor.visitLdcInsn(string) } - internal companion object { - private val SIGNATURE_LETTERS by lazy { - mapOf( - java.lang.Byte::class.java to "B", - java.lang.Short::class.java to "S", - java.lang.Integer::class.java to "I", - java.lang.Long::class.java to "J", - java.lang.Float::class.java to "F", - java.lang.Double::class.java to "D" - ) - } + companion object { + private val SIGNATURE_LETTERS = mapOf( + java.lang.Byte::class.java to "B", + java.lang.Short::class.java to "S", + java.lang.Integer::class.java to "I", + java.lang.Long::class.java to "J", + java.lang.Float::class.java to "F", + java.lang.Double::class.java to "D" + ) - private val INLINABLE_NUMBERS by lazy { SIGNATURE_LETTERS.keys } + private val INLINABLE_NUMBERS = SIGNATURE_LETTERS.keys - internal const val FUNCTIONAL_COMPILED_EXPRESSION_CLASS = - "scientifik/kmath/asm/FunctionalCompiledExpression" - - internal const val MAP_CLASS = "java/util/Map" - internal const val OBJECT_CLASS = "java/lang/Object" - internal const val ALGEBRA_CLASS = "scientifik/kmath/operations/Algebra" - internal const val SPACE_OPERATIONS_CLASS = "scientifik/kmath/operations/SpaceOperations" - internal const val STRING_CLASS = "java/lang/String" - internal const val NUMBER_CLASS = "java/lang/Number" + const val FUNCTIONAL_COMPILED_EXPRESSION_CLASS = "scientifik/kmath/asm/FunctionalCompiledExpression" + const val MAP_CLASS = "java/util/Map" + const val OBJECT_CLASS = "java/lang/Object" + const val ALGEBRA_CLASS = "scientifik/kmath/operations/Algebra" + const val SPACE_OPERATIONS_CLASS = "scientifik/kmath/operations/SpaceOperations" + const val STRING_CLASS = "java/lang/String" + const val NUMBER_CLASS = "java/lang/Number" } } diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/MethodVisitors.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/methodVisitors.kt similarity index 100% rename from kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/MethodVisitors.kt rename to kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/methodVisitors.kt diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/Optimization.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/optimization.kt similarity index 72% rename from kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/Optimization.kt rename to kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/optimization.kt index 815f292ce..d9f950ceb 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/Optimization.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/optimization.kt @@ -2,7 +2,7 @@ package scientifik.kmath.asm.internal import org.objectweb.asm.Opcodes import scientifik.kmath.asm.AsmConstantExpression -import scientifik.kmath.asm.AsmNode +import scientifik.kmath.asm.AsmExpression import scientifik.kmath.operations.Algebra private val methodNameAdapters: Map = mapOf("+" to "add", "*" to "multiply", "/" to "divide") @@ -16,7 +16,7 @@ internal fun hasSpecific(context: Algebra, name: String, arity: Int): Boo return true } -internal fun AsmGenerationContext.tryInvokeSpecific(context: Algebra, name: String, arity: Int): Boolean { +internal fun AsmBuilder.tryInvokeSpecific(context: Algebra, name: String, arity: Int): Boolean { val aName = methodNameAdapters[name] ?: name context::class.java.methods.find { it.name == aName && it.parameters.size == arity } @@ -26,9 +26,9 @@ internal fun AsmGenerationContext.tryInvokeSpecific(context: Algebra, val sig = buildString { append('(') - repeat(arity) { append("L${AsmGenerationContext.OBJECT_CLASS};") } + repeat(arity) { append("L${AsmBuilder.OBJECT_CLASS};") } append(')') - append("L${AsmGenerationContext.OBJECT_CLASS};") + append("L${AsmBuilder.OBJECT_CLASS};") } visitAlgebraOperation( @@ -42,8 +42,7 @@ internal fun AsmGenerationContext.tryInvokeSpecific(context: Algebra, return true } -@PublishedApi -internal fun AsmNode.optimize(): AsmNode { +internal fun AsmExpression.optimize(): AsmExpression { val a = tryEvaluate() - return if (a == null) this else AsmConstantExpression(a) + return if (a == null) this else AsmConstantExpression(type, algebra, a) } diff --git a/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/asm/TestAsmAlgebras.kt b/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/asm/TestAsmAlgebras.kt similarity index 98% rename from kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/asm/TestAsmAlgebras.kt rename to kmath-ast/src/jvmTest/kotlin/scietifik/kmath/asm/TestAsmAlgebras.kt index 07f895c40..06f776597 100644 --- a/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/asm/TestAsmAlgebras.kt +++ b/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/asm/TestAsmAlgebras.kt @@ -1,4 +1,4 @@ -package scietifik.kmath.ast.asm +package scietifik.kmath.asm import scientifik.kmath.asm.asmField import scientifik.kmath.asm.asmRing diff --git a/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/asm/TestAsmExpressions.kt b/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/asm/TestAsmExpressions.kt similarity index 94% rename from kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/asm/TestAsmExpressions.kt rename to kmath-ast/src/jvmTest/kotlin/scietifik/kmath/asm/TestAsmExpressions.kt index 2839b8a25..40d990537 100644 --- a/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/asm/TestAsmExpressions.kt +++ b/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/asm/TestAsmExpressions.kt @@ -1,4 +1,4 @@ -package scietifik.kmath.ast.asm +package scietifik.kmath.asm import scientifik.kmath.asm.asmField import scientifik.kmath.expressions.invoke diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressions.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressionAlgebra.kt similarity index 100% rename from kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressions.kt rename to kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressionAlgebra.kt diff --git a/kmath-core/src/commonTest/kotlin/scientifik/kmath/expressions/ExpressionFieldTest.kt b/kmath-core/src/commonTest/kotlin/scientifik/kmath/expressions/ExpressionFieldTest.kt index b933f6a17..9eae60efc 100644 --- a/kmath-core/src/commonTest/kotlin/scientifik/kmath/expressions/ExpressionFieldTest.kt +++ b/kmath-core/src/commonTest/kotlin/scientifik/kmath/expressions/ExpressionFieldTest.kt @@ -31,7 +31,7 @@ class ExpressionFieldTest { @Test fun separateContext() { - fun FunctionalExpressionField.expression(): Expression { + fun FunctionalExpressionField.expression(): Expression { val x = variable("x") return x * x + 2 * x + one } @@ -42,7 +42,7 @@ class ExpressionFieldTest { @Test fun valueExpression() { - val expressionBuilder: FunctionalExpressionField.() -> Expression = { + val expressionBuilder: FunctionalExpressionField.() -> Expression = { val x = variable("x") x * x + 2 * x + one } -- 2.34.1 From 521ea8bddcb906bd43b9d1ac82712f2fcb305a19 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Mon, 15 Jun 2020 17:36:30 +0700 Subject: [PATCH 068/156] Rename ClassWriters.kt to be consistent with local code style, rename AsmBuilders.kt to asm.kt, rename AsmNode back to AsmExpression, rename AsmGenerator to AsmBuilder --- .../scientifik/kmath/asm/AsmBuilders.kt | 52 ------ .../scientifik/kmath/asm/AsmExpressions.kt | 148 +++++++++--------- .../kotlin/scientifik/kmath/asm/asm.kt | 40 +++-- .../{AsmGenerator.kt => AsmBuilder.kt} | 12 +- .../{ClassWriters.kt => classWriters.kt} | 0 .../kmath/asm/internal/optimization.kt | 10 +- 6 files changed, 108 insertions(+), 154 deletions(-) delete mode 100644 kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmBuilders.kt rename kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/{AsmGenerator.kt => AsmBuilder.kt} (96%) rename kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/{ClassWriters.kt => classWriters.kt} (100%) diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmBuilders.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmBuilders.kt deleted file mode 100644 index 223278756..000000000 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmBuilders.kt +++ /dev/null @@ -1,52 +0,0 @@ -package scientifik.kmath.asm - -import scientifik.kmath.asm.internal.AsmGenerator -import scientifik.kmath.ast.MST -import scientifik.kmath.ast.evaluate -import scientifik.kmath.expressions.Expression -import scientifik.kmath.operations.* - -@PublishedApi -internal fun buildName(expression: AsmNode<*>, collision: Int = 0): String { - val name = "scientifik.kmath.expressions.generated.AsmCompiledExpression_${expression.hashCode()}_$collision" - - try { - Class.forName(name) - } catch (ignored: ClassNotFoundException) { - return name - } - - return buildName(expression, collision + 1) -} - -@PublishedApi -internal inline fun AsmNode.compile(algebra: Algebra): Expression = - AsmGenerator(T::class.java, algebra, buildName(this), this).getInstance() - -inline fun , E : AsmExpressionAlgebra> A.asm( - expressionAlgebra: E, - block: E.() -> AsmNode -): Expression = expressionAlgebra.block().compile(expressionAlgebra.algebra) - -inline fun , E : AsmExpressionAlgebra> A.asm( - expressionAlgebra: E, - ast: MST -): Expression = asm(expressionAlgebra) { evaluate(ast) } - -inline fun A.asmSpace(block: AsmExpressionSpace.() -> AsmNode): Expression where A : NumericAlgebra, A : Space = - AsmExpressionSpace(this).let { it.block().compile(it.algebra) } - -inline fun A.asmSpace(ast: MST): Expression where A : NumericAlgebra, A : Space = - asmSpace { evaluate(ast) } - -inline fun A.asmRing(block: AsmExpressionRing.() -> AsmNode): Expression where A : NumericAlgebra, A : Ring = - AsmExpressionRing(this).let { it.block().compile(it.algebra) } - -inline fun A.asmRing(ast: MST): Expression where A : NumericAlgebra, A : Ring = - asmRing { evaluate(ast) } - -inline fun A.asmField(block: AsmExpressionField.() -> AsmNode): Expression where A : NumericAlgebra, A : Field = - AsmExpressionField(this).let { it.block().compile(it.algebra) } - -inline fun A.asmField(ast: MST): Expression where A : NumericAlgebra, A : Field = - asmRing { evaluate(ast) } diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmExpressions.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmExpressions.kt index 72a53d899..55d263117 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmExpressions.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmExpressions.kt @@ -1,6 +1,6 @@ package scientifik.kmath.asm -import scientifik.kmath.asm.internal.AsmGenerator +import scientifik.kmath.asm.internal.AsmBuilder import scientifik.kmath.asm.internal.hasSpecific import scientifik.kmath.asm.internal.optimize import scientifik.kmath.asm.internal.tryInvokeSpecific @@ -9,11 +9,11 @@ import scientifik.kmath.expressions.ExpressionAlgebra import scientifik.kmath.operations.* /** - * A function declaration that could be compiled to [AsmGenerator]. + * A function declaration that could be compiled to [AsmBuilder]. * * @param T the type the stored function returns. */ -abstract class AsmNode internal constructor() { +abstract class AsmExpression internal constructor() { /** * Tries to evaluate this function without its variables. This method is intended for optimization. * @@ -24,18 +24,18 @@ abstract class AsmNode internal constructor() { /** * Compiles this declaration. * - * @param gen the target [AsmGenerator]. + * @param gen the target [AsmBuilder]. */ @PublishedApi - internal abstract fun compile(gen: AsmGenerator) + internal abstract fun compile(gen: AsmBuilder) } -internal class AsmUnaryOperation(private val context: Algebra, private val name: String, expr: AsmNode) : - AsmNode() { - private val expr: AsmNode = expr.optimize() +internal class AsmUnaryOperation(private val context: Algebra, private val name: String, expr: AsmExpression) : + AsmExpression() { + private val expr: AsmExpression = expr.optimize() override fun tryEvaluate(): T? = context { unaryOperation(name, expr.tryEvaluate() ?: return@context null) } - override fun compile(gen: AsmGenerator) { + override fun compile(gen: AsmBuilder) { gen.loadAlgebra() if (!hasSpecific(context, name, 1)) @@ -47,11 +47,11 @@ internal class AsmUnaryOperation(private val context: Algebra, private val return gen.invokeAlgebraOperation( - owner = AsmGenerator.ALGEBRA_CLASS, + owner = AsmBuilder.ALGEBRA_CLASS, method = "unaryOperation", - descriptor = "(L${AsmGenerator.STRING_CLASS};" + - "L${AsmGenerator.OBJECT_CLASS};)" + - "L${AsmGenerator.OBJECT_CLASS};" + descriptor = "(L${AsmBuilder.STRING_CLASS};" + + "L${AsmBuilder.OBJECT_CLASS};)" + + "L${AsmBuilder.OBJECT_CLASS};" ) } } @@ -59,11 +59,11 @@ internal class AsmUnaryOperation(private val context: Algebra, private val internal class AsmBinaryOperation( private val context: Algebra, private val name: String, - first: AsmNode, - second: AsmNode -) : AsmNode() { - private val first: AsmNode = first.optimize() - private val second: AsmNode = second.optimize() + first: AsmExpression, + second: AsmExpression +) : AsmExpression() { + private val first: AsmExpression = first.optimize() + private val second: AsmExpression = second.optimize() override fun tryEvaluate(): T? = context { binaryOperation( @@ -73,7 +73,7 @@ internal class AsmBinaryOperation( ) } - override fun compile(gen: AsmGenerator) { + override fun compile(gen: AsmBuilder) { gen.loadAlgebra() if (!hasSpecific(context, name, 2)) @@ -86,56 +86,56 @@ internal class AsmBinaryOperation( return gen.invokeAlgebraOperation( - owner = AsmGenerator.ALGEBRA_CLASS, + owner = AsmBuilder.ALGEBRA_CLASS, method = "binaryOperation", - descriptor = "(L${AsmGenerator.STRING_CLASS};" + - "L${AsmGenerator.OBJECT_CLASS};" + - "L${AsmGenerator.OBJECT_CLASS};)" + - "L${AsmGenerator.OBJECT_CLASS};" + descriptor = "(L${AsmBuilder.STRING_CLASS};" + + "L${AsmBuilder.OBJECT_CLASS};" + + "L${AsmBuilder.OBJECT_CLASS};)" + + "L${AsmBuilder.OBJECT_CLASS};" ) } } internal class AsmVariableExpression(private val name: String, private val default: T? = null) : - AsmNode() { - override fun compile(gen: AsmGenerator): Unit = gen.loadVariable(name, default) + AsmExpression() { + override fun compile(gen: AsmBuilder): Unit = gen.loadVariable(name, default) } internal class AsmConstantExpression(private val value: T) : - AsmNode() { + AsmExpression() { override fun tryEvaluate(): T = value - override fun compile(gen: AsmGenerator): Unit = gen.loadTConstant(value) + override fun compile(gen: AsmBuilder): Unit = gen.loadTConstant(value) } internal class AsmConstProductExpression( private val context: Space, - expr: AsmNode, + expr: AsmExpression, private val const: Number -) : AsmNode() { - private val expr: AsmNode = expr.optimize() +) : AsmExpression() { + private val expr: AsmExpression = expr.optimize() override fun tryEvaluate(): T? = context { (expr.tryEvaluate() ?: return@context null) * const } - override fun compile(gen: AsmGenerator) { + override fun compile(gen: AsmBuilder) { gen.loadAlgebra() gen.loadNumberConstant(const) expr.compile(gen) gen.invokeAlgebraOperation( - owner = AsmGenerator.SPACE_OPERATIONS_CLASS, + owner = AsmBuilder.SPACE_OPERATIONS_CLASS, method = "multiply", - descriptor = "(L${AsmGenerator.OBJECT_CLASS};" + - "L${AsmGenerator.NUMBER_CLASS};)" + - "L${AsmGenerator.OBJECT_CLASS};" + descriptor = "(L${AsmBuilder.OBJECT_CLASS};" + + "L${AsmBuilder.NUMBER_CLASS};)" + + "L${AsmBuilder.OBJECT_CLASS};" ) } } internal class AsmNumberExpression(private val context: NumericAlgebra, private val value: Number) : - AsmNode() { + AsmExpression() { override fun tryEvaluate(): T? = context.number(value) - override fun compile(gen: AsmGenerator): Unit = gen.loadNumberConstant(value) + override fun compile(gen: AsmBuilder): Unit = gen.loadNumberConstant(value) } internal abstract class FunctionalCompiledExpression internal constructor( @@ -146,10 +146,10 @@ internal abstract class FunctionalCompiledExpression internal constructor( } /** - * A context class for [AsmNode] construction. + * A context class for [AsmExpression] construction. */ -interface AsmExpressionAlgebra> : NumericAlgebra>, - ExpressionAlgebra> { +interface AsmExpressionAlgebra> : NumericAlgebra>, + ExpressionAlgebra> { /** * The algebra to provide for AsmExpressions built. */ @@ -158,108 +158,108 @@ interface AsmExpressionAlgebra> : NumericAlgebra = AsmNumberExpression(algebra, value) + override fun number(value: Number): AsmExpression = AsmNumberExpression(algebra, value) /** * Builds an AsmExpression of constant expression which does not depend on arguments. */ - override fun const(value: T): AsmNode = AsmConstantExpression(value) + override fun const(value: T): AsmExpression = AsmConstantExpression(value) /** * Builds an AsmExpression to access a variable. */ - override fun variable(name: String, default: T?): AsmNode = AsmVariableExpression(name, default) + override fun variable(name: String, default: T?): AsmExpression = AsmVariableExpression(name, default) /** * Builds an AsmExpression of dynamic call of binary operation [operation] on [left] and [right]. */ - override fun binaryOperation(operation: String, left: AsmNode, right: AsmNode): AsmNode = + override fun binaryOperation(operation: String, left: AsmExpression, right: AsmExpression): AsmExpression = AsmBinaryOperation(algebra, operation, left, right) /** * Builds an AsmExpression of dynamic call of unary operation with name [operation] on [arg]. */ - override fun unaryOperation(operation: String, arg: AsmNode): AsmNode = + override fun unaryOperation(operation: String, arg: AsmExpression): AsmExpression = AsmUnaryOperation(algebra, operation, arg) } /** - * A context class for [AsmNode] construction for [Space] algebras. + * A context class for [AsmExpression] construction for [Space] algebras. */ open class AsmExpressionSpace(override val algebra: A) : AsmExpressionAlgebra, - Space> where A : Space, A : NumericAlgebra { - override val zero: AsmNode + Space> where A : Space, A : NumericAlgebra { + override val zero: AsmExpression get() = const(algebra.zero) /** * Builds an AsmExpression of addition of two another expressions. */ - override fun add(a: AsmNode, b: AsmNode): AsmNode = + override fun add(a: AsmExpression, b: AsmExpression): AsmExpression = AsmBinaryOperation(algebra, SpaceOperations.PLUS_OPERATION, a, b) /** * Builds an AsmExpression of multiplication of expression by number. */ - override fun multiply(a: AsmNode, k: Number): AsmNode = AsmConstProductExpression(algebra, a, k) + override fun multiply(a: AsmExpression, k: Number): AsmExpression = AsmConstProductExpression(algebra, a, k) - operator fun AsmNode.plus(arg: T): AsmNode = this + const(arg) - operator fun AsmNode.minus(arg: T): AsmNode = this - const(arg) - operator fun T.plus(arg: AsmNode): AsmNode = arg + this - operator fun T.minus(arg: AsmNode): AsmNode = arg - this + operator fun AsmExpression.plus(arg: T): AsmExpression = this + const(arg) + operator fun AsmExpression.minus(arg: T): AsmExpression = this - const(arg) + operator fun T.plus(arg: AsmExpression): AsmExpression = arg + this + operator fun T.minus(arg: AsmExpression): AsmExpression = arg - this - override fun unaryOperation(operation: String, arg: AsmNode): AsmNode = + override fun unaryOperation(operation: String, arg: AsmExpression): AsmExpression = super.unaryOperation(operation, arg) - override fun binaryOperation(operation: String, left: AsmNode, right: AsmNode): AsmNode = + override fun binaryOperation(operation: String, left: AsmExpression, right: AsmExpression): AsmExpression = super.binaryOperation(operation, left, right) } /** - * A context class for [AsmNode] construction for [Ring] algebras. + * A context class for [AsmExpression] construction for [Ring] algebras. */ open class AsmExpressionRing(override val algebra: A) : AsmExpressionSpace(algebra), - Ring> where A : Ring, A : NumericAlgebra { - override val one: AsmNode + Ring> where A : Ring, A : NumericAlgebra { + override val one: AsmExpression get() = const(algebra.one) /** * Builds an AsmExpression of multiplication of two expressions. */ - override fun multiply(a: AsmNode, b: AsmNode): AsmNode = + override fun multiply(a: AsmExpression, b: AsmExpression): AsmExpression = AsmBinaryOperation(algebra, RingOperations.TIMES_OPERATION, a, b) - operator fun AsmNode.times(arg: T): AsmNode = this * const(arg) - operator fun T.times(arg: AsmNode): AsmNode = arg * this + operator fun AsmExpression.times(arg: T): AsmExpression = this * const(arg) + operator fun T.times(arg: AsmExpression): AsmExpression = arg * this - override fun unaryOperation(operation: String, arg: AsmNode): AsmNode = + override fun unaryOperation(operation: String, arg: AsmExpression): AsmExpression = super.unaryOperation(operation, arg) - override fun binaryOperation(operation: String, left: AsmNode, right: AsmNode): AsmNode = + override fun binaryOperation(operation: String, left: AsmExpression, right: AsmExpression): AsmExpression = super.binaryOperation(operation, left, right) - override fun number(value: Number): AsmNode = super.number(value) + override fun number(value: Number): AsmExpression = super.number(value) } /** - * A context class for [AsmNode] construction for [Field] algebras. + * A context class for [AsmExpression] construction for [Field] algebras. */ open class AsmExpressionField(override val algebra: A) : AsmExpressionRing(algebra), - Field> where A : Field, A : NumericAlgebra { + Field> where A : Field, A : NumericAlgebra { /** * Builds an AsmExpression of division an expression by another one. */ - override fun divide(a: AsmNode, b: AsmNode): AsmNode = + override fun divide(a: AsmExpression, b: AsmExpression): AsmExpression = AsmBinaryOperation(algebra, FieldOperations.DIV_OPERATION, a, b) - operator fun AsmNode.div(arg: T): AsmNode = this / const(arg) - operator fun T.div(arg: AsmNode): AsmNode = arg / this + operator fun AsmExpression.div(arg: T): AsmExpression = this / const(arg) + operator fun T.div(arg: AsmExpression): AsmExpression = arg / this - override fun unaryOperation(operation: String, arg: AsmNode): AsmNode = + override fun unaryOperation(operation: String, arg: AsmExpression): AsmExpression = super.unaryOperation(operation, arg) - override fun binaryOperation(operation: String, left: AsmNode, right: AsmNode): AsmNode = + override fun binaryOperation(operation: String, left: AsmExpression, right: AsmExpression): AsmExpression = super.binaryOperation(operation, left, right) - override fun number(value: Number): AsmNode = super.number(value) + override fun number(value: Number): AsmExpression = super.number(value) } diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/asm.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/asm.kt index cc3e36e94..a2bbb254c 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/asm.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/asm.kt @@ -1,13 +1,12 @@ package scientifik.kmath.asm +import scientifik.kmath.asm.internal.AsmBuilder import scientifik.kmath.ast.MST import scientifik.kmath.ast.evaluate import scientifik.kmath.expressions.Expression -import scientifik.kmath.operations.Field -import scientifik.kmath.operations.NumericAlgebra -import scientifik.kmath.operations.Ring -import scientifik.kmath.operations.Space +import scientifik.kmath.operations.* +@PublishedApi internal fun buildName(expression: AsmExpression<*>, collision: Int = 0): String { val name = "scientifik.kmath.expressions.generated.AsmCompiledExpression_${expression.hashCode()}_$collision" @@ -20,28 +19,35 @@ internal fun buildName(expression: AsmExpression<*>, collision: Int = 0): String return buildName(expression, collision + 1) } -inline fun , E : AsmExpressionAlgebra> A.asm( +@PublishedApi +internal inline fun AsmExpression.compile(algebra: Algebra): Expression = + AsmBuilder(T::class.java, algebra, buildName(this), this).getInstance() + +inline fun , E : AsmExpressionAlgebra> A.asm( expressionAlgebra: E, block: E.() -> AsmExpression -): Expression = expressionAlgebra.block() +): Expression = expressionAlgebra.block().compile(expressionAlgebra.algebra) -inline fun NumericAlgebra.asm(ast: MST): Expression = - AsmExpressionAlgebra(T::class, this).evaluate(ast) +inline fun , E : AsmExpressionAlgebra> A.asm( + expressionAlgebra: E, + ast: MST +): Expression = asm(expressionAlgebra) { evaluate(ast) } -inline fun A.asmSpace(block: AsmExpressionSpace.() -> AsmExpression): Expression where A : NumericAlgebra, A : Space = - AsmExpressionSpace(T::class, this).block() +inline fun A.asmSpace(block: AsmExpressionSpace.() -> AsmExpression): Expression where A : NumericAlgebra, A : Space = + AsmExpressionSpace(this).let { it.block().compile(it.algebra) } -inline fun A.asmSpace(ast: MST): Expression where A : NumericAlgebra, A : Space = +inline fun A.asmSpace(ast: MST): Expression where A : NumericAlgebra, A : Space = asmSpace { evaluate(ast) } -inline fun A.asmRing(block: AsmExpressionRing.() -> AsmExpression): Expression where A : NumericAlgebra, A : Ring = - AsmExpressionRing(T::class, this).block() +inline fun A.asmRing(block: AsmExpressionRing.() -> AsmExpression): Expression where A : NumericAlgebra, A : Ring = + AsmExpressionRing(this).let { it.block().compile(it.algebra) } -inline fun A.asmRing(ast: MST): Expression where A : NumericAlgebra, A : Ring = +inline fun A.asmRing(ast: MST): Expression where A : NumericAlgebra, A : Ring = asmRing { evaluate(ast) } -inline fun A.asmField(block: AsmExpressionField.() -> AsmExpression): Expression where A : NumericAlgebra, A : Field = - AsmExpressionField(T::class, this).block() +inline fun A.asmField(block: AsmExpressionField.() -> AsmExpression): Expression where A : NumericAlgebra, A : Field = + AsmExpressionField(this).let { it.block().compile(it.algebra) } -inline fun A.asmField(ast: MST): Expression where A : NumericAlgebra, A : Field = +inline fun A.asmField(ast: MST): Expression where A : NumericAlgebra, A : Field = asmRing { evaluate(ast) } + diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmGenerator.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt similarity index 96% rename from kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmGenerator.kt rename to kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt index 0c6975cd9..720d44b45 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmGenerator.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt @@ -4,13 +4,13 @@ import org.objectweb.asm.ClassWriter import org.objectweb.asm.Label import org.objectweb.asm.MethodVisitor import org.objectweb.asm.Opcodes -import scientifik.kmath.asm.AsmNode +import scientifik.kmath.asm.AsmExpression import scientifik.kmath.asm.FunctionalCompiledExpression -import scientifik.kmath.asm.internal.AsmGenerator.ClassLoader +import scientifik.kmath.asm.internal.AsmBuilder.ClassLoader import scientifik.kmath.operations.Algebra /** - * ASM Generator is a structure that abstracts building a class that unwraps [AsmNode] to plain Java expression. + * ASM Builder is a structure that abstracts building a class that unwraps [AsmExpression] to plain Java expression. * This class uses [ClassLoader] for loading the generated class, then it is able to instantiate the new class. * * @param T the type of AsmExpression to unwrap. @@ -18,11 +18,11 @@ import scientifik.kmath.operations.Algebra * @param className the unique class name of new loaded class. */ @PublishedApi -internal class AsmGenerator @PublishedApi internal constructor( +internal class AsmBuilder @PublishedApi internal constructor( private val classOfT: Class<*>, private val algebra: Algebra, private val className: String, - private val root: AsmNode + private val root: AsmExpression ) { private class ClassLoader(parent: java.lang.ClassLoader) : java.lang.ClassLoader(parent) { internal fun defineClass(name: String?, b: ByteArray): Class<*> = defineClass(name, b, 0, b.size) @@ -112,7 +112,7 @@ internal class AsmGenerator @PublishedApi internal constructor( visitCode() val l0 = Label() visitLabel(l0) - root.compile(this@AsmGenerator) + root.compile(this@AsmBuilder) visitReturnObject() val l1 = Label() visitLabel(l1) diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/ClassWriters.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/classWriters.kt similarity index 100% rename from kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/ClassWriters.kt rename to kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/classWriters.kt diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/optimization.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/optimization.kt index 3b37f81e5..2fb23d137 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/optimization.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/optimization.kt @@ -2,7 +2,7 @@ package scientifik.kmath.asm.internal import org.objectweb.asm.Opcodes import scientifik.kmath.asm.AsmConstantExpression -import scientifik.kmath.asm.AsmNode +import scientifik.kmath.asm.AsmExpression import scientifik.kmath.operations.Algebra private val methodNameAdapters: Map = mapOf("+" to "add", "*" to "multiply", "/" to "divide") @@ -16,7 +16,7 @@ internal fun hasSpecific(context: Algebra, name: String, arity: Int): Boo return true } -internal fun AsmGenerator.tryInvokeSpecific(context: Algebra, name: String, arity: Int): Boolean { +internal fun AsmBuilder.tryInvokeSpecific(context: Algebra, name: String, arity: Int): Boolean { val aName = methodNameAdapters[name] ?: name context::class.java.methods.find { it.name == aName && it.parameters.size == arity } @@ -26,9 +26,9 @@ internal fun AsmGenerator.tryInvokeSpecific(context: Algebra, name: St val sig = buildString { append('(') - repeat(arity) { append("L${AsmGenerator.OBJECT_CLASS};") } + repeat(arity) { append("L${AsmBuilder.OBJECT_CLASS};") } append(')') - append("L${AsmGenerator.OBJECT_CLASS};") + append("L${AsmBuilder.OBJECT_CLASS};") } invokeAlgebraOperation( @@ -43,7 +43,7 @@ internal fun AsmGenerator.tryInvokeSpecific(context: Algebra, name: St } @PublishedApi -internal fun AsmNode.optimize(): AsmNode { +internal fun AsmExpression.optimize(): AsmExpression { val a = tryEvaluate() return if (a == null) this else AsmConstantExpression(a) } -- 2.34.1 From 2580ab347e0fc53c8c030e63c3eb8de96e52b839 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Mon, 15 Jun 2020 17:37:11 +0700 Subject: [PATCH 069/156] Make ClassWriter extensions internal --- .../kotlin/scientifik/kmath/asm/internal/classWriters.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/classWriters.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/classWriters.kt index 84f736ee7..95d713b18 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/classWriters.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/classWriters.kt @@ -3,9 +3,9 @@ package scientifik.kmath.asm.internal import org.objectweb.asm.ClassWriter import org.objectweb.asm.MethodVisitor -inline fun ClassWriter(flags: Int, block: ClassWriter.() -> Unit): ClassWriter = ClassWriter(flags).apply(block) +internal inline fun ClassWriter(flags: Int, block: ClassWriter.() -> Unit): ClassWriter = ClassWriter(flags).apply(block) -inline fun ClassWriter.visitMethod( +internal inline fun ClassWriter.visitMethod( access: Int, name: String, descriptor: String, -- 2.34.1 From 1e2460c5b3e4f03b8351fd5ad7b5feb63311923f Mon Sep 17 00:00:00 2001 From: Commander Tvis Date: Mon, 15 Jun 2020 21:02:38 +0700 Subject: [PATCH 070/156] Rename --- .../kmath/asm/internal/{MethodVisitors.kt => methodVisitors.kt} | 0 .../kmath/asm/internal/{Optimization.kt => optimization.kt} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/{MethodVisitors.kt => methodVisitors.kt} (100%) rename kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/{Optimization.kt => optimization.kt} (100%) diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/MethodVisitors.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/methodVisitors.kt similarity index 100% rename from kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/MethodVisitors.kt rename to kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/methodVisitors.kt diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/Optimization.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/optimization.kt similarity index 100% rename from kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/Optimization.kt rename to kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/optimization.kt -- 2.34.1 From 548966f5bd69ad53c690dd4e42d9f8747053fa32 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Tue, 16 Jun 2020 01:50:20 +0700 Subject: [PATCH 071/156] Implement copy for DataViewMemory --- .../jsMain/kotlin/scientifik/memory/DataViewMemory.kt | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/kmath-memory/src/jsMain/kotlin/scientifik/memory/DataViewMemory.kt b/kmath-memory/src/jsMain/kotlin/scientifik/memory/DataViewMemory.kt index 59945efb9..22cf2bac1 100644 --- a/kmath-memory/src/jsMain/kotlin/scientifik/memory/DataViewMemory.kt +++ b/kmath-memory/src/jsMain/kotlin/scientifik/memory/DataViewMemory.kt @@ -11,16 +11,15 @@ class DataViewMemory(val view: DataView) : Memory { override fun view(offset: Int, length: Int): Memory { require(offset >= 0) { "offset shouldn't be negative: $offset" } require(length >= 0) { "length shouldn't be negative: $length" } - if (offset + length > size) { + + if (offset + length > size) throw IndexOutOfBoundsException("offset + length > size: $offset + $length > $size") - } + return DataViewMemory(DataView(view.buffer, view.byteOffset + offset, length)) } - override fun copy(): Memory { - TODO("not implemented") //To change body of created functions use File | Settings | File Templates. - } + override fun copy(): Memory = DataViewMemory(DataView(view.buffer.slice(0))) private val reader = object : MemoryReader { override val memory: Memory get() = this@DataViewMemory @@ -94,4 +93,4 @@ actual fun Memory.Companion.allocate(length: Int): Memory { actual fun Memory.Companion.wrap(array: ByteArray): Memory { @Suppress("CAST_NEVER_SUCCEEDS") val int8Array = array as Int8Array return DataViewMemory(DataView(int8Array.buffer, int8Array.byteOffset, int8Array.length)) -} \ No newline at end of file +} -- 2.34.1 From 19c3f03cc6b5b4f8d6fe40ef204355d9e90ddfd4 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Tue, 16 Jun 2020 01:53:12 +0700 Subject: [PATCH 072/156] Specify types explicitly --- .../src/jsMain/kotlin/scientifik/memory/DataViewMemory.kt | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/kmath-memory/src/jsMain/kotlin/scientifik/memory/DataViewMemory.kt b/kmath-memory/src/jsMain/kotlin/scientifik/memory/DataViewMemory.kt index 22cf2bac1..38ec14824 100644 --- a/kmath-memory/src/jsMain/kotlin/scientifik/memory/DataViewMemory.kt +++ b/kmath-memory/src/jsMain/kotlin/scientifik/memory/DataViewMemory.kt @@ -18,10 +18,9 @@ class DataViewMemory(val view: DataView) : Memory { return DataViewMemory(DataView(view.buffer, view.byteOffset + offset, length)) } - override fun copy(): Memory = DataViewMemory(DataView(view.buffer.slice(0))) - private val reader = object : MemoryReader { + private val reader: MemoryReader = object : MemoryReader { override val memory: Memory get() = this@DataViewMemory override fun readDouble(offset: Int): Double = view.getFloat64(offset, false) @@ -44,7 +43,7 @@ class DataViewMemory(val view: DataView) : Memory { override fun reader(): MemoryReader = reader - private val writer = object : MemoryWriter { + private val writer: MemoryWriter = object : MemoryWriter { override val memory: Memory get() = this@DataViewMemory override fun writeDouble(offset: Int, value: Double) { @@ -75,7 +74,6 @@ class DataViewMemory(val view: DataView) : Memory { override fun release() { //does nothing on JS } - } override fun writer(): MemoryWriter = writer -- 2.34.1 From 96550922cdbb582a556d6740ad51cd4c884b0684 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Mon, 15 Jun 2020 22:07:31 +0300 Subject: [PATCH 073/156] Removal of AsmExpression --- .../scientifik/kmath/structures/ComplexND.kt | 2 +- .../scientifik/kmath/structures/NDField.kt | 8 +- .../kotlin/scientifik/kmath/ast/MST.kt | 11 +- .../kotlin/scientifik/kmath/ast/MSTAlgebra.kt | 33 ++ .../scientifik/kmath/asm/AsmExpressions.kt | 292 ------------------ .../kotlin/scientifik/kmath/asm/asm.kt | 122 ++++++-- .../kmath/asm/internal/AsmBuilder.kt | 18 +- .../kmath/asm/internal/optimization.kt | 12 +- .../scietifik/kmath/asm/TestAsmAlgebras.kt | 137 ++++---- .../scietifik/kmath/asm/TestAsmExpressions.kt | 8 +- .../kotlin/scietifik/kmath/ast/AsmTest.kt | 6 +- .../commons/expressions/DiffExpression.kt | 8 +- .../kotlin/scientifik/kmath/misc/AutoDiff.kt | 18 +- .../scientifik/kmath/operations/Algebra.kt | 25 +- 14 files changed, 253 insertions(+), 447 deletions(-) create mode 100644 kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MSTAlgebra.kt delete mode 100644 kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmExpressions.kt diff --git a/examples/src/main/kotlin/scientifik/kmath/structures/ComplexND.kt b/examples/src/main/kotlin/scientifik/kmath/structures/ComplexND.kt index cc8b68d85..991cd34a1 100644 --- a/examples/src/main/kotlin/scientifik/kmath/structures/ComplexND.kt +++ b/examples/src/main/kotlin/scientifik/kmath/structures/ComplexND.kt @@ -27,7 +27,7 @@ fun main() { val complexTime = measureTimeMillis { complexField.run { - var res = one + var res: NDBuffer = one repeat(n) { res += 1.0 } diff --git a/examples/src/main/kotlin/scientifik/kmath/structures/NDField.kt b/examples/src/main/kotlin/scientifik/kmath/structures/NDField.kt index cfd1206ff..2aafb504d 100644 --- a/examples/src/main/kotlin/scientifik/kmath/structures/NDField.kt +++ b/examples/src/main/kotlin/scientifik/kmath/structures/NDField.kt @@ -23,14 +23,14 @@ fun main() { measureAndPrint("Automatic field addition") { autoField.run { - var res = one + var res: NDBuffer = one repeat(n) { - res += 1.0 + res += number(1.0) } } } - measureAndPrint("Element addition"){ + measureAndPrint("Element addition") { var res = genericField.one repeat(n) { res += 1.0 @@ -63,7 +63,7 @@ fun main() { genericField.run { var res: NDBuffer = one repeat(n) { - res += 1.0 + res += one // con't avoid using `one` due to resolution ambiguity } } } diff --git a/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MST.kt b/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MST.kt index 900b9297a..142d27f93 100644 --- a/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MST.kt +++ b/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MST.kt @@ -1,5 +1,6 @@ package scientifik.kmath.ast +import scientifik.kmath.operations.Algebra import scientifik.kmath.operations.NumericAlgebra import scientifik.kmath.operations.RealField @@ -40,12 +41,14 @@ sealed class MST { //TODO add a function with named arguments -fun NumericAlgebra.evaluate(node: MST): T { +fun Algebra.evaluate(node: MST): T { return when (node) { - is MST.Numeric -> number(node.value) + is MST.Numeric -> (this as? NumericAlgebra)?.number(node.value) + ?: error("Numeric nodes are not supported by $this") is MST.Symbolic -> symbol(node.value) is MST.Unary -> unaryOperation(node.operation, evaluate(node.value)) is MST.Binary -> when { + this !is NumericAlgebra -> binaryOperation(node.operation, evaluate(node.left), evaluate(node.right)) node.left is MST.Numeric && node.right is MST.Numeric -> { val number = RealField.binaryOperation( node.operation, @@ -59,4 +62,6 @@ fun NumericAlgebra.evaluate(node: MST): T { else -> binaryOperation(node.operation, evaluate(node.left), evaluate(node.right)) } } -} \ No newline at end of file +} + +fun MST.compile(algebra: Algebra): T = algebra.evaluate(this) \ No newline at end of file diff --git a/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MSTAlgebra.kt b/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MSTAlgebra.kt new file mode 100644 index 000000000..45693645c --- /dev/null +++ b/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MSTAlgebra.kt @@ -0,0 +1,33 @@ +@file:Suppress("DELEGATED_MEMBER_HIDES_SUPERTYPE_OVERRIDE") + +package scientifik.kmath.ast + +import scientifik.kmath.operations.* + +object MSTAlgebra : NumericAlgebra { + override fun symbol(value: String): MST = MST.Symbolic(value) + + override fun unaryOperation(operation: String, arg: MST): MST = MST.Unary(operation, arg) + + override fun binaryOperation(operation: String, left: MST, right: MST): MST = MST.Binary(operation, left, right) + + override fun number(value: Number): MST = MST.Numeric(value) +} + +object MSTSpace : Space, NumericAlgebra by MSTAlgebra { + override val zero: MST = number(0.0) + + override fun add(a: MST, b: MST): MST = binaryOperation(SpaceOperations.PLUS_OPERATION, a, b) + + override fun multiply(a: MST, k: Number): MST = binaryOperation(RingOperations.TIMES_OPERATION, a, number(k)) +} + +object MSTRing : Ring, Space by MSTSpace { + override val one: MST = number(1.0) + + override fun multiply(a: MST, b: MST): MST = binaryOperation(RingOperations.TIMES_OPERATION, a, b) +} + +object MSTField : Field, Ring by MSTRing { + override fun divide(a: MST, b: MST): MST = binaryOperation(FieldOperations.DIV_OPERATION, a, b) +} \ No newline at end of file diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmExpressions.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmExpressions.kt deleted file mode 100644 index a8b3ff976..000000000 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/AsmExpressions.kt +++ /dev/null @@ -1,292 +0,0 @@ -package scientifik.kmath.asm - -import scientifik.kmath.asm.internal.AsmBuilder -import scientifik.kmath.asm.internal.hasSpecific -import scientifik.kmath.asm.internal.optimize -import scientifik.kmath.asm.internal.tryInvokeSpecific -import scientifik.kmath.expressions.Expression -import scientifik.kmath.expressions.ExpressionAlgebra -import scientifik.kmath.operations.* -import kotlin.reflect.KClass - -/** - * A function declaration that could be compiled to [AsmBuilder]. - * - * @param T the type the stored function returns. - */ -sealed class AsmExpression: Expression { - abstract val type: KClass - - abstract val algebra: Algebra - - /** - * Tries to evaluate this function without its variables. This method is intended for optimization. - * - * @return `null` if the function depends on its variables, the value if the function is a constant. - */ - internal open fun tryEvaluate(): T? = null - - /** - * Compiles this declaration. - * - * @param gen the target [AsmBuilder]. - */ - internal abstract fun appendTo(gen: AsmBuilder) - - /** - * Compile and cache the expression - */ - private val compiledExpression by lazy{ - val builder = AsmBuilder(type.java, algebra, buildName(this)) - this.appendTo(builder) - builder.generate() - } - - override fun invoke(arguments: Map): T = compiledExpression.invoke(arguments) -} - -internal class AsmUnaryOperation( - override val type: KClass, - override val algebra: Algebra, - private val name: String, - expr: AsmExpression -) : AsmExpression() { - private val expr: AsmExpression = expr.optimize() - override fun tryEvaluate(): T? = algebra { unaryOperation(name, expr.tryEvaluate() ?: return@algebra null) } - - override fun appendTo(gen: AsmBuilder) { - gen.visitLoadAlgebra() - - if (!hasSpecific(algebra, name, 1)) - gen.visitStringConstant(name) - - expr.appendTo(gen) - - if (gen.tryInvokeSpecific(algebra, name, 1)) - return - - gen.visitAlgebraOperation( - owner = AsmBuilder.ALGEBRA_CLASS, - method = "unaryOperation", - descriptor = "(L${AsmBuilder.STRING_CLASS};" + - "L${AsmBuilder.OBJECT_CLASS};)" + - "L${AsmBuilder.OBJECT_CLASS};" - ) - } -} - -internal class AsmBinaryOperation( - override val type: KClass, - override val algebra: Algebra, - private val name: String, - first: AsmExpression, - second: AsmExpression -) : AsmExpression() { - private val first: AsmExpression = first.optimize() - private val second: AsmExpression = second.optimize() - - override fun tryEvaluate(): T? = algebra { - binaryOperation( - name, - first.tryEvaluate() ?: return@algebra null, - second.tryEvaluate() ?: return@algebra null - ) - } - - override fun appendTo(gen: AsmBuilder) { - gen.visitLoadAlgebra() - - if (!hasSpecific(algebra, name, 2)) - gen.visitStringConstant(name) - - first.appendTo(gen) - second.appendTo(gen) - - if (gen.tryInvokeSpecific(algebra, name, 2)) - return - - gen.visitAlgebraOperation( - owner = AsmBuilder.ALGEBRA_CLASS, - method = "binaryOperation", - descriptor = "(L${AsmBuilder.STRING_CLASS};" + - "L${AsmBuilder.OBJECT_CLASS};" + - "L${AsmBuilder.OBJECT_CLASS};)" + - "L${AsmBuilder.OBJECT_CLASS};" - ) - } -} - -internal class AsmVariableExpression( - override val type: KClass, - override val algebra: Algebra, - private val name: String, - private val default: T? = null -) : AsmExpression() { - override fun appendTo(gen: AsmBuilder): Unit = gen.visitLoadFromVariables(name, default) -} - -internal class AsmConstantExpression( - override val type: KClass, - override val algebra: Algebra, - private val value: T -) : AsmExpression() { - override fun tryEvaluate(): T = value - override fun appendTo(gen: AsmBuilder): Unit = gen.visitLoadFromConstants(value) -} - -internal class AsmConstProductExpression( - override val type: KClass, - override val algebra: Space, - expr: AsmExpression, - private val const: Number -) : AsmExpression() { - private val expr: AsmExpression = expr.optimize() - - override fun tryEvaluate(): T? = algebra { (expr.tryEvaluate() ?: return@algebra null) * const } - - override fun appendTo(gen: AsmBuilder) { - gen.visitLoadAlgebra() - gen.visitNumberConstant(const) - expr.appendTo(gen) - - gen.visitAlgebraOperation( - owner = AsmBuilder.SPACE_OPERATIONS_CLASS, - method = "multiply", - descriptor = "(L${AsmBuilder.OBJECT_CLASS};" + - "L${AsmBuilder.NUMBER_CLASS};)" + - "L${AsmBuilder.OBJECT_CLASS};" - ) - } -} - -internal class AsmNumberExpression( - override val type: KClass, - override val algebra: NumericAlgebra, - private val value: Number -) : AsmExpression() { - override fun tryEvaluate(): T? = algebra.number(value) - - override fun appendTo(gen: AsmBuilder): Unit = gen.visitNumberConstant(value) -} - -internal abstract class FunctionalCompiledExpression internal constructor( - @JvmField protected val algebra: Algebra, - @JvmField protected val constants: Array -) : Expression { - abstract override fun invoke(arguments: Map): T -} - -/** - * A context class for [AsmExpression] construction. - * - * @param algebra The algebra to provide for AsmExpressions built. - */ -open class AsmExpressionAlgebra>(val type: KClass, val algebra: A) : - NumericAlgebra>, ExpressionAlgebra> { - - /** - * Builds an AsmExpression to wrap a number. - */ - override fun number(value: Number): AsmExpression = AsmNumberExpression(type, algebra, value) - - /** - * Builds an AsmExpression of constant expression which does not depend on arguments. - */ - override fun const(value: T): AsmExpression = AsmConstantExpression(type, algebra, value) - - /** - * Builds an AsmExpression to access a variable. - */ - override fun variable(name: String, default: T?): AsmExpression = AsmVariableExpression(type, algebra, name, default) - - /** - * Builds an AsmExpression of dynamic call of binary operation [operation] on [left] and [right]. - */ - override fun binaryOperation(operation: String, left: AsmExpression, right: AsmExpression): AsmExpression = - AsmBinaryOperation(type, algebra, operation, left, right) - - /** - * Builds an AsmExpression of dynamic call of unary operation with name [operation] on [arg]. - */ - override fun unaryOperation(operation: String, arg: AsmExpression): AsmExpression = - AsmUnaryOperation(type, algebra, operation, arg) -} - -/** - * A context class for [AsmExpression] construction for [Space] algebras. - */ -open class AsmExpressionSpace(type: KClass, algebra: A) : AsmExpressionAlgebra(type, algebra), - Space> where A : Space, A : NumericAlgebra { - override val zero: AsmExpression get() = const(algebra.zero) - - /** - * Builds an AsmExpression of addition of two another expressions. - */ - override fun add(a: AsmExpression, b: AsmExpression): AsmExpression = - AsmBinaryOperation(type, algebra, SpaceOperations.PLUS_OPERATION, a, b) - - /** - * Builds an AsmExpression of multiplication of expression by number. - */ - override fun multiply(a: AsmExpression, k: Number): AsmExpression = AsmConstProductExpression(type, algebra, a, k) - - operator fun AsmExpression.plus(arg: T): AsmExpression = this + const(arg) - operator fun AsmExpression.minus(arg: T): AsmExpression = this - const(arg) - operator fun T.plus(arg: AsmExpression): AsmExpression = arg + this - operator fun T.minus(arg: AsmExpression): AsmExpression = arg - this - - override fun unaryOperation(operation: String, arg: AsmExpression): AsmExpression = - super.unaryOperation(operation, arg) - - override fun binaryOperation(operation: String, left: AsmExpression, right: AsmExpression): AsmExpression = - super.binaryOperation(operation, left, right) -} - -/** - * A context class for [AsmExpression] construction for [Ring] algebras. - */ -open class AsmExpressionRing(type: KClass, algebra: A) : AsmExpressionSpace(type, algebra), - Ring> where A : Ring, A : NumericAlgebra { - override val one: AsmExpression get() = const(algebra.one) - - /** - * Builds an AsmExpression of multiplication of two expressions. - */ - override fun multiply(a: AsmExpression, b: AsmExpression): AsmExpression = - AsmBinaryOperation(type, algebra, RingOperations.TIMES_OPERATION, a, b) - - operator fun AsmExpression.times(arg: T): AsmExpression = this * const(arg) - operator fun T.times(arg: AsmExpression): AsmExpression = arg * this - - override fun unaryOperation(operation: String, arg: AsmExpression): AsmExpression = - super.unaryOperation(operation, arg) - - override fun binaryOperation(operation: String, left: AsmExpression, right: AsmExpression): AsmExpression = - super.binaryOperation(operation, left, right) - - override fun number(value: Number): AsmExpression = super.number(value) -} - -/** - * A context class for [AsmExpression] construction for [Field] algebras. - */ -open class AsmExpressionField(type: KClass, algebra: A) : - AsmExpressionRing(type, algebra), - Field> where A : Field, A : NumericAlgebra { - /** - * Builds an AsmExpression of division an expression by another one. - */ - override fun divide(a: AsmExpression, b: AsmExpression): AsmExpression = - AsmBinaryOperation(type, algebra, FieldOperations.DIV_OPERATION, a, b) - - operator fun AsmExpression.div(arg: T): AsmExpression = this / const(arg) - operator fun T.div(arg: AsmExpression): AsmExpression = arg / this - - override fun unaryOperation(operation: String, arg: AsmExpression): AsmExpression = - super.unaryOperation(operation, arg) - - override fun binaryOperation(operation: String, left: AsmExpression, right: AsmExpression): AsmExpression = - super.binaryOperation(operation, left, right) - - override fun number(value: Number): AsmExpression = super.number(value) -} diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/asm.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/asm.kt index cc3e36e94..48e368fc3 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/asm.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/asm.kt @@ -1,47 +1,103 @@ package scientifik.kmath.asm +import scientifik.kmath.asm.internal.AsmBuilder +import scientifik.kmath.asm.internal.hasSpecific +import scientifik.kmath.asm.internal.tryInvokeSpecific import scientifik.kmath.ast.MST -import scientifik.kmath.ast.evaluate +import scientifik.kmath.ast.MSTField +import scientifik.kmath.ast.MSTRing +import scientifik.kmath.ast.MSTSpace import scientifik.kmath.expressions.Expression -import scientifik.kmath.operations.Field -import scientifik.kmath.operations.NumericAlgebra -import scientifik.kmath.operations.Ring -import scientifik.kmath.operations.Space +import scientifik.kmath.operations.* +import kotlin.reflect.KClass -internal fun buildName(expression: AsmExpression<*>, collision: Int = 0): String { - val name = "scientifik.kmath.expressions.generated.AsmCompiledExpression_${expression.hashCode()}_$collision" - try { - Class.forName(name) - } catch (ignored: ClassNotFoundException) { - return name +/** + * Compile given MST to an Expression using AST compiler + */ +fun MST.compileWith(type: KClass, algebra: Algebra): Expression { + + fun buildName(mst: MST, collision: Int = 0): String { + val name = "scientifik.kmath.expressions.generated.AsmCompiledExpression_${mst.hashCode()}_$collision" + + try { + Class.forName(name) + } catch (ignored: ClassNotFoundException) { + return name + } + + return buildName(mst, collision + 1) } - return buildName(expression, collision + 1) + fun AsmBuilder.visit(node: MST): Unit { + when (node) { + is MST.Symbolic -> visitLoadFromVariables(node.value) + is MST.Numeric -> { + val constant = if (algebra is NumericAlgebra) { + algebra.number(node.value) + } else { + error("Number literals are not supported in $algebra") + } + visitLoadFromConstants(constant) + } + is MST.Unary -> { + visitLoadAlgebra() + + if (!hasSpecific(algebra, node.operation, 1)) visitStringConstant(node.operation) + + visit(node.value) + + if (!tryInvokeSpecific(algebra, node.operation, 1)) { + visitAlgebraOperation( + owner = AsmBuilder.ALGEBRA_CLASS, + method = "unaryOperation", + descriptor = "(L${AsmBuilder.STRING_CLASS};" + + "L${AsmBuilder.OBJECT_CLASS};)" + + "L${AsmBuilder.OBJECT_CLASS};" + ) + } + } + is MST.Binary -> { + visitLoadAlgebra() + + if (!hasSpecific(algebra, node.operation, 2)) + visitStringConstant(node.operation) + + visit(node.left) + visit(node.right) + + if (!tryInvokeSpecific(algebra, node.operation, 2)) { + + visitAlgebraOperation( + owner = AsmBuilder.ALGEBRA_CLASS, + method = "binaryOperation", + descriptor = "(L${AsmBuilder.STRING_CLASS};" + + "L${AsmBuilder.OBJECT_CLASS};" + + "L${AsmBuilder.OBJECT_CLASS};)" + + "L${AsmBuilder.OBJECT_CLASS};" + ) + } + } + } + } + + val builder = AsmBuilder(type.java, algebra, buildName(this)) + builder.visit(this) + return builder.generate() } -inline fun , E : AsmExpressionAlgebra> A.asm( - expressionAlgebra: E, - block: E.() -> AsmExpression -): Expression = expressionAlgebra.block() +inline fun Algebra.compile(mst: MST): Expression = mst.compileWith(T::class, this) -inline fun NumericAlgebra.asm(ast: MST): Expression = - AsmExpressionAlgebra(T::class, this).evaluate(ast) +inline fun , E : Algebra> A.asm( + mstAlgebra: E, + block: E.() -> MST +): Expression = mstAlgebra.block().compileWith(T::class, this) -inline fun A.asmSpace(block: AsmExpressionSpace.() -> AsmExpression): Expression where A : NumericAlgebra, A : Space = - AsmExpressionSpace(T::class, this).block() +inline fun > A.asmInSpace(block: MSTSpace.() -> MST): Expression = + MSTSpace.block().compileWith(T::class, this) -inline fun A.asmSpace(ast: MST): Expression where A : NumericAlgebra, A : Space = - asmSpace { evaluate(ast) } +inline fun > A.asmInRing(block: MSTRing.() -> MST): Expression = + MSTRing.block().compileWith(T::class, this) -inline fun A.asmRing(block: AsmExpressionRing.() -> AsmExpression): Expression where A : NumericAlgebra, A : Ring = - AsmExpressionRing(T::class, this).block() - -inline fun A.asmRing(ast: MST): Expression where A : NumericAlgebra, A : Ring = - asmRing { evaluate(ast) } - -inline fun A.asmField(block: AsmExpressionField.() -> AsmExpression): Expression where A : NumericAlgebra, A : Field = - AsmExpressionField(T::class, this).block() - -inline fun A.asmField(ast: MST): Expression where A : NumericAlgebra, A : Field = - asmRing { evaluate(ast) } +inline fun > A.asmInField(block: MSTField.() -> MST): Expression = + MSTField.block().compileWith(T::class, this) diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt index eefde66e4..076093ee1 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt @@ -4,10 +4,19 @@ import org.objectweb.asm.ClassWriter import org.objectweb.asm.Label import org.objectweb.asm.MethodVisitor import org.objectweb.asm.Opcodes -import scientifik.kmath.asm.FunctionalCompiledExpression import scientifik.kmath.asm.internal.AsmBuilder.AsmClassLoader +import scientifik.kmath.expressions.Expression import scientifik.kmath.operations.Algebra + +private abstract class FunctionalCompiledExpression internal constructor( + @JvmField protected val algebra: Algebra, + @JvmField protected val constants: Array +) : Expression { + abstract override fun invoke(arguments: Map): T +} + + /** * AsmGenerationContext is a structure that abstracts building a class that unwraps [AsmNode] to plain Java * expression. This class uses [AsmClassLoader] for loading the generated class, then it is able to instantiate the new @@ -16,6 +25,8 @@ import scientifik.kmath.operations.Algebra * @param T the type of AsmExpression to unwrap. * @param algebra the algebra the applied AsmExpressions use. * @param className the unique class name of new loaded class. + * + * @author [Iaroslav Postovalov](https://github.com/CommanderTvis) */ internal class AsmBuilder( private val classOfT: Class<*>, @@ -44,7 +55,6 @@ internal class AsmBuilder( private val invokeMethodVisitor: MethodVisitor private val invokeL0: Label private lateinit var invokeL1: Label - private var generatedInstance: FunctionalCompiledExpression? = null init { asmCompiledClassWriter.visit( @@ -113,8 +123,7 @@ internal class AsmBuilder( } @Suppress("UNCHECKED_CAST") - fun generate(): FunctionalCompiledExpression { - generatedInstance?.let { return it } + fun generate(): Expression { invokeMethodVisitor.run { visitInsn(Opcodes.ARETURN) @@ -182,7 +191,6 @@ internal class AsmBuilder( .first() .newInstance(algebra, constants.toTypedArray()) as FunctionalCompiledExpression - generatedInstance = new return new } diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/optimization.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/optimization.kt index d9f950ceb..029939f16 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/optimization.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/optimization.kt @@ -1,8 +1,6 @@ package scientifik.kmath.asm.internal import org.objectweb.asm.Opcodes -import scientifik.kmath.asm.AsmConstantExpression -import scientifik.kmath.asm.AsmExpression import scientifik.kmath.operations.Algebra private val methodNameAdapters: Map = mapOf("+" to "add", "*" to "multiply", "/" to "divide") @@ -41,8 +39,8 @@ internal fun AsmBuilder.tryInvokeSpecific(context: Algebra, name: Stri return true } - -internal fun AsmExpression.optimize(): AsmExpression { - val a = tryEvaluate() - return if (a == null) this else AsmConstantExpression(type, algebra, a) -} +// +//internal fun AsmExpression.optimize(): AsmExpression { +// val a = tryEvaluate() +// return if (a == null) this else AsmConstantExpression(type, algebra, a) +//} diff --git a/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/asm/TestAsmAlgebras.kt b/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/asm/TestAsmAlgebras.kt index 06f776597..a6cb1e247 100644 --- a/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/asm/TestAsmAlgebras.kt +++ b/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/asm/TestAsmAlgebras.kt @@ -1,75 +1,66 @@ package scietifik.kmath.asm -import scientifik.kmath.asm.asmField -import scientifik.kmath.asm.asmRing -import scientifik.kmath.asm.asmSpace -import scientifik.kmath.expressions.invoke -import scientifik.kmath.operations.ByteRing -import scientifik.kmath.operations.RealField -import kotlin.test.Test -import kotlin.test.assertEquals - -class TestAsmAlgebras { - @Test - fun space() { - val res = ByteRing.asmSpace { - binaryOperation( - "+", - - unaryOperation( - "+", - 3.toByte() - (2.toByte() + (multiply( - add(const(1), const(1)), - 2 - ) + 1.toByte()) * 3.toByte() - 1.toByte()) - ), - - number(1) - ) + variable("x") + zero - }("x" to 2.toByte()) - - assertEquals(16, res) - } - - @Test - fun ring() { - val res = ByteRing.asmRing { - binaryOperation( - "+", - - unaryOperation( - "+", - (3.toByte() - (2.toByte() + (multiply( - add(const(1), const(1)), - 2 - ) + 1.toByte()))) * 3.0 - 1.toByte() - ), - - number(1) - ) * const(2) - }() - - assertEquals(24, res) - } - - @Test - fun field() { - val res = RealField.asmField { - divide(binaryOperation( - "+", - - unaryOperation( - "+", - (3.0 - (2.0 + (multiply( - add(const(1.0), const(1.0)), - 2 - ) + 1.0))) * 3 - 1.0 - ), - - number(1) - ) / 2, const(2.0)) * one - }() - - assertEquals(3.0, res) - } -} +// +//class TestAsmAlgebras { +// @Test +// fun space() { +// val res = ByteRing.asmInRing { +// binaryOperation( +// "+", +// +// unaryOperation( +// "+", +// 3.toByte() - (2.toByte() + (multiply( +// add(number(1), number(1)), +// 2 +// ) + 1.toByte()) * 3.toByte() - 1.toByte()) +// ), +// +// number(1) +// ) + symbol("x") + zero +// }("x" to 2.toByte()) +// +// assertEquals(16, res) +// } +// +// @Test +// fun ring() { +// val res = ByteRing.asmInRing { +// binaryOperation( +// "+", +// +// unaryOperation( +// "+", +// (3.toByte() - (2.toByte() + (multiply( +// add(const(1), const(1)), +// 2 +// ) + 1.toByte()))) * 3.0 - 1.toByte() +// ), +// +// number(1) +// ) * const(2) +// }() +// +// assertEquals(24, res) +// } +// +// @Test +// fun field() { +// val res = RealField.asmInField { +// +(3 - 2 + 2*(number(1)+1.0) +// +// unaryOperation( +// "+", +// (3.0 - (2.0 + (multiply( +// add((1.0), const(1.0)), +// 2 +// ) + 1.0))) * 3 - 1.0 +// )+ +// +// number(1) +// ) / 2, const(2.0)) * one +// }() +// +// assertEquals(3.0, res) +// } +//} diff --git a/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/asm/TestAsmExpressions.kt b/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/asm/TestAsmExpressions.kt index 40d990537..bfbf5e926 100644 --- a/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/asm/TestAsmExpressions.kt +++ b/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/asm/TestAsmExpressions.kt @@ -1,6 +1,6 @@ package scietifik.kmath.asm -import scientifik.kmath.asm.asmField +import scientifik.kmath.asm.asmInField import scientifik.kmath.expressions.invoke import scientifik.kmath.operations.RealField import kotlin.test.Test @@ -9,13 +9,13 @@ import kotlin.test.assertEquals class TestAsmExpressions { @Test fun testUnaryOperationInvocation() { - val res = RealField.asmField { unaryOperation("+", variable("x")) }("x" to 2.0) - assertEquals(2.0, res) + val res = RealField.asmInField { -symbol("x") }("x" to 2.0) + assertEquals(-2.0, res) } @Test fun testConstProductInvocation() { - val res = RealField.asmField { variable("x") * 2 }("x" to 2.0) + val res = RealField.asmInField { symbol("x") * 2 }("x" to 2.0) assertEquals(4.0, res) } } diff --git a/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/AsmTest.kt b/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/AsmTest.kt index c92524b5d..752b3b601 100644 --- a/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/AsmTest.kt +++ b/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/AsmTest.kt @@ -1,18 +1,18 @@ package scietifik.kmath.ast -import scientifik.kmath.asm.asmField +import scientifik.kmath.asm.compile import scientifik.kmath.ast.parseMath import scientifik.kmath.expressions.invoke import scientifik.kmath.operations.Complex import scientifik.kmath.operations.ComplexField -import kotlin.test.assertEquals import kotlin.test.Test +import kotlin.test.assertEquals class AsmTest { @Test fun parsedExpression() { val mst = "2+2*(2+2)".parseMath() - val res = ComplexField.asmField(mst)() + val res = ComplexField.compile(mst)() assertEquals(Complex(10.0, 0.0), res) } } diff --git a/kmath-commons/src/main/kotlin/scientifik/kmath/commons/expressions/DiffExpression.kt b/kmath-commons/src/main/kotlin/scientifik/kmath/commons/expressions/DiffExpression.kt index f6f61e08a..a38fd52a8 100644 --- a/kmath-commons/src/main/kotlin/scientifik/kmath/commons/expressions/DiffExpression.kt +++ b/kmath-commons/src/main/kotlin/scientifik/kmath/commons/expressions/DiffExpression.kt @@ -74,10 +74,10 @@ class DerivativeStructureField( override fun ln(arg: DerivativeStructure): DerivativeStructure = arg.log() - operator fun DerivativeStructure.plus(n: Number): DerivativeStructure = add(n.toDouble()) - operator fun DerivativeStructure.minus(n: Number): DerivativeStructure = subtract(n.toDouble()) - operator fun Number.plus(s: DerivativeStructure) = s + this - operator fun Number.minus(s: DerivativeStructure) = s - this + override operator fun DerivativeStructure.plus(b: Number): DerivativeStructure = add(b.toDouble()) + override operator fun DerivativeStructure.minus(b: Number): DerivativeStructure = subtract(b.toDouble()) + override operator fun Number.plus(b: DerivativeStructure) = b + this + override operator fun Number.minus(b: DerivativeStructure) = b - this } /** diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/misc/AutoDiff.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/misc/AutoDiff.kt index ed77054cf..076701a4f 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/misc/AutoDiff.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/misc/AutoDiff.kt @@ -90,20 +90,20 @@ abstract class AutoDiffField> : Field> { // Overloads for Double constants - operator fun Number.plus(that: Variable): Variable = - derive(variable { this@plus.toDouble() * one + that.value }) { z -> - that.d += z.d + override operator fun Number.plus(b: Variable): Variable = + derive(variable { this@plus.toDouble() * one + b.value }) { z -> + b.d += z.d } - operator fun Variable.plus(b: Number): Variable = b.plus(this) + override operator fun Variable.plus(b: Number): Variable = b.plus(this) - operator fun Number.minus(that: Variable): Variable = - derive(variable { this@minus.toDouble() * one - that.value }) { z -> - that.d -= z.d + override operator fun Number.minus(b: Variable): Variable = + derive(variable { this@minus.toDouble() * one - b.value }) { z -> + b.d -= z.d } - operator fun Variable.minus(that: Number): Variable = - derive(variable { this@minus.value - one * that.toDouble() }) { z -> + override operator fun Variable.minus(b: Number): Variable = + derive(variable { this@minus.value - one * b.toDouble() }) { z -> this@minus.d += z.d } } 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 8ed3f329e..52b6bba02 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Algebra.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Algebra.kt @@ -12,9 +12,6 @@ interface Algebra { */ fun symbol(value: String): T = error("Wrapping of '$value' is not supported in $this") - @Deprecated("Symbol is more concise",replaceWith = ReplaceWith("symbol")) - fun raw(value: String): T = symbol(value) - /** * Dynamic call of unary operation with name [operation] on [arg] */ @@ -64,6 +61,8 @@ interface SpaceOperations : Algebra { //Operation to be performed in this context. Could be moved to extensions in case of KEEP-176 operator fun T.unaryMinus(): T = multiply(this, -1.0) + operator fun T.unaryPlus(): T = this + operator fun T.plus(b: T): T = add(this, b) operator fun T.minus(b: T): T = add(this, -b) operator fun T.times(k: Number) = multiply(this, k.toDouble()) @@ -138,17 +137,25 @@ interface Ring : Space, RingOperations, NumericAlgebra { override fun number(value: Number): T = one * value.toDouble() override fun leftSideNumberOperation(operation: String, left: Number, right: T): T = when (operation) { + SpaceOperations.PLUS_OPERATION -> left + right + SpaceOperations.MINUS_OPERATION -> left - right RingOperations.TIMES_OPERATION -> left * right else -> super.leftSideNumberOperation(operation, left, right) } - //TODO those operators are blocked by type conflict in RealField + override fun rightSideNumberOperation(operation: String, left: T, right: Number): T = when (operation) { + SpaceOperations.PLUS_OPERATION -> left + right + SpaceOperations.MINUS_OPERATION -> left - right + RingOperations.TIMES_OPERATION -> left * right + else -> super.rightSideNumberOperation(operation, left, right) + } -// 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 + + operator fun T.plus(b: Number) = this.plus(number(b)) + operator fun Number.plus(b: T) = b + this + + operator fun T.minus(b: Number) = this.minus(number(b)) + operator fun Number.minus(b: T) = -b + this } /** -- 2.34.1 From 15d7a20b43166b576df63a434bdcd0bc40632840 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Tue, 16 Jun 2020 14:16:36 +0700 Subject: [PATCH 074/156] Add removed AsmCompiledExpression, move buildName to buildName.kt, refactor compileWith --- .../kotlin/scientifik/kmath/asm/asm.kt | 37 ++++++------------- .../kmath/asm/internal/AsmBuilder.kt | 17 ++++----- .../asm/internal/AsmCompiledExpression.kt | 12 ++++++ .../kmath/asm/internal/buildName.kt | 15 ++++++++ .../kmath/asm/internal/optimization.kt | 7 +--- 5 files changed, 46 insertions(+), 42 deletions(-) create mode 100644 kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmCompiledExpression.kt create mode 100644 kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/buildName.kt diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/asm.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/asm.kt index 48e368fc3..e7644998e 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/asm.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/asm.kt @@ -1,6 +1,7 @@ package scientifik.kmath.asm import scientifik.kmath.asm.internal.AsmBuilder +import scientifik.kmath.asm.internal.buildName import scientifik.kmath.asm.internal.hasSpecific import scientifik.kmath.asm.internal.tryInvokeSpecific import scientifik.kmath.ast.MST @@ -11,44 +12,30 @@ import scientifik.kmath.expressions.Expression import scientifik.kmath.operations.* import kotlin.reflect.KClass - /** * Compile given MST to an Expression using AST compiler */ fun MST.compileWith(type: KClass, algebra: Algebra): Expression { - - fun buildName(mst: MST, collision: Int = 0): String { - val name = "scientifik.kmath.expressions.generated.AsmCompiledExpression_${mst.hashCode()}_$collision" - - try { - Class.forName(name) - } catch (ignored: ClassNotFoundException) { - return name - } - - return buildName(mst, collision + 1) - } - - fun AsmBuilder.visit(node: MST): Unit { + fun AsmBuilder.visit(node: MST) { when (node) { - is MST.Symbolic -> visitLoadFromVariables(node.value) + is MST.Symbolic -> loadVariable(node.value) is MST.Numeric -> { val constant = if (algebra is NumericAlgebra) { algebra.number(node.value) } else { error("Number literals are not supported in $algebra") } - visitLoadFromConstants(constant) + loadTConstant(constant) } is MST.Unary -> { - visitLoadAlgebra() + loadAlgebra() - if (!hasSpecific(algebra, node.operation, 1)) visitStringConstant(node.operation) + if (!hasSpecific(algebra, node.operation, 1)) loadStringConstant(node.operation) visit(node.value) if (!tryInvokeSpecific(algebra, node.operation, 1)) { - visitAlgebraOperation( + invokeAlgebraOperation( owner = AsmBuilder.ALGEBRA_CLASS, method = "unaryOperation", descriptor = "(L${AsmBuilder.STRING_CLASS};" + @@ -58,17 +45,17 @@ fun MST.compileWith(type: KClass, algebra: Algebra): Expression< } } is MST.Binary -> { - visitLoadAlgebra() + loadAlgebra() if (!hasSpecific(algebra, node.operation, 2)) - visitStringConstant(node.operation) + loadStringConstant(node.operation) visit(node.left) visit(node.right) if (!tryInvokeSpecific(algebra, node.operation, 2)) { - visitAlgebraOperation( + invokeAlgebraOperation( owner = AsmBuilder.ALGEBRA_CLASS, method = "binaryOperation", descriptor = "(L${AsmBuilder.STRING_CLASS};" + @@ -81,9 +68,7 @@ fun MST.compileWith(type: KClass, algebra: Algebra): Expression< } } - val builder = AsmBuilder(type.java, algebra, buildName(this)) - builder.visit(this) - return builder.generate() + return AsmBuilder(type.java, algebra, buildName(this)) { visit(this@compileWith) }.getInstance() } inline fun Algebra.compile(mst: MST): Expression = mst.compileWith(T::class, this) diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt index 720d44b45..73ef77cc5 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt @@ -4,9 +4,8 @@ import org.objectweb.asm.ClassWriter import org.objectweb.asm.Label import org.objectweb.asm.MethodVisitor import org.objectweb.asm.Opcodes -import scientifik.kmath.asm.AsmExpression -import scientifik.kmath.asm.FunctionalCompiledExpression import scientifik.kmath.asm.internal.AsmBuilder.ClassLoader +import scientifik.kmath.ast.MST import scientifik.kmath.operations.Algebra /** @@ -22,7 +21,7 @@ internal class AsmBuilder @PublishedApi internal constructor( private val classOfT: Class<*>, private val algebra: Algebra, private val className: String, - private val root: AsmExpression + private val evaluateMethodVisitor: AsmBuilder.() -> Unit ) { private class ClassLoader(parent: java.lang.ClassLoader) : java.lang.ClassLoader(parent) { internal fun defineClass(name: String?, b: ByteArray): Class<*> = defineClass(name, b, 0, b.size) @@ -42,10 +41,10 @@ internal class AsmBuilder @PublishedApi internal constructor( private val invokeArgumentsVar: Int = 1 private val constants: MutableList = mutableListOf() private lateinit var invokeMethodVisitor: MethodVisitor - private var generatedInstance: FunctionalCompiledExpression? = null + private var generatedInstance: AsmCompiledExpression? = null @Suppress("UNCHECKED_CAST") - fun getInstance(): FunctionalCompiledExpression { + fun getInstance(): AsmCompiledExpression { generatedInstance?.let { return it } val classWriter = ClassWriter(ClassWriter.COMPUTE_FRAMES) { @@ -112,7 +111,7 @@ internal class AsmBuilder @PublishedApi internal constructor( visitCode() val l0 = Label() visitLabel(l0) - root.compile(this@AsmBuilder) + evaluateMethodVisitor() visitReturnObject() val l1 = Label() visitLabel(l1) @@ -178,7 +177,7 @@ internal class AsmBuilder @PublishedApi internal constructor( .defineClass(className, classWriter.toByteArray()) .constructors .first() - .newInstance(algebra, constants.toTypedArray()) as FunctionalCompiledExpression + .newInstance(algebra, constants.toTypedArray()) as AsmCompiledExpression generatedInstance = new return new @@ -296,13 +295,11 @@ internal class AsmBuilder @PublishedApi internal constructor( private val INLINABLE_NUMBERS: Set> by lazy { SIGNATURE_LETTERS.keys } internal const val FUNCTIONAL_COMPILED_EXPRESSION_CLASS = - "scientifik/kmath/asm/FunctionalCompiledExpression" + "scientifik/kmath/asm/internal/AsmCompiledExpression" internal const val MAP_CLASS = "java/util/Map" internal const val OBJECT_CLASS = "java/lang/Object" internal const val ALGEBRA_CLASS = "scientifik/kmath/operations/Algebra" - internal const val SPACE_OPERATIONS_CLASS = "scientifik/kmath/operations/SpaceOperations" internal const val STRING_CLASS = "java/lang/String" - internal const val NUMBER_CLASS = "java/lang/Number" } } diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmCompiledExpression.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmCompiledExpression.kt new file mode 100644 index 000000000..0e113099a --- /dev/null +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmCompiledExpression.kt @@ -0,0 +1,12 @@ +package scientifik.kmath.asm.internal + +import scientifik.kmath.expressions.Expression +import scientifik.kmath.operations.Algebra + +internal abstract class AsmCompiledExpression internal constructor( + @JvmField protected val algebra: Algebra, + @JvmField protected val constants: Array +) : Expression { + abstract override fun invoke(arguments: Map): T +} + diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/buildName.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/buildName.kt new file mode 100644 index 000000000..65652d72b --- /dev/null +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/buildName.kt @@ -0,0 +1,15 @@ +package scientifik.kmath.asm.internal + +import scientifik.kmath.ast.MST + +internal fun buildName(mst: MST, collision: Int = 0): String { + val name = "scientifik.kmath.asm.generated.AsmCompiledExpression_${mst.hashCode()}_$collision" + + try { + Class.forName(name) + } catch (ignored: ClassNotFoundException) { + return name + } + + return buildName(mst, collision + 1) +} diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/optimization.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/optimization.kt index 029939f16..a39dba6b1 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/optimization.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/optimization.kt @@ -29,7 +29,7 @@ internal fun AsmBuilder.tryInvokeSpecific(context: Algebra, name: Stri append("L${AsmBuilder.OBJECT_CLASS};") } - visitAlgebraOperation( + invokeAlgebraOperation( owner = owner, method = aName, descriptor = sig, @@ -39,8 +39,3 @@ internal fun AsmBuilder.tryInvokeSpecific(context: Algebra, name: Stri return true } -// -//internal fun AsmExpression.optimize(): AsmExpression { -// val a = tryEvaluate() -// return if (a == null) this else AsmConstantExpression(type, algebra, a) -//} -- 2.34.1 From 91a9e2a5e9c66821c0e5b008710b6cb798dbf9bd Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Tue, 16 Jun 2020 14:20:17 +0700 Subject: [PATCH 075/156] Remove @PublishedApi --- .../jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt index 73ef77cc5..5525e623f 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt @@ -16,8 +16,7 @@ import scientifik.kmath.operations.Algebra * @param algebra the algebra the applied AsmExpressions use. * @param className the unique class name of new loaded class. */ -@PublishedApi -internal class AsmBuilder @PublishedApi internal constructor( +internal class AsmBuilder internal constructor( private val classOfT: Class<*>, private val algebra: Algebra, private val className: String, -- 2.34.1 From c3cecc5a1611d4793dfaf4dd059d96a9f8455971 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Tue, 16 Jun 2020 14:21:13 +0700 Subject: [PATCH 076/156] Rename variable --- .../kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt index 5525e623f..337219029 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt @@ -5,7 +5,6 @@ import org.objectweb.asm.Label import org.objectweb.asm.MethodVisitor import org.objectweb.asm.Opcodes import scientifik.kmath.asm.internal.AsmBuilder.ClassLoader -import scientifik.kmath.ast.MST import scientifik.kmath.operations.Algebra /** @@ -20,7 +19,7 @@ internal class AsmBuilder internal constructor( private val classOfT: Class<*>, private val algebra: Algebra, private val className: String, - private val evaluateMethodVisitor: AsmBuilder.() -> Unit + private val invokeLabel0Visitor: AsmBuilder.() -> Unit ) { private class ClassLoader(parent: java.lang.ClassLoader) : java.lang.ClassLoader(parent) { internal fun defineClass(name: String?, b: ByteArray): Class<*> = defineClass(name, b, 0, b.size) @@ -110,7 +109,7 @@ internal class AsmBuilder internal constructor( visitCode() val l0 = Label() visitLabel(l0) - evaluateMethodVisitor() + invokeLabel0Visitor() visitReturnObject() val l1 = Label() visitLabel(l1) -- 2.34.1 From 3d5036c982389ca45c66933454a212edd423ce60 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Tue, 16 Jun 2020 10:27:54 +0300 Subject: [PATCH 077/156] Fix MSTAlgebra delegation --- .../kotlin/scientifik/kmath/ast/MSTAlgebra.kt | 22 +++++++++--- .../kotlin/scientifik/kmath/asm/asm.kt | 36 ++++++++++--------- 2 files changed, 37 insertions(+), 21 deletions(-) diff --git a/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MSTAlgebra.kt b/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MSTAlgebra.kt index 45693645c..4a5900a48 100644 --- a/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MSTAlgebra.kt +++ b/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MSTAlgebra.kt @@ -1,4 +1,3 @@ -@file:Suppress("DELEGATED_MEMBER_HIDES_SUPERTYPE_OVERRIDE") package scientifik.kmath.ast @@ -7,9 +6,11 @@ import scientifik.kmath.operations.* object MSTAlgebra : NumericAlgebra { override fun symbol(value: String): MST = MST.Symbolic(value) - override fun unaryOperation(operation: String, arg: MST): MST = MST.Unary(operation, arg) + override fun unaryOperation(operation: String, arg: MST): MST = + MST.Unary(operation, arg) - override fun binaryOperation(operation: String, left: MST, right: MST): MST = MST.Binary(operation, left, right) + override fun binaryOperation(operation: String, left: MST, right: MST): MST = + MST.Binary(operation, left, right) override fun number(value: Number): MST = MST.Numeric(value) } @@ -17,17 +18,28 @@ object MSTAlgebra : NumericAlgebra { object MSTSpace : Space, NumericAlgebra by MSTAlgebra { override val zero: MST = number(0.0) - override fun add(a: MST, b: MST): MST = binaryOperation(SpaceOperations.PLUS_OPERATION, a, b) + override fun add(a: MST, b: MST): MST = + binaryOperation(SpaceOperations.PLUS_OPERATION, a, b) - override fun multiply(a: MST, k: Number): MST = binaryOperation(RingOperations.TIMES_OPERATION, a, number(k)) + override fun multiply(a: MST, k: Number): MST = + binaryOperation(RingOperations.TIMES_OPERATION, a, number(k)) + + override fun binaryOperation(operation: String, left: MST, right: MST): MST = + MSTAlgebra.binaryOperation(operation, left, right) + + override fun unaryOperation(operation: String, arg: MST): MST = MSTAlgebra.unaryOperation(operation, arg) } object MSTRing : Ring, Space by MSTSpace { override val one: MST = number(1.0) override fun multiply(a: MST, b: MST): MST = binaryOperation(RingOperations.TIMES_OPERATION, a, b) + override fun binaryOperation(operation: String, left: MST, right: MST): MST = + MSTSpace.binaryOperation(operation, left, right) } object MSTField : Field, Ring by MSTRing { override fun divide(a: MST, b: MST): MST = binaryOperation(FieldOperations.DIV_OPERATION, a, b) + override fun binaryOperation(operation: String, left: MST, right: MST): MST = + MSTRing.binaryOperation(operation, left, right) } \ No newline at end of file diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/asm.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/asm.kt index 48e368fc3..46739c49c 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/asm.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/asm.kt @@ -4,11 +4,10 @@ import scientifik.kmath.asm.internal.AsmBuilder import scientifik.kmath.asm.internal.hasSpecific import scientifik.kmath.asm.internal.tryInvokeSpecific import scientifik.kmath.ast.MST -import scientifik.kmath.ast.MSTField -import scientifik.kmath.ast.MSTRing -import scientifik.kmath.ast.MSTSpace +import scientifik.kmath.ast.MSTExpression import scientifik.kmath.expressions.Expression -import scientifik.kmath.operations.* +import scientifik.kmath.operations.Algebra +import scientifik.kmath.operations.NumericAlgebra import kotlin.reflect.KClass @@ -88,16 +87,21 @@ fun MST.compileWith(type: KClass, algebra: Algebra): Expression< inline fun Algebra.compile(mst: MST): Expression = mst.compileWith(T::class, this) -inline fun , E : Algebra> A.asm( - mstAlgebra: E, - block: E.() -> MST -): Expression = mstAlgebra.block().compileWith(T::class, this) +/** + * Optimize performance of an [MSTExpression] using ASM codegen + */ +inline fun MSTExpression.compile(): Expression = mst.compileWith(T::class, algebra) -inline fun > A.asmInSpace(block: MSTSpace.() -> MST): Expression = - MSTSpace.block().compileWith(T::class, this) - -inline fun > A.asmInRing(block: MSTRing.() -> MST): Expression = - MSTRing.block().compileWith(T::class, this) - -inline fun > A.asmInField(block: MSTField.() -> MST): Expression = - MSTField.block().compileWith(T::class, this) +//inline fun , E : Algebra> A.asm( +// mstAlgebra: E, +// block: E.() -> MST +//): Expression = mstAlgebra.block().compileWith(T::class, this) +// +//inline fun > A.asmInSpace(block: MSTSpace.() -> MST): Expression = +// MSTSpace.block().compileWith(T::class, this) +// +//inline fun > A.asmInRing(block: MSTRing.() -> MST): Expression = +// MSTRing.block().compileWith(T::class, this) +// +//inline fun > A.asmInField(block: MSTField.() -> MST): Expression = +// MSTField.block().compileWith(T::class, this) -- 2.34.1 From 8264806958764be299d69a98d0804ea50d5e485e Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Tue, 16 Jun 2020 14:52:02 +0300 Subject: [PATCH 078/156] Algebra delegates update. --- kmath-ast/build.gradle.kts | 5 ++ .../kotlin/scientifik/kmath/ast/MSTAlgebra.kt | 55 +++++++++++++++---- .../scientifik/kmath/ast/MSTExpression.kt | 46 ++++++++++++++-- .../kotlin/scientifik/kmath/asm/asm.kt | 21 ++----- .../scietifik/kmath/asm/TestAsmExpressions.kt | 9 ++- .../kotlin/scietifik/kmath/ast/AsmTest.kt | 5 +- .../scientifik/kmath/expressions/Builders.kt | 6 +- .../kmath/expressions/Expression.kt | 13 +++-- .../FunctionalExpressionAlgebra.kt | 8 +-- 9 files changed, 117 insertions(+), 51 deletions(-) diff --git a/kmath-ast/build.gradle.kts b/kmath-ast/build.gradle.kts index 9764fccc3..511571fc9 100644 --- a/kmath-ast/build.gradle.kts +++ b/kmath-ast/build.gradle.kts @@ -7,6 +7,11 @@ repositories { } kotlin.sourceSets { +// all { +// languageSettings.apply{ +// enableLanguageFeature("NewInference") +// } +// } commonMain { dependencies { api(project(":kmath-core")) diff --git a/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MSTAlgebra.kt b/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MSTAlgebra.kt index 4a5900a48..07194a7bb 100644 --- a/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MSTAlgebra.kt +++ b/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MSTAlgebra.kt @@ -1,9 +1,10 @@ - package scientifik.kmath.ast import scientifik.kmath.operations.* object MSTAlgebra : NumericAlgebra { + override fun number(value: Number): MST = MST.Numeric(value) + override fun symbol(value: String): MST = MST.Symbolic(value) override fun unaryOperation(operation: String, arg: MST): MST = @@ -11,13 +12,15 @@ object MSTAlgebra : NumericAlgebra { override fun binaryOperation(operation: String, left: MST, right: MST): MST = MST.Binary(operation, left, right) - - override fun number(value: Number): MST = MST.Numeric(value) } -object MSTSpace : Space, NumericAlgebra by MSTAlgebra { +object MSTSpace : Space, NumericAlgebra { override val zero: MST = number(0.0) + override fun number(value: Number): MST = MST.Numeric(value) + + override fun symbol(value: String): MST = MST.Symbolic(value) + override fun add(a: MST, b: MST): MST = binaryOperation(SpaceOperations.PLUS_OPERATION, a, b) @@ -30,16 +33,44 @@ object MSTSpace : Space, NumericAlgebra by MSTAlgebra { override fun unaryOperation(operation: String, arg: MST): MST = MSTAlgebra.unaryOperation(operation, arg) } -object MSTRing : Ring, Space by MSTSpace { - override val one: MST = number(1.0) +object MSTRing : Ring, NumericAlgebra { + override fun number(value: Number): MST = MST.Numeric(value) + override fun symbol(value: String): MST = MST.Symbolic(value) + + override val zero: MST = MSTSpace.number(0.0) + override val one: MST = number(1.0) + override fun add(a: MST, b: MST): MST = + MSTAlgebra.binaryOperation(SpaceOperations.PLUS_OPERATION, a, b) + + override fun multiply(a: MST, k: Number): MST = + MSTAlgebra.binaryOperation(RingOperations.TIMES_OPERATION, a, MSTSpace.number(k)) + + override fun multiply(a: MST, b: MST): MST = + binaryOperation(RingOperations.TIMES_OPERATION, a, b) - override fun multiply(a: MST, b: MST): MST = binaryOperation(RingOperations.TIMES_OPERATION, a, b) override fun binaryOperation(operation: String, left: MST, right: MST): MST = - MSTSpace.binaryOperation(operation, left, right) + MSTAlgebra.binaryOperation(operation, left, right) } -object MSTField : Field, Ring by MSTRing { - override fun divide(a: MST, b: MST): MST = binaryOperation(FieldOperations.DIV_OPERATION, a, b) +object MSTField : Field{ + override fun symbol(value: String): MST = MST.Symbolic(value) + override fun number(value: Number): MST = MST.Numeric(value) + + override val zero: MST = MSTSpace.number(0.0) + override val one: MST = number(1.0) + override fun add(a: MST, b: MST): MST = + MSTAlgebra.binaryOperation(SpaceOperations.PLUS_OPERATION, a, b) + + + override fun multiply(a: MST, k: Number): MST = + MSTAlgebra.binaryOperation(RingOperations.TIMES_OPERATION, a, MSTSpace.number(k)) + + override fun multiply(a: MST, b: MST): MST = + binaryOperation(RingOperations.TIMES_OPERATION, a, b) + + override fun divide(a: MST, b: MST): MST = + binaryOperation(FieldOperations.DIV_OPERATION, a, b) + override fun binaryOperation(operation: String, left: MST, right: MST): MST = - MSTRing.binaryOperation(operation, left, right) -} \ No newline at end of file + MSTAlgebra.binaryOperation(operation, left, right) +} diff --git a/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MSTExpression.kt b/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MSTExpression.kt index dbd5238e3..61703cac7 100644 --- a/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MSTExpression.kt +++ b/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MSTExpression.kt @@ -1,19 +1,55 @@ package scientifik.kmath.ast import scientifik.kmath.expressions.Expression -import scientifik.kmath.operations.NumericAlgebra +import scientifik.kmath.expressions.FunctionalExpressionField +import scientifik.kmath.expressions.FunctionalExpressionRing +import scientifik.kmath.expressions.FunctionalExpressionSpace +import scientifik.kmath.operations.* /** * The expression evaluates MST on-flight. Should be much faster than functional expression, but slower than ASM-generated expressions. */ -class MSTExpression(val algebra: NumericAlgebra, val mst: MST) : Expression { +class MSTExpression(val algebra: Algebra, val mst: MST) : Expression { /** * Substitute algebra raw value */ - private inner class InnerAlgebra(val arguments: Map) : NumericAlgebra by algebra { - override fun symbol(value: String): T = arguments[value] ?: super.symbol(value) + private inner class InnerAlgebra(val arguments: Map) : NumericAlgebra{ + override fun symbol(value: String): T = arguments[value] ?: algebra.symbol(value) + override fun unaryOperation(operation: String, arg: T): T = algebra.unaryOperation(operation, arg) + + override fun binaryOperation(operation: String, left: T, right: T): T =algebra.binaryOperation(operation, left, right) + + override fun number(value: Number): T = if(algebra is NumericAlgebra){ + algebra.number(value) + } else{ + error("Numeric nodes are not supported by $this") + } } override fun invoke(arguments: Map): T = InnerAlgebra(arguments).evaluate(mst) -} \ No newline at end of file +} + + +inline fun , E : Algebra> A.mst( + mstAlgebra: E, + block: E.() -> MST +): MSTExpression = MSTExpression(this, mstAlgebra.block()) + +inline fun Space.mstInSpace(block: MSTSpace.() -> MST): MSTExpression = + MSTExpression(this, MSTSpace.block()) + +inline fun Ring.mstInRing(block: MSTRing.() -> MST): MSTExpression = + MSTExpression(this, MSTRing.block()) + +inline fun Field.mstInField(block: MSTField.() -> MST): MSTExpression = + MSTExpression(this, MSTField.block()) + +inline fun > FunctionalExpressionSpace.mstInSpace(block: MSTSpace.() -> MST): MSTExpression = + algebra.mstInSpace(block) + +inline fun > FunctionalExpressionRing.mstInRing(block: MSTRing.() -> MST): MSTExpression = + algebra.mstInRing(block) + +inline fun > FunctionalExpressionField.mstInField(block: MSTField.() -> MST): MSTExpression = + algebra.mstInField(block) \ No newline at end of file diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/asm.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/asm.kt index e4bd705b4..bb091732e 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/asm.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/asm.kt @@ -70,23 +70,12 @@ fun MST.compileWith(type: KClass, algebra: Algebra): Expression< return AsmBuilder(type.java, algebra, buildName(this)) { visit(this@compileWith) }.getInstance() } -inline fun Algebra.compile(mst: MST): Expression = mst.compileWith(T::class, this) +/** + * Compile an [MST] to ASM using given algebra + */ +inline fun Algebra.expresion(mst: MST): Expression = mst.compileWith(T::class, this) /** * Optimize performance of an [MSTExpression] using ASM codegen */ -inline fun MSTExpression.compile(): Expression = mst.compileWith(T::class, algebra) - -//inline fun , E : Algebra> A.asm( -// mstAlgebra: E, -// block: E.() -> MST -//): Expression = mstAlgebra.block().compileWith(T::class, this) -// -//inline fun > A.asmInSpace(block: MSTSpace.() -> MST): Expression = -// MSTSpace.block().compileWith(T::class, this) -// -//inline fun > A.asmInRing(block: MSTRing.() -> MST): Expression = -// MSTRing.block().compileWith(T::class, this) -// -//inline fun > A.asmInField(block: MSTField.() -> MST): Expression = -// MSTField.block().compileWith(T::class, this) +inline fun MSTExpression.compile(): Expression = mst.compileWith(T::class, algebra) \ No newline at end of file diff --git a/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/asm/TestAsmExpressions.kt b/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/asm/TestAsmExpressions.kt index bfbf5e926..82af1a927 100644 --- a/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/asm/TestAsmExpressions.kt +++ b/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/asm/TestAsmExpressions.kt @@ -1,6 +1,8 @@ package scietifik.kmath.asm -import scientifik.kmath.asm.asmInField +import scientifik.kmath.asm.compile +import scientifik.kmath.ast.mstInField +import scientifik.kmath.ast.mstInSpace import scientifik.kmath.expressions.invoke import scientifik.kmath.operations.RealField import kotlin.test.Test @@ -9,13 +11,14 @@ import kotlin.test.assertEquals class TestAsmExpressions { @Test fun testUnaryOperationInvocation() { - val res = RealField.asmInField { -symbol("x") }("x" to 2.0) + val expression = RealField.mstInSpace { -symbol("x") }.compile() + val res = expression("x" to 2.0) assertEquals(-2.0, res) } @Test fun testConstProductInvocation() { - val res = RealField.asmInField { symbol("x") * 2 }("x" to 2.0) + val res = RealField.mstInField { symbol("x") * 2 }("x" to 2.0) assertEquals(4.0, res) } } diff --git a/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/AsmTest.kt b/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/AsmTest.kt index 752b3b601..f0f9a8bc1 100644 --- a/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/AsmTest.kt +++ b/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/AsmTest.kt @@ -1,8 +1,7 @@ package scietifik.kmath.ast -import scientifik.kmath.asm.compile +import scientifik.kmath.ast.evaluate import scientifik.kmath.ast.parseMath -import scientifik.kmath.expressions.invoke import scientifik.kmath.operations.Complex import scientifik.kmath.operations.ComplexField import kotlin.test.Test @@ -12,7 +11,7 @@ class AsmTest { @Test fun parsedExpression() { val mst = "2+2*(2+2)".parseMath() - val res = ComplexField.compile(mst)() + val res = ComplexField.evaluate(mst) assertEquals(Complex(10.0, 0.0), res) } } diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/Builders.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/Builders.kt index cdd6f695b..9f1503285 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/Builders.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/Builders.kt @@ -7,17 +7,17 @@ import scientifik.kmath.operations.Space /** * Create a functional expression on this [Space] */ -fun Space.buildExpression(block: FunctionalExpressionSpace>.() -> Expression): Expression = +fun Space.spaceExpression(block: FunctionalExpressionSpace>.() -> Expression): Expression = FunctionalExpressionSpace(this).run(block) /** * Create a functional expression on this [Ring] */ -fun Ring.buildExpression(block: FunctionalExpressionRing>.() -> Expression): Expression = +fun Ring.ringExpression(block: FunctionalExpressionRing>.() -> Expression): Expression = FunctionalExpressionRing(this).run(block) /** * Create a functional expression on this [Field] */ -fun Field.buildExpression(block: FunctionalExpressionField>.() -> Expression): Expression = +fun Field.fieldExpression(block: FunctionalExpressionField>.() -> Expression): Expression = FunctionalExpressionField(this).run(block) 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 eaf3cd1d7..e512b1cd8 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/Expression.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/Expression.kt @@ -8,11 +8,14 @@ import scientifik.kmath.operations.Algebra interface Expression { operator fun invoke(arguments: Map): T - companion object { - operator fun invoke(block: (Map) -> T): Expression = object : Expression { - override fun invoke(arguments: Map): T = block(arguments) - } - } + companion object +} + +/** + * Create simple lazily evaluated expression inside given algebra + */ +fun Algebra.expression(block: Algebra.(arguments: Map) -> T): Expression = object: Expression { + override fun invoke(arguments: Map): T = block(arguments) } operator fun Expression.invoke(vararg pairs: Pair): T = invoke(mapOf(*pairs)) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressionAlgebra.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressionAlgebra.kt index a441a80c0..c8d6e8eb0 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressionAlgebra.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressionAlgebra.kt @@ -69,10 +69,10 @@ interface FunctionalExpressionAlgebra> : ExpressionAlgebra(override val algebra: A) : FunctionalExpressionAlgebra, - Space> where A : Space { - override val zero: Expression - get() = const(algebra.zero) +open class FunctionalExpressionSpace>(override val algebra: A) : + FunctionalExpressionAlgebra, Space> { + + override val zero: Expression get() = const(algebra.zero) /** * Builds an Expression of addition of two another expressions. -- 2.34.1 From a1f0188b8b0b3d5577c4ba8dec31ccf72ff2834e Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Tue, 16 Jun 2020 16:29:52 +0300 Subject: [PATCH 079/156] Functional expression builders --- .../FunctionalExpressionAlgebra.kt | 25 ++++++++++++------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressionAlgebra.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressionAlgebra.kt index c8d6e8eb0..a8a26aa33 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressionAlgebra.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressionAlgebra.kt @@ -36,12 +36,10 @@ internal class FunctionalConstProductExpression( /** * A context class for [Expression] construction. + * + * @param algebra The algebra to provide for Expressions built. */ -interface FunctionalExpressionAlgebra> : ExpressionAlgebra> { - /** - * The algebra to provide for Expressions built. - */ - val algebra: A +abstract class FunctionalExpressionAlgebra>(val algebra: A) : ExpressionAlgebra> { /** * Builds an Expression of constant expression which does not depend on arguments. @@ -69,8 +67,8 @@ interface FunctionalExpressionAlgebra> : ExpressionAlgebra>(override val algebra: A) : - FunctionalExpressionAlgebra, Space> { +open class FunctionalExpressionSpace>(algebra: A) : + FunctionalExpressionAlgebra(algebra), Space> { override val zero: Expression get() = const(algebra.zero) @@ -98,7 +96,7 @@ open class FunctionalExpressionSpace>(override val algebra: A) : super.binaryOperation(operation, left, right) } -open class FunctionalExpressionRing(override val algebra: A) : FunctionalExpressionSpace(algebra), +open class FunctionalExpressionRing(algebra: A) : FunctionalExpressionSpace(algebra), Ring> where A : Ring, A : NumericAlgebra { override val one: Expression get() = const(algebra.one) @@ -119,7 +117,7 @@ open class FunctionalExpressionRing(override val algebra: A) : FunctionalE super.binaryOperation(operation, left, right) } -open class FunctionalExpressionField(override val algebra: A) : +open class FunctionalExpressionField(algebra: A) : FunctionalExpressionRing(algebra), Field> where A : Field, A : NumericAlgebra { /** @@ -137,3 +135,12 @@ open class FunctionalExpressionField(override val algebra: A) : override fun binaryOperation(operation: String, left: Expression, right: Expression): Expression = super.binaryOperation(operation, left, right) } + +inline fun > A.expressionInSpace(block: FunctionalExpressionSpace.() -> Expression): Expression = + FunctionalExpressionSpace(this).block() + +inline fun > A.expressionInRing(block: FunctionalExpressionRing.() -> Expression): Expression = + FunctionalExpressionRing(this).block() + +inline fun > A.expressionInField(block: FunctionalExpressionField.() -> Expression): Expression = + FunctionalExpressionField(this).block() \ No newline at end of file -- 2.34.1 From d6e7eb8143029541d2ff6c5ef320c627ccb66973 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Thu, 18 Jun 2020 11:35:20 +0700 Subject: [PATCH 080/156] Add advanced specialization for primitive non-bridge methods --- .../kotlin/scientifik/kmath/asm/asm.kt | 7 +- .../kmath/asm/internal/AsmBuilder.kt | 65 ++++++++++++++++++- .../{optimization.kt => specialization.kt} | 4 +- .../scietifik/kmath/asm/TestAsmAlgebras.kt | 35 ++++++++-- 4 files changed, 98 insertions(+), 13 deletions(-) rename kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/{optimization.kt => specialization.kt} (90%) diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/asm.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/asm.kt index bb091732e..2345757f1 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/asm.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/asm.kt @@ -19,11 +19,10 @@ fun MST.compileWith(type: KClass, algebra: Algebra): Expression< when (node) { is MST.Symbolic -> loadVariable(node.value) is MST.Numeric -> { - val constant = if (algebra is NumericAlgebra) { - algebra.number(node.value) - } else { + val constant = if (algebra is NumericAlgebra) + algebra.number(node.value) else error("Number literals are not supported in $algebra") - } + loadTConstant(constant) } is MST.Unary -> { diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt index 337219029..351d5b754 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt @@ -6,6 +6,7 @@ import org.objectweb.asm.MethodVisitor import org.objectweb.asm.Opcodes import scientifik.kmath.asm.internal.AsmBuilder.ClassLoader import scientifik.kmath.operations.Algebra +import java.io.File /** * ASM Builder is a structure that abstracts building a class that unwraps [AsmExpression] to plain Java expression. @@ -39,12 +40,23 @@ internal class AsmBuilder internal constructor( private val invokeArgumentsVar: Int = 1 private val constants: MutableList = mutableListOf() private lateinit var invokeMethodVisitor: MethodVisitor + private var primitiveMode = false + internal var primitiveType = OBJECT_CLASS + internal var primitiveTypeSig = "L$OBJECT_CLASS;" + internal var primitiveTypeReturnSig = "L$OBJECT_CLASS;" private var generatedInstance: AsmCompiledExpression? = null @Suppress("UNCHECKED_CAST") fun getInstance(): AsmCompiledExpression { generatedInstance?.let { return it } + if (classOfT in SIGNATURE_LETTERS) { + primitiveMode = true + primitiveTypeSig = SIGNATURE_LETTERS.getValue(classOfT) + primitiveType = SIGNATURE_LETTERS.getValue(classOfT) + primitiveTypeReturnSig = "L$T_CLASS;" + } + val classWriter = ClassWriter(ClassWriter.COMPUTE_FRAMES) { visit( Opcodes.V1_8, @@ -110,6 +122,10 @@ internal class AsmBuilder internal constructor( val l0 = Label() visitLabel(l0) invokeLabel0Visitor() + + if (primitiveMode) + boxPrimitiveToNumber() + visitReturnObject() val l1 = Label() visitLabel(l1) @@ -171,6 +187,8 @@ internal class AsmBuilder internal constructor( visitEnd() } + File("dump.class").writeBytes(classWriter.toByteArray()) + val new = classLoader .defineClass(className, classWriter.toByteArray()) .constructors @@ -182,6 +200,11 @@ internal class AsmBuilder internal constructor( } internal fun loadTConstant(value: T) { + if (primitiveMode) { + loadNumberConstant(value as Number) + return + } + if (classOfT in INLINABLE_NUMBERS) { loadNumberConstant(value as Number) invokeMethodVisitor.visitCheckCast(T_CLASS) @@ -191,6 +214,25 @@ internal class AsmBuilder internal constructor( loadConstant(value as Any, T_CLASS) } + private fun unboxInPrimitiveMode() { + if (!primitiveMode) + return + + unboxNumberToPrimitive() + } + + private fun boxPrimitiveToNumber() { + invokeMethodVisitor.visitInvokeStatic(T_CLASS, "valueOf", "($primitiveTypeSig)L$T_CLASS;") + } + + private fun unboxNumberToPrimitive() { + invokeMethodVisitor.visitInvokeVirtual( + owner = NUMBER_CLASS, + name = NUMBER_CONVERTER_METHODS.getValue(primitiveType), + descriptor = "()${primitiveTypeSig}" + ) + } + private fun loadConstant(value: Any, type: String) { val idx = if (value in constants) constants.indexOf(value) else constants.apply { add(value) }.lastIndex @@ -199,13 +241,14 @@ internal class AsmBuilder internal constructor( visitGetField(owner = slashesClassName, name = "constants", descriptor = "[L$OBJECT_CLASS;") visitLdcOrIntConstant(idx) visitGetObjectArrayElement() - invokeMethodVisitor.visitCheckCast(type) + visitCheckCast(type) + unboxInPrimitiveMode() } } private fun loadThis(): Unit = invokeMethodVisitor.visitLoadObjectVar(invokeThisVar) - internal fun loadNumberConstant(value: Number) { + private fun loadNumberConstant(value: Number) { val clazz = value.javaClass val c = clazz.name.replace('.', '/') val sigLetter = SIGNATURE_LETTERS[clazz] @@ -218,7 +261,9 @@ internal class AsmBuilder internal constructor( else -> invokeMethodVisitor.visitLdcInsn(value) } - invokeMethodVisitor.visitInvokeStatic(c, "valueOf", "($sigLetter)L${c};") + if (!primitiveMode) + invokeMethodVisitor.visitInvokeStatic(c, "valueOf", "($sigLetter)L${c};") + return } @@ -251,6 +296,7 @@ internal class AsmBuilder internal constructor( ) invokeMethodVisitor.visitCheckCast(T_CLASS) + unboxInPrimitiveMode() } internal fun loadAlgebra() { @@ -274,6 +320,7 @@ internal class AsmBuilder internal constructor( ) { invokeMethodVisitor.visitMethodInsn(opcode, owner, method, descriptor, isInterface) invokeMethodVisitor.visitCheckCast(T_CLASS) + unboxInPrimitiveMode() } internal fun loadStringConstant(string: String): Unit = invokeMethodVisitor.visitLdcInsn(string) @@ -290,11 +337,23 @@ internal class AsmBuilder internal constructor( ) } + private val NUMBER_CONVERTER_METHODS by lazy { + mapOf( + "B" to "byteValue", + "S" to "shortValue", + "I" to "intValue", + "J" to "longValue", + "F" to "floatValue", + "D" to "doubleValue" + ) + } + private val INLINABLE_NUMBERS: Set> by lazy { SIGNATURE_LETTERS.keys } internal const val FUNCTIONAL_COMPILED_EXPRESSION_CLASS = "scientifik/kmath/asm/internal/AsmCompiledExpression" + internal const val NUMBER_CLASS = "java/lang/Number" internal const val MAP_CLASS = "java/util/Map" internal const val OBJECT_CLASS = "java/lang/Object" internal const val ALGEBRA_CLASS = "scientifik/kmath/operations/Algebra" diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/optimization.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/specialization.kt similarity index 90% rename from kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/optimization.kt rename to kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/specialization.kt index a39dba6b1..f279c94f4 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/optimization.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/specialization.kt @@ -24,9 +24,9 @@ internal fun AsmBuilder.tryInvokeSpecific(context: Algebra, name: Stri val sig = buildString { append('(') - repeat(arity) { append("L${AsmBuilder.OBJECT_CLASS};") } + repeat(arity) { append(primitiveTypeSig) } append(')') - append("L${AsmBuilder.OBJECT_CLASS};") + append(primitiveTypeReturnSig) } invokeAlgebraOperation( diff --git a/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/asm/TestAsmAlgebras.kt b/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/asm/TestAsmAlgebras.kt index a6cb1e247..294da32e9 100644 --- a/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/asm/TestAsmAlgebras.kt +++ b/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/asm/TestAsmAlgebras.kt @@ -1,10 +1,37 @@ package scietifik.kmath.asm -// -//class TestAsmAlgebras { +import scientifik.kmath.asm.compile +import scientifik.kmath.ast.mstInSpace +import scientifik.kmath.expressions.invoke +import scientifik.kmath.operations.ByteRing +import kotlin.test.Test +import kotlin.test.assertEquals + +class TestAsmAlgebras { + @Test + fun space() { + val res = ByteRing.mstInSpace { + binaryOperation( + "+", + + unaryOperation( + "+", + number(3.toByte()) - (number(2.toByte()) + (multiply( + add(number(1), number(1)), + 2 + ) + number(1.toByte()) * 3.toByte() - number(1.toByte()))) + ), + + number(1) + ) + symbol("x") + zero + }.compile()("x" to 2.toByte()) + + assertEquals(16, res) + } + // @Test // fun space() { -// val res = ByteRing.asmInRing { +// val res = ByteRing.asm { // binaryOperation( // "+", // @@ -63,4 +90,4 @@ package scietifik.kmath.asm // // assertEquals(3.0, res) // } -//} +} -- 2.34.1 From e9ff33c4f95973aa3ebd5d376e847d453f2429f9 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Fri, 19 Jun 2020 23:56:35 +0700 Subject: [PATCH 081/156] Write KDoc comments for AsmBuilder, minimal refactor of it --- .../kmath/asm/internal/AsmBuilder.kt | 82 +++++++++++++++++-- .../kmath/asm/internal/optimization.kt | 3 +- 2 files changed, 75 insertions(+), 10 deletions(-) diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt index 337219029..649d06277 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt @@ -11,9 +11,12 @@ import scientifik.kmath.operations.Algebra * ASM Builder is a structure that abstracts building a class that unwraps [AsmExpression] to plain Java expression. * This class uses [ClassLoader] for loading the generated class, then it is able to instantiate the new class. * - * @param T the type of AsmExpression to unwrap. - * @param algebra the algebra the applied AsmExpressions use. - * @param className the unique class name of new loaded class. + * @param T the type generated [AsmCompiledExpression] operates. + * @property classOfT the [Class] of T. + * @property algebra the algebra the applied AsmExpressions use. + * @property className the unique class name of new loaded class. + * @property invokeLabel0Visitor the function applied to this object when the L0 label of invoke implementation is + * being written. */ internal class AsmBuilder internal constructor( private val classOfT: Class<*>, @@ -21,10 +24,16 @@ internal class AsmBuilder internal constructor( private val className: String, private val invokeLabel0Visitor: AsmBuilder.() -> Unit ) { + /** + * Internal classloader of [AsmBuilder] with alias to define class from byte array. + */ private class ClassLoader(parent: java.lang.ClassLoader) : java.lang.ClassLoader(parent) { internal fun defineClass(name: String?, b: ByteArray): Class<*> = defineClass(name, b, 0, b.size) } + /** + * The instance of [ClassLoader] used by this builder. + */ private val classLoader: ClassLoader = ClassLoader(javaClass.classLoader) @@ -35,14 +44,39 @@ internal class AsmBuilder internal constructor( private val T_CLASS: String = classOfT.name.replace('.', '/') private val slashesClassName: String = className.replace(oldChar = '.', newChar = '/') + + /** + * Index of `this` variable in invoke method of [AsmCompiledExpression] built subclass. + */ private val invokeThisVar: Int = 0 + + /** + * Index of `arguments` variable in invoke method of [AsmCompiledExpression] built subclass. + */ private val invokeArgumentsVar: Int = 1 + + /** + * List of constants to provide to [AsmCompiledExpression] subclass. + */ private val constants: MutableList = mutableListOf() + + /** + * Method visitor of `invoke` method of [AsmCompiledExpression] subclass. + */ private lateinit var invokeMethodVisitor: MethodVisitor + + /** + * The cache of [AsmCompiledExpression] subclass built by this builder. + */ private var generatedInstance: AsmCompiledExpression? = null + /** + * Subclasses, loads and instantiates the [AsmCompiledExpression] for given parameters. + * + * The built instance is cached. + */ @Suppress("UNCHECKED_CAST") - fun getInstance(): AsmCompiledExpression { + internal fun getInstance(): AsmCompiledExpression { generatedInstance?.let { return it } val classWriter = ClassWriter(ClassWriter.COMPUTE_FRAMES) { @@ -181,6 +215,9 @@ internal class AsmBuilder internal constructor( return new } + /** + * Loads a constant from + */ internal fun loadTConstant(value: T) { if (classOfT in INLINABLE_NUMBERS) { loadNumberConstant(value as Number) @@ -191,6 +228,9 @@ internal class AsmBuilder internal constructor( loadConstant(value as Any, T_CLASS) } + /** + * Loads an object constant [value] stored in [AsmCompiledExpression.constants] and casts it to [type]. + */ private fun loadConstant(value: Any, type: String) { val idx = if (value in constants) constants.indexOf(value) else constants.apply { add(value) }.lastIndex @@ -205,7 +245,12 @@ internal class AsmBuilder internal constructor( private fun loadThis(): Unit = invokeMethodVisitor.visitLoadObjectVar(invokeThisVar) - internal fun loadNumberConstant(value: Number) { + /** + * Either loads a numeric constant [value] from [AsmCompiledExpression.constants] field or boxes a primitive + * constant from the constant pool (some numbers with special opcodes like [Opcodes.ICONST_0] aren't even loaded + * from it). + */ + private fun loadNumberConstant(value: Number) { val clazz = value.javaClass val c = clazz.name.replace('.', '/') val sigLetter = SIGNATURE_LETTERS[clazz] @@ -225,6 +270,9 @@ internal class AsmBuilder internal constructor( loadConstant(value, c) } + /** + * Loads a variable [name] from [AsmCompiledExpression.invoke] [Map] parameter. The [defaultValue] may be provided. + */ internal fun loadVariable(name: String, defaultValue: T? = null): Unit = invokeMethodVisitor.run { visitLoadObjectVar(invokeArgumentsVar) @@ -253,6 +301,9 @@ internal class AsmBuilder internal constructor( invokeMethodVisitor.visitCheckCast(T_CLASS) } + /** + * Loads algebra from according field of [AsmCompiledExpression] and casts it to class of [algebra] provided. + */ internal fun loadAlgebra() { loadThis() @@ -265,20 +316,32 @@ internal class AsmBuilder internal constructor( invokeMethodVisitor.visitCheckCast(T_ALGEBRA_CLASS) } + /** + * Writes a method instruction of opcode with its [owner], [method] and its [descriptor]. The default opcode is + * [Opcodes.INVOKEINTERFACE], since most Algebra functions are declared in interface. [loadAlgebra] should be + * called before the arguments and this operation. + * + * The result is casted to [T] automatically. + */ internal fun invokeAlgebraOperation( owner: String, method: String, descriptor: String, - opcode: Int = Opcodes.INVOKEINTERFACE, - isInterface: Boolean = true + opcode: Int = Opcodes.INVOKEINTERFACE ) { - invokeMethodVisitor.visitMethodInsn(opcode, owner, method, descriptor, isInterface) + invokeMethodVisitor.visitMethodInsn(opcode, owner, method, descriptor, opcode == Opcodes.INVOKEINTERFACE) invokeMethodVisitor.visitCheckCast(T_CLASS) } + /** + * Writes a LDC Instruction with string constant provided. + */ internal fun loadStringConstant(string: String): Unit = invokeMethodVisitor.visitLdcInsn(string) internal companion object { + /** + * Maps JVM primitive numbers boxed types to their letters of JVM signature convention. + */ private val SIGNATURE_LETTERS: Map, String> by lazy { mapOf( java.lang.Byte::class.java to "B", @@ -290,6 +353,9 @@ internal class AsmBuilder internal constructor( ) } + /** + * Provides boxed number types values of which can be stored in JVM bytecode constant pool. + */ private val INLINABLE_NUMBERS: Set> by lazy { SIGNATURE_LETTERS.keys } internal const val FUNCTIONAL_COMPILED_EXPRESSION_CLASS = diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/optimization.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/optimization.kt index a39dba6b1..889f3accb 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/optimization.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/optimization.kt @@ -33,8 +33,7 @@ internal fun AsmBuilder.tryInvokeSpecific(context: Algebra, name: Stri owner = owner, method = aName, descriptor = sig, - opcode = Opcodes.INVOKEVIRTUAL, - isInterface = false + opcode = Opcodes.INVOKEVIRTUAL ) return true -- 2.34.1 From ba499da2dac0b1487ac850974e6f8c5f721818e8 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Sat, 20 Jun 2020 00:05:00 +0700 Subject: [PATCH 082/156] More KDoc comments --- .../kmath/asm/internal/AsmCompiledExpression.kt | 7 +++++++ .../kotlin/scientifik/kmath/asm/internal/buildName.kt | 8 +++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmCompiledExpression.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmCompiledExpression.kt index 0e113099a..a5236927c 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmCompiledExpression.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmCompiledExpression.kt @@ -3,6 +3,13 @@ package scientifik.kmath.asm.internal import scientifik.kmath.expressions.Expression import scientifik.kmath.operations.Algebra +/** + * [Expression] partial implementation to have it subclassed by actual implementations. Provides unified storage for + * objects needed to implement the expression. + * + * @property algebra the algebra to delegate calls. + * @property constants the constants array to have persistent objects to reference in [invoke]. + */ internal abstract class AsmCompiledExpression internal constructor( @JvmField protected val algebra: Algebra, @JvmField protected val constants: Array diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/buildName.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/buildName.kt index 65652d72b..66bd039c3 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/buildName.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/buildName.kt @@ -2,7 +2,13 @@ package scientifik.kmath.asm.internal import scientifik.kmath.ast.MST -internal fun buildName(mst: MST, collision: Int = 0): String { +/** + * Creates a class name for [AsmCompiledExpression] subclassed to implement [mst] provided. + * + * This methods helps to avoid collisions of class name to prevent loading several classes with the same name. If there + * is a colliding class, change [collision] parameter or leave it `0` to check existing classes recursively. + */ +internal tailrec fun buildName(mst: MST, collision: Int = 0): String { val name = "scientifik.kmath.asm.generated.AsmCompiledExpression_${mst.hashCode()}_$collision" try { -- 2.34.1 From 635d708de5377d9e655136403eebb50ae886932a Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Sat, 20 Jun 2020 00:08:53 +0700 Subject: [PATCH 083/156] Add missing KDoc comments --- .../src/jvmMain/kotlin/scientifik/kmath/asm/asm.kt | 2 +- .../scientifik/kmath/asm/internal/optimization.kt | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/asm.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/asm.kt index bb091732e..43c1a377d 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/asm.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/asm.kt @@ -73,7 +73,7 @@ fun MST.compileWith(type: KClass, algebra: Algebra): Expression< /** * Compile an [MST] to ASM using given algebra */ -inline fun Algebra.expresion(mst: MST): Expression = mst.compileWith(T::class, this) +inline fun Algebra.expression(mst: MST): Expression = mst.compileWith(T::class, this) /** * Optimize performance of an [MSTExpression] using ASM codegen diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/optimization.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/optimization.kt index 889f3accb..e81a7fd1a 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/optimization.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/optimization.kt @@ -5,6 +5,11 @@ import scientifik.kmath.operations.Algebra private val methodNameAdapters: Map = mapOf("+" to "add", "*" to "multiply", "/" to "divide") +/** + * Checks if the target [context] for code generation contains a method with needed [name] and [arity]. + * + * @return `true` if contains, else `false`. + */ internal fun hasSpecific(context: Algebra, name: String, arity: Int): Boolean { val aName = methodNameAdapters[name] ?: name @@ -14,6 +19,12 @@ internal fun hasSpecific(context: Algebra, name: String, arity: Int): Boo return true } +/** + * Checks if the target [context] for code generation contains a method with needed [name] and [arity] and inserts + * [AsmBuilder.invokeAlgebraOperation] of this method. + * + * @return `true` if contains, else `false`. + */ internal fun AsmBuilder.tryInvokeSpecific(context: Algebra, name: String, arity: Int): Boolean { val aName = methodNameAdapters[name] ?: name -- 2.34.1 From 62ebda33021e9fc7dbf41e5d23ea57b0db5d9442 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Sun, 21 Jun 2020 20:23:50 +0700 Subject: [PATCH 084/156] Update readme, accident documentation-related refactor --- kmath-ast/README.md | 63 ++++++++++++++++++- .../kotlin/scientifik/kmath/asm/asm.kt | 10 ++- .../kmath/asm/internal/AsmBuilder.kt | 4 +- .../kmath/asm/internal/optimization.kt | 12 +++- 4 files changed, 78 insertions(+), 11 deletions(-) diff --git a/kmath-ast/README.md b/kmath-ast/README.md index f9dbd4663..b5ca5886f 100644 --- a/kmath-ast/README.md +++ b/kmath-ast/README.md @@ -1,4 +1,61 @@ -# AST based expression representation and operations +# AST-based expression representation and operations (`kmath-ast`) -## Dynamic expression code generation -Contributed by [Iaroslav Postovalov](https://github.com/CommanderTvis). \ No newline at end of file +This subproject implements the following features: + +- Expression Language and its parser. +- MST as expression language's syntax intermediate representation. +- Type-safe builder of MST. +- Evaluating expressions by traversing MST. + +## Dynamic expression code generation with OW2 ASM + +`kmath-ast` JVM module supports runtime code generation to eliminate overhead of tree traversal. Code generator builds +a special implementation of `Expression` with implemented `invoke` function. + +For example, the following builder: + +```kotlin + RealField.mstInField { symbol("x") + 2 }.compile() +``` + +… leads to generation of bytecode, which can be decompiled to the following Java class: + +```java +package scientifik.kmath.asm.generated; + +import java.util.Map; +import scientifik.kmath.asm.internal.AsmCompiledExpression; +import scientifik.kmath.operations.Algebra; +import scientifik.kmath.operations.RealField; + +// The class's name is build with MST's hash-code and collision fixing number. +public final class AsmCompiledExpression_45045_0 extends AsmCompiledExpression { + // Plain constructor + public AsmCompiledExpression_45045_0(Algebra algebra, Object[] constants) { + super(algebra, constants); + } + + // The actual dynamic code: + public final Double invoke(Map arguments) { + return (Double)((RealField)super.algebra).add((Double)arguments.get("x"), (Double)2.0D); + } +} +``` + +### Example Usage + +This API is an extension to MST and MSTExpression APIs. You may optimize both MST and MSTExpression: + +```kotlin +RealField.mstInField { symbol("x") + 2 }.compile() +RealField.expression("2+2".parseMath()) +``` + +### Known issues + +- Using numeric algebras causes boxing and calling bridge methods. +- The same classes may be generated and loaded twice, so it is recommended to cache compiled expressions to avoid +class loading overhead. +- This API is not supported by non-dynamic JVM implementations (like TeaVM and GraalVM) because of using class loaders. + +Contributed by [Iaroslav Postovalov](https://github.com/CommanderTvis). diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/asm.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/asm.kt index 43c1a377d..350bb95bb 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/asm.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/asm.kt @@ -4,11 +4,11 @@ import scientifik.kmath.asm.internal.AsmBuilder import scientifik.kmath.asm.internal.buildName import scientifik.kmath.asm.internal.hasSpecific import scientifik.kmath.asm.internal.tryInvokeSpecific -import scientifik.kmath.ast.MST -import scientifik.kmath.ast.MSTExpression +import scientifik.kmath.ast.* import scientifik.kmath.expressions.Expression import scientifik.kmath.operations.Algebra import scientifik.kmath.operations.NumericAlgebra +import scientifik.kmath.operations.RealField import kotlin.reflect.KClass /** @@ -78,4 +78,8 @@ inline fun Algebra.expression(mst: MST): Expression = ms /** * Optimize performance of an [MSTExpression] using ASM codegen */ -inline fun MSTExpression.compile(): Expression = mst.compileWith(T::class, algebra) \ No newline at end of file +inline fun MSTExpression.compile(): Expression = mst.compileWith(T::class, algebra) + +fun main() { + RealField.mstInField { symbol("x") + 2 }.compile() +} diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt index 649d06277..53fc4c4c1 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt @@ -8,7 +8,7 @@ import scientifik.kmath.asm.internal.AsmBuilder.ClassLoader import scientifik.kmath.operations.Algebra /** - * ASM Builder is a structure that abstracts building a class that unwraps [AsmExpression] to plain Java expression. + * ASM Builder is a structure that abstracts building a class for unwrapping [MST] to plain Java expression. * This class uses [ClassLoader] for loading the generated class, then it is able to instantiate the new class. * * @param T the type generated [AsmCompiledExpression] operates. @@ -343,7 +343,7 @@ internal class AsmBuilder internal constructor( * Maps JVM primitive numbers boxed types to their letters of JVM signature convention. */ private val SIGNATURE_LETTERS: Map, String> by lazy { - mapOf( + hashMapOf( java.lang.Byte::class.java to "B", java.lang.Short::class.java to "S", java.lang.Integer::class.java to "I", diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/optimization.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/optimization.kt index e81a7fd1a..efad97763 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/optimization.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/optimization.kt @@ -3,7 +3,13 @@ package scientifik.kmath.asm.internal import org.objectweb.asm.Opcodes import scientifik.kmath.operations.Algebra -private val methodNameAdapters: Map = mapOf("+" to "add", "*" to "multiply", "/" to "divide") +private val methodNameAdapters: Map by lazy { + hashMapOf( + "+" to "add", + "*" to "multiply", + "/" to "divide" + ) +} /** * Checks if the target [context] for code generation contains a method with needed [name] and [arity]. @@ -13,7 +19,7 @@ private val methodNameAdapters: Map = mapOf("+" to "add", "*" to internal fun hasSpecific(context: Algebra, name: String, arity: Int): Boolean { val aName = methodNameAdapters[name] ?: name - context::class.java.methods.find { it.name == aName && it.parameters.size == arity } + context.javaClass.methods.find { it.name == aName && it.parameters.size == arity } ?: return false return true @@ -28,7 +34,7 @@ internal fun hasSpecific(context: Algebra, name: String, arity: Int): Boo internal fun AsmBuilder.tryInvokeSpecific(context: Algebra, name: String, arity: Int): Boolean { val aName = methodNameAdapters[name] ?: name - context::class.java.methods.find { it.name == aName && it.parameters.size == arity } + context.javaClass.methods.find { it.name == aName && it.parameters.size == arity } ?: return false val owner = context::class.java.name.replace('.', '/') -- 2.34.1 From e99f7ad360d2c0efb448c7dcdd2825dc1c9d663c Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Mon, 22 Jun 2020 04:05:52 +0700 Subject: [PATCH 085/156] Optimize constant pooling --- kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/asm.kt | 6 +++--- .../kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt | 1 + .../scientifik/kmath/asm/internal/methodVisitors.kt | 8 ++++++++ 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/asm.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/asm.kt index 43c1a377d..feb3745b0 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/asm.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/asm.kt @@ -19,11 +19,11 @@ fun MST.compileWith(type: KClass, algebra: Algebra): Expression< when (node) { is MST.Symbolic -> loadVariable(node.value) is MST.Numeric -> { - val constant = if (algebra is NumericAlgebra) { + val constant = if (algebra is NumericAlgebra) algebra.number(node.value) - } else { + else error("Number literals are not supported in $algebra") - } + loadTConstant(constant) } is MST.Unary -> { diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt index 649d06277..8b6892235 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt @@ -260,6 +260,7 @@ internal class AsmBuilder internal constructor( is Int -> invokeMethodVisitor.visitLdcOrIntConstant(value) is Double -> invokeMethodVisitor.visitLdcOrDoubleConstant(value) is Float -> invokeMethodVisitor.visitLdcOrFloatConstant(value) + is Long -> invokeMethodVisitor.visitLdcOrLongConstant(value) else -> invokeMethodVisitor.visitLdcInsn(value) } diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/methodVisitors.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/methodVisitors.kt index 70fb3bd44..6ecce8833 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/methodVisitors.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/methodVisitors.kt @@ -11,6 +11,8 @@ internal fun MethodVisitor.visitLdcOrIntConstant(value: Int): Unit = when (value 3 -> visitInsn(ICONST_3) 4 -> visitInsn(ICONST_4) 5 -> visitInsn(ICONST_5) + in -128..127 -> visitIntInsn(BIPUSH, value) + in -32768..32767 -> visitIntInsn(SIPUSH, value) else -> visitLdcInsn(value) } @@ -20,6 +22,12 @@ internal fun MethodVisitor.visitLdcOrDoubleConstant(value: Double): Unit = when else -> visitLdcInsn(value) } +internal fun MethodVisitor.visitLdcOrLongConstant(value: Long): Unit = when (value) { + 0L -> visitInsn(LCONST_0) + 1L -> visitInsn(LCONST_1) + else -> visitLdcInsn(value) +} + internal fun MethodVisitor.visitLdcOrFloatConstant(value: Float): Unit = when (value) { 0f -> visitInsn(FCONST_0) 1f -> visitInsn(FCONST_1) -- 2.34.1 From 29c6d2596733d25c4babc80aa90856b877153f42 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Mon, 22 Jun 2020 15:15:46 +0700 Subject: [PATCH 086/156] Optimize constant pooling for Byte and Short --- .../jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt index 8b6892235..ff66e69b0 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt @@ -257,6 +257,8 @@ internal class AsmBuilder internal constructor( if (sigLetter != null) { when (value) { + is Byte -> invokeMethodVisitor.visitLdcOrIntConstant(value.toInt()) + is Short -> invokeMethodVisitor.visitLdcOrIntConstant(value.toInt()) is Int -> invokeMethodVisitor.visitLdcOrIntConstant(value) is Double -> invokeMethodVisitor.visitLdcOrDoubleConstant(value) is Float -> invokeMethodVisitor.visitLdcOrFloatConstant(value) -- 2.34.1 From 675ace272c06f5bada1640d8c9219687f3e1ee91 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Tue, 23 Jun 2020 03:38:20 +0700 Subject: [PATCH 087/156] Minor Gradle settings modification, add benchmarks of different Expression implementatinos --- examples/build.gradle.kts | 10 ++-- .../ast/ExpressionsInterpretersBenchmark.kt | 54 +++++++++++++++++++ .../kmath/structures/ViktorBenchmark.kt | 17 ++---- settings.gradle.kts | 2 + 4 files changed, 64 insertions(+), 19 deletions(-) create mode 100644 examples/src/benchmarks/kotlin/scientifik/kmath/ast/ExpressionsInterpretersBenchmark.kt diff --git a/examples/build.gradle.kts b/examples/build.gradle.kts index 2fab47ac0..eadfc3f6b 100644 --- a/examples/build.gradle.kts +++ b/examples/build.gradle.kts @@ -1,16 +1,13 @@ -import org.jetbrains.kotlin.allopen.gradle.AllOpenExtension import org.jetbrains.kotlin.gradle.tasks.KotlinCompile plugins { java kotlin("jvm") - kotlin("plugin.allopen") version "1.3.71" - id("kotlinx.benchmark") version "0.2.0-dev-7" + kotlin("plugin.allopen") + id("kotlinx.benchmark") } -configure { - annotation("org.openjdk.jmh.annotations.State") -} +allOpen.annotation("org.openjdk.jmh.annotations.State") repositories { maven("http://dl.bintray.com/kyonifer/maven") @@ -24,6 +21,7 @@ sourceSets { } dependencies { + implementation(project(":kmath-ast")) implementation(project(":kmath-core")) implementation(project(":kmath-coroutines")) implementation(project(":kmath-commons")) diff --git a/examples/src/benchmarks/kotlin/scientifik/kmath/ast/ExpressionsInterpretersBenchmark.kt b/examples/src/benchmarks/kotlin/scientifik/kmath/ast/ExpressionsInterpretersBenchmark.kt new file mode 100644 index 000000000..c5474e1d2 --- /dev/null +++ b/examples/src/benchmarks/kotlin/scientifik/kmath/ast/ExpressionsInterpretersBenchmark.kt @@ -0,0 +1,54 @@ +package scientifik.kmath.ast + +import org.openjdk.jmh.annotations.Benchmark +import org.openjdk.jmh.annotations.Scope +import org.openjdk.jmh.annotations.State +import scientifik.kmath.asm.compile +import scientifik.kmath.expressions.Expression +import scientifik.kmath.expressions.expressionInField +import scientifik.kmath.operations.Field +import scientifik.kmath.operations.RealField +import kotlin.random.Random + +@State(Scope.Benchmark) +class ExpressionsInterpretersBenchmark { + private val algebra: Field = RealField + private val random: Random = Random(1) + + @Benchmark + fun functionalExpression() { + val expr = algebra.expressionInField { + variable("x") * const(2.0) + const(2.0) / variable("x") - const(16.0) + } + + invokeAndSum(expr) + } + + @Benchmark + fun mstExpression() { + val expr = algebra.mstInField { + symbol("x") * number(2.0) + number(2.0) / symbol("x") - number(16.0) + } + + invokeAndSum(expr) + } + + @Benchmark + fun asmExpression() { + val expr = algebra.mstInField { + symbol("x") * number(2.0) + number(2.0) / symbol("x") - number(16.0) + }.compile() + + invokeAndSum(expr) + } + + private fun invokeAndSum(expr: Expression) { + var sum = 0.0 + + repeat(1000000) { + sum += expr("x" to random.nextDouble()) + } + + println(sum) + } +} diff --git a/examples/src/benchmarks/kotlin/scientifik/kmath/structures/ViktorBenchmark.kt b/examples/src/benchmarks/kotlin/scientifik/kmath/structures/ViktorBenchmark.kt index be4115d81..5dc166cd9 100644 --- a/examples/src/benchmarks/kotlin/scientifik/kmath/structures/ViktorBenchmark.kt +++ b/examples/src/benchmarks/kotlin/scientifik/kmath/structures/ViktorBenchmark.kt @@ -23,9 +23,7 @@ class ViktorBenchmark { fun `Automatic field addition`() { autoField.run { var res = one - repeat(n) { - res += 1.0 - } + repeat(n) { res += one } } } @@ -33,9 +31,7 @@ class ViktorBenchmark { fun `Viktor field addition`() { viktorField.run { var res = one - repeat(n) { - res += one - } + repeat(n) { res += one } } } @@ -43,9 +39,7 @@ class ViktorBenchmark { fun `Raw Viktor`() { val one = F64Array.full(init = 1.0, shape = *intArrayOf(dim, dim)) var res = one - repeat(n) { - res = res + one - } + repeat(n) { res = res + one } } @Benchmark @@ -53,10 +47,7 @@ class ViktorBenchmark { realField.run { val fortyTwo = produce { 42.0 } var res = one - - repeat(n) { - res = ln(fortyTwo) - } + repeat(n) { res = ln(fortyTwo) } } } diff --git a/settings.gradle.kts b/settings.gradle.kts index 465ecfca8..487e1d87f 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -3,10 +3,12 @@ pluginManagement { val toolsVersion = "0.5.0" plugins { + id("kotlinx.benchmark") version "0.2.0-dev-8" id("scientifik.mpp") version toolsVersion id("scientifik.jvm") version toolsVersion id("scientifik.atomic") version toolsVersion id("scientifik.publish") version toolsVersion + kotlin("plugin.allopen") version "1.3.72" } repositories { -- 2.34.1 From 668d13c9d1b276d81f155d05d39e89ebf29b6c4a Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Tue, 23 Jun 2020 20:03:45 +0300 Subject: [PATCH 088/156] Minor refactoring + domains --- build.gradle.kts | 2 +- examples/build.gradle.kts | 8 +-- .../kmath/structures/ViktorBenchmark.kt | 10 +-- .../random/CMRandomGeneratorWrapper.kt | 38 +++++++++++ .../kotlin/scientifik/kmath/domains/Domain.kt | 15 +++++ .../kmath/domains/HyperSquareDomain.kt | 67 +++++++++++++++++++ .../scientifik/kmath/domains/RealDomain.kt | 65 ++++++++++++++++++ .../kmath/domains/UnconstrainedDomain.kt | 36 ++++++++++ .../kmath/domains/UnivariateDomain.kt | 48 +++++++++++++ .../scientifik/kmath/real/RealVector.kt | 25 +++---- .../scientifik/kmath/real/realMatrix.kt | 4 ++ .../scientifik/kmath/histogram/Histogram.kt | 10 +-- .../kotlin/scientifik/memory/MemorySpec.kt | 1 + 13 files changed, 294 insertions(+), 35 deletions(-) create mode 100644 kmath-commons/src/main/kotlin/scientifik/kmath/commons/random/CMRandomGeneratorWrapper.kt create mode 100644 kmath-core/src/commonMain/kotlin/scientifik/kmath/domains/Domain.kt create mode 100644 kmath-core/src/commonMain/kotlin/scientifik/kmath/domains/HyperSquareDomain.kt create mode 100644 kmath-core/src/commonMain/kotlin/scientifik/kmath/domains/RealDomain.kt create mode 100644 kmath-core/src/commonMain/kotlin/scientifik/kmath/domains/UnconstrainedDomain.kt create mode 100644 kmath-core/src/commonMain/kotlin/scientifik/kmath/domains/UnivariateDomain.kt diff --git a/build.gradle.kts b/build.gradle.kts index 6d102a77a..052b457c5 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -2,7 +2,7 @@ plugins { id("scientifik.publish") apply false } -val kmathVersion by extra("0.1.4-dev-7") +val kmathVersion by extra("0.1.4-dev-8") val bintrayRepo by extra("scientifik") val githubProject by extra("kmath") diff --git a/examples/build.gradle.kts b/examples/build.gradle.kts index 2fab47ac0..fb47c998f 100644 --- a/examples/build.gradle.kts +++ b/examples/build.gradle.kts @@ -4,8 +4,8 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile plugins { java kotlin("jvm") - kotlin("plugin.allopen") version "1.3.71" - id("kotlinx.benchmark") version "0.2.0-dev-7" + kotlin("plugin.allopen") version "1.3.72" + id("kotlinx.benchmark") version "0.2.0-dev-8" } configure { @@ -33,8 +33,8 @@ dependencies { implementation(project(":kmath-dimensions")) implementation("com.kyonifer:koma-core-ejml:0.12") implementation("org.jetbrains.kotlinx:kotlinx-io-jvm:0.2.0-npm-dev-6") - implementation("org.jetbrains.kotlinx:kotlinx.benchmark.runtime:0.2.0-dev-7") - "benchmarksCompile"(sourceSets.main.get().compileClasspath) + implementation("org.jetbrains.kotlinx:kotlinx.benchmark.runtime:0.2.0-dev-8") + "benchmarksCompile"(sourceSets.main.get().output + sourceSets.main.get().compileClasspath) //sourceSets.main.output + sourceSets.main.runtimeClasspath } // Configure benchmark diff --git a/examples/src/benchmarks/kotlin/scientifik/kmath/structures/ViktorBenchmark.kt b/examples/src/benchmarks/kotlin/scientifik/kmath/structures/ViktorBenchmark.kt index be4115d81..54105f778 100644 --- a/examples/src/benchmarks/kotlin/scientifik/kmath/structures/ViktorBenchmark.kt +++ b/examples/src/benchmarks/kotlin/scientifik/kmath/structures/ViktorBenchmark.kt @@ -20,7 +20,7 @@ class ViktorBenchmark { final val viktorField = ViktorNDField(intArrayOf(dim, dim)) @Benchmark - fun `Automatic field addition`() { + fun automaticFieldAddition() { autoField.run { var res = one repeat(n) { @@ -30,7 +30,7 @@ class ViktorBenchmark { } @Benchmark - fun `Viktor field addition`() { + fun viktorFieldAddition() { viktorField.run { var res = one repeat(n) { @@ -40,7 +40,7 @@ class ViktorBenchmark { } @Benchmark - fun `Raw Viktor`() { + fun rawViktor() { val one = F64Array.full(init = 1.0, shape = *intArrayOf(dim, dim)) var res = one repeat(n) { @@ -49,7 +49,7 @@ class ViktorBenchmark { } @Benchmark - fun `Real field log`() { + fun realdFieldLog() { realField.run { val fortyTwo = produce { 42.0 } var res = one @@ -61,7 +61,7 @@ class ViktorBenchmark { } @Benchmark - fun `Raw Viktor log`() { + fun rawViktorLog() { val fortyTwo = F64Array.full(dim, dim, init = 42.0) var res: F64Array repeat(n) { diff --git a/kmath-commons/src/main/kotlin/scientifik/kmath/commons/random/CMRandomGeneratorWrapper.kt b/kmath-commons/src/main/kotlin/scientifik/kmath/commons/random/CMRandomGeneratorWrapper.kt new file mode 100644 index 000000000..13e79d60e --- /dev/null +++ b/kmath-commons/src/main/kotlin/scientifik/kmath/commons/random/CMRandomGeneratorWrapper.kt @@ -0,0 +1,38 @@ +package scientifik.kmath.commons.random + +import scientifik.kmath.prob.RandomGenerator + +class CMRandomGeneratorWrapper(val factory: (IntArray) -> RandomGenerator) : + org.apache.commons.math3.random.RandomGenerator { + private var generator = factory(intArrayOf()) + + override fun nextBoolean(): Boolean = generator.nextBoolean() + + override fun nextFloat(): Float = generator.nextDouble().toFloat() + + override fun setSeed(seed: Int) { + generator = factory(intArrayOf(seed)) + } + + override fun setSeed(seed: IntArray) { + generator = factory(seed) + } + + override fun setSeed(seed: Long) { + setSeed(seed.toInt()) + } + + override fun nextBytes(bytes: ByteArray) { + generator.fillBytes(bytes) + } + + override fun nextInt(): Int = generator.nextInt() + + override fun nextInt(n: Int): Int = generator.nextInt(n) + + override fun nextGaussian(): Double = TODO() + + override fun nextDouble(): Double = generator.nextDouble() + + override fun nextLong(): Long = generator.nextLong() +} \ No newline at end of file diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/domains/Domain.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/domains/Domain.kt new file mode 100644 index 000000000..333b77cb4 --- /dev/null +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/domains/Domain.kt @@ -0,0 +1,15 @@ +package scientifik.kmath.domains + +import scientifik.kmath.linear.Point + +/** + * A simple geometric domain + */ +interface Domain { + operator fun contains(point: Point): Boolean + + /** + * Number of hyperspace dimensions + */ + val dimension: Int +} \ No newline at end of file diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/domains/HyperSquareDomain.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/domains/HyperSquareDomain.kt new file mode 100644 index 000000000..21912b87c --- /dev/null +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/domains/HyperSquareDomain.kt @@ -0,0 +1,67 @@ +/* + * Copyright 2015 Alexander Nozik. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package scientifik.kmath.domains + +import scientifik.kmath.linear.Point +import scientifik.kmath.structures.DoubleBuffer +import scientifik.kmath.structures.indices + +/** + * + * HyperSquareDomain class. + * + * @author Alexander Nozik + */ +class HyperSquareDomain(private val lower: DoubleBuffer, private val upper: DoubleBuffer) : RealDomain { + + override operator fun contains(point: Point): Boolean = point.indices.all { i -> + point[i] in lower[i]..upper[i] + } + + override val dimension: Int get() = lower.size + + override fun getLowerBound(num: Int, point: Point): Double? = lower[num] + + override fun getLowerBound(num: Int): Double? = lower[num] + + override fun getUpperBound(num: Int, point: Point): Double? = upper[num] + + override fun getUpperBound(num: Int): Double? = upper[num] + + override fun nearestInDomain(point: Point): Point { + val res: DoubleArray = DoubleArray(point.size) { i -> + when { + point[i] < lower[i] -> lower[i] + point[i] > upper[i] -> upper[i] + else -> point[i] + } + } + return DoubleBuffer(*res) + } + + override fun volume(): Double { + var res = 1.0 + for (i in 0 until dimension) { + if (lower[i].isInfinite() || upper[i].isInfinite()) { + return Double.POSITIVE_INFINITY + } + if (upper[i] > lower[i]) { + res *= upper[i] - lower[i] + } + } + return res + } +} \ No newline at end of file diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/domains/RealDomain.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/domains/RealDomain.kt new file mode 100644 index 000000000..89115887e --- /dev/null +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/domains/RealDomain.kt @@ -0,0 +1,65 @@ +/* + * Copyright 2015 Alexander Nozik. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package scientifik.kmath.domains + +import scientifik.kmath.linear.Point + +/** + * n-dimensional volume + * + * @author Alexander Nozik + */ +interface RealDomain: Domain { + + fun nearestInDomain(point: Point): Point + + /** + * The lower edge for the domain going down from point + * @param num + * @param point + * @return + */ + fun getLowerBound(num: Int, point: Point): Double? + + /** + * The upper edge of the domain going up from point + * @param num + * @param point + * @return + */ + fun getUpperBound(num: Int, point: Point): Double? + + /** + * Global lower edge + * @param num + * @return + */ + fun getLowerBound(num: Int): Double? + + /** + * Global upper edge + * @param num + * @return + */ + fun getUpperBound(num: Int): Double? + + /** + * Hyper volume + * @return + */ + fun volume(): Double + +} \ No newline at end of file diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/domains/UnconstrainedDomain.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/domains/UnconstrainedDomain.kt new file mode 100644 index 000000000..e49fd3b37 --- /dev/null +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/domains/UnconstrainedDomain.kt @@ -0,0 +1,36 @@ +/* + * Copyright 2015 Alexander Nozik. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package scientifik.kmath.domains + +import scientifik.kmath.linear.Point + +class UnconstrainedDomain(override val dimension: Int) : RealDomain { + + override operator fun contains(point: Point): Boolean = true + + override fun getLowerBound(num: Int, point: Point): Double? = Double.NEGATIVE_INFINITY + + override fun getLowerBound(num: Int): Double? = Double.NEGATIVE_INFINITY + + override fun getUpperBound(num: Int, point: Point): Double? = Double.POSITIVE_INFINITY + + override fun getUpperBound(num: Int): Double? = Double.POSITIVE_INFINITY + + override fun nearestInDomain(point: Point): Point = point + + override fun volume(): Double = Double.POSITIVE_INFINITY + +} \ No newline at end of file diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/domains/UnivariateDomain.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/domains/UnivariateDomain.kt new file mode 100644 index 000000000..ef521d5ea --- /dev/null +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/domains/UnivariateDomain.kt @@ -0,0 +1,48 @@ +package scientifik.kmath.domains + +import scientifik.kmath.linear.Point +import scientifik.kmath.structures.asBuffer + +inline class UnivariateDomain(val range: ClosedFloatingPointRange) : RealDomain { + + operator fun contains(d: Double): Boolean = range.contains(d) + + override operator fun contains(point: Point): Boolean { + require(point.size == 0) + return contains(point[0]) + } + + override fun nearestInDomain(point: Point): Point { + require(point.size == 1) + val value = point[0] + return when{ + value in range -> point + value >= range.endInclusive -> doubleArrayOf(range.endInclusive).asBuffer() + else -> doubleArrayOf(range.start).asBuffer() + } + } + + override fun getLowerBound(num: Int, point: Point): Double? { + require(num == 0) + return range.start + } + + override fun getUpperBound(num: Int, point: Point): Double? { + require(num == 0) + return range.endInclusive + } + + override fun getLowerBound(num: Int): Double? { + require(num == 0) + return range.start + } + + override fun getUpperBound(num: Int): Double? { + require(num == 0) + return range.endInclusive + } + + override fun volume(): Double = range.endInclusive - range.start + + override val dimension: Int get() = 1 +} \ No newline at end of file diff --git a/kmath-for-real/src/commonMain/kotlin/scientifik/kmath/real/RealVector.kt b/kmath-for-real/src/commonMain/kotlin/scientifik/kmath/real/RealVector.kt index ff4c835ed..23c7e19cb 100644 --- a/kmath-for-real/src/commonMain/kotlin/scientifik/kmath/real/RealVector.kt +++ b/kmath-for-real/src/commonMain/kotlin/scientifik/kmath/real/RealVector.kt @@ -12,26 +12,23 @@ import scientifik.kmath.structures.asBuffer import scientifik.kmath.structures.asIterable import kotlin.math.sqrt +typealias RealPoint = Point + fun DoubleArray.asVector() = RealVector(this.asBuffer()) fun List.asVector() = RealVector(this.asBuffer()) - object VectorL2Norm : Norm, Double> { override fun norm(arg: Point): Double = sqrt(arg.asIterable().sumByDouble { it.toDouble() }) } inline class RealVector(private val point: Point) : - SpaceElement, RealVector, VectorSpace>, Point { + SpaceElement>, RealPoint { - override val context: VectorSpace - get() = space( - point.size - ) + override val context: VectorSpace get() = space(point.size) - override fun unwrap(): Point = point + override fun unwrap(): RealPoint = point - override fun Point.wrap(): RealVector = - RealVector(this) + override fun RealPoint.wrap(): RealVector = RealVector(this) override val size: Int get() = point.size @@ -48,12 +45,8 @@ inline class RealVector(private val point: Point) : operator fun invoke(vararg values: Double): RealVector = values.asVector() - fun space(dim: Int): BufferVectorSpace = - spaceCache.getOrPut(dim) { - BufferVectorSpace( - dim, - RealField - ) { size, init -> Buffer.real(size, init) } - } + fun space(dim: Int): BufferVectorSpace = spaceCache.getOrPut(dim) { + BufferVectorSpace(dim, RealField) { size, init -> Buffer.real(size, init) } + } } } \ No newline at end of file diff --git a/kmath-for-real/src/commonMain/kotlin/scientifik/kmath/real/realMatrix.kt b/kmath-for-real/src/commonMain/kotlin/scientifik/kmath/real/realMatrix.kt index 813d89577..0f4ccf2a8 100644 --- a/kmath-for-real/src/commonMain/kotlin/scientifik/kmath/real/realMatrix.kt +++ b/kmath-for-real/src/commonMain/kotlin/scientifik/kmath/real/realMatrix.kt @@ -27,6 +27,10 @@ typealias RealMatrix = Matrix fun realMatrix(rowNum: Int, colNum: Int, initializer: (i: Int, j: Int) -> Double): RealMatrix = MatrixContext.real.produce(rowNum, colNum, initializer) +fun Array.toMatrix(): RealMatrix{ + return MatrixContext.real.produce(size, this[0].size) { row, col -> this[row][col] } +} + fun Sequence.toMatrix(): RealMatrix = toList().let { MatrixContext.real.produce(it.size, it[0].size) { row, col -> it[row][col] } } diff --git a/kmath-histograms/src/commonMain/kotlin/scientifik/kmath/histogram/Histogram.kt b/kmath-histograms/src/commonMain/kotlin/scientifik/kmath/histogram/Histogram.kt index 329af72a1..5199669f5 100644 --- a/kmath-histograms/src/commonMain/kotlin/scientifik/kmath/histogram/Histogram.kt +++ b/kmath-histograms/src/commonMain/kotlin/scientifik/kmath/histogram/Histogram.kt @@ -1,18 +1,10 @@ package scientifik.kmath.histogram +import scientifik.kmath.domains.Domain import scientifik.kmath.linear.Point import scientifik.kmath.structures.ArrayBuffer import scientifik.kmath.structures.DoubleBuffer -/** - * A simple geometric domain - * TODO move to geometry module - */ -interface Domain { - operator fun contains(vector: Point): Boolean - val dimension: Int -} - /** * The bin in the histogram. The histogram is by definition always done in the real space */ diff --git a/kmath-memory/src/commonMain/kotlin/scientifik/memory/MemorySpec.kt b/kmath-memory/src/commonMain/kotlin/scientifik/memory/MemorySpec.kt index 0896f0dcb..7999aa2ab 100644 --- a/kmath-memory/src/commonMain/kotlin/scientifik/memory/MemorySpec.kt +++ b/kmath-memory/src/commonMain/kotlin/scientifik/memory/MemorySpec.kt @@ -10,6 +10,7 @@ interface MemorySpec { val objectSize: Int fun MemoryReader.read(offset: Int): T + //TODO consider thread safety fun MemoryWriter.write(offset: Int, value: T) } -- 2.34.1 From ea8c0db85445cdcdaa06d8ce78f839cf0d2162d6 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Tue, 23 Jun 2020 21:46:05 +0300 Subject: [PATCH 089/156] Histogram bin fix --- .../kotlin/scientifik/kmath/histogram/RealHistogram.kt | 4 ++-- .../kotlin/scientifik/kmath/histogram/UnivariateHistogram.kt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/kmath-histograms/src/commonMain/kotlin/scientifik/kmath/histogram/RealHistogram.kt b/kmath-histograms/src/commonMain/kotlin/scientifik/kmath/histogram/RealHistogram.kt index 4438f5d60..f9d815421 100644 --- a/kmath-histograms/src/commonMain/kotlin/scientifik/kmath/histogram/RealHistogram.kt +++ b/kmath-histograms/src/commonMain/kotlin/scientifik/kmath/histogram/RealHistogram.kt @@ -1,8 +1,8 @@ package scientifik.kmath.histogram import scientifik.kmath.linear.Point -import scientifik.kmath.real.asVector import scientifik.kmath.operations.SpaceOperations +import scientifik.kmath.real.asVector import scientifik.kmath.structures.* import kotlin.math.floor @@ -21,7 +21,7 @@ data class BinDef>(val space: SpaceOperations>, val c class MultivariateBin>(val def: BinDef, override val value: Number) : Bin { - override fun contains(vector: Point): Boolean = def.contains(vector) + override fun contains(point: Point): Boolean = def.contains(point) override val dimension: Int get() = def.center.size diff --git a/kmath-histograms/src/jvmMain/kotlin/scientifik/kmath/histogram/UnivariateHistogram.kt b/kmath-histograms/src/jvmMain/kotlin/scientifik/kmath/histogram/UnivariateHistogram.kt index dcc5ac0eb..af01205bf 100644 --- a/kmath-histograms/src/jvmMain/kotlin/scientifik/kmath/histogram/UnivariateHistogram.kt +++ b/kmath-histograms/src/jvmMain/kotlin/scientifik/kmath/histogram/UnivariateHistogram.kt @@ -16,7 +16,7 @@ class UnivariateBin(val position: Double, val size: Double, val counter: LongCou operator fun contains(value: Double): Boolean = value in (position - size / 2)..(position + size / 2) - override fun contains(vector: Buffer): Boolean = contains(vector[0]) + override fun contains(point: Buffer): Boolean = contains(point[0]) internal operator fun inc() = this.also { counter.increment() } -- 2.34.1 From 9a3709624d5b415f5ae0bc9c7c3208a5dccb651e Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Wed, 24 Jun 2020 15:54:17 +0700 Subject: [PATCH 090/156] Use hashMap instead of map --- .../kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt index 77a4ee9e9..95c84ca51 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt @@ -387,7 +387,7 @@ internal class AsmBuilder internal constructor( * Maps JVM primitive numbers boxed types to their letters of JVM signature convention. */ private val SIGNATURE_LETTERS: Map, String> by lazy { - mapOf( + hashMapOf( java.lang.Byte::class.java to "B", java.lang.Short::class.java to "S", java.lang.Integer::class.java to "I", @@ -397,8 +397,8 @@ internal class AsmBuilder internal constructor( ) } - private val NUMBER_CONVERTER_METHODS by lazy { - mapOf( + private val NUMBER_CONVERTER_METHODS: Map by lazy { + hashMapOf( "B" to "byteValue", "S" to "shortValue", "I" to "intValue", -- 2.34.1 From f16ebb1682b40a54b08e41a25be1412f170e873e Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Wed, 24 Jun 2020 15:55:25 +0700 Subject: [PATCH 091/156] Remove accidentally left debug main function --- kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/asm.kt | 4 ---- 1 file changed, 4 deletions(-) diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/asm.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/asm.kt index 3ee071c77..2036ffec6 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/asm.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/asm.kt @@ -79,7 +79,3 @@ inline fun Algebra.expression(mst: MST): Expression = ms * Optimize performance of an [MSTExpression] using ASM codegen */ inline fun MSTExpression.compile(): Expression = mst.compileWith(T::class, algebra) - -fun main() { - RealField.mstInField { symbol("x") + 2 }.compile() -} -- 2.34.1 From 02f42ee56a8047af9ab7846b5e0b271084c9192a Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Wed, 24 Jun 2020 20:55:48 +0700 Subject: [PATCH 092/156] Eliminate bridging --- .../kotlin/scientifik/kmath/asm/asm.kt | 63 ++- .../kmath/asm/internal/AsmBuilder.kt | 366 ++++++++++-------- .../asm/internal/AsmCompiledExpression.kt | 1 - .../scientifik/kmath/asm/internal/classes.kt | 7 + .../kmath/asm/internal/methodVisitors.kt | 59 +-- .../kmath/asm/internal/specialization.kt | 34 +- .../scietifik/kmath/asm/TestAsmAlgebras.kt | 20 +- 7 files changed, 274 insertions(+), 276 deletions(-) create mode 100644 kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/classes.kt diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/asm.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/asm.kt index a09a7ebe3..a3af80ccd 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/asm.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/asm.kt @@ -1,8 +1,9 @@ package scientifik.kmath.asm +import org.objectweb.asm.Type import scientifik.kmath.asm.internal.AsmBuilder +import scientifik.kmath.asm.internal.buildExpectationStack import scientifik.kmath.asm.internal.buildName -import scientifik.kmath.asm.internal.hasSpecific import scientifik.kmath.asm.internal.tryInvokeSpecific import scientifik.kmath.ast.MST import scientifik.kmath.ast.MSTExpression @@ -18,6 +19,7 @@ fun MST.compileWith(type: KClass, algebra: Algebra): Expression< fun AsmBuilder.visit(node: MST) { when (node) { is MST.Symbolic -> loadVariable(node.value) + is MST.Numeric -> { val constant = if (algebra is NumericAlgebra) algebra.number(node.value) @@ -26,48 +28,49 @@ fun MST.compileWith(type: KClass, algebra: Algebra): Expression< loadTConstant(constant) } + is MST.Unary -> { loadAlgebra() - - if (!hasSpecific(algebra, node.operation, 1)) loadStringConstant(node.operation) - + if (!buildExpectationStack(algebra, node.operation, 1)) loadStringConstant(node.operation) visit(node.value) - if (!tryInvokeSpecific(algebra, node.operation, 1)) { - invokeAlgebraOperation( - owner = AsmBuilder.ALGEBRA_CLASS, - method = "unaryOperation", - descriptor = "(L${AsmBuilder.STRING_CLASS};" + - "L${AsmBuilder.OBJECT_CLASS};)" + - "L${AsmBuilder.OBJECT_CLASS};" - ) - } + if (!tryInvokeSpecific(algebra, node.operation, 1)) invokeAlgebraOperation( + owner = AsmBuilder.ALGEBRA_TYPE.internalName, + method = "unaryOperation", + + descriptor = Type.getMethodDescriptor( + AsmBuilder.OBJECT_TYPE, + AsmBuilder.STRING_TYPE, + AsmBuilder.OBJECT_TYPE + ), + + tArity = 1 + ) } is MST.Binary -> { loadAlgebra() - - if (!hasSpecific(algebra, node.operation, 2)) - loadStringConstant(node.operation) - + if (!buildExpectationStack(algebra, node.operation, 2)) loadStringConstant(node.operation) visit(node.left) visit(node.right) - if (!tryInvokeSpecific(algebra, node.operation, 2)) { + if (!tryInvokeSpecific(algebra, node.operation, 2)) invokeAlgebraOperation( + owner = AsmBuilder.ALGEBRA_TYPE.internalName, + method = "binaryOperation", - invokeAlgebraOperation( - owner = AsmBuilder.ALGEBRA_CLASS, - method = "binaryOperation", - descriptor = "(L${AsmBuilder.STRING_CLASS};" + - "L${AsmBuilder.OBJECT_CLASS};" + - "L${AsmBuilder.OBJECT_CLASS};)" + - "L${AsmBuilder.OBJECT_CLASS};" - ) - } + descriptor = Type.getMethodDescriptor( + AsmBuilder.OBJECT_TYPE, + AsmBuilder.STRING_TYPE, + AsmBuilder.OBJECT_TYPE, + AsmBuilder.OBJECT_TYPE + ), + + tArity = 2 + ) } } } - return AsmBuilder(type.java, algebra, buildName(this)) { visit(this@compileWith) }.getInstance() + return AsmBuilder(type, algebra, buildName(this)) { visit(this@compileWith) }.getInstance() } /** @@ -79,7 +82,3 @@ inline fun Algebra.expression(mst: MST): Expression = ms * Optimize performance of an [MSTExpression] using ASM codegen */ inline fun MSTExpression.compile(): Expression = mst.compileWith(T::class, algebra) - -fun main() { - RealField.mstInField { symbol("x") + 2 }.compile() -} diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt index 95c84ca51..2c9cd48d9 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt @@ -1,14 +1,17 @@ package scientifik.kmath.asm.internal -import org.objectweb.asm.ClassWriter -import org.objectweb.asm.Label -import org.objectweb.asm.MethodVisitor -import org.objectweb.asm.Opcodes +import org.objectweb.asm.* +import org.objectweb.asm.Opcodes.AALOAD +import org.objectweb.asm.Opcodes.RETURN +import org.objectweb.asm.commons.InstructionAdapter import scientifik.kmath.asm.internal.AsmBuilder.ClassLoader +import scientifik.kmath.ast.MST import scientifik.kmath.operations.Algebra +import java.util.* +import kotlin.reflect.KClass /** - * ASM Builder is a structure that abstracts building a class that unwraps [AsmExpression] to plain Java expression. + * ASM Builder is a structure that abstracts building a class designated to unwrap [MST] to plain Java expression. * This class uses [ClassLoader] for loading the generated class, then it is able to instantiate the new class. * * @param T the type of AsmExpression to unwrap. @@ -16,7 +19,7 @@ import scientifik.kmath.operations.Algebra * @param className the unique class name of new loaded class. */ internal class AsmBuilder internal constructor( - private val classOfT: Class<*>, + private val classOfT: KClass<*>, private val algebra: Algebra, private val className: String, private val invokeLabel0Visitor: AsmBuilder.() -> Unit @@ -31,16 +34,16 @@ internal class AsmBuilder internal constructor( /** * The instance of [ClassLoader] used by this builder. */ - private val classLoader: ClassLoader = - ClassLoader(javaClass.classLoader) + private val classLoader: ClassLoader = ClassLoader(javaClass.classLoader) @Suppress("PrivatePropertyName") - private val T_ALGEBRA_CLASS: String = algebra.javaClass.name.replace(oldChar = '.', newChar = '/') + private val T_ALGEBRA_TYPE: Type = algebra::class.asm @Suppress("PrivatePropertyName") - private val T_CLASS: String = classOfT.name.replace('.', '/') + internal val T_TYPE: Type = classOfT.asm - private val slashesClassName: String = className.replace(oldChar = '.', newChar = '/') + @Suppress("PrivatePropertyName") + private val CLASS_TYPE: Type = Type.getObjectType(className.replace(oldChar = '.', newChar = '/'))!! /** * Index of `this` variable in invoke method of [AsmCompiledExpression] built subclass. @@ -60,11 +63,16 @@ internal class AsmBuilder internal constructor( /** * Method visitor of `invoke` method of [AsmCompiledExpression] subclass. */ - private lateinit var invokeMethodVisitor: MethodVisitor - private var primitiveMode = false - internal var primitiveType = OBJECT_CLASS - internal var primitiveTypeSig = "L$OBJECT_CLASS;" - internal var primitiveTypeReturnSig = "L$OBJECT_CLASS;" + private lateinit var invokeMethodVisitor: InstructionAdapter + internal var primitiveMode = false + + @Suppress("PropertyName") + internal var PRIMITIVE_MASK: Type = OBJECT_TYPE + + @Suppress("PropertyName") + internal var PRIMITIVE_MASK_BOXED: Type = OBJECT_TYPE + private val typeStack = Stack() + internal val expectationStack = Stack().apply { push(T_TYPE) } /** * The cache of [AsmCompiledExpression] subclass built by this builder. @@ -80,89 +88,85 @@ internal class AsmBuilder internal constructor( fun getInstance(): AsmCompiledExpression { generatedInstance?.let { return it } - if (classOfT in SIGNATURE_LETTERS) { + if (SIGNATURE_LETTERS.containsKey(classOfT.java)) { primitiveMode = true - primitiveTypeSig = SIGNATURE_LETTERS.getValue(classOfT) - primitiveType = SIGNATURE_LETTERS.getValue(classOfT) - primitiveTypeReturnSig = "L$T_CLASS;" + PRIMITIVE_MASK = SIGNATURE_LETTERS.getValue(classOfT.java) + PRIMITIVE_MASK_BOXED = T_TYPE } val classWriter = ClassWriter(ClassWriter.COMPUTE_FRAMES) { visit( Opcodes.V1_8, Opcodes.ACC_PUBLIC or Opcodes.ACC_FINAL or Opcodes.ACC_SUPER, - slashesClassName, - "L$FUNCTIONAL_COMPILED_EXPRESSION_CLASS;", - FUNCTIONAL_COMPILED_EXPRESSION_CLASS, + CLASS_TYPE.internalName, + "L${ASM_COMPILED_EXPRESSION_TYPE.internalName}<${T_TYPE.descriptor}>;", + ASM_COMPILED_EXPRESSION_TYPE.internalName, arrayOf() ) visitMethod( - access = Opcodes.ACC_PUBLIC, - name = "", - descriptor = "(L$ALGEBRA_CLASS;[L$OBJECT_CLASS;)V", - signature = null, - exceptions = null - ) { + Opcodes.ACC_PUBLIC, + "", + Type.getMethodDescriptor(Type.VOID_TYPE, ALGEBRA_TYPE, OBJECT_ARRAY_TYPE), + null, + null + ).instructionAdapter { val thisVar = 0 val algebraVar = 1 val constantsVar = 2 val l0 = Label() visitLabel(l0) - visitLoadObjectVar(thisVar) - visitLoadObjectVar(algebraVar) - visitLoadObjectVar(constantsVar) + load(thisVar, CLASS_TYPE) + load(algebraVar, ALGEBRA_TYPE) + load(constantsVar, OBJECT_ARRAY_TYPE) - visitInvokeSpecial( - FUNCTIONAL_COMPILED_EXPRESSION_CLASS, + invokespecial( + ASM_COMPILED_EXPRESSION_TYPE.internalName, "", - "(L$ALGEBRA_CLASS;[L$OBJECT_CLASS;)V" + Type.getMethodDescriptor(Type.VOID_TYPE, ALGEBRA_TYPE, OBJECT_ARRAY_TYPE), + false ) val l1 = Label() visitLabel(l1) - visitReturn() + visitInsn(RETURN) val l2 = Label() visitLabel(l2) - visitLocalVariable("this", "L$slashesClassName;", null, l0, l2, thisVar) + visitLocalVariable("this", CLASS_TYPE.descriptor, null, l0, l2, thisVar) visitLocalVariable( "algebra", - "L$ALGEBRA_CLASS;", - "L$ALGEBRA_CLASS;", + ALGEBRA_TYPE.descriptor, + "L${ALGEBRA_TYPE.internalName}<${T_TYPE.descriptor}>;", l0, l2, algebraVar ) - visitLocalVariable("constants", "[L$OBJECT_CLASS;", null, l0, l2, constantsVar) + visitLocalVariable("constants", OBJECT_ARRAY_TYPE.descriptor, null, l0, l2, constantsVar) visitMaxs(0, 3) visitEnd() } visitMethod( - access = Opcodes.ACC_PUBLIC or Opcodes.ACC_FINAL, - name = "invoke", - descriptor = "(L$MAP_CLASS;)L$T_CLASS;", - signature = "(L$MAP_CLASS;)L$T_CLASS;", - exceptions = null - ) { + Opcodes.ACC_PUBLIC or Opcodes.ACC_FINAL, + "invoke", + Type.getMethodDescriptor(T_TYPE, MAP_TYPE), + "(L${MAP_TYPE.internalName}<${STRING_TYPE.descriptor}+${T_TYPE.descriptor}>;)${T_TYPE.descriptor}", + null + ).instructionAdapter { invokeMethodVisitor = this visitCode() val l0 = Label() visitLabel(l0) invokeLabel0Visitor() - - if (primitiveMode) - boxPrimitiveToNumber() - - visitReturnObject() + areturn(T_TYPE) val l1 = Label() visitLabel(l1) visitLocalVariable( "this", - "L$slashesClassName;", + CLASS_TYPE.descriptor, null, l0, l1, @@ -171,8 +175,8 @@ internal class AsmBuilder internal constructor( visitLocalVariable( "arguments", - "L$MAP_CLASS;", - "L$MAP_CLASS;", + MAP_TYPE.descriptor, + "L${MAP_TYPE.internalName}<${STRING_TYPE.descriptor}+${T_TYPE.descriptor}>;", l0, l1, invokeArgumentsVar @@ -183,28 +187,28 @@ internal class AsmBuilder internal constructor( } visitMethod( - access = Opcodes.ACC_PUBLIC or Opcodes.ACC_FINAL or Opcodes.ACC_BRIDGE or Opcodes.ACC_SYNTHETIC, - name = "invoke", - descriptor = "(L$MAP_CLASS;)L$OBJECT_CLASS;", - signature = null, - exceptions = null - ) { + Opcodes.ACC_PUBLIC or Opcodes.ACC_FINAL or Opcodes.ACC_BRIDGE or Opcodes.ACC_SYNTHETIC, + "invoke", + Type.getMethodDescriptor(OBJECT_TYPE, MAP_TYPE), + null, + null + ).instructionAdapter { val thisVar = 0 val argumentsVar = 1 visitCode() val l0 = Label() visitLabel(l0) - visitLoadObjectVar(thisVar) - visitLoadObjectVar(argumentsVar) - visitInvokeVirtual(owner = slashesClassName, name = "invoke", descriptor = "(L$MAP_CLASS;)L$T_CLASS;") - visitReturnObject() + load(thisVar, OBJECT_TYPE) + load(argumentsVar, MAP_TYPE) + invokevirtual(CLASS_TYPE.internalName, "invoke", Type.getMethodDescriptor(T_TYPE, MAP_TYPE), false) + areturn(T_TYPE) val l1 = Label() visitLabel(l1) visitLocalVariable( "this", - "L$slashesClassName;", - T_CLASS, + CLASS_TYPE.descriptor, + null, l0, l1, thisVar @@ -231,116 +235,111 @@ internal class AsmBuilder internal constructor( * Loads a constant from */ internal fun loadTConstant(value: T) { - if (primitiveMode) { - loadNumberConstant(value as Number) + if (classOfT.java in INLINABLE_NUMBERS) { + val expectedType = expectationStack.pop()!! + val mustBeBoxed = expectedType.sort == Type.OBJECT + loadNumberConstant(value as Number, mustBeBoxed) + if (mustBeBoxed) typeStack.push(T_TYPE) else typeStack.push(PRIMITIVE_MASK) return } - if (classOfT in INLINABLE_NUMBERS) { - loadNumberConstant(value as Number) - invokeMethodVisitor.visitCheckCast(T_CLASS) - return - } - - loadConstant(value as Any, T_CLASS) + loadConstant(value as Any, T_TYPE) } - /** - * Loads an object constant [value] stored in [AsmCompiledExpression.constants] and casts it to [type]. - */ - private fun unboxInPrimitiveMode() { - if (!primitiveMode) - return + private fun box(): Unit = invokeMethodVisitor.invokestatic( + T_TYPE.internalName, + "valueOf", + Type.getMethodDescriptor(T_TYPE, PRIMITIVE_MASK), + false + ) - unboxNumberToPrimitive() - } + private fun unbox(): Unit = invokeMethodVisitor.invokevirtual( + NUMBER_TYPE.internalName, + NUMBER_CONVERTER_METHODS.getValue(PRIMITIVE_MASK), + Type.getMethodDescriptor(PRIMITIVE_MASK), + false + ) - private fun boxPrimitiveToNumber() { - invokeMethodVisitor.visitInvokeStatic(T_CLASS, "valueOf", "($primitiveTypeSig)L$T_CLASS;") - } - - private fun unboxNumberToPrimitive() { - invokeMethodVisitor.visitInvokeVirtual( - owner = NUMBER_CLASS, - name = NUMBER_CONVERTER_METHODS.getValue(primitiveType), - descriptor = "()${primitiveTypeSig}" - ) - } - - private fun loadConstant(value: Any, type: String) { + private fun loadConstant(value: Any, type: Type): Unit = invokeMethodVisitor.run { val idx = if (value in constants) constants.indexOf(value) else constants.apply { add(value) }.lastIndex - - invokeMethodVisitor.run { - loadThis() - visitGetField(owner = slashesClassName, name = "constants", descriptor = "[L$OBJECT_CLASS;") - visitLdcOrIntConstant(idx) - visitGetObjectArrayElement() - invokeMethodVisitor.visitCheckCast(type) - } + loadThis() + getfield(CLASS_TYPE.internalName, "constants", OBJECT_ARRAY_TYPE.descriptor) + iconst(idx) + visitInsn(AALOAD) + checkcast(type) } - private fun loadThis(): Unit = invokeMethodVisitor.visitLoadObjectVar(invokeThisVar) + private fun loadThis(): Unit = invokeMethodVisitor.load(invokeThisVar, CLASS_TYPE) /** - * Either loads a numeric constant [value] from [AsmCompiledExpression.constants] field or boxes a primitive + * Either loads a numeric constant [value] from [AsmCompiledExpression] constants field or boxes a primitive * constant from the constant pool (some numbers with special opcodes like [Opcodes.ICONST_0] aren't even loaded * from it). */ - private fun loadNumberConstant(value: Number) { - val clazz = value.javaClass - val c = clazz.name.replace('.', '/') - val sigLetter = SIGNATURE_LETTERS[clazz] + private fun loadNumberConstant(value: Number, mustBeBoxed: Boolean) { + val boxed = value::class.asm + val primitive = BOXED_TO_PRIMITIVES[boxed] - if (sigLetter != null) { - when (value) { - is Byte -> invokeMethodVisitor.visitLdcOrIntConstant(value.toInt()) - is Short -> invokeMethodVisitor.visitLdcOrIntConstant(value.toInt()) - is Int -> invokeMethodVisitor.visitLdcOrIntConstant(value) - is Double -> invokeMethodVisitor.visitLdcOrDoubleConstant(value) - is Float -> invokeMethodVisitor.visitLdcOrFloatConstant(value) - is Long -> invokeMethodVisitor.visitLdcOrLongConstant(value) - else -> invokeMethodVisitor.visitLdcInsn(value) + if (primitive != null) { + when (primitive) { + Type.BYTE_TYPE -> invokeMethodVisitor.iconst(value.toInt()) + Type.DOUBLE_TYPE -> invokeMethodVisitor.dconst(value.toDouble()) + Type.FLOAT_TYPE -> invokeMethodVisitor.fconst(value.toFloat()) + Type.LONG_TYPE -> invokeMethodVisitor.lconst(value.toLong()) + Type.INT_TYPE -> invokeMethodVisitor.iconst(value.toInt()) + Type.SHORT_TYPE -> invokeMethodVisitor.iconst(value.toInt()) } - if (!primitiveMode) - invokeMethodVisitor.visitInvokeStatic(c, "valueOf", "($sigLetter)L${c};") + if (mustBeBoxed) { + box() + invokeMethodVisitor.checkcast(T_TYPE) + } return } - loadConstant(value, c) + loadConstant(value, boxed) + if (!mustBeBoxed) unbox() + else invokeMethodVisitor.checkcast(T_TYPE) } /** * Loads a variable [name] from [AsmCompiledExpression.invoke] [Map] parameter. The [defaultValue] may be provided. */ internal fun loadVariable(name: String, defaultValue: T? = null): Unit = invokeMethodVisitor.run { - visitLoadObjectVar(invokeArgumentsVar) + load(invokeArgumentsVar, OBJECT_ARRAY_TYPE) if (defaultValue != null) { - visitLdcInsn(name) + loadStringConstant(name) loadTConstant(defaultValue) - visitInvokeInterface( - owner = MAP_CLASS, - name = "getOrDefault", - descriptor = "(L$OBJECT_CLASS;L$OBJECT_CLASS;)L$OBJECT_CLASS;" + invokeinterface( + MAP_TYPE.internalName, + "getOrDefault", + Type.getMethodDescriptor(OBJECT_TYPE, OBJECT_TYPE, OBJECT_TYPE) ) - invokeMethodVisitor.visitCheckCast(T_CLASS) + invokeMethodVisitor.checkcast(T_TYPE) return } - visitLdcInsn(name) + loadStringConstant(name) - visitInvokeInterface( - owner = MAP_CLASS, - name = "get", - descriptor = "(L$OBJECT_CLASS;)L$OBJECT_CLASS;" + invokeinterface( + MAP_TYPE.internalName, + "get", + Type.getMethodDescriptor(OBJECT_TYPE, OBJECT_TYPE) ) - invokeMethodVisitor.visitCheckCast(T_CLASS) - unboxInPrimitiveMode() + invokeMethodVisitor.checkcast(T_TYPE) + val expectedType = expectationStack.pop()!! + + if (expectedType.sort == Type.OBJECT) + typeStack.push(T_TYPE) + else { + unbox() + typeStack.push(PRIMITIVE_MASK) + } } /** @@ -349,13 +348,10 @@ internal class AsmBuilder internal constructor( internal fun loadAlgebra() { loadThis() - invokeMethodVisitor.visitGetField( - owner = FUNCTIONAL_COMPILED_EXPRESSION_CLASS, - name = "algebra", - descriptor = "L$ALGEBRA_CLASS;" - ) - - invokeMethodVisitor.visitCheckCast(T_ALGEBRA_CLASS) + invokeMethodVisitor.run { + getfield(ASM_COMPILED_EXPRESSION_TYPE.internalName, "algebra", ALGEBRA_TYPE.descriptor) + checkcast(T_ALGEBRA_TYPE) + } } /** @@ -369,42 +365,70 @@ internal class AsmBuilder internal constructor( owner: String, method: String, descriptor: String, - opcode: Int = Opcodes.INVOKEINTERFACE, - isInterface: Boolean = true + tArity: Int, + opcode: Int = Opcodes.INVOKEINTERFACE ) { - invokeMethodVisitor.visitMethodInsn(opcode, owner, method, descriptor, isInterface) - invokeMethodVisitor.visitCheckCast(T_CLASS) - unboxInPrimitiveMode() + repeat(tArity) { typeStack.pop() } + + invokeMethodVisitor.visitMethodInsn( + opcode, + owner, + method, + descriptor, + opcode == Opcodes.INVOKEINTERFACE + ) + + invokeMethodVisitor.checkcast(T_TYPE) + val isLastExpr = expectationStack.size == 1 + val expectedType = expectationStack.pop()!! + + if (expectedType.sort == Type.OBJECT || isLastExpr) + typeStack.push(T_TYPE) + else { + unbox() + typeStack.push(PRIMITIVE_MASK) + } } /** * Writes a LDC Instruction with string constant provided. */ - internal fun loadStringConstant(string: String): Unit = invokeMethodVisitor.visitLdcInsn(string) + internal fun loadStringConstant(string: String): Unit = invokeMethodVisitor.aconst(string) internal companion object { /** * Maps JVM primitive numbers boxed types to their letters of JVM signature convention. */ - private val SIGNATURE_LETTERS: Map, String> by lazy { + private val SIGNATURE_LETTERS: Map, Type> by lazy { hashMapOf( - java.lang.Byte::class.java to "B", - java.lang.Short::class.java to "S", - java.lang.Integer::class.java to "I", - java.lang.Long::class.java to "J", - java.lang.Float::class.java to "F", - java.lang.Double::class.java to "D" + java.lang.Byte::class.java to Type.BYTE_TYPE, + java.lang.Short::class.java to Type.SHORT_TYPE, + java.lang.Integer::class.java to Type.INT_TYPE, + java.lang.Long::class.java to Type.LONG_TYPE, + java.lang.Float::class.java to Type.FLOAT_TYPE, + java.lang.Double::class.java to Type.DOUBLE_TYPE ) } - private val NUMBER_CONVERTER_METHODS: Map by lazy { + private val BOXED_TO_PRIMITIVES: Map by lazy { hashMapOf( - "B" to "byteValue", - "S" to "shortValue", - "I" to "intValue", - "J" to "longValue", - "F" to "floatValue", - "D" to "doubleValue" + java.lang.Byte::class.asm to Type.BYTE_TYPE, + java.lang.Short::class.asm to Type.SHORT_TYPE, + java.lang.Integer::class.asm to Type.INT_TYPE, + java.lang.Long::class.asm to Type.LONG_TYPE, + java.lang.Float::class.asm to Type.FLOAT_TYPE, + java.lang.Double::class.asm to Type.DOUBLE_TYPE + ) + } + + private val NUMBER_CONVERTER_METHODS: Map by lazy { + hashMapOf( + Type.BYTE_TYPE to "byteValue", + Type.SHORT_TYPE to "shortValue", + Type.INT_TYPE to "intValue", + Type.LONG_TYPE to "longValue", + Type.FLOAT_TYPE to "floatValue", + Type.DOUBLE_TYPE to "doubleValue" ) } @@ -412,14 +436,14 @@ internal class AsmBuilder internal constructor( * Provides boxed number types values of which can be stored in JVM bytecode constant pool. */ private val INLINABLE_NUMBERS: Set> by lazy { SIGNATURE_LETTERS.keys } + internal val ASM_COMPILED_EXPRESSION_TYPE: Type = AsmCompiledExpression::class.asm + internal val NUMBER_TYPE: Type = java.lang.Number::class.asm + internal val MAP_TYPE: Type = java.util.Map::class.asm + internal val OBJECT_TYPE: Type = java.lang.Object::class.asm - internal const val FUNCTIONAL_COMPILED_EXPRESSION_CLASS = - "scientifik/kmath/asm/internal/AsmCompiledExpression" - - internal const val NUMBER_CLASS = "java/lang/Number" - internal const val MAP_CLASS = "java/util/Map" - internal const val OBJECT_CLASS = "java/lang/Object" - internal const val ALGEBRA_CLASS = "scientifik/kmath/operations/Algebra" - internal const val STRING_CLASS = "java/lang/String" + @Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN", "RemoveRedundantQualifierName") + internal val OBJECT_ARRAY_TYPE: Type = Array::class.asm + internal val ALGEBRA_TYPE: Type = Algebra::class.asm + internal val STRING_TYPE: Type = java.lang.String::class.asm } } diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmCompiledExpression.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmCompiledExpression.kt index a5236927c..7c4a9fc99 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmCompiledExpression.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmCompiledExpression.kt @@ -16,4 +16,3 @@ internal abstract class AsmCompiledExpression internal constructor( ) : Expression { abstract override fun invoke(arguments: Map): T } - diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/classes.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/classes.kt new file mode 100644 index 000000000..dc0b35531 --- /dev/null +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/classes.kt @@ -0,0 +1,7 @@ +package scientifik.kmath.asm.internal + +import org.objectweb.asm.Type +import kotlin.reflect.KClass + +internal val KClass<*>.asm: Type + get() = Type.getType(java) diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/methodVisitors.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/methodVisitors.kt index 6ecce8833..7b0d346b7 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/methodVisitors.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/methodVisitors.kt @@ -1,60 +1,9 @@ package scientifik.kmath.asm.internal import org.objectweb.asm.MethodVisitor -import org.objectweb.asm.Opcodes.* +import org.objectweb.asm.commons.InstructionAdapter -internal fun MethodVisitor.visitLdcOrIntConstant(value: Int): Unit = when (value) { - -1 -> visitInsn(ICONST_M1) - 0 -> visitInsn(ICONST_0) - 1 -> visitInsn(ICONST_1) - 2 -> visitInsn(ICONST_2) - 3 -> visitInsn(ICONST_3) - 4 -> visitInsn(ICONST_4) - 5 -> visitInsn(ICONST_5) - in -128..127 -> visitIntInsn(BIPUSH, value) - in -32768..32767 -> visitIntInsn(SIPUSH, value) - else -> visitLdcInsn(value) -} +fun MethodVisitor.instructionAdapter(): InstructionAdapter = InstructionAdapter(this) -internal fun MethodVisitor.visitLdcOrDoubleConstant(value: Double): Unit = when (value) { - 0.0 -> visitInsn(DCONST_0) - 1.0 -> visitInsn(DCONST_1) - else -> visitLdcInsn(value) -} - -internal fun MethodVisitor.visitLdcOrLongConstant(value: Long): Unit = when (value) { - 0L -> visitInsn(LCONST_0) - 1L -> visitInsn(LCONST_1) - else -> visitLdcInsn(value) -} - -internal fun MethodVisitor.visitLdcOrFloatConstant(value: Float): Unit = when (value) { - 0f -> visitInsn(FCONST_0) - 1f -> visitInsn(FCONST_1) - 2f -> visitInsn(FCONST_2) - else -> visitLdcInsn(value) -} - -internal fun MethodVisitor.visitInvokeInterface(owner: String, name: String, descriptor: String): Unit = - visitMethodInsn(INVOKEINTERFACE, owner, name, descriptor, true) - -internal fun MethodVisitor.visitInvokeVirtual(owner: String, name: String, descriptor: String): Unit = - visitMethodInsn(INVOKEVIRTUAL, owner, name, descriptor, false) - -internal fun MethodVisitor.visitInvokeStatic(owner: String, name: String, descriptor: String): Unit = - visitMethodInsn(INVOKESTATIC, owner, name, descriptor, false) - -internal fun MethodVisitor.visitInvokeSpecial(owner: String, name: String, descriptor: String): Unit = - visitMethodInsn(INVOKESPECIAL, owner, name, descriptor, false) - -internal fun MethodVisitor.visitCheckCast(type: String): Unit = visitTypeInsn(CHECKCAST, type) - -internal fun MethodVisitor.visitGetField(owner: String, name: String, descriptor: String): Unit = - visitFieldInsn(GETFIELD, owner, name, descriptor) - -internal fun MethodVisitor.visitLoadObjectVar(`var`: Int): Unit = visitVarInsn(ALOAD, `var`) - -internal fun MethodVisitor.visitGetObjectArrayElement(): Unit = visitInsn(AALOAD) - -internal fun MethodVisitor.visitReturn(): Unit = visitInsn(RETURN) -internal fun MethodVisitor.visitReturnObject(): Unit = visitInsn(ARETURN) +fun MethodVisitor.instructionAdapter(block: InstructionAdapter.() -> Unit): InstructionAdapter = + instructionAdapter().apply(block) diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/specialization.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/specialization.kt index 51d056f8c..2e15a1a93 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/specialization.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/specialization.kt @@ -1,6 +1,7 @@ package scientifik.kmath.asm.internal import org.objectweb.asm.Opcodes +import org.objectweb.asm.Type import scientifik.kmath.operations.Algebra private val methodNameAdapters: Map by lazy { @@ -12,17 +13,19 @@ private val methodNameAdapters: Map by lazy { } /** - * Checks if the target [context] for code generation contains a method with needed [name] and [arity]. + * Checks if the target [context] for code generation contains a method with needed [name] and [arity], also builds + * type expectation stack for needed arity. * * @return `true` if contains, else `false`. */ -internal fun hasSpecific(context: Algebra, name: String, arity: Int): Boolean { +internal fun AsmBuilder.buildExpectationStack(context: Algebra, name: String, arity: Int): Boolean { val aName = methodNameAdapters[name] ?: name - context.javaClass.methods.find { it.name == aName && it.parameters.size == arity } - ?: return false + val hasSpecific = context.javaClass.methods.find { it.name == aName && it.parameters.size == arity } != null + val t = if (primitiveMode && hasSpecific) PRIMITIVE_MASK else T_TYPE + repeat(arity) { expectationStack.push(t) } - return true + return hasSpecific } /** @@ -34,22 +37,23 @@ internal fun hasSpecific(context: Algebra, name: String, arity: Int): Boo internal fun AsmBuilder.tryInvokeSpecific(context: Algebra, name: String, arity: Int): Boolean { val aName = methodNameAdapters[name] ?: name - context.javaClass.methods.find { it.name == aName && it.parameters.size == arity } - ?: return false + val method = + context.javaClass.methods.find { + var suitableSignature = it.name == aName && it.parameters.size == arity + + if (primitiveMode && it.isBridge) + suitableSignature = false + + suitableSignature + } ?: return false val owner = context::class.java.name.replace('.', '/') - val sig = buildString { - append('(') - repeat(arity) { append(primitiveTypeSig) } - append(')') - append(primitiveTypeReturnSig) - } - invokeAlgebraOperation( owner = owner, method = aName, - descriptor = sig, + descriptor = Type.getMethodDescriptor(PRIMITIVE_MASK_BOXED, *Array(arity) { PRIMITIVE_MASK }), + tArity = arity, opcode = Opcodes.INVOKEVIRTUAL ) diff --git a/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/asm/TestAsmAlgebras.kt b/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/asm/TestAsmAlgebras.kt index 294da32e9..ce5b7cbf9 100644 --- a/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/asm/TestAsmAlgebras.kt +++ b/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/asm/TestAsmAlgebras.kt @@ -10,7 +10,23 @@ import kotlin.test.assertEquals class TestAsmAlgebras { @Test fun space() { - val res = ByteRing.mstInSpace { + val res1 = ByteRing.mstInSpace { + binaryOperation( + "+", + + unaryOperation( + "+", + number(3.toByte()) - (number(2.toByte()) + (multiply( + add(number(1), number(1)), + 2 + ) + number(1.toByte()) * 3.toByte() - number(1.toByte()))) + ), + + number(1) + ) + symbol("x") + zero + }("x" to 2.toByte()) + + val res2 = ByteRing.mstInSpace { binaryOperation( "+", @@ -26,7 +42,7 @@ class TestAsmAlgebras { ) + symbol("x") + zero }.compile()("x" to 2.toByte()) - assertEquals(16, res) + assertEquals(res1, res2) } // @Test -- 2.34.1 From fffc75215313dd580e1b23ddd32eca04e84cc7b1 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Wed, 24 Jun 2020 21:17:06 +0700 Subject: [PATCH 093/156] Add more tests and improve current, fix type stack underflow exception --- .../kmath/asm/internal/AsmBuilder.kt | 2 +- .../scietifik/kmath/asm/TestAsmAlgebras.kt | 123 +++++++++--------- .../scietifik/kmath/asm/TestAsmExpressions.kt | 7 + .../kotlin/scietifik/kmath/ast/AsmTest.kt | 15 ++- .../kotlin/scietifik/kmath/ast/ParserTest.kt | 12 +- 5 files changed, 92 insertions(+), 67 deletions(-) diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt index 2c9cd48d9..8f45c4044 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt @@ -368,7 +368,7 @@ internal class AsmBuilder internal constructor( tArity: Int, opcode: Int = Opcodes.INVOKEINTERFACE ) { - repeat(tArity) { typeStack.pop() } + repeat(tArity) { if (!typeStack.empty()) typeStack.pop() } invokeMethodVisitor.visitMethodInsn( opcode, diff --git a/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/asm/TestAsmAlgebras.kt b/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/asm/TestAsmAlgebras.kt index ce5b7cbf9..4c2be811e 100644 --- a/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/asm/TestAsmAlgebras.kt +++ b/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/asm/TestAsmAlgebras.kt @@ -1,9 +1,12 @@ package scietifik.kmath.asm import scientifik.kmath.asm.compile +import scientifik.kmath.ast.mstInField +import scientifik.kmath.ast.mstInRing import scientifik.kmath.ast.mstInSpace import scientifik.kmath.expressions.invoke import scientifik.kmath.operations.ByteRing +import scientifik.kmath.operations.RealField import kotlin.test.Test import kotlin.test.assertEquals @@ -45,65 +48,63 @@ class TestAsmAlgebras { assertEquals(res1, res2) } -// @Test -// fun space() { -// val res = ByteRing.asm { -// binaryOperation( -// "+", -// -// unaryOperation( -// "+", -// 3.toByte() - (2.toByte() + (multiply( -// add(number(1), number(1)), -// 2 -// ) + 1.toByte()) * 3.toByte() - 1.toByte()) -// ), -// -// number(1) -// ) + symbol("x") + zero -// }("x" to 2.toByte()) -// -// assertEquals(16, res) -// } -// -// @Test -// fun ring() { -// val res = ByteRing.asmInRing { -// binaryOperation( -// "+", -// -// unaryOperation( -// "+", -// (3.toByte() - (2.toByte() + (multiply( -// add(const(1), const(1)), -// 2 -// ) + 1.toByte()))) * 3.0 - 1.toByte() -// ), -// -// number(1) -// ) * const(2) -// }() -// -// assertEquals(24, res) -// } -// -// @Test -// fun field() { -// val res = RealField.asmInField { -// +(3 - 2 + 2*(number(1)+1.0) -// -// unaryOperation( -// "+", -// (3.0 - (2.0 + (multiply( -// add((1.0), const(1.0)), -// 2 -// ) + 1.0))) * 3 - 1.0 -// )+ -// -// number(1) -// ) / 2, const(2.0)) * one -// }() -// -// assertEquals(3.0, res) -// } + @Test + fun ring() { + val res1 = ByteRing.mstInRing { + binaryOperation( + "+", + + unaryOperation( + "+", + (symbol("x") - (2.toByte() + (multiply( + add(number(1), number(1)), + 2 + ) + 1.toByte()))) * 3.0 - 1.toByte() + ), + + number(1) + ) * number(2) + }("x" to 3.toByte()) + + val res2 = ByteRing.mstInRing { + binaryOperation( + "+", + + unaryOperation( + "+", + (symbol("x") - (2.toByte() + (multiply( + add(number(1), number(1)), + 2 + ) + 1.toByte()))) * 3.0 - 1.toByte() + ), + + number(1) + ) * number(2) + }.compile()("x" to 3.toByte()) + + assertEquals(res1, res2) + } + + @Test + fun field() { + val res1 = RealField.mstInField { + +(3 - 2 + 2 * number(1) + 1.0) + binaryOperation( + "+", + (3.0 - (symbol("x") + (multiply(add(number(1.0), number(1.0)), 2) + 1.0))) * 3 - 1.0 + + number(1), + 1 / 2 + number(2.0) * one + ) + }("x" to 2.0) + + val res2 = RealField.mstInField { + +(3 - 2 + 2 * number(1) + 1.0) + binaryOperation( + "+", + (3.0 - (symbol("x") + (multiply(add(number(1.0), number(1.0)), 2) + 1.0))) * 3 - 1.0 + + number(1), + 1 / 2 + number(2.0) * one + ) + }.compile()("x" to 2.0) + + assertEquals(res1, res2) + } } diff --git a/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/asm/TestAsmExpressions.kt b/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/asm/TestAsmExpressions.kt index 82af1a927..824201aa7 100644 --- a/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/asm/TestAsmExpressions.kt +++ b/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/asm/TestAsmExpressions.kt @@ -16,6 +16,13 @@ class TestAsmExpressions { assertEquals(-2.0, res) } + @Test + fun testBinaryOperationInvocation() { + val expression = RealField.mstInSpace { -symbol("x") + number(1.0) }.compile() + val res = expression("x" to 2.0) + assertEquals(-1.0, res) + } + @Test fun testConstProductInvocation() { val res = RealField.mstInField { symbol("x") * 2 }("x" to 2.0) diff --git a/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/AsmTest.kt b/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/AsmTest.kt index f0f9a8bc1..08d7fff47 100644 --- a/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/AsmTest.kt +++ b/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/AsmTest.kt @@ -1,7 +1,10 @@ package scietifik.kmath.ast -import scientifik.kmath.ast.evaluate +import scientifik.kmath.asm.compile +import scientifik.kmath.asm.expression +import scientifik.kmath.ast.mstInField import scientifik.kmath.ast.parseMath +import scientifik.kmath.expressions.invoke import scientifik.kmath.operations.Complex import scientifik.kmath.operations.ComplexField import kotlin.test.Test @@ -9,9 +12,15 @@ import kotlin.test.assertEquals class AsmTest { @Test - fun parsedExpression() { + fun `compile MST`() { val mst = "2+2*(2+2)".parseMath() - val res = ComplexField.evaluate(mst) + val res = ComplexField.expression(mst)() + assertEquals(Complex(10.0, 0.0), res) + } + + @Test + fun `compile MSTExpression`() { + val res = ComplexField.mstInField { number(2) + number(2) * (number(2) + number(2)) }.compile()() assertEquals(Complex(10.0, 0.0), res) } } diff --git a/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/ParserTest.kt b/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/ParserTest.kt index 06546302e..5394a4b00 100644 --- a/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/ParserTest.kt +++ b/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/ParserTest.kt @@ -1,17 +1,25 @@ package scietifik.kmath.ast import scientifik.kmath.ast.evaluate +import scientifik.kmath.ast.mstInField import scientifik.kmath.ast.parseMath +import scientifik.kmath.expressions.invoke import scientifik.kmath.operations.Complex import scientifik.kmath.operations.ComplexField -import kotlin.test.assertEquals import kotlin.test.Test +import kotlin.test.assertEquals internal class ParserTest { @Test - fun parsedExpression() { + fun `evaluate MST`() { val mst = "2+2*(2+2)".parseMath() val res = ComplexField.evaluate(mst) assertEquals(Complex(10.0, 0.0), res) } + + @Test + fun `evaluate MSTExpression`() { + val res = ComplexField.mstInField { number(2) + number(2) * (number(2) + number(2)) }() + assertEquals(Complex(10.0, 0.0), res) + } } -- 2.34.1 From e47ec1aeb9587b0f04f54f99d90c4da1df933152 Mon Sep 17 00:00:00 2001 From: Commander Tvis Date: Thu, 25 Jun 2020 10:07:36 +0700 Subject: [PATCH 094/156] Delete AsmCompiledExpression abstract class, implement dynamic field generation to reduce quantity of cast instructions, minor refactor and renaming of internal APIs --- kmath-ast/README.md | 18 +- .../kmath/asm/internal/AsmBuilder.kt | 220 +++++++++--------- .../asm/internal/AsmCompiledExpression.kt | 18 -- .../kmath/asm/internal/buildName.kt | 3 +- .../kmath/asm/internal/classWriters.kt | 12 +- .../kmath/asm/internal/instructionAdapters.kt | 10 + .../kmath/asm/internal/methodVisitors.kt | 4 +- .../kmath/asm/internal/specialization.kt | 2 +- 8 files changed, 140 insertions(+), 147 deletions(-) delete mode 100644 kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmCompiledExpression.kt create mode 100644 kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/instructionAdapters.kt diff --git a/kmath-ast/README.md b/kmath-ast/README.md index b5ca5886f..4563e17cf 100644 --- a/kmath-ast/README.md +++ b/kmath-ast/README.md @@ -24,20 +24,20 @@ For example, the following builder: package scientifik.kmath.asm.generated; import java.util.Map; -import scientifik.kmath.asm.internal.AsmCompiledExpression; -import scientifik.kmath.operations.Algebra; +import scientifik.kmath.expressions.Expression; import scientifik.kmath.operations.RealField; -// The class's name is build with MST's hash-code and collision fixing number. -public final class AsmCompiledExpression_45045_0 extends AsmCompiledExpression { - // Plain constructor - public AsmCompiledExpression_45045_0(Algebra algebra, Object[] constants) { - super(algebra, constants); +public final class AsmCompiledExpression_1073786867_0 implements Expression { + private final RealField algebra; + private final Object[] constants; + + public AsmCompiledExpression_1073786867_0(RealField algebra, Object[] constants) { + this.algebra = algebra; + this.constants = constants; } - // The actual dynamic code: public final Double invoke(Map arguments) { - return (Double)((RealField)super.algebra).add((Double)arguments.get("x"), (Double)2.0D); + return (Double)this.algebra.add(((Double)arguments.get("x")).doubleValue(), 2.0D); } } ``` diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt index 8f45c4044..536d6136d 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt @@ -6,6 +6,7 @@ import org.objectweb.asm.Opcodes.RETURN import org.objectweb.asm.commons.InstructionAdapter import scientifik.kmath.asm.internal.AsmBuilder.ClassLoader import scientifik.kmath.ast.MST +import scientifik.kmath.expressions.Expression import scientifik.kmath.operations.Algebra import java.util.* import kotlin.reflect.KClass @@ -36,32 +37,27 @@ internal class AsmBuilder internal constructor( */ private val classLoader: ClassLoader = ClassLoader(javaClass.classLoader) - @Suppress("PrivatePropertyName") - private val T_ALGEBRA_TYPE: Type = algebra::class.asm - - @Suppress("PrivatePropertyName") - internal val T_TYPE: Type = classOfT.asm - - @Suppress("PrivatePropertyName") - private val CLASS_TYPE: Type = Type.getObjectType(className.replace(oldChar = '.', newChar = '/'))!! + private val tAlgebraType: Type = algebra::class.asm + internal val tType: Type = classOfT.asm + private val classType: Type = Type.getObjectType(className.replace(oldChar = '.', newChar = '/'))!! /** - * Index of `this` variable in invoke method of [AsmCompiledExpression] built subclass. + * Index of `this` variable in invoke method of the built subclass. */ private val invokeThisVar: Int = 0 /** - * Index of `arguments` variable in invoke method of [AsmCompiledExpression] built subclass. + * Index of `arguments` variable in invoke method of the built subclass. */ private val invokeArgumentsVar: Int = 1 /** - * List of constants to provide to [AsmCompiledExpression] subclass. + * List of constants to provide to the subclass. */ private val constants: MutableList = mutableListOf() /** - * Method visitor of `invoke` method of [AsmCompiledExpression] subclass. + * Method visitor of `invoke` method of the subclass. */ private lateinit var invokeMethodVisitor: InstructionAdapter internal var primitiveMode = false @@ -72,78 +68,92 @@ internal class AsmBuilder internal constructor( @Suppress("PropertyName") internal var PRIMITIVE_MASK_BOXED: Type = OBJECT_TYPE private val typeStack = Stack() - internal val expectationStack = Stack().apply { push(T_TYPE) } + internal val expectationStack: Stack = Stack().apply { push(tType) } /** - * The cache of [AsmCompiledExpression] subclass built by this builder. + * The cache for instance built by this builder. */ - private var generatedInstance: AsmCompiledExpression? = null + private var generatedInstance: Expression? = null /** - * Subclasses, loads and instantiates the [AsmCompiledExpression] for given parameters. + * Subclasses, loads and instantiates [Expression] for given parameters. * * The built instance is cached. */ @Suppress("UNCHECKED_CAST") - fun getInstance(): AsmCompiledExpression { + fun getInstance(): Expression { generatedInstance?.let { return it } - if (SIGNATURE_LETTERS.containsKey(classOfT.java)) { + if (SIGNATURE_LETTERS.containsKey(classOfT)) { primitiveMode = true - PRIMITIVE_MASK = SIGNATURE_LETTERS.getValue(classOfT.java) - PRIMITIVE_MASK_BOXED = T_TYPE + PRIMITIVE_MASK = SIGNATURE_LETTERS.getValue(classOfT) + PRIMITIVE_MASK_BOXED = tType } val classWriter = ClassWriter(ClassWriter.COMPUTE_FRAMES) { visit( Opcodes.V1_8, Opcodes.ACC_PUBLIC or Opcodes.ACC_FINAL or Opcodes.ACC_SUPER, - CLASS_TYPE.internalName, - "L${ASM_COMPILED_EXPRESSION_TYPE.internalName}<${T_TYPE.descriptor}>;", - ASM_COMPILED_EXPRESSION_TYPE.internalName, - arrayOf() + classType.internalName, + "${OBJECT_TYPE.descriptor}L${EXPRESSION_TYPE.internalName}<${tType.descriptor}>;", + OBJECT_TYPE.internalName, + arrayOf(EXPRESSION_TYPE.internalName) + ) + + visitField( + access = Opcodes.ACC_PRIVATE or Opcodes.ACC_FINAL, + name = "algebra", + descriptor = tAlgebraType.descriptor, + signature = null, + value = null, + block = FieldVisitor::visitEnd + ) + + visitField( + access = Opcodes.ACC_PRIVATE or Opcodes.ACC_FINAL, + name = "constants", + descriptor = OBJECT_ARRAY_TYPE.descriptor, + signature = null, + value = null, + block = FieldVisitor::visitEnd ) visitMethod( Opcodes.ACC_PUBLIC, "", - Type.getMethodDescriptor(Type.VOID_TYPE, ALGEBRA_TYPE, OBJECT_ARRAY_TYPE), + Type.getMethodDescriptor(Type.VOID_TYPE, tAlgebraType, OBJECT_ARRAY_TYPE), null, null ).instructionAdapter { val thisVar = 0 val algebraVar = 1 val constantsVar = 2 - val l0 = Label() - visitLabel(l0) - load(thisVar, CLASS_TYPE) - load(algebraVar, ALGEBRA_TYPE) + val l0 = label() + load(thisVar, classType) + invokespecial(OBJECT_TYPE.internalName, "", Type.getMethodDescriptor(Type.VOID_TYPE), false) + label() + load(thisVar, classType) + load(algebraVar, tAlgebraType) + putfield(classType.internalName, "algebra", tAlgebraType.descriptor) + label() + load(thisVar, classType) load(constantsVar, OBJECT_ARRAY_TYPE) - - invokespecial( - ASM_COMPILED_EXPRESSION_TYPE.internalName, - "", - Type.getMethodDescriptor(Type.VOID_TYPE, ALGEBRA_TYPE, OBJECT_ARRAY_TYPE), - false - ) - - val l1 = Label() - visitLabel(l1) + putfield(classType.internalName, "constants", OBJECT_ARRAY_TYPE.descriptor) + label() visitInsn(RETURN) - val l2 = Label() - visitLabel(l2) - visitLocalVariable("this", CLASS_TYPE.descriptor, null, l0, l2, thisVar) + val l4 = label() + visitLocalVariable("this", classType.descriptor, null, l0, l4, thisVar) visitLocalVariable( "algebra", - ALGEBRA_TYPE.descriptor, - "L${ALGEBRA_TYPE.internalName}<${T_TYPE.descriptor}>;", + tAlgebraType.descriptor, + null, l0, - l2, + l4, algebraVar ) - visitLocalVariable("constants", OBJECT_ARRAY_TYPE.descriptor, null, l0, l2, constantsVar) + visitLocalVariable("constants", OBJECT_ARRAY_TYPE.descriptor, null, l0, l4, constantsVar) visitMaxs(0, 3) visitEnd() } @@ -151,22 +161,20 @@ internal class AsmBuilder internal constructor( visitMethod( Opcodes.ACC_PUBLIC or Opcodes.ACC_FINAL, "invoke", - Type.getMethodDescriptor(T_TYPE, MAP_TYPE), - "(L${MAP_TYPE.internalName}<${STRING_TYPE.descriptor}+${T_TYPE.descriptor}>;)${T_TYPE.descriptor}", + Type.getMethodDescriptor(tType, MAP_TYPE), + "(L${MAP_TYPE.internalName}<${STRING_TYPE.descriptor}+${tType.descriptor}>;)${tType.descriptor}", null ).instructionAdapter { invokeMethodVisitor = this visitCode() - val l0 = Label() - visitLabel(l0) + val l0 = label() invokeLabel0Visitor() - areturn(T_TYPE) - val l1 = Label() - visitLabel(l1) + areturn(tType) + val l1 = label() visitLocalVariable( "this", - CLASS_TYPE.descriptor, + classType.descriptor, null, l0, l1, @@ -176,7 +184,7 @@ internal class AsmBuilder internal constructor( visitLocalVariable( "arguments", MAP_TYPE.descriptor, - "L${MAP_TYPE.internalName}<${STRING_TYPE.descriptor}+${T_TYPE.descriptor}>;", + "L${MAP_TYPE.internalName}<${STRING_TYPE.descriptor}+${tType.descriptor}>;", l0, l1, invokeArgumentsVar @@ -196,18 +204,16 @@ internal class AsmBuilder internal constructor( val thisVar = 0 val argumentsVar = 1 visitCode() - val l0 = Label() - visitLabel(l0) + val l0 = label() load(thisVar, OBJECT_TYPE) load(argumentsVar, MAP_TYPE) - invokevirtual(CLASS_TYPE.internalName, "invoke", Type.getMethodDescriptor(T_TYPE, MAP_TYPE), false) - areturn(T_TYPE) - val l1 = Label() - visitLabel(l1) + invokevirtual(classType.internalName, "invoke", Type.getMethodDescriptor(tType, MAP_TYPE), false) + areturn(tType) + val l1 = label() visitLocalVariable( "this", - CLASS_TYPE.descriptor, + classType.descriptor, null, l0, l1, @@ -225,7 +231,7 @@ internal class AsmBuilder internal constructor( .defineClass(className, classWriter.toByteArray()) .constructors .first() - .newInstance(algebra, constants.toTypedArray()) as AsmCompiledExpression + .newInstance(algebra, constants.toTypedArray()) as Expression generatedInstance = new return new @@ -235,21 +241,21 @@ internal class AsmBuilder internal constructor( * Loads a constant from */ internal fun loadTConstant(value: T) { - if (classOfT.java in INLINABLE_NUMBERS) { + if (classOfT in INLINABLE_NUMBERS) { val expectedType = expectationStack.pop()!! val mustBeBoxed = expectedType.sort == Type.OBJECT loadNumberConstant(value as Number, mustBeBoxed) - if (mustBeBoxed) typeStack.push(T_TYPE) else typeStack.push(PRIMITIVE_MASK) + if (mustBeBoxed) typeStack.push(tType) else typeStack.push(PRIMITIVE_MASK) return } - loadConstant(value as Any, T_TYPE) + loadConstant(value as Any, tType) } private fun box(): Unit = invokeMethodVisitor.invokestatic( - T_TYPE.internalName, + tType.internalName, "valueOf", - Type.getMethodDescriptor(T_TYPE, PRIMITIVE_MASK), + Type.getMethodDescriptor(tType, PRIMITIVE_MASK), false ) @@ -263,16 +269,16 @@ internal class AsmBuilder internal constructor( private fun loadConstant(value: Any, type: Type): Unit = invokeMethodVisitor.run { val idx = if (value in constants) constants.indexOf(value) else constants.apply { add(value) }.lastIndex loadThis() - getfield(CLASS_TYPE.internalName, "constants", OBJECT_ARRAY_TYPE.descriptor) + getfield(classType.internalName, "constants", OBJECT_ARRAY_TYPE.descriptor) iconst(idx) visitInsn(AALOAD) checkcast(type) } - private fun loadThis(): Unit = invokeMethodVisitor.load(invokeThisVar, CLASS_TYPE) + private fun loadThis(): Unit = invokeMethodVisitor.load(invokeThisVar, classType) /** - * Either loads a numeric constant [value] from [AsmCompiledExpression] constants field or boxes a primitive + * Either loads a numeric constant [value] from the class's constants field or boxes a primitive * constant from the constant pool (some numbers with special opcodes like [Opcodes.ICONST_0] aren't even loaded * from it). */ @@ -292,7 +298,7 @@ internal class AsmBuilder internal constructor( if (mustBeBoxed) { box() - invokeMethodVisitor.checkcast(T_TYPE) + invokeMethodVisitor.checkcast(tType) } return @@ -300,11 +306,11 @@ internal class AsmBuilder internal constructor( loadConstant(value, boxed) if (!mustBeBoxed) unbox() - else invokeMethodVisitor.checkcast(T_TYPE) + else invokeMethodVisitor.checkcast(tType) } /** - * Loads a variable [name] from [AsmCompiledExpression.invoke] [Map] parameter. The [defaultValue] may be provided. + * Loads a variable [name] arguments [Map] parameter of [Expression.invoke]. The [defaultValue] may be provided. */ internal fun loadVariable(name: String, defaultValue: T? = null): Unit = invokeMethodVisitor.run { load(invokeArgumentsVar, OBJECT_ARRAY_TYPE) @@ -319,7 +325,7 @@ internal class AsmBuilder internal constructor( Type.getMethodDescriptor(OBJECT_TYPE, OBJECT_TYPE, OBJECT_TYPE) ) - invokeMethodVisitor.checkcast(T_TYPE) + invokeMethodVisitor.checkcast(tType) return } @@ -331,11 +337,11 @@ internal class AsmBuilder internal constructor( Type.getMethodDescriptor(OBJECT_TYPE, OBJECT_TYPE) ) - invokeMethodVisitor.checkcast(T_TYPE) + invokeMethodVisitor.checkcast(tType) val expectedType = expectationStack.pop()!! if (expectedType.sort == Type.OBJECT) - typeStack.push(T_TYPE) + typeStack.push(tType) else { unbox() typeStack.push(PRIMITIVE_MASK) @@ -343,15 +349,11 @@ internal class AsmBuilder internal constructor( } /** - * Loads algebra from according field of [AsmCompiledExpression] and casts it to class of [algebra] provided. + * Loads algebra from according field of the class and casts it to class of [algebra] provided. */ internal fun loadAlgebra() { loadThis() - - invokeMethodVisitor.run { - getfield(ASM_COMPILED_EXPRESSION_TYPE.internalName, "algebra", ALGEBRA_TYPE.descriptor) - checkcast(T_ALGEBRA_TYPE) - } + invokeMethodVisitor.getfield(classType.internalName, "algebra", tAlgebraType.descriptor) } /** @@ -368,7 +370,12 @@ internal class AsmBuilder internal constructor( tArity: Int, opcode: Int = Opcodes.INVOKEINTERFACE ) { - repeat(tArity) { if (!typeStack.empty()) typeStack.pop() } + run loop@{ + repeat(tArity) { + if (typeStack.empty()) return@loop + typeStack.pop() + } + } invokeMethodVisitor.visitMethodInsn( opcode, @@ -378,12 +385,12 @@ internal class AsmBuilder internal constructor( opcode == Opcodes.INVOKEINTERFACE ) - invokeMethodVisitor.checkcast(T_TYPE) + invokeMethodVisitor.checkcast(tType) val isLastExpr = expectationStack.size == 1 val expectedType = expectationStack.pop()!! if (expectedType.sort == Type.OBJECT || isLastExpr) - typeStack.push(T_TYPE) + typeStack.push(tType) else { unbox() typeStack.push(PRIMITIVE_MASK) @@ -399,27 +406,18 @@ internal class AsmBuilder internal constructor( /** * Maps JVM primitive numbers boxed types to their letters of JVM signature convention. */ - private val SIGNATURE_LETTERS: Map, Type> by lazy { + private val SIGNATURE_LETTERS: Map, Type> by lazy { hashMapOf( - java.lang.Byte::class.java to Type.BYTE_TYPE, - java.lang.Short::class.java to Type.SHORT_TYPE, - java.lang.Integer::class.java to Type.INT_TYPE, - java.lang.Long::class.java to Type.LONG_TYPE, - java.lang.Float::class.java to Type.FLOAT_TYPE, - java.lang.Double::class.java to Type.DOUBLE_TYPE + java.lang.Byte::class to Type.BYTE_TYPE, + java.lang.Short::class to Type.SHORT_TYPE, + java.lang.Integer::class to Type.INT_TYPE, + java.lang.Long::class to Type.LONG_TYPE, + java.lang.Float::class to Type.FLOAT_TYPE, + java.lang.Double::class to Type.DOUBLE_TYPE ) } - private val BOXED_TO_PRIMITIVES: Map by lazy { - hashMapOf( - java.lang.Byte::class.asm to Type.BYTE_TYPE, - java.lang.Short::class.asm to Type.SHORT_TYPE, - java.lang.Integer::class.asm to Type.INT_TYPE, - java.lang.Long::class.asm to Type.LONG_TYPE, - java.lang.Float::class.asm to Type.FLOAT_TYPE, - java.lang.Double::class.asm to Type.DOUBLE_TYPE - ) - } + private val BOXED_TO_PRIMITIVES: Map by lazy { SIGNATURE_LETTERS.mapKeys { (k, _) -> k.asm } } private val NUMBER_CONVERTER_METHODS: Map by lazy { hashMapOf( @@ -435,15 +433,15 @@ internal class AsmBuilder internal constructor( /** * Provides boxed number types values of which can be stored in JVM bytecode constant pool. */ - private val INLINABLE_NUMBERS: Set> by lazy { SIGNATURE_LETTERS.keys } - internal val ASM_COMPILED_EXPRESSION_TYPE: Type = AsmCompiledExpression::class.asm - internal val NUMBER_TYPE: Type = java.lang.Number::class.asm - internal val MAP_TYPE: Type = java.util.Map::class.asm - internal val OBJECT_TYPE: Type = java.lang.Object::class.asm + private val INLINABLE_NUMBERS: Set> by lazy { SIGNATURE_LETTERS.keys } + internal val EXPRESSION_TYPE: Type by lazy { Expression::class.asm } + internal val NUMBER_TYPE: Type by lazy { java.lang.Number::class.asm } + internal val MAP_TYPE: Type by lazy { java.util.Map::class.asm } + internal val OBJECT_TYPE: Type by lazy { java.lang.Object::class.asm } @Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN", "RemoveRedundantQualifierName") - internal val OBJECT_ARRAY_TYPE: Type = Array::class.asm - internal val ALGEBRA_TYPE: Type = Algebra::class.asm - internal val STRING_TYPE: Type = java.lang.String::class.asm + internal val OBJECT_ARRAY_TYPE: Type by lazy { Array::class.asm } + internal val ALGEBRA_TYPE: Type by lazy { Algebra::class.asm } + internal val STRING_TYPE: Type by lazy { java.lang.String::class.asm } } } diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmCompiledExpression.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmCompiledExpression.kt deleted file mode 100644 index 7c4a9fc99..000000000 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmCompiledExpression.kt +++ /dev/null @@ -1,18 +0,0 @@ -package scientifik.kmath.asm.internal - -import scientifik.kmath.expressions.Expression -import scientifik.kmath.operations.Algebra - -/** - * [Expression] partial implementation to have it subclassed by actual implementations. Provides unified storage for - * objects needed to implement the expression. - * - * @property algebra the algebra to delegate calls. - * @property constants the constants array to have persistent objects to reference in [invoke]. - */ -internal abstract class AsmCompiledExpression internal constructor( - @JvmField protected val algebra: Algebra, - @JvmField protected val constants: Array -) : Expression { - abstract override fun invoke(arguments: Map): T -} diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/buildName.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/buildName.kt index 66bd039c3..41dbf5807 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/buildName.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/buildName.kt @@ -1,9 +1,10 @@ package scientifik.kmath.asm.internal import scientifik.kmath.ast.MST +import scientifik.kmath.expressions.Expression /** - * Creates a class name for [AsmCompiledExpression] subclassed to implement [mst] provided. + * Creates a class name for [Expression] subclassed to implement [mst] provided. * * This methods helps to avoid collisions of class name to prevent loading several classes with the same name. If there * is a colliding class, change [collision] parameter or leave it `0` to check existing classes recursively. diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/classWriters.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/classWriters.kt index 95d713b18..af5c1049d 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/classWriters.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/classWriters.kt @@ -1,15 +1,17 @@ package scientifik.kmath.asm.internal import org.objectweb.asm.ClassWriter +import org.objectweb.asm.FieldVisitor import org.objectweb.asm.MethodVisitor -internal inline fun ClassWriter(flags: Int, block: ClassWriter.() -> Unit): ClassWriter = ClassWriter(flags).apply(block) +internal inline fun ClassWriter(flags: Int, block: ClassWriter.() -> Unit): ClassWriter = + ClassWriter(flags).apply(block) -internal inline fun ClassWriter.visitMethod( +internal inline fun ClassWriter.visitField( access: Int, name: String, descriptor: String, signature: String?, - exceptions: Array?, - block: MethodVisitor.() -> Unit -): MethodVisitor = visitMethod(access, name, descriptor, signature, exceptions).apply(block) + value: Any?, + block: FieldVisitor.() -> Unit +): FieldVisitor = visitField(access, name, descriptor, signature, value).apply(block) diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/instructionAdapters.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/instructionAdapters.kt new file mode 100644 index 000000000..f47293687 --- /dev/null +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/instructionAdapters.kt @@ -0,0 +1,10 @@ +package scientifik.kmath.asm.internal + +import org.objectweb.asm.Label +import org.objectweb.asm.commons.InstructionAdapter + +internal fun InstructionAdapter.label(): Label { + val l = Label() + visitLabel(l) + return l +} diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/methodVisitors.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/methodVisitors.kt index 7b0d346b7..aaae02ebb 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/methodVisitors.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/methodVisitors.kt @@ -3,7 +3,7 @@ package scientifik.kmath.asm.internal import org.objectweb.asm.MethodVisitor import org.objectweb.asm.commons.InstructionAdapter -fun MethodVisitor.instructionAdapter(): InstructionAdapter = InstructionAdapter(this) +internal fun MethodVisitor.instructionAdapter(): InstructionAdapter = InstructionAdapter(this) -fun MethodVisitor.instructionAdapter(block: InstructionAdapter.() -> Unit): InstructionAdapter = +internal fun MethodVisitor.instructionAdapter(block: InstructionAdapter.() -> Unit): InstructionAdapter = instructionAdapter().apply(block) diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/specialization.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/specialization.kt index 2e15a1a93..4c7a0d57e 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/specialization.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/specialization.kt @@ -22,7 +22,7 @@ internal fun AsmBuilder.buildExpectationStack(context: Algebra, name: val aName = methodNameAdapters[name] ?: name val hasSpecific = context.javaClass.methods.find { it.name == aName && it.parameters.size == arity } != null - val t = if (primitiveMode && hasSpecific) PRIMITIVE_MASK else T_TYPE + val t = if (primitiveMode && hasSpecific) PRIMITIVE_MASK else tType repeat(arity) { expectationStack.push(t) } return hasSpecific -- 2.34.1 From f7f9ce7817cb66c8b5af6e0a590c5332dc3dc1f0 Mon Sep 17 00:00:00 2001 From: Commander Tvis Date: Thu, 25 Jun 2020 10:07:36 +0700 Subject: [PATCH 095/156] Delete AsmCompiledExpression abstract class, implement dynamic field generation to reduce quantity of cast instructions, minor refactor and renaming of internal APIs --- kmath-ast/README.md | 18 +- .../kmath/asm/internal/AsmBuilder.kt | 220 +++++++++--------- .../asm/internal/AsmCompiledExpression.kt | 18 -- .../kmath/asm/internal/buildName.kt | 3 +- .../kmath/asm/internal/classWriters.kt | 12 +- .../kmath/asm/internal/instructionAdapters.kt | 10 + .../kmath/asm/internal/methodVisitors.kt | 4 +- .../kmath/asm/internal/specialization.kt | 2 +- 8 files changed, 140 insertions(+), 147 deletions(-) delete mode 100644 kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmCompiledExpression.kt create mode 100644 kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/instructionAdapters.kt diff --git a/kmath-ast/README.md b/kmath-ast/README.md index b5ca5886f..4563e17cf 100644 --- a/kmath-ast/README.md +++ b/kmath-ast/README.md @@ -24,20 +24,20 @@ For example, the following builder: package scientifik.kmath.asm.generated; import java.util.Map; -import scientifik.kmath.asm.internal.AsmCompiledExpression; -import scientifik.kmath.operations.Algebra; +import scientifik.kmath.expressions.Expression; import scientifik.kmath.operations.RealField; -// The class's name is build with MST's hash-code and collision fixing number. -public final class AsmCompiledExpression_45045_0 extends AsmCompiledExpression { - // Plain constructor - public AsmCompiledExpression_45045_0(Algebra algebra, Object[] constants) { - super(algebra, constants); +public final class AsmCompiledExpression_1073786867_0 implements Expression { + private final RealField algebra; + private final Object[] constants; + + public AsmCompiledExpression_1073786867_0(RealField algebra, Object[] constants) { + this.algebra = algebra; + this.constants = constants; } - // The actual dynamic code: public final Double invoke(Map arguments) { - return (Double)((RealField)super.algebra).add((Double)arguments.get("x"), (Double)2.0D); + return (Double)this.algebra.add(((Double)arguments.get("x")).doubleValue(), 2.0D); } } ``` diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt index 8f45c4044..536d6136d 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt @@ -6,6 +6,7 @@ import org.objectweb.asm.Opcodes.RETURN import org.objectweb.asm.commons.InstructionAdapter import scientifik.kmath.asm.internal.AsmBuilder.ClassLoader import scientifik.kmath.ast.MST +import scientifik.kmath.expressions.Expression import scientifik.kmath.operations.Algebra import java.util.* import kotlin.reflect.KClass @@ -36,32 +37,27 @@ internal class AsmBuilder internal constructor( */ private val classLoader: ClassLoader = ClassLoader(javaClass.classLoader) - @Suppress("PrivatePropertyName") - private val T_ALGEBRA_TYPE: Type = algebra::class.asm - - @Suppress("PrivatePropertyName") - internal val T_TYPE: Type = classOfT.asm - - @Suppress("PrivatePropertyName") - private val CLASS_TYPE: Type = Type.getObjectType(className.replace(oldChar = '.', newChar = '/'))!! + private val tAlgebraType: Type = algebra::class.asm + internal val tType: Type = classOfT.asm + private val classType: Type = Type.getObjectType(className.replace(oldChar = '.', newChar = '/'))!! /** - * Index of `this` variable in invoke method of [AsmCompiledExpression] built subclass. + * Index of `this` variable in invoke method of the built subclass. */ private val invokeThisVar: Int = 0 /** - * Index of `arguments` variable in invoke method of [AsmCompiledExpression] built subclass. + * Index of `arguments` variable in invoke method of the built subclass. */ private val invokeArgumentsVar: Int = 1 /** - * List of constants to provide to [AsmCompiledExpression] subclass. + * List of constants to provide to the subclass. */ private val constants: MutableList = mutableListOf() /** - * Method visitor of `invoke` method of [AsmCompiledExpression] subclass. + * Method visitor of `invoke` method of the subclass. */ private lateinit var invokeMethodVisitor: InstructionAdapter internal var primitiveMode = false @@ -72,78 +68,92 @@ internal class AsmBuilder internal constructor( @Suppress("PropertyName") internal var PRIMITIVE_MASK_BOXED: Type = OBJECT_TYPE private val typeStack = Stack() - internal val expectationStack = Stack().apply { push(T_TYPE) } + internal val expectationStack: Stack = Stack().apply { push(tType) } /** - * The cache of [AsmCompiledExpression] subclass built by this builder. + * The cache for instance built by this builder. */ - private var generatedInstance: AsmCompiledExpression? = null + private var generatedInstance: Expression? = null /** - * Subclasses, loads and instantiates the [AsmCompiledExpression] for given parameters. + * Subclasses, loads and instantiates [Expression] for given parameters. * * The built instance is cached. */ @Suppress("UNCHECKED_CAST") - fun getInstance(): AsmCompiledExpression { + fun getInstance(): Expression { generatedInstance?.let { return it } - if (SIGNATURE_LETTERS.containsKey(classOfT.java)) { + if (SIGNATURE_LETTERS.containsKey(classOfT)) { primitiveMode = true - PRIMITIVE_MASK = SIGNATURE_LETTERS.getValue(classOfT.java) - PRIMITIVE_MASK_BOXED = T_TYPE + PRIMITIVE_MASK = SIGNATURE_LETTERS.getValue(classOfT) + PRIMITIVE_MASK_BOXED = tType } val classWriter = ClassWriter(ClassWriter.COMPUTE_FRAMES) { visit( Opcodes.V1_8, Opcodes.ACC_PUBLIC or Opcodes.ACC_FINAL or Opcodes.ACC_SUPER, - CLASS_TYPE.internalName, - "L${ASM_COMPILED_EXPRESSION_TYPE.internalName}<${T_TYPE.descriptor}>;", - ASM_COMPILED_EXPRESSION_TYPE.internalName, - arrayOf() + classType.internalName, + "${OBJECT_TYPE.descriptor}L${EXPRESSION_TYPE.internalName}<${tType.descriptor}>;", + OBJECT_TYPE.internalName, + arrayOf(EXPRESSION_TYPE.internalName) + ) + + visitField( + access = Opcodes.ACC_PRIVATE or Opcodes.ACC_FINAL, + name = "algebra", + descriptor = tAlgebraType.descriptor, + signature = null, + value = null, + block = FieldVisitor::visitEnd + ) + + visitField( + access = Opcodes.ACC_PRIVATE or Opcodes.ACC_FINAL, + name = "constants", + descriptor = OBJECT_ARRAY_TYPE.descriptor, + signature = null, + value = null, + block = FieldVisitor::visitEnd ) visitMethod( Opcodes.ACC_PUBLIC, "", - Type.getMethodDescriptor(Type.VOID_TYPE, ALGEBRA_TYPE, OBJECT_ARRAY_TYPE), + Type.getMethodDescriptor(Type.VOID_TYPE, tAlgebraType, OBJECT_ARRAY_TYPE), null, null ).instructionAdapter { val thisVar = 0 val algebraVar = 1 val constantsVar = 2 - val l0 = Label() - visitLabel(l0) - load(thisVar, CLASS_TYPE) - load(algebraVar, ALGEBRA_TYPE) + val l0 = label() + load(thisVar, classType) + invokespecial(OBJECT_TYPE.internalName, "", Type.getMethodDescriptor(Type.VOID_TYPE), false) + label() + load(thisVar, classType) + load(algebraVar, tAlgebraType) + putfield(classType.internalName, "algebra", tAlgebraType.descriptor) + label() + load(thisVar, classType) load(constantsVar, OBJECT_ARRAY_TYPE) - - invokespecial( - ASM_COMPILED_EXPRESSION_TYPE.internalName, - "", - Type.getMethodDescriptor(Type.VOID_TYPE, ALGEBRA_TYPE, OBJECT_ARRAY_TYPE), - false - ) - - val l1 = Label() - visitLabel(l1) + putfield(classType.internalName, "constants", OBJECT_ARRAY_TYPE.descriptor) + label() visitInsn(RETURN) - val l2 = Label() - visitLabel(l2) - visitLocalVariable("this", CLASS_TYPE.descriptor, null, l0, l2, thisVar) + val l4 = label() + visitLocalVariable("this", classType.descriptor, null, l0, l4, thisVar) visitLocalVariable( "algebra", - ALGEBRA_TYPE.descriptor, - "L${ALGEBRA_TYPE.internalName}<${T_TYPE.descriptor}>;", + tAlgebraType.descriptor, + null, l0, - l2, + l4, algebraVar ) - visitLocalVariable("constants", OBJECT_ARRAY_TYPE.descriptor, null, l0, l2, constantsVar) + visitLocalVariable("constants", OBJECT_ARRAY_TYPE.descriptor, null, l0, l4, constantsVar) visitMaxs(0, 3) visitEnd() } @@ -151,22 +161,20 @@ internal class AsmBuilder internal constructor( visitMethod( Opcodes.ACC_PUBLIC or Opcodes.ACC_FINAL, "invoke", - Type.getMethodDescriptor(T_TYPE, MAP_TYPE), - "(L${MAP_TYPE.internalName}<${STRING_TYPE.descriptor}+${T_TYPE.descriptor}>;)${T_TYPE.descriptor}", + Type.getMethodDescriptor(tType, MAP_TYPE), + "(L${MAP_TYPE.internalName}<${STRING_TYPE.descriptor}+${tType.descriptor}>;)${tType.descriptor}", null ).instructionAdapter { invokeMethodVisitor = this visitCode() - val l0 = Label() - visitLabel(l0) + val l0 = label() invokeLabel0Visitor() - areturn(T_TYPE) - val l1 = Label() - visitLabel(l1) + areturn(tType) + val l1 = label() visitLocalVariable( "this", - CLASS_TYPE.descriptor, + classType.descriptor, null, l0, l1, @@ -176,7 +184,7 @@ internal class AsmBuilder internal constructor( visitLocalVariable( "arguments", MAP_TYPE.descriptor, - "L${MAP_TYPE.internalName}<${STRING_TYPE.descriptor}+${T_TYPE.descriptor}>;", + "L${MAP_TYPE.internalName}<${STRING_TYPE.descriptor}+${tType.descriptor}>;", l0, l1, invokeArgumentsVar @@ -196,18 +204,16 @@ internal class AsmBuilder internal constructor( val thisVar = 0 val argumentsVar = 1 visitCode() - val l0 = Label() - visitLabel(l0) + val l0 = label() load(thisVar, OBJECT_TYPE) load(argumentsVar, MAP_TYPE) - invokevirtual(CLASS_TYPE.internalName, "invoke", Type.getMethodDescriptor(T_TYPE, MAP_TYPE), false) - areturn(T_TYPE) - val l1 = Label() - visitLabel(l1) + invokevirtual(classType.internalName, "invoke", Type.getMethodDescriptor(tType, MAP_TYPE), false) + areturn(tType) + val l1 = label() visitLocalVariable( "this", - CLASS_TYPE.descriptor, + classType.descriptor, null, l0, l1, @@ -225,7 +231,7 @@ internal class AsmBuilder internal constructor( .defineClass(className, classWriter.toByteArray()) .constructors .first() - .newInstance(algebra, constants.toTypedArray()) as AsmCompiledExpression + .newInstance(algebra, constants.toTypedArray()) as Expression generatedInstance = new return new @@ -235,21 +241,21 @@ internal class AsmBuilder internal constructor( * Loads a constant from */ internal fun loadTConstant(value: T) { - if (classOfT.java in INLINABLE_NUMBERS) { + if (classOfT in INLINABLE_NUMBERS) { val expectedType = expectationStack.pop()!! val mustBeBoxed = expectedType.sort == Type.OBJECT loadNumberConstant(value as Number, mustBeBoxed) - if (mustBeBoxed) typeStack.push(T_TYPE) else typeStack.push(PRIMITIVE_MASK) + if (mustBeBoxed) typeStack.push(tType) else typeStack.push(PRIMITIVE_MASK) return } - loadConstant(value as Any, T_TYPE) + loadConstant(value as Any, tType) } private fun box(): Unit = invokeMethodVisitor.invokestatic( - T_TYPE.internalName, + tType.internalName, "valueOf", - Type.getMethodDescriptor(T_TYPE, PRIMITIVE_MASK), + Type.getMethodDescriptor(tType, PRIMITIVE_MASK), false ) @@ -263,16 +269,16 @@ internal class AsmBuilder internal constructor( private fun loadConstant(value: Any, type: Type): Unit = invokeMethodVisitor.run { val idx = if (value in constants) constants.indexOf(value) else constants.apply { add(value) }.lastIndex loadThis() - getfield(CLASS_TYPE.internalName, "constants", OBJECT_ARRAY_TYPE.descriptor) + getfield(classType.internalName, "constants", OBJECT_ARRAY_TYPE.descriptor) iconst(idx) visitInsn(AALOAD) checkcast(type) } - private fun loadThis(): Unit = invokeMethodVisitor.load(invokeThisVar, CLASS_TYPE) + private fun loadThis(): Unit = invokeMethodVisitor.load(invokeThisVar, classType) /** - * Either loads a numeric constant [value] from [AsmCompiledExpression] constants field or boxes a primitive + * Either loads a numeric constant [value] from the class's constants field or boxes a primitive * constant from the constant pool (some numbers with special opcodes like [Opcodes.ICONST_0] aren't even loaded * from it). */ @@ -292,7 +298,7 @@ internal class AsmBuilder internal constructor( if (mustBeBoxed) { box() - invokeMethodVisitor.checkcast(T_TYPE) + invokeMethodVisitor.checkcast(tType) } return @@ -300,11 +306,11 @@ internal class AsmBuilder internal constructor( loadConstant(value, boxed) if (!mustBeBoxed) unbox() - else invokeMethodVisitor.checkcast(T_TYPE) + else invokeMethodVisitor.checkcast(tType) } /** - * Loads a variable [name] from [AsmCompiledExpression.invoke] [Map] parameter. The [defaultValue] may be provided. + * Loads a variable [name] arguments [Map] parameter of [Expression.invoke]. The [defaultValue] may be provided. */ internal fun loadVariable(name: String, defaultValue: T? = null): Unit = invokeMethodVisitor.run { load(invokeArgumentsVar, OBJECT_ARRAY_TYPE) @@ -319,7 +325,7 @@ internal class AsmBuilder internal constructor( Type.getMethodDescriptor(OBJECT_TYPE, OBJECT_TYPE, OBJECT_TYPE) ) - invokeMethodVisitor.checkcast(T_TYPE) + invokeMethodVisitor.checkcast(tType) return } @@ -331,11 +337,11 @@ internal class AsmBuilder internal constructor( Type.getMethodDescriptor(OBJECT_TYPE, OBJECT_TYPE) ) - invokeMethodVisitor.checkcast(T_TYPE) + invokeMethodVisitor.checkcast(tType) val expectedType = expectationStack.pop()!! if (expectedType.sort == Type.OBJECT) - typeStack.push(T_TYPE) + typeStack.push(tType) else { unbox() typeStack.push(PRIMITIVE_MASK) @@ -343,15 +349,11 @@ internal class AsmBuilder internal constructor( } /** - * Loads algebra from according field of [AsmCompiledExpression] and casts it to class of [algebra] provided. + * Loads algebra from according field of the class and casts it to class of [algebra] provided. */ internal fun loadAlgebra() { loadThis() - - invokeMethodVisitor.run { - getfield(ASM_COMPILED_EXPRESSION_TYPE.internalName, "algebra", ALGEBRA_TYPE.descriptor) - checkcast(T_ALGEBRA_TYPE) - } + invokeMethodVisitor.getfield(classType.internalName, "algebra", tAlgebraType.descriptor) } /** @@ -368,7 +370,12 @@ internal class AsmBuilder internal constructor( tArity: Int, opcode: Int = Opcodes.INVOKEINTERFACE ) { - repeat(tArity) { if (!typeStack.empty()) typeStack.pop() } + run loop@{ + repeat(tArity) { + if (typeStack.empty()) return@loop + typeStack.pop() + } + } invokeMethodVisitor.visitMethodInsn( opcode, @@ -378,12 +385,12 @@ internal class AsmBuilder internal constructor( opcode == Opcodes.INVOKEINTERFACE ) - invokeMethodVisitor.checkcast(T_TYPE) + invokeMethodVisitor.checkcast(tType) val isLastExpr = expectationStack.size == 1 val expectedType = expectationStack.pop()!! if (expectedType.sort == Type.OBJECT || isLastExpr) - typeStack.push(T_TYPE) + typeStack.push(tType) else { unbox() typeStack.push(PRIMITIVE_MASK) @@ -399,27 +406,18 @@ internal class AsmBuilder internal constructor( /** * Maps JVM primitive numbers boxed types to their letters of JVM signature convention. */ - private val SIGNATURE_LETTERS: Map, Type> by lazy { + private val SIGNATURE_LETTERS: Map, Type> by lazy { hashMapOf( - java.lang.Byte::class.java to Type.BYTE_TYPE, - java.lang.Short::class.java to Type.SHORT_TYPE, - java.lang.Integer::class.java to Type.INT_TYPE, - java.lang.Long::class.java to Type.LONG_TYPE, - java.lang.Float::class.java to Type.FLOAT_TYPE, - java.lang.Double::class.java to Type.DOUBLE_TYPE + java.lang.Byte::class to Type.BYTE_TYPE, + java.lang.Short::class to Type.SHORT_TYPE, + java.lang.Integer::class to Type.INT_TYPE, + java.lang.Long::class to Type.LONG_TYPE, + java.lang.Float::class to Type.FLOAT_TYPE, + java.lang.Double::class to Type.DOUBLE_TYPE ) } - private val BOXED_TO_PRIMITIVES: Map by lazy { - hashMapOf( - java.lang.Byte::class.asm to Type.BYTE_TYPE, - java.lang.Short::class.asm to Type.SHORT_TYPE, - java.lang.Integer::class.asm to Type.INT_TYPE, - java.lang.Long::class.asm to Type.LONG_TYPE, - java.lang.Float::class.asm to Type.FLOAT_TYPE, - java.lang.Double::class.asm to Type.DOUBLE_TYPE - ) - } + private val BOXED_TO_PRIMITIVES: Map by lazy { SIGNATURE_LETTERS.mapKeys { (k, _) -> k.asm } } private val NUMBER_CONVERTER_METHODS: Map by lazy { hashMapOf( @@ -435,15 +433,15 @@ internal class AsmBuilder internal constructor( /** * Provides boxed number types values of which can be stored in JVM bytecode constant pool. */ - private val INLINABLE_NUMBERS: Set> by lazy { SIGNATURE_LETTERS.keys } - internal val ASM_COMPILED_EXPRESSION_TYPE: Type = AsmCompiledExpression::class.asm - internal val NUMBER_TYPE: Type = java.lang.Number::class.asm - internal val MAP_TYPE: Type = java.util.Map::class.asm - internal val OBJECT_TYPE: Type = java.lang.Object::class.asm + private val INLINABLE_NUMBERS: Set> by lazy { SIGNATURE_LETTERS.keys } + internal val EXPRESSION_TYPE: Type by lazy { Expression::class.asm } + internal val NUMBER_TYPE: Type by lazy { java.lang.Number::class.asm } + internal val MAP_TYPE: Type by lazy { java.util.Map::class.asm } + internal val OBJECT_TYPE: Type by lazy { java.lang.Object::class.asm } @Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN", "RemoveRedundantQualifierName") - internal val OBJECT_ARRAY_TYPE: Type = Array::class.asm - internal val ALGEBRA_TYPE: Type = Algebra::class.asm - internal val STRING_TYPE: Type = java.lang.String::class.asm + internal val OBJECT_ARRAY_TYPE: Type by lazy { Array::class.asm } + internal val ALGEBRA_TYPE: Type by lazy { Algebra::class.asm } + internal val STRING_TYPE: Type by lazy { java.lang.String::class.asm } } } diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmCompiledExpression.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmCompiledExpression.kt deleted file mode 100644 index 7c4a9fc99..000000000 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmCompiledExpression.kt +++ /dev/null @@ -1,18 +0,0 @@ -package scientifik.kmath.asm.internal - -import scientifik.kmath.expressions.Expression -import scientifik.kmath.operations.Algebra - -/** - * [Expression] partial implementation to have it subclassed by actual implementations. Provides unified storage for - * objects needed to implement the expression. - * - * @property algebra the algebra to delegate calls. - * @property constants the constants array to have persistent objects to reference in [invoke]. - */ -internal abstract class AsmCompiledExpression internal constructor( - @JvmField protected val algebra: Algebra, - @JvmField protected val constants: Array -) : Expression { - abstract override fun invoke(arguments: Map): T -} diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/buildName.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/buildName.kt index 66bd039c3..41dbf5807 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/buildName.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/buildName.kt @@ -1,9 +1,10 @@ package scientifik.kmath.asm.internal import scientifik.kmath.ast.MST +import scientifik.kmath.expressions.Expression /** - * Creates a class name for [AsmCompiledExpression] subclassed to implement [mst] provided. + * Creates a class name for [Expression] subclassed to implement [mst] provided. * * This methods helps to avoid collisions of class name to prevent loading several classes with the same name. If there * is a colliding class, change [collision] parameter or leave it `0` to check existing classes recursively. diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/classWriters.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/classWriters.kt index 95d713b18..af5c1049d 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/classWriters.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/classWriters.kt @@ -1,15 +1,17 @@ package scientifik.kmath.asm.internal import org.objectweb.asm.ClassWriter +import org.objectweb.asm.FieldVisitor import org.objectweb.asm.MethodVisitor -internal inline fun ClassWriter(flags: Int, block: ClassWriter.() -> Unit): ClassWriter = ClassWriter(flags).apply(block) +internal inline fun ClassWriter(flags: Int, block: ClassWriter.() -> Unit): ClassWriter = + ClassWriter(flags).apply(block) -internal inline fun ClassWriter.visitMethod( +internal inline fun ClassWriter.visitField( access: Int, name: String, descriptor: String, signature: String?, - exceptions: Array?, - block: MethodVisitor.() -> Unit -): MethodVisitor = visitMethod(access, name, descriptor, signature, exceptions).apply(block) + value: Any?, + block: FieldVisitor.() -> Unit +): FieldVisitor = visitField(access, name, descriptor, signature, value).apply(block) diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/instructionAdapters.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/instructionAdapters.kt new file mode 100644 index 000000000..f47293687 --- /dev/null +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/instructionAdapters.kt @@ -0,0 +1,10 @@ +package scientifik.kmath.asm.internal + +import org.objectweb.asm.Label +import org.objectweb.asm.commons.InstructionAdapter + +internal fun InstructionAdapter.label(): Label { + val l = Label() + visitLabel(l) + return l +} diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/methodVisitors.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/methodVisitors.kt index 7b0d346b7..aaae02ebb 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/methodVisitors.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/methodVisitors.kt @@ -3,7 +3,7 @@ package scientifik.kmath.asm.internal import org.objectweb.asm.MethodVisitor import org.objectweb.asm.commons.InstructionAdapter -fun MethodVisitor.instructionAdapter(): InstructionAdapter = InstructionAdapter(this) +internal fun MethodVisitor.instructionAdapter(): InstructionAdapter = InstructionAdapter(this) -fun MethodVisitor.instructionAdapter(block: InstructionAdapter.() -> Unit): InstructionAdapter = +internal fun MethodVisitor.instructionAdapter(block: InstructionAdapter.() -> Unit): InstructionAdapter = instructionAdapter().apply(block) diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/specialization.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/specialization.kt index 2e15a1a93..4c7a0d57e 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/specialization.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/specialization.kt @@ -22,7 +22,7 @@ internal fun AsmBuilder.buildExpectationStack(context: Algebra, name: val aName = methodNameAdapters[name] ?: name val hasSpecific = context.javaClass.methods.find { it.name == aName && it.parameters.size == arity } != null - val t = if (primitiveMode && hasSpecific) PRIMITIVE_MASK else T_TYPE + val t = if (primitiveMode && hasSpecific) PRIMITIVE_MASK else tType repeat(arity) { expectationStack.push(t) } return hasSpecific -- 2.34.1 From c9de04a6106384c0128b4d0445b3e00bc0216379 Mon Sep 17 00:00:00 2001 From: Commander Tvis Date: Thu, 25 Jun 2020 10:24:21 +0700 Subject: [PATCH 096/156] Make benchmarks 'naive' --- .../ast/ExpressionsInterpretersBenchmark.kt | 34 ++++++++++++++----- 1 file changed, 25 insertions(+), 9 deletions(-) rename examples/src/{benchmarks => main}/kotlin/scientifik/kmath/ast/ExpressionsInterpretersBenchmark.kt (70%) diff --git a/examples/src/benchmarks/kotlin/scientifik/kmath/ast/ExpressionsInterpretersBenchmark.kt b/examples/src/main/kotlin/scientifik/kmath/ast/ExpressionsInterpretersBenchmark.kt similarity index 70% rename from examples/src/benchmarks/kotlin/scientifik/kmath/ast/ExpressionsInterpretersBenchmark.kt rename to examples/src/main/kotlin/scientifik/kmath/ast/ExpressionsInterpretersBenchmark.kt index c5474e1d2..17a70a4aa 100644 --- a/examples/src/benchmarks/kotlin/scientifik/kmath/ast/ExpressionsInterpretersBenchmark.kt +++ b/examples/src/main/kotlin/scientifik/kmath/ast/ExpressionsInterpretersBenchmark.kt @@ -1,21 +1,16 @@ package scientifik.kmath.ast -import org.openjdk.jmh.annotations.Benchmark -import org.openjdk.jmh.annotations.Scope -import org.openjdk.jmh.annotations.State import scientifik.kmath.asm.compile import scientifik.kmath.expressions.Expression import scientifik.kmath.expressions.expressionInField +import scientifik.kmath.expressions.invoke import scientifik.kmath.operations.Field import scientifik.kmath.operations.RealField import kotlin.random.Random +import kotlin.system.measureTimeMillis -@State(Scope.Benchmark) class ExpressionsInterpretersBenchmark { private val algebra: Field = RealField - private val random: Random = Random(1) - - @Benchmark fun functionalExpression() { val expr = algebra.expressionInField { variable("x") * const(2.0) + const(2.0) / variable("x") - const(16.0) @@ -24,7 +19,6 @@ class ExpressionsInterpretersBenchmark { invokeAndSum(expr) } - @Benchmark fun mstExpression() { val expr = algebra.mstInField { symbol("x") * number(2.0) + number(2.0) / symbol("x") - number(16.0) @@ -33,7 +27,6 @@ class ExpressionsInterpretersBenchmark { invokeAndSum(expr) } - @Benchmark fun asmExpression() { val expr = algebra.mstInField { symbol("x") * number(2.0) + number(2.0) / symbol("x") - number(16.0) @@ -43,6 +36,7 @@ class ExpressionsInterpretersBenchmark { } private fun invokeAndSum(expr: Expression) { + val random = Random(0) var sum = 0.0 repeat(1000000) { @@ -52,3 +46,25 @@ class ExpressionsInterpretersBenchmark { println(sum) } } + +fun main() { + val benchmark = ExpressionsInterpretersBenchmark() + + val fe = measureTimeMillis { + benchmark.functionalExpression() + } + + println("fe=$fe") + + val mst = measureTimeMillis { + benchmark.mstExpression() + } + + println("mst=$mst") + + val asm = measureTimeMillis { + benchmark.asmExpression() + } + + println("asm=$asm") +} -- 2.34.1 From b11a7f1426a707168d8174be4be43fd1d352fed3 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Thu, 25 Jun 2020 19:29:31 +0700 Subject: [PATCH 097/156] Update README.md --- kmath-ast/README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/kmath-ast/README.md b/kmath-ast/README.md index 4563e17cf..0e375f14d 100644 --- a/kmath-ast/README.md +++ b/kmath-ast/README.md @@ -48,12 +48,11 @@ This API is an extension to MST and MSTExpression APIs. You may optimize both MS ```kotlin RealField.mstInField { symbol("x") + 2 }.compile() -RealField.expression("2+2".parseMath()) +RealField.expression("x+2".parseMath()) ``` ### Known issues -- Using numeric algebras causes boxing and calling bridge methods. - The same classes may be generated and loaded twice, so it is recommended to cache compiled expressions to avoid class loading overhead. - This API is not supported by non-dynamic JVM implementations (like TeaVM and GraalVM) because of using class loaders. -- 2.34.1 From 23816d33665314016dfc46b67a34d09568e56640 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Thu, 25 Jun 2020 19:42:13 +0700 Subject: [PATCH 098/156] Update KDoc comments, optimize imports --- .../kmath/asm/internal/AsmBuilder.kt | 101 +++++++++++++++--- .../kmath/asm/internal/classWriters.kt | 1 - .../kmath/asm/internal/specialization.kt | 4 +- 3 files changed, 89 insertions(+), 17 deletions(-) diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt index 536d6136d..a291ba4ee 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt @@ -18,6 +18,7 @@ import kotlin.reflect.KClass * @param T the type of AsmExpression to unwrap. * @param algebra the algebra the applied AsmExpressions use. * @param className the unique class name of new loaded class. + * @param invokeLabel0Visitor the function to apply to this object when generating invoke method, label 0. */ internal class AsmBuilder internal constructor( private val classOfT: KClass<*>, @@ -37,8 +38,19 @@ internal class AsmBuilder internal constructor( */ private val classLoader: ClassLoader = ClassLoader(javaClass.classLoader) + /** + * ASM Type for [algebra] + */ private val tAlgebraType: Type = algebra::class.asm + + /** + * ASM type for [T] + */ internal val tType: Type = classOfT.asm + + /** + * ASM type for new class + */ private val classType: Type = Type.getObjectType(className.replace(oldChar = '.', newChar = '/'))!! /** @@ -60,14 +72,30 @@ internal class AsmBuilder internal constructor( * Method visitor of `invoke` method of the subclass. */ private lateinit var invokeMethodVisitor: InstructionAdapter + + /** + * State if [T] a primitive type, so [AsmBuilder] may generate direct primitive calls. + */ internal var primitiveMode = false - @Suppress("PropertyName") - internal var PRIMITIVE_MASK: Type = OBJECT_TYPE + /** + * Primitive type to apple for specific primitive calls. Use [OBJECT_TYPE], if not in [primitiveMode]. + */ + internal var primitiveMask: Type = OBJECT_TYPE - @Suppress("PropertyName") - internal var PRIMITIVE_MASK_BOXED: Type = OBJECT_TYPE + /** + * Boxed primitive type to apple for specific primitive calls. Use [OBJECT_TYPE], if not in [primitiveMode]. + */ + internal var primitiveMaskBoxed: Type = OBJECT_TYPE + + /** + * Stack of useful objects types on stack to verify types. + */ private val typeStack = Stack() + + /** + * Stack of useful objects types on stack expected by algebra calls. + */ internal val expectationStack: Stack = Stack().apply { push(tType) } /** @@ -86,8 +114,8 @@ internal class AsmBuilder internal constructor( if (SIGNATURE_LETTERS.containsKey(classOfT)) { primitiveMode = true - PRIMITIVE_MASK = SIGNATURE_LETTERS.getValue(classOfT) - PRIMITIVE_MASK_BOXED = tType + primitiveMask = SIGNATURE_LETTERS.getValue(classOfT) + primitiveMaskBoxed = tType } val classWriter = ClassWriter(ClassWriter.COMPUTE_FRAMES) { @@ -238,34 +266,43 @@ internal class AsmBuilder internal constructor( } /** - * Loads a constant from + * Loads a [T] constant from [constants]. */ internal fun loadTConstant(value: T) { if (classOfT in INLINABLE_NUMBERS) { val expectedType = expectationStack.pop()!! val mustBeBoxed = expectedType.sort == Type.OBJECT loadNumberConstant(value as Number, mustBeBoxed) - if (mustBeBoxed) typeStack.push(tType) else typeStack.push(PRIMITIVE_MASK) + if (mustBeBoxed) typeStack.push(tType) else typeStack.push(primitiveMask) return } loadConstant(value as Any, tType) } + /** + * Boxes the current value and pushes it. + */ private fun box(): Unit = invokeMethodVisitor.invokestatic( tType.internalName, "valueOf", - Type.getMethodDescriptor(tType, PRIMITIVE_MASK), + Type.getMethodDescriptor(tType, primitiveMask), false ) + /** + * Unboxes the current boxed value and pushes it. + */ private fun unbox(): Unit = invokeMethodVisitor.invokevirtual( NUMBER_TYPE.internalName, - NUMBER_CONVERTER_METHODS.getValue(PRIMITIVE_MASK), - Type.getMethodDescriptor(PRIMITIVE_MASK), + NUMBER_CONVERTER_METHODS.getValue(primitiveMask), + Type.getMethodDescriptor(primitiveMask), false ) + /** + * Loads [java.lang.Object] constant from constants. + */ private fun loadConstant(value: Any, type: Type): Unit = invokeMethodVisitor.run { val idx = if (value in constants) constants.indexOf(value) else constants.apply { add(value) }.lastIndex loadThis() @@ -275,6 +312,9 @@ internal class AsmBuilder internal constructor( checkcast(type) } + /** + * Loads this variable. + */ private fun loadThis(): Unit = invokeMethodVisitor.load(invokeThisVar, classType) /** @@ -344,7 +384,7 @@ internal class AsmBuilder internal constructor( typeStack.push(tType) else { unbox() - typeStack.push(PRIMITIVE_MASK) + typeStack.push(primitiveMask) } } @@ -393,7 +433,7 @@ internal class AsmBuilder internal constructor( typeStack.push(tType) else { unbox() - typeStack.push(PRIMITIVE_MASK) + typeStack.push(primitiveMask) } } @@ -404,7 +444,7 @@ internal class AsmBuilder internal constructor( internal companion object { /** - * Maps JVM primitive numbers boxed types to their letters of JVM signature convention. + * Maps JVM primitive numbers boxed types to their primitive ASM types. */ private val SIGNATURE_LETTERS: Map, Type> by lazy { hashMapOf( @@ -417,8 +457,14 @@ internal class AsmBuilder internal constructor( ) } + /** + * Maps JVM primitive numbers boxed ASM types to their primitive ASM types. + */ private val BOXED_TO_PRIMITIVES: Map by lazy { SIGNATURE_LETTERS.mapKeys { (k, _) -> k.asm } } + /** + * Maps primitive ASM types to [Number] functions unboxing them. + */ private val NUMBER_CONVERTER_METHODS: Map by lazy { hashMapOf( Type.BYTE_TYPE to "byteValue", @@ -434,14 +480,41 @@ internal class AsmBuilder internal constructor( * Provides boxed number types values of which can be stored in JVM bytecode constant pool. */ private val INLINABLE_NUMBERS: Set> by lazy { SIGNATURE_LETTERS.keys } + + /** + * ASM type for [Expression]. + */ internal val EXPRESSION_TYPE: Type by lazy { Expression::class.asm } + + /** + * ASM type for [java.lang.Number]. + */ internal val NUMBER_TYPE: Type by lazy { java.lang.Number::class.asm } + + /** + * ASM type for [java.util.Map]. + */ internal val MAP_TYPE: Type by lazy { java.util.Map::class.asm } + + /** + * ASM type for [java.lang.Object]. + */ internal val OBJECT_TYPE: Type by lazy { java.lang.Object::class.asm } + /** + * ASM type for array of [java.lang.Object]. + */ @Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN", "RemoveRedundantQualifierName") internal val OBJECT_ARRAY_TYPE: Type by lazy { Array::class.asm } + + /** + * ASM type for [Algebra]. + */ internal val ALGEBRA_TYPE: Type by lazy { Algebra::class.asm } + + /** + * ASM type for [java.lang.String]. + */ internal val STRING_TYPE: Type by lazy { java.lang.String::class.asm } } } diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/classWriters.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/classWriters.kt index af5c1049d..00093aaa7 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/classWriters.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/classWriters.kt @@ -2,7 +2,6 @@ package scientifik.kmath.asm.internal import org.objectweb.asm.ClassWriter import org.objectweb.asm.FieldVisitor -import org.objectweb.asm.MethodVisitor internal inline fun ClassWriter(flags: Int, block: ClassWriter.() -> Unit): ClassWriter = ClassWriter(flags).apply(block) diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/specialization.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/specialization.kt index 4c7a0d57e..252509d59 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/specialization.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/specialization.kt @@ -22,7 +22,7 @@ internal fun AsmBuilder.buildExpectationStack(context: Algebra, name: val aName = methodNameAdapters[name] ?: name val hasSpecific = context.javaClass.methods.find { it.name == aName && it.parameters.size == arity } != null - val t = if (primitiveMode && hasSpecific) PRIMITIVE_MASK else tType + val t = if (primitiveMode && hasSpecific) primitiveMask else tType repeat(arity) { expectationStack.push(t) } return hasSpecific @@ -52,7 +52,7 @@ internal fun AsmBuilder.tryInvokeSpecific(context: Algebra, name: Stri invokeAlgebraOperation( owner = owner, method = aName, - descriptor = Type.getMethodDescriptor(PRIMITIVE_MASK_BOXED, *Array(arity) { PRIMITIVE_MASK }), + descriptor = Type.getMethodDescriptor(primitiveMaskBoxed, *Array(arity) { primitiveMask }), tArity = arity, opcode = Opcodes.INVOKEVIRTUAL ) -- 2.34.1 From 46f99139e2850a4bfd63e3823b48b9e026fec017 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Thu, 25 Jun 2020 19:45:33 +0700 Subject: [PATCH 099/156] Update number literal call in tests --- .../src/jvmTest/kotlin/scietifik/kmath/asm/TestAsmAlgebras.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/asm/TestAsmAlgebras.kt b/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/asm/TestAsmAlgebras.kt index 4c2be811e..6ce769613 100644 --- a/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/asm/TestAsmAlgebras.kt +++ b/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/asm/TestAsmAlgebras.kt @@ -92,7 +92,7 @@ class TestAsmAlgebras { "+", (3.0 - (symbol("x") + (multiply(add(number(1.0), number(1.0)), 2) + 1.0))) * 3 - 1.0 + number(1), - 1 / 2 + number(2.0) * one + number(1) / 2 + number(2.0) * one ) }("x" to 2.0) @@ -101,7 +101,7 @@ class TestAsmAlgebras { "+", (3.0 - (symbol("x") + (multiply(add(number(1.0), number(1.0)), 2) + 1.0))) * 3 - 1.0 + number(1), - 1 / 2 + number(2.0) * one + number(1) / 2 + number(2.0) * one ) }.compile()("x" to 2.0) -- 2.34.1 From 7faa48be582bc34f997b50104c3e3dc30f3ee979 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Thu, 25 Jun 2020 19:46:32 +0700 Subject: [PATCH 100/156] Add zero call in MSTField test --- .../src/jvmTest/kotlin/scietifik/kmath/asm/TestAsmAlgebras.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/asm/TestAsmAlgebras.kt b/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/asm/TestAsmAlgebras.kt index 6ce769613..079a6be0f 100644 --- a/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/asm/TestAsmAlgebras.kt +++ b/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/asm/TestAsmAlgebras.kt @@ -93,7 +93,7 @@ class TestAsmAlgebras { (3.0 - (symbol("x") + (multiply(add(number(1.0), number(1.0)), 2) + 1.0))) * 3 - 1.0 + number(1), number(1) / 2 + number(2.0) * one - ) + ) + zero }("x" to 2.0) val res2 = RealField.mstInField { @@ -102,7 +102,7 @@ class TestAsmAlgebras { (3.0 - (symbol("x") + (multiply(add(number(1.0), number(1.0)), 2) + 1.0))) * 3 - 1.0 + number(1), number(1) / 2 + number(2.0) * one - ) + ) + zero }.compile()("x" to 2.0) assertEquals(res1, res2) -- 2.34.1 From 3528fa16dbc4c1750a56c7ee292044dbec2b4ef7 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Thu, 25 Jun 2020 20:10:38 +0700 Subject: [PATCH 101/156] Add missing dependency in examples --- examples/build.gradle.kts | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/build.gradle.kts b/examples/build.gradle.kts index fb47c998f..73def3572 100644 --- a/examples/build.gradle.kts +++ b/examples/build.gradle.kts @@ -24,6 +24,7 @@ sourceSets { } dependencies { + implementation(project(":kmath-ast")) implementation(project(":kmath-core")) implementation(project(":kmath-coroutines")) implementation(project(":kmath-commons")) -- 2.34.1 From e2cc3c8efefa3b6a7d604990e1b1ca0a4860678b Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Thu, 25 Jun 2020 20:54:14 +0700 Subject: [PATCH 102/156] Specify type explicitly, minor implementation refactor --- .../kotlin/scientifik/kmath/asm/asm.kt | 4 +-- .../kmath/asm/internal/AsmBuilder.kt | 9 ++--- .../kmath/asm/internal/specialization.kt | 35 ++++++++----------- 3 files changed, 21 insertions(+), 27 deletions(-) diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/asm.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/asm.kt index a3af80ccd..bb456e6eb 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/asm.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/asm.kt @@ -44,7 +44,7 @@ fun MST.compileWith(type: KClass, algebra: Algebra): Expression< AsmBuilder.OBJECT_TYPE ), - tArity = 1 + expectedArity = 1 ) } is MST.Binary -> { @@ -64,7 +64,7 @@ fun MST.compileWith(type: KClass, algebra: Algebra): Expression< AsmBuilder.OBJECT_TYPE ), - tArity = 2 + expectedArity = 2 ) } } diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt index a291ba4ee..ebe25e19f 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt @@ -76,7 +76,7 @@ internal class AsmBuilder internal constructor( /** * State if [T] a primitive type, so [AsmBuilder] may generate direct primitive calls. */ - internal var primitiveMode = false + internal var primitiveMode: Boolean = false /** * Primitive type to apple for specific primitive calls. Use [OBJECT_TYPE], if not in [primitiveMode]. @@ -91,7 +91,7 @@ internal class AsmBuilder internal constructor( /** * Stack of useful objects types on stack to verify types. */ - private val typeStack = Stack() + private val typeStack: Stack = Stack() /** * Stack of useful objects types on stack expected by algebra calls. @@ -345,6 +345,7 @@ internal class AsmBuilder internal constructor( } loadConstant(value, boxed) + if (!mustBeBoxed) unbox() else invokeMethodVisitor.checkcast(tType) } @@ -407,11 +408,11 @@ internal class AsmBuilder internal constructor( owner: String, method: String, descriptor: String, - tArity: Int, + expectedArity: Int, opcode: Int = Opcodes.INVOKEINTERFACE ) { run loop@{ - repeat(tArity) { + repeat(expectedArity) { if (typeStack.empty()) return@loop typeStack.pop() } diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/specialization.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/specialization.kt index 252509d59..e54acf6f9 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/specialization.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/specialization.kt @@ -5,11 +5,7 @@ import org.objectweb.asm.Type import scientifik.kmath.operations.Algebra private val methodNameAdapters: Map by lazy { - hashMapOf( - "+" to "add", - "*" to "multiply", - "/" to "divide" - ) + hashMapOf("+" to "add", "*" to "multiply", "/" to "divide") } /** @@ -19,12 +15,10 @@ private val methodNameAdapters: Map by lazy { * @return `true` if contains, else `false`. */ internal fun AsmBuilder.buildExpectationStack(context: Algebra, name: String, arity: Int): Boolean { - val aName = methodNameAdapters[name] ?: name - - val hasSpecific = context.javaClass.methods.find { it.name == aName && it.parameters.size == arity } != null + val theName = methodNameAdapters[name] ?: name + val hasSpecific = context.javaClass.methods.find { it.name == theName && it.parameters.size == arity } != null val t = if (primitiveMode && hasSpecific) primitiveMask else tType repeat(arity) { expectationStack.push(t) } - return hasSpecific } @@ -35,25 +29,24 @@ internal fun AsmBuilder.buildExpectationStack(context: Algebra, name: * @return `true` if contains, else `false`. */ internal fun AsmBuilder.tryInvokeSpecific(context: Algebra, name: String, arity: Int): Boolean { - val aName = methodNameAdapters[name] ?: name + val theName = methodNameAdapters[name] ?: name - val method = - context.javaClass.methods.find { - var suitableSignature = it.name == aName && it.parameters.size == arity + context.javaClass.methods.find { + var suitableSignature = it.name == theName && it.parameters.size == arity - if (primitiveMode && it.isBridge) - suitableSignature = false + if (primitiveMode && it.isBridge) + suitableSignature = false - suitableSignature - } ?: return false + suitableSignature + } ?: return false - val owner = context::class.java.name.replace('.', '/') + val owner = context::class.asm invokeAlgebraOperation( - owner = owner, - method = aName, + owner = owner.internalName, + method = theName, descriptor = Type.getMethodDescriptor(primitiveMaskBoxed, *Array(arity) { primitiveMask }), - tArity = arity, + expectedArity = arity, opcode = Opcodes.INVOKEVIRTUAL ) -- 2.34.1 From a275e74cf287bb3cf6b929b78d0db2b329f3cb43 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Fri, 26 Jun 2020 14:57:07 +0700 Subject: [PATCH 103/156] Add mapping for other dynamic operations --- .../kotlin/scientifik/kmath/operations/NumberAlgebra.kt | 4 ++++ .../kotlin/scientifik/kmath/operations/OptionalOperations.kt | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/NumberAlgebra.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/NumberAlgebra.kt index a1b845ccc..3fb57656c 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/NumberAlgebra.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/NumberAlgebra.kt @@ -17,6 +17,10 @@ interface ExtendedFieldOperations : override fun unaryOperation(operation: String, arg: T): T = when (operation) { TrigonometricOperations.COS_OPERATION -> cos(arg) TrigonometricOperations.SIN_OPERATION -> sin(arg) + TrigonometricOperations.TAN_OPERATION -> tan(arg) + InverseTrigonometricOperations.ACOS_OPERATION -> acos(arg) + InverseTrigonometricOperations.ASIN_OPERATION -> asin(arg) + InverseTrigonometricOperations.ATAN_OPERATION -> atan(arg) PowerOperations.SQRT_OPERATION -> sqrt(arg) ExponentialOperations.EXP_OPERATION -> exp(arg) ExponentialOperations.LN_OPERATION -> ln(arg) 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 a0266c78b..542d6376b 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/OptionalOperations.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/OptionalOperations.kt @@ -13,7 +13,7 @@ package scientifik.kmath.operations interface TrigonometricOperations : FieldOperations { fun sin(arg: T): T fun cos(arg: T): T - fun tan(arg: T): T = sin(arg) / cos(arg) + fun tan(arg: T): T companion object { const val SIN_OPERATION = "sin" -- 2.34.1 From 5ab6960e9b1a0b51ffca0cf30ee5eb83c550989c Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Fri, 26 Jun 2020 15:55:01 +0700 Subject: [PATCH 104/156] Add mapIntrinsics.kt, update specialization mappings --- .../kotlin/scientifik/kmath/asm/asm.kt | 1 + .../kmath/asm/internal/AsmBuilder.kt | 37 +++++++-------- .../kmath/asm/internal/mapIntrinsics.kt | 7 +++ .../kmath/asm/internal/specialization.kt | 15 +++++-- .../scietifik/kmath/asm/TestAsmAlgebras.kt | 2 +- .../scietifik/kmath/asm/TestAsmExpressions.kt | 2 +- .../scietifik/kmath/asm/TestSpecialization.kt | 45 +++++++++++++++++++ .../kotlin/scietifik/kmath/ast/AsmTest.kt | 2 +- 8 files changed, 84 insertions(+), 27 deletions(-) create mode 100644 kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/mapIntrinsics.kt create mode 100644 kmath-ast/src/jvmTest/kotlin/scietifik/kmath/asm/TestSpecialization.kt diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/asm.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/asm.kt index bb456e6eb..af39d9091 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/asm.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/asm.kt @@ -47,6 +47,7 @@ fun MST.compileWith(type: KClass, algebra: Algebra): Expression< expectedArity = 1 ) } + is MST.Binary -> { loadAlgebra() if (!buildExpectationStack(algebra, node.operation, 2)) loadStringConstant(node.operation) diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt index ebe25e19f..89c9dca9d 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt @@ -354,31 +354,23 @@ internal class AsmBuilder internal constructor( * Loads a variable [name] arguments [Map] parameter of [Expression.invoke]. The [defaultValue] may be provided. */ internal fun loadVariable(name: String, defaultValue: T? = null): Unit = invokeMethodVisitor.run { - load(invokeArgumentsVar, OBJECT_ARRAY_TYPE) + load(invokeArgumentsVar, MAP_TYPE) + aconst(name) - if (defaultValue != null) { - loadStringConstant(name) + if (defaultValue != null) loadTConstant(defaultValue) + else + aconst(null) - invokeinterface( - MAP_TYPE.internalName, - "getOrDefault", - Type.getMethodDescriptor(OBJECT_TYPE, OBJECT_TYPE, OBJECT_TYPE) - ) - - invokeMethodVisitor.checkcast(tType) - return - } - - loadStringConstant(name) - - invokeinterface( - MAP_TYPE.internalName, - "get", - Type.getMethodDescriptor(OBJECT_TYPE, OBJECT_TYPE) + invokestatic( + MAP_INTRINSICS_TYPE.internalName, + "getOrFail", + Type.getMethodDescriptor(OBJECT_TYPE, MAP_TYPE, OBJECT_TYPE, OBJECT_TYPE), + false ) - invokeMethodVisitor.checkcast(tType) + checkcast(tType) + val expectedType = expectationStack.pop()!! if (expectedType.sort == Type.OBJECT) @@ -517,5 +509,10 @@ internal class AsmBuilder internal constructor( * ASM type for [java.lang.String]. */ internal val STRING_TYPE: Type by lazy { java.lang.String::class.asm } + + /** + * ASM type for MapIntrinsics. + */ + internal val MAP_INTRINSICS_TYPE: Type by lazy { Type.getObjectType("scientifik/kmath/asm/internal/MapIntrinsics") } } } diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/mapIntrinsics.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/mapIntrinsics.kt new file mode 100644 index 000000000..7f7126b55 --- /dev/null +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/mapIntrinsics.kt @@ -0,0 +1,7 @@ +@file:JvmName("MapIntrinsics") + +package scientifik.kmath.asm.internal + +internal fun Map.getOrFail(key: K, default: V?): V { + return this[key] ?: default ?: error("Parameter not found: $key") +} diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/specialization.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/specialization.kt index e54acf6f9..a8d5a605f 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/specialization.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/specialization.kt @@ -4,8 +4,15 @@ import org.objectweb.asm.Opcodes import org.objectweb.asm.Type import scientifik.kmath.operations.Algebra -private val methodNameAdapters: Map by lazy { - hashMapOf("+" to "add", "*" to "multiply", "/" to "divide") +private val methodNameAdapters: Map, String> by lazy { + hashMapOf( + "+" to 2 to "add", + "*" to 2 to "multiply", + "/" to 2 to "divide", + "+" to 1 to "unaryPlus", + "-" to 1 to "unaryMinus", + "-" to 2 to "minus" + ) } /** @@ -15,7 +22,7 @@ private val methodNameAdapters: Map by lazy { * @return `true` if contains, else `false`. */ internal fun AsmBuilder.buildExpectationStack(context: Algebra, name: String, arity: Int): Boolean { - val theName = methodNameAdapters[name] ?: name + val theName = methodNameAdapters[name to arity] ?: name val hasSpecific = context.javaClass.methods.find { it.name == theName && it.parameters.size == arity } != null val t = if (primitiveMode && hasSpecific) primitiveMask else tType repeat(arity) { expectationStack.push(t) } @@ -29,7 +36,7 @@ internal fun AsmBuilder.buildExpectationStack(context: Algebra, name: * @return `true` if contains, else `false`. */ internal fun AsmBuilder.tryInvokeSpecific(context: Algebra, name: String, arity: Int): Boolean { - val theName = methodNameAdapters[name] ?: name + val theName = methodNameAdapters[name to arity] ?: name context.javaClass.methods.find { var suitableSignature = it.name == theName && it.parameters.size == arity diff --git a/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/asm/TestAsmAlgebras.kt b/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/asm/TestAsmAlgebras.kt index 079a6be0f..3acc6eb28 100644 --- a/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/asm/TestAsmAlgebras.kt +++ b/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/asm/TestAsmAlgebras.kt @@ -10,7 +10,7 @@ import scientifik.kmath.operations.RealField import kotlin.test.Test import kotlin.test.assertEquals -class TestAsmAlgebras { +internal class TestAsmAlgebras { @Test fun space() { val res1 = ByteRing.mstInSpace { diff --git a/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/asm/TestAsmExpressions.kt b/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/asm/TestAsmExpressions.kt index 824201aa7..36c254c38 100644 --- a/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/asm/TestAsmExpressions.kt +++ b/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/asm/TestAsmExpressions.kt @@ -8,7 +8,7 @@ import scientifik.kmath.operations.RealField import kotlin.test.Test import kotlin.test.assertEquals -class TestAsmExpressions { +internal class TestAsmExpressions { @Test fun testUnaryOperationInvocation() { val expression = RealField.mstInSpace { -symbol("x") }.compile() diff --git a/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/asm/TestSpecialization.kt b/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/asm/TestSpecialization.kt new file mode 100644 index 000000000..f3b07df56 --- /dev/null +++ b/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/asm/TestSpecialization.kt @@ -0,0 +1,45 @@ +package scietifik.kmath.asm + +import scientifik.kmath.asm.compile +import scientifik.kmath.ast.mstInField +import scientifik.kmath.expressions.invoke +import scientifik.kmath.operations.RealField +import kotlin.test.Test +import kotlin.test.assertEquals + +internal class TestSpecialization { + @Test + fun testUnaryPlus() { + val expr = RealField.mstInField { unaryOperation("+", symbol("x")) }.compile() + val res = expr("x" to 2.0) + assertEquals(2.0, res) + } + + @Test + fun testUnaryMinus() { + val expr = RealField.mstInField { unaryOperation("-", symbol("x")) }.compile() + val res = expr("x" to 2.0) + assertEquals(-2.0, res) + } + + @Test + fun testAdd() { + val expr = RealField.mstInField { binaryOperation("+", symbol("x"), symbol("x")) }.compile() + val res = expr("x" to 2.0) + assertEquals(4.0, res) + } + + @Test + fun testMinus() { + val expr = RealField.mstInField { binaryOperation("-", symbol("x"), symbol("x")) }.compile() + val res = expr("x" to 2.0) + assertEquals(0.0, res) + } + + @Test + fun testDivide() { + val expr = RealField.mstInField { binaryOperation("/", symbol("x"), symbol("x")) }.compile() + val res = expr("x" to 2.0) + assertEquals(1.0, res) + } +} diff --git a/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/AsmTest.kt b/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/AsmTest.kt index 08d7fff47..23203172e 100644 --- a/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/AsmTest.kt +++ b/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/AsmTest.kt @@ -10,7 +10,7 @@ import scientifik.kmath.operations.ComplexField import kotlin.test.Test import kotlin.test.assertEquals -class AsmTest { +internal class AsmTest { @Test fun `compile MST`() { val mst = "2+2*(2+2)".parseMath() -- 2.34.1 From 90c287d42fe2b2f7703ae4dad80da824bf05e125 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Fri, 26 Jun 2020 15:59:24 +0700 Subject: [PATCH 105/156] Add tests for MapInstrinsics --- .../scietifik/kmath/asm/TestAsmVariables.kt | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 kmath-ast/src/jvmTest/kotlin/scietifik/kmath/asm/TestAsmVariables.kt diff --git a/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/asm/TestAsmVariables.kt b/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/asm/TestAsmVariables.kt new file mode 100644 index 000000000..aafc75448 --- /dev/null +++ b/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/asm/TestAsmVariables.kt @@ -0,0 +1,22 @@ +package scietifik.kmath.asm + +import scientifik.kmath.ast.mstInRing +import scientifik.kmath.expressions.invoke +import scientifik.kmath.operations.ByteRing +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertFailsWith + +internal class TestAsmVariables { + @Test + fun testVariableWithoutDefault() { + val expr = ByteRing.mstInRing { symbol("x") } + assertEquals(1.toByte(), expr("x" to 1.toByte())) + } + + @Test + fun testVariableWithoutDefaultFails() { + val expr = ByteRing.mstInRing { symbol("x") } + assertFailsWith { expr() } + } +} -- 2.34.1 From 092728b1c328a6e515de5c68ade5e99bc460c4e5 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Fri, 26 Jun 2020 16:01:50 +0700 Subject: [PATCH 106/156] Replace Stack with ArrayDeque --- .../scientifik/kmath/asm/internal/AsmBuilder.kt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt index 89c9dca9d..c9f797787 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt @@ -91,12 +91,12 @@ internal class AsmBuilder internal constructor( /** * Stack of useful objects types on stack to verify types. */ - private val typeStack: Stack = Stack() + private val typeStack: ArrayDeque = ArrayDeque() /** * Stack of useful objects types on stack expected by algebra calls. */ - internal val expectationStack: Stack = Stack().apply { push(tType) } + internal val expectationStack: ArrayDeque = ArrayDeque().apply { push(tType) } /** * The cache for instance built by this builder. @@ -270,7 +270,7 @@ internal class AsmBuilder internal constructor( */ internal fun loadTConstant(value: T) { if (classOfT in INLINABLE_NUMBERS) { - val expectedType = expectationStack.pop()!! + val expectedType = expectationStack.pop() val mustBeBoxed = expectedType.sort == Type.OBJECT loadNumberConstant(value as Number, mustBeBoxed) if (mustBeBoxed) typeStack.push(tType) else typeStack.push(primitiveMask) @@ -371,7 +371,7 @@ internal class AsmBuilder internal constructor( checkcast(tType) - val expectedType = expectationStack.pop()!! + val expectedType = expectationStack.pop() if (expectedType.sort == Type.OBJECT) typeStack.push(tType) @@ -405,7 +405,7 @@ internal class AsmBuilder internal constructor( ) { run loop@{ repeat(expectedArity) { - if (typeStack.empty()) return@loop + if (typeStack.isEmpty()) return@loop typeStack.pop() } } @@ -420,7 +420,7 @@ internal class AsmBuilder internal constructor( invokeMethodVisitor.checkcast(tType) val isLastExpr = expectationStack.size == 1 - val expectedType = expectationStack.pop()!! + val expectedType = expectationStack.pop() if (expectedType.sort == Type.OBJECT || isLastExpr) typeStack.push(tType) -- 2.34.1 From 2df97ca4c3e2f33680dae70181e06c3dc2fe1349 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Fri, 26 Jun 2020 16:05:13 +0700 Subject: [PATCH 107/156] Update README.md, add suppression --- kmath-ast/README.md | 4 +++- .../kotlin/scientifik/kmath/asm/internal/classWriters.kt | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/kmath-ast/README.md b/kmath-ast/README.md index 0e375f14d..12d425460 100644 --- a/kmath-ast/README.md +++ b/kmath-ast/README.md @@ -24,6 +24,7 @@ For example, the following builder: package scientifik.kmath.asm.generated; import java.util.Map; +import scientifik.kmath.asm.internal.MapIntrinsics; import scientifik.kmath.expressions.Expression; import scientifik.kmath.operations.RealField; @@ -37,9 +38,10 @@ public final class AsmCompiledExpression_1073786867_0 implements Expression arguments) { - return (Double)this.algebra.add(((Double)arguments.get("x")).doubleValue(), 2.0D); + return (Double)this.algebra.add(((Double)MapIntrinsics.getOrFail(arguments, "x", (Object)null)).doubleValue(), 2.0D); } } + ``` ### Example Usage diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/classWriters.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/classWriters.kt index 00093aaa7..7f0770b28 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/classWriters.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/classWriters.kt @@ -3,6 +3,7 @@ package scientifik.kmath.asm.internal import org.objectweb.asm.ClassWriter import org.objectweb.asm.FieldVisitor +@Suppress("FunctionName") internal inline fun ClassWriter(flags: Int, block: ClassWriter.() -> Unit): ClassWriter = ClassWriter(flags).apply(block) -- 2.34.1 From 0ee1d31571a59659fbac33482a4e8cce16cf83fb Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Fri, 26 Jun 2020 20:57:47 +0700 Subject: [PATCH 108/156] Fix MSTField and MSTRing invalid unary operation, update according ASM tests --- .../kotlin/scientifik/kmath/ast/MSTAlgebra.kt | 37 +++++++++---------- ...ialization.kt => TestAsmSpecialization.kt} | 23 ++++++------ 2 files changed, 29 insertions(+), 31 deletions(-) rename kmath-ast/src/jvmTest/kotlin/scietifik/kmath/asm/{TestSpecialization.kt => TestAsmSpecialization.kt} (68%) diff --git a/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MSTAlgebra.kt b/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MSTAlgebra.kt index 07194a7bb..f741fc8c4 100644 --- a/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MSTAlgebra.kt +++ b/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MSTAlgebra.kt @@ -34,43 +34,40 @@ object MSTSpace : Space, NumericAlgebra { } object MSTRing : Ring, NumericAlgebra { - override fun number(value: Number): MST = MST.Numeric(value) - override fun symbol(value: String): MST = MST.Symbolic(value) - override val zero: MST = MSTSpace.number(0.0) override val one: MST = number(1.0) - override fun add(a: MST, b: MST): MST = - MSTAlgebra.binaryOperation(SpaceOperations.PLUS_OPERATION, a, b) + + override fun number(value: Number): MST = MST.Numeric(value) + override fun symbol(value: String): MST = MST.Symbolic(value) + override fun add(a: MST, b: MST): MST = binaryOperation(SpaceOperations.PLUS_OPERATION, a, b) override fun multiply(a: MST, k: Number): MST = - MSTAlgebra.binaryOperation(RingOperations.TIMES_OPERATION, a, MSTSpace.number(k)) + binaryOperation(RingOperations.TIMES_OPERATION, a, MSTSpace.number(k)) - override fun multiply(a: MST, b: MST): MST = - binaryOperation(RingOperations.TIMES_OPERATION, a, b) + override fun multiply(a: MST, b: MST): MST = binaryOperation(RingOperations.TIMES_OPERATION, a, b) override fun binaryOperation(operation: String, left: MST, right: MST): MST = MSTAlgebra.binaryOperation(operation, left, right) + + override fun unaryOperation(operation: String, arg: MST): MST = MSTAlgebra.unaryOperation(operation, arg) } -object MSTField : Field{ - override fun symbol(value: String): MST = MST.Symbolic(value) - override fun number(value: Number): MST = MST.Numeric(value) - +object MSTField : Field { override val zero: MST = MSTSpace.number(0.0) override val one: MST = number(1.0) - override fun add(a: MST, b: MST): MST = - MSTAlgebra.binaryOperation(SpaceOperations.PLUS_OPERATION, a, b) + override fun symbol(value: String): MST = MST.Symbolic(value) + override fun number(value: Number): MST = MST.Numeric(value) + override fun add(a: MST, b: MST): MST = binaryOperation(SpaceOperations.PLUS_OPERATION, a, b) override fun multiply(a: MST, k: Number): MST = - MSTAlgebra.binaryOperation(RingOperations.TIMES_OPERATION, a, MSTSpace.number(k)) + binaryOperation(RingOperations.TIMES_OPERATION, a, MSTSpace.number(k)) - override fun multiply(a: MST, b: MST): MST = - binaryOperation(RingOperations.TIMES_OPERATION, a, b) - - override fun divide(a: MST, b: MST): MST = - binaryOperation(FieldOperations.DIV_OPERATION, a, b) + override fun multiply(a: MST, b: MST): MST = binaryOperation(RingOperations.TIMES_OPERATION, a, b) + override fun divide(a: MST, b: MST): MST = binaryOperation(FieldOperations.DIV_OPERATION, a, b) override fun binaryOperation(operation: String, left: MST, right: MST): MST = MSTAlgebra.binaryOperation(operation, left, right) + + override fun unaryOperation(operation: String, arg: MST): MST = MSTAlgebra.unaryOperation(operation, arg) } diff --git a/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/asm/TestSpecialization.kt b/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/asm/TestAsmSpecialization.kt similarity index 68% rename from kmath-ast/src/jvmTest/kotlin/scietifik/kmath/asm/TestSpecialization.kt rename to kmath-ast/src/jvmTest/kotlin/scietifik/kmath/asm/TestAsmSpecialization.kt index f3b07df56..b571e076f 100644 --- a/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/asm/TestSpecialization.kt +++ b/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/asm/TestAsmSpecialization.kt @@ -7,39 +7,40 @@ import scientifik.kmath.operations.RealField import kotlin.test.Test import kotlin.test.assertEquals -internal class TestSpecialization { +internal class TestAsmSpecialization { @Test fun testUnaryPlus() { val expr = RealField.mstInField { unaryOperation("+", symbol("x")) }.compile() - val res = expr("x" to 2.0) - assertEquals(2.0, res) + assertEquals(2.0, expr("x" to 2.0)) } @Test fun testUnaryMinus() { val expr = RealField.mstInField { unaryOperation("-", symbol("x")) }.compile() - val res = expr("x" to 2.0) - assertEquals(-2.0, res) + assertEquals(-2.0, expr("x" to 2.0)) } @Test fun testAdd() { val expr = RealField.mstInField { binaryOperation("+", symbol("x"), symbol("x")) }.compile() - val res = expr("x" to 2.0) - assertEquals(4.0, res) + assertEquals(4.0, expr("x" to 2.0)) + } + + @Test + fun testSine() { + val expr = RealField.mstInField { unaryOperation("sin", symbol("x")) }.compile() + assertEquals(0.0, expr("x" to 0.0)) } @Test fun testMinus() { val expr = RealField.mstInField { binaryOperation("-", symbol("x"), symbol("x")) }.compile() - val res = expr("x" to 2.0) - assertEquals(0.0, res) + assertEquals(0.0, expr("x" to 2.0)) } @Test fun testDivide() { val expr = RealField.mstInField { binaryOperation("/", symbol("x"), symbol("x")) }.compile() - val res = expr("x" to 2.0) - assertEquals(1.0, res) + assertEquals(1.0, expr("x" to 2.0)) } } -- 2.34.1 From d962ab4d11298fd6cd699edd584c3c12c260d11e Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Fri, 26 Jun 2020 21:02:22 +0700 Subject: [PATCH 109/156] Rename and refactor MstAlgebra (ex-MSTAlgebra) (and its subclasses), MstExpression (ex-MSTExpression) --- .../scientifik/kmath/ast/MSTExpression.kt | 55 ------------------- .../ast/{MSTAlgebra.kt => MstAlgebra.kt} | 41 +++++++------- .../scientifik/kmath/ast/MstExpression.kt | 55 +++++++++++++++++++ .../kotlin/scientifik/kmath/asm/asm.kt | 6 +- 4 files changed, 78 insertions(+), 79 deletions(-) delete mode 100644 kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MSTExpression.kt rename kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/{MSTAlgebra.kt => MstAlgebra.kt} (62%) create mode 100644 kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MstExpression.kt diff --git a/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MSTExpression.kt b/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MSTExpression.kt deleted file mode 100644 index 61703cac7..000000000 --- a/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MSTExpression.kt +++ /dev/null @@ -1,55 +0,0 @@ -package scientifik.kmath.ast - -import scientifik.kmath.expressions.Expression -import scientifik.kmath.expressions.FunctionalExpressionField -import scientifik.kmath.expressions.FunctionalExpressionRing -import scientifik.kmath.expressions.FunctionalExpressionSpace -import scientifik.kmath.operations.* - -/** - * The expression evaluates MST on-flight. Should be much faster than functional expression, but slower than ASM-generated expressions. - */ -class MSTExpression(val algebra: Algebra, val mst: MST) : Expression { - - /** - * Substitute algebra raw value - */ - private inner class InnerAlgebra(val arguments: Map) : NumericAlgebra{ - override fun symbol(value: String): T = arguments[value] ?: algebra.symbol(value) - override fun unaryOperation(operation: String, arg: T): T = algebra.unaryOperation(operation, arg) - - override fun binaryOperation(operation: String, left: T, right: T): T =algebra.binaryOperation(operation, left, right) - - override fun number(value: Number): T = if(algebra is NumericAlgebra){ - algebra.number(value) - } else{ - error("Numeric nodes are not supported by $this") - } - } - - override fun invoke(arguments: Map): T = InnerAlgebra(arguments).evaluate(mst) -} - - -inline fun , E : Algebra> A.mst( - mstAlgebra: E, - block: E.() -> MST -): MSTExpression = MSTExpression(this, mstAlgebra.block()) - -inline fun Space.mstInSpace(block: MSTSpace.() -> MST): MSTExpression = - MSTExpression(this, MSTSpace.block()) - -inline fun Ring.mstInRing(block: MSTRing.() -> MST): MSTExpression = - MSTExpression(this, MSTRing.block()) - -inline fun Field.mstInField(block: MSTField.() -> MST): MSTExpression = - MSTExpression(this, MSTField.block()) - -inline fun > FunctionalExpressionSpace.mstInSpace(block: MSTSpace.() -> MST): MSTExpression = - algebra.mstInSpace(block) - -inline fun > FunctionalExpressionRing.mstInRing(block: MSTRing.() -> MST): MSTExpression = - algebra.mstInRing(block) - -inline fun > FunctionalExpressionField.mstInField(block: MSTField.() -> MST): MSTExpression = - algebra.mstInField(block) \ No newline at end of file diff --git a/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MSTAlgebra.kt b/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MstAlgebra.kt similarity index 62% rename from kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MSTAlgebra.kt rename to kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MstAlgebra.kt index f741fc8c4..007cf57c4 100644 --- a/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MSTAlgebra.kt +++ b/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MstAlgebra.kt @@ -2,7 +2,7 @@ package scientifik.kmath.ast import scientifik.kmath.operations.* -object MSTAlgebra : NumericAlgebra { +object MstAlgebra : NumericAlgebra { override fun number(value: Number): MST = MST.Numeric(value) override fun symbol(value: String): MST = MST.Symbolic(value) @@ -14,12 +14,11 @@ object MSTAlgebra : NumericAlgebra { MST.Binary(operation, left, right) } -object MSTSpace : Space, NumericAlgebra { +object MstSpace : Space, NumericAlgebra { override val zero: MST = number(0.0) - override fun number(value: Number): MST = MST.Numeric(value) - - override fun symbol(value: String): MST = MST.Symbolic(value) + override fun number(value: Number): MST = MstAlgebra.number(value) + override fun symbol(value: String): MST = MstAlgebra.symbol(value) override fun add(a: MST, b: MST): MST = binaryOperation(SpaceOperations.PLUS_OPERATION, a, b) @@ -28,46 +27,46 @@ object MSTSpace : Space, NumericAlgebra { binaryOperation(RingOperations.TIMES_OPERATION, a, number(k)) override fun binaryOperation(operation: String, left: MST, right: MST): MST = - MSTAlgebra.binaryOperation(operation, left, right) + MstAlgebra.binaryOperation(operation, left, right) - override fun unaryOperation(operation: String, arg: MST): MST = MSTAlgebra.unaryOperation(operation, arg) + override fun unaryOperation(operation: String, arg: MST): MST = MstAlgebra.unaryOperation(operation, arg) } -object MSTRing : Ring, NumericAlgebra { - override val zero: MST = MSTSpace.number(0.0) +object MstRing : Ring, NumericAlgebra { + override val zero: MST = number(0.0) override val one: MST = number(1.0) - override fun number(value: Number): MST = MST.Numeric(value) - override fun symbol(value: String): MST = MST.Symbolic(value) + override fun number(value: Number): MST = MstAlgebra.number(value) + override fun symbol(value: String): MST = MstAlgebra.symbol(value) override fun add(a: MST, b: MST): MST = binaryOperation(SpaceOperations.PLUS_OPERATION, a, b) override fun multiply(a: MST, k: Number): MST = - binaryOperation(RingOperations.TIMES_OPERATION, a, MSTSpace.number(k)) + binaryOperation(RingOperations.TIMES_OPERATION, a, MstSpace.number(k)) override fun multiply(a: MST, b: MST): MST = binaryOperation(RingOperations.TIMES_OPERATION, a, b) override fun binaryOperation(operation: String, left: MST, right: MST): MST = - MSTAlgebra.binaryOperation(operation, left, right) + MstAlgebra.binaryOperation(operation, left, right) - override fun unaryOperation(operation: String, arg: MST): MST = MSTAlgebra.unaryOperation(operation, arg) + override fun unaryOperation(operation: String, arg: MST): MST = MstAlgebra.unaryOperation(operation, arg) } -object MSTField : Field { - override val zero: MST = MSTSpace.number(0.0) +object MstField : Field { + override val zero: MST = number(0.0) override val one: MST = number(1.0) - override fun symbol(value: String): MST = MST.Symbolic(value) - override fun number(value: Number): MST = MST.Numeric(value) + override fun symbol(value: String): MST = MstAlgebra.symbol(value) + override fun number(value: Number): MST = MstAlgebra.number(value) override fun add(a: MST, b: MST): MST = binaryOperation(SpaceOperations.PLUS_OPERATION, a, b) override fun multiply(a: MST, k: Number): MST = - binaryOperation(RingOperations.TIMES_OPERATION, a, MSTSpace.number(k)) + binaryOperation(RingOperations.TIMES_OPERATION, a, MstSpace.number(k)) override fun multiply(a: MST, b: MST): MST = binaryOperation(RingOperations.TIMES_OPERATION, a, b) override fun divide(a: MST, b: MST): MST = binaryOperation(FieldOperations.DIV_OPERATION, a, b) override fun binaryOperation(operation: String, left: MST, right: MST): MST = - MSTAlgebra.binaryOperation(operation, left, right) + MstAlgebra.binaryOperation(operation, left, right) - override fun unaryOperation(operation: String, arg: MST): MST = MSTAlgebra.unaryOperation(operation, arg) + override fun unaryOperation(operation: String, arg: MST): MST = MstAlgebra.unaryOperation(operation, arg) } diff --git a/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MstExpression.kt b/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MstExpression.kt new file mode 100644 index 000000000..1468c3ad4 --- /dev/null +++ b/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MstExpression.kt @@ -0,0 +1,55 @@ +package scientifik.kmath.ast + +import scientifik.kmath.expressions.Expression +import scientifik.kmath.expressions.FunctionalExpressionField +import scientifik.kmath.expressions.FunctionalExpressionRing +import scientifik.kmath.expressions.FunctionalExpressionSpace +import scientifik.kmath.operations.* + +/** + * The expression evaluates MST on-flight. Should be much faster than functional expression, but slower than ASM-generated expressions. + */ +class MstExpression(val algebra: Algebra, val mst: MST) : Expression { + + /** + * Substitute algebra raw value + */ + private inner class InnerAlgebra(val arguments: Map) : NumericAlgebra { + override fun symbol(value: String): T = arguments[value] ?: algebra.symbol(value) + override fun unaryOperation(operation: String, arg: T): T = algebra.unaryOperation(operation, arg) + + override fun binaryOperation(operation: String, left: T, right: T): T = + algebra.binaryOperation(operation, left, right) + + override fun number(value: Number): T = if (algebra is NumericAlgebra) + algebra.number(value) + else + error("Numeric nodes are not supported by $this") + } + + override fun invoke(arguments: Map): T = InnerAlgebra(arguments).evaluate(mst) +} + + +inline fun , E : Algebra> A.mst( + mstAlgebra: E, + block: E.() -> MST +): MstExpression = MstExpression(this, mstAlgebra.block()) + +inline fun Space.mstInSpace(block: MstSpace.() -> MST): MstExpression = + MstExpression(this, MstSpace.block()) + +inline fun Ring.mstInRing(block: MstRing.() -> MST): MstExpression = + MstExpression(this, MstRing.block()) + +inline fun Field.mstInField(block: MstField.() -> MST): MstExpression = + MstExpression(this, MstField.block()) + +inline fun > FunctionalExpressionSpace.mstInSpace(block: MstSpace.() -> MST): MstExpression = + algebra.mstInSpace(block) + +inline fun > FunctionalExpressionRing.mstInRing(block: MstRing.() -> MST): MstExpression = + algebra.mstInRing(block) + +inline fun > FunctionalExpressionField.mstInField(block: MstField.() -> MST): MstExpression = + algebra.mstInField(block) \ No newline at end of file diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/asm.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/asm.kt index af39d9091..468ed01ba 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/asm.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/asm.kt @@ -6,7 +6,7 @@ import scientifik.kmath.asm.internal.buildExpectationStack import scientifik.kmath.asm.internal.buildName import scientifik.kmath.asm.internal.tryInvokeSpecific import scientifik.kmath.ast.MST -import scientifik.kmath.ast.MSTExpression +import scientifik.kmath.ast.MstExpression import scientifik.kmath.expressions.Expression import scientifik.kmath.operations.Algebra import scientifik.kmath.operations.NumericAlgebra @@ -80,6 +80,6 @@ fun MST.compileWith(type: KClass, algebra: Algebra): Expression< inline fun Algebra.expression(mst: MST): Expression = mst.compileWith(T::class, this) /** - * Optimize performance of an [MSTExpression] using ASM codegen + * Optimize performance of an [MstExpression] using ASM codegen */ -inline fun MSTExpression.compile(): Expression = mst.compileWith(T::class, algebra) +inline fun MstExpression.compile(): Expression = mst.compileWith(T::class, algebra) -- 2.34.1 From ec46f5cf229109fe266e888f29b301d62bb2d3ea Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Fri, 26 Jun 2020 21:02:31 +0700 Subject: [PATCH 110/156] Update README.md --- kmath-ast/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kmath-ast/README.md b/kmath-ast/README.md index 12d425460..62b18b4b5 100644 --- a/kmath-ast/README.md +++ b/kmath-ast/README.md @@ -46,7 +46,7 @@ public final class AsmCompiledExpression_1073786867_0 implements Expression Date: Fri, 26 Jun 2020 21:39:39 +0700 Subject: [PATCH 111/156] Add explicit toRegex call to have better IDE support --- .../kotlin/scientifik/kmath/ast/parser.kt | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/parser.kt b/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/parser.kt index cec61a8ff..30a92c5ae 100644 --- a/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/parser.kt +++ b/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/parser.kt @@ -16,15 +16,15 @@ import scientifik.kmath.operations.SpaceOperations * TODO move to common */ private object ArithmeticsEvaluator : Grammar() { - val num by token("-?[\\d.]+(?:[eE]-?\\d+)?") - val lpar by token("\\(") - val rpar by token("\\)") - val mul by token("\\*") - val pow by token("\\^") - val div by token("/") - val minus by token("-") - val plus by token("\\+") - val ws by token("\\s+", ignore = true) + val num by token("-?[\\d.]+(?:[eE]-?\\d+)?".toRegex()) + val lpar by token("\\(".toRegex()) + val rpar by token("\\)".toRegex()) + val mul by token("\\*".toRegex()) + val pow by token("\\^".toRegex()) + val div by token("/".toRegex()) + val minus by token("-".toRegex()) + val plus by token("\\+".toRegex()) + val ws by token("\\s+".toRegex(), ignore = true) val number: Parser by num use { MST.Numeric(text.toDouble()) } -- 2.34.1 From bf89aa09e561da09bde2df50355d7adf312c75cc Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Fri, 26 Jun 2020 22:05:42 +0700 Subject: [PATCH 112/156] Add static imports for Opcodes --- .../kmath/asm/internal/AsmBuilder.kt | 26 +++++++++---------- .../kmath/asm/internal/specialization.kt | 4 +-- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt index c9f797787..cea6be933 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt @@ -1,8 +1,7 @@ package scientifik.kmath.asm.internal import org.objectweb.asm.* -import org.objectweb.asm.Opcodes.AALOAD -import org.objectweb.asm.Opcodes.RETURN +import org.objectweb.asm.Opcodes.* import org.objectweb.asm.commons.InstructionAdapter import scientifik.kmath.asm.internal.AsmBuilder.ClassLoader import scientifik.kmath.ast.MST @@ -120,8 +119,8 @@ internal class AsmBuilder internal constructor( val classWriter = ClassWriter(ClassWriter.COMPUTE_FRAMES) { visit( - Opcodes.V1_8, - Opcodes.ACC_PUBLIC or Opcodes.ACC_FINAL or Opcodes.ACC_SUPER, + V1_8, + ACC_PUBLIC or ACC_FINAL or ACC_SUPER, classType.internalName, "${OBJECT_TYPE.descriptor}L${EXPRESSION_TYPE.internalName}<${tType.descriptor}>;", OBJECT_TYPE.internalName, @@ -129,7 +128,7 @@ internal class AsmBuilder internal constructor( ) visitField( - access = Opcodes.ACC_PRIVATE or Opcodes.ACC_FINAL, + access = ACC_PRIVATE or ACC_FINAL, name = "algebra", descriptor = tAlgebraType.descriptor, signature = null, @@ -138,7 +137,7 @@ internal class AsmBuilder internal constructor( ) visitField( - access = Opcodes.ACC_PRIVATE or Opcodes.ACC_FINAL, + access = ACC_PRIVATE or ACC_FINAL, name = "constants", descriptor = OBJECT_ARRAY_TYPE.descriptor, signature = null, @@ -147,7 +146,7 @@ internal class AsmBuilder internal constructor( ) visitMethod( - Opcodes.ACC_PUBLIC, + ACC_PUBLIC, "", Type.getMethodDescriptor(Type.VOID_TYPE, tAlgebraType, OBJECT_ARRAY_TYPE), null, @@ -187,7 +186,7 @@ internal class AsmBuilder internal constructor( } visitMethod( - Opcodes.ACC_PUBLIC or Opcodes.ACC_FINAL, + ACC_PUBLIC or ACC_FINAL, "invoke", Type.getMethodDescriptor(tType, MAP_TYPE), "(L${MAP_TYPE.internalName}<${STRING_TYPE.descriptor}+${tType.descriptor}>;)${tType.descriptor}", @@ -223,7 +222,7 @@ internal class AsmBuilder internal constructor( } visitMethod( - Opcodes.ACC_PUBLIC or Opcodes.ACC_FINAL or Opcodes.ACC_BRIDGE or Opcodes.ACC_SYNTHETIC, + ACC_PUBLIC or ACC_FINAL or ACC_BRIDGE or ACC_SYNTHETIC, "invoke", Type.getMethodDescriptor(OBJECT_TYPE, MAP_TYPE), null, @@ -351,7 +350,8 @@ internal class AsmBuilder internal constructor( } /** - * Loads a variable [name] arguments [Map] parameter of [Expression.invoke]. The [defaultValue] may be provided. + * Loads a variable [name] from arguments [Map] parameter of [Expression.invoke]. The [defaultValue] may be + * provided. */ internal fun loadVariable(name: String, defaultValue: T? = null): Unit = invokeMethodVisitor.run { load(invokeArgumentsVar, MAP_TYPE) @@ -391,7 +391,7 @@ internal class AsmBuilder internal constructor( /** * Writes a method instruction of opcode with its [owner], [method] and its [descriptor]. The default opcode is - * [Opcodes.INVOKEINTERFACE], since most Algebra functions are declared in interface. [loadAlgebra] should be + * [Opcodes.INVOKEINTERFACE], since most Algebra functions are declared in interfaces. [loadAlgebra] should be * called before the arguments and this operation. * * The result is casted to [T] automatically. @@ -401,7 +401,7 @@ internal class AsmBuilder internal constructor( method: String, descriptor: String, expectedArity: Int, - opcode: Int = Opcodes.INVOKEINTERFACE + opcode: Int = INVOKEINTERFACE ) { run loop@{ repeat(expectedArity) { @@ -415,7 +415,7 @@ internal class AsmBuilder internal constructor( owner, method, descriptor, - opcode == Opcodes.INVOKEINTERFACE + opcode == INVOKEINTERFACE ) invokeMethodVisitor.checkcast(tType) diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/specialization.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/specialization.kt index a8d5a605f..a6d2c045b 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/specialization.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/specialization.kt @@ -1,6 +1,6 @@ package scientifik.kmath.asm.internal -import org.objectweb.asm.Opcodes +import org.objectweb.asm.Opcodes.INVOKEVIRTUAL import org.objectweb.asm.Type import scientifik.kmath.operations.Algebra @@ -54,7 +54,7 @@ internal fun AsmBuilder.tryInvokeSpecific(context: Algebra, name: Stri method = theName, descriptor = Type.getMethodDescriptor(primitiveMaskBoxed, *Array(arity) { primitiveMask }), expectedArity = arity, - opcode = Opcodes.INVOKEVIRTUAL + opcode = INVOKEVIRTUAL ) return true -- 2.34.1 From 4b067f7a97c2a147b046944dfb66ddb5c78b5577 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sat, 27 Jun 2020 12:19:43 +0300 Subject: [PATCH 113/156] DoubleBuffer -> RealBuffer. Number algebra refactoring. --- doc/buffers.md | 2 +- .../kmath/structures/BufferBenchmark.kt | 4 +- .../structures/StructureReadBenchmark.kt | 2 +- .../structures/StructureWriteBenchmark.kt | 4 +- .../commons/transform/Transformations.kt | 2 +- .../kmath/domains/HyperSquareDomain.kt | 6 +- .../scientifik/kmath/linear/BufferMatrix.kt | 8 +- .../scientifik/kmath/structures/Buffers.kt | 62 +----------- .../kmath/structures/FlaggedBuffer.kt | 53 +++++++++++ .../scientifik/kmath/structures/IntBuffer.kt | 20 ++++ .../scientifik/kmath/structures/LongBuffer.kt | 19 ++++ .../{DoubleBuffer.kt => RealBuffer.kt} | 12 +-- .../kmath/structures/RealBufferField.kt | 94 +++++++++---------- .../kmath/structures/RealNDField.kt | 6 +- .../kmath/structures/ShortBuffer.kt | 20 ++++ .../scientifik/kmath/streaming/BufferFlow.kt | 8 +- .../scientifik/kmath/real/RealVector.kt | 4 +- .../scientifik/kmath/real/realBuffer.kt | 6 +- .../scientifik/kmath/real/realMatrix.kt | 10 +- .../scientifik/kmath/histogram/Histogram.kt | 6 +- .../kmath/histogram/RealHistogram.kt | 2 +- 21 files changed, 205 insertions(+), 145 deletions(-) create mode 100644 kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/FlaggedBuffer.kt create mode 100644 kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/IntBuffer.kt create mode 100644 kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/LongBuffer.kt rename kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/{DoubleBuffer.kt => RealBuffer.kt} (59%) create mode 100644 kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ShortBuffer.kt diff --git a/doc/buffers.md b/doc/buffers.md index b0b7489b3..52a9df86e 100644 --- a/doc/buffers.md +++ b/doc/buffers.md @@ -2,7 +2,7 @@ Buffer is one of main building blocks of kmath. It is a basic interface allowing random-access read and write (with `MutableBuffer`). There are different types of buffers: -* Primitive buffers wrapping like `DoubleBuffer` which are wrapping primitive arrays. +* Primitive buffers wrapping like `RealBuffer` which are wrapping primitive arrays. * Boxing `ListBuffer` wrapping a list * Functionally defined `VirtualBuffer` which does not hold a state itself, but provides a function to calculate value * `MemoryBuffer` allows direct allocation of objects in continuous memory block. diff --git a/examples/src/benchmarks/kotlin/scientifik/kmath/structures/BufferBenchmark.kt b/examples/src/benchmarks/kotlin/scientifik/kmath/structures/BufferBenchmark.kt index 9676b5e4a..e40b0c4b7 100644 --- a/examples/src/benchmarks/kotlin/scientifik/kmath/structures/BufferBenchmark.kt +++ b/examples/src/benchmarks/kotlin/scientifik/kmath/structures/BufferBenchmark.kt @@ -10,8 +10,8 @@ import scientifik.kmath.operations.complex class BufferBenchmark { @Benchmark - fun genericDoubleBufferReadWrite() { - val buffer = DoubleBuffer(size){it.toDouble()} + fun genericRealBufferReadWrite() { + val buffer = RealBuffer(size){it.toDouble()} (0 until size).forEach { buffer[it] diff --git a/examples/src/main/kotlin/scientifik/kmath/structures/StructureReadBenchmark.kt b/examples/src/main/kotlin/scientifik/kmath/structures/StructureReadBenchmark.kt index ecfb4ab20..a33fdb2c4 100644 --- a/examples/src/main/kotlin/scientifik/kmath/structures/StructureReadBenchmark.kt +++ b/examples/src/main/kotlin/scientifik/kmath/structures/StructureReadBenchmark.kt @@ -6,7 +6,7 @@ fun main(args: Array) { val n = 6000 val array = DoubleArray(n * n) { 1.0 } - val buffer = DoubleBuffer(array) + val buffer = RealBuffer(array) val strides = DefaultStrides(intArrayOf(n, n)) val structure = BufferNDStructure(strides, buffer) diff --git a/examples/src/main/kotlin/scientifik/kmath/structures/StructureWriteBenchmark.kt b/examples/src/main/kotlin/scientifik/kmath/structures/StructureWriteBenchmark.kt index 2d16cc8f4..0241f12ad 100644 --- a/examples/src/main/kotlin/scientifik/kmath/structures/StructureWriteBenchmark.kt +++ b/examples/src/main/kotlin/scientifik/kmath/structures/StructureWriteBenchmark.kt @@ -26,10 +26,10 @@ fun main(args: Array) { } println("Array mapping finished in $time2 millis") - val buffer = DoubleBuffer(DoubleArray(n * n) { 1.0 }) + val buffer = RealBuffer(DoubleArray(n * n) { 1.0 }) val time3 = measureTimeMillis { - val target = DoubleBuffer(DoubleArray(n * n)) + val target = RealBuffer(DoubleArray(n * n)) val res = array.forEachIndexed { index, value -> target[index] = value + 1 } diff --git a/kmath-commons/src/main/kotlin/scientifik/kmath/commons/transform/Transformations.kt b/kmath-commons/src/main/kotlin/scientifik/kmath/commons/transform/Transformations.kt index bcb3ea87b..eb1b5b69a 100644 --- a/kmath-commons/src/main/kotlin/scientifik/kmath/commons/transform/Transformations.kt +++ b/kmath-commons/src/main/kotlin/scientifik/kmath/commons/transform/Transformations.kt @@ -18,7 +18,7 @@ object Transformations { private fun Buffer.toArray(): Array = Array(size) { org.apache.commons.math3.complex.Complex(get(it).re, get(it).im) } - private fun Buffer.asArray() = if (this is DoubleBuffer) { + private fun Buffer.asArray() = if (this is RealBuffer) { array } else { DoubleArray(size) { i -> get(i) } diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/domains/HyperSquareDomain.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/domains/HyperSquareDomain.kt index 21912b87c..e0019c96b 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/domains/HyperSquareDomain.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/domains/HyperSquareDomain.kt @@ -16,7 +16,7 @@ package scientifik.kmath.domains import scientifik.kmath.linear.Point -import scientifik.kmath.structures.DoubleBuffer +import scientifik.kmath.structures.RealBuffer import scientifik.kmath.structures.indices /** @@ -25,7 +25,7 @@ import scientifik.kmath.structures.indices * * @author Alexander Nozik */ -class HyperSquareDomain(private val lower: DoubleBuffer, private val upper: DoubleBuffer) : RealDomain { +class HyperSquareDomain(private val lower: RealBuffer, private val upper: RealBuffer) : RealDomain { override operator fun contains(point: Point): Boolean = point.indices.all { i -> point[i] in lower[i]..upper[i] @@ -49,7 +49,7 @@ class HyperSquareDomain(private val lower: DoubleBuffer, private val upper: Doub else -> point[i] } } - return DoubleBuffer(*res) + return RealBuffer(*res) } override fun volume(): Double { diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/BufferMatrix.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/BufferMatrix.kt index 73b18b810..c4c38284b 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/BufferMatrix.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/BufferMatrix.kt @@ -30,11 +30,11 @@ object RealMatrixContext : GenericMatrixContext { override val elementContext get() = RealField override inline fun produce(rows: Int, columns: Int, initializer: (i: Int, j: Int) -> Double): Matrix { - val buffer = DoubleBuffer(rows * columns) { offset -> initializer(offset / columns, offset % columns) } + val buffer = RealBuffer(rows * columns) { offset -> initializer(offset / columns, offset % columns) } return BufferMatrix(rows, columns, buffer) } - override inline fun point(size: Int, initializer: (Int) -> Double): Point = DoubleBuffer(size,initializer) + override inline fun point(size: Int, initializer: (Int) -> Double): Point = RealBuffer(size,initializer) } class BufferMatrix( @@ -102,7 +102,7 @@ infix fun BufferMatrix.dot(other: BufferMatrix): BufferMatrix.unsafeArray(): DoubleArray = if (this is DoubleBuffer) { + fun Buffer.unsafeArray(): DoubleArray = if (this is RealBuffer) { array } else { DoubleArray(size) { get(it) } @@ -119,6 +119,6 @@ infix fun BufferMatrix.dot(other: BufferMatrix): BufferMatrix { companion object { - inline fun real(size: Int, initializer: (Int) -> Double): DoubleBuffer { + inline fun real(size: Int, initializer: (Int) -> Double): RealBuffer { val array = DoubleArray(size) { initializer(it) } - return DoubleBuffer(array) + return RealBuffer(array) } /** @@ -51,7 +51,7 @@ interface Buffer { inline fun auto(type: KClass, size: Int, crossinline initializer: (Int) -> T): Buffer { //TODO add resolution based on Annotation or companion resolution return when (type) { - Double::class -> DoubleBuffer(DoubleArray(size) { initializer(it) as Double }) as Buffer + Double::class -> RealBuffer(DoubleArray(size) { initializer(it) as Double }) as Buffer Short::class -> ShortBuffer(ShortArray(size) { initializer(it) as Short }) as Buffer Int::class -> IntBuffer(IntArray(size) { initializer(it) as Int }) as Buffer Long::class -> LongBuffer(LongArray(size) { initializer(it) as Long }) as Buffer @@ -93,7 +93,7 @@ interface MutableBuffer : Buffer { @Suppress("UNCHECKED_CAST") inline fun auto(type: KClass, size: Int, initializer: (Int) -> T): MutableBuffer { return when (type) { - Double::class -> DoubleBuffer(DoubleArray(size) { initializer(it) as Double }) as MutableBuffer + Double::class -> RealBuffer(DoubleArray(size) { initializer(it) as Double }) as MutableBuffer Short::class -> ShortBuffer(ShortArray(size) { initializer(it) as Short }) as MutableBuffer Int::class -> IntBuffer(IntArray(size) { initializer(it) as Int }) as MutableBuffer Long::class -> LongBuffer(LongArray(size) { initializer(it) as Long }) as MutableBuffer @@ -109,12 +109,11 @@ interface MutableBuffer : Buffer { auto(T::class, size, initializer) val real: MutableBufferFactory = { size: Int, initializer: (Int) -> Double -> - DoubleBuffer(DoubleArray(size) { initializer(it) }) + RealBuffer(DoubleArray(size) { initializer(it) }) } } } - inline class ListBuffer(val list: List) : Buffer { override val size: Int @@ -163,57 +162,6 @@ class ArrayBuffer(private val array: Array) : MutableBuffer { fun Array.asBuffer(): ArrayBuffer = ArrayBuffer(this) -inline class ShortBuffer(val array: ShortArray) : MutableBuffer { - override val size: Int get() = array.size - - override fun get(index: Int): Short = array[index] - - override fun set(index: Int, value: Short) { - array[index] = value - } - - override fun iterator() = array.iterator() - - override fun copy(): MutableBuffer = ShortBuffer(array.copyOf()) - -} - -fun ShortArray.asBuffer() = ShortBuffer(this) - -inline class IntBuffer(val array: IntArray) : MutableBuffer { - override val size: Int get() = array.size - - override fun get(index: Int): Int = array[index] - - override fun set(index: Int, value: Int) { - array[index] = value - } - - override fun iterator() = array.iterator() - - override fun copy(): MutableBuffer = IntBuffer(array.copyOf()) - -} - -fun IntArray.asBuffer() = IntBuffer(this) - -inline class LongBuffer(val array: LongArray) : MutableBuffer { - override val size: Int get() = array.size - - override fun get(index: Int): Long = array[index] - - override fun set(index: Int, value: Long) { - array[index] = value - } - - override fun iterator() = array.iterator() - - override fun copy(): MutableBuffer = LongBuffer(array.copyOf()) - -} - -fun LongArray.asBuffer() = LongBuffer(this) - inline class ReadOnlyBuffer(val buffer: MutableBuffer) : Buffer { override val size: Int get() = buffer.size diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/FlaggedBuffer.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/FlaggedBuffer.kt new file mode 100644 index 000000000..749e4eeec --- /dev/null +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/FlaggedBuffer.kt @@ -0,0 +1,53 @@ +package scientifik.kmath.structures + +import kotlin.experimental.and + +enum class ValueFlag(val mask: Byte) { + NAN(0b0000_0001), + MISSING(0b0000_0010), + NEGATIVE_INFINITY(0b0000_0100), + POSITIVE_INFINITY(0b0000_1000) +} + +/** + * A buffer with flagged values + */ +interface FlaggedBuffer : Buffer { + fun getFlag(index: Int): Byte +} + +/** + * The value is valid if all flags are down + */ +fun FlaggedBuffer<*>.isValid(index: Int) = getFlag(index) != 0.toByte() + +fun FlaggedBuffer<*>.hasFlag(index: Int, flag: ValueFlag) = (getFlag(index) and flag.mask) != 0.toByte() + +fun FlaggedBuffer<*>.isMissing(index: Int) = hasFlag(index, ValueFlag.MISSING) + +/** + * A real buffer which supports flags for each value like NaN or Missing + */ +class FlaggedRealBuffer(val values: DoubleArray, val flags: ByteArray) : FlaggedBuffer, Buffer { + init { + require(values.size == flags.size) { "Values and flags must have the same dimensions" } + } + + override fun getFlag(index: Int): Byte = flags[index] + + override val size: Int get() = values.size + + override fun get(index: Int): Double? = if (isValid(index)) values[index] else null + + override fun iterator(): Iterator = values.indices.asSequence().map { + if (isValid(it)) values[it] else null + }.iterator() +} + +inline fun FlaggedRealBuffer.forEachValid(block: (Double) -> Unit) { + for(i in indices){ + if(isValid(i)){ + block(values[i]) + } + } +} \ No newline at end of file diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/IntBuffer.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/IntBuffer.kt new file mode 100644 index 000000000..a354c5de0 --- /dev/null +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/IntBuffer.kt @@ -0,0 +1,20 @@ +package scientifik.kmath.structures + +inline class IntBuffer(val array: IntArray) : MutableBuffer { + override val size: Int get() = array.size + + override fun get(index: Int): Int = array[index] + + override fun set(index: Int, value: Int) { + array[index] = value + } + + override fun iterator() = array.iterator() + + override fun copy(): MutableBuffer = + IntBuffer(array.copyOf()) + +} + + +fun IntArray.asBuffer() = IntBuffer(this) \ No newline at end of file diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/LongBuffer.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/LongBuffer.kt new file mode 100644 index 000000000..fa6229a71 --- /dev/null +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/LongBuffer.kt @@ -0,0 +1,19 @@ +package scientifik.kmath.structures + +inline class LongBuffer(val array: LongArray) : MutableBuffer { + override val size: Int get() = array.size + + override fun get(index: Int): Long = array[index] + + override fun set(index: Int, value: Long) { + array[index] = value + } + + override fun iterator() = array.iterator() + + override fun copy(): MutableBuffer = + LongBuffer(array.copyOf()) + +} + +fun LongArray.asBuffer() = LongBuffer(this) \ No newline at end of file diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/DoubleBuffer.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/RealBuffer.kt similarity index 59% rename from kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/DoubleBuffer.kt rename to kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/RealBuffer.kt index c0b7f713b..f48ace3a9 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/DoubleBuffer.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/RealBuffer.kt @@ -1,6 +1,6 @@ package scientifik.kmath.structures -inline class DoubleBuffer(val array: DoubleArray) : MutableBuffer { +inline class RealBuffer(val array: DoubleArray) : MutableBuffer { override val size: Int get() = array.size override fun get(index: Int): Double = array[index] @@ -12,23 +12,23 @@ inline class DoubleBuffer(val array: DoubleArray) : MutableBuffer { override fun iterator() = array.iterator() override fun copy(): MutableBuffer = - DoubleBuffer(array.copyOf()) + RealBuffer(array.copyOf()) } @Suppress("FunctionName") -inline fun DoubleBuffer(size: Int, init: (Int) -> Double): DoubleBuffer = DoubleBuffer(DoubleArray(size) { init(it) }) +inline fun RealBuffer(size: Int, init: (Int) -> Double): RealBuffer = RealBuffer(DoubleArray(size) { init(it) }) @Suppress("FunctionName") -fun DoubleBuffer(vararg doubles: Double): DoubleBuffer = DoubleBuffer(doubles) +fun RealBuffer(vararg doubles: Double): RealBuffer = RealBuffer(doubles) /** * Transform buffer of doubles into array for high performance operations */ val MutableBuffer.array: DoubleArray - get() = if (this is DoubleBuffer) { + get() = if (this is RealBuffer) { array } else { DoubleArray(size) { get(it) } } -fun DoubleArray.asBuffer() = DoubleBuffer(this) \ No newline at end of file +fun DoubleArray.asBuffer() = RealBuffer(this) \ No newline at end of file diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/RealBufferField.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/RealBufferField.kt index 88c8c29db..a91000a2a 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/RealBufferField.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/RealBufferField.kt @@ -9,143 +9,143 @@ import kotlin.math.* * A simple field over linear buffers of [Double] */ object RealBufferFieldOperations : ExtendedFieldOperations> { - override fun add(a: Buffer, b: Buffer): DoubleBuffer { + override fun add(a: Buffer, b: Buffer): RealBuffer { require(b.size == a.size) { "The size of the first buffer ${a.size} should be the same as for second one: ${b.size} " } - return if (a is DoubleBuffer && b is DoubleBuffer) { + return if (a is RealBuffer && b is RealBuffer) { val aArray = a.array val bArray = b.array - DoubleBuffer(DoubleArray(a.size) { aArray[it] + bArray[it] }) + RealBuffer(DoubleArray(a.size) { aArray[it] + bArray[it] }) } else { - DoubleBuffer(DoubleArray(a.size) { a[it] + b[it] }) + RealBuffer(DoubleArray(a.size) { a[it] + b[it] }) } } - override fun multiply(a: Buffer, k: Number): DoubleBuffer { + override fun multiply(a: Buffer, k: Number): RealBuffer { val kValue = k.toDouble() - return if (a is DoubleBuffer) { + return if (a is RealBuffer) { val aArray = a.array - DoubleBuffer(DoubleArray(a.size) { aArray[it] * kValue }) + RealBuffer(DoubleArray(a.size) { aArray[it] * kValue }) } else { - DoubleBuffer(DoubleArray(a.size) { a[it] * kValue }) + RealBuffer(DoubleArray(a.size) { a[it] * kValue }) } } - override fun multiply(a: Buffer, b: Buffer): DoubleBuffer { + override fun multiply(a: Buffer, b: Buffer): RealBuffer { require(b.size == a.size) { "The size of the first buffer ${a.size} should be the same as for second one: ${b.size} " } - return if (a is DoubleBuffer && b is DoubleBuffer) { + return if (a is RealBuffer && b is RealBuffer) { val aArray = a.array val bArray = b.array - DoubleBuffer(DoubleArray(a.size) { aArray[it] * bArray[it] }) + RealBuffer(DoubleArray(a.size) { aArray[it] * bArray[it] }) } else { - DoubleBuffer(DoubleArray(a.size) { a[it] * b[it] }) + RealBuffer(DoubleArray(a.size) { a[it] * b[it] }) } } - override fun divide(a: Buffer, b: Buffer): DoubleBuffer { + override fun divide(a: Buffer, b: Buffer): RealBuffer { require(b.size == a.size) { "The size of the first buffer ${a.size} should be the same as for second one: ${b.size} " } - return if (a is DoubleBuffer && b is DoubleBuffer) { + return if (a is RealBuffer && b is RealBuffer) { val aArray = a.array val bArray = b.array - DoubleBuffer(DoubleArray(a.size) { aArray[it] / bArray[it] }) + RealBuffer(DoubleArray(a.size) { aArray[it] / bArray[it] }) } else { - DoubleBuffer(DoubleArray(a.size) { a[it] / b[it] }) + RealBuffer(DoubleArray(a.size) { a[it] / b[it] }) } } - override fun sin(arg: Buffer): DoubleBuffer { - return if (arg is DoubleBuffer) { + override fun sin(arg: Buffer): RealBuffer { + return if (arg is RealBuffer) { val array = arg.array - DoubleBuffer(DoubleArray(arg.size) { sin(array[it]) }) + RealBuffer(DoubleArray(arg.size) { sin(array[it]) }) } else { - DoubleBuffer(DoubleArray(arg.size) { sin(arg[it]) }) + RealBuffer(DoubleArray(arg.size) { sin(arg[it]) }) } } - override fun cos(arg: Buffer): DoubleBuffer { - return if (arg is DoubleBuffer) { + override fun cos(arg: Buffer): RealBuffer { + return if (arg is RealBuffer) { val array = arg.array - DoubleBuffer(DoubleArray(arg.size) { cos(array[it]) }) + RealBuffer(DoubleArray(arg.size) { cos(array[it]) }) } else { - DoubleBuffer(DoubleArray(arg.size) { cos(arg[it]) }) + RealBuffer(DoubleArray(arg.size) { cos(arg[it]) }) } } - override fun power(arg: Buffer, pow: Number): DoubleBuffer { - return if (arg is DoubleBuffer) { + override fun power(arg: Buffer, pow: Number): RealBuffer { + return if (arg is RealBuffer) { val array = arg.array - DoubleBuffer(DoubleArray(arg.size) { array[it].pow(pow.toDouble()) }) + RealBuffer(DoubleArray(arg.size) { array[it].pow(pow.toDouble()) }) } else { - DoubleBuffer(DoubleArray(arg.size) { arg[it].pow(pow.toDouble()) }) + RealBuffer(DoubleArray(arg.size) { arg[it].pow(pow.toDouble()) }) } } - override fun exp(arg: Buffer): DoubleBuffer { - return if (arg is DoubleBuffer) { + override fun exp(arg: Buffer): RealBuffer { + return if (arg is RealBuffer) { val array = arg.array - DoubleBuffer(DoubleArray(arg.size) { exp(array[it]) }) + RealBuffer(DoubleArray(arg.size) { exp(array[it]) }) } else { - DoubleBuffer(DoubleArray(arg.size) { exp(arg[it]) }) + RealBuffer(DoubleArray(arg.size) { exp(arg[it]) }) } } - override fun ln(arg: Buffer): DoubleBuffer { - return if (arg is DoubleBuffer) { + override fun ln(arg: Buffer): RealBuffer { + return if (arg is RealBuffer) { val array = arg.array - DoubleBuffer(DoubleArray(arg.size) { ln(array[it]) }) + RealBuffer(DoubleArray(arg.size) { ln(array[it]) }) } else { - DoubleBuffer(DoubleArray(arg.size) { ln(arg[it]) }) + RealBuffer(DoubleArray(arg.size) { ln(arg[it]) }) } } } class RealBufferField(val size: Int) : ExtendedField> { - override val zero: Buffer by lazy { DoubleBuffer(size) { 0.0 } } + override val zero: Buffer by lazy { RealBuffer(size) { 0.0 } } - override val one: Buffer by lazy { DoubleBuffer(size) { 1.0 } } + override val one: Buffer by lazy { RealBuffer(size) { 1.0 } } - override fun add(a: Buffer, b: Buffer): DoubleBuffer { + override fun add(a: Buffer, b: Buffer): RealBuffer { require(a.size == size) { "The buffer size ${a.size} does not match context size $size" } return RealBufferFieldOperations.add(a, b) } - override fun multiply(a: Buffer, k: Number): DoubleBuffer { + override fun multiply(a: Buffer, k: Number): RealBuffer { require(a.size == size) { "The buffer size ${a.size} does not match context size $size" } return RealBufferFieldOperations.multiply(a, k) } - override fun multiply(a: Buffer, b: Buffer): DoubleBuffer { + override fun multiply(a: Buffer, b: Buffer): RealBuffer { require(a.size == size) { "The buffer size ${a.size} does not match context size $size" } return RealBufferFieldOperations.multiply(a, b) } - override fun divide(a: Buffer, b: Buffer): DoubleBuffer { + override fun divide(a: Buffer, b: Buffer): RealBuffer { require(a.size == size) { "The buffer size ${a.size} does not match context size $size" } return RealBufferFieldOperations.divide(a, b) } - override fun sin(arg: Buffer): DoubleBuffer { + override fun sin(arg: Buffer): RealBuffer { require(arg.size == size) { "The buffer size ${arg.size} does not match context size $size" } return RealBufferFieldOperations.sin(arg) } - override fun cos(arg: Buffer): DoubleBuffer { + override fun cos(arg: Buffer): RealBuffer { require(arg.size == size) { "The buffer size ${arg.size} does not match context size $size" } return RealBufferFieldOperations.cos(arg) } - override fun power(arg: Buffer, pow: Number): DoubleBuffer { + override fun power(arg: Buffer, pow: Number): RealBuffer { require(arg.size == size) { "The buffer size ${arg.size} does not match context size $size" } return RealBufferFieldOperations.power(arg, pow) } - override fun exp(arg: Buffer): DoubleBuffer { + override fun exp(arg: Buffer): RealBuffer { require(arg.size == size) { "The buffer size ${arg.size} does not match context size $size" } return RealBufferFieldOperations.exp(arg) } - override fun ln(arg: Buffer): DoubleBuffer { + override fun ln(arg: Buffer): RealBuffer { require(arg.size == size) { "The buffer size ${arg.size} does not match context size $size" } return RealBufferFieldOperations.ln(arg) } 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 8c1bd4239..4a5f10790 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/RealNDField.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/RealNDField.kt @@ -16,7 +16,7 @@ class RealNDField(override val shape: IntArray) : override val one by lazy { produce { one } } inline fun buildBuffer(size: Int, crossinline initializer: (Int) -> Double): Buffer = - DoubleBuffer(DoubleArray(size) { initializer(it) }) + RealBuffer(DoubleArray(size) { initializer(it) }) /** * Inline transform an NDStructure to @@ -82,7 +82,7 @@ class RealNDField(override val shape: IntArray) : */ inline fun BufferedNDField.produceInline(crossinline initializer: RealField.(Int) -> Double): RealNDElement { val array = DoubleArray(strides.linearSize) { offset -> RealField.initializer(offset) } - return BufferedNDFieldElement(this, DoubleBuffer(array)) + return BufferedNDFieldElement(this, RealBuffer(array)) } /** @@ -96,7 +96,7 @@ inline fun RealNDElement.mapIndexed(crossinline transform: RealField.(index: Int */ inline fun RealNDElement.map(crossinline transform: RealField.(Double) -> Double): RealNDElement { val array = DoubleArray(strides.linearSize) { offset -> RealField.transform(buffer[offset]) } - return BufferedNDFieldElement(context, DoubleBuffer(array)) + return BufferedNDFieldElement(context, RealBuffer(array)) } /** diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ShortBuffer.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ShortBuffer.kt new file mode 100644 index 000000000..f4b2f7d13 --- /dev/null +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ShortBuffer.kt @@ -0,0 +1,20 @@ +package scientifik.kmath.structures + +inline class ShortBuffer(val array: ShortArray) : MutableBuffer { + override val size: Int get() = array.size + + override fun get(index: Int): Short = array[index] + + override fun set(index: Int, value: Short) { + array[index] = value + } + + override fun iterator() = array.iterator() + + override fun copy(): MutableBuffer = + ShortBuffer(array.copyOf()) + +} + + +fun ShortArray.asBuffer() = ShortBuffer(this) \ No newline at end of file diff --git a/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/streaming/BufferFlow.kt b/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/streaming/BufferFlow.kt index bef21a680..54da66bb7 100644 --- a/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/streaming/BufferFlow.kt +++ b/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/streaming/BufferFlow.kt @@ -5,7 +5,7 @@ import kotlinx.coroutines.flow.* import scientifik.kmath.chains.BlockingRealChain import scientifik.kmath.structures.Buffer import scientifik.kmath.structures.BufferFactory -import scientifik.kmath.structures.DoubleBuffer +import scientifik.kmath.structures.RealBuffer import scientifik.kmath.structures.asBuffer /** @@ -45,7 +45,7 @@ fun Flow.chunked(bufferSize: Int, bufferFactory: BufferFactory): Flow< /** * Specialized flow chunker for real buffer */ -fun Flow.chunked(bufferSize: Int): Flow = flow { +fun Flow.chunked(bufferSize: Int): Flow = flow { require(bufferSize > 0) { "Resulting chunk size must be more than zero" } if (this@chunked is BlockingRealChain) { @@ -61,13 +61,13 @@ fun Flow.chunked(bufferSize: Int): Flow = flow { array[counter] = element counter++ if (counter == bufferSize) { - val buffer = DoubleBuffer(array) + val buffer = RealBuffer(array) emit(buffer) counter = 0 } } if (counter > 0) { - emit(DoubleBuffer(counter) { array[it] }) + emit(RealBuffer(counter) { array[it] }) } } } diff --git a/kmath-for-real/src/commonMain/kotlin/scientifik/kmath/real/RealVector.kt b/kmath-for-real/src/commonMain/kotlin/scientifik/kmath/real/RealVector.kt index 23c7e19cb..2b89904e3 100644 --- a/kmath-for-real/src/commonMain/kotlin/scientifik/kmath/real/RealVector.kt +++ b/kmath-for-real/src/commonMain/kotlin/scientifik/kmath/real/RealVector.kt @@ -7,7 +7,7 @@ import scientifik.kmath.operations.Norm import scientifik.kmath.operations.RealField import scientifik.kmath.operations.SpaceElement import scientifik.kmath.structures.Buffer -import scientifik.kmath.structures.DoubleBuffer +import scientifik.kmath.structures.RealBuffer import scientifik.kmath.structures.asBuffer import scientifik.kmath.structures.asIterable import kotlin.math.sqrt @@ -41,7 +41,7 @@ inline class RealVector(private val point: Point) : private val spaceCache = HashMap>() inline operator fun invoke(dim: Int, initializer: (Int) -> Double) = - RealVector(DoubleBuffer(dim, initializer)) + RealVector(RealBuffer(dim, initializer)) operator fun invoke(vararg values: Double): RealVector = values.asVector() diff --git a/kmath-for-real/src/commonMain/kotlin/scientifik/kmath/real/realBuffer.kt b/kmath-for-real/src/commonMain/kotlin/scientifik/kmath/real/realBuffer.kt index d9ee4d90b..82c0e86b2 100644 --- a/kmath-for-real/src/commonMain/kotlin/scientifik/kmath/real/realBuffer.kt +++ b/kmath-for-real/src/commonMain/kotlin/scientifik/kmath/real/realBuffer.kt @@ -1,8 +1,8 @@ package scientifik.kmath.real -import scientifik.kmath.structures.DoubleBuffer +import scientifik.kmath.structures.RealBuffer /** - * Simplified [DoubleBuffer] to array comparison + * Simplified [RealBuffer] to array comparison */ -fun DoubleBuffer.contentEquals(vararg doubles: Double) = array.contentEquals(doubles) \ No newline at end of file +fun RealBuffer.contentEquals(vararg doubles: Double) = array.contentEquals(doubles) \ No newline at end of file diff --git a/kmath-for-real/src/commonMain/kotlin/scientifik/kmath/real/realMatrix.kt b/kmath-for-real/src/commonMain/kotlin/scientifik/kmath/real/realMatrix.kt index 0f4ccf2a8..65f86eec7 100644 --- a/kmath-for-real/src/commonMain/kotlin/scientifik/kmath/real/realMatrix.kt +++ b/kmath-for-real/src/commonMain/kotlin/scientifik/kmath/real/realMatrix.kt @@ -5,8 +5,8 @@ import scientifik.kmath.linear.RealMatrixContext.elementContext import scientifik.kmath.linear.VirtualMatrix import scientifik.kmath.operations.sum import scientifik.kmath.structures.Buffer -import scientifik.kmath.structures.DoubleBuffer import scientifik.kmath.structures.Matrix +import scientifik.kmath.structures.RealBuffer import scientifik.kmath.structures.asIterable import kotlin.math.pow @@ -133,22 +133,22 @@ fun Matrix.extractColumns(columnRange: IntRange): RealMatrix = fun Matrix.extractColumn(columnIndex: Int): RealMatrix = extractColumns(columnIndex..columnIndex) -fun Matrix.sumByColumn(): DoubleBuffer = DoubleBuffer(colNum) { j -> +fun Matrix.sumByColumn(): RealBuffer = RealBuffer(colNum) { j -> val column = columns[j] with(elementContext) { sum(column.asIterable()) } } -fun Matrix.minByColumn(): DoubleBuffer = DoubleBuffer(colNum) { j -> +fun Matrix.minByColumn(): RealBuffer = RealBuffer(colNum) { j -> columns[j].asIterable().min() ?: throw Exception("Cannot produce min on empty column") } -fun Matrix.maxByColumn(): DoubleBuffer = DoubleBuffer(colNum) { j -> +fun Matrix.maxByColumn(): RealBuffer = RealBuffer(colNum) { j -> columns[j].asIterable().max() ?: throw Exception("Cannot produce min on empty column") } -fun Matrix.averageByColumn(): DoubleBuffer = DoubleBuffer(colNum) { j -> +fun Matrix.averageByColumn(): RealBuffer = RealBuffer(colNum) { j -> columns[j].asIterable().average() } diff --git a/kmath-histograms/src/commonMain/kotlin/scientifik/kmath/histogram/Histogram.kt b/kmath-histograms/src/commonMain/kotlin/scientifik/kmath/histogram/Histogram.kt index 5199669f5..43d50ad20 100644 --- a/kmath-histograms/src/commonMain/kotlin/scientifik/kmath/histogram/Histogram.kt +++ b/kmath-histograms/src/commonMain/kotlin/scientifik/kmath/histogram/Histogram.kt @@ -3,7 +3,7 @@ package scientifik.kmath.histogram import scientifik.kmath.domains.Domain import scientifik.kmath.linear.Point import scientifik.kmath.structures.ArrayBuffer -import scientifik.kmath.structures.DoubleBuffer +import scientifik.kmath.structures.RealBuffer /** * The bin in the histogram. The histogram is by definition always done in the real space @@ -43,9 +43,9 @@ interface MutableHistogram> : Histogram { fun MutableHistogram.put(vararg point: T) = put(ArrayBuffer(point)) fun MutableHistogram.put(vararg point: Number) = - put(DoubleBuffer(point.map { it.toDouble() }.toDoubleArray())) + put(RealBuffer(point.map { it.toDouble() }.toDoubleArray())) -fun MutableHistogram.put(vararg point: Double) = put(DoubleBuffer(point)) +fun MutableHistogram.put(vararg point: Double) = put(RealBuffer(point)) fun MutableHistogram.fill(sequence: Iterable>) = sequence.forEach { put(it) } diff --git a/kmath-histograms/src/commonMain/kotlin/scientifik/kmath/histogram/RealHistogram.kt b/kmath-histograms/src/commonMain/kotlin/scientifik/kmath/histogram/RealHistogram.kt index f9d815421..628a68461 100644 --- a/kmath-histograms/src/commonMain/kotlin/scientifik/kmath/histogram/RealHistogram.kt +++ b/kmath-histograms/src/commonMain/kotlin/scientifik/kmath/histogram/RealHistogram.kt @@ -50,7 +50,7 @@ class RealHistogram( override val dimension: Int get() = lower.size - private val binSize = DoubleBuffer(dimension) { (upper[it] - lower[it]) / binNums[it] } + private val binSize = RealBuffer(dimension) { (upper[it] - lower[it]) / binNums[it] } init { // argument checks -- 2.34.1 From efcfb4425366ee32c2aafb733c1dab8567ea9abe Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Sat, 27 Jun 2020 20:04:22 +0700 Subject: [PATCH 114/156] Refactor Algebra call building --- .../kotlin/scientifik/kmath/asm/asm.kt | 51 +++++-------------- .../kmath/asm/internal/specialization.kt | 29 ++++++++++- 2 files changed, 40 insertions(+), 40 deletions(-) diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/asm.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/asm.kt index 468ed01ba..ef2330533 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/asm.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/asm.kt @@ -1,10 +1,8 @@ package scientifik.kmath.asm -import org.objectweb.asm.Type import scientifik.kmath.asm.internal.AsmBuilder -import scientifik.kmath.asm.internal.buildExpectationStack +import scientifik.kmath.asm.internal.buildAlgebraOperationCall import scientifik.kmath.asm.internal.buildName -import scientifik.kmath.asm.internal.tryInvokeSpecific import scientifik.kmath.ast.MST import scientifik.kmath.ast.MstExpression import scientifik.kmath.expressions.Expression @@ -29,44 +27,21 @@ fun MST.compileWith(type: KClass, algebra: Algebra): Expression< loadTConstant(constant) } - is MST.Unary -> { - loadAlgebra() - if (!buildExpectationStack(algebra, node.operation, 1)) loadStringConstant(node.operation) - visit(node.value) + is MST.Unary -> buildAlgebraOperationCall( + context = algebra, + name = node.operation, + fallbackMethodName = "unaryOperation", + arity = 1 + ) { visit(node.value) } - if (!tryInvokeSpecific(algebra, node.operation, 1)) invokeAlgebraOperation( - owner = AsmBuilder.ALGEBRA_TYPE.internalName, - method = "unaryOperation", - - descriptor = Type.getMethodDescriptor( - AsmBuilder.OBJECT_TYPE, - AsmBuilder.STRING_TYPE, - AsmBuilder.OBJECT_TYPE - ), - - expectedArity = 1 - ) - } - - is MST.Binary -> { - loadAlgebra() - if (!buildExpectationStack(algebra, node.operation, 2)) loadStringConstant(node.operation) + is MST.Binary -> buildAlgebraOperationCall( + context = algebra, + name = node.operation, + fallbackMethodName = "binaryOperation", + arity = 2 + ) { visit(node.left) visit(node.right) - - if (!tryInvokeSpecific(algebra, node.operation, 2)) invokeAlgebraOperation( - owner = AsmBuilder.ALGEBRA_TYPE.internalName, - method = "binaryOperation", - - descriptor = Type.getMethodDescriptor( - AsmBuilder.OBJECT_TYPE, - AsmBuilder.STRING_TYPE, - AsmBuilder.OBJECT_TYPE, - AsmBuilder.OBJECT_TYPE - ), - - expectedArity = 2 - ) } } } diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/specialization.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/specialization.kt index a6d2c045b..003dc47dd 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/specialization.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/specialization.kt @@ -21,7 +21,7 @@ private val methodNameAdapters: Map, String> by lazy { * * @return `true` if contains, else `false`. */ -internal fun AsmBuilder.buildExpectationStack(context: Algebra, name: String, arity: Int): Boolean { +private fun AsmBuilder.buildExpectationStack(context: Algebra, name: String, arity: Int): Boolean { val theName = methodNameAdapters[name to arity] ?: name val hasSpecific = context.javaClass.methods.find { it.name == theName && it.parameters.size == arity } != null val t = if (primitiveMode && hasSpecific) primitiveMask else tType @@ -35,7 +35,7 @@ internal fun AsmBuilder.buildExpectationStack(context: Algebra, name: * * @return `true` if contains, else `false`. */ -internal fun AsmBuilder.tryInvokeSpecific(context: Algebra, name: String, arity: Int): Boolean { +private fun AsmBuilder.tryInvokeSpecific(context: Algebra, name: String, arity: Int): Boolean { val theName = methodNameAdapters[name to arity] ?: name context.javaClass.methods.find { @@ -59,3 +59,28 @@ internal fun AsmBuilder.tryInvokeSpecific(context: Algebra, name: Stri return true } + +internal fun AsmBuilder.buildAlgebraOperationCall( + context: Algebra, + name: String, + fallbackMethodName: String, + arity: Int, + parameters: AsmBuilder.() -> Unit +) { + loadAlgebra() + if (!buildExpectationStack(context, name, arity)) loadStringConstant(name) + parameters() + + if (!tryInvokeSpecific(context, name, arity)) invokeAlgebraOperation( + owner = AsmBuilder.ALGEBRA_TYPE.internalName, + method = fallbackMethodName, + + descriptor = Type.getMethodDescriptor( + AsmBuilder.OBJECT_TYPE, + AsmBuilder.STRING_TYPE, + *Array(arity) { AsmBuilder.OBJECT_TYPE } + ), + + expectedArity = arity + ) +} -- 2.34.1 From e98fc126c4118bcf8750ea85bb25bca2d85b145f Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Sat, 27 Jun 2020 20:15:14 +0700 Subject: [PATCH 115/156] Merge various codegen utilities into one file --- .../kmath/asm/internal/buildName.kt | 22 ------- .../kmath/asm/internal/classWriters.kt | 17 ----- .../scientifik/kmath/asm/internal/classes.kt | 7 -- .../{specialization.kt => codegenUtils.kt} | 64 ++++++++++++++++++- .../kmath/asm/internal/instructionAdapters.kt | 10 --- .../kmath/asm/internal/methodVisitors.kt | 9 --- 6 files changed, 63 insertions(+), 66 deletions(-) delete mode 100644 kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/buildName.kt delete mode 100644 kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/classWriters.kt delete mode 100644 kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/classes.kt rename kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/{specialization.kt => codegenUtils.kt} (57%) delete mode 100644 kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/instructionAdapters.kt delete mode 100644 kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/methodVisitors.kt diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/buildName.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/buildName.kt deleted file mode 100644 index 41dbf5807..000000000 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/buildName.kt +++ /dev/null @@ -1,22 +0,0 @@ -package scientifik.kmath.asm.internal - -import scientifik.kmath.ast.MST -import scientifik.kmath.expressions.Expression - -/** - * Creates a class name for [Expression] subclassed to implement [mst] provided. - * - * This methods helps to avoid collisions of class name to prevent loading several classes with the same name. If there - * is a colliding class, change [collision] parameter or leave it `0` to check existing classes recursively. - */ -internal tailrec fun buildName(mst: MST, collision: Int = 0): String { - val name = "scientifik.kmath.asm.generated.AsmCompiledExpression_${mst.hashCode()}_$collision" - - try { - Class.forName(name) - } catch (ignored: ClassNotFoundException) { - return name - } - - return buildName(mst, collision + 1) -} diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/classWriters.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/classWriters.kt deleted file mode 100644 index 7f0770b28..000000000 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/classWriters.kt +++ /dev/null @@ -1,17 +0,0 @@ -package scientifik.kmath.asm.internal - -import org.objectweb.asm.ClassWriter -import org.objectweb.asm.FieldVisitor - -@Suppress("FunctionName") -internal inline fun ClassWriter(flags: Int, block: ClassWriter.() -> Unit): ClassWriter = - ClassWriter(flags).apply(block) - -internal inline fun ClassWriter.visitField( - access: Int, - name: String, - descriptor: String, - signature: String?, - value: Any?, - block: FieldVisitor.() -> Unit -): FieldVisitor = visitField(access, name, descriptor, signature, value).apply(block) diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/classes.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/classes.kt deleted file mode 100644 index dc0b35531..000000000 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/classes.kt +++ /dev/null @@ -1,7 +0,0 @@ -package scientifik.kmath.asm.internal - -import org.objectweb.asm.Type -import kotlin.reflect.KClass - -internal val KClass<*>.asm: Type - get() = Type.getType(java) diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/specialization.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/codegenUtils.kt similarity index 57% rename from kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/specialization.kt rename to kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/codegenUtils.kt index 003dc47dd..46d07976d 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/specialization.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/codegenUtils.kt @@ -1,8 +1,12 @@ package scientifik.kmath.asm.internal +import org.objectweb.asm.* import org.objectweb.asm.Opcodes.INVOKEVIRTUAL -import org.objectweb.asm.Type +import org.objectweb.asm.commons.InstructionAdapter +import scientifik.kmath.ast.MST +import scientifik.kmath.expressions.Expression import scientifik.kmath.operations.Algebra +import kotlin.reflect.KClass private val methodNameAdapters: Map, String> by lazy { hashMapOf( @@ -15,6 +19,60 @@ private val methodNameAdapters: Map, String> by lazy { ) } +internal val KClass<*>.asm: Type + get() = Type.getType(java) + +/** + * Creates an [InstructionAdapter] from this [MethodVisitor]. + */ +private fun MethodVisitor.instructionAdapter(): InstructionAdapter = InstructionAdapter(this) + +/** + * Creates an [InstructionAdapter] from this [MethodVisitor] and applies [block] to it. + */ +internal fun MethodVisitor.instructionAdapter(block: InstructionAdapter.() -> Unit): InstructionAdapter = + instructionAdapter().apply(block) + +/** + * Constructs a [Label], then applies it to this visitor. + */ +internal fun MethodVisitor.label(): Label { + val l = Label() + visitLabel(l) + return l +} + +/** + * Creates a class name for [Expression] subclassed to implement [mst] provided. + * + * This methods helps to avoid collisions of class name to prevent loading several classes with the same name. If there + * is a colliding class, change [collision] parameter or leave it `0` to check existing classes recursively. + */ +internal tailrec fun buildName(mst: MST, collision: Int = 0): String { + val name = "scientifik.kmath.asm.generated.AsmCompiledExpression_${mst.hashCode()}_$collision" + + try { + Class.forName(name) + } catch (ignored: ClassNotFoundException) { + return name + } + + return buildName(mst, collision + 1) +} + +@Suppress("FunctionName") +internal inline fun ClassWriter(flags: Int, block: ClassWriter.() -> Unit): ClassWriter = + ClassWriter(flags).apply(block) + +internal inline fun ClassWriter.visitField( + access: Int, + name: String, + descriptor: String, + signature: String?, + value: Any?, + block: FieldVisitor.() -> Unit +): FieldVisitor = visitField(access, name, descriptor, signature, value).apply(block) + /** * Checks if the target [context] for code generation contains a method with needed [name] and [arity], also builds * type expectation stack for needed arity. @@ -60,6 +118,9 @@ private fun AsmBuilder.tryInvokeSpecific(context: Algebra, name: Strin return true } +/** + * Builds specialized algebra call with option to fallback to generic algebra operation accepting String. + */ internal fun AsmBuilder.buildAlgebraOperationCall( context: Algebra, name: String, @@ -84,3 +145,4 @@ internal fun AsmBuilder.buildAlgebraOperationCall( expectedArity = arity ) } + diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/instructionAdapters.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/instructionAdapters.kt deleted file mode 100644 index f47293687..000000000 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/instructionAdapters.kt +++ /dev/null @@ -1,10 +0,0 @@ -package scientifik.kmath.asm.internal - -import org.objectweb.asm.Label -import org.objectweb.asm.commons.InstructionAdapter - -internal fun InstructionAdapter.label(): Label { - val l = Label() - visitLabel(l) - return l -} diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/methodVisitors.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/methodVisitors.kt deleted file mode 100644 index aaae02ebb..000000000 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/methodVisitors.kt +++ /dev/null @@ -1,9 +0,0 @@ -package scientifik.kmath.asm.internal - -import org.objectweb.asm.MethodVisitor -import org.objectweb.asm.commons.InstructionAdapter - -internal fun MethodVisitor.instructionAdapter(): InstructionAdapter = InstructionAdapter(this) - -internal fun MethodVisitor.instructionAdapter(block: InstructionAdapter.() -> Unit): InstructionAdapter = - instructionAdapter().apply(block) -- 2.34.1 From e91c5a57c493dbee9f8f1545b294b69848572cf0 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Sat, 27 Jun 2020 20:31:42 +0700 Subject: [PATCH 116/156] Minor refactor for changed ExtendedFieldOperations, replace DoubleBuffer with RealBuffer --- .../kmath/operations/NumberAlgebra.kt | 4 +- .../kmath/operations/OptionalOperations.kt | 4 +- .../kmath/structures/RealBufferField.kt | 160 ++++++++---------- 3 files changed, 76 insertions(+), 92 deletions(-) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/NumberAlgebra.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/NumberAlgebra.kt index 3fb57656c..953c5a112 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/NumberAlgebra.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/NumberAlgebra.kt @@ -7,7 +7,6 @@ import kotlin.math.pow as kpow * Advanced Number-like field that implements basic operations */ interface ExtendedFieldOperations : - FieldOperations, InverseTrigonometricOperations, PowerOperations, ExponentialOperations { @@ -24,9 +23,8 @@ interface ExtendedFieldOperations : PowerOperations.SQRT_OPERATION -> sqrt(arg) ExponentialOperations.EXP_OPERATION -> exp(arg) ExponentialOperations.LN_OPERATION -> ln(arg) - else -> super.unaryOperation(operation, arg) + else -> super.unaryOperation(operation, arg) } - } interface ExtendedField : ExtendedFieldOperations, Field { 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 542d6376b..709f0260f 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/OptionalOperations.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/OptionalOperations.kt @@ -64,7 +64,7 @@ fun >> sqr(arg: T): T = arg pow 2.0 /* Exponential */ -interface ExponentialOperations: Algebra { +interface ExponentialOperations : Algebra { fun exp(arg: T): T fun ln(arg: T): T @@ -81,4 +81,4 @@ 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) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/RealBufferField.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/RealBufferField.kt index 2fb6d15d4..826203d1f 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/RealBufferField.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/RealBufferField.kt @@ -9,185 +9,171 @@ import kotlin.math.* * A simple field over linear buffers of [Double] */ object RealBufferFieldOperations : ExtendedFieldOperations> { - - override fun add(a: Buffer, b: Buffer): DoubleBuffer { + override fun add(a: Buffer, b: Buffer): RealBuffer { require(b.size == a.size) { "The size of the first buffer ${a.size} should be the same as for second one: ${b.size} " } - return if (a is DoubleBuffer && b is DoubleBuffer) { + return if (a is RealBuffer && b is RealBuffer) { val aArray = a.array val bArray = b.array - DoubleBuffer(DoubleArray(a.size) { aArray[it] + bArray[it] }) - } else { - DoubleBuffer(DoubleArray(a.size) { a[it] + b[it] }) - } + RealBuffer(DoubleArray(a.size) { aArray[it] + bArray[it] }) + } else + RealBuffer(DoubleArray(a.size) { a[it] + b[it] }) } - override fun multiply(a: Buffer, k: Number): DoubleBuffer { + override fun multiply(a: Buffer, k: Number): RealBuffer { val kValue = k.toDouble() - return if (a is DoubleBuffer) { + return if (a is RealBuffer) { val aArray = a.array - DoubleBuffer(DoubleArray(a.size) { aArray[it] * kValue }) - } else { - DoubleBuffer(DoubleArray(a.size) { a[it] * kValue }) - } + RealBuffer(DoubleArray(a.size) { aArray[it] * kValue }) + } else + RealBuffer(DoubleArray(a.size) { a[it] * kValue }) } - override fun multiply(a: Buffer, b: Buffer): DoubleBuffer { + override fun multiply(a: Buffer, b: Buffer): RealBuffer { require(b.size == a.size) { "The size of the first buffer ${a.size} should be the same as for second one: ${b.size} " } - return if (a is DoubleBuffer && b is DoubleBuffer) { + return if (a is RealBuffer && b is RealBuffer) { val aArray = a.array val bArray = b.array - DoubleBuffer(DoubleArray(a.size) { aArray[it] * bArray[it] }) - } else { - DoubleBuffer(DoubleArray(a.size) { a[it] * b[it] }) - } + RealBuffer(DoubleArray(a.size) { aArray[it] * bArray[it] }) + } else + RealBuffer(DoubleArray(a.size) { a[it] * b[it] }) } - override fun divide(a: Buffer, b: Buffer): DoubleBuffer { + override fun divide(a: Buffer, b: Buffer): RealBuffer { require(b.size == a.size) { "The size of the first buffer ${a.size} should be the same as for second one: ${b.size} " } - return if (a is DoubleBuffer && b is DoubleBuffer) { + return if (a is RealBuffer && b is RealBuffer) { val aArray = a.array val bArray = b.array - DoubleBuffer(DoubleArray(a.size) { aArray[it] / bArray[it] }) - } else { - DoubleBuffer(DoubleArray(a.size) { a[it] / b[it] }) - } + RealBuffer(DoubleArray(a.size) { aArray[it] / bArray[it] }) + } else + RealBuffer(DoubleArray(a.size) { a[it] / b[it] }) } - override fun sin(arg: Buffer): DoubleBuffer = if (arg is DoubleBuffer) { + override fun sin(arg: Buffer): RealBuffer = if (arg is RealBuffer) { val array = arg.array - DoubleBuffer(DoubleArray(arg.size) { sin(array[it]) }) + RealBuffer(DoubleArray(arg.size) { sin(array[it]) }) } else { - DoubleBuffer(DoubleArray(arg.size) { sin(arg[it]) }) + RealBuffer(DoubleArray(arg.size) { sin(arg[it]) }) } - override fun cos(arg: Buffer): DoubleBuffer = if (arg is DoubleBuffer) { + override fun cos(arg: Buffer): RealBuffer = if (arg is RealBuffer) { val array = arg.array - DoubleBuffer(DoubleArray(arg.size) { cos(array[it]) }) + RealBuffer(DoubleArray(arg.size) { cos(array[it]) }) + } else + RealBuffer(DoubleArray(arg.size) { cos(arg[it]) }) + + override fun tan(arg: Buffer): RealBuffer = if (arg is RealBuffer) { + val array = arg.array + RealBuffer(DoubleArray(arg.size) { tan(array[it]) }) + } else + RealBuffer(DoubleArray(arg.size) { tan(arg[it]) }) + + override fun asin(arg: Buffer): RealBuffer = if (arg is RealBuffer) { + val array = arg.array + RealBuffer(DoubleArray(arg.size) { asin(array[it]) }) } else { - DoubleBuffer(DoubleArray(arg.size) { cos(arg[it]) }) + RealBuffer(DoubleArray(arg.size) { asin(arg[it]) }) } - override fun tan(arg: Buffer): DoubleBuffer = if (arg is DoubleBuffer) { + override fun acos(arg: Buffer): RealBuffer = if (arg is RealBuffer) { val array = arg.array - DoubleBuffer(DoubleArray(arg.size) { tan(array[it]) }) - } else { - DoubleBuffer(DoubleArray(arg.size) { tan(arg[it]) }) - } + RealBuffer(DoubleArray(arg.size) { acos(array[it]) }) + } else + RealBuffer(DoubleArray(arg.size) { acos(arg[it]) }) - override fun asin(arg: Buffer): DoubleBuffer = if (arg is DoubleBuffer) { + override fun atan(arg: Buffer): RealBuffer = if (arg is RealBuffer) { val array = arg.array - DoubleBuffer(DoubleArray(arg.size) { asin(array[it]) }) - } else { - DoubleBuffer(DoubleArray(arg.size) { asin(arg[it]) }) - } + RealBuffer(DoubleArray(arg.size) { atan(array[it]) }) + } else + RealBuffer(DoubleArray(arg.size) { atan(arg[it]) }) - override fun acos(arg: Buffer): DoubleBuffer = if (arg is DoubleBuffer) { + override fun power(arg: Buffer, pow: Number): RealBuffer = if (arg is RealBuffer) { val array = arg.array - DoubleBuffer(DoubleArray(arg.size) { acos(array[it]) }) - } else { - DoubleBuffer(DoubleArray(arg.size) { acos(arg[it]) }) - } + RealBuffer(DoubleArray(arg.size) { array[it].pow(pow.toDouble()) }) + } else + RealBuffer(DoubleArray(arg.size) { arg[it].pow(pow.toDouble()) }) - override fun atan(arg: Buffer): DoubleBuffer = if (arg is DoubleBuffer) { + override fun exp(arg: Buffer): RealBuffer = if (arg is RealBuffer) { val array = arg.array - DoubleBuffer(DoubleArray(arg.size) { atan(array[it]) }) - } else { - DoubleBuffer(DoubleArray(arg.size) { atan(arg[it]) }) - } + RealBuffer(DoubleArray(arg.size) { exp(array[it]) }) + } else + RealBuffer(DoubleArray(arg.size) { exp(arg[it]) }) - override fun power(arg: Buffer, pow: Number): DoubleBuffer = if (arg is DoubleBuffer) { + override fun ln(arg: Buffer): RealBuffer = if (arg is RealBuffer) { val array = arg.array - DoubleBuffer(DoubleArray(arg.size) { array[it].pow(pow.toDouble()) }) - } else { - DoubleBuffer(DoubleArray(arg.size) { arg[it].pow(pow.toDouble()) }) - } - - override fun exp(arg: Buffer): DoubleBuffer = if (arg is DoubleBuffer) { - val array = arg.array - DoubleBuffer(DoubleArray(arg.size) { exp(array[it]) }) - } else { - DoubleBuffer(DoubleArray(arg.size) { exp(arg[it]) }) - } - - override fun ln(arg: Buffer): DoubleBuffer = if (arg is DoubleBuffer) { - val array = arg.array - DoubleBuffer(DoubleArray(arg.size) { ln(array[it]) }) - } else { - DoubleBuffer(DoubleArray(arg.size) { ln(arg[it]) }) - } + RealBuffer(DoubleArray(arg.size) { ln(array[it]) }) + } else + RealBuffer(DoubleArray(arg.size) { ln(arg[it]) }) } class RealBufferField(val size: Int) : ExtendedField> { + override val zero: Buffer by lazy { RealBuffer(size) { 0.0 } } + override val one: Buffer by lazy { RealBuffer(size) { 1.0 } } - override val zero: Buffer by lazy { DoubleBuffer(size) { 0.0 } } - - override val one: Buffer by lazy { DoubleBuffer(size) { 1.0 } } - - override fun add(a: Buffer, b: Buffer): DoubleBuffer { + override fun add(a: Buffer, b: Buffer): RealBuffer { require(a.size == size) { "The buffer size ${a.size} does not match context size $size" } return RealBufferFieldOperations.add(a, b) } - override fun multiply(a: Buffer, k: Number): DoubleBuffer { + override fun multiply(a: Buffer, k: Number): RealBuffer { require(a.size == size) { "The buffer size ${a.size} does not match context size $size" } return RealBufferFieldOperations.multiply(a, k) } - override fun multiply(a: Buffer, b: Buffer): DoubleBuffer { + override fun multiply(a: Buffer, b: Buffer): RealBuffer { require(a.size == size) { "The buffer size ${a.size} does not match context size $size" } return RealBufferFieldOperations.multiply(a, b) } - override fun divide(a: Buffer, b: Buffer): DoubleBuffer { + override fun divide(a: Buffer, b: Buffer): RealBuffer { require(a.size == size) { "The buffer size ${a.size} does not match context size $size" } return RealBufferFieldOperations.divide(a, b) } - override fun sin(arg: Buffer): DoubleBuffer { + override fun sin(arg: Buffer): RealBuffer { require(arg.size == size) { "The buffer size ${arg.size} does not match context size $size" } return RealBufferFieldOperations.sin(arg) } - override fun cos(arg: Buffer): DoubleBuffer { + override fun cos(arg: Buffer): RealBuffer { require(arg.size == size) { "The buffer size ${arg.size} does not match context size $size" } return RealBufferFieldOperations.cos(arg) } - override fun tan(arg: Buffer): DoubleBuffer { + override fun tan(arg: Buffer): RealBuffer { require(arg.size == size) { "The buffer size ${arg.size} does not match context size $size" } return RealBufferFieldOperations.tan(arg) } - override fun asin(arg: Buffer): DoubleBuffer { + override fun asin(arg: Buffer): RealBuffer { require(arg.size == size) { "The buffer size ${arg.size} does not match context size $size" } return RealBufferFieldOperations.asin(arg) } - override fun acos(arg: Buffer): DoubleBuffer { + override fun acos(arg: Buffer): RealBuffer { require(arg.size == size) { "The buffer size ${arg.size} does not match context size $size" } return RealBufferFieldOperations.acos(arg) } - override fun atan(arg: Buffer): DoubleBuffer { + override fun atan(arg: Buffer): RealBuffer { require(arg.size == size) { "The buffer size ${arg.size} does not match context size $size" } return RealBufferFieldOperations.atan(arg) } - override fun power(arg: Buffer, pow: Number): DoubleBuffer { + override fun power(arg: Buffer, pow: Number): RealBuffer { require(arg.size == size) { "The buffer size ${arg.size} does not match context size $size" } return RealBufferFieldOperations.power(arg, pow) } - override fun exp(arg: Buffer): DoubleBuffer { + override fun exp(arg: Buffer): RealBuffer { require(arg.size == size) { "The buffer size ${arg.size} does not match context size $size" } return RealBufferFieldOperations.exp(arg) } - override fun ln(arg: Buffer): DoubleBuffer { + override fun ln(arg: Buffer): RealBuffer { require(arg.size == size) { "The buffer size ${arg.size} does not match context size $size" } return RealBufferFieldOperations.ln(arg) } -- 2.34.1 From 54cb9b18830b9f1b790f7dd978350f20cfeddc6e Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Sat, 27 Jun 2020 21:56:58 +0700 Subject: [PATCH 117/156] Update kmath-ast/README.md to decipher acronyms --- kmath-ast/README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/kmath-ast/README.md b/kmath-ast/README.md index 62b18b4b5..a72ecd327 100644 --- a/kmath-ast/README.md +++ b/kmath-ast/README.md @@ -1,13 +1,13 @@ -# AST-based expression representation and operations (`kmath-ast`) +# Abstract syntax tree expression representation and operations (`kmath-ast`) This subproject implements the following features: - Expression Language and its parser. -- MST as expression language's syntax intermediate representation. -- Type-safe builder of MST. +- MST (Mathematical Syntax Tree) as expression language's syntax intermediate representation. +- Type-safe builder for MST. - Evaluating expressions by traversing MST. -## Dynamic expression code generation with OW2 ASM +## Dynamic expression code generation with ObjectWeb ASM `kmath-ast` JVM module supports runtime code generation to eliminate overhead of tree traversal. Code generator builds a special implementation of `Expression` with implemented `invoke` function. @@ -46,7 +46,7 @@ public final class AsmCompiledExpression_1073786867_0 implements Expression Date: Sat, 27 Jun 2020 22:18:58 +0700 Subject: [PATCH 118/156] Update kmath/ast README.md to add Maven artifact reference --- kmath-ast/README.md | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/kmath-ast/README.md b/kmath-ast/README.md index a72ecd327..ccfe98a96 100644 --- a/kmath-ast/README.md +++ b/kmath-ast/README.md @@ -7,6 +7,33 @@ This subproject implements the following features: - Type-safe builder for MST. - Evaluating expressions by traversing MST. +> #### Artifact: +> This module is distributed in the artifact `scientifik:kmath-ast:0.1.4-dev-8`. +> +> **Gradle:** +> +> ```gradle +> repositories { +> maven { url 'https://dl.bintray.com/mipt-npm/dev' } +> } +> +> dependencies { +> implementation 'scientifik:kmath-ast:0.1.4-dev-8' +> } +> ``` +> **Gradle Kotlin DSL:** +> +> ```kotlin +> repositories { +> maven("https://dl.bintray.com/mipt-npm/dev") +> } +> +> dependencies { +> implementation("scientifik:kmath-ast:0.1.4-dev-8") +> } +> ``` +> + ## Dynamic expression code generation with ObjectWeb ASM `kmath-ast` JVM module supports runtime code generation to eliminate overhead of tree traversal. Code generator builds -- 2.34.1 From d7f5d9f53f7950d0718e5904e03f99a4ac6dbaab Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Sat, 27 Jun 2020 22:21:44 +0700 Subject: [PATCH 119/156] Add general repository reference to kmath-ast/README.md --- kmath-ast/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/kmath-ast/README.md b/kmath-ast/README.md index ccfe98a96..ec80f8169 100644 --- a/kmath-ast/README.md +++ b/kmath-ast/README.md @@ -14,6 +14,7 @@ This subproject implements the following features: > > ```gradle > repositories { +> maven { url 'https://dl.bintray.com/mipt-npm/scientifik' } > maven { url 'https://dl.bintray.com/mipt-npm/dev' } > } > @@ -25,6 +26,7 @@ This subproject implements the following features: > > ```kotlin > repositories { +> maven("https://dl.bintray.com/mipt-npm/scientifik") > maven("https://dl.bintray.com/mipt-npm/dev") > } > -- 2.34.1 From 63c001648e41770879f7a12f94622134e20cf609 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Sun, 28 Jun 2020 02:08:26 +0700 Subject: [PATCH 120/156] Remove duplicated code, optimize constants field generation, add overloads for getOrFail in mapIntrinsics --- kmath-ast/README.md | 12 +- .../kmath/asm/internal/AsmBuilder.kt | 155 ++++++++++-------- .../kmath/asm/internal/codegenUtils.kt | 11 +- .../kmath/asm/internal/instructionAdapters.kt | 10 -- .../kmath/asm/internal/mapIntrinsics.kt | 6 +- .../kotlin/scietifik/kmath/ast/AsmTest.kt | 3 +- 6 files changed, 106 insertions(+), 91 deletions(-) delete mode 100644 kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/instructionAdapters.kt diff --git a/kmath-ast/README.md b/kmath-ast/README.md index ec80f8169..baeb60c39 100644 --- a/kmath-ast/README.md +++ b/kmath-ast/README.md @@ -59,15 +59,13 @@ import scientifik.kmath.operations.RealField; public final class AsmCompiledExpression_1073786867_0 implements Expression { private final RealField algebra; - private final Object[] constants; - - public AsmCompiledExpression_1073786867_0(RealField algebra, Object[] constants) { - this.algebra = algebra; - this.constants = constants; - } public final Double invoke(Map arguments) { - return (Double)this.algebra.add(((Double)MapIntrinsics.getOrFail(arguments, "x", (Object)null)).doubleValue(), 2.0D); + return (Double)this.algebra.add(((Double)MapIntrinsics.getOrFail(arguments, "x")).doubleValue(), 2.0D); + } + + public AsmCompiledExpression_1073786867_0(RealField algebra) { + this.algebra = algebra; } } diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt index cea6be933..92f808be8 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt @@ -38,17 +38,17 @@ internal class AsmBuilder internal constructor( private val classLoader: ClassLoader = ClassLoader(javaClass.classLoader) /** - * ASM Type for [algebra] + * ASM Type for [algebra]. */ private val tAlgebraType: Type = algebra::class.asm /** - * ASM type for [T] + * ASM type for [T]. */ internal val tType: Type = classOfT.asm /** - * ASM type for new class + * ASM type for new class. */ private val classType: Type = Type.getObjectType(className.replace(oldChar = '.', newChar = '/'))!! @@ -72,6 +72,11 @@ internal class AsmBuilder internal constructor( */ private lateinit var invokeMethodVisitor: InstructionAdapter + /** + * State if this [AsmBuilder] needs to generate constants field. + */ + private var hasConstants = true + /** * State if [T] a primitive type, so [AsmBuilder] may generate direct primitive calls. */ @@ -108,7 +113,7 @@ internal class AsmBuilder internal constructor( * The built instance is cached. */ @Suppress("UNCHECKED_CAST") - fun getInstance(): Expression { + internal fun getInstance(): Expression { generatedInstance?.let { return it } if (SIGNATURE_LETTERS.containsKey(classOfT)) { @@ -127,64 +132,6 @@ internal class AsmBuilder internal constructor( arrayOf(EXPRESSION_TYPE.internalName) ) - visitField( - access = ACC_PRIVATE or ACC_FINAL, - name = "algebra", - descriptor = tAlgebraType.descriptor, - signature = null, - value = null, - block = FieldVisitor::visitEnd - ) - - visitField( - access = ACC_PRIVATE or ACC_FINAL, - name = "constants", - descriptor = OBJECT_ARRAY_TYPE.descriptor, - signature = null, - value = null, - block = FieldVisitor::visitEnd - ) - - visitMethod( - ACC_PUBLIC, - "", - Type.getMethodDescriptor(Type.VOID_TYPE, tAlgebraType, OBJECT_ARRAY_TYPE), - null, - null - ).instructionAdapter { - val thisVar = 0 - val algebraVar = 1 - val constantsVar = 2 - val l0 = label() - load(thisVar, classType) - invokespecial(OBJECT_TYPE.internalName, "", Type.getMethodDescriptor(Type.VOID_TYPE), false) - label() - load(thisVar, classType) - load(algebraVar, tAlgebraType) - putfield(classType.internalName, "algebra", tAlgebraType.descriptor) - label() - load(thisVar, classType) - load(constantsVar, OBJECT_ARRAY_TYPE) - putfield(classType.internalName, "constants", OBJECT_ARRAY_TYPE.descriptor) - label() - visitInsn(RETURN) - val l4 = label() - visitLocalVariable("this", classType.descriptor, null, l0, l4, thisVar) - - visitLocalVariable( - "algebra", - tAlgebraType.descriptor, - null, - l0, - l4, - algebraVar - ) - - visitLocalVariable("constants", OBJECT_ARRAY_TYPE.descriptor, null, l0, l4, constantsVar) - visitMaxs(0, 3) - visitEnd() - } - visitMethod( ACC_PUBLIC or ACC_FINAL, "invoke", @@ -251,6 +198,78 @@ internal class AsmBuilder internal constructor( visitEnd() } + hasConstants = constants.isNotEmpty() + + visitField( + access = ACC_PRIVATE or ACC_FINAL, + name = "algebra", + descriptor = tAlgebraType.descriptor, + signature = null, + value = null, + block = FieldVisitor::visitEnd + ) + + if (hasConstants) + visitField( + access = ACC_PRIVATE or ACC_FINAL, + name = "constants", + descriptor = OBJECT_ARRAY_TYPE.descriptor, + signature = null, + value = null, + block = FieldVisitor::visitEnd + ) + + visitMethod( + ACC_PUBLIC, + "", + + Type.getMethodDescriptor( + Type.VOID_TYPE, + tAlgebraType, + *OBJECT_ARRAY_TYPE.wrapToArrayIf { hasConstants }), + + null, + null + ).instructionAdapter { + val thisVar = 0 + val algebraVar = 1 + val constantsVar = 2 + val l0 = label() + load(thisVar, classType) + invokespecial(OBJECT_TYPE.internalName, "", Type.getMethodDescriptor(Type.VOID_TYPE), false) + label() + load(thisVar, classType) + load(algebraVar, tAlgebraType) + putfield(classType.internalName, "algebra", tAlgebraType.descriptor) + + if (hasConstants) { + label() + load(thisVar, classType) + load(constantsVar, OBJECT_ARRAY_TYPE) + putfield(classType.internalName, "constants", OBJECT_ARRAY_TYPE.descriptor) + } + + label() + visitInsn(RETURN) + val l4 = label() + visitLocalVariable("this", classType.descriptor, null, l0, l4, thisVar) + + visitLocalVariable( + "algebra", + tAlgebraType.descriptor, + null, + l0, + l4, + algebraVar + ) + + if (hasConstants) + visitLocalVariable("constants", OBJECT_ARRAY_TYPE.descriptor, null, l0, l4, constantsVar) + + visitMaxs(0, 3) + visitEnd() + } + visitEnd() } @@ -258,7 +277,7 @@ internal class AsmBuilder internal constructor( .defineClass(className, classWriter.toByteArray()) .constructors .first() - .newInstance(algebra, constants.toTypedArray()) as Expression + .newInstance(algebra, *(constants.toTypedArray().wrapToArrayIf { hasConstants })) as Expression generatedInstance = new return new @@ -359,18 +378,20 @@ internal class AsmBuilder internal constructor( if (defaultValue != null) loadTConstant(defaultValue) - else - aconst(null) invokestatic( MAP_INTRINSICS_TYPE.internalName, "getOrFail", - Type.getMethodDescriptor(OBJECT_TYPE, MAP_TYPE, OBJECT_TYPE, OBJECT_TYPE), + + Type.getMethodDescriptor( + OBJECT_TYPE, + MAP_TYPE, + OBJECT_TYPE, + *OBJECT_TYPE.wrapToArrayIf { defaultValue != null }), false ) checkcast(tType) - val expectedType = expectationStack.pop() if (expectedType.sort == Type.OBJECT) diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/codegenUtils.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/codegenUtils.kt index 46d07976d..63681ad96 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/codegenUtils.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/codegenUtils.kt @@ -22,6 +22,14 @@ private val methodNameAdapters: Map, String> by lazy { internal val KClass<*>.asm: Type get() = Type.getType(java) +/** + * Returns singleton array with this value if the [predicate] is true, returns empty array otherwise. + */ +internal inline fun T.wrapToArrayIf(predicate: (T) -> Boolean): Array = if (predicate(this)) + arrayOf(this) +else + emptyArray() + /** * Creates an [InstructionAdapter] from this [MethodVisitor]. */ @@ -121,7 +129,7 @@ private fun AsmBuilder.tryInvokeSpecific(context: Algebra, name: Strin /** * Builds specialized algebra call with option to fallback to generic algebra operation accepting String. */ -internal fun AsmBuilder.buildAlgebraOperationCall( +internal inline fun AsmBuilder.buildAlgebraOperationCall( context: Algebra, name: String, fallbackMethodName: String, @@ -145,4 +153,3 @@ internal fun AsmBuilder.buildAlgebraOperationCall( expectedArity = arity ) } - diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/instructionAdapters.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/instructionAdapters.kt deleted file mode 100644 index f47293687..000000000 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/instructionAdapters.kt +++ /dev/null @@ -1,10 +0,0 @@ -package scientifik.kmath.asm.internal - -import org.objectweb.asm.Label -import org.objectweb.asm.commons.InstructionAdapter - -internal fun InstructionAdapter.label(): Label { - val l = Label() - visitLabel(l) - return l -} diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/mapIntrinsics.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/mapIntrinsics.kt index 7f7126b55..80e83c1bf 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/mapIntrinsics.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/mapIntrinsics.kt @@ -2,6 +2,6 @@ package scientifik.kmath.asm.internal -internal fun Map.getOrFail(key: K, default: V?): V { - return this[key] ?: default ?: error("Parameter not found: $key") -} +@JvmOverloads +internal fun Map.getOrFail(key: K, default: V? = null): V = + this[key] ?: default ?: error("Parameter not found: $key") diff --git a/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/AsmTest.kt b/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/AsmTest.kt index 23203172e..75659cc35 100644 --- a/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/AsmTest.kt +++ b/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/AsmTest.kt @@ -13,8 +13,7 @@ import kotlin.test.assertEquals internal class AsmTest { @Test fun `compile MST`() { - val mst = "2+2*(2+2)".parseMath() - val res = ComplexField.expression(mst)() + val res = ComplexField.expression("2+2*(2+2)".parseMath())() assertEquals(Complex(10.0, 0.0), res) } -- 2.34.1 From 7372197fe1ed27b1adf348ff8a7082696f25cc8d Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Sun, 28 Jun 2020 02:10:26 +0700 Subject: [PATCH 121/156] Specifty type explicitly --- .../jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt index 92f808be8..d51d17992 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt @@ -75,7 +75,7 @@ internal class AsmBuilder internal constructor( /** * State if this [AsmBuilder] needs to generate constants field. */ - private var hasConstants = true + private var hasConstants: Boolean = true /** * State if [T] a primitive type, so [AsmBuilder] may generate direct primitive calls. -- 2.34.1 From 2a34110f1d7ac8deab47f65818641537ca58eff9 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Sun, 28 Jun 2020 17:16:15 +0700 Subject: [PATCH 122/156] Implement advanced specialization for numeric functions --- .../kotlin/scientifik/kmath/asm/asm.kt | 15 +--- .../kmath/asm/internal/AsmBuilder.kt | 75 +++++++++++----- .../scientifik/kmath/asm/internal/MstType.kt | 17 ++++ .../kmath/asm/internal/codegenUtils.kt | 87 ++++++++++++------- .../kmath/asm/TestAsmSpecialization.kt | 9 ++ 5 files changed, 137 insertions(+), 66 deletions(-) create mode 100644 kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/MstType.kt diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/asm.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/asm.kt index ef2330533..f0022a43a 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/asm.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/asm.kt @@ -1,6 +1,7 @@ package scientifik.kmath.asm import scientifik.kmath.asm.internal.AsmBuilder +import scientifik.kmath.asm.internal.MstType import scientifik.kmath.asm.internal.buildAlgebraOperationCall import scientifik.kmath.asm.internal.buildName import scientifik.kmath.ast.MST @@ -17,28 +18,20 @@ fun MST.compileWith(type: KClass, algebra: Algebra): Expression< fun AsmBuilder.visit(node: MST) { when (node) { is MST.Symbolic -> loadVariable(node.value) - - is MST.Numeric -> { - val constant = if (algebra is NumericAlgebra) - algebra.number(node.value) - else - error("Number literals are not supported in $algebra") - - loadTConstant(constant) - } + is MST.Numeric -> loadNumeric(node.value) is MST.Unary -> buildAlgebraOperationCall( context = algebra, name = node.operation, fallbackMethodName = "unaryOperation", - arity = 1 + parameterTypes = arrayOf(MstType.fromMst(node.value)) ) { visit(node.value) } is MST.Binary -> buildAlgebraOperationCall( context = algebra, name = node.operation, fallbackMethodName = "binaryOperation", - arity = 2 + parameterTypes = arrayOf(MstType.fromMst(node.left), MstType.fromMst(node.right)) ) { visit(node.left) visit(node.right) diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt index d51d17992..e550bc563 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt @@ -7,7 +7,9 @@ import scientifik.kmath.asm.internal.AsmBuilder.ClassLoader import scientifik.kmath.ast.MST import scientifik.kmath.expressions.Expression import scientifik.kmath.operations.Algebra +import scientifik.kmath.operations.NumericAlgebra import java.util.* +import java.util.stream.Collectors import kotlin.reflect.KClass /** @@ -20,7 +22,7 @@ import kotlin.reflect.KClass * @param invokeLabel0Visitor the function to apply to this object when generating invoke method, label 0. */ internal class AsmBuilder internal constructor( - private val classOfT: KClass<*>, + internal val classOfT: KClass<*>, private val algebra: Algebra, private val className: String, private val invokeLabel0Visitor: AsmBuilder.() -> Unit @@ -100,7 +102,7 @@ internal class AsmBuilder internal constructor( /** * Stack of useful objects types on stack expected by algebra calls. */ - internal val expectationStack: ArrayDeque = ArrayDeque().apply { push(tType) } + internal val expectationStack: ArrayDeque = ArrayDeque(listOf(tType)) /** * The cache for instance built by this builder. @@ -286,42 +288,50 @@ internal class AsmBuilder internal constructor( /** * Loads a [T] constant from [constants]. */ - internal fun loadTConstant(value: T) { + private fun loadTConstant(value: T) { if (classOfT in INLINABLE_NUMBERS) { val expectedType = expectationStack.pop() val mustBeBoxed = expectedType.sort == Type.OBJECT loadNumberConstant(value as Number, mustBeBoxed) + + if (mustBeBoxed) + invokeMethodVisitor.checkcast(tType) + if (mustBeBoxed) typeStack.push(tType) else typeStack.push(primitiveMask) return } - loadConstant(value as Any, tType) + loadObjectConstant(value as Any, tType) } /** * Boxes the current value and pushes it. */ - private fun box(): Unit = invokeMethodVisitor.invokestatic( - tType.internalName, - "valueOf", - Type.getMethodDescriptor(tType, primitiveMask), - false - ) + private fun box(primitive: Type) { + val r = PRIMITIVES_TO_BOXED.getValue(primitive) + + invokeMethodVisitor.invokestatic( + r.internalName, + "valueOf", + Type.getMethodDescriptor(r, primitive), + false + ) + } /** * Unboxes the current boxed value and pushes it. */ - private fun unbox(): Unit = invokeMethodVisitor.invokevirtual( + private fun unboxTo(primitive: Type) = invokeMethodVisitor.invokevirtual( NUMBER_TYPE.internalName, - NUMBER_CONVERTER_METHODS.getValue(primitiveMask), - Type.getMethodDescriptor(primitiveMask), + NUMBER_CONVERTER_METHODS.getValue(primitive), + Type.getMethodDescriptor(primitive), false ) /** * Loads [java.lang.Object] constant from constants. */ - private fun loadConstant(value: Any, type: Type): Unit = invokeMethodVisitor.run { + private fun loadObjectConstant(value: Any, type: Type): Unit = invokeMethodVisitor.run { val idx = if (value in constants) constants.indexOf(value) else constants.apply { add(value) }.lastIndex loadThis() getfield(classType.internalName, "constants", OBJECT_ARRAY_TYPE.descriptor) @@ -330,6 +340,15 @@ internal class AsmBuilder internal constructor( checkcast(type) } + fun loadNumeric(value: Number) { + if (expectationStack.peek() == NUMBER_TYPE) { + loadNumberConstant(value, true) + expectationStack.pop() + typeStack.push(NUMBER_TYPE) + } else (algebra as? NumericAlgebra)?.number(value)?.let { loadTConstant(it) } + ?: error("Cannot resolve numeric $value since target algebra is not numeric, and the current operation doesn't accept numbers.") + } + /** * Loads this variable. */ @@ -354,18 +373,16 @@ internal class AsmBuilder internal constructor( Type.SHORT_TYPE -> invokeMethodVisitor.iconst(value.toInt()) } - if (mustBeBoxed) { - box() - invokeMethodVisitor.checkcast(tType) - } + if (mustBeBoxed) + box(primitive) return } - loadConstant(value, boxed) + loadObjectConstant(value, boxed) - if (!mustBeBoxed) unbox() - else invokeMethodVisitor.checkcast(tType) + if (!mustBeBoxed) + unboxTo(primitiveMask) } /** @@ -397,7 +414,7 @@ internal class AsmBuilder internal constructor( if (expectedType.sort == Type.OBJECT) typeStack.push(tType) else { - unbox() + unboxTo(primitiveMask) typeStack.push(primitiveMask) } } @@ -446,7 +463,7 @@ internal class AsmBuilder internal constructor( if (expectedType.sort == Type.OBJECT || isLastExpr) typeStack.push(tType) else { - unbox() + unboxTo(primitiveMask) typeStack.push(primitiveMask) } } @@ -476,6 +493,18 @@ internal class AsmBuilder internal constructor( */ private val BOXED_TO_PRIMITIVES: Map by lazy { SIGNATURE_LETTERS.mapKeys { (k, _) -> k.asm } } + /** + * Maps JVM primitive numbers boxed ASM types to their primitive ASM types. + */ + private val PRIMITIVES_TO_BOXED: Map by lazy { + BOXED_TO_PRIMITIVES.entries.stream().collect( + Collectors.toMap( + Map.Entry::value, + Map.Entry::key + ) + ) + } + /** * Maps primitive ASM types to [Number] functions unboxing them. */ diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/MstType.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/MstType.kt new file mode 100644 index 000000000..bf73d304b --- /dev/null +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/MstType.kt @@ -0,0 +1,17 @@ +package scientifik.kmath.asm.internal + +import scientifik.kmath.ast.MST + +internal enum class MstType { + GENERAL, + NUMBER; + + companion object { + fun fromMst(mst: MST): MstType { + if (mst is MST.Numeric) + return NUMBER + + return GENERAL + } + } +} diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/codegenUtils.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/codegenUtils.kt index 63681ad96..a637289b8 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/codegenUtils.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/codegenUtils.kt @@ -6,6 +6,7 @@ import org.objectweb.asm.commons.InstructionAdapter import scientifik.kmath.ast.MST import scientifik.kmath.expressions.Expression import scientifik.kmath.operations.Algebra +import java.lang.reflect.Method import kotlin.reflect.KClass private val methodNameAdapters: Map, String> by lazy { @@ -25,10 +26,8 @@ internal val KClass<*>.asm: Type /** * Returns singleton array with this value if the [predicate] is true, returns empty array otherwise. */ -internal inline fun T.wrapToArrayIf(predicate: (T) -> Boolean): Array = if (predicate(this)) - arrayOf(this) -else - emptyArray() +internal inline fun T.wrapToArrayIf(predicate: (T) -> Boolean): Array = + if (predicate(this)) arrayOf(this) else emptyArray() /** * Creates an [InstructionAdapter] from this [MethodVisitor]. @@ -44,11 +43,7 @@ internal fun MethodVisitor.instructionAdapter(block: InstructionAdapter.() -> Un /** * Constructs a [Label], then applies it to this visitor. */ -internal fun MethodVisitor.label(): Label { - val l = Label() - visitLabel(l) - return l -} +internal fun MethodVisitor.label(): Label = Label().also { visitLabel(it) } /** * Creates a class name for [Expression] subclassed to implement [mst] provided. @@ -81,44 +76,71 @@ internal inline fun ClassWriter.visitField( block: FieldVisitor.() -> Unit ): FieldVisitor = visitField(access, name, descriptor, signature, value).apply(block) +private fun AsmBuilder.findSpecific(context: Algebra, name: String, parameterTypes: Array): Method? = + context.javaClass.methods.find { method -> + val nameValid = method.name == name + val arityValid = method.parameters.size == parameterTypes.size + val notBridgeInPrimitive = !(primitiveMode && method.isBridge) + + val paramsValid = method.parameterTypes.zip(parameterTypes).all { (type, mstType) -> + !(mstType != MstType.NUMBER && type == java.lang.Number::class.java) + } + + nameValid && arityValid && notBridgeInPrimitive && paramsValid + } + /** - * Checks if the target [context] for code generation contains a method with needed [name] and [arity], also builds + * Checks if the target [context] for code generation contains a method with needed [name] and arity, also builds * type expectation stack for needed arity. * * @return `true` if contains, else `false`. */ -private fun AsmBuilder.buildExpectationStack(context: Algebra, name: String, arity: Int): Boolean { - val theName = methodNameAdapters[name to arity] ?: name - val hasSpecific = context.javaClass.methods.find { it.name == theName && it.parameters.size == arity } != null - val t = if (primitiveMode && hasSpecific) primitiveMask else tType - repeat(arity) { expectationStack.push(t) } - return hasSpecific +private fun AsmBuilder.buildExpectationStack( + context: Algebra, + name: String, + parameterTypes: Array +): Boolean { + val arity = parameterTypes.size + val specific = findSpecific(context, methodNameAdapters[name to arity] ?: name, parameterTypes) + + if (specific != null) + mapTypes(specific, parameterTypes).reversed().forEach { expectationStack.push(it) } + else + repeat(arity) { expectationStack.push(tType) } + + return specific != null } +private fun AsmBuilder.mapTypes(method: Method, parameterTypes: Array): List = method + .parameterTypes + .zip(parameterTypes) + .map { (type, mstType) -> + when { + type == java.lang.Number::class.java && mstType == MstType.NUMBER -> AsmBuilder.NUMBER_TYPE + else -> if (primitiveMode) primitiveMask else primitiveMaskBoxed + } + } + /** - * Checks if the target [context] for code generation contains a method with needed [name] and [arity] and inserts + * Checks if the target [context] for code generation contains a method with needed [name] and arity and inserts * [AsmBuilder.invokeAlgebraOperation] of this method. * * @return `true` if contains, else `false`. */ -private fun AsmBuilder.tryInvokeSpecific(context: Algebra, name: String, arity: Int): Boolean { +private fun AsmBuilder.tryInvokeSpecific( + context: Algebra, + name: String, + parameterTypes: Array +): Boolean { + val arity = parameterTypes.size val theName = methodNameAdapters[name to arity] ?: name - - context.javaClass.methods.find { - var suitableSignature = it.name == theName && it.parameters.size == arity - - if (primitiveMode && it.isBridge) - suitableSignature = false - - suitableSignature - } ?: return false - + val spec = findSpecific(context, theName, parameterTypes) ?: return false val owner = context::class.asm invokeAlgebraOperation( owner = owner.internalName, method = theName, - descriptor = Type.getMethodDescriptor(primitiveMaskBoxed, *Array(arity) { primitiveMask }), + descriptor = Type.getMethodDescriptor(primitiveMaskBoxed, *mapTypes(spec, parameterTypes).toTypedArray()), expectedArity = arity, opcode = INVOKEVIRTUAL ) @@ -133,14 +155,15 @@ internal inline fun AsmBuilder.buildAlgebraOperationCall( context: Algebra, name: String, fallbackMethodName: String, - arity: Int, + parameterTypes: Array, parameters: AsmBuilder.() -> Unit ) { + val arity = parameterTypes.size loadAlgebra() - if (!buildExpectationStack(context, name, arity)) loadStringConstant(name) + if (!buildExpectationStack(context, name, parameterTypes)) loadStringConstant(name) parameters() - if (!tryInvokeSpecific(context, name, arity)) invokeAlgebraOperation( + if (!tryInvokeSpecific(context, name, parameterTypes)) invokeAlgebraOperation( owner = AsmBuilder.ALGEBRA_TYPE.internalName, method = fallbackMethodName, diff --git a/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/asm/TestAsmSpecialization.kt b/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/asm/TestAsmSpecialization.kt index b571e076f..a88431e9d 100644 --- a/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/asm/TestAsmSpecialization.kt +++ b/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/asm/TestAsmSpecialization.kt @@ -43,4 +43,13 @@ internal class TestAsmSpecialization { val expr = RealField.mstInField { binaryOperation("/", symbol("x"), symbol("x")) }.compile() assertEquals(1.0, expr("x" to 2.0)) } + + @Test + fun testPower() { + val expr = RealField + .mstInField { binaryOperation("power", symbol("x"), number(2)) } + .compile() + + assertEquals(4.0, expr("x" to 2.0)) + } } -- 2.34.1 From 3d85c2249739b6b796c009a56ae507ad9ac49998 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Sun, 28 Jun 2020 19:08:44 +0700 Subject: [PATCH 123/156] Encapsulate classOfT property of AsmBuilder --- .../kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt index e550bc563..5531fd5dc 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt @@ -16,13 +16,13 @@ import kotlin.reflect.KClass * ASM Builder is a structure that abstracts building a class designated to unwrap [MST] to plain Java expression. * This class uses [ClassLoader] for loading the generated class, then it is able to instantiate the new class. * - * @param T the type of AsmExpression to unwrap. - * @param algebra the algebra the applied AsmExpressions use. - * @param className the unique class name of new loaded class. - * @param invokeLabel0Visitor the function to apply to this object when generating invoke method, label 0. + * @property T the type of AsmExpression to unwrap. + * @property algebra the algebra the applied AsmExpressions use. + * @property className the unique class name of new loaded class. + * @property invokeLabel0Visitor the function to apply to this object when generating invoke method, label 0. */ internal class AsmBuilder internal constructor( - internal val classOfT: KClass<*>, + private val classOfT: KClass<*>, private val algebra: Algebra, private val className: String, private val invokeLabel0Visitor: AsmBuilder.() -> Unit -- 2.34.1 From 415d11749aee1caa8aefbf102b0b89baa8976195 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Sun, 28 Jun 2020 22:06:50 +0700 Subject: [PATCH 124/156] Improve big arithmetics algebra in JVM module of kmath-core --- .../kmath/asm/internal/AsmBuilder.kt | 2 +- .../scientifik/kmath/operations/BigNumbers.kt | 45 ++++++++++++++----- 2 files changed, 35 insertions(+), 12 deletions(-) diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt index 5531fd5dc..f3ee81c1c 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt @@ -340,7 +340,7 @@ internal class AsmBuilder internal constructor( checkcast(type) } - fun loadNumeric(value: Number) { + internal fun loadNumeric(value: Number) { if (expectationStack.peek() == NUMBER_TYPE) { loadNumberConstant(value, true) expectationStack.pop() diff --git a/kmath-core/src/jvmMain/kotlin/scientifik/kmath/operations/BigNumbers.kt b/kmath-core/src/jvmMain/kotlin/scientifik/kmath/operations/BigNumbers.kt index e6f09c040..1c1c0a5bf 100644 --- a/kmath-core/src/jvmMain/kotlin/scientifik/kmath/operations/BigNumbers.kt +++ b/kmath-core/src/jvmMain/kotlin/scientifik/kmath/operations/BigNumbers.kt @@ -7,31 +7,54 @@ import java.math.MathContext /** * A field wrapper for Java [BigInteger] */ -object JBigIntegerField : Field { - override val zero: BigInteger = BigInteger.ZERO - override val one: BigInteger = BigInteger.ONE +object JBigIntegerRing : Ring, PowerOperations { + override val zero: BigInteger + get() = BigInteger.ZERO + + override val one: BigInteger + get() = BigInteger.ONE override fun add(a: BigInteger, b: BigInteger): BigInteger = a.add(b) - + override fun BigInteger.minus(b: BigInteger): BigInteger = this.subtract(b) override fun multiply(a: BigInteger, k: Number): BigInteger = a.multiply(k.toInt().toBigInteger()) - override fun multiply(a: BigInteger, b: BigInteger): BigInteger = a.multiply(b) - - override fun divide(a: BigInteger, b: BigInteger): BigInteger = a.div(b) + override fun power(arg: BigInteger, pow: Number): BigInteger = arg.pow(pow.toInt()) + override fun sqrt(arg: BigInteger): BigInteger = arg.sqrt() + override fun BigInteger.unaryMinus(): BigInteger = negate() } /** * A Field wrapper for Java [BigDecimal] */ -class JBigDecimalField(val mathContext: MathContext = MathContext.DECIMAL64) : Field { - override val zero: BigDecimal = BigDecimal.ZERO - override val one: BigDecimal = BigDecimal.ONE +abstract class JBigDecimalFieldBase internal constructor(val mathContext: MathContext = MathContext.DECIMAL64) : + Field, + PowerOperations { + override val zero: BigDecimal + get() = BigDecimal.ZERO + + override val one: BigDecimal + get() = BigDecimal.ONE override fun add(a: BigDecimal, b: BigDecimal): BigDecimal = a.add(b) + override fun BigDecimal.minus(b: BigDecimal): BigDecimal { + return subtract(b) + } override fun multiply(a: BigDecimal, k: Number): BigDecimal = a.multiply(k.toDouble().toBigDecimal(mathContext), mathContext) override fun multiply(a: BigDecimal, b: BigDecimal): BigDecimal = a.multiply(b, mathContext) override fun divide(a: BigDecimal, b: BigDecimal): BigDecimal = a.divide(b, mathContext) -} \ No newline at end of file + override fun power(arg: BigDecimal, pow: Number): BigDecimal = arg.pow(pow.toInt(), mathContext) + override fun sqrt(arg: BigDecimal): BigDecimal = arg.sqrt(mathContext) + override fun BigDecimal.unaryMinus(): BigDecimal = negate(mathContext) + +} + +class JBigDecimalField(mathContext: MathContext = MathContext.DECIMAL64) : JBigDecimalFieldBase(mathContext) { + companion object : JBigDecimalFieldBase() +} + +fun main() { + JBigDecimalField { one pow 2 } +} -- 2.34.1 From 1614eef4526066dcab71220db2929f19f32fe5f9 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Sun, 28 Jun 2020 22:10:39 +0700 Subject: [PATCH 125/156] Revert change of JBigIntegerField to Ring, delete unused psvm function --- .../kotlin/scientifik/kmath/operations/BigNumbers.kt | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/kmath-core/src/jvmMain/kotlin/scientifik/kmath/operations/BigNumbers.kt b/kmath-core/src/jvmMain/kotlin/scientifik/kmath/operations/BigNumbers.kt index 1c1c0a5bf..32feeb0d1 100644 --- a/kmath-core/src/jvmMain/kotlin/scientifik/kmath/operations/BigNumbers.kt +++ b/kmath-core/src/jvmMain/kotlin/scientifik/kmath/operations/BigNumbers.kt @@ -7,13 +7,14 @@ import java.math.MathContext /** * A field wrapper for Java [BigInteger] */ -object JBigIntegerRing : Ring, PowerOperations { +object JBigIntegerField : Field, PowerOperations { override val zero: BigInteger get() = BigInteger.ZERO override val one: BigInteger get() = BigInteger.ONE + override fun divide(a: BigInteger, b: BigInteger): BigInteger = a.div(b) override fun add(a: BigInteger, b: BigInteger): BigInteger = a.add(b) override fun BigInteger.minus(b: BigInteger): BigInteger = this.subtract(b) override fun multiply(a: BigInteger, k: Number): BigInteger = a.multiply(k.toInt().toBigInteger()) @@ -54,7 +55,3 @@ abstract class JBigDecimalFieldBase internal constructor(val mathContext: MathCo class JBigDecimalField(mathContext: MathContext = MathContext.DECIMAL64) : JBigDecimalFieldBase(mathContext) { companion object : JBigDecimalFieldBase() } - -fun main() { - JBigDecimalField { one pow 2 } -} -- 2.34.1 From e96ecaddcfab5c8ab659c2b302d8ff7630b8cb95 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Sun, 28 Jun 2020 22:21:18 +0700 Subject: [PATCH 126/156] Revert implementing PowerOperations for JBigIntegerField --- .../jvmMain/kotlin/scientifik/kmath/operations/BigNumbers.kt | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/kmath-core/src/jvmMain/kotlin/scientifik/kmath/operations/BigNumbers.kt b/kmath-core/src/jvmMain/kotlin/scientifik/kmath/operations/BigNumbers.kt index 32feeb0d1..48d3c8c59 100644 --- a/kmath-core/src/jvmMain/kotlin/scientifik/kmath/operations/BigNumbers.kt +++ b/kmath-core/src/jvmMain/kotlin/scientifik/kmath/operations/BigNumbers.kt @@ -7,7 +7,7 @@ import java.math.MathContext /** * A field wrapper for Java [BigInteger] */ -object JBigIntegerField : Field, PowerOperations { +object JBigIntegerField : Field { override val zero: BigInteger get() = BigInteger.ZERO @@ -19,8 +19,6 @@ object JBigIntegerField : Field, PowerOperations { override fun BigInteger.minus(b: BigInteger): BigInteger = this.subtract(b) override fun multiply(a: BigInteger, k: Number): BigInteger = a.multiply(k.toInt().toBigInteger()) override fun multiply(a: BigInteger, b: BigInteger): BigInteger = a.multiply(b) - override fun power(arg: BigInteger, pow: Number): BigInteger = arg.pow(pow.toInt()) - override fun sqrt(arg: BigInteger): BigInteger = arg.sqrt() override fun BigInteger.unaryMinus(): BigInteger = negate() } -- 2.34.1 From 2b6bf6950329055c0ab9d7f6eba220c1023948b3 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Fri, 3 Jul 2020 20:21:41 +0700 Subject: [PATCH 127/156] Add missing repositories --- kmath-ast/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/kmath-ast/README.md b/kmath-ast/README.md index baeb60c39..a59688c8e 100644 --- a/kmath-ast/README.md +++ b/kmath-ast/README.md @@ -16,6 +16,7 @@ This subproject implements the following features: > repositories { > maven { url 'https://dl.bintray.com/mipt-npm/scientifik' } > maven { url 'https://dl.bintray.com/mipt-npm/dev' } +> maven { url https://dl.bintray.com/hotkeytlt/maven' } > } > > dependencies { @@ -28,6 +29,7 @@ This subproject implements the following features: > repositories { > maven("https://dl.bintray.com/mipt-npm/scientifik") > maven("https://dl.bintray.com/mipt-npm/dev") +> maven("https://dl.bintray.com/hotkeytlt/maven") > } > > dependencies { -- 2.34.1 From bce2a8460e22e85b7a9d38f2c8d40da475e1983b Mon Sep 17 00:00:00 2001 From: Iaroslav Postovalov Date: Mon, 20 Jul 2020 20:40:03 +0700 Subject: [PATCH 128/156] Add reference ANTLR grammar, implement missing rules and operations in parser, also add support for symbols in ASM --- kmath-ast/reference/ArithmeticsEvaluator.g4 | 59 +++++++++++++++ .../kotlin/scientifik/kmath/ast/MST.kt | 38 +++++----- .../kotlin/scientifik/kmath/ast/parser.kt | 71 ++++++++++++------- .../kotlin/scientifik/kmath/asm/asm.kt | 15 +++- .../kmath/asm/internal/AsmBuilder.kt | 2 +- .../kotlin/scietifik/kmath/ast/ParserTest.kt | 35 +++++++++ 6 files changed, 173 insertions(+), 47 deletions(-) create mode 100644 kmath-ast/reference/ArithmeticsEvaluator.g4 diff --git a/kmath-ast/reference/ArithmeticsEvaluator.g4 b/kmath-ast/reference/ArithmeticsEvaluator.g4 new file mode 100644 index 000000000..684a9de44 --- /dev/null +++ b/kmath-ast/reference/ArithmeticsEvaluator.g4 @@ -0,0 +1,59 @@ +grammar ArithmeticsEvaluator; + +fragment DIGIT: '0'..'9'; +fragment LETTER: 'a'..'z'; +fragment UNDERSCORE: '_'; + +ID: (LETTER | UNDERSCORE) (LETTER | UNDERSCORE | DIGIT)*; +NUM: (DIGIT | '.')+ ([eE] MINUS? DIGIT+)?; +MUL: '*'; +DIV: '/'; +PLUS: '+'; +MINUS: '-'; +POW: '^'; +COMMA: ','; +LPAR: '('; +RPAR: ')'; +WS: [ \n\t\r]+ -> skip; + +number + : NUM + ; + +singular + : ID + ; + +unaryFunction + : ID LPAR subSumChain RPAR + ; + +binaryFunction + : ID LPAR subSumChain COMMA subSumChain RPAR + ; + +term + : number + | singular + | unaryFunction + | binaryFunction + | MINUS term + | LPAR subSumChain RPAR + ; + +powChain + : term (POW term)* + ; + + +divMulChain + : powChain ((PLUS | MINUS) powChain)* + ; + +subSumChain + : divMulChain ((DIV | MUL) divMulChain)* + ; + +rootParser + : subSumChain EOF + ; diff --git a/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MST.kt b/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MST.kt index 142d27f93..e87df882f 100644 --- a/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MST.kt +++ b/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MST.kt @@ -41,27 +41,25 @@ sealed class MST { //TODO add a function with named arguments -fun Algebra.evaluate(node: MST): T { - return when (node) { - is MST.Numeric -> (this as? NumericAlgebra)?.number(node.value) - ?: error("Numeric nodes are not supported by $this") - is MST.Symbolic -> symbol(node.value) - is MST.Unary -> unaryOperation(node.operation, evaluate(node.value)) - is MST.Binary -> when { - this !is NumericAlgebra -> binaryOperation(node.operation, evaluate(node.left), evaluate(node.right)) - node.left is MST.Numeric && node.right is MST.Numeric -> { - val number = RealField.binaryOperation( - node.operation, - node.left.value.toDouble(), - node.right.value.toDouble() - ) - number(number) - } - node.left is MST.Numeric -> leftSideNumberOperation(node.operation, node.left.value, evaluate(node.right)) - node.right is MST.Numeric -> rightSideNumberOperation(node.operation, evaluate(node.left), node.right.value) - else -> binaryOperation(node.operation, evaluate(node.left), evaluate(node.right)) +fun Algebra.evaluate(node: MST): T = when (node) { + is MST.Numeric -> (this as? NumericAlgebra)?.number(node.value) + ?: error("Numeric nodes are not supported by $this") + is MST.Symbolic -> symbol(node.value) + is MST.Unary -> unaryOperation(node.operation, evaluate(node.value)) + is MST.Binary -> when { + this !is NumericAlgebra -> binaryOperation(node.operation, evaluate(node.left), evaluate(node.right)) + node.left is MST.Numeric && node.right is MST.Numeric -> { + val number = RealField.binaryOperation( + node.operation, + node.left.value.toDouble(), + node.right.value.toDouble() + ) + number(number) } + node.left is MST.Numeric -> leftSideNumberOperation(node.operation, node.left.value, evaluate(node.right)) + node.right is MST.Numeric -> rightSideNumberOperation(node.operation, evaluate(node.left), node.right.value) + else -> binaryOperation(node.operation, evaluate(node.left), evaluate(node.right)) } } -fun MST.compile(algebra: Algebra): T = algebra.evaluate(this) \ No newline at end of file +fun MST.compile(algebra: Algebra): T = algebra.evaluate(this) diff --git a/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/parser.kt b/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/parser.kt index 30a92c5ae..ae0b6656d 100644 --- a/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/parser.kt +++ b/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/parser.kt @@ -5,6 +5,8 @@ import com.github.h0tk3y.betterParse.grammar.Grammar import com.github.h0tk3y.betterParse.grammar.parseToEnd import com.github.h0tk3y.betterParse.grammar.parser import com.github.h0tk3y.betterParse.grammar.tryParseToEnd +import com.github.h0tk3y.betterParse.lexer.Token +import com.github.h0tk3y.betterParse.lexer.TokenMatch import com.github.h0tk3y.betterParse.parser.ParseResult import com.github.h0tk3y.betterParse.parser.Parser import scientifik.kmath.operations.FieldOperations @@ -15,45 +17,66 @@ import scientifik.kmath.operations.SpaceOperations /** * TODO move to common */ -private object ArithmeticsEvaluator : Grammar() { - val num by token("-?[\\d.]+(?:[eE]-?\\d+)?".toRegex()) - val lpar by token("\\(".toRegex()) - val rpar by token("\\)".toRegex()) - val mul by token("\\*".toRegex()) - val pow by token("\\^".toRegex()) - val div by token("/".toRegex()) - val minus by token("-".toRegex()) - val plus by token("\\+".toRegex()) - val ws by token("\\s+".toRegex(), ignore = true) +object ArithmeticsEvaluator : Grammar() { + private val num: Token by token("[\\d.]+(?:[eE]-?\\d+)?".toRegex()) + private val id: Token by token("[a-z_][\\da-z_]*".toRegex()) + private val lpar: Token by token("\\(".toRegex()) + private val rpar: Token by token("\\)".toRegex()) + private val comma: Token by token(",".toRegex()) + private val mul: Token by token("\\*".toRegex()) + private val pow: Token by token("\\^".toRegex()) + private val div: Token by token("/".toRegex()) + private val minus: Token by token("-".toRegex()) + private val plus: Token by token("\\+".toRegex()) + private val ws: Token by token("\\s+".toRegex(), ignore = true) - val number: Parser by num use { MST.Numeric(text.toDouble()) } + private val number: Parser by num use { MST.Numeric(text.toDouble()) } + private val singular: Parser by id use { MST.Symbolic(text) } - val term: Parser by number or - (skip(minus) and parser(this::term) map { MST.Unary(SpaceOperations.MINUS_OPERATION, it) }) or - (skip(lpar) and parser(this::rootParser) and skip(rpar)) + private val unaryFunction: Parser by (id and skip(lpar) and parser(::subSumChain) and skip(rpar)) + .map { (id, term) -> MST.Unary(id.text, term) } - val powChain by leftAssociative(term, pow) { a, _, b -> + private val binaryFunction: Parser by id + .and(skip(lpar)) + .and(parser(::subSumChain)) + .and(skip(comma)) + .and(parser(::subSumChain)) + .and(skip(rpar)) + .map { (id, left, right) -> MST.Binary(id.text, left, right) } + + private val term: Parser by number + .or(binaryFunction) + .or(unaryFunction) + .or(singular) + .or(skip(minus) and parser(::term) map { MST.Unary(SpaceOperations.MINUS_OPERATION, it) }) + .or(skip(lpar) and parser(::subSumChain) and skip(rpar)) + + private val powChain: Parser by leftAssociative(term = term, operator = pow) { a, _, b -> MST.Binary(PowerOperations.POW_OPERATION, a, b) } - val divMulChain: Parser by leftAssociative(powChain, div or mul use { type }) { a, op, b -> - if (op == div) { + private val divMulChain: Parser by leftAssociative( + term = powChain, + operator = div or mul use TokenMatch::type + ) { a, op, b -> + if (op == div) MST.Binary(FieldOperations.DIV_OPERATION, a, b) - } else { + else MST.Binary(RingOperations.TIMES_OPERATION, a, b) - } } - val subSumChain: Parser by leftAssociative(divMulChain, plus or minus use { type }) { a, op, b -> - if (op == plus) { + private val subSumChain: Parser by leftAssociative( + term = divMulChain, + operator = plus or minus use TokenMatch::type + ) { a, op, b -> + if (op == plus) MST.Binary(SpaceOperations.PLUS_OPERATION, a, b) - } else { + else MST.Binary(SpaceOperations.MINUS_OPERATION, a, b) - } } override val rootParser: Parser by subSumChain } fun String.tryParseMath(): ParseResult = ArithmeticsEvaluator.tryParseToEnd(this) -fun String.parseMath(): MST = ArithmeticsEvaluator.parseToEnd(this) \ No newline at end of file +fun String.parseMath(): MST = ArithmeticsEvaluator.parseToEnd(this) diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/asm.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/asm.kt index f0022a43a..ee0ea15ff 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/asm.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/asm.kt @@ -8,7 +8,6 @@ import scientifik.kmath.ast.MST import scientifik.kmath.ast.MstExpression import scientifik.kmath.expressions.Expression import scientifik.kmath.operations.Algebra -import scientifik.kmath.operations.NumericAlgebra import kotlin.reflect.KClass /** @@ -17,7 +16,19 @@ import kotlin.reflect.KClass fun MST.compileWith(type: KClass, algebra: Algebra): Expression { fun AsmBuilder.visit(node: MST) { when (node) { - is MST.Symbolic -> loadVariable(node.value) + is MST.Symbolic -> { + val symbol = try { + algebra.symbol(node.value) + } catch (ignored: Throwable) { + null + } + + if (symbol != null) + loadTConstant(symbol) + else + loadVariable(node.value) + } + is MST.Numeric -> loadNumeric(node.value) is MST.Unary -> buildAlgebraOperationCall( diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt index 5531fd5dc..a947b3478 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt @@ -288,7 +288,7 @@ internal class AsmBuilder internal constructor( /** * Loads a [T] constant from [constants]. */ - private fun loadTConstant(value: T) { + internal fun loadTConstant(value: T) { if (classOfT in INLINABLE_NUMBERS) { val expectedType = expectationStack.pop() val mustBeBoxed = expectedType.sort == Type.OBJECT diff --git a/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/ParserTest.kt b/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/ParserTest.kt index 5394a4b00..9179c3428 100644 --- a/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/ParserTest.kt +++ b/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/ParserTest.kt @@ -4,8 +4,10 @@ import scientifik.kmath.ast.evaluate import scientifik.kmath.ast.mstInField import scientifik.kmath.ast.parseMath import scientifik.kmath.expressions.invoke +import scientifik.kmath.operations.Algebra import scientifik.kmath.operations.Complex import scientifik.kmath.operations.ComplexField +import scientifik.kmath.operations.RealField import kotlin.test.Test import kotlin.test.assertEquals @@ -22,4 +24,37 @@ internal class ParserTest { val res = ComplexField.mstInField { number(2) + number(2) * (number(2) + number(2)) }() assertEquals(Complex(10.0, 0.0), res) } + + @Test + fun `evaluate MST with singular`() { + val mst = "i".parseMath() + val res = ComplexField.evaluate(mst) + assertEquals(ComplexField.i, res) + } + + + @Test + fun `evaluate MST with unary function`() { + val mst = "sin(0)".parseMath() + val res = RealField.evaluate(mst) + assertEquals(0.0, res) + } + + @Test + fun `evaluate MST with binary function`() { + val magicalAlgebra = object : Algebra { + override fun symbol(value: String): String = value + + override fun unaryOperation(operation: String, arg: String): String = throw NotImplementedError() + + override fun binaryOperation(operation: String, left: String, right: String): String = when (operation) { + "magic" -> "$left ★ $right" + else -> throw NotImplementedError() + } + } + + val mst = "magic(a, b)".parseMath() + val res = magicalAlgebra.evaluate(mst) + assertEquals("a ★ b", res) + } } -- 2.34.1 From 2864b2d12e4ad7100b2978a5f4378fd4cb5b846b Mon Sep 17 00:00:00 2001 From: Iaroslav Postovalov Date: Sun, 26 Jul 2020 11:17:47 +0700 Subject: [PATCH 129/156] Move https://dl.bintray.com/hotkeytlt/maven to another RepositoryHandler --- build.gradle.kts | 1 + kmath-ast/build.gradle.kts | 12 ++---------- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 052b457c5..8a2ba3617 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -11,6 +11,7 @@ allprojects { repositories { jcenter() maven("https://dl.bintray.com/kotlin/kotlinx") + maven("https://dl.bintray.com/hotkeytlt/maven") } group = "scientifik" diff --git a/kmath-ast/build.gradle.kts b/kmath-ast/build.gradle.kts index 511571fc9..4f67d57eb 100644 --- a/kmath-ast/build.gradle.kts +++ b/kmath-ast/build.gradle.kts @@ -1,10 +1,4 @@ -plugins { - id("scientifik.mpp") -} - -repositories { - maven("https://dl.bintray.com/hotkeytlt/maven") -} +plugins { id("scientifik.mpp") } kotlin.sourceSets { // all { @@ -30,8 +24,6 @@ kotlin.sourceSets { } jsMain { - dependencies { - implementation("com.github.h0tk3y.betterParse:better-parse-js:0.4.0-alpha-3") - } + dependencies { implementation("com.github.h0tk3y.betterParse:better-parse-js:0.4.0-alpha-3") } } } \ No newline at end of file -- 2.34.1 From 186575d8b335128eabbe701bbcaf237b883f9c8d Mon Sep 17 00:00:00 2001 From: Iaroslav Postovalov Date: Sun, 26 Jul 2020 11:28:19 +0700 Subject: [PATCH 130/156] Override number function for JBigIntegerField and JBigDecimalField --- .../kotlin/scientifik/kmath/operations/BigNumbers.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/kmath-core/src/jvmMain/kotlin/scientifik/kmath/operations/BigNumbers.kt b/kmath-core/src/jvmMain/kotlin/scientifik/kmath/operations/BigNumbers.kt index 48d3c8c59..eb268bb5e 100644 --- a/kmath-core/src/jvmMain/kotlin/scientifik/kmath/operations/BigNumbers.kt +++ b/kmath-core/src/jvmMain/kotlin/scientifik/kmath/operations/BigNumbers.kt @@ -14,6 +14,7 @@ object JBigIntegerField : Field { override val one: BigInteger get() = BigInteger.ONE + override fun number(value: Number): BigInteger = BigInteger.valueOf(value.toLong()) override fun divide(a: BigInteger, b: BigInteger): BigInteger = a.div(b) override fun add(a: BigInteger, b: BigInteger): BigInteger = a.add(b) override fun BigInteger.minus(b: BigInteger): BigInteger = this.subtract(b) @@ -35,9 +36,8 @@ abstract class JBigDecimalFieldBase internal constructor(val mathContext: MathCo get() = BigDecimal.ONE override fun add(a: BigDecimal, b: BigDecimal): BigDecimal = a.add(b) - override fun BigDecimal.minus(b: BigDecimal): BigDecimal { - return subtract(b) - } + override fun BigDecimal.minus(b: BigDecimal): BigDecimal = subtract(b) + override fun number(value: Number): BigDecimal = BigDecimal.valueOf(value.toDouble()) override fun multiply(a: BigDecimal, k: Number): BigDecimal = a.multiply(k.toDouble().toBigDecimal(mathContext), mathContext) -- 2.34.1 From 45087f8b2de6b615ea627400b8dd12edc8e0918d Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Mon, 27 Jul 2020 15:37:18 +0700 Subject: [PATCH 131/156] Add capital latin letters support --- kmath-ast/reference/ArithmeticsEvaluator.g4 | 3 ++- kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/parser.kt | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/kmath-ast/reference/ArithmeticsEvaluator.g4 b/kmath-ast/reference/ArithmeticsEvaluator.g4 index 684a9de44..137d42860 100644 --- a/kmath-ast/reference/ArithmeticsEvaluator.g4 +++ b/kmath-ast/reference/ArithmeticsEvaluator.g4 @@ -2,9 +2,10 @@ grammar ArithmeticsEvaluator; fragment DIGIT: '0'..'9'; fragment LETTER: 'a'..'z'; +fragment CAPITAL_LETTER: 'A'..'Z' fragment UNDERSCORE: '_'; -ID: (LETTER | UNDERSCORE) (LETTER | UNDERSCORE | DIGIT)*; +ID: (LETTER | UNDERSCORE | CAPITAL_LETTER) (LETTER | UNDERSCORE | DIGIT | CAPITAL_LETTER)*; NUM: (DIGIT | '.')+ ([eE] MINUS? DIGIT+)?; MUL: '*'; DIV: '/'; diff --git a/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/parser.kt b/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/parser.kt index ae0b6656d..27f133d6a 100644 --- a/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/parser.kt +++ b/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/parser.kt @@ -19,7 +19,7 @@ import scientifik.kmath.operations.SpaceOperations */ object ArithmeticsEvaluator : Grammar() { private val num: Token by token("[\\d.]+(?:[eE]-?\\d+)?".toRegex()) - private val id: Token by token("[a-z_][\\da-z_]*".toRegex()) + private val id: Token by token("[a-z_A-Z][\\da-z_A-Z]*".toRegex()) private val lpar: Token by token("\\(".toRegex()) private val rpar: Token by token("\\)".toRegex()) private val comma: Token by token(",".toRegex()) -- 2.34.1 From a5499260fae7e412da6ca6fcd9769382d7d9357f Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Mon, 27 Jul 2020 15:45:05 +0700 Subject: [PATCH 132/156] Update num rule to match 1e+1 like numbers --- kmath-ast/reference/ArithmeticsEvaluator.g4 | 8 ++++---- .../src/commonMain/kotlin/scientifik/kmath/ast/parser.kt | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/kmath-ast/reference/ArithmeticsEvaluator.g4 b/kmath-ast/reference/ArithmeticsEvaluator.g4 index 137d42860..ba8166bc8 100644 --- a/kmath-ast/reference/ArithmeticsEvaluator.g4 +++ b/kmath-ast/reference/ArithmeticsEvaluator.g4 @@ -2,11 +2,11 @@ grammar ArithmeticsEvaluator; fragment DIGIT: '0'..'9'; fragment LETTER: 'a'..'z'; -fragment CAPITAL_LETTER: 'A'..'Z' +fragment CAPITAL_LETTER: 'A'..'Z'; fragment UNDERSCORE: '_'; ID: (LETTER | UNDERSCORE | CAPITAL_LETTER) (LETTER | UNDERSCORE | DIGIT | CAPITAL_LETTER)*; -NUM: (DIGIT | '.')+ ([eE] MINUS? DIGIT+)?; +NUM: (DIGIT | '.')+ ([eE] (MINUS? | PLUS?) DIGIT+)?; MUL: '*'; DIV: '/'; PLUS: '+'; @@ -17,7 +17,7 @@ LPAR: '('; RPAR: ')'; WS: [ \n\t\r]+ -> skip; -number +num : NUM ; @@ -34,7 +34,7 @@ binaryFunction ; term - : number + : num | singular | unaryFunction | binaryFunction diff --git a/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/parser.kt b/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/parser.kt index 27f133d6a..a0b729ffe 100644 --- a/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/parser.kt +++ b/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/parser.kt @@ -18,7 +18,7 @@ import scientifik.kmath.operations.SpaceOperations * TODO move to common */ object ArithmeticsEvaluator : Grammar() { - private val num: Token by token("[\\d.]+(?:[eE]-?\\d+)?".toRegex()) + private val num: Token by token("[\\d.]+(?:[eE][-+]?\\d+)?".toRegex()) private val id: Token by token("[a-z_A-Z][\\da-z_A-Z]*".toRegex()) private val lpar: Token by token("\\(".toRegex()) private val rpar: Token by token("\\)".toRegex()) -- 2.34.1 From f8383deb708e0b88a89ff76ddb0021c1facf5464 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Mon, 27 Jul 2020 15:49:36 +0700 Subject: [PATCH 133/156] Fix operators in reference --- kmath-ast/reference/ArithmeticsEvaluator.g4 | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/kmath-ast/reference/ArithmeticsEvaluator.g4 b/kmath-ast/reference/ArithmeticsEvaluator.g4 index ba8166bc8..9835ec7f7 100644 --- a/kmath-ast/reference/ArithmeticsEvaluator.g4 +++ b/kmath-ast/reference/ArithmeticsEvaluator.g4 @@ -46,13 +46,12 @@ powChain : term (POW term)* ; - divMulChain - : powChain ((PLUS | MINUS) powChain)* + : powChain ((DIV | MUL) powChain)* ; subSumChain - : divMulChain ((DIV | MUL) divMulChain)* + : divMulChain ((PLUS | MINUS) divMulChain)* ; rootParser -- 2.34.1 From 1ebd3626969e07b3ee16fe4f77a03b2315563fcd Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Mon, 27 Jul 2020 15:58:09 +0700 Subject: [PATCH 134/156] Update num token in reference --- kmath-ast/reference/ArithmeticsEvaluator.g4 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kmath-ast/reference/ArithmeticsEvaluator.g4 b/kmath-ast/reference/ArithmeticsEvaluator.g4 index 9835ec7f7..dc47b23fb 100644 --- a/kmath-ast/reference/ArithmeticsEvaluator.g4 +++ b/kmath-ast/reference/ArithmeticsEvaluator.g4 @@ -6,7 +6,7 @@ fragment CAPITAL_LETTER: 'A'..'Z'; fragment UNDERSCORE: '_'; ID: (LETTER | UNDERSCORE | CAPITAL_LETTER) (LETTER | UNDERSCORE | DIGIT | CAPITAL_LETTER)*; -NUM: (DIGIT | '.')+ ([eE] (MINUS? | PLUS?) DIGIT+)?; +NUM: (DIGIT | '.')+ ([eE] [-+]? DIGIT+)?; MUL: '*'; DIV: '/'; PLUS: '+'; -- 2.34.1 From c64a89c6b6296517d7c7b9690e7ae319ae54fcd8 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Mon, 27 Jul 2020 19:27:59 +0700 Subject: [PATCH 135/156] Implement power as binary operation with unchecked cast, add tests on parser precedence --- .../kotlin/scientifik/kmath/ast/MST.kt | 8 ++--- .../kmath/ast/ParserPrecedenceTest.kt | 36 +++++++++++++++++++ .../kmath/operations/NumberAlgebra.kt | 11 ++++-- 3 files changed, 48 insertions(+), 7 deletions(-) create mode 100644 kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/ParserPrecedenceTest.kt diff --git a/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MST.kt b/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MST.kt index e87df882f..1127c18aa 100644 --- a/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MST.kt +++ b/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MST.kt @@ -2,7 +2,6 @@ package scientifik.kmath.ast import scientifik.kmath.operations.Algebra import scientifik.kmath.operations.NumericAlgebra -import scientifik.kmath.operations.RealField /** * A Mathematical Syntax Tree node for mathematical expressions @@ -49,12 +48,11 @@ fun Algebra.evaluate(node: MST): T = when (node) { is MST.Binary -> when { this !is NumericAlgebra -> binaryOperation(node.operation, evaluate(node.left), evaluate(node.right)) node.left is MST.Numeric && node.right is MST.Numeric -> { - val number = RealField.binaryOperation( + binaryOperation( node.operation, - node.left.value.toDouble(), - node.right.value.toDouble() + number(node.left.value), + number(node.right.value) ) - number(number) } node.left is MST.Numeric -> leftSideNumberOperation(node.operation, node.left.value, evaluate(node.right)) node.right is MST.Numeric -> rightSideNumberOperation(node.operation, evaluate(node.left), node.right.value) diff --git a/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/ParserPrecedenceTest.kt b/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/ParserPrecedenceTest.kt new file mode 100644 index 000000000..9bdbb12c9 --- /dev/null +++ b/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/ParserPrecedenceTest.kt @@ -0,0 +1,36 @@ +package scietifik.kmath.ast + +import scientifik.kmath.ast.evaluate +import scientifik.kmath.ast.parseMath +import scientifik.kmath.operations.Field +import scientifik.kmath.operations.RealField +import kotlin.test.Test +import kotlin.test.assertEquals + +internal class ParserPrecedenceTest { + private val f: Field = RealField + + @Test + fun test1(): Unit = assertEquals(6.0, f.evaluate("2*2+2".parseMath())) + + @Test + fun test2(): Unit = assertEquals(6.0, f.evaluate("2+2*2".parseMath())) + + @Test + fun test3(): Unit = assertEquals(10.0, f.evaluate("2^3+2".parseMath())) + + @Test + fun test4(): Unit = assertEquals(10.0, f.evaluate("2+2^3".parseMath())) + + @Test + fun test5(): Unit = assertEquals(16.0, f.evaluate("2^3*2".parseMath())) + + @Test + fun test6(): Unit = assertEquals(16.0, f.evaluate("2*2^3".parseMath())) + + @Test + fun test7(): Unit = assertEquals(18.0, f.evaluate("2+2^3*2".parseMath())) + + @Test + fun test8(): Unit = assertEquals(18.0, f.evaluate("2*2^3+2".parseMath())) +} diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/NumberAlgebra.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/NumberAlgebra.kt index 953c5a112..ca5fa68f0 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/NumberAlgebra.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/NumberAlgebra.kt @@ -34,6 +34,13 @@ interface ExtendedField : ExtendedFieldOperations, Field { } } +interface NumberExtendedField : ExtendedField { + override fun binaryOperation(operation: String, left: T, right: T): T = when (operation) { + PowerOperations.POW_OPERATION -> power(left, right as Number) + else -> super.binaryOperation(operation, left, right) + } +} + /** * Real field element wrapping double. * @@ -53,7 +60,7 @@ inline class Real(val value: Double) : FieldElement { * A field for double without boxing. Does not produce appropriate field element */ @Suppress("EXTENSION_SHADOWED_BY_MEMBER", "OVERRIDE_BY_INLINE", "NOTHING_TO_INLINE") -object RealField : ExtendedField, Norm { +object RealField : NumberExtendedField, Norm { override val zero: Double = 0.0 override inline fun add(a: Double, b: Double) = a + b override inline fun multiply(a: Double, b: Double) = a * b @@ -88,7 +95,7 @@ object RealField : ExtendedField, Norm { } @Suppress("EXTENSION_SHADOWED_BY_MEMBER", "OVERRIDE_BY_INLINE", "NOTHING_TO_INLINE") -object FloatField : ExtendedField, Norm { +object FloatField : NumberExtendedField, Norm { override val zero: Float = 0f override inline fun add(a: Float, b: Float) = a + b override inline fun multiply(a: Float, b: Float) = a * b -- 2.34.1 From 0995dca8b857d07d5739dc34e8b75e8df78703f6 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Mon, 27 Jul 2020 20:46:57 +0700 Subject: [PATCH 136/156] Delete unchecked cast, revert evaluate changes, add RealField handling "pow" operation --- .../kotlin/scientifik/kmath/ast/MST.kt | 11 ++++++++--- .../kmath/operations/NumberAlgebra.kt | 17 ++++++++--------- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MST.kt b/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MST.kt index 1127c18aa..46558cbfb 100644 --- a/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MST.kt +++ b/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MST.kt @@ -2,6 +2,7 @@ package scientifik.kmath.ast import scientifik.kmath.operations.Algebra import scientifik.kmath.operations.NumericAlgebra +import scientifik.kmath.operations.RealField /** * A Mathematical Syntax Tree node for mathematical expressions @@ -47,13 +48,17 @@ fun Algebra.evaluate(node: MST): T = when (node) { is MST.Unary -> unaryOperation(node.operation, evaluate(node.value)) is MST.Binary -> when { this !is NumericAlgebra -> binaryOperation(node.operation, evaluate(node.left), evaluate(node.right)) + node.left is MST.Numeric && node.right is MST.Numeric -> { - binaryOperation( + val number = RealField.binaryOperation( node.operation, - number(node.left.value), - number(node.right.value) + node.left.value.toDouble(), + node.right.value.toDouble() ) + + number(number) } + node.left is MST.Numeric -> leftSideNumberOperation(node.operation, node.left.value, evaluate(node.right)) node.right is MST.Numeric -> rightSideNumberOperation(node.operation, evaluate(node.left), node.right.value) else -> binaryOperation(node.operation, evaluate(node.left), evaluate(node.right)) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/NumberAlgebra.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/NumberAlgebra.kt index ca5fa68f0..2c57d0098 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/NumberAlgebra.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/NumberAlgebra.kt @@ -1,5 +1,6 @@ package scientifik.kmath.operations +import scientifik.kmath.operations.RealField.pow import kotlin.math.abs import kotlin.math.pow as kpow @@ -34,13 +35,6 @@ interface ExtendedField : ExtendedFieldOperations, Field { } } -interface NumberExtendedField : ExtendedField { - override fun binaryOperation(operation: String, left: T, right: T): T = when (operation) { - PowerOperations.POW_OPERATION -> power(left, right as Number) - else -> super.binaryOperation(operation, left, right) - } -} - /** * Real field element wrapping double. * @@ -60,7 +54,7 @@ inline class Real(val value: Double) : FieldElement { * A field for double without boxing. Does not produce appropriate field element */ @Suppress("EXTENSION_SHADOWED_BY_MEMBER", "OVERRIDE_BY_INLINE", "NOTHING_TO_INLINE") -object RealField : NumberExtendedField, Norm { +object RealField : ExtendedField, Norm { override val zero: Double = 0.0 override inline fun add(a: Double, b: Double) = a + b override inline fun multiply(a: Double, b: Double) = a * b @@ -92,10 +86,15 @@ object RealField : NumberExtendedField, Norm { override inline fun Double.times(b: Double) = this * b override inline fun Double.div(b: Double) = this / b + + override fun binaryOperation(operation: String, left: Double, right: Double): Double = when (operation) { + PowerOperations.POW_OPERATION -> left pow right + else -> super.binaryOperation(operation, left, right) + } } @Suppress("EXTENSION_SHADOWED_BY_MEMBER", "OVERRIDE_BY_INLINE", "NOTHING_TO_INLINE") -object FloatField : NumberExtendedField, Norm { +object FloatField : ExtendedField, Norm { override val zero: Float = 0f override inline fun add(a: Float, b: Float) = a + b override inline fun multiply(a: Float, b: Float) = a * b -- 2.34.1 From 11c98d6acfaf955f632e24d9ab77a54d002aefec Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Mon, 27 Jul 2020 23:06:28 +0700 Subject: [PATCH 137/156] Upgrade better-parse to 0.4.0 --- kmath-ast/build.gradle.kts | 8 +----- .../kotlin/scientifik/kmath/ast/parser.kt | 26 ++++++++++--------- 2 files changed, 15 insertions(+), 19 deletions(-) diff --git a/kmath-ast/build.gradle.kts b/kmath-ast/build.gradle.kts index 4f67d57eb..d13a7712d 100644 --- a/kmath-ast/build.gradle.kts +++ b/kmath-ast/build.gradle.kts @@ -9,21 +9,15 @@ kotlin.sourceSets { commonMain { dependencies { api(project(":kmath-core")) - implementation("com.github.h0tk3y.betterParse:better-parse-multiplatform:0.4.0-alpha-3") - implementation("com.github.h0tk3y.betterParse:better-parse-multiplatform-metadata:0.4.0-alpha-3") + implementation("com.github.h0tk3y.betterParse:better-parse:0.4.0") } } jvmMain { dependencies { - implementation("com.github.h0tk3y.betterParse:better-parse-jvm:0.4.0-alpha-3") implementation("org.ow2.asm:asm:8.0.1") implementation("org.ow2.asm:asm-commons:8.0.1") implementation(kotlin("reflect")) } } - - jsMain { - dependencies { implementation("com.github.h0tk3y.betterParse:better-parse-js:0.4.0-alpha-3") } - } } \ No newline at end of file diff --git a/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/parser.kt b/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/parser.kt index a0b729ffe..11797d79f 100644 --- a/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/parser.kt +++ b/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/parser.kt @@ -7,6 +7,7 @@ import com.github.h0tk3y.betterParse.grammar.parser import com.github.h0tk3y.betterParse.grammar.tryParseToEnd import com.github.h0tk3y.betterParse.lexer.Token import com.github.h0tk3y.betterParse.lexer.TokenMatch +import com.github.h0tk3y.betterParse.lexer.regexToken import com.github.h0tk3y.betterParse.parser.ParseResult import com.github.h0tk3y.betterParse.parser.Parser import scientifik.kmath.operations.FieldOperations @@ -15,20 +16,21 @@ import scientifik.kmath.operations.RingOperations import scientifik.kmath.operations.SpaceOperations /** - * TODO move to common + * TODO move to core */ object ArithmeticsEvaluator : Grammar() { - private val num: Token by token("[\\d.]+(?:[eE][-+]?\\d+)?".toRegex()) - private val id: Token by token("[a-z_A-Z][\\da-z_A-Z]*".toRegex()) - private val lpar: Token by token("\\(".toRegex()) - private val rpar: Token by token("\\)".toRegex()) - private val comma: Token by token(",".toRegex()) - private val mul: Token by token("\\*".toRegex()) - private val pow: Token by token("\\^".toRegex()) - private val div: Token by token("/".toRegex()) - private val minus: Token by token("-".toRegex()) - private val plus: Token by token("\\+".toRegex()) - private val ws: Token by token("\\s+".toRegex(), ignore = true) + // TODO replace with "...".toRegex() when better-parse 0.4.1 is released + private val num: Token by regexToken("[\\d.]+(?:[eE][-+]?\\d+)?") + private val id: Token by regexToken("[a-z_A-Z][\\da-z_A-Z]*") + private val lpar: Token by regexToken("\\(") + private val rpar: Token by regexToken("\\)") + private val comma: Token by regexToken(",") + private val mul: Token by regexToken("\\*") + private val pow: Token by regexToken("\\^") + private val div: Token by regexToken("/") + private val minus: Token by regexToken("-") + private val plus: Token by regexToken("\\+") + private val ws: Token by regexToken("\\s+", ignore = true) private val number: Parser by num use { MST.Numeric(text.toDouble()) } private val singular: Parser by id use { MST.Symbolic(text) } -- 2.34.1 From 87ff735a3119df44451dba2b52507b84b264521f Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Tue, 28 Jul 2020 09:02:03 +0300 Subject: [PATCH 138/156] Update Dimensions.kt Fix D3 typo. --- .../kotlin/scientifik/kmath/dimensions/Dimensions.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kmath-dimensions/src/commonMain/kotlin/scientifik/kmath/dimensions/Dimensions.kt b/kmath-dimensions/src/commonMain/kotlin/scientifik/kmath/dimensions/Dimensions.kt index 37e89c111..f40483cfd 100644 --- a/kmath-dimensions/src/commonMain/kotlin/scientifik/kmath/dimensions/Dimensions.kt +++ b/kmath-dimensions/src/commonMain/kotlin/scientifik/kmath/dimensions/Dimensions.kt @@ -31,5 +31,5 @@ object D2 : Dimension { } object D3 : Dimension { - override val dim: UInt get() = 31U -} \ No newline at end of file + override val dim: UInt get() = 3U +} -- 2.34.1 From b9f871ae7686b86e6c2047a3171e22f186669803 Mon Sep 17 00:00:00 2001 From: Iaroslav Postovalov Date: Thu, 30 Jul 2020 03:43:57 +0700 Subject: [PATCH 139/156] Upgrade Gradle from 6.5 to 6.5.1 --- gradle/wrapper/gradle-wrapper.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 622ab64a3..bb8b2fc26 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.5.1-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -- 2.34.1 From 9fded79af09b08ccc39e7dedf6b7b34979ba4d45 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Fri, 31 Jul 2020 16:46:32 +0700 Subject: [PATCH 140/156] Move codestyle.md --- {docs => doc}/codestyle.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {docs => doc}/codestyle.md (100%) diff --git a/docs/codestyle.md b/doc/codestyle.md similarity index 100% rename from docs/codestyle.md rename to doc/codestyle.md -- 2.34.1 From ae7aefeb6aa58100721144dfeec0aebdbe87f878 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Wed, 5 Aug 2020 03:58:00 +0700 Subject: [PATCH 141/156] Specify type explicitly EVERYWHERE in kmath-core, add newlines at ends of files, refactor minor problems, improve documentation --- .../kotlin/scientifik/kmath/domains/Domain.kt | 11 +- .../kmath/domains/HyperSquareDomain.kt | 5 +- .../scientifik/kmath/domains/RealDomain.kt | 6 +- .../kmath/domains/UnconstrainedDomain.kt | 4 +- .../kmath/domains/UnivariateDomain.kt | 7 +- .../kmath/expressions/Expression.kt | 9 +- .../FunctionalExpressionAlgebra.kt | 2 +- .../scientifik/kmath/linear/BufferMatrix.kt | 16 +- .../scientifik/kmath/linear/FeaturedMatrix.kt | 12 +- .../kmath/linear/LUPDecomposition.kt | 24 +- .../scientifik/kmath/linear/LinearAlgebra.kt | 2 +- .../scientifik/kmath/linear/MatrixContext.kt | 8 +- .../scientifik/kmath/linear/MatrixFeatures.kt | 10 +- .../scientifik/kmath/linear/VectorSpace.kt | 6 +- .../scientifik/kmath/linear/VirtualMatrix.kt | 4 +- .../kotlin/scientifik/kmath/misc/AutoDiff.kt | 10 +- .../kotlin/scientifik/kmath/misc/Grids.kt | 2 +- .../scientifik/kmath/misc/cumulative.kt | 35 +-- .../scientifik/kmath/operations/Algebra.kt | 225 +++++++++++++++--- .../kmath/operations/AlgebraElements.kt | 22 +- .../kmath/operations/AlgebraExtensions.kt | 2 +- .../scientifik/kmath/operations/BigInt.kt | 24 +- .../scientifik/kmath/operations/Complex.kt | 14 +- .../kmath/operations/NumberAlgebra.kt | 125 +++++----- .../kmath/operations/OptionalOperations.kt | 186 ++++++++++++--- .../kmath/structures/BoxingNDField.kt | 8 +- .../kmath/structures/BoxingNDRing.kt | 8 +- .../kmath/structures/BufferAccessor2D.kt | 8 +- .../kmath/structures/BufferedNDAlgebra.kt | 4 +- .../kmath/structures/BufferedNDElement.kt | 16 +- .../scientifik/kmath/structures/Buffers.kt | 21 +- .../kmath/structures/ComplexNDField.kt | 37 ++- .../kmath/structures/ExtendedNDField.kt | 3 - .../kmath/structures/FlaggedBuffer.kt | 12 +- .../scientifik/kmath/structures/IntBuffer.kt | 5 +- .../scientifik/kmath/structures/LongBuffer.kt | 4 +- .../kmath/structures/MemoryBuffer.kt | 14 +- .../scientifik/kmath/structures/NDAlgebra.kt | 26 +- .../scientifik/kmath/structures/NDElement.kt | 29 ++- .../kmath/structures/NDStructure.kt | 41 +++- .../scientifik/kmath/structures/RealBuffer.kt | 4 +- .../kmath/structures/RealNDField.kt | 26 +- .../kmath/structures/ShortBuffer.kt | 5 +- .../kmath/structures/ShortNDRing.kt | 21 +- .../kmath/structures/Structure1D.kt | 8 +- .../kmath/structures/Structure2D.kt | 6 +- .../kmath/expressions/ExpressionFieldTest.kt | 6 +- .../scientifik/kmath/linear/MatrixTest.kt | 12 +- .../kmath/linear/RealLUSolverTest.kt | 2 +- .../scientifik/kmath/misc/AutoDiffTest.kt | 9 +- .../scientifik/kmath/misc/CumulativeKtTest.kt | 2 +- .../kmath/operations/BigIntAlgebraTest.kt | 1 - .../kmath/operations/BigIntConstructorTest.kt | 4 +- .../kmath/operations/BigIntConversionsTest.kt | 4 +- .../kmath/operations/BigIntOperationsTest.kt | 24 +- .../kmath/operations/RealFieldTest.kt | 2 +- .../kmath/structures/ComplexBufferSpecTest.kt | 2 +- .../kmath/structures/NDFieldTest.kt | 2 +- .../kmath/structures/NumberNDFieldTest.kt | 4 +- 59 files changed, 722 insertions(+), 429 deletions(-) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/domains/Domain.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/domains/Domain.kt index 333b77cb4..341383bfb 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/domains/Domain.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/domains/Domain.kt @@ -3,13 +3,18 @@ package scientifik.kmath.domains import scientifik.kmath.linear.Point /** - * A simple geometric domain + * A simple geometric domain. + * + * @param T the type of element of this domain. */ interface Domain { + /** + * Checks if the specified point is contained in this domain. + */ operator fun contains(point: Point): Boolean /** - * Number of hyperspace dimensions + * Number of hyperspace dimensions. */ val dimension: Int -} \ No newline at end of file +} diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/domains/HyperSquareDomain.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/domains/HyperSquareDomain.kt index e0019c96b..66798c42f 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/domains/HyperSquareDomain.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/domains/HyperSquareDomain.kt @@ -42,13 +42,14 @@ class HyperSquareDomain(private val lower: RealBuffer, private val upper: RealBu override fun getUpperBound(num: Int): Double? = upper[num] override fun nearestInDomain(point: Point): Point { - val res: DoubleArray = DoubleArray(point.size) { i -> + val res = DoubleArray(point.size) { i -> when { point[i] < lower[i] -> lower[i] point[i] > upper[i] -> upper[i] else -> point[i] } } + return RealBuffer(*res) } @@ -64,4 +65,4 @@ class HyperSquareDomain(private val lower: RealBuffer, private val upper: RealBu } return res } -} \ No newline at end of file +} diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/domains/RealDomain.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/domains/RealDomain.kt index 89115887e..7507ccd59 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/domains/RealDomain.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/domains/RealDomain.kt @@ -22,8 +22,7 @@ import scientifik.kmath.linear.Point * * @author Alexander Nozik */ -interface RealDomain: Domain { - +interface RealDomain : Domain { fun nearestInDomain(point: Point): Point /** @@ -61,5 +60,4 @@ interface RealDomain: Domain { * @return */ fun volume(): Double - -} \ No newline at end of file +} diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/domains/UnconstrainedDomain.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/domains/UnconstrainedDomain.kt index e49fd3b37..595a3dbe7 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/domains/UnconstrainedDomain.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/domains/UnconstrainedDomain.kt @@ -18,7 +18,6 @@ package scientifik.kmath.domains import scientifik.kmath.linear.Point class UnconstrainedDomain(override val dimension: Int) : RealDomain { - override operator fun contains(point: Point): Boolean = true override fun getLowerBound(num: Int, point: Point): Double? = Double.NEGATIVE_INFINITY @@ -32,5 +31,4 @@ class UnconstrainedDomain(override val dimension: Int) : RealDomain { override fun nearestInDomain(point: Point): Point = point override fun volume(): Double = Double.POSITIVE_INFINITY - -} \ No newline at end of file +} diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/domains/UnivariateDomain.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/domains/UnivariateDomain.kt index ef521d5ea..280dc7d66 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/domains/UnivariateDomain.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/domains/UnivariateDomain.kt @@ -4,7 +4,6 @@ import scientifik.kmath.linear.Point import scientifik.kmath.structures.asBuffer inline class UnivariateDomain(val range: ClosedFloatingPointRange) : RealDomain { - operator fun contains(d: Double): Boolean = range.contains(d) override operator fun contains(point: Point): Boolean { @@ -15,10 +14,10 @@ inline class UnivariateDomain(val range: ClosedFloatingPointRange) : Rea override fun nearestInDomain(point: Point): Point { require(point.size == 1) val value = point[0] - return when{ + return when { value in range -> point value >= range.endInclusive -> doubleArrayOf(range.endInclusive).asBuffer() - else -> doubleArrayOf(range.start).asBuffer() + else -> doubleArrayOf(range.start).asBuffer() } } @@ -45,4 +44,4 @@ inline class UnivariateDomain(val range: ClosedFloatingPointRange) : Rea override fun volume(): Double = range.endInclusive - range.start override val dimension: Int get() = 1 -} \ 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 e512b1cd8..b21a414f5 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/Expression.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/Expression.kt @@ -14,9 +14,10 @@ interface Expression { /** * Create simple lazily evaluated expression inside given algebra */ -fun Algebra.expression(block: Algebra.(arguments: Map) -> T): Expression = object: Expression { - override fun invoke(arguments: Map): T = block(arguments) -} +fun Algebra.expression(block: Algebra.(arguments: Map) -> T): Expression = + object : Expression { + override fun invoke(arguments: Map): T = block(arguments) + } operator fun Expression.invoke(vararg pairs: Pair): T = invoke(mapOf(*pairs)) @@ -33,4 +34,4 @@ interface ExpressionAlgebra : Algebra { * A constant expression which does not depend on arguments */ fun const(value: T): E -} \ No newline at end of file +} diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressionAlgebra.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressionAlgebra.kt index a8a26aa33..5a8a10717 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressionAlgebra.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressionAlgebra.kt @@ -143,4 +143,4 @@ inline fun > A.expressionInRing(block: FunctionalExpressionRing> A.expressionInField(block: FunctionalExpressionField.() -> Expression): Expression = - FunctionalExpressionField(this).block() \ No newline at end of file + FunctionalExpressionField(this).block() diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/BufferMatrix.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/BufferMatrix.kt index c4c38284b..2e1f32501 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/BufferMatrix.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/BufferMatrix.kt @@ -19,22 +19,20 @@ class BufferMatrixContext>( override fun point(size: Int, initializer: (Int) -> T): Point = bufferFactory(size, initializer) - companion object { - - } + companion object } @Suppress("OVERRIDE_BY_INLINE") object RealMatrixContext : GenericMatrixContext { - override val elementContext get() = RealField + override val elementContext: RealField get() = RealField override inline fun produce(rows: Int, columns: Int, initializer: (i: Int, j: Int) -> Double): Matrix { val buffer = RealBuffer(rows * columns) { offset -> initializer(offset / columns, offset % columns) } return BufferMatrix(rows, columns, buffer) } - override inline fun point(size: Int, initializer: (Int) -> Double): Point = RealBuffer(size,initializer) + override inline fun point(size: Int, initializer: (Int) -> Double): Point = RealBuffer(size, initializer) } class BufferMatrix( @@ -52,7 +50,7 @@ class BufferMatrix( override val shape: IntArray get() = intArrayOf(rowNum, colNum) - override fun suggestFeature(vararg features: MatrixFeature) = + override fun suggestFeature(vararg features: MatrixFeature): BufferMatrix = BufferMatrix(rowNum, colNum, buffer, this.features + features) override fun get(index: IntArray): T = get(index[0], index[1]) @@ -84,8 +82,8 @@ class BufferMatrix( override fun toString(): String { return if (rowNum <= 5 && colNum <= 5) { "Matrix(rowsNum = $rowNum, colNum = $colNum, features=$features)\n" + - rows.asSequence().joinToString(prefix = "(", postfix = ")", separator = "\n ") { - it.asSequence().joinToString(separator = "\t") { it.toString() } + rows.asSequence().joinToString(prefix = "(", postfix = ")", separator = "\n ") { buffer -> + buffer.asSequence().joinToString(separator = "\t") { it.toString() } } } else { "Matrix(rowsNum = $rowNum, colNum = $colNum, features=$features)" @@ -121,4 +119,4 @@ infix fun BufferMatrix.dot(other: BufferMatrix): BufferMatrix : Matrix { */ fun suggestFeature(vararg features: MatrixFeature): FeaturedMatrix - companion object { - - } + companion object } -fun Structure2D.Companion.real(rows: Int, columns: Int, initializer: (Int, Int) -> Double) = +fun Structure2D.Companion.real(rows: Int, columns: Int, initializer: (Int, Int) -> Double): Matrix = MatrixContext.real.produce(rows, columns, initializer) /** @@ -41,7 +39,7 @@ fun Structure2D.Companion.square(vararg elements: T): FeaturedMatrix.features get() = (this as? FeaturedMatrix)?.features?: emptySet() +val Matrix<*>.features: Set get() = (this as? FeaturedMatrix)?.features ?: emptySet() /** * Check if matrix has the given feature class @@ -68,7 +66,7 @@ fun > GenericMatrixContext.one(rows: Int, columns: In * A virtual matrix of zeroes */ fun > GenericMatrixContext.zero(rows: Int, columns: Int): FeaturedMatrix = - VirtualMatrix(rows, columns) { _, _ -> elementContext.zero } + VirtualMatrix(rows, columns) { _, _ -> elementContext.zero } class TransposedFeature(val original: Matrix) : MatrixFeature @@ -83,4 +81,4 @@ fun Matrix.transpose(): Matrix { ) { i, j -> get(j, i) } } -infix fun Matrix.dot(other: Matrix): Matrix = with(MatrixContext.real) { dot(other) } \ No newline at end of file +infix fun Matrix.dot(other: Matrix): Matrix = with(MatrixContext.real) { dot(other) } diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/LUPDecomposition.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/LUPDecomposition.kt index 75d7da169..d04a99fbb 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/LUPDecomposition.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/LUPDecomposition.kt @@ -18,7 +18,7 @@ class LUPDecomposition( private val even: Boolean ) : LUPDecompositionFeature, DeterminantFeature { - val elementContext get() = context.elementContext + val elementContext: Field get() = context.elementContext /** * Returns the matrix L of the decomposition. @@ -67,7 +67,7 @@ class LUPDecomposition( } -fun , F : Field> GenericMatrixContext.abs(value: T) = +fun , F : Field> GenericMatrixContext.abs(value: T): T = if (value > elementContext.zero) value else with(elementContext) { -value } @@ -169,9 +169,10 @@ fun , F : Field> GenericMatrixContext.lup( inline fun , F : Field> GenericMatrixContext.lup( matrix: Matrix, noinline checkSingular: (T) -> Boolean -) = lup(T::class, matrix, checkSingular) +): LUPDecomposition = lup(T::class, matrix, checkSingular) -fun GenericMatrixContext.lup(matrix: Matrix) = lup(Double::class, matrix) { it < 1e-11 } +fun GenericMatrixContext.lup(matrix: Matrix): LUPDecomposition = + lup(Double::class, matrix) { it < 1e-11 } fun LUPDecomposition.solve(type: KClass, matrix: Matrix): Matrix { @@ -185,7 +186,7 @@ fun LUPDecomposition.solve(type: KClass, matrix: Matrix): Mat // Apply permutations to b val bp = create { _, _ -> zero } - for (row in 0 until pivot.size) { + for (row in pivot.indices) { val bpRow = bp.row(row) val pRow = pivot[row] for (col in 0 until matrix.colNum) { @@ -194,7 +195,7 @@ fun LUPDecomposition.solve(type: KClass, matrix: Matrix): Mat } // Solve LY = b - for (col in 0 until pivot.size) { + for (col in pivot.indices) { val bpCol = bp.row(col) for (i in col + 1 until pivot.size) { val bpI = bp.row(i) @@ -225,7 +226,7 @@ fun LUPDecomposition.solve(type: KClass, matrix: Matrix): Mat } } -inline fun LUPDecomposition.solve(matrix: Matrix) = solve(T::class, matrix) +inline fun LUPDecomposition.solve(matrix: Matrix): Matrix = solve(T::class, matrix) /** * Solve a linear equation **a*x = b** @@ -240,13 +241,12 @@ inline fun , F : Field> GenericMatrixContext. return decomposition.solve(T::class, b) } -fun RealMatrixContext.solve(a: Matrix, b: Matrix) = - solve(a, b) { it < 1e-11 } +fun RealMatrixContext.solve(a: Matrix, b: Matrix): Matrix = solve(a, b) { it < 1e-11 } inline fun , F : Field> GenericMatrixContext.inverse( matrix: Matrix, noinline checkSingular: (T) -> Boolean -) = solve(matrix, one(matrix.rowNum, matrix.colNum), checkSingular) +): Matrix = solve(matrix, one(matrix.rowNum, matrix.colNum), checkSingular) -fun RealMatrixContext.inverse(matrix: Matrix) = - solve(matrix, one(matrix.rowNum, matrix.colNum)) { it < 1e-11 } \ No newline at end of file +fun RealMatrixContext.inverse(matrix: Matrix): Matrix = + solve(matrix, one(matrix.rowNum, matrix.colNum)) { it < 1e-11 } diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/LinearAlgebra.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/LinearAlgebra.kt index abd8603f4..fb49d18ed 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/LinearAlgebra.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/LinearAlgebra.kt @@ -25,4 +25,4 @@ fun Matrix.asPoint(): Point = error("Can't convert matrix with more than one column to vector") } -fun Point.asMatrix() = VirtualMatrix(size, 1) { i, _ -> get(i) } \ No newline at end of file +fun Point.asMatrix(): VirtualMatrix = VirtualMatrix(size, 1) { i, _ -> get(i) } diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/MatrixContext.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/MatrixContext.kt index 7797fdadf..5dc86a7dd 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/MatrixContext.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/MatrixContext.kt @@ -29,7 +29,7 @@ interface MatrixContext : SpaceOperations> { /** * Non-boxing double matrix */ - val real = RealMatrixContext + val real: RealMatrixContext = RealMatrixContext /** * A structured matrix with custom buffer @@ -82,12 +82,12 @@ interface GenericMatrixContext> : MatrixContext { } } - override operator fun Matrix.unaryMinus() = + override operator fun Matrix.unaryMinus(): Matrix = produce(rowNum, colNum) { i, j -> elementContext.run { -get(i, j) } } override fun add(a: Matrix, b: Matrix): Matrix { if (a.rowNum != b.rowNum || a.colNum != b.colNum) error("Matrix operation dimension mismatch. [${a.rowNum},${a.colNum}] + [${b.rowNum},${b.colNum}]") - return produce(a.rowNum, a.colNum) { i, j -> elementContext.run { a.get(i, j) + b[i, j] } } + return produce(a.rowNum, a.colNum) { i, j -> elementContext.run { a[i, j] + b[i, j] } } } override operator fun Matrix.minus(b: Matrix): Matrix { @@ -96,7 +96,7 @@ interface GenericMatrixContext> : MatrixContext { } override fun multiply(a: Matrix, k: Number): Matrix = - produce(a.rowNum, a.colNum) { i, j -> elementContext.run { a.get(i, j) * k } } + produce(a.rowNum, a.colNum) { i, j -> elementContext.run { a[i, j] * k } } operator fun Number.times(matrix: FeaturedMatrix): Matrix = matrix * this diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/MatrixFeatures.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/MatrixFeatures.kt index de315071f..87cfe21b0 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/MatrixFeatures.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/MatrixFeatures.kt @@ -1,7 +1,7 @@ package scientifik.kmath.linear /** - * A marker interface representing some matrix feature like diagonal, sparce, zero, etc. Features used to optimize matrix + * A marker interface representing some matrix feature like diagonal, sparse, zero, etc. Features used to optimize matrix * operations performance in some cases. */ interface MatrixFeature @@ -36,19 +36,19 @@ interface DeterminantFeature : MatrixFeature { } @Suppress("FunctionName") -fun DeterminantFeature(determinant: T) = object: DeterminantFeature{ +fun DeterminantFeature(determinant: T): DeterminantFeature = object : DeterminantFeature { override val determinant: T = determinant } /** * Lower triangular matrix */ -object LFeature: MatrixFeature +object LFeature : MatrixFeature /** * Upper triangular feature */ -object UFeature: MatrixFeature +object UFeature : MatrixFeature /** * TODO add documentation @@ -59,4 +59,4 @@ interface LUPDecompositionFeature : MatrixFeature { val p: FeaturedMatrix } -//TODO add sparse matrix feature \ No newline at end of file +//TODO add sparse matrix feature diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/VectorSpace.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/VectorSpace.kt index 8e14e2882..691b464fc 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/VectorSpace.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/VectorSpace.kt @@ -54,7 +54,7 @@ interface VectorSpace> : Space> { size: Int, space: S, bufferFactory: BufferFactory = Buffer.Companion::boxing - ) = BufferVectorSpace(size, space, bufferFactory) + ): BufferVectorSpace = BufferVectorSpace(size, space, bufferFactory) /** * Automatic buffered vector, unboxed if it is possible @@ -70,6 +70,6 @@ class BufferVectorSpace>( override val space: S, val bufferFactory: BufferFactory ) : VectorSpace { - override fun produce(initializer: (Int) -> T) = bufferFactory(size, initializer) + override fun produce(initializer: (Int) -> T): Buffer = bufferFactory(size, initializer) //override fun produceElement(initializer: (Int) -> T): Vector = BufferVector(this, produce(initializer)) -} \ No newline at end of file +} diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/VirtualMatrix.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/VirtualMatrix.kt index 0806cabea..207151d57 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/VirtualMatrix.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/VirtualMatrix.kt @@ -20,7 +20,7 @@ class VirtualMatrix( override fun get(i: Int, j: Int): T = generator(i, j) - override fun suggestFeature(vararg features: MatrixFeature) = + override fun suggestFeature(vararg features: MatrixFeature): VirtualMatrix = VirtualMatrix(rowNum, colNum, this.features + features, generator) override fun equals(other: Any?): Boolean { @@ -56,4 +56,4 @@ class VirtualMatrix( } } } -} \ No newline at end of file +} diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/misc/AutoDiff.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/misc/AutoDiff.kt index 076701a4f..db8863ae8 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/misc/AutoDiff.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/misc/AutoDiff.kt @@ -22,12 +22,12 @@ class DerivationResult( val deriv: Map, T>, val context: Field ) : Variable(value) { - fun deriv(variable: Variable) = deriv[variable] ?: context.zero + fun deriv(variable: Variable): T = deriv[variable] ?: context.zero /** * compute divergence */ - fun div() = context.run { sum(deriv.values) } + fun div(): T = context.run { sum(deriv.values) } /** * Compute a gradient for variables in given order @@ -53,7 +53,7 @@ class DerivationResult( * ``` */ fun > F.deriv(body: AutoDiffField.() -> Variable): DerivationResult = - AutoDiffContext(this).run { + AutoDiffContext(this).run { val result = body() result.d = context.one// computing derivative w.r.t result runBackwardPass() @@ -86,7 +86,7 @@ abstract class AutoDiffField> : Field> { abstract fun variable(value: T): Variable - inline fun variable(block: F.() -> T) = variable(context.block()) + inline fun variable(block: F.() -> T): Variable = variable(context.block()) // Overloads for Double constants @@ -236,4 +236,4 @@ fun > AutoDiffField.sin(x: Variable): Var fun > AutoDiffField.cos(x: Variable): Variable = derive(variable { cos(x.value) }) { z -> x.d -= z.d * sin(x.value) - } \ No newline at end of file + } diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/misc/Grids.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/misc/Grids.kt index f040fb8d4..d3bf0891f 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/misc/Grids.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/misc/Grids.kt @@ -43,4 +43,4 @@ fun ClosedFloatingPointRange.toSequenceWithPoints(numPoints: Int): Seque fun ClosedFloatingPointRange.toGrid(numPoints: Int): DoubleArray { if (numPoints < 2) error("Can't create generic grid with less than two points") return DoubleArray(numPoints) { i -> start + (endInclusive - start) / (numPoints - 1) * i } -} \ No newline at end of file +} 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 c3cfc448a..a0f4525cc 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/misc/cumulative.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/misc/cumulative.kt @@ -1,14 +1,15 @@ package scientifik.kmath.misc import scientifik.kmath.operations.Space +import scientifik.kmath.operations.invoke import kotlin.jvm.JvmName - /** - * Generic cumulative operation on iterator - * @param T type of initial iterable - * @param R type of resulting iterable - * @param initial lazy evaluated + * Generic cumulative operation on iterator. + * + * @param T the type of initial iterable. + * @param R the type of resulting iterable. + * @param initial lazy evaluated. */ fun Iterator.cumulative(initial: R, operation: (R, T) -> R): Iterator = object : Iterator { var state: R = initial @@ -36,41 +37,41 @@ fun List.cumulative(initial: R, operation: (R, T) -> R): List = /** * Cumulative sum with custom space */ -fun Iterable.cumulativeSum(space: Space) = with(space) { +fun Iterable.cumulativeSum(space: Space): Iterable = space { cumulative(zero) { element: T, sum: T -> sum + element } } @JvmName("cumulativeSumOfDouble") -fun Iterable.cumulativeSum() = this.cumulative(0.0) { element, sum -> sum + element } +fun Iterable.cumulativeSum(): Iterable = this.cumulative(0.0) { element, sum -> sum + element } @JvmName("cumulativeSumOfInt") -fun Iterable.cumulativeSum() = this.cumulative(0) { element, sum -> sum + element } +fun Iterable.cumulativeSum(): Iterable = this.cumulative(0) { element, sum -> sum + element } @JvmName("cumulativeSumOfLong") -fun Iterable.cumulativeSum() = this.cumulative(0L) { element, sum -> sum + element } +fun Iterable.cumulativeSum(): Iterable = this.cumulative(0L) { element, sum -> sum + element } -fun Sequence.cumulativeSum(space: Space) = with(space) { +fun Sequence.cumulativeSum(space: Space): Sequence = with(space) { cumulative(zero) { element: T, sum: T -> sum + element } } @JvmName("cumulativeSumOfDouble") -fun Sequence.cumulativeSum() = this.cumulative(0.0) { element, sum -> sum + element } +fun Sequence.cumulativeSum(): Sequence = this.cumulative(0.0) { element, sum -> sum + element } @JvmName("cumulativeSumOfInt") -fun Sequence.cumulativeSum() = this.cumulative(0) { element, sum -> sum + element } +fun Sequence.cumulativeSum(): Sequence = this.cumulative(0) { element, sum -> sum + element } @JvmName("cumulativeSumOfLong") -fun Sequence.cumulativeSum() = this.cumulative(0L) { element, sum -> sum + element } +fun Sequence.cumulativeSum(): Sequence = this.cumulative(0L) { element, sum -> sum + element } -fun List.cumulativeSum(space: Space) = with(space) { +fun List.cumulativeSum(space: Space): List = with(space) { cumulative(zero) { element: T, sum: T -> sum + element } } @JvmName("cumulativeSumOfDouble") -fun List.cumulativeSum() = this.cumulative(0.0) { element, sum -> sum + element } +fun List.cumulativeSum(): List = this.cumulative(0.0) { element, sum -> sum + element } @JvmName("cumulativeSumOfInt") -fun List.cumulativeSum() = this.cumulative(0) { element, sum -> sum + element } +fun List.cumulativeSum(): List = 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(): List = this.cumulative(0L) { element, sum -> sum + element } 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 52b6bba02..aa190b31f 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Algebra.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Algebra.kt @@ -1,10 +1,15 @@ package scientifik.kmath.operations +/** + * Stub for DSL the [Algebra] is. + */ @DslMarker annotation class KMathContext /** - * Marker interface for any algebra + * Represents an algebraic structure. + * + * @param T the type of element of this structure. */ interface Algebra { /** @@ -24,50 +29,122 @@ interface Algebra { } /** - * An algebra with numeric representation of members + * An algebraic structure where elements can have numeric representation. + * + * @param T the type of element of this structure. */ interface NumericAlgebra : Algebra { /** - * Wrap a number + * Wraps a number. */ fun number(value: Number): T + /** + * Dynamic call of binary operation [operation] on [left] and [right] where left element is [Number]. + */ fun leftSideNumberOperation(operation: String, left: Number, right: T): T = binaryOperation(operation, number(left), right) + /** + * Dynamic call of binary operation [operation] on [left] and [right] where right element is [Number]. + */ fun rightSideNumberOperation(operation: String, left: T, right: Number): T = leftSideNumberOperation(operation, right, left) } /** - * Call a block with an [Algebra] as receiver + * Call a block with an [Algebra] as receiver. */ inline operator fun , R> A.invoke(block: A.() -> R): R = run(block) /** - * Space-like operations without neutral element + * Represents semigroup, i.e. algebraic structure with associative binary operation called "addition". + * + * In KMath groups are called spaces, and also define multiplication of element by [Number]. + * + * @param T the type of element of this semigroup. */ interface SpaceOperations : Algebra { /** - * Addition operation for two context elements + * Addition of two elements. + * + * @param a the addend. + * @param b the augend. + * @return the sum. */ fun add(a: T, b: T): T /** - * Multiplication operation for context element and real number + * Multiplication of element by scalar. + * + * @param a the multiplier. + * @param k the multiplicand. + * @return the produce. */ fun multiply(a: T, k: Number): T - //Operation to be performed in this context. Could be moved to extensions in case of KEEP-176 + // Operations to be performed in this context. Could be moved to extensions in case of KEEP-176 + + /** + * The negation of this element. + * + * @receiver this value. + * @return the additive inverse of this value. + */ operator fun T.unaryMinus(): T = multiply(this, -1.0) + /** + * Returns this value. + * + * @receiver this value. + * @return this value. + */ operator fun T.unaryPlus(): T = this + /** + * Addition of two elements. + * + * @receiver the addend. + * @param b the augend. + * @return the sum. + */ operator fun T.plus(b: T): T = add(this, b) + + /** + * Subtraction of two elements. + * + * @receiver the minuend. + * @param b the subtrahend. + * @return the difference. + */ operator fun T.minus(b: T): T = add(this, -b) - 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 + + /** + * Multiplication of this element by a scalar. + * + * @receiver the multiplier. + * @param k the multiplicand. + * @return the product. + */ + operator fun T.times(k: Number): T = multiply(this, k.toDouble()) + + /** + * Division of this element by scalar. + * + * @receiver the dividend. + * @param k the divisor. + * @return the quotient. + */ + operator fun T.div(k: Number): T = multiply(this, 1.0 / k.toDouble()) + + /** + * Multiplication of this number by element. + * + * @receiver the multiplier. + * @param b the multiplicand. + * @return the product. + */ + operator fun Number.times(b: T): T = b * this override fun unaryOperation(operation: String, arg: T): T = when (operation) { PLUS_OPERATION -> arg @@ -82,37 +159,56 @@ interface SpaceOperations : Algebra { } companion object { - const val PLUS_OPERATION = "+" - const val MINUS_OPERATION = "-" - const val NOT_OPERATION = "!" + /** + * The identifier of addition. + */ + const val PLUS_OPERATION: String = "+" + + /** + * The identifier of subtraction (and negation). + */ + const val MINUS_OPERATION: String = "-" + + const val NOT_OPERATION: String = "!" } } - /** - * A general interface representing linear context of some kind. - * The context defines sum operation for its elements and multiplication by real value. - * One must note that in some cases context is a singleton class, but in some cases it - * works as a context for operations inside it. + * Represents group, i.e. algebraic structure with associative binary operation called "addition" and its neutral + * element. * - * TODO do we need non-commutative context? + * In KMath groups are called spaces, and also define multiplication of element by [Number]. + * + * @param T the type of element of this group. */ interface Space : SpaceOperations { /** - * Neutral element for sum operation + * The neutral element of addition. */ val zero: T } /** - * Operations on ring without multiplication neutral element + * Represents semiring, i.e. algebraic structure with two associative binary operations called "addition" and + * "multiplication". + * + * @param T the type of element of this semiring. */ interface RingOperations : SpaceOperations { /** - * Multiplication for two field elements + * Multiplies two elements. + * + * @param a the multiplier. + * @param b the multiplicand. */ fun multiply(a: T, b: T): T + /** + * Multiplies this element by scalar. + * + * @receiver the multiplier. + * @param b the multiplicand. + */ operator fun T.times(b: T): T = multiply(this, b) override fun binaryOperation(operation: String, left: T, right: T): T = when (operation) { @@ -121,12 +217,18 @@ interface RingOperations : SpaceOperations { } companion object { - const val TIMES_OPERATION = "*" + /** + * The identifier of multiplication. + */ + const val TIMES_OPERATION: String = "*" } } /** - * The same as {@link Space} but with additional multiplication operation + * Represents ring, i.e. algebraic structure with two associative binary operations called "addition" and + * "multiplication" and their neutral elements. + * + * @param T the type of element of this ring. */ interface Ring : Space, RingOperations, NumericAlgebra { /** @@ -150,20 +252,64 @@ interface Ring : Space, RingOperations, NumericAlgebra { else -> super.rightSideNumberOperation(operation, left, right) } + /** + * Addition of element and scalar. + * + * @receiver the addend. + * @param b the augend. + */ + operator fun T.plus(b: Number): T = this + number(b) - operator fun T.plus(b: Number) = this.plus(number(b)) - operator fun Number.plus(b: T) = b + this + /** + * Addition of scalar and element. + * + * @receiver the addend. + * @param b the augend. + */ + operator fun Number.plus(b: T): T = b + this - operator fun T.minus(b: Number) = this.minus(number(b)) - operator fun Number.minus(b: T) = -b + this + /** + * Subtraction of element from number. + * + * @receiver the minuend. + * @param b the subtrahend. + * @receiver the difference. + */ + operator fun T.minus(b: Number): T = this - number(b) + + /** + * Subtraction of number from element. + * + * @receiver the minuend. + * @param b the subtrahend. + * @receiver the difference. + */ + operator fun Number.minus(b: T): T = -b + this } /** - * All ring operations but without neutral elements + * Represents semifield, i.e. algebraic structure with three operations: associative "addition" and "multiplication", + * and "division". + * + * @param T the type of element of this semifield. */ interface FieldOperations : RingOperations { + /** + * Division of two elements. + * + * @param a the dividend. + * @param b the divisor. + * @return the quotient. + */ fun divide(a: T, b: T): T + /** + * Division of two elements. + * + * @receiver the dividend. + * @param b the divisor. + * @return the quotient. + */ operator fun T.div(b: T): T = divide(this, b) override fun binaryOperation(operation: String, left: T, right: T): T = when (operation) { @@ -172,13 +318,26 @@ interface FieldOperations : RingOperations { } companion object { - const val DIV_OPERATION = "/" + /** + * The identifier of division. + */ + const val DIV_OPERATION: String = "/" } } /** - * Four operations algebra + * Represents field, i.e. algebraic structure with three operations: associative "addition" and "multiplication", + * and "division" and their neutral elements. + * + * @param T the type of element of this semifield. */ interface Field : Ring, FieldOperations { - operator fun Number.div(b: T) = this * divide(one, b) + /** + * Division of element by scalar. + * + * @receiver the dividend. + * @param b the divisor. + * @return the quotient. + */ + operator fun Number.div(b: T): T = this * divide(one, b) } diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/AlgebraElements.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/AlgebraElements.kt index 093021ae3..9b430af39 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/AlgebraElements.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/AlgebraElements.kt @@ -2,13 +2,12 @@ package scientifik.kmath.operations /** * The generic mathematics elements which is able to store its context - * @param T the type of space operation results - * @param I self type of the element. Needed for static type checking - * @param C the type of mathematical context for this element + * + * @param C the type of mathematical context for this element. */ interface MathElement { /** - * The context this element belongs to + * The context this element belongs to. */ val context: C } @@ -25,18 +24,17 @@ interface MathWrapper { * @param S the type of space */ interface SpaceElement, S : Space> : MathElement, MathWrapper { - - operator fun plus(b: T) = context.add(unwrap(), b).wrap() - operator fun minus(b: T) = context.add(unwrap(), context.multiply(b, -1.0)).wrap() - operator fun times(k: Number) = context.multiply(unwrap(), k.toDouble()).wrap() - operator fun div(k: Number) = context.multiply(unwrap(), 1.0 / k.toDouble()).wrap() + operator fun plus(b: T): I = context.add(unwrap(), b).wrap() + operator fun minus(b: T): I = context.add(unwrap(), context.multiply(b, -1.0)).wrap() + operator fun times(k: Number): I = context.multiply(unwrap(), k.toDouble()).wrap() + operator fun div(k: Number): I = context.multiply(unwrap(), 1.0 / k.toDouble()).wrap() } /** * Ring element */ interface RingElement, R : Ring> : SpaceElement { - operator fun times(b: T) = context.multiply(unwrap(), b).wrap() + operator fun times(b: T): I = context.multiply(unwrap(), b).wrap() } /** @@ -44,5 +42,5 @@ interface RingElement, R : Ring> : SpaceElement, F : Field> : RingElement { override val context: F - operator fun div(b: T) = context.divide(unwrap(), b).wrap() -} \ No newline at end of file + operator fun div(b: T): I = context.divide(unwrap(), b).wrap() +} diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/AlgebraExtensions.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/AlgebraExtensions.kt index bfb4199a3..a6d508ee4 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/AlgebraExtensions.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/AlgebraExtensions.kt @@ -12,4 +12,4 @@ fun RingOperations.power(arg: T, power: Int): T { res *= arg } return res -} \ No newline at end of file +} diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/BigInt.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/BigInt.kt index 5e7d49b1c..fd7719157 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/BigInt.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/BigInt.kt @@ -194,8 +194,8 @@ class BigInt internal constructor( } infix fun or(other: BigInt): BigInt { - if (this == ZERO) return other; - if (other == ZERO) return this; + if (this == ZERO) return other + if (other == ZERO) return this val resSize = max(this.magnitude.size, other.magnitude.size) val newMagnitude: Magnitude = Magnitude(resSize) for (i in 0 until resSize) { @@ -210,7 +210,7 @@ class BigInt internal constructor( } infix fun and(other: BigInt): BigInt { - if ((this == ZERO) or (other == ZERO)) return ZERO; + if ((this == ZERO) or (other == ZERO)) return ZERO val resSize = min(this.magnitude.size, other.magnitude.size) val newMagnitude: Magnitude = Magnitude(resSize) for (i in 0 until resSize) { @@ -260,7 +260,7 @@ class BigInt internal constructor( } companion object { - const val BASE = 0xffffffffUL + const val BASE: ULong = 0xffffffffUL const val BASE_SIZE: Int = 32 val ZERO: BigInt = BigInt(0, uintArrayOf()) val ONE: BigInt = BigInt(1, uintArrayOf(1u)) @@ -394,12 +394,12 @@ fun abs(x: BigInt): BigInt = x.abs() /** * Convert this [Int] to [BigInt] */ -fun Int.toBigInt() = BigInt(sign.toByte(), uintArrayOf(kotlin.math.abs(this).toUInt())) +fun Int.toBigInt(): BigInt = BigInt(sign.toByte(), uintArrayOf(kotlin.math.abs(this).toUInt())) /** * Convert this [Long] to [BigInt] */ -fun Long.toBigInt() = BigInt( +fun Long.toBigInt(): BigInt = BigInt( sign.toByte(), stripLeadingZeros( uintArrayOf( (kotlin.math.abs(this).toULong() and BASE).toUInt(), @@ -411,17 +411,17 @@ fun Long.toBigInt() = BigInt( /** * Convert UInt to [BigInt] */ -fun UInt.toBigInt() = BigInt(1, uintArrayOf(this)) +fun UInt.toBigInt(): BigInt = BigInt(1, uintArrayOf(this)) /** * Convert ULong to [BigInt] */ -fun ULong.toBigInt() = BigInt( +fun ULong.toBigInt(): BigInt = BigInt( 1, stripLeadingZeros( uintArrayOf( - (this and BigInt.BASE).toUInt(), - ((this shr BigInt.BASE_SIZE) and BigInt.BASE).toUInt() + (this and BASE).toUInt(), + ((this shr BASE_SIZE) and BASE).toUInt() ) ) ) @@ -434,7 +434,7 @@ fun UIntArray.toBigInt(sign: Byte): BigInt { return BigInt(sign, this.copyOf()) } -val hexChToInt = hashMapOf( +val hexChToInt: MutableMap = hashMapOf( '0' to 0, '1' to 1, '2' to 2, '3' to 3, '4' to 4, '5' to 5, '6' to 6, '7' to 7, '8' to 8, '9' to 9, 'A' to 10, 'B' to 11, @@ -497,4 +497,4 @@ fun NDElement.Companion.bigInt( vararg shape: Int, initializer: BigIntField.(IntArray) -> BigInt ): BufferedNDRingElement = - NDAlgebra.bigInt(*shape).produce(initializer) \ No newline at end of file + NDAlgebra.bigInt(*shape).produce(initializer) 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 398ea4395..ffd6c8dec 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Complex.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Complex.kt @@ -18,7 +18,7 @@ object ComplexField : ExtendedField { override val one: Complex = Complex(1.0, 0.0) - val i = Complex(0.0, 1.0) + val i: Complex = Complex(0.0, 1.0) override fun add(a: Complex, b: Complex): Complex = Complex(a.re + b.re, a.im + b.im) @@ -45,15 +45,15 @@ object ComplexField : ExtendedField { override fun ln(arg: Complex): Complex = ln(arg.r) + i * atan2(arg.im, arg.re) - operator fun Double.plus(c: Complex) = add(this.toComplex(), c) + operator fun Double.plus(c: Complex): Complex = add(this.toComplex(), c) - operator fun Double.minus(c: Complex) = add(this.toComplex(), -c) + operator fun Double.minus(c: Complex): Complex = add(this.toComplex(), -c) - operator fun Complex.plus(d: Double) = d + this + operator fun Complex.plus(d: Double): Complex = d + this - operator fun Complex.minus(d: Double) = add(this, -d.toComplex()) + operator fun Complex.minus(d: Double): Complex = add(this, -d.toComplex()) - operator fun Double.times(c: Complex) = Complex(c.re * this, c.im * this) + operator fun Double.times(c: Complex): Complex = Complex(c.re * this, c.im * this) override fun symbol(value: String): Complex = if (value == "i") { i @@ -104,7 +104,7 @@ val Complex.r: Double get() = sqrt(re * re + im * im) */ val Complex.theta: Double get() = atan(im / re) -fun Double.toComplex() = Complex(this, 0.0) +fun Double.toComplex(): Complex = Complex(this, 0.0) inline fun Buffer.Companion.complex(size: Int, crossinline init: (Int) -> Complex): Buffer { return MemoryBuffer.create(Complex, size, init) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/NumberAlgebra.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/NumberAlgebra.kt index 2c57d0098..2145eb4b5 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/NumberAlgebra.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/NumberAlgebra.kt @@ -1,6 +1,5 @@ package scientifik.kmath.operations -import scientifik.kmath.operations.RealField.pow import kotlin.math.abs import kotlin.math.pow as kpow @@ -38,6 +37,8 @@ interface ExtendedField : ExtendedFieldOperations, Field { /** * Real field element wrapping double. * + * @property value the [Double] value wrapped by this [Real]. + * * TODO inline does not work due to compiler bug. Waiting for fix for KT-27586 */ inline class Real(val value: Double) : FieldElement { @@ -45,7 +46,7 @@ inline class Real(val value: Double) : FieldElement { override fun Double.wrap(): Real = Real(value) - override val context get() = RealField + override val context: RealField get() = RealField companion object } @@ -56,36 +57,36 @@ inline class Real(val value: Double) : FieldElement { @Suppress("EXTENSION_SHADOWED_BY_MEMBER", "OVERRIDE_BY_INLINE", "NOTHING_TO_INLINE") object RealField : ExtendedField, Norm { override val zero: Double = 0.0 - override inline fun add(a: Double, b: Double) = a + b - override inline fun multiply(a: Double, b: Double) = a * b - override inline fun multiply(a: Double, k: Number) = a * k.toDouble() + override inline fun add(a: Double, b: Double): Double = a + b + override inline fun multiply(a: Double, b: Double): Double = a * b + override inline fun multiply(a: Double, k: Number): Double = a * k.toDouble() override val one: Double = 1.0 - override inline fun divide(a: Double, b: Double) = a / b + override inline fun divide(a: Double, b: Double): Double = a / b - override inline fun sin(arg: Double) = kotlin.math.sin(arg) - override inline fun cos(arg: Double) = kotlin.math.cos(arg) + override inline fun sin(arg: Double): Double = kotlin.math.sin(arg) + override inline fun cos(arg: Double): Double = kotlin.math.cos(arg) override inline fun tan(arg: Double): Double = kotlin.math.tan(arg) override inline fun acos(arg: Double): Double = kotlin.math.acos(arg) override inline fun asin(arg: Double): Double = kotlin.math.asin(arg) override inline fun atan(arg: Double): Double = kotlin.math.atan(arg) - override inline fun power(arg: Double, pow: Number) = arg.kpow(pow.toDouble()) + override inline fun power(arg: Double, pow: Number): Double = arg.kpow(pow.toDouble()) - override inline fun exp(arg: Double) = kotlin.math.exp(arg) - override inline fun ln(arg: Double) = kotlin.math.ln(arg) + override inline fun exp(arg: Double): Double = kotlin.math.exp(arg) + override inline fun ln(arg: Double): Double = kotlin.math.ln(arg) - override inline fun norm(arg: Double) = abs(arg) + override inline fun norm(arg: Double): Double = abs(arg) - override inline fun Double.unaryMinus() = -this + override inline fun Double.unaryMinus(): Double = -this - override inline fun Double.plus(b: Double) = this + b + override inline fun Double.plus(b: Double): Double = this + b - override inline fun Double.minus(b: Double) = this - b + override inline fun Double.minus(b: Double): Double = this - b - override inline fun Double.times(b: Double) = this * b + override inline fun Double.times(b: Double): Double = this * b - override inline fun Double.div(b: Double) = this / b + override inline fun Double.div(b: Double): Double = this / b override fun binaryOperation(operation: String, left: Double, right: Double): Double = when (operation) { PowerOperations.POW_OPERATION -> left pow right @@ -96,36 +97,36 @@ object RealField : ExtendedField, Norm { @Suppress("EXTENSION_SHADOWED_BY_MEMBER", "OVERRIDE_BY_INLINE", "NOTHING_TO_INLINE") object FloatField : ExtendedField, Norm { override val zero: Float = 0f - override inline fun add(a: Float, b: Float) = a + b - override inline fun multiply(a: Float, b: Float) = a * b - override inline fun multiply(a: Float, k: Number) = a * k.toFloat() + override inline fun add(a: Float, b: Float): Float = a + b + override inline fun multiply(a: Float, b: Float): Float = a * b + override inline fun multiply(a: Float, k: Number): Float = a * k.toFloat() override val one: Float = 1f - override inline fun divide(a: Float, b: Float) = a / b + override inline fun divide(a: Float, b: Float): Float = a / b - override inline fun sin(arg: Float) = kotlin.math.sin(arg) - override inline fun cos(arg: Float) = kotlin.math.cos(arg) - override inline fun tan(arg: Float) = kotlin.math.tan(arg) - override inline fun acos(arg: Float) = kotlin.math.acos(arg) - override inline fun asin(arg: Float) = kotlin.math.asin(arg) - override inline fun atan(arg: Float) = kotlin.math.atan(arg) + override inline fun sin(arg: Float): Float = kotlin.math.sin(arg) + override inline fun cos(arg: Float): Float = kotlin.math.cos(arg) + override inline fun tan(arg: Float): Float = kotlin.math.tan(arg) + override inline fun acos(arg: Float): Float = kotlin.math.acos(arg) + override inline fun asin(arg: Float): Float = kotlin.math.asin(arg) + override inline fun atan(arg: Float): Float = kotlin.math.atan(arg) - override inline fun power(arg: Float, pow: Number) = arg.pow(pow.toFloat()) + override inline fun power(arg: Float, pow: Number): Float = arg.pow(pow.toFloat()) - override inline fun exp(arg: Float) = kotlin.math.exp(arg) - override inline fun ln(arg: Float) = kotlin.math.ln(arg) + override inline fun exp(arg: Float): Float = kotlin.math.exp(arg) + override inline fun ln(arg: Float): Float = kotlin.math.ln(arg) - override inline fun norm(arg: Float) = abs(arg) + override inline fun norm(arg: Float): Float = abs(arg) - override inline fun Float.unaryMinus() = -this + override inline fun Float.unaryMinus(): Float = -this - override inline fun Float.plus(b: Float) = this + b + override inline fun Float.plus(b: Float): Float = this + b - override inline fun Float.minus(b: Float) = this - b + override inline fun Float.minus(b: Float): Float = this - b - override inline fun Float.times(b: Float) = this * b + override inline fun Float.times(b: Float): Float = this * b - override inline fun Float.div(b: Float) = this / b + override inline fun Float.div(b: Float): Float = this / b } /** @@ -134,14 +135,14 @@ object FloatField : ExtendedField, Norm { @Suppress("EXTENSION_SHADOWED_BY_MEMBER", "OVERRIDE_BY_INLINE", "NOTHING_TO_INLINE") object IntRing : Ring, Norm { override val zero: Int = 0 - override inline fun add(a: Int, b: Int) = a + b - override inline fun multiply(a: Int, b: Int) = a * b - override inline fun multiply(a: Int, k: Number) = k.toInt() * a + override inline fun add(a: Int, b: Int): Int = a + b + override inline fun multiply(a: Int, b: Int): Int = a * b + override inline fun multiply(a: Int, k: Number): Int = k.toInt() * a override val one: Int = 1 - override inline fun norm(arg: Int) = abs(arg) + override inline fun norm(arg: Int): Int = abs(arg) - override inline fun Int.unaryMinus() = -this + override inline fun Int.unaryMinus(): Int = -this override inline fun Int.plus(b: Int): Int = this + b @@ -156,20 +157,20 @@ object IntRing : Ring, Norm { @Suppress("EXTENSION_SHADOWED_BY_MEMBER", "OVERRIDE_BY_INLINE", "NOTHING_TO_INLINE") object ShortRing : Ring, Norm { override val zero: Short = 0 - override inline fun add(a: Short, b: Short) = (a + b).toShort() - override inline fun multiply(a: Short, b: Short) = (a * b).toShort() - override inline fun multiply(a: Short, k: Number) = (a * k.toShort()).toShort() + override inline fun add(a: Short, b: Short): Short = (a + b).toShort() + override inline fun multiply(a: Short, b: Short): Short = (a * b).toShort() + override inline fun multiply(a: Short, k: Number): Short = (a * k.toShort()).toShort() override val one: Short = 1 override fun norm(arg: Short): Short = if (arg > 0) arg else (-arg).toShort() - override inline fun Short.unaryMinus() = (-this).toShort() + override inline fun Short.unaryMinus(): Short = (-this).toShort() - override inline fun Short.plus(b: Short) = (this + b).toShort() + override inline fun Short.plus(b: Short): Short = (this + b).toShort() - override inline fun Short.minus(b: Short) = (this - b).toShort() + override inline fun Short.minus(b: Short): Short = (this - b).toShort() - override inline fun Short.times(b: Short) = (this * b).toShort() + override inline fun Short.times(b: Short): Short = (this * b).toShort() } /** @@ -178,20 +179,20 @@ object ShortRing : Ring, Norm { @Suppress("EXTENSION_SHADOWED_BY_MEMBER", "OVERRIDE_BY_INLINE", "NOTHING_TO_INLINE") object ByteRing : Ring, Norm { override val zero: Byte = 0 - override inline fun add(a: Byte, b: Byte) = (a + b).toByte() - override inline fun multiply(a: Byte, b: Byte) = (a * b).toByte() - override inline fun multiply(a: Byte, k: Number) = (a * k.toByte()).toByte() + override inline fun add(a: Byte, b: Byte): Byte = (a + b).toByte() + override inline fun multiply(a: Byte, b: Byte): Byte = (a * b).toByte() + override inline fun multiply(a: Byte, k: Number): Byte = (a * k.toByte()).toByte() override val one: Byte = 1 override fun norm(arg: Byte): Byte = if (arg > 0) arg else (-arg).toByte() - override inline fun Byte.unaryMinus() = (-this).toByte() + override inline fun Byte.unaryMinus(): Byte = (-this).toByte() - override inline fun Byte.plus(b: Byte) = (this + b).toByte() + override inline fun Byte.plus(b: Byte): Byte = (this + b).toByte() - override inline fun Byte.minus(b: Byte) = (this - b).toByte() + override inline fun Byte.minus(b: Byte): Byte = (this - b).toByte() - override inline fun Byte.times(b: Byte) = (this * b).toByte() + override inline fun Byte.times(b: Byte): Byte = (this * b).toByte() } /** @@ -200,18 +201,18 @@ object ByteRing : Ring, Norm { @Suppress("EXTENSION_SHADOWED_BY_MEMBER", "OVERRIDE_BY_INLINE", "NOTHING_TO_INLINE") object LongRing : Ring, Norm { override val zero: Long = 0 - override inline fun add(a: Long, b: Long) = (a + b) - override inline fun multiply(a: Long, b: Long) = (a * b) - override inline fun multiply(a: Long, k: Number) = a * k.toLong() + override inline fun add(a: Long, b: Long): Long = (a + b) + override inline fun multiply(a: Long, b: Long): Long = (a * b) + override inline fun multiply(a: Long, k: Number): Long = a * k.toLong() override val one: Long = 1 override fun norm(arg: Long): Long = abs(arg) - override inline fun Long.unaryMinus() = (-this) + override inline fun Long.unaryMinus(): Long = (-this) - override inline fun Long.plus(b: Long) = (this + b) + override inline fun Long.plus(b: Long): Long = (this + b) - override inline fun Long.minus(b: Long) = (this - b) + override inline fun Long.minus(b: Long): Long = (this - b) - override inline fun Long.times(b: Long) = (this * b) + override inline fun Long.times(b: Long): Long = (this * 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 709f0260f..75adb1dee 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/OptionalOperations.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/OptionalOperations.kt @@ -1,84 +1,210 @@ package scientifik.kmath.operations - -/* Trigonometric operations */ - /** - * A container for trigonometric operations for specific type. Trigonometric operations are limited to fields. + * A container for trigonometric operations for specific type. They are limited to semifields. * * The operations are not exposed to class directly to avoid method bloat but instead are declared in the field. - * It also allows to override behavior for optional operations - * + * It also allows to override behavior for optional operations. */ interface TrigonometricOperations : FieldOperations { + /** + * Computes the sine of [arg]. + */ fun sin(arg: T): T + + /** + * Computes the cosine of [arg]. + */ fun cos(arg: T): T + + /** + * Computes the tangent of [arg]. + */ fun tan(arg: T): T companion object { - const val SIN_OPERATION = "sin" - const val COS_OPERATION = "cos" - const val TAN_OPERATION = "tan" + /** + * The identifier of sine. + */ + const val SIN_OPERATION: String = "sin" + + /** + * The identifier of cosine. + */ + const val COS_OPERATION: String = "cos" + + /** + * The identifier of tangent. + */ + const val TAN_OPERATION: String = "tan" } } +/** + * A container for inverse trigonometric operations for specific type. They are limited to semifields. + * + * The operations are not exposed to class directly to avoid method bloat but instead are declared in the field. + * It also allows to override behavior for optional operations. + */ interface InverseTrigonometricOperations : TrigonometricOperations { + /** + * Computes the inverse sine of [arg]. + */ fun asin(arg: T): T + + /** + * Computes the inverse cosine of [arg]. + */ fun acos(arg: T): T + + /** + * Computes the inverse tangent of [arg]. + */ fun atan(arg: T): T companion object { - const val ASIN_OPERATION = "asin" - const val ACOS_OPERATION = "acos" - const val ATAN_OPERATION = "atan" + /** + * The identifier of inverse sine. + */ + const val ASIN_OPERATION: String = "asin" + + /** + * The identifier of inverse cosine. + */ + const val ACOS_OPERATION: String = "acos" + + /** + * The identifier of inverse tangent. + */ + const val ATAN_OPERATION: String = "atan" } } -fun >> sin(arg: T): T = arg.context.sin(arg) -fun >> cos(arg: T): T = arg.context.cos(arg) -fun >> tan(arg: T): T = arg.context.tan(arg) -fun >> asin(arg: T): T = arg.context.asin(arg) -fun >> acos(arg: T): T = arg.context.acos(arg) -fun >> atan(arg: T): T = arg.context.atan(arg) - -/* Power and roots */ - /** - * A context extension to include power operations like square roots, etc + * Computes the sine of [arg]. + */ +fun >> sin(arg: T): T = arg.context.sin(arg) + +/** + * Computes the cosine of [arg]. + */ +fun >> cos(arg: T): T = arg.context.cos(arg) + +/** + * Computes the tangent of [arg]. + */ +fun >> tan(arg: T): T = arg.context.tan(arg) + +/** + * Computes the inverse sine of [arg]. + */ +fun >> asin(arg: T): T = arg.context.asin(arg) + +/** + * Computes the inverse cosine of [arg]. + */ +fun >> acos(arg: T): T = arg.context.acos(arg) + +/** + * Computes the inverse tangent of [arg]. + */ +fun >> atan(arg: T): T = arg.context.atan(arg) + +/** + * A context extension to include power operations based on exponentiation. */ interface PowerOperations : Algebra { + /** + * Raises [arg] to the power [pow]. + */ fun power(arg: T, pow: Number): T - fun sqrt(arg: T) = power(arg, 0.5) - infix fun T.pow(pow: Number) = power(this, pow) + /** + * Computes the square root of the value [arg]. + */ + fun sqrt(arg: T): T = power(arg, 0.5) + + /** + * Raises this value to the power [pow]. + */ + infix fun T.pow(pow: Number): T = power(this, pow) companion object { - const val POW_OPERATION = "pow" - const val SQRT_OPERATION = "sqrt" + /** + * The identifier of exponentiation. + */ + const val POW_OPERATION: String = "pow" + + /** + * The identifier of square root. + */ + const val SQRT_OPERATION: String = "sqrt" } } +/** + * Raises [arg] to the power [pow]. + */ infix fun >> T.pow(power: Double): T = context.power(this, power) + +/** + * Computes the square root of the value [arg]. + */ fun >> sqrt(arg: T): T = arg pow 0.5 + +/** + * Computes the square of the value [arg]. + */ fun >> sqr(arg: T): T = arg pow 2.0 -/* Exponential */ - +/** + * A container for operations related to `exp` and `ln` functions. + */ interface ExponentialOperations : Algebra { + /** + * Computes Euler's number `e` raised to the power of the value [arg]. + */ fun exp(arg: T): T + + /** + * Computes the natural logarithm (base `e`) of the value [arg]. + */ fun ln(arg: T): T companion object { - const val EXP_OPERATION = "exp" - const val LN_OPERATION = "ln" + /** + * The identifier of exponential function. + */ + const val EXP_OPERATION: String = "exp" + + /** + * The identifier of natural logarithm. + */ + const val LN_OPERATION: String = "ln" } } +/** + * The identifier of exponential function. + */ fun >> exp(arg: T): T = arg.context.exp(arg) + +/** + * The identifier of natural logarithm. + */ fun >> ln(arg: T): T = arg.context.ln(arg) +/** + * A container for norm functional on element. + */ interface Norm { + /** + * Computes the norm of [arg] (i.e. absolute value or vector length). + */ fun norm(arg: T): R } +/** + * Computes the norm of [arg] (i.e. absolute value or vector length). + */ fun >, R> norm(arg: T): R = arg.context.norm(arg) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BoxingNDField.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BoxingNDField.kt index e6d4b226d..516f9a8cd 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BoxingNDField.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BoxingNDField.kt @@ -19,10 +19,10 @@ class BoxingNDField>( if (!elements.all { it.strides == this.strides }) error("Element strides are not the same as context strides") } - override val zero by lazy { produce { zero } } - override val one by lazy { produce { one } } + override val zero: BufferedNDFieldElement by lazy { produce { zero } } + override val one: BufferedNDFieldElement by lazy { produce { one } } - override fun produce(initializer: F.(IntArray) -> T) = + override fun produce(initializer: F.(IntArray) -> T): BufferedNDFieldElement = BufferedNDFieldElement( this, buildBuffer(strides.linearSize) { offset -> elementContext.initializer(strides.index(offset)) }) @@ -79,4 +79,4 @@ inline fun , R> F.nd( ): R { val ndfield: BoxingNDField = NDField.boxing(this, *shape, bufferFactory = bufferFactory) return ndfield.action() -} \ No newline at end of file +} diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BoxingNDRing.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BoxingNDRing.kt index 39fc555e8..f7be95736 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BoxingNDRing.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BoxingNDRing.kt @@ -18,10 +18,10 @@ class BoxingNDRing>( if (!elements.all { it.strides == this.strides }) error("Element strides are not the same as context strides") } - override val zero by lazy { produce { zero } } - override val one by lazy { produce { one } } + override val zero: BufferedNDRingElement by lazy { produce { zero } } + override val one: BufferedNDRingElement by lazy { produce { one } } - override fun produce(initializer: R.(IntArray) -> T) = + override fun produce(initializer: R.(IntArray) -> T): BufferedNDRingElement = BufferedNDRingElement( this, buildBuffer(strides.linearSize) { offset -> elementContext.initializer(strides.index(offset)) }) @@ -69,4 +69,4 @@ class BoxingNDRing>( override fun NDBuffer.toElement(): RingElement, *, out BufferedNDRing> = BufferedNDRingElement(this@BoxingNDRing, buffer) -} \ No newline at end of file +} diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BufferAccessor2D.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BufferAccessor2D.kt index b14da5d99..00832b69c 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BufferAccessor2D.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BufferAccessor2D.kt @@ -7,16 +7,16 @@ import kotlin.reflect.KClass */ class BufferAccessor2D(val type: KClass, val rowNum: Int, val colNum: Int) { - operator fun Buffer.get(i: Int, j: Int) = get(i + colNum * j) + operator fun Buffer.get(i: Int, j: Int): T = get(i + colNum * j) operator fun MutableBuffer.set(i: Int, j: Int, value: T) { set(i + colNum * j, value) } - inline fun create(init: (i: Int, j: Int) -> T) = + inline fun create(init: (i: Int, j: Int) -> T): MutableBuffer = MutableBuffer.auto(type, rowNum * colNum) { offset -> init(offset / colNum, offset % colNum) } - fun create(mat: Structure2D) = create { i, j -> mat[i, j] } + fun create(mat: Structure2D): MutableBuffer = create { i, j -> mat[i, j] } //TODO optimize wrapper fun MutableBuffer.collect(): Structure2D = @@ -41,5 +41,5 @@ class BufferAccessor2D(val type: KClass, val rowNum: Int, val colNum /** * Get row */ - fun MutableBuffer.row(i: Int) = Row(this, i) + fun MutableBuffer.row(i: Int): Row = Row(this, i) } diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BufferedNDAlgebra.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BufferedNDAlgebra.kt index 9742f3662..baf1a05a6 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BufferedNDAlgebra.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BufferedNDAlgebra.kt @@ -2,7 +2,7 @@ package scientifik.kmath.structures import scientifik.kmath.operations.* -interface BufferedNDAlgebra: NDAlgebra>{ +interface BufferedNDAlgebra : NDAlgebra> { val strides: Strides override fun check(vararg elements: NDBuffer) { @@ -30,7 +30,7 @@ interface BufferedNDAlgebra: NDAlgebra>{ } -interface BufferedNDSpace> : NDSpace>, BufferedNDAlgebra { +interface BufferedNDSpace> : NDSpace>, BufferedNDAlgebra { override fun NDBuffer.toElement(): SpaceElement, *, out BufferedNDSpace> } diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BufferedNDElement.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BufferedNDElement.kt index be80f66bf..d1d622b23 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BufferedNDElement.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BufferedNDElement.kt @@ -8,7 +8,7 @@ import scientifik.kmath.operations.* abstract class BufferedNDElement : NDBuffer(), NDElement> { abstract override val context: BufferedNDAlgebra - override val strides get() = context.strides + override val strides: Strides get() = context.strides override val shape: IntArray get() = context.shape } @@ -54,9 +54,9 @@ class BufferedNDFieldElement>( /** - * Element by element application of any operation on elements to the whole array. Just like in numpy + * Element by element application of any operation on elements to the whole array. Just like in numpy. */ -operator fun > Function1.invoke(ndElement: BufferedNDElement) = +operator fun > Function1.invoke(ndElement: BufferedNDElement): MathElement> = ndElement.context.run { map(ndElement) { invoke(it) }.toElement() } /* plus and minus */ @@ -64,13 +64,13 @@ operator fun > Function1.invoke(ndElement: BufferedN /** * Summation operation for [BufferedNDElement] and single element */ -operator fun > BufferedNDElement.plus(arg: T) = +operator fun > BufferedNDElement.plus(arg: T): NDElement> = context.map(this) { it + arg }.wrap() /** * Subtraction operation between [BufferedNDElement] and single element */ -operator fun > BufferedNDElement.minus(arg: T) = +operator fun > BufferedNDElement.minus(arg: T): NDElement> = context.map(this) { it - arg }.wrap() /* prod and div */ @@ -78,11 +78,11 @@ operator fun > BufferedNDElement.minus(arg: T) = /** * Product operation for [BufferedNDElement] and single element */ -operator fun > BufferedNDElement.times(arg: T) = +operator fun > BufferedNDElement.times(arg: T): NDElement> = context.map(this) { it * arg }.wrap() /** * Division operation between [BufferedNDElement] and single element */ -operator fun > BufferedNDElement.div(arg: T) = - context.map(this) { it / arg }.wrap() \ No newline at end of file +operator fun > BufferedNDElement.div(arg: T): NDElement> = + context.map(this) { it / arg }.wrap() 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 5789de9ee..723348e3f 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/Buffers.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/Buffers.kt @@ -15,22 +15,22 @@ typealias MutableBufferFactory = (Int, (Int) -> T) -> MutableBuffer interface Buffer { /** - * The size of the buffer + * The size of this buffer. */ val size: Int /** - * Get element at given index + * Gets element at given index. */ operator fun get(index: Int): T /** - * Iterate over all elements + * Iterates over all elements. */ operator fun iterator(): Iterator /** - * Check content eqiality with another buffer + * Checks content equality with another buffer. */ fun contentEquals(other: Buffer<*>): Boolean = asSequence().mapIndexed { index, value -> value == other[index] }.all { it } @@ -124,10 +124,9 @@ inline class ListBuffer(val list: List) : Buffer { override fun iterator(): Iterator = list.iterator() } -fun List.asBuffer() = ListBuffer(this) +fun List.asBuffer(): ListBuffer = ListBuffer(this) -@Suppress("FunctionName") -inline fun ListBuffer(size: Int, init: (Int) -> T) = List(size, init).asBuffer() +inline fun ListBuffer(size: Int, init: (Int) -> T): ListBuffer = List(size, init).asBuffer() inline class MutableListBuffer(val list: MutableList) : MutableBuffer { @@ -165,13 +164,13 @@ fun Array.asBuffer(): ArrayBuffer = ArrayBuffer(this) inline class ReadOnlyBuffer(val buffer: MutableBuffer) : Buffer { override val size: Int get() = buffer.size - override fun get(index: Int): T = buffer.get(index) + override fun get(index: Int): T = buffer[index] - override fun iterator() = buffer.iterator() + override fun iterator(): Iterator = buffer.iterator() } /** - * A buffer with content calculated on-demand. The calculated contect is not stored, so it is recalculated on each call. + * A buffer with content calculated on-demand. The calculated content is not stored, so it is recalculated on each call. * Useful when one needs single element from the buffer. */ class VirtualBuffer(override val size: Int, private val generator: (Int) -> T) : Buffer { @@ -205,4 +204,4 @@ fun Buffer.asReadOnly(): Buffer = if (this is MutableBuffer) { */ typealias BufferTransform = (Buffer) -> Buffer -typealias SuspendBufferTransform = suspend (Buffer) -> Buffer \ No newline at end of file +typealias SuspendBufferTransform = suspend (Buffer) -> Buffer diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ComplexNDField.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ComplexNDField.kt index c7e672c28..2f7d20b6d 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ComplexNDField.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ComplexNDField.kt @@ -17,8 +17,8 @@ class ComplexNDField(override val shape: IntArray) : override val strides: Strides = DefaultStrides(shape) override val elementContext: ComplexField get() = ComplexField - override val zero by lazy { produce { zero } } - override val one by lazy { produce { one } } + override val zero: ComplexNDElement by lazy { produce { zero } } + override val one: ComplexNDElement by lazy { produce { one } } inline fun buildBuffer(size: Int, crossinline initializer: (Int) -> Complex): Buffer = Buffer.complex(size) { initializer(it) } @@ -69,23 +69,23 @@ class ComplexNDField(override val shape: IntArray) : override fun NDBuffer.toElement(): FieldElement, *, out BufferedNDField> = BufferedNDFieldElement(this@ComplexNDField, buffer) - override fun power(arg: NDBuffer, pow: Number) = map(arg) { power(it, pow) } + override fun power(arg: NDBuffer, pow: Number): ComplexNDElement = map(arg) { power(it, pow) } - override fun exp(arg: NDBuffer) = map(arg) { exp(it) } + override fun exp(arg: NDBuffer): ComplexNDElement = map(arg) { exp(it) } - override fun ln(arg: NDBuffer) = map(arg) { ln(it) } + override fun ln(arg: NDBuffer): ComplexNDElement = map(arg) { ln(it) } - override fun sin(arg: NDBuffer) = map(arg) { sin(it) } + override fun sin(arg: NDBuffer): ComplexNDElement = map(arg) { sin(it) } - override fun cos(arg: NDBuffer) = map(arg) { cos(it) } + override fun cos(arg: NDBuffer): ComplexNDElement = map(arg) { cos(it) } - override fun tan(arg: NDBuffer): NDBuffer = map(arg) { tan(it) } + override fun tan(arg: NDBuffer): ComplexNDElement = map(arg) { tan(it) } - override fun asin(arg: NDBuffer): NDBuffer = map(arg) { asin(it) } + override fun asin(arg: NDBuffer): ComplexNDElement = map(arg) { asin(it) } - override fun acos(arg: NDBuffer): NDBuffer = map(arg) {acos(it)} + override fun acos(arg: NDBuffer): ComplexNDElement = map(arg) { acos(it) } - override fun atan(arg: NDBuffer): NDBuffer = map(arg) {atan(it)} + override fun atan(arg: NDBuffer): ComplexNDElement = map(arg) { atan(it) } } @@ -100,7 +100,7 @@ inline fun BufferedNDField.produceInline(crossinline init /** * Map one [ComplexNDElement] using function with indexes */ -inline fun ComplexNDElement.mapIndexed(crossinline transform: ComplexField.(index: IntArray, Complex) -> Complex) = +inline fun ComplexNDElement.mapIndexed(crossinline transform: ComplexField.(index: IntArray, Complex) -> Complex): ComplexNDElement = context.produceInline { offset -> transform(strides.index(offset), buffer[offset]) } /** @@ -114,7 +114,7 @@ inline fun ComplexNDElement.map(crossinline transform: ComplexField.(Complex) -> /** * Element by element application of any operation on elements to the whole array. Just like in numpy */ -operator fun Function1.invoke(ndElement: ComplexNDElement) = +operator fun Function1.invoke(ndElement: ComplexNDElement): ComplexNDElement = ndElement.map { this@invoke(it) } @@ -123,19 +123,18 @@ operator fun Function1.invoke(ndElement: ComplexNDElement) = /** * Summation operation for [BufferedNDElement] and single element */ -operator fun ComplexNDElement.plus(arg: Complex) = - map { it + arg } +operator fun ComplexNDElement.plus(arg: Complex): ComplexNDElement = map { it + arg } /** * Subtraction operation between [BufferedNDElement] and single element */ -operator fun ComplexNDElement.minus(arg: Complex) = +operator fun ComplexNDElement.minus(arg: Complex): ComplexNDElement = map { it - arg } -operator fun ComplexNDElement.plus(arg: Double) = +operator fun ComplexNDElement.plus(arg: Double): ComplexNDElement = map { it + arg } -operator fun ComplexNDElement.minus(arg: Double) = +operator fun ComplexNDElement.minus(arg: Double): ComplexNDElement = map { it - arg } fun NDField.Companion.complex(vararg shape: Int): ComplexNDField = ComplexNDField(shape) @@ -148,4 +147,4 @@ fun NDElement.Companion.complex(vararg shape: Int, initializer: ComplexField.(In */ inline fun ComplexField.nd(vararg shape: Int, action: ComplexNDField.() -> R): R { return NDField.complex(*shape).run(action) -} \ No newline at end of file +} 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 776cff880..1fe449632 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ExtendedNDField.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ExtendedNDField.kt @@ -4,7 +4,6 @@ import scientifik.kmath.operations.ExtendedField interface ExtendedNDField, N : NDStructure> : NDField, ExtendedField - ///** // * NDField that supports [ExtendedField] operations on its elements // */ @@ -36,5 +35,3 @@ interface ExtendedNDField, N : NDStructure> : N // return produce { with(elementContext) { cos(arg[it]) } } // } //} - - diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/FlaggedBuffer.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/FlaggedBuffer.kt index 749e4eeec..2382e2c57 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/FlaggedBuffer.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/FlaggedBuffer.kt @@ -19,11 +19,11 @@ interface FlaggedBuffer : Buffer { /** * The value is valid if all flags are down */ -fun FlaggedBuffer<*>.isValid(index: Int) = getFlag(index) != 0.toByte() +fun FlaggedBuffer<*>.isValid(index: Int): Boolean = getFlag(index) != 0.toByte() -fun FlaggedBuffer<*>.hasFlag(index: Int, flag: ValueFlag) = (getFlag(index) and flag.mask) != 0.toByte() +fun FlaggedBuffer<*>.hasFlag(index: Int, flag: ValueFlag): Boolean = (getFlag(index) and flag.mask) != 0.toByte() -fun FlaggedBuffer<*>.isMissing(index: Int) = hasFlag(index, ValueFlag.MISSING) +fun FlaggedBuffer<*>.isMissing(index: Int): Boolean = hasFlag(index, ValueFlag.MISSING) /** * A real buffer which supports flags for each value like NaN or Missing @@ -45,9 +45,9 @@ class FlaggedRealBuffer(val values: DoubleArray, val flags: ByteArray) : Flagged } inline fun FlaggedRealBuffer.forEachValid(block: (Double) -> Unit) { - for(i in indices){ - if(isValid(i)){ + for (i in indices) { + if (isValid(i)) { block(values[i]) } } -} \ No newline at end of file +} diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/IntBuffer.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/IntBuffer.kt index a354c5de0..b277374ab 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/IntBuffer.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/IntBuffer.kt @@ -9,12 +9,11 @@ inline class IntBuffer(val array: IntArray) : MutableBuffer { array[index] = value } - override fun iterator() = array.iterator() + override fun iterator(): IntIterator = array.iterator() override fun copy(): MutableBuffer = IntBuffer(array.copyOf()) } - -fun IntArray.asBuffer() = IntBuffer(this) \ No newline at end of file +fun IntArray.asBuffer(): IntBuffer = IntBuffer(this) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/LongBuffer.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/LongBuffer.kt index fa6229a71..3ee2e75a2 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/LongBuffer.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/LongBuffer.kt @@ -9,11 +9,11 @@ inline class LongBuffer(val array: LongArray) : MutableBuffer { array[index] = value } - override fun iterator() = array.iterator() + override fun iterator(): LongIterator = array.iterator() override fun copy(): MutableBuffer = LongBuffer(array.copyOf()) } -fun LongArray.asBuffer() = LongBuffer(this) \ No newline at end of file +fun LongArray.asBuffer(): LongBuffer = LongBuffer(this) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/MemoryBuffer.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/MemoryBuffer.kt index a09f09165..b15487114 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/MemoryBuffer.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/MemoryBuffer.kt @@ -3,7 +3,7 @@ package scientifik.kmath.structures import scientifik.memory.* /** - * A non-boxing buffer based on [ByteBuffer] storage + * A non-boxing buffer over [Memory] object. */ open class MemoryBuffer(protected val memory: Memory, protected val spec: MemorySpec) : Buffer { @@ -17,7 +17,7 @@ open class MemoryBuffer(protected val memory: Memory, protected val spe companion object { - fun create(spec: MemorySpec, size: Int) = + fun create(spec: MemorySpec, size: Int): MemoryBuffer = MemoryBuffer(Memory.allocate(size * spec.objectSize), spec) inline fun create( @@ -36,25 +36,25 @@ open class MemoryBuffer(protected val memory: Memory, protected val spe class MutableMemoryBuffer(memory: Memory, spec: MemorySpec) : MemoryBuffer(memory, spec), MutableBuffer { - private val writer = memory.writer() + private val writer: MemoryWriter = memory.writer() - override fun set(index: Int, value: T) = writer.write(spec, spec.objectSize * index, value) + override fun set(index: Int, value: T): Unit = writer.write(spec, spec.objectSize * index, value) override fun copy(): MutableBuffer = MutableMemoryBuffer(memory.copy(), spec) companion object { - fun create(spec: MemorySpec, size: Int) = + fun create(spec: MemorySpec, size: Int): MutableMemoryBuffer = MutableMemoryBuffer(Memory.allocate(size * spec.objectSize), spec) inline fun create( spec: MemorySpec, size: Int, crossinline initializer: (Int) -> T - ) = + ): MutableMemoryBuffer = MutableMemoryBuffer(Memory.allocate(size * spec.objectSize), spec).also { buffer -> (0 until size).forEach { buffer[it] = initializer(it) } } } -} \ No newline at end of file +} diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/NDAlgebra.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/NDAlgebra.kt index c826565cf..750133351 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/NDAlgebra.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/NDAlgebra.kt @@ -56,7 +56,7 @@ interface NDAlgebra> { /** * element-by-element invoke a function working on [T] on a [NDStructure] */ - operator fun Function1.invoke(structure: N) = map(structure) { value -> this@invoke(value) } + operator fun Function1.invoke(structure: N): N = map(structure) { value -> this@invoke(value) } companion object } @@ -76,12 +76,12 @@ interface NDSpace, N : NDStructure> : Space, NDAlgebra add(arg, value) } + operator fun N.plus(arg: T): N = map(this) { value -> add(arg, value) } - operator fun N.minus(arg: T) = map(this) { value -> add(arg, -value) } + operator fun N.minus(arg: T): N = map(this) { value -> add(arg, -value) } - operator fun T.plus(arg: N) = map(arg) { value -> add(this@plus, value) } - operator fun T.minus(arg: N) = map(arg) { value -> add(-this@minus, value) } + operator fun T.plus(arg: N): N = map(arg) { value -> add(this@plus, value) } + operator fun T.minus(arg: N): N = map(arg) { value -> add(-this@minus, value) } companion object } @@ -97,20 +97,18 @@ interface NDRing, N : NDStructure> : Ring, NDSpace override fun multiply(a: N, b: N): N = combine(a, b) { aValue, bValue -> multiply(aValue, bValue) } //TODO move to extensions after KEEP-176 - operator fun N.times(arg: T) = map(this) { value -> multiply(arg, value) } + operator fun N.times(arg: T): N = map(this) { value -> multiply(arg, value) } - operator fun T.times(arg: N) = map(arg) { value -> multiply(this@times, value) } + operator fun T.times(arg: N): N = map(arg) { value -> multiply(this@times, value) } companion object } /** * Field for n-dimensional structures. - * @param shape - the list of dimensions of the array - * @param elementField - operations field defined on individual array element + * * @param T - the type of the element contained in ND structure * @param F - field of structure elements - * @param R - actual nd-element type of this field */ interface NDField, N : NDStructure> : Field, NDRing { @@ -120,9 +118,9 @@ interface NDField, N : NDStructure> : Field, NDRing divide(aValue, bValue) } //TODO move to extensions after KEEP-176 - operator fun N.div(arg: T) = map(this) { value -> divide(arg, value) } + operator fun N.div(arg: T): N = map(this) { value -> divide(arg, value) } - operator fun T.div(arg: N) = map(arg) { divide(it, this@div) } + operator fun T.div(arg: N): N = map(arg) { divide(it, this@div) } companion object { @@ -131,7 +129,7 @@ interface NDField, N : NDStructure> : Field, NDRing, N : NDStructure> : Field, NDRing = Buffer.Companion::boxing - ) = BoxingNDField(shape, field, bufferFactory) + ): BoxingNDField = BoxingNDField(shape, field, bufferFactory) /** * Create a most suitable implementation for nd-field using reified class. 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 a18a03364..9dfe2b5a8 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/NDElement.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/NDElement.kt @@ -23,19 +23,23 @@ interface NDElement> : NDStructure { /** * Create a optimized NDArray of doubles */ - fun real(shape: IntArray, initializer: RealField.(IntArray) -> Double = { 0.0 }) = + fun real(shape: IntArray, initializer: RealField.(IntArray) -> Double = { 0.0 }): RealNDElement = NDField.real(*shape).produce(initializer) - fun real1D(dim: Int, initializer: (Int) -> Double = { _ -> 0.0 }) = + fun real1D(dim: Int, initializer: (Int) -> Double = { _ -> 0.0 }): RealNDElement = real(intArrayOf(dim)) { initializer(it[0]) } - fun real2D(dim1: Int, dim2: Int, initializer: (Int, Int) -> Double = { _, _ -> 0.0 }) = + fun real2D(dim1: Int, dim2: Int, initializer: (Int, Int) -> Double = { _, _ -> 0.0 }): RealNDElement = 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]) } + fun real3D( + dim1: Int, + dim2: Int, + dim3: Int, + initializer: (Int, Int, Int) -> Double = { _, _, _ -> 0.0 } + ): RealNDElement = real(intArrayOf(dim1, dim2, dim3)) { initializer(it[0], it[1], it[2]) } /** @@ -62,16 +66,17 @@ interface NDElement> : NDStructure { } -fun > NDElement.mapIndexed(transform: C.(index: IntArray, T) -> T) = +fun > NDElement.mapIndexed(transform: C.(index: IntArray, T) -> T): NDElement = context.mapIndexed(unwrap(), transform).wrap() -fun > NDElement.map(transform: C.(T) -> T) = context.map(unwrap(), transform).wrap() +fun > NDElement.map(transform: C.(T) -> T): NDElement = + context.map(unwrap(), transform).wrap() /** * Element by element application of any operation on elements to the whole [NDElement] */ -operator fun > Function1.invoke(ndElement: NDElement) = +operator fun > Function1.invoke(ndElement: NDElement): NDElement = ndElement.map { value -> this@invoke(value) } /* plus and minus */ @@ -79,13 +84,13 @@ operator fun > Function1.invoke(ndElement: NDElem /** * Summation operation for [NDElement] and single element */ -operator fun , N : NDStructure> NDElement.plus(arg: T) = +operator fun , N : NDStructure> NDElement.plus(arg: T): NDElement = map { value -> arg + value } /** * Subtraction operation between [NDElement] and single element */ -operator fun , N : NDStructure> NDElement.minus(arg: T) = +operator fun , N : NDStructure> NDElement.minus(arg: T): NDElement = map { value -> arg - value } /* prod and div */ @@ -93,13 +98,13 @@ operator fun , N : NDStructure> NDElement.minus(arg: /** * Product operation for [NDElement] and single element */ -operator fun , N : NDStructure> NDElement.times(arg: T) = +operator fun , N : NDStructure> NDElement.times(arg: T): NDElement = map { value -> arg * value } /** * Division operation between [NDElement] and single element */ -operator fun , N : NDStructure> NDElement.div(arg: T) = +operator fun , N : NDStructure> NDElement.div(arg: T): NDElement = map { value -> arg / value } 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 236a6f366..bcd74ed2a 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/NDStructure.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/NDStructure.kt @@ -8,7 +8,7 @@ interface NDStructure { val shape: IntArray - val dimension get() = shape.size + val dimension: Int get() = shape.size operator fun get(index: IntArray): T @@ -44,32 +44,49 @@ interface NDStructure { strides: Strides, bufferFactory: BufferFactory = Buffer.Companion::boxing, initializer: (IntArray) -> T - ) = + ): BufferNDStructure = BufferNDStructure(strides, bufferFactory(strides.linearSize) { i -> initializer(strides.index(i)) }) /** * Inline create NDStructure with non-boxing buffer implementation if it is possible */ - inline fun auto(strides: Strides, crossinline initializer: (IntArray) -> T) = + inline fun auto( + strides: Strides, + crossinline initializer: (IntArray) -> T + ): BufferNDStructure = BufferNDStructure(strides, Buffer.auto(strides.linearSize) { i -> initializer(strides.index(i)) }) - inline fun auto(type: KClass, strides: Strides, crossinline initializer: (IntArray) -> T) = + inline fun auto( + type: KClass, + strides: Strides, + crossinline initializer: (IntArray) -> T + ): BufferNDStructure = BufferNDStructure(strides, Buffer.auto(type, strides.linearSize) { i -> initializer(strides.index(i)) }) fun build( shape: IntArray, bufferFactory: BufferFactory = Buffer.Companion::boxing, initializer: (IntArray) -> T - ) = build(DefaultStrides(shape), bufferFactory, initializer) + ): BufferNDStructure = build(DefaultStrides(shape), bufferFactory, initializer) - inline fun auto(shape: IntArray, crossinline initializer: (IntArray) -> T) = + inline fun auto( + shape: IntArray, + crossinline initializer: (IntArray) -> T + ): BufferNDStructure = auto(DefaultStrides(shape), initializer) @JvmName("autoVarArg") - inline fun auto(vararg shape: Int, crossinline initializer: (IntArray) -> T) = + inline fun auto( + vararg shape: Int, + crossinline initializer: (IntArray) -> T + ): BufferNDStructure = auto(DefaultStrides(shape), initializer) - inline fun auto(type: KClass, vararg shape: Int, crossinline initializer: (IntArray) -> T) = + inline fun auto( + type: KClass, + vararg shape: Int, + crossinline initializer: (IntArray) -> T + ): BufferNDStructure = auto(type, DefaultStrides(shape), initializer) } } @@ -128,7 +145,7 @@ class DefaultStrides private constructor(override val shape: IntArray) : Strides /** * Strides for memory access */ - override val strides by lazy { + override val strides: List by lazy { sequence { var current = 1 yield(1) @@ -238,7 +255,7 @@ inline fun NDStructure.mapToBuffer( } /** - * Mutable ND buffer based on linear [autoBuffer] + * Mutable ND buffer based on linear [MutableBuffer]. */ class MutableBufferNDStructure( override val strides: Strides, @@ -251,7 +268,7 @@ class MutableBufferNDStructure( } } - override fun set(index: IntArray, value: T) = buffer.set(strides.offset(index), value) + override fun set(index: IntArray, value: T): Unit = buffer.set(strides.offset(index), value) } inline fun NDStructure.combine( @@ -260,4 +277,4 @@ inline fun NDStructure.combine( ): NDStructure { if (!this.shape.contentEquals(struct.shape)) error("Shape mismatch in structure combination") return NDStructure.auto(shape) { block(this[it], struct[it]) } -} \ No newline at end of file +} diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/RealBuffer.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/RealBuffer.kt index f48ace3a9..20187fe19 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/RealBuffer.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/RealBuffer.kt @@ -9,7 +9,7 @@ inline class RealBuffer(val array: DoubleArray) : MutableBuffer { array[index] = value } - override fun iterator() = array.iterator() + override fun iterator(): DoubleIterator = array.iterator() override fun copy(): MutableBuffer = RealBuffer(array.copyOf()) @@ -31,4 +31,4 @@ val MutableBuffer.array: DoubleArray DoubleArray(size) { get(it) } } -fun DoubleArray.asBuffer() = RealBuffer(this) \ No newline at end of file +fun DoubleArray.asBuffer(): RealBuffer = RealBuffer(this) 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 8c90f90c7..b89f28233 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/RealNDField.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/RealNDField.kt @@ -12,8 +12,8 @@ class RealNDField(override val shape: IntArray) : override val strides: Strides = DefaultStrides(shape) override val elementContext: RealField get() = RealField - override val zero by lazy { produce { zero } } - override val one by lazy { produce { one } } + override val zero: RealNDElement by lazy { produce { zero } } + override val one: RealNDElement by lazy { produce { one } } inline fun buildBuffer(size: Int, crossinline initializer: (Int) -> Double): Buffer = RealBuffer(DoubleArray(size) { initializer(it) }) @@ -64,15 +64,15 @@ class RealNDField(override val shape: IntArray) : override fun NDBuffer.toElement(): FieldElement, *, out BufferedNDField> = BufferedNDFieldElement(this@RealNDField, buffer) - override fun power(arg: NDBuffer, pow: Number) = map(arg) { power(it, pow) } + override fun power(arg: NDBuffer, pow: Number): RealNDElement = map(arg) { power(it, pow) } - override fun exp(arg: NDBuffer) = map(arg) { exp(it) } + override fun exp(arg: NDBuffer): RealNDElement = map(arg) { exp(it) } - override fun ln(arg: NDBuffer) = map(arg) { ln(it) } + override fun ln(arg: NDBuffer): RealNDElement = map(arg) { ln(it) } - override fun sin(arg: NDBuffer) = map(arg) { sin(it) } + override fun sin(arg: NDBuffer): RealNDElement = map(arg) { sin(it) } - override fun cos(arg: NDBuffer) = map(arg) { cos(it) } + override fun cos(arg: NDBuffer): RealNDElement = map(arg) { cos(it) } override fun tan(arg: NDBuffer): NDBuffer = map(arg) { tan(it) } @@ -95,7 +95,7 @@ inline fun BufferedNDField.produceInline(crossinline initiali /** * Map one [RealNDElement] using function with indexes */ -inline fun RealNDElement.mapIndexed(crossinline transform: RealField.(index: IntArray, Double) -> Double) = +inline fun RealNDElement.mapIndexed(crossinline transform: RealField.(index: IntArray, Double) -> Double): RealNDElement = context.produceInline { offset -> transform(strides.index(offset), buffer[offset]) } /** @@ -107,9 +107,9 @@ inline fun RealNDElement.map(crossinline transform: RealField.(Double) -> Double } /** - * Element by element application of any operation on elements to the whole array. Just like in numpy + * Element by element application of any operation on elements to the whole array. Just like in numpy. */ -operator fun Function1.invoke(ndElement: RealNDElement) = +operator fun Function1.invoke(ndElement: RealNDElement): RealNDElement = ndElement.map { this@invoke(it) } @@ -118,13 +118,13 @@ operator fun Function1.invoke(ndElement: RealNDElement) = /** * Summation operation for [BufferedNDElement] and single element */ -operator fun RealNDElement.plus(arg: Double) = +operator fun RealNDElement.plus(arg: Double): RealNDElement = map { it + arg } /** * Subtraction operation between [BufferedNDElement] and single element */ -operator fun RealNDElement.minus(arg: Double) = +operator fun RealNDElement.minus(arg: Double): RealNDElement = map { it - arg } /** @@ -132,4 +132,4 @@ operator fun RealNDElement.minus(arg: Double) = */ inline fun RealField.nd(vararg shape: Int, action: RealNDField.() -> R): R { return NDField.real(*shape).run(action) -} \ No newline at end of file +} diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ShortBuffer.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ShortBuffer.kt index f4b2f7d13..24c94a885 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ShortBuffer.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ShortBuffer.kt @@ -9,12 +9,11 @@ inline class ShortBuffer(val array: ShortArray) : MutableBuffer { array[index] = value } - override fun iterator() = array.iterator() + override fun iterator(): ShortIterator = array.iterator() override fun copy(): MutableBuffer = ShortBuffer(array.copyOf()) } - -fun ShortArray.asBuffer() = ShortBuffer(this) \ No newline at end of file +fun ShortArray.asBuffer(): ShortBuffer = ShortBuffer(this) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ShortNDRing.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ShortNDRing.kt index 6b09c91de..f404a2a27 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ShortNDRing.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ShortNDRing.kt @@ -12,8 +12,8 @@ class ShortNDRing(override val shape: IntArray) : override val strides: Strides = DefaultStrides(shape) override val elementContext: ShortRing get() = ShortRing - override val zero by lazy { produce { ShortRing.zero } } - override val one by lazy { produce { ShortRing.one } } + override val zero: ShortNDElement by lazy { produce { zero } } + override val one: ShortNDElement by lazy { produce { one } } inline fun buildBuffer(size: Int, crossinline initializer: (Int) -> Short): Buffer = ShortBuffer(ShortArray(size) { initializer(it) }) @@ -40,6 +40,7 @@ class ShortNDRing(override val shape: IntArray) : transform: ShortRing.(index: IntArray, Short) -> Short ): ShortNDElement { check(arg) + return BufferedNDRingElement( this, buildBuffer(arg.strides.linearSize) { offset -> @@ -67,7 +68,7 @@ class ShortNDRing(override val shape: IntArray) : /** - * Fast element production using function inlining + * Fast element production using function inlining. */ inline fun BufferedNDRing.produceInline(crossinline initializer: ShortRing.(Int) -> Short): ShortNDElement { val array = ShortArray(strides.linearSize) { offset -> ShortRing.initializer(offset) } @@ -75,22 +76,22 @@ inline fun BufferedNDRing.produceInline(crossinline initialize } /** - * Element by element application of any operation on elements to the whole array. Just like in numpy + * Element by element application of any operation on elements to the whole array. */ -operator fun Function1.invoke(ndElement: ShortNDElement) = +operator fun Function1.invoke(ndElement: ShortNDElement): ShortNDElement = ndElement.context.produceInline { i -> invoke(ndElement.buffer[i]) } /* plus and minus */ /** - * Summation operation for [StridedNDFieldElement] and single element + * Summation operation for [ShortNDElement] and single element. */ -operator fun ShortNDElement.plus(arg: Short) = +operator fun ShortNDElement.plus(arg: Short): ShortNDElement = context.produceInline { i -> (buffer[i] + arg).toShort() } /** - * Subtraction operation between [StridedNDFieldElement] and single element + * Subtraction operation between [ShortNDElement] and single element. */ -operator fun ShortNDElement.minus(arg: Short) = - context.produceInline { i -> (buffer[i] - arg).toShort() } \ No newline at end of file +operator fun ShortNDElement.minus(arg: Short): ShortNDElement = + context.produceInline { i -> (buffer[i] - arg).toShort() } diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/Structure1D.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/Structure1D.kt index 9974538ec..faf022367 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/Structure1D.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/Structure1D.kt @@ -17,7 +17,7 @@ interface Structure1D : NDStructure, Buffer { /** * A 1D wrapper for nd-structure */ -private inline class Structure1DWrapper(val structure: NDStructure) : Structure1D{ +private inline class Structure1DWrapper(val structure: NDStructure) : Structure1D { override val shape: IntArray get() = structure.shape override val size: Int get() = structure.shape[0] @@ -39,14 +39,14 @@ private inline class Buffer1DWrapper(val buffer: Buffer) : Structure1D override fun elements(): Sequence> = asSequence().mapIndexed { index, value -> intArrayOf(index) to value } - override fun get(index: Int): T = buffer.get(index) + override fun get(index: Int): T = buffer[index] } /** * Represent a [NDStructure] as [Structure1D]. Throw error in case of dimension mismatch */ fun NDStructure.as1D(): Structure1D = if (shape.size == 1) { - if( this is NDBuffer){ + if (this is NDBuffer) { Buffer1DWrapper(this.buffer) } else { Structure1DWrapper(this) @@ -59,4 +59,4 @@ fun NDStructure.as1D(): Structure1D = if (shape.size == 1) { /** * Represent this buffer as 1D structure */ -fun Buffer.asND(): Structure1D = Buffer1DWrapper(this) \ No newline at end of file +fun Buffer.asND(): Structure1D = Buffer1DWrapper(this) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/Structure2D.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/Structure2D.kt index bfaf8338d..30fd556d3 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/Structure2D.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/Structure2D.kt @@ -32,9 +32,7 @@ interface Structure2D : NDStructure { } } - companion object { - - } + companion object } /** @@ -57,4 +55,4 @@ fun NDStructure.as2D(): Structure2D = if (shape.size == 2) { error("Can't create 2d-structure from ${shape.size}d-structure") } -typealias Matrix = Structure2D \ No newline at end of file +typealias Matrix = Structure2D diff --git a/kmath-core/src/commonTest/kotlin/scientifik/kmath/expressions/ExpressionFieldTest.kt b/kmath-core/src/commonTest/kotlin/scientifik/kmath/expressions/ExpressionFieldTest.kt index 9eae60efc..22b924ef9 100644 --- a/kmath-core/src/commonTest/kotlin/scientifik/kmath/expressions/ExpressionFieldTest.kt +++ b/kmath-core/src/commonTest/kotlin/scientifik/kmath/expressions/ExpressionFieldTest.kt @@ -31,7 +31,7 @@ class ExpressionFieldTest { @Test fun separateContext() { - fun FunctionalExpressionField.expression(): Expression { + fun FunctionalExpressionField.expression(): Expression { val x = variable("x") return x * x + 2 * x + one } @@ -42,7 +42,7 @@ class ExpressionFieldTest { @Test fun valueExpression() { - val expressionBuilder: FunctionalExpressionField.() -> Expression = { + val expressionBuilder: FunctionalExpressionField.() -> Expression = { val x = variable("x") x * x + 2 * x + one } @@ -50,4 +50,4 @@ class ExpressionFieldTest { val expression = FunctionalExpressionField(RealField).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/linear/MatrixTest.kt b/kmath-core/src/commonTest/kotlin/scientifik/kmath/linear/MatrixTest.kt index dcd510e32..987426250 100644 --- a/kmath-core/src/commonTest/kotlin/scientifik/kmath/linear/MatrixTest.kt +++ b/kmath-core/src/commonTest/kotlin/scientifik/kmath/linear/MatrixTest.kt @@ -49,17 +49,17 @@ class MatrixTest { @Test fun test2DDot() { - val firstMatrix = NDStructure.auto(2,3){ (i, j) -> (i + j).toDouble() }.as2D() - val secondMatrix = NDStructure.auto(3,2){ (i, j) -> (i + j).toDouble() }.as2D() + val firstMatrix = NDStructure.auto(2, 3) { (i, j) -> (i + j).toDouble() }.as2D() + val secondMatrix = NDStructure.auto(3, 2) { (i, j) -> (i + j).toDouble() }.as2D() MatrixContext.real.run { // val firstMatrix = produce(2, 3) { i, j -> (i + j).toDouble() } // val secondMatrix = produce(3, 2) { i, j -> (i + j).toDouble() } val result = firstMatrix dot secondMatrix assertEquals(2, result.rowNum) assertEquals(2, result.colNum) - assertEquals(8.0, result[0,1]) - assertEquals(8.0, result[1,0]) - assertEquals(14.0, result[1,1]) + assertEquals(8.0, result[0, 1]) + assertEquals(8.0, result[1, 0]) + assertEquals(14.0, result[1, 1]) } } -} \ No newline at end of file +} 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 56a0b7aad..34bd8a0e3 100644 --- a/kmath-core/src/commonTest/kotlin/scientifik/kmath/linear/RealLUSolverTest.kt +++ b/kmath-core/src/commonTest/kotlin/scientifik/kmath/linear/RealLUSolverTest.kt @@ -48,4 +48,4 @@ class RealLUSolverTest { assertEquals(expected, inverted) } -} \ No newline at end of file +} diff --git a/kmath-core/src/commonTest/kotlin/scientifik/kmath/misc/AutoDiffTest.kt b/kmath-core/src/commonTest/kotlin/scientifik/kmath/misc/AutoDiffTest.kt index 9dc8f5ef7..c08a63ccb 100644 --- a/kmath-core/src/commonTest/kotlin/scientifik/kmath/misc/AutoDiffTest.kt +++ b/kmath-core/src/commonTest/kotlin/scientifik/kmath/misc/AutoDiffTest.kt @@ -8,10 +8,10 @@ import kotlin.test.assertEquals import kotlin.test.assertTrue class AutoDiffTest { + fun Variable(int: Int): Variable = Variable(int.toDouble()) - fun Variable(int: Int) = Variable(int.toDouble()) - - fun deriv(body: AutoDiffField.() -> Variable) = RealField.deriv(body) + fun deriv(body: AutoDiffField.() -> Variable): DerivationResult = + RealField.deriv(body) @Test fun testPlusX2() { @@ -178,5 +178,4 @@ class AutoDiffTest { private fun assertApprox(a: Double, b: Double) { if ((a - b) > 1e-10) assertEquals(a, b) } - -} \ No newline at end of file +} diff --git a/kmath-core/src/commonTest/kotlin/scientifik/kmath/misc/CumulativeKtTest.kt b/kmath-core/src/commonTest/kotlin/scientifik/kmath/misc/CumulativeKtTest.kt index e7c99e7d0..82ea5318f 100644 --- a/kmath-core/src/commonTest/kotlin/scientifik/kmath/misc/CumulativeKtTest.kt +++ b/kmath-core/src/commonTest/kotlin/scientifik/kmath/misc/CumulativeKtTest.kt @@ -10,4 +10,4 @@ class CumulativeKtTest { val cumulative = initial.cumulativeSum() assertEquals(listOf(-1.0, 1.0, 2.0, 3.0), cumulative) } -} \ No newline at end of file +} diff --git a/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/BigIntAlgebraTest.kt b/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/BigIntAlgebraTest.kt index 5ae977196..c22d2f27b 100644 --- a/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/BigIntAlgebraTest.kt +++ b/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/BigIntAlgebraTest.kt @@ -47,4 +47,3 @@ class BigIntAlgebraTest { } } - diff --git a/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/BigIntConstructorTest.kt b/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/BigIntConstructorTest.kt index 2af1b7e50..5e3f6d1b0 100644 --- a/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/BigIntConstructorTest.kt +++ b/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/BigIntConstructorTest.kt @@ -19,8 +19,8 @@ class BigIntConstructorTest { @Test fun testConstructor_0xffffffffaL() { - val x = -0xffffffffaL.toBigInt() + val x = (-0xffffffffaL).toBigInt() val y = uintArrayOf(0xfffffffaU, 0xfU).toBigInt(-1) assertEquals(x, y) } -} \ No newline at end of file +} diff --git a/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/BigIntConversionsTest.kt b/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/BigIntConversionsTest.kt index 51b9509e0..41df1968d 100644 --- a/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/BigIntConversionsTest.kt +++ b/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/BigIntConversionsTest.kt @@ -19,7 +19,7 @@ class BigIntConversionsTest { @Test fun testToString_0x17ead2ffffd() { - val x = -0x17ead2ffffdL.toBigInt() + val x = (-0x17ead2ffffdL).toBigInt() assertEquals("-0x17ead2ffffd", x.toString()) } @@ -40,4 +40,4 @@ class BigIntConversionsTest { val x = "-7059135710711894913860".parseBigInteger() assertEquals("-0x17ead2ffffd11223344", x.toString()) } -} \ No newline at end of file +} diff --git a/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/BigIntOperationsTest.kt b/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/BigIntOperationsTest.kt index 72ac9f229..b7f4cf43b 100644 --- a/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/BigIntOperationsTest.kt +++ b/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/BigIntOperationsTest.kt @@ -31,7 +31,7 @@ class BigIntOperationsTest { @Test fun testUnaryMinus() { val x = 1234.toBigInt() - val y = -1234.toBigInt() + val y = (-1234).toBigInt() assertEquals(-x, y) } @@ -48,18 +48,18 @@ class BigIntOperationsTest { @Test fun testMinus__2_1() { - val x = -2.toBigInt() + val x = (-2).toBigInt() val y = 1.toBigInt() val res = x - y - val sum = -3.toBigInt() + val sum = (-3).toBigInt() assertEquals(sum, res) } @Test fun testMinus___2_1() { - val x = -2.toBigInt() + val x = (-2).toBigInt() val y = 1.toBigInt() val res = -x - y @@ -74,7 +74,7 @@ class BigIntOperationsTest { val y = 0xffffffffaL.toBigInt() val res = x - y - val sum = -0xfffffcfc1L.toBigInt() + val sum = (-0xfffffcfc1L).toBigInt() assertEquals(sum, res) } @@ -92,11 +92,11 @@ class BigIntOperationsTest { @Test fun testMultiply__2_3() { - val x = -2.toBigInt() + val x = (-2).toBigInt() val y = 3.toBigInt() val res = x * y - val prod = -6.toBigInt() + val prod = (-6).toBigInt() assertEquals(prod, res) } @@ -129,7 +129,7 @@ class BigIntOperationsTest { val y = -0xfff456 val res = x * y - val prod = -0xffe579ad5dc2L.toBigInt() + val prod = (-0xffe579ad5dc2L).toBigInt() assertEquals(prod, res) } @@ -259,7 +259,7 @@ class BigIntOperationsTest { val y = -3 val res = x / y - val div = -6.toBigInt() + val div = (-6).toBigInt() assertEquals(div, res) } @@ -267,10 +267,10 @@ class BigIntOperationsTest { @Test fun testBigDivision_20__3() { val x = 20.toBigInt() - val y = -3.toBigInt() + val y = (-3).toBigInt() val res = x / y - val div = -6.toBigInt() + val div = (-6).toBigInt() assertEquals(div, res) } @@ -378,4 +378,4 @@ class BigIntOperationsTest { return assertEquals(res, x % mod) } -} \ No newline at end of file +} 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 779cfc4b8..9dfa3bdd1 100644 --- a/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/RealFieldTest.kt +++ b/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/RealFieldTest.kt @@ -11,4 +11,4 @@ class RealFieldTest { } assertEquals(5.0, sqrt) } -} \ No newline at end of file +} diff --git a/kmath-core/src/commonTest/kotlin/scientifik/kmath/structures/ComplexBufferSpecTest.kt b/kmath-core/src/commonTest/kotlin/scientifik/kmath/structures/ComplexBufferSpecTest.kt index 454683dac..cbbe6f0f4 100644 --- a/kmath-core/src/commonTest/kotlin/scientifik/kmath/structures/ComplexBufferSpecTest.kt +++ b/kmath-core/src/commonTest/kotlin/scientifik/kmath/structures/ComplexBufferSpecTest.kt @@ -11,4 +11,4 @@ class ComplexBufferSpecTest { val buffer = Buffer.complex(20) { Complex(it.toDouble(), -it.toDouble()) } assertEquals(Complex(5.0, -5.0), buffer[5]) } -} \ No newline at end of file +} diff --git a/kmath-core/src/commonTest/kotlin/scientifik/kmath/structures/NDFieldTest.kt b/kmath-core/src/commonTest/kotlin/scientifik/kmath/structures/NDFieldTest.kt index 39cce5c67..7abeefca6 100644 --- a/kmath-core/src/commonTest/kotlin/scientifik/kmath/structures/NDFieldTest.kt +++ b/kmath-core/src/commonTest/kotlin/scientifik/kmath/structures/NDFieldTest.kt @@ -10,4 +10,4 @@ class NDFieldTest { val ndArray = NDElement.real(intArrayOf(10, 10)) { (it[0] + it[1]).toDouble() } assertEquals(ndArray[5, 5], 10.0) } -} \ 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 60f1f9979..d48aabfd0 100644 --- a/kmath-core/src/commonTest/kotlin/scientifik/kmath/structures/NumberNDFieldTest.kt +++ b/kmath-core/src/commonTest/kotlin/scientifik/kmath/structures/NumberNDFieldTest.kt @@ -8,8 +8,8 @@ import kotlin.test.Test import kotlin.test.assertEquals class NumberNDFieldTest { - val array1 = real2D(3, 3) { i, j -> (i + j).toDouble() } - val array2 = real2D(3, 3) { i, j -> (i - j).toDouble() } + val array1: RealNDElement = real2D(3, 3) { i, j -> (i + j).toDouble() } + val array2: RealNDElement = real2D(3, 3) { i, j -> (i - j).toDouble() } @Test fun testSum() { -- 2.34.1 From 8debed704873df4bf9af77151bbce81dbb920057 Mon Sep 17 00:00:00 2001 From: Iaroslav Postovalov Date: Wed, 5 Aug 2020 17:25:56 +0700 Subject: [PATCH 142/156] Improve documentation --- .../kmath/operations/AlgebraElements.kt | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/AlgebraElements.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/AlgebraElements.kt index 9b430af39..0de2109c4 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/AlgebraElements.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/AlgebraElements.kt @@ -24,9 +24,36 @@ interface MathWrapper { * @param S the type of space */ interface SpaceElement, S : Space> : MathElement, MathWrapper { + /** + * Adds element to this one. + * + * @param b the augend. + * @return the sum. + */ operator fun plus(b: T): I = context.add(unwrap(), b).wrap() + + /** + * Subtracts element from this one. + * + * @param b the subtrahend. + * @return the difference. + */ operator fun minus(b: T): I = context.add(unwrap(), context.multiply(b, -1.0)).wrap() + + /** + * Multiplies this element by number. + * + * @param k the multiplicand. + * @return the product. + */ operator fun times(k: Number): I = context.multiply(unwrap(), k.toDouble()).wrap() + + /** + * Divides this element by number. + * + * @param k the divisor. + * @return the quotient. + */ operator fun div(k: Number): I = context.multiply(unwrap(), 1.0 / k.toDouble()).wrap() } @@ -34,6 +61,12 @@ interface SpaceElement, S : Space> : MathElement * Ring element */ interface RingElement, R : Ring> : SpaceElement { + /** + * Multiplies this element by another one. + * + * @param b the multiplicand. + * @return the product. + */ operator fun times(b: T): I = context.multiply(unwrap(), b).wrap() } @@ -42,5 +75,12 @@ interface RingElement, R : Ring> : SpaceElement, F : Field> : RingElement { override val context: F + + /** + * Divides this element by another one. + * + * @param b the divisor. + * @return the quotient. + */ operator fun div(b: T): I = context.divide(unwrap(), b).wrap() } -- 2.34.1 From aed10329ebb6ea7bdedad469fbaa161ee41fba6a Mon Sep 17 00:00:00 2001 From: Iaroslav Postovalov Date: Wed, 5 Aug 2020 17:30:11 +0700 Subject: [PATCH 143/156] Improve documentation of elements --- .../kmath/operations/AlgebraElements.kt | 21 +++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/AlgebraElements.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/AlgebraElements.kt index 0de2109c4..534f56e0d 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/AlgebraElements.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/AlgebraElements.kt @@ -18,10 +18,11 @@ interface MathWrapper { } /** - * The element of linear context - * @param T the type of space operation results - * @param I self type of the element. Needed for static type checking - * @param S the type of space + * The element of [Space]. + * + * @param T the type of space operation results. + * @param I self type of the element. Needed for static type checking. + * @param S the type of space. */ interface SpaceElement, S : Space> : MathElement, MathWrapper { /** @@ -58,7 +59,11 @@ interface SpaceElement, S : Space> : MathElement } /** - * Ring element + * The element of [Ring]. + * + * @param T the type of space operation results. + * @param I self type of the element. Needed for static type checking. + * @param R the type of space. */ interface RingElement, R : Ring> : SpaceElement { /** @@ -71,7 +76,11 @@ interface RingElement, R : Ring> : SpaceElement, F : Field> : RingElement { override val context: F -- 2.34.1 From 8367e13057fb19df3b6c356f6d5846bb92ffc6bd Mon Sep 17 00:00:00 2001 From: Iaroslav Postovalov Date: Wed, 5 Aug 2020 18:41:58 +0700 Subject: [PATCH 144/156] Improve documentation of complex and memoryspec --- .../scientifik/kmath/operations/Complex.kt | 57 ++++++++++++++++--- .../kotlin/scientifik/memory/MemorySpec.kt | 29 ++++++---- 2 files changed, 67 insertions(+), 19 deletions(-) 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 ffd6c8dec..0ce144a33 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Complex.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Complex.kt @@ -11,13 +11,16 @@ import kotlin.math.* private val PI_DIV_2 = Complex(PI / 2, 0) /** - * A field for complex numbers + * A field of [Complex]. */ object ComplexField : ExtendedField { override val zero: Complex = Complex(0.0, 0.0) override val one: Complex = Complex(1.0, 0.0) + /** + * The imaginary unit. + */ val i: Complex = Complex(0.0, 1.0) override fun add(a: Complex, b: Complex): Complex = Complex(a.re + b.re, a.im + b.im) @@ -45,25 +48,59 @@ object ComplexField : ExtendedField { override fun ln(arg: Complex): Complex = ln(arg.r) + i * atan2(arg.im, arg.re) + /** + * Adds complex number to real one. + * + * @receiver the addend. + * @param c the augend. + * @return the sum. + */ operator fun Double.plus(c: Complex): Complex = add(this.toComplex(), c) + /** + * Subtracts complex number from real one. + * + * @receiver the minuend. + * @param c the subtrahend. + * @return the difference. + */ operator fun Double.minus(c: Complex): Complex = add(this.toComplex(), -c) + /** + * Adds real number to complex one. + * + * @receiver the addend. + * @param d the augend. + * @return the sum. + */ operator fun Complex.plus(d: Double): Complex = d + this + /** + * Subtracts real number from complex one. + * + * @receiver the minuend. + * @param d the subtrahend. + * @return the difference. + */ operator fun Complex.minus(d: Double): Complex = add(this, -d.toComplex()) + /** + * Multiplies real number by complex one. + * + * @receiver the multiplier. + * @param c the multiplicand. + * @receiver the product. + */ operator fun Double.times(c: Complex): Complex = Complex(c.re * this, c.im * this) - override fun symbol(value: String): Complex = if (value == "i") { - i - } else { - super.symbol(value) - } + override fun symbol(value: String): Complex = if (value == "i") i else super.symbol(value) } /** - * Complex number class + * Represents complex number. + * + * @property re The real part. + * @property im The imaginary part. */ data class Complex(val re: Double, val im: Double) : FieldElement, Comparable { constructor(re: Number, im: Number) : this(re.toDouble(), im.toDouble()) @@ -104,6 +141,12 @@ val Complex.r: Double get() = sqrt(re * re + im * im) */ val Complex.theta: Double get() = atan(im / re) +/** + * Creates a complex number with real part equal to this real. + * + * @receiver the real part. + * @return the new complex number. + */ fun Double.toComplex(): Complex = Complex(this, 0.0) inline fun Buffer.Companion.complex(size: Int, crossinline init: (Int) -> Complex): Buffer { diff --git a/kmath-memory/src/commonMain/kotlin/scientifik/memory/MemorySpec.kt b/kmath-memory/src/commonMain/kotlin/scientifik/memory/MemorySpec.kt index 7999aa2ab..4d0035d09 100644 --- a/kmath-memory/src/commonMain/kotlin/scientifik/memory/MemorySpec.kt +++ b/kmath-memory/src/commonMain/kotlin/scientifik/memory/MemorySpec.kt @@ -1,7 +1,9 @@ package scientifik.memory /** - * A specification to read or write custom objects with fixed size in bytes + * A specification to read or write custom objects with fixed size in bytes. + * + * @param T the type of object this spec manages. */ interface MemorySpec { /** @@ -9,27 +11,30 @@ interface MemorySpec { */ val objectSize: Int + /** + * Reads the object starting from [offset]. + */ fun MemoryReader.read(offset: Int): T - //TODO consider thread safety + + // TODO consider thread safety + + /** + * Writes the object [value] starting from [offset]. + */ fun MemoryWriter.write(offset: Int, value: T) } -fun MemoryReader.read(spec: MemorySpec, offset: Int): T = spec.run { read(offset) } -fun MemoryWriter.write(spec: MemorySpec, offset: Int, value: T) = spec.run { write(offset, value) } +fun MemoryReader.read(spec: MemorySpec, offset: Int): T = with(spec) { read(offset) } +fun MemoryWriter.write(spec: MemorySpec, offset: Int, value: T): Unit = with(spec) { write(offset, value) } -inline fun MemoryReader.readArray(spec: MemorySpec, offset: Int, size: Int) = +inline fun MemoryReader.readArray(spec: MemorySpec, offset: Int, size: Int): Array = Array(size) { i -> spec.run { read(offset + i * objectSize) } } -fun MemoryWriter.writeArray(spec: MemorySpec, offset: Int, array: Array) { - spec.run { - for (i in array.indices) { - write(offset + i * objectSize, array[i]) - } - } -} +fun MemoryWriter.writeArray(spec: MemorySpec, offset: Int, array: Array): Unit = + with(spec) { array.indices.forEach { i -> write(offset + i * objectSize, array[i]) } } //TODO It is possible to add elastic MemorySpec with unknown object size \ No newline at end of file -- 2.34.1 From 7d32fe0af82c4133b8173ff0f6228d417104760c Mon Sep 17 00:00:00 2001 From: Iaroslav Postovalov Date: Wed, 5 Aug 2020 18:43:20 +0700 Subject: [PATCH 145/156] Update documentation of NumberAlgebra --- .../kotlin/scientifik/kmath/operations/NumberAlgebra.kt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/NumberAlgebra.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/NumberAlgebra.kt index 2145eb4b5..f99a6bc0f 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/NumberAlgebra.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/NumberAlgebra.kt @@ -52,7 +52,7 @@ inline class Real(val value: Double) : FieldElement { } /** - * A field for double without boxing. Does not produce appropriate field element + * A field for [Double] without boxing. Does not produce appropriate field element. */ @Suppress("EXTENSION_SHADOWED_BY_MEMBER", "OVERRIDE_BY_INLINE", "NOTHING_TO_INLINE") object RealField : ExtendedField, Norm { @@ -94,6 +94,9 @@ object RealField : ExtendedField, Norm { } } +/** + * A field for [Float] without boxing. Does not produce appropriate field element. + */ @Suppress("EXTENSION_SHADOWED_BY_MEMBER", "OVERRIDE_BY_INLINE", "NOTHING_TO_INLINE") object FloatField : ExtendedField, Norm { override val zero: Float = 0f -- 2.34.1 From 4c39b9801747d5016e0e1e04414ce868d6fdce43 Mon Sep 17 00:00:00 2001 From: Iaroslav Postovalov Date: Wed, 5 Aug 2020 18:44:28 +0700 Subject: [PATCH 146/156] Update documentation of NumberAlgebra --- .../kotlin/scientifik/kmath/operations/NumberAlgebra.kt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/NumberAlgebra.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/NumberAlgebra.kt index f99a6bc0f..b113e07a1 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/NumberAlgebra.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/NumberAlgebra.kt @@ -4,7 +4,7 @@ import kotlin.math.abs import kotlin.math.pow as kpow /** - * Advanced Number-like field that implements basic operations + * Advanced Number-like semifield that implements basic operations. */ interface ExtendedFieldOperations : InverseTrigonometricOperations, @@ -27,6 +27,10 @@ interface ExtendedFieldOperations : } } + +/** + * Advanced Number-like field that implements basic operations. + */ interface ExtendedField : ExtendedFieldOperations, Field { override fun rightSideNumberOperation(operation: String, left: T, right: Number): T = when (operation) { PowerOperations.POW_OPERATION -> power(left, right) -- 2.34.1 From eae218ff5f0c46bbab8a0d425f54467944a0d71b Mon Sep 17 00:00:00 2001 From: Iaroslav Postovalov Date: Wed, 5 Aug 2020 20:55:50 +0700 Subject: [PATCH 147/156] Update algebra extensions, improve docs --- .../kmath/operations/AlgebraExtensions.kt | 55 +++++++++++++++++-- .../kmath/operations/OptionalOperations.kt | 6 +- .../scientifik/kmath/structures/Buffers.kt | 31 +++++++++-- .../scientifik/kmath/structures/IntBuffer.kt | 11 ++++ .../scientifik/kmath/operations/BigNumbers.kt | 9 ++- 5 files changed, 100 insertions(+), 12 deletions(-) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/AlgebraExtensions.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/AlgebraExtensions.kt index a6d508ee4..6712d3baa 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/AlgebraExtensions.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/AlgebraExtensions.kt @@ -1,15 +1,62 @@ package scientifik.kmath.operations +/** + * Returns the sum of all elements in the iterable in this [Space]. + * + * @receiver the algebra that provides addition. + * @param data the collection to sum up. + * @return the sum. + */ fun Space.sum(data: Iterable): T = data.fold(zero) { left, right -> add(left, right) } + +/** + * Returns the sum of all elements in the sequence in this [Space]. + * + * @receiver the algebra that provides addition. + * @param data the collection to sum up. + * @return the sum. + */ fun Space.sum(data: Sequence): T = data.fold(zero) { left, right -> add(left, right) } +/** + * Returns the sum of all elements in the iterable in provided space. + * + * @receiver the collection to sum up. + * @param space the algebra that provides addition. + * @return the sum. + */ fun > Iterable.sumWith(space: S): T = space.sum(this) //TODO optimized power operation -fun RingOperations.power(arg: T, power: Int): T { + +/** + * Raises [arg] to the natural power [power]. + * + * @receiver the algebra to provide multiplication. + * @param arg the base. + * @param power the exponent. + * @return the base raised to the power. + */ +fun Ring.power(arg: T, power: Int): T { + require(power >= 0) { "The power can't be negative." } + require(power != 0 || arg != zero) { "The $zero raised to $power is not defined." } + if (power == 0) return one var res = arg - repeat(power - 1) { - res *= arg - } + repeat(power - 1) { res *= arg } return res } + +/** + * Raises [arg] to the integer power [power]. + * + * @receiver the algebra to provide multiplication and division. + * @param arg the base. + * @param power the exponent. + * @return the base raised to the power. + */ +fun Field.power(arg: T, power: Int): T { + require(power != 0 || arg != zero) { "The $zero raised to $power is not defined." } + if (power == 0) return one + if (power < 0) return one / (this as Ring).power(arg, -power) + return (this as Ring).power(arg, power) +} 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 75adb1dee..dea45a145 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/OptionalOperations.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/OptionalOperations.kt @@ -143,7 +143,11 @@ interface PowerOperations : Algebra { } /** - * Raises [arg] to the power [pow]. + * Raises this element to the power [pow]. + * + * @receiver the base. + * @param power the exponent. + * @return the base raised to the power. */ infix fun >> T.pow(power: Double): T = context.power(this, power) 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 723348e3f..294549c31 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/Buffers.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/Buffers.kt @@ -4,13 +4,22 @@ import scientifik.kmath.operations.Complex import scientifik.kmath.operations.complex import kotlin.reflect.KClass - +/** + * Function that produces [Buffer] from its size and function that supplies values. + * + * @param T the type of buffer. + */ typealias BufferFactory = (Int, (Int) -> T) -> Buffer -typealias MutableBufferFactory = (Int, (Int) -> T) -> MutableBuffer - /** - * A generic random access structure for both primitives and objects + * Function that produces [MutableBuffer] from its size and function that supplies values. + * + * @param T the type of buffer. + */ +typealias MutableBufferFactory = (Int, (Int) -> T) -> MutableBuffer + +/** + * A generic random-access structure for both primitives and objects. */ interface Buffer { @@ -69,12 +78,24 @@ interface Buffer { } } +/** + * Creates a sequence that returns all elements from this [Buffer]. + */ fun Buffer.asSequence(): Sequence = Sequence(::iterator) +/** + * Creates an iterable that returns all elements from this [Buffer]. + */ fun Buffer.asIterable(): Iterable = Iterable(::iterator) -val Buffer<*>.indices: IntRange get() = IntRange(0, size - 1) +/** + * Returns an [IntRange] of the valid indices for this [Buffer]. + */ +val Buffer<*>.indices: IntRange get() = 0 until size +/** + * A generic mutable random-access structure for both primitives and objects. + */ interface MutableBuffer : Buffer { operator fun set(index: Int, value: T) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/IntBuffer.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/IntBuffer.kt index b277374ab..99fd052aa 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/IntBuffer.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/IntBuffer.kt @@ -1,5 +1,10 @@ package scientifik.kmath.structures +/** + * Specialized [MutableBuffer] implementation over [IntArray]. + * + * @property array the underlying array. + */ inline class IntBuffer(val array: IntArray) : MutableBuffer { override val size: Int get() = array.size @@ -16,4 +21,10 @@ inline class IntBuffer(val array: IntArray) : MutableBuffer { } +/** + * Returns [IntBuffer] over this array. + * + * @receiver the array. + * @return the new buffer. + */ fun IntArray.asBuffer(): IntBuffer = IntBuffer(this) diff --git a/kmath-core/src/jvmMain/kotlin/scientifik/kmath/operations/BigNumbers.kt b/kmath-core/src/jvmMain/kotlin/scientifik/kmath/operations/BigNumbers.kt index eb268bb5e..06f2b31ad 100644 --- a/kmath-core/src/jvmMain/kotlin/scientifik/kmath/operations/BigNumbers.kt +++ b/kmath-core/src/jvmMain/kotlin/scientifik/kmath/operations/BigNumbers.kt @@ -5,7 +5,7 @@ import java.math.BigInteger import java.math.MathContext /** - * A field wrapper for Java [BigInteger] + * A field over [BigInteger]. */ object JBigIntegerField : Field { override val zero: BigInteger @@ -24,7 +24,9 @@ object JBigIntegerField : Field { } /** - * A Field wrapper for Java [BigDecimal] + * An abstract field over [BigDecimal]. + * + * @property mathContext the [MathContext] to use. */ abstract class JBigDecimalFieldBase internal constructor(val mathContext: MathContext = MathContext.DECIMAL64) : Field, @@ -50,6 +52,9 @@ abstract class JBigDecimalFieldBase internal constructor(val mathContext: MathCo } +/** + * A field over [BigDecimal]. + */ class JBigDecimalField(mathContext: MathContext = MathContext.DECIMAL64) : JBigDecimalFieldBase(mathContext) { companion object : JBigDecimalFieldBase() } -- 2.34.1 From 9f6bd116f660003c36d6655c2930a8a62a4e8770 Mon Sep 17 00:00:00 2001 From: Iaroslav Postovalov Date: Thu, 6 Aug 2020 12:39:50 +0700 Subject: [PATCH 148/156] Document and make more consistent buffers API --- .../scientifik/kmath/structures/Buffers.kt | 5 +++- .../scientifik/kmath/structures/IntBuffer.kt | 22 +++++++++++++++++ .../scientifik/kmath/structures/RealBuffer.kt | 24 ++++++++++++++----- 3 files changed, 44 insertions(+), 7 deletions(-) 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 294549c31..73c3f96f4 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/Buffers.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/Buffers.kt @@ -97,10 +97,13 @@ val Buffer<*>.indices: IntRange get() = 0 until size * A generic mutable random-access structure for both primitives and objects. */ interface MutableBuffer : Buffer { + /** + * Sets the array element at the specified [index] to the specified [value]. + */ operator fun set(index: Int, value: T) /** - * A shallow copy of the buffer + * Returns a shallow copy of the buffer. */ fun copy(): MutableBuffer diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/IntBuffer.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/IntBuffer.kt index 99fd052aa..3bf8ef6ab 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/IntBuffer.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/IntBuffer.kt @@ -21,6 +21,28 @@ inline class IntBuffer(val array: IntArray) : MutableBuffer { } +/** + * Creates a new [IntBuffer] with the specified [size], where each element is calculated by calling the specified + * [init] function. + * + * The function [init] is called for each array element sequentially starting from the first one. + * It should return the value for an array element given its index. + */ +@Suppress("FunctionName") +inline fun IntBuffer(size: Int, init: (Int) -> Int): IntBuffer = IntBuffer(IntArray(size) { init(it) }) + +/** + * Returns a new [IntBuffer] of given elements. + */ +@Suppress("FunctionName") +fun IntBuffer(vararg ints: Int): IntBuffer = IntBuffer(ints) + +/** + * Returns a [IntArray] containing all of the elements of this [MutableBuffer]. + */ +val MutableBuffer.array: IntArray + get() = (if (this is IntBuffer) array else IntArray(size) { get(it) }) + /** * Returns [IntBuffer] over this array. * diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/RealBuffer.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/RealBuffer.kt index 20187fe19..715f3a017 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/RealBuffer.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/RealBuffer.kt @@ -15,20 +15,32 @@ inline class RealBuffer(val array: DoubleArray) : MutableBuffer { RealBuffer(array.copyOf()) } +/** + * Creates a new array with the specified [size], where each element is calculated by calling the specified + * [init] function. + * + * The function [init] is called for each array element sequentially starting from the first one. + * It should return the value for an array element given its index. + */ @Suppress("FunctionName") inline fun RealBuffer(size: Int, init: (Int) -> Double): RealBuffer = RealBuffer(DoubleArray(size) { init(it) }) +/** + * Returns a new [RealBuffer] of given elements. + */ @Suppress("FunctionName") fun RealBuffer(vararg doubles: Double): RealBuffer = RealBuffer(doubles) /** - * Transform buffer of doubles into array for high performance operations + * Returns a [DoubleArray] containing all of the elements of this [MutableBuffer]. */ val MutableBuffer.array: DoubleArray - get() = if (this is RealBuffer) { - array - } else { - DoubleArray(size) { get(it) } - } + get() = (if (this is RealBuffer) array else DoubleArray(size) { get(it) }) +/** + * Returns [RealBuffer] over this array. + * + * @receiver the array. + * @return the new buffer. + */ fun DoubleArray.asBuffer(): RealBuffer = RealBuffer(this) -- 2.34.1 From 0a8044ddb3cf4fd6fc38431106daea4aa4ff5302 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Fri, 7 Aug 2020 15:20:26 +0700 Subject: [PATCH 149/156] Update buffers documentation, make API more consistent, minor changes --- .../scientifik/kmath/expressions/Builders.kt | 6 +- .../kmath/operations/AlgebraElements.kt | 13 ++++ .../scientifik/kmath/structures/Buffers.kt | 63 +++++++++++++++---- .../kmath/structures/FlaggedBuffer.kt | 22 ++++++- .../kmath/structures/FloatBuffer.kt | 49 +++++++++++++++ .../scientifik/kmath/structures/IntBuffer.kt | 4 +- .../scientifik/kmath/structures/LongBuffer.kt | 31 +++++++++ .../kmath/structures/MemoryBuffer.kt | 13 +++- .../scientifik/kmath/structures/NDAlgebra.kt | 4 +- .../scientifik/kmath/structures/RealBuffer.kt | 11 ++-- .../kmath/structures/RealBufferField.kt | 7 ++- .../kmath/structures/ShortBuffer.kt | 31 +++++++++ 12 files changed, 227 insertions(+), 27 deletions(-) create mode 100644 kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/FloatBuffer.kt diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/Builders.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/Builders.kt index 9f1503285..834ef9e24 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/Builders.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/Builders.kt @@ -5,19 +5,19 @@ import scientifik.kmath.operations.Ring import scientifik.kmath.operations.Space /** - * Create a functional expression on this [Space] + * Creates a functional expression with this [Space]. */ fun Space.spaceExpression(block: FunctionalExpressionSpace>.() -> Expression): Expression = FunctionalExpressionSpace(this).run(block) /** - * Create a functional expression on this [Ring] + * Creates a functional expression with this [Ring]. */ fun Ring.ringExpression(block: FunctionalExpressionRing>.() -> Expression): Expression = FunctionalExpressionRing(this).run(block) /** - * Create a functional expression on this [Field] + * Creates a functional expression with this [Field]. */ fun Field.fieldExpression(block: FunctionalExpressionField>.() -> Expression): Expression = FunctionalExpressionField(this).run(block) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/AlgebraElements.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/AlgebraElements.kt index 534f56e0d..197897c14 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/AlgebraElements.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/AlgebraElements.kt @@ -12,8 +12,21 @@ interface MathElement { val context: C } +/** + * Represents element that can be wrapped to its "primitive" value. + * + * @param T the type wrapped by this wrapper. + * @param I the type of this wrapper. + */ interface MathWrapper { + /** + * Unwraps [I] to [T]. + */ fun unwrap(): T + + /** + * Wraps [T] to [I]. + */ fun T.wrap(): I } 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 73c3f96f4..5fdf79e88 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/Buffers.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/Buffers.kt @@ -19,10 +19,11 @@ typealias BufferFactory = (Int, (Int) -> T) -> Buffer typealias MutableBufferFactory = (Int, (Int) -> T) -> MutableBuffer /** - * A generic random-access structure for both primitives and objects. + * A generic immutable random-access structure for both primitives and objects. + * + * @param T the type of elements contained in the buffer. */ interface Buffer { - /** * The size of this buffer. */ @@ -45,7 +46,6 @@ interface Buffer { asSequence().mapIndexed { index, value -> value == other[index] }.all { it } companion object { - inline fun real(size: Int, initializer: (Int) -> Double): RealBuffer { val array = DoubleArray(size) { initializer(it) } return RealBuffer(array) @@ -95,6 +95,8 @@ val Buffer<*>.indices: IntRange get() = 0 until size /** * A generic mutable random-access structure for both primitives and objects. + * + * @param T the type of elements contained in the buffer. */ interface MutableBuffer : Buffer { /** @@ -138,8 +140,13 @@ interface MutableBuffer : Buffer { } } +/** + * [Buffer] implementation over [List]. + * + * @param T the type of elements contained in the buffer. + * @property list The underlying list. + */ inline class ListBuffer(val list: List) : Buffer { - override val size: Int get() = list.size @@ -148,10 +155,26 @@ inline class ListBuffer(val list: List) : Buffer { override fun iterator(): Iterator = list.iterator() } +/** + * Returns an [ListBuffer] that wraps the original list. + */ fun List.asBuffer(): ListBuffer = ListBuffer(this) +/** + * Creates a new [ListBuffer] with the specified [size], where each element is calculated by calling the specified + * [init] function. + * + * The function [init] is called for each array element sequentially starting from the first one. + * It should return the value for an array element given its index. + */ inline fun ListBuffer(size: Int, init: (Int) -> T): ListBuffer = List(size, init).asBuffer() +/** + * [MutableBuffer] implementation over [MutableList]. + * + * @param T the type of elements contained in the buffer. + * @property list The underlying list. + */ inline class MutableListBuffer(val list: MutableList) : MutableBuffer { override val size: Int @@ -167,8 +190,14 @@ inline class MutableListBuffer(val list: MutableList) : MutableBuffer { override fun copy(): MutableBuffer = MutableListBuffer(ArrayList(list)) } +/** + * [MutableBuffer] implementation over [Array]. + * + * @param T the type of elements contained in the buffer. + * @property array The underlying array. + */ class ArrayBuffer(private val array: Array) : MutableBuffer { - //Can't inline because array is invariant + // Can't inline because array is invariant override val size: Int get() = array.size @@ -183,8 +212,17 @@ class ArrayBuffer(private val array: Array) : MutableBuffer { override fun copy(): MutableBuffer = ArrayBuffer(array.copyOf()) } +/** + * Returns an [ArrayBuffer] that wraps the original array. + */ fun Array.asBuffer(): ArrayBuffer = ArrayBuffer(this) +/** + * Immutable wrapper for [MutableBuffer]. + * + * @param T the type of elements contained in the buffer. + * @property buffer The underlying buffer. + */ inline class ReadOnlyBuffer(val buffer: MutableBuffer) : Buffer { override val size: Int get() = buffer.size @@ -196,6 +234,8 @@ inline class ReadOnlyBuffer(val buffer: MutableBuffer) : Buffer { /** * A buffer with content calculated on-demand. The calculated content is not stored, so it is recalculated on each call. * Useful when one needs single element from the buffer. + * + * @param T the type of elements provided by the buffer. */ class VirtualBuffer(override val size: Int, private val generator: (Int) -> T) : Buffer { override fun get(index: Int): T { @@ -215,17 +255,16 @@ class VirtualBuffer(override val size: Int, private val generator: (Int) -> T } /** - * Convert this buffer to read-only buffer + * Convert this buffer to read-only buffer. */ -fun Buffer.asReadOnly(): Buffer = if (this is MutableBuffer) { - ReadOnlyBuffer(this) -} else { - this -} +fun Buffer.asReadOnly(): Buffer = if (this is MutableBuffer) ReadOnlyBuffer(this) else this /** - * Typealias for buffer transformations + * Typealias for buffer transformations. */ typealias BufferTransform = (Buffer) -> Buffer +/** + * Typealias for buffer transformations with suspend function. + */ typealias SuspendBufferTransform = suspend (Buffer) -> Buffer diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/FlaggedBuffer.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/FlaggedBuffer.kt index 2382e2c57..a2d0a71b3 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/FlaggedBuffer.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/FlaggedBuffer.kt @@ -2,15 +2,35 @@ package scientifik.kmath.structures import kotlin.experimental.and +/** + * Represents flags to supply additional info about values of buffer. + * + * @property mask bit mask value of this flag. + */ enum class ValueFlag(val mask: Byte) { + /** + * Reports the value is NaN. + */ NAN(0b0000_0001), + + /** + * Reports the value doesn't present in the buffer (when the type of value doesn't support `null`). + */ MISSING(0b0000_0010), + + /** + * Reports the value is negative infinity. + */ NEGATIVE_INFINITY(0b0000_0100), + + /** + * Reports the value is positive infinity + */ POSITIVE_INFINITY(0b0000_1000) } /** - * A buffer with flagged values + * A buffer with flagged values. */ interface FlaggedBuffer : Buffer { fun getFlag(index: Int): Byte diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/FloatBuffer.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/FloatBuffer.kt new file mode 100644 index 000000000..e42df8c14 --- /dev/null +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/FloatBuffer.kt @@ -0,0 +1,49 @@ +package scientifik.kmath.structures + +/** + * Specialized [MutableBuffer] implementation over [FloatArray]. + * + * @property array the underlying array. + */ +inline class FloatBuffer(val array: FloatArray) : MutableBuffer { + override val size: Int get() = array.size + + override fun get(index: Int): Float = array[index] + + override fun set(index: Int, value: Float) { + array[index] = value + } + + override fun iterator(): FloatIterator = array.iterator() + + override fun copy(): MutableBuffer = + FloatBuffer(array.copyOf()) +} + +/** + * Creates a new [FloatBuffer] with the specified [size], where each element is calculated by calling the specified + * [init] function. + * + * The function [init] is called for each array element sequentially starting from the first one. + * It should return the value for an buffer element given its index. + */ +inline fun FloatBuffer(size: Int, init: (Int) -> Float): FloatBuffer = FloatBuffer(FloatArray(size) { init(it) }) + +/** + * Returns a new [FloatBuffer] of given elements. + */ +fun FloatBuffer(vararg floats: Float): FloatBuffer = FloatBuffer(floats) + +/** + * Returns a [FloatArray] containing all of the elements of this [MutableBuffer]. + */ +val MutableBuffer.array: FloatArray + get() = (if (this is FloatBuffer) array else FloatArray(size) { get(it) }) + +/** + * Returns [FloatBuffer] over this array. + * + * @receiver the array. + * @return the new buffer. + */ +fun FloatArray.asBuffer(): FloatBuffer = FloatBuffer(this) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/IntBuffer.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/IntBuffer.kt index 3bf8ef6ab..a3f0f3c3e 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/IntBuffer.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/IntBuffer.kt @@ -26,15 +26,13 @@ inline class IntBuffer(val array: IntArray) : MutableBuffer { * [init] function. * * The function [init] is called for each array element sequentially starting from the first one. - * It should return the value for an array element given its index. + * It should return the value for an buffer element given its index. */ -@Suppress("FunctionName") inline fun IntBuffer(size: Int, init: (Int) -> Int): IntBuffer = IntBuffer(IntArray(size) { init(it) }) /** * Returns a new [IntBuffer] of given elements. */ -@Suppress("FunctionName") fun IntBuffer(vararg ints: Int): IntBuffer = IntBuffer(ints) /** diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/LongBuffer.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/LongBuffer.kt index 3ee2e75a2..912656c68 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/LongBuffer.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/LongBuffer.kt @@ -1,5 +1,10 @@ package scientifik.kmath.structures +/** + * Specialized [MutableBuffer] implementation over [LongArray]. + * + * @property array the underlying array. + */ inline class LongBuffer(val array: LongArray) : MutableBuffer { override val size: Int get() = array.size @@ -16,4 +21,30 @@ inline class LongBuffer(val array: LongArray) : MutableBuffer { } +/** + * Creates a new [LongBuffer] with the specified [size], where each element is calculated by calling the specified + * [init] function. + * + * The function [init] is called for each array element sequentially starting from the first one. + * It should return the value for an buffer element given its index. + */ +inline fun LongBuffer(size: Int, init: (Int) -> Long): LongBuffer = LongBuffer(LongArray(size) { init(it) }) + +/** + * Returns a new [LongBuffer] of given elements. + */ +fun LongBuffer(vararg longs: Long): LongBuffer = LongBuffer(longs) + +/** + * Returns a [IntArray] containing all of the elements of this [MutableBuffer]. + */ +val MutableBuffer.array: LongArray + get() = (if (this is LongBuffer) array else LongArray(size) { get(it) }) + +/** + * Returns [LongBuffer] over this array. + * + * @receiver the array. + * @return the new buffer. + */ fun LongArray.asBuffer(): LongBuffer = LongBuffer(this) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/MemoryBuffer.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/MemoryBuffer.kt index b15487114..70e4a8f0f 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/MemoryBuffer.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/MemoryBuffer.kt @@ -4,12 +4,16 @@ import scientifik.memory.* /** * A non-boxing buffer over [Memory] object. + * + * @param T the type of elements contained in the buffer. + * @property memory the underlying memory segment. + * @property spec the spec of [T] type. */ open class MemoryBuffer(protected val memory: Memory, protected val spec: MemorySpec) : Buffer { override val size: Int get() = memory.size / spec.objectSize - private val reader = memory.reader() + private val reader: MemoryReader = memory.reader() override fun get(index: Int): T = reader.read(spec, spec.objectSize * index) @@ -33,6 +37,13 @@ open class MemoryBuffer(protected val memory: Memory, protected val spe } } +/** + * A mutable non-boxing buffer over [Memory] object. + * + * @param T the type of elements contained in the buffer. + * @property memory the underlying memory segment. + * @property spec the spec of [T] type. + */ class MutableMemoryBuffer(memory: Memory, spec: MemorySpec) : MemoryBuffer(memory, spec), MutableBuffer { diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/NDAlgebra.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/NDAlgebra.kt index 750133351..f2805529c 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/NDAlgebra.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/NDAlgebra.kt @@ -107,8 +107,8 @@ interface NDRing, N : NDStructure> : Ring, NDSpace /** * Field for n-dimensional structures. * - * @param T - the type of the element contained in ND structure - * @param F - field of structure elements + * @param T the type of the element contained in ND structure + * @param F field of structure elements */ interface NDField, N : NDStructure> : Field, NDRing { diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/RealBuffer.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/RealBuffer.kt index 715f3a017..e999e12b2 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/RealBuffer.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/RealBuffer.kt @@ -1,5 +1,10 @@ package scientifik.kmath.structures +/** + * Specialized [MutableBuffer] implementation over [DoubleArray]. + * + * @property array the underlying array. + */ inline class RealBuffer(val array: DoubleArray) : MutableBuffer { override val size: Int get() = array.size @@ -16,19 +21,17 @@ inline class RealBuffer(val array: DoubleArray) : MutableBuffer { } /** - * Creates a new array with the specified [size], where each element is calculated by calling the specified + * Creates a new [RealBuffer] with the specified [size], where each element is calculated by calling the specified * [init] function. * * The function [init] is called for each array element sequentially starting from the first one. - * It should return the value for an array element given its index. + * It should return the value for an buffer element given its index. */ -@Suppress("FunctionName") inline fun RealBuffer(size: Int, init: (Int) -> Double): RealBuffer = RealBuffer(DoubleArray(size) { init(it) }) /** * Returns a new [RealBuffer] of given elements. */ -@Suppress("FunctionName") fun RealBuffer(vararg doubles: Double): RealBuffer = RealBuffer(doubles) /** diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/RealBufferField.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/RealBufferField.kt index 826203d1f..33198aac1 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/RealBufferField.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/RealBufferField.kt @@ -6,7 +6,7 @@ import kotlin.math.* /** - * A simple field over linear buffers of [Double] + * [ExtendedFieldOperations] over [RealBuffer]. */ object RealBufferFieldOperations : ExtendedFieldOperations> { override fun add(a: Buffer, b: Buffer): RealBuffer { @@ -109,6 +109,11 @@ object RealBufferFieldOperations : ExtendedFieldOperations> { RealBuffer(DoubleArray(arg.size) { ln(arg[it]) }) } +/** + * [ExtendedField] over [RealBuffer]. + * + * @property size the size of buffers to operate on. + */ class RealBufferField(val size: Int) : ExtendedField> { override val zero: Buffer by lazy { RealBuffer(size) { 0.0 } } override val one: Buffer by lazy { RealBuffer(size) { 1.0 } } diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ShortBuffer.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ShortBuffer.kt index 24c94a885..a67cbec62 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ShortBuffer.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ShortBuffer.kt @@ -1,5 +1,10 @@ package scientifik.kmath.structures +/** + * Specialized [MutableBuffer] implementation over [ShortBuffer]. + * + * @property array the underlying array. + */ inline class ShortBuffer(val array: ShortArray) : MutableBuffer { override val size: Int get() = array.size @@ -16,4 +21,30 @@ inline class ShortBuffer(val array: ShortArray) : MutableBuffer { } +/** + * Creates a new [ShortBuffer] with the specified [size], where each element is calculated by calling the specified + * [init] function. + * + * The function [init] is called for each array element sequentially starting from the first one. + * It should return the value for an buffer element given its index. + */ +inline fun ShortBuffer(size: Int, init: (Int) -> Short): ShortBuffer = ShortBuffer(ShortArray(size) { init(it) }) + +/** + * Returns a new [ShortBuffer] of given elements. + */ +fun ShortBuffer(vararg shorts: Short): ShortBuffer = ShortBuffer(shorts) + +/** + * Returns a [ShortArray] containing all of the elements of this [MutableBuffer]. + */ +val MutableBuffer.array: ShortArray + get() = (if (this is ShortBuffer) array else ShortArray(size) { get(it) }) + +/** + * Returns [ShortBuffer] over this array. + * + * @receiver the array. + * @return the new buffer. + */ fun ShortArray.asBuffer(): ShortBuffer = ShortBuffer(this) -- 2.34.1 From c8cd6cd28857b14199ee99683f789eb11a28794e Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Sat, 8 Aug 2020 04:21:59 +0700 Subject: [PATCH 150/156] Document memory module and several ND objects --- .../kmath/structures/BoxingNDField.kt | 1 - .../kmath/structures/BufferedNDAlgebra.kt | 3 +- .../kmath/structures/ComplexNDField.kt | 4 +- .../kmath/structures/ExtendedNDField.kt | 7 ++ .../kmath/structures/MemoryBuffer.kt | 1 - .../scientifik/kmath/structures/NDAlgebra.kt | 7 +- .../kmath/structures/NDStructure.kt | 77 ++++++++++++--- .../kmath/structures/RealNDField.kt | 4 +- .../kotlin/scientifik/memory/Memory.kt | 97 ++++++++++++++++--- .../kotlin/scientifik/memory/MemorySpec.kt | 17 +++- .../scientifik/memory/DataViewMemory.kt | 18 ++-- .../scientifik/memory/ByteBufferMemory.kt | 48 ++++----- 12 files changed, 217 insertions(+), 67 deletions(-) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BoxingNDField.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BoxingNDField.kt index 516f9a8cd..4cbb565c1 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BoxingNDField.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BoxingNDField.kt @@ -3,7 +3,6 @@ package scientifik.kmath.structures import scientifik.kmath.operations.Field import scientifik.kmath.operations.FieldElement - class BoxingNDField>( override val shape: IntArray, override val elementContext: F, diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BufferedNDAlgebra.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BufferedNDAlgebra.kt index baf1a05a6..06922c56f 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BufferedNDAlgebra.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BufferedNDAlgebra.kt @@ -11,7 +11,8 @@ interface BufferedNDAlgebra : NDAlgebra> { /** * 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 structure is already [NDBuffer], conversion is free. If not, it could be expensive because iteration over + * indices. * * If the argument is [NDBuffer] with different strides structure, the new element will be produced. */ diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ComplexNDField.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ComplexNDField.kt index 2f7d20b6d..be0b9e5c6 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ComplexNDField.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ComplexNDField.kt @@ -98,13 +98,13 @@ inline fun BufferedNDField.produceInline(crossinline init } /** - * Map one [ComplexNDElement] using function with indexes + * Map one [ComplexNDElement] using function with indices. */ inline fun ComplexNDElement.mapIndexed(crossinline transform: ComplexField.(index: IntArray, Complex) -> Complex): ComplexNDElement = context.produceInline { offset -> transform(strides.index(offset), buffer[offset]) } /** - * Map one [ComplexNDElement] using function without indexes + * Map one [ComplexNDElement] using function without indices. */ inline fun ComplexNDElement.map(crossinline transform: ComplexField.(Complex) -> Complex): ComplexNDElement { val buffer = Buffer.complex(strides.linearSize) { offset -> ComplexField.transform(buffer[offset]) } 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 1fe449632..24aa48c6b 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ExtendedNDField.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ExtendedNDField.kt @@ -2,6 +2,13 @@ package scientifik.kmath.structures import scientifik.kmath.operations.ExtendedField +/** + * [ExtendedField] over [NDStructure]. + * + * @param T the type of the element contained in ND structure. + * @param N the type of ND structure. + * @param F the extended field of structure elements. + */ interface ExtendedNDField, N : NDStructure> : NDField, ExtendedField ///** diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/MemoryBuffer.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/MemoryBuffer.kt index 70e4a8f0f..1d0c87580 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/MemoryBuffer.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/MemoryBuffer.kt @@ -10,7 +10,6 @@ import scientifik.memory.* * @property spec the spec of [T] type. */ open class MemoryBuffer(protected val memory: Memory, protected val spec: MemorySpec) : Buffer { - override val size: Int get() = memory.size / spec.objectSize private val reader: MemoryReader = memory.reader() diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/NDAlgebra.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/NDAlgebra.kt index f2805529c..f09db3c72 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/NDAlgebra.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/NDAlgebra.kt @@ -105,10 +105,11 @@ interface NDRing, N : NDStructure> : Ring, NDSpace } /** - * Field for n-dimensional structures. + * Field of [NDStructure]. * - * @param T the type of the element contained in ND structure - * @param F field of structure elements + * @param T the type of the element contained in ND structure. + * @param N the type of ND structure. + * @param F field of structure elements. */ interface NDField, N : NDStructure> : Field, NDRing { 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 bcd74ed2a..9d7735053 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/NDStructure.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/NDStructure.kt @@ -3,15 +3,38 @@ package scientifik.kmath.structures import kotlin.jvm.JvmName import kotlin.reflect.KClass - +/** + * Represents n-dimensional structure, i.e. multidimensional container of items of the same type and size. The number + * of dimensions and items in an array is defined by its shape, which is a sequence of non-negative integers that + * specify the sizes of each dimension. + * + * @param T the type of items. + */ interface NDStructure { - + /** + * The shape of structure, i.e. non-empty sequence of non-negative integers that specify sizes of dimensions of + * this structure. + */ val shape: IntArray + /** + * The count of dimensions in this structure. It should be equal to size of [shape]. + */ val dimension: Int get() = shape.size + /** + * Returns the value at the specified indices. + * + * @param index the indices. + * @return the value. + */ operator fun get(index: IntArray): T + /** + * Returns the sequence of all the elements associated by their indices. + * + * @return the lazy sequence of pairs of indices to values. + */ fun elements(): Sequence> override fun equals(other: Any?): Boolean @@ -19,6 +42,9 @@ interface NDStructure { override fun hashCode(): Int companion object { + /** + * Indicates whether some [NDStructure] is equal to another one. + */ fun equals(st1: NDStructure<*>, st2: NDStructure<*>): Boolean { if (st1 === st2) return true @@ -36,9 +62,9 @@ interface NDStructure { } /** - * Create a NDStructure with explicit buffer factory + * Creates a NDStructure with explicit buffer factory. * - * Strides should be reused if possible + * Strides should be reused if possible. */ fun build( strides: Strides, @@ -91,9 +117,24 @@ interface NDStructure { } } +/** + * Returns the value at the specified indices. + * + * @param index the indices. + * @return the value. + */ operator fun NDStructure.get(vararg index: Int): T = get(index) +/** + * Represents mutable [NDStructure]. + */ interface MutableNDStructure : NDStructure { + /** + * Inserts an item at the specified indices. + * + * @param index the indices. + * @param value the value. + */ operator fun set(index: IntArray, value: T) } @@ -104,7 +145,7 @@ inline fun MutableNDStructure.mapInPlace(action: (IntArray, T) -> T) { } /** - * A way to convert ND index to linear one and back + * A way to convert ND index to linear one and back. */ interface Strides { /** @@ -141,6 +182,9 @@ interface Strides { } } +/** + * Simple implementation of [Strides]. + */ class DefaultStrides private constructor(override val shape: IntArray) : Strides { /** * Strides for memory access @@ -180,19 +224,14 @@ class DefaultStrides private constructor(override val shape: IntArray) : Strides override val linearSize: Int get() = strides[shape.size] - override fun equals(other: Any?): Boolean { if (this === other) return true if (other !is DefaultStrides) return false - if (!shape.contentEquals(other.shape)) return false - return true } - override fun hashCode(): Int { - return shape.contentHashCode() - } + override fun hashCode(): Int = shape.contentHashCode() companion object { private val defaultStridesCache = HashMap() @@ -204,8 +243,20 @@ class DefaultStrides private constructor(override val shape: IntArray) : Strides } } +/** + * Represents [NDStructure] over [Buffer]. + * + * @param T the type of items. + */ abstract class NDBuffer : NDStructure { + /** + * The underlying buffer. + */ abstract val buffer: Buffer + + /** + * The strides to access elements of [Buffer] by linear indices. + */ abstract val strides: Strides override fun get(index: IntArray): T = buffer[strides.offset(index)] @@ -263,8 +314,8 @@ class MutableBufferNDStructure( ) : NDBuffer(), MutableNDStructure { init { - if (strides.linearSize != buffer.size) { - error("Expected buffer side of ${strides.linearSize}, but found ${buffer.size}") + require(strides.linearSize == buffer.size) { + "Expected buffer side of ${strides.linearSize}, but found ${buffer.size}" } } 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 b89f28233..e2a1a33df 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/RealNDField.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/RealNDField.kt @@ -93,13 +93,13 @@ inline fun BufferedNDField.produceInline(crossinline initiali } /** - * Map one [RealNDElement] using function with indexes + * Map one [RealNDElement] using function with indices. */ inline fun RealNDElement.mapIndexed(crossinline transform: RealField.(index: IntArray, Double) -> Double): RealNDElement = context.produceInline { offset -> transform(strides.index(offset), buffer[offset]) } /** - * Map one [RealNDElement] using function without indexes + * Map one [RealNDElement] using function without indices. */ inline fun RealNDElement.map(crossinline transform: RealField.(Double) -> Double): RealNDElement { val array = DoubleArray(strides.linearSize) { offset -> RealField.transform(buffer[offset]) } diff --git a/kmath-memory/src/commonMain/kotlin/scientifik/memory/Memory.kt b/kmath-memory/src/commonMain/kotlin/scientifik/memory/Memory.kt index 7c6886202..a749a7074 100644 --- a/kmath-memory/src/commonMain/kotlin/scientifik/memory/Memory.kt +++ b/kmath-memory/src/commonMain/kotlin/scientifik/memory/Memory.kt @@ -1,77 +1,148 @@ package scientifik.memory +/** + * Represents a display of certain memory structure. + */ interface Memory { + /** + * The length of this memory in bytes. + */ val size: Int /** - * Get a projection of this memory (it reflects the changes in the parent memory block) + * Get a projection of this memory (it reflects the changes in the parent memory block). */ fun view(offset: Int, length: Int): Memory /** - * Create a copy of this memory, which does not know anything about this memory + * Creates an independent copy of this memory. */ fun copy(): Memory /** - * Create and possibly register a new reader + * Gets or creates a reader of this memory. */ fun reader(): MemoryReader + /** + * Gets or creates a writer of this memory. + */ fun writer(): MemoryWriter - companion object { - - } + companion object } +/** + * The interface to read primitive types in this memory. + */ interface MemoryReader { + /** + * The underlying memory. + */ val memory: Memory + /** + * Reads [Double] at certain [offset]. + */ fun readDouble(offset: Int): Double + + /** + * Reads [Float] at certain [offset]. + */ fun readFloat(offset: Int): Float + + /** + * Reads [Byte] at certain [offset]. + */ fun readByte(offset: Int): Byte + + /** + * Reads [Short] at certain [offset]. + */ fun readShort(offset: Int): Short + + /** + * Reads [Int] at certain [offset]. + */ fun readInt(offset: Int): Int + + /** + * Reads [Long] at certain [offset]. + */ fun readLong(offset: Int): Long + /** + * Disposes this reader if needed. + */ fun release() } /** - * Use the memory for read then release the reader + * Uses the memory for read then releases the reader. */ inline fun Memory.read(block: MemoryReader.() -> Unit) { - reader().apply(block).apply { release() } + reader().apply(block).release() } +/** + * The interface to write primitive types into this memory. + */ interface MemoryWriter { + /** + * The underlying memory. + */ val memory: Memory + /** + * Writes [Double] at certain [offset]. + */ fun writeDouble(offset: Int, value: Double) + + /** + * Writes [Float] at certain [offset]. + */ fun writeFloat(offset: Int, value: Float) + + /** + * Writes [Byte] at certain [offset]. + */ fun writeByte(offset: Int, value: Byte) + + /** + * Writes [Short] at certain [offset]. + */ fun writeShort(offset: Int, value: Short) + + /** + * Writes [Int] at certain [offset]. + */ fun writeInt(offset: Int, value: Int) + + /** + * Writes [Long] at certain [offset]. + */ fun writeLong(offset: Int, value: Long) + /** + * Disposes this writer if needed. + */ fun release() } /** - * Use the memory for write then release the writer + * Uses the memory for write then releases the writer. */ inline fun Memory.write(block: MemoryWriter.() -> Unit) { - writer().apply(block).apply { release() } + writer().apply(block).release() } /** - * Allocate the most effective platform-specific memory + * Allocates the most effective platform-specific memory. */ expect fun Memory.Companion.allocate(length: Int): Memory /** - * Wrap a [Memory] around existing [ByteArray]. This operation is unsafe since the array is not copied - * and could be mutated independently from the resulting [Memory] + * Wraps a [Memory] around existing [ByteArray]. This operation is unsafe since the array is not copied + * and could be mutated independently from the resulting [Memory]. */ expect fun Memory.Companion.wrap(array: ByteArray): Memory diff --git a/kmath-memory/src/commonMain/kotlin/scientifik/memory/MemorySpec.kt b/kmath-memory/src/commonMain/kotlin/scientifik/memory/MemorySpec.kt index 4d0035d09..59a93f290 100644 --- a/kmath-memory/src/commonMain/kotlin/scientifik/memory/MemorySpec.kt +++ b/kmath-memory/src/commonMain/kotlin/scientifik/memory/MemorySpec.kt @@ -7,7 +7,7 @@ package scientifik.memory */ interface MemorySpec { /** - * Size of [T] in bytes after serialization + * Size of [T] in bytes after serialization. */ val objectSize: Int @@ -24,9 +24,19 @@ interface MemorySpec { fun MemoryWriter.write(offset: Int, value: T) } +/** + * Reads the object with [spec] starting from [offset]. + */ fun MemoryReader.read(spec: MemorySpec, offset: Int): T = with(spec) { read(offset) } + +/** + * Writes the object [value] with [spec] starting from [offset]. + */ fun MemoryWriter.write(spec: MemorySpec, offset: Int, value: T): Unit = with(spec) { write(offset, value) } +/** + * Reads array of [size] objects mapped by [spec] at certain [offset]. + */ inline fun MemoryReader.readArray(spec: MemorySpec, offset: Int, size: Int): Array = Array(size) { i -> spec.run { @@ -34,7 +44,10 @@ inline fun MemoryReader.readArray(spec: MemorySpec, offset: } } +/** + * Writes [array] of objects mapped by [spec] at certain [offset]. + */ fun MemoryWriter.writeArray(spec: MemorySpec, offset: Int, array: Array): Unit = with(spec) { array.indices.forEach { i -> write(offset + i * objectSize, array[i]) } } -//TODO It is possible to add elastic MemorySpec with unknown object size \ No newline at end of file +// TODO It is possible to add elastic MemorySpec with unknown object size diff --git a/kmath-memory/src/jsMain/kotlin/scientifik/memory/DataViewMemory.kt b/kmath-memory/src/jsMain/kotlin/scientifik/memory/DataViewMemory.kt index 38ec14824..974750502 100644 --- a/kmath-memory/src/jsMain/kotlin/scientifik/memory/DataViewMemory.kt +++ b/kmath-memory/src/jsMain/kotlin/scientifik/memory/DataViewMemory.kt @@ -4,13 +4,13 @@ import org.khronos.webgl.ArrayBuffer import org.khronos.webgl.DataView import org.khronos.webgl.Int8Array -class DataViewMemory(val view: DataView) : Memory { - +private class DataViewMemory(val view: DataView) : Memory { override val size: Int get() = view.byteLength override fun view(offset: Int, length: Int): Memory { require(offset >= 0) { "offset shouldn't be negative: $offset" } require(length >= 0) { "length shouldn't be negative: $length" } + require(offset + length <= size) { "Can't view memory outside the parent region." } if (offset + length > size) throw IndexOutOfBoundsException("offset + length > size: $offset + $length > $size") @@ -33,11 +33,11 @@ class DataViewMemory(val view: DataView) : Memory { override fun readInt(offset: Int): Int = view.getInt32(offset, false) - override fun readLong(offset: Int): Long = (view.getInt32(offset, false).toLong() shl 32) or - view.getInt32(offset + 4, false).toLong() + override fun readLong(offset: Int): Long = + view.getInt32(offset, false).toLong() shl 32 or view.getInt32(offset + 4, false).toLong() override fun release() { - // does nothing on JS because of GC + // does nothing on JS } } @@ -72,7 +72,7 @@ class DataViewMemory(val view: DataView) : Memory { } override fun release() { - //does nothing on JS + // does nothing on JS } } @@ -81,13 +81,17 @@ class DataViewMemory(val view: DataView) : Memory { } /** - * Allocate the most effective platform-specific memory + * Allocates memory based on a [DataView]. */ actual fun Memory.Companion.allocate(length: Int): Memory { val buffer = ArrayBuffer(length) return DataViewMemory(DataView(buffer, 0, length)) } +/** + * Wraps a [Memory] around existing [ByteArray]. This operation is unsafe since the array is not copied + * and could be mutated independently from the resulting [Memory]. + */ actual fun Memory.Companion.wrap(array: ByteArray): Memory { @Suppress("CAST_NEVER_SUCCEEDS") val int8Array = array as Int8Array return DataViewMemory(DataView(int8Array.buffer, int8Array.byteOffset, int8Array.length)) diff --git a/kmath-memory/src/jvmMain/kotlin/scientifik/memory/ByteBufferMemory.kt b/kmath-memory/src/jvmMain/kotlin/scientifik/memory/ByteBufferMemory.kt index 9ec2b3a09..b5a0dd51b 100644 --- a/kmath-memory/src/jvmMain/kotlin/scientifik/memory/ByteBufferMemory.kt +++ b/kmath-memory/src/jvmMain/kotlin/scientifik/memory/ByteBufferMemory.kt @@ -6,19 +6,18 @@ import java.nio.file.Files import java.nio.file.Path import java.nio.file.StandardOpenOption - private class ByteBufferMemory( val buffer: ByteBuffer, val startOffset: Int = 0, override val size: Int = buffer.limit() ) : Memory { - - @Suppress("NOTHING_TO_INLINE") private inline fun position(o: Int): Int = startOffset + o override fun view(offset: Int, length: Int): Memory { - if (offset + length > size) error("Selecting a Memory view outside of memory range") + require(offset >= 0) { "offset shouldn't be negative: $offset" } + require(length >= 0) { "length shouldn't be negative: $length" } + require(offset + length <= size) { "Can't view memory outside the parent region." } return ByteBufferMemory(buffer, position(offset), length) } @@ -28,10 +27,9 @@ private class ByteBufferMemory( copy.put(buffer) copy.flip() return ByteBufferMemory(copy) - } - private val reader = object : MemoryReader { + private val reader: MemoryReader = object : MemoryReader { override val memory: Memory get() = this@ByteBufferMemory override fun readDouble(offset: Int) = buffer.getDouble(position(offset)) @@ -47,13 +45,13 @@ private class ByteBufferMemory( override fun readLong(offset: Int) = buffer.getLong(position(offset)) override fun release() { - //does nothing on JVM + // does nothing on JVM } } override fun reader(): MemoryReader = reader - private val writer = object : MemoryWriter { + private val writer: MemoryWriter = object : MemoryWriter { override val memory: Memory get() = this@ByteBufferMemory override fun writeDouble(offset: Int, value: Double) { @@ -81,7 +79,7 @@ private class ByteBufferMemory( } override fun release() { - //does nothing on JVM + // does nothing on JVM } } @@ -89,26 +87,32 @@ private class ByteBufferMemory( } /** - * Allocate the most effective platform-specific memory + * Allocates memory based on a [ByteBuffer]. */ -actual fun Memory.Companion.allocate(length: Int): Memory { - val buffer = ByteBuffer.allocate(length) - return ByteBufferMemory(buffer) -} +actual fun Memory.Companion.allocate(length: Int): Memory = + ByteBufferMemory(checkNotNull(ByteBuffer.allocate(length))) -actual fun Memory.Companion.wrap(array: ByteArray): Memory { - val buffer = ByteBuffer.wrap(array) - return ByteBufferMemory(buffer) -} +/** + * Wraps a [Memory] around existing [ByteArray]. This operation is unsafe since the array is not copied + * and could be mutated independently from the resulting [Memory]. + */ +actual fun Memory.Companion.wrap(array: ByteArray): Memory = ByteBufferMemory(checkNotNull(ByteBuffer.wrap(array))) +/** + * Wraps this [ByteBuffer] to [Memory] object. + * + * @receiver the byte buffer. + * @param startOffset the start offset. + * @param size the size of memory to map. + * @return the [Memory] object. + */ fun ByteBuffer.asMemory(startOffset: Int = 0, size: Int = limit()): Memory = ByteBufferMemory(this, startOffset, size) /** - * Use direct memory-mapped buffer from file to read something and close it afterwards. + * Uses direct memory-mapped buffer from file to read something and close it afterwards. */ -fun Path.readAsMemory(position: Long = 0, size: Long = Files.size(this), block: Memory.() -> R): R { - return FileChannel.open(this, StandardOpenOption.READ).use { +fun Path.readAsMemory(position: Long = 0, size: Long = Files.size(this), block: Memory.() -> R): R = + FileChannel.open(this, StandardOpenOption.READ).use { ByteBufferMemory(it.map(FileChannel.MapMode.READ_ONLY, position, size)).block() } -} \ No newline at end of file -- 2.34.1 From cb5234a334d2ffe07403d6ac3b553fd388f54619 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Sat, 8 Aug 2020 04:57:20 +0700 Subject: [PATCH 151/156] Document AST and expressions API, implement ExtendedField over MST and Expression --- .../kotlin/scientifik/kmath/ast/MST.kt | 47 ++++++++---- .../kotlin/scientifik/kmath/ast/MstAlgebra.kt | 74 +++++++++++++------ .../scientifik/kmath/ast/MstExpression.kt | 65 ++++++++++++---- .../kotlin/scientifik/kmath/ast/parser.kt | 13 ++++ .../scientifik/kmath/expressions/Builders.kt | 8 ++ .../kmath/expressions/Expression.kt | 12 +++ .../FunctionalExpressionAlgebra.kt | 39 ++++++++-- .../scientifik/kmath/streaming/RingBuffer.kt | 2 +- 8 files changed, 202 insertions(+), 58 deletions(-) diff --git a/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MST.kt b/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MST.kt index 46558cbfb..0e8151c04 100644 --- a/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MST.kt +++ b/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MST.kt @@ -5,42 +5,54 @@ import scientifik.kmath.operations.NumericAlgebra import scientifik.kmath.operations.RealField /** - * A Mathematical Syntax Tree node for mathematical expressions + * A Mathematical Syntax Tree node for mathematical expressions. */ sealed class MST { - /** - * A node containing unparsed string + * A node containing raw string. + * + * @property value the value of this node. */ data class Symbolic(val value: String) : MST() /** - * A node containing a number + * A node containing a numeric value or scalar. + * + * @property value the value of this number. */ data class Numeric(val value: Number) : MST() /** - * A node containing an unary operation + * A node containing an unary operation. + * + * @property operation the identifier of operation. + * @property value the argument of this operation. */ data class Unary(val operation: String, val value: MST) : MST() { - companion object { - const val ABS_OPERATION = "abs" - //TODO add operations - } + companion object } /** - * A node containing binary operation + * A node containing binary operation. + * + * @property operation the identifier operation. + * @property left the left operand. + * @property right the right operand. */ data class Binary(val operation: String, val left: MST, val right: MST) : MST() { companion object } } -//TODO add a function with positional arguments - -//TODO add a function with named arguments +// TODO add a function with named arguments +/** + * Interprets the [MST] node with this [Algebra]. + * + * @receiver the algebra that provides operations. + * @param node the node to evaluate. + * @return the value of expression. + */ fun Algebra.evaluate(node: MST): T = when (node) { is MST.Numeric -> (this as? NumericAlgebra)?.number(node.value) ?: error("Numeric nodes are not supported by $this") @@ -65,4 +77,11 @@ fun Algebra.evaluate(node: MST): T = when (node) { } } -fun MST.compile(algebra: Algebra): T = algebra.evaluate(this) +/** + * Interprets the [MST] node with this [Algebra]. + * + * @receiver the node to evaluate. + * @param algebra the algebra that provides operations. + * @return the value of expression. + */ +fun MST.interpret(algebra: Algebra): T = algebra.evaluate(this) diff --git a/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MstAlgebra.kt b/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MstAlgebra.kt index 007cf57c4..b47c7cae8 100644 --- a/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MstAlgebra.kt +++ b/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MstAlgebra.kt @@ -2,6 +2,9 @@ package scientifik.kmath.ast import scientifik.kmath.operations.* +/** + * [Algebra] over [MST] nodes. + */ object MstAlgebra : NumericAlgebra { override fun number(value: Number): MST = MST.Numeric(value) @@ -14,17 +17,16 @@ object MstAlgebra : NumericAlgebra { MST.Binary(operation, left, right) } +/** + * [Space] over [MST] nodes. + */ object MstSpace : Space, NumericAlgebra { override val zero: MST = number(0.0) override fun number(value: Number): MST = MstAlgebra.number(value) override fun symbol(value: String): MST = MstAlgebra.symbol(value) - - override fun add(a: MST, b: MST): MST = - binaryOperation(SpaceOperations.PLUS_OPERATION, a, b) - - override fun multiply(a: MST, k: Number): MST = - binaryOperation(RingOperations.TIMES_OPERATION, a, number(k)) + override fun add(a: MST, b: MST): MST = binaryOperation(SpaceOperations.PLUS_OPERATION, a, b) + override fun multiply(a: MST, k: Number): MST = binaryOperation(RingOperations.TIMES_OPERATION, a, number(k)) override fun binaryOperation(operation: String, left: MST, right: MST): MST = MstAlgebra.binaryOperation(operation, left, right) @@ -32,41 +34,69 @@ object MstSpace : Space, NumericAlgebra { override fun unaryOperation(operation: String, arg: MST): MST = MstAlgebra.unaryOperation(operation, arg) } +/** + * [Ring] over [MST] nodes. + */ object MstRing : Ring, NumericAlgebra { override val zero: MST = number(0.0) override val one: MST = number(1.0) - override fun number(value: Number): MST = MstAlgebra.number(value) - override fun symbol(value: String): MST = MstAlgebra.symbol(value) - override fun add(a: MST, b: MST): MST = binaryOperation(SpaceOperations.PLUS_OPERATION, a, b) + override fun number(value: Number): MST = MstSpace.number(value) + override fun symbol(value: String): MST = MstSpace.symbol(value) + override fun add(a: MST, b: MST): MST = MstSpace.add(a, b) - override fun multiply(a: MST, k: Number): MST = - binaryOperation(RingOperations.TIMES_OPERATION, a, MstSpace.number(k)) + override fun multiply(a: MST, k: Number): MST = MstSpace.multiply(a, k) override fun multiply(a: MST, b: MST): MST = binaryOperation(RingOperations.TIMES_OPERATION, a, b) override fun binaryOperation(operation: String, left: MST, right: MST): MST = - MstAlgebra.binaryOperation(operation, left, right) + MstSpace.binaryOperation(operation, left, right) override fun unaryOperation(operation: String, arg: MST): MST = MstAlgebra.unaryOperation(operation, arg) } +/** + * [Field] over [MST] nodes. + */ object MstField : Field { override val zero: MST = number(0.0) override val one: MST = number(1.0) - override fun symbol(value: String): MST = MstAlgebra.symbol(value) - override fun number(value: Number): MST = MstAlgebra.number(value) - override fun add(a: MST, b: MST): MST = binaryOperation(SpaceOperations.PLUS_OPERATION, a, b) - - override fun multiply(a: MST, k: Number): MST = - binaryOperation(RingOperations.TIMES_OPERATION, a, MstSpace.number(k)) - - override fun multiply(a: MST, b: MST): MST = binaryOperation(RingOperations.TIMES_OPERATION, a, b) + override fun symbol(value: String): MST = MstRing.symbol(value) + override fun number(value: Number): MST = MstRing.number(value) + override fun add(a: MST, b: MST): MST = MstRing.add(a, b) + override fun multiply(a: MST, k: Number): MST = MstRing.multiply(a, k) + override fun multiply(a: MST, b: MST): MST = MstRing.multiply(a, b) override fun divide(a: MST, b: MST): MST = binaryOperation(FieldOperations.DIV_OPERATION, a, b) override fun binaryOperation(operation: String, left: MST, right: MST): MST = - MstAlgebra.binaryOperation(operation, left, right) + MstRing.binaryOperation(operation, left, right) - override fun unaryOperation(operation: String, arg: MST): MST = MstAlgebra.unaryOperation(operation, arg) + override fun unaryOperation(operation: String, arg: MST): MST = MstRing.unaryOperation(operation, arg) +} + +/** + * [ExtendedField] over [MST] nodes. + */ +object MstExtendedField : ExtendedField { + override val zero: MST = number(0.0) + override val one: MST = number(1.0) + + override fun sin(arg: MST): MST = unaryOperation(TrigonometricOperations.SIN_OPERATION, arg) + override fun cos(arg: MST): MST = unaryOperation(TrigonometricOperations.COS_OPERATION, arg) + override fun asin(arg: MST): MST = unaryOperation(InverseTrigonometricOperations.ASIN_OPERATION, arg) + override fun acos(arg: MST): MST = unaryOperation(InverseTrigonometricOperations.ACOS_OPERATION, arg) + override fun atan(arg: MST): MST = unaryOperation(InverseTrigonometricOperations.ATAN_OPERATION, arg) + override fun add(a: MST, b: MST): MST = MstField.add(a, b) + override fun multiply(a: MST, k: Number): MST = MstField.multiply(a, k) + override fun multiply(a: MST, b: MST): MST = MstField.multiply(a, b) + override fun divide(a: MST, b: MST): MST = MstField.divide(a, b) + override fun power(arg: MST, pow: Number): MST = binaryOperation(PowerOperations.POW_OPERATION, arg, number(pow)) + override fun exp(arg: MST): MST = unaryOperation(ExponentialOperations.EXP_OPERATION, arg) + override fun ln(arg: MST): MST = unaryOperation(ExponentialOperations.LN_OPERATION, arg) + + override fun binaryOperation(operation: String, left: MST, right: MST): MST = + MstField.binaryOperation(operation, left, right) + + override fun unaryOperation(operation: String, arg: MST): MST = MstField.unaryOperation(operation, arg) } diff --git a/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MstExpression.kt b/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MstExpression.kt index 1468c3ad4..59f3f15d8 100644 --- a/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MstExpression.kt +++ b/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MstExpression.kt @@ -1,19 +1,16 @@ package scientifik.kmath.ast -import scientifik.kmath.expressions.Expression -import scientifik.kmath.expressions.FunctionalExpressionField -import scientifik.kmath.expressions.FunctionalExpressionRing -import scientifik.kmath.expressions.FunctionalExpressionSpace +import scientifik.kmath.expressions.* import scientifik.kmath.operations.* /** - * The expression evaluates MST on-flight. Should be much faster than functional expression, but slower than ASM-generated expressions. + * The expression evaluates MST on-flight. Should be much faster than functional expression, but slower than + * ASM-generated expressions. + * + * @property algebra the algebra that provides operations. + * @property mst the [MST] node. */ class MstExpression(val algebra: Algebra, val mst: MST) : Expression { - - /** - * Substitute algebra raw value - */ private inner class InnerAlgebra(val arguments: Map) : NumericAlgebra { override fun symbol(value: String): T = arguments[value] ?: algebra.symbol(value) override fun unaryOperation(operation: String, arg: T): T = algebra.unaryOperation(operation, arg) @@ -30,26 +27,62 @@ class MstExpression(val algebra: Algebra, val mst: MST) : Expression { override fun invoke(arguments: Map): T = InnerAlgebra(arguments).evaluate(mst) } - +/** + * Builds [MstExpression] over [Algebra]. + */ inline fun , E : Algebra> A.mst( mstAlgebra: E, block: E.() -> MST ): MstExpression = MstExpression(this, mstAlgebra.block()) +/** + * Builds [MstExpression] over [Space]. + */ inline fun Space.mstInSpace(block: MstSpace.() -> MST): MstExpression = MstExpression(this, MstSpace.block()) +/** + * Builds [MstExpression] over [Ring]. + */ inline fun Ring.mstInRing(block: MstRing.() -> MST): MstExpression = MstExpression(this, MstRing.block()) +/** + * Builds [MstExpression] over [Field]. + */ inline fun Field.mstInField(block: MstField.() -> MST): MstExpression = MstExpression(this, MstField.block()) -inline fun > FunctionalExpressionSpace.mstInSpace(block: MstSpace.() -> MST): MstExpression = - algebra.mstInSpace(block) +/** + * Builds [MstExpression] over [ExtendedField]. + */ +inline fun Field.mstInExtendedField(block: MstExtendedField.() -> MST): MstExpression = + MstExpression(this, MstExtendedField.block()) -inline fun > FunctionalExpressionRing.mstInRing(block: MstRing.() -> MST): MstExpression = - algebra.mstInRing(block) +/** + * Builds [MstExpression] over [FunctionalExpressionSpace]. + */ +inline fun > FunctionalExpressionSpace.mstInSpace( + block: MstSpace.() -> MST +): MstExpression = algebra.mstInSpace(block) -inline fun > FunctionalExpressionField.mstInField(block: MstField.() -> MST): MstExpression = - algebra.mstInField(block) \ No newline at end of file +/** + * Builds [MstExpression] over [FunctionalExpressionRing]. + */ +inline fun > FunctionalExpressionRing.mstInRing( + block: MstRing.() -> MST +): MstExpression = algebra.mstInRing(block) + +/** + * Builds [MstExpression] over [FunctionalExpressionField]. + */ +inline fun > FunctionalExpressionField.mstInField( + block: MstField.() -> MST +): MstExpression = algebra.mstInField(block) + +/** + * Builds [MstExpression] over [FunctionalExpressionExtendedField]. + */ +inline fun > FunctionalExpressionExtendedField.mstInExtendedField( + block: MstExtendedField.() -> MST +): MstExpression = algebra.mstInExtendedField(block) diff --git a/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/parser.kt b/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/parser.kt index 11797d79f..cba335a8d 100644 --- a/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/parser.kt +++ b/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/parser.kt @@ -80,5 +80,18 @@ object ArithmeticsEvaluator : Grammar() { override val rootParser: Parser by subSumChain } +/** + * Tries to parse the string into [MST]. + * + * @receiver the string to parse. + * @return the [MST] node. + */ fun String.tryParseMath(): ParseResult = ArithmeticsEvaluator.tryParseToEnd(this) + +/** + * Parses the string into [MST]. + * + * @receiver the string to parse. + * @return the [MST] node. + */ fun String.parseMath(): MST = ArithmeticsEvaluator.parseToEnd(this) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/Builders.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/Builders.kt index 834ef9e24..8cd6e28f8 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/Builders.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/Builders.kt @@ -1,5 +1,6 @@ package scientifik.kmath.expressions +import scientifik.kmath.operations.ExtendedField import scientifik.kmath.operations.Field import scientifik.kmath.operations.Ring import scientifik.kmath.operations.Space @@ -21,3 +22,10 @@ fun Ring.ringExpression(block: FunctionalExpressionRing>.() -> */ fun Field.fieldExpression(block: FunctionalExpressionField>.() -> Expression): Expression = FunctionalExpressionField(this).run(block) + +/** + * Creates a functional expression with this [ExtendedField]. + */ +fun ExtendedField.fieldExpression( + block: FunctionalExpressionExtendedField>.() -> Expression +): Expression = FunctionalExpressionExtendedField(this).run(block) 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 b21a414f5..380822f78 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/Expression.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/Expression.kt @@ -6,6 +6,12 @@ import scientifik.kmath.operations.Algebra * An elementary function that could be invoked on a map of arguments */ interface Expression { + /** + * Calls this expression from arguments. + * + * @param arguments the map of arguments. + * @return the value. + */ operator fun invoke(arguments: Map): T companion object @@ -19,6 +25,12 @@ fun Algebra.expression(block: Algebra.(arguments: Map) -> T override fun invoke(arguments: Map): T = block(arguments) } +/** + * Calls this expression from arguments. + * + * @param pairs the pair of arguments' names to values. + * @return the value. + */ operator fun Expression.invoke(vararg pairs: Pair): T = invoke(mapOf(*pairs)) /** diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressionAlgebra.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressionAlgebra.kt index 5a8a10717..dd5fb572a 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressionAlgebra.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressionAlgebra.kt @@ -40,7 +40,6 @@ internal class FunctionalConstProductExpression( * @param algebra The algebra to provide for Expressions built. */ abstract class FunctionalExpressionAlgebra>(val algebra: A) : ExpressionAlgebra> { - /** * Builds an Expression of constant expression which does not depend on arguments. */ @@ -69,14 +68,13 @@ abstract class FunctionalExpressionAlgebra>(val algebra: A) : */ open class FunctionalExpressionSpace>(algebra: A) : FunctionalExpressionAlgebra(algebra), Space> { - override val zero: Expression get() = const(algebra.zero) /** * Builds an Expression of addition of two another expressions. */ override fun add(a: Expression, b: Expression): Expression = - FunctionalBinaryOperation(algebra, SpaceOperations.PLUS_OPERATION, a, b) + binaryOperation(SpaceOperations.PLUS_OPERATION, a, b) /** * Builds an Expression of multiplication of expression by number. @@ -105,7 +103,7 @@ open class FunctionalExpressionRing(algebra: A) : FunctionalExpressionSpac * Builds an Expression of multiplication of two expressions. */ override fun multiply(a: Expression, b: Expression): Expression = - FunctionalBinaryOperation(algebra, RingOperations.TIMES_OPERATION, a, b) + binaryOperation(RingOperations.TIMES_OPERATION, a, b) operator fun Expression.times(arg: T): Expression = this * const(arg) operator fun T.times(arg: Expression): Expression = arg * this @@ -124,7 +122,7 @@ open class FunctionalExpressionField(algebra: A) : * Builds an Expression of division an expression by another one. */ override fun divide(a: Expression, b: Expression): Expression = - FunctionalBinaryOperation(algebra, FieldOperations.DIV_OPERATION, a, b) + binaryOperation(FieldOperations.DIV_OPERATION, a, b) operator fun Expression.div(arg: T): Expression = this / const(arg) operator fun T.div(arg: Expression): Expression = arg / this @@ -136,6 +134,34 @@ open class FunctionalExpressionField(algebra: A) : super.binaryOperation(operation, left, right) } +open class FunctionalExpressionExtendedField(algebra: A) : + FunctionalExpressionField(algebra), + ExtendedField> where A : ExtendedField, A : NumericAlgebra { + override fun sin(arg: Expression): Expression = unaryOperation(TrigonometricOperations.SIN_OPERATION, arg) + override fun cos(arg: Expression): Expression = unaryOperation(TrigonometricOperations.COS_OPERATION, arg) + + override fun asin(arg: Expression): Expression = + unaryOperation(InverseTrigonometricOperations.ASIN_OPERATION, arg) + + override fun acos(arg: Expression): Expression = + unaryOperation(InverseTrigonometricOperations.ACOS_OPERATION, arg) + + override fun atan(arg: Expression): Expression = + unaryOperation(InverseTrigonometricOperations.ATAN_OPERATION, arg) + + override fun power(arg: Expression, pow: Number): Expression = + binaryOperation(PowerOperations.POW_OPERATION, arg, number(pow)) + + override fun exp(arg: Expression): Expression = unaryOperation(ExponentialOperations.EXP_OPERATION, arg) + override fun ln(arg: Expression): Expression = unaryOperation(ExponentialOperations.LN_OPERATION, arg) + + override fun unaryOperation(operation: String, arg: Expression): Expression = + super.unaryOperation(operation, arg) + + override fun binaryOperation(operation: String, left: Expression, right: Expression): Expression = + super.binaryOperation(operation, left, right) +} + inline fun > A.expressionInSpace(block: FunctionalExpressionSpace.() -> Expression): Expression = FunctionalExpressionSpace(this).block() @@ -144,3 +170,6 @@ inline fun > A.expressionInRing(block: FunctionalExpressionRing> A.expressionInField(block: FunctionalExpressionField.() -> Expression): Expression = FunctionalExpressionField(this).block() + +inline fun > A.expressionInExtendedField(block: FunctionalExpressionExtendedField.() -> Expression): Expression = + FunctionalExpressionExtendedField(this).block() diff --git a/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/streaming/RingBuffer.kt b/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/streaming/RingBuffer.kt index 6b99e34ff..fca9ffb9f 100644 --- a/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/streaming/RingBuffer.kt +++ b/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/streaming/RingBuffer.kt @@ -11,7 +11,7 @@ import kotlin.reflect.KClass * Thread-safe ring buffer */ @Suppress("UNCHECKED_CAST") -internal class RingBuffer( +class RingBuffer( private val buffer: MutableBuffer, private var startIndex: Int = 0, size: Int = 0 -- 2.34.1 From 6114099e68fad71e169ec86ce4dac8925fe9e6da Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Sat, 8 Aug 2020 15:51:04 +0700 Subject: [PATCH 152/156] Edit doc files, update readmes, document coroutines API --- doc/algebra.md | 124 ++++++++++-------- doc/buffers.md | 4 +- doc/codestyle.md | 34 +++-- doc/linear.md | 2 +- doc/nd-structure.md | 2 +- kmath-ast/README.md | 8 +- kmath-core/README.md | 40 ++++++ kmath-core/build.gradle.kts | 8 +- .../scientifik/kmath/operations/Algebra.kt | 9 +- .../kmath/chains/BlockingIntChain.kt | 2 +- .../kmath/chains/BlockingRealChain.kt | 2 +- .../kotlin/scientifik/kmath/chains/Chain.kt | 21 ++- .../scientifik/kmath/chains/flowExtra.kt | 2 +- .../kmath/coroutines/coroutinesExtra.kt | 13 +- .../scientifik/kmath/streaming/BufferFlow.kt | 4 +- .../scientifik/kmath/streaming/RingBuffer.kt | 8 +- .../scientifik/kmath/chains/ChainExt.kt | 2 +- .../kmath/structures/LazyNDStructure.kt | 21 ++- .../kmath/streaming/BufferFlowTest.kt | 5 +- .../kmath/streaming/RingBufferTest.kt | 12 +- 20 files changed, 189 insertions(+), 134 deletions(-) create mode 100644 kmath-core/README.md diff --git a/doc/algebra.md b/doc/algebra.md index 015f4fc82..b1b77a31f 100644 --- a/doc/algebra.md +++ b/doc/algebra.md @@ -1,110 +1,124 @@ -# Algebra and algebra elements +# Algebraic Structures and Algebraic Elements -The mathematical operations in `kmath` are generally separated from mathematical objects. -This means that in order to perform an operation, say `+`, one needs two objects of a type `T` and -and algebra context which defines appropriate operation, say `Space`. Next one needs to run actual operation -in the context: +The mathematical operations in KMath are generally separated from mathematical objects. This means that to perform an +operation, say `+`, one needs two objects of a type `T` and an algebra context, which draws appropriate operation up, +say `Space`. Next one needs to run the actual operation in the context: ```kotlin -val a: T -val b: T -val space: Space +import scientifik.kmath.operations.* -val c = space.run{a + b} +val a: T = ... +val b: T = ... +val space: Space = ... + +val c = space { a + b } ``` -From the first glance, this distinction seems to be a needless complication, but in fact one needs -to remember that in mathematics, one could define different operations on the same objects. For example, -one could use different types of geometry for vectors. +At first glance, this distinction seems to be a needless complication, but in fact one needs to remember that in +mathematics, one could draw up different operations on same objects. For example, one could use different types of +geometry for vectors. -## Algebra hierarchy +## Algebraic Structures Mathematical contexts have the following hierarchy: -**Space** <- **Ring** <- **Field** +**Algebra** ← **Space** ← **Ring** ← **Field** -All classes follow abstract mathematical constructs. -[Space](http://mathworld.wolfram.com/Space.html) defines `zero` element, addition operation and multiplication by constant, -[Ring](http://mathworld.wolfram.com/Ring.html) adds multiplication and unit `one` element, -[Field](http://mathworld.wolfram.com/Field.html) adds division operation. +These interfaces follow real algebraic structures: -Typical case of `Field` is the `RealField` which works on doubles. And typical case of `Space` is a `VectorSpace`. +- [Space](https://mathworld.wolfram.com/VectorSpace.html) defines addition, its neutral element (i.e. 0) and scalar +multiplication; +- [Ring](http://mathworld.wolfram.com/Ring.html) adds multiplication and its neutral element (i.e. 1); +- [Field](http://mathworld.wolfram.com/Field.html) adds division operation. -In some cases algebra context could hold additional operation like `exp` or `sin`, in this case it inherits appropriate -interface. Also a context could have an operation which produces an element outside of its context. For example -`Matrix` `dot` operation produces a matrix with new dimensions which can be incompatible with initial matrix in -terms of linear operations. +A typical implementation of `Field` is the `RealField` which works on doubles, and `VectorSpace` for `Space`. -## Algebra element +In some cases algebra context can hold additional operations like `exp` or `sin`, and then it inherits appropriate +interface. Also, contexts may have operations, which produce elements outside of the context. For example, `Matrix.dot` +operation produces a matrix with new dimensions, which can be incompatible with initial matrix in terms of linear +operations. -In order to achieve more familiar behavior (where you apply operations directly to mathematical objects), without involving contexts -`kmath` introduces special type objects called `MathElement`. A `MathElement` is basically some object coupled to +## Algebraic Element + +To achieve more familiar behavior (where you apply operations directly to mathematical objects), without involving +contexts KMath submits special type objects called `MathElement`. A `MathElement` is basically some object coupled to a mathematical context. For example `Complex` is the pair of real numbers representing real and imaginary parts, -but it also holds reference to the `ComplexField` singleton which allows to perform direct operations on `Complex` +but it also holds reference to the `ComplexField` singleton, which allows performing direct operations on `Complex` numbers without explicit involving the context like: ```kotlin - val c1 = Complex(1.0, 1.0) - val c2 = Complex(1.0, -1.0) - val c3 = c1 + c2 + 3.0.toComplex() - //or with field notation: - val c4 = ComplexField.run{c1 + i - 2.0} +import scientifik.kmath.operations.* + +// Using elements +val c1 = Complex(1.0, 1.0) +val c2 = Complex(1.0, -1.0) +val c3 = c1 + c2 + 3.0.toComplex() + +// Using context +val c4 = ComplexField { c1 + i - 2.0 } ``` Both notations have their pros and cons. -The hierarchy for algebra elements follows the hierarchy for the corresponding algebra. +The hierarchy for algebraic elements follows the hierarchy for the corresponding algebraic structures. -**MathElement** <- **SpaceElement** <- **RingElement** <- **FieldElement** +**MathElement** ← **SpaceElement** ← **RingElement** ← **FieldElement** -**MathElement** is the generic common ancestor of the class with context. +`MathElement` is the generic common ancestor of the class with context. -One important distinction between algebra elements and algebra contexts is that algebra element has three type parameters: +One major distinction between algebraic elements and algebraic contexts is that elements have three type +parameters: -1. The type of elements, field operates on. -2. The self-type of the element returned from operation (must be algebra element). +1. The type of elements, the field operates on. +2. The self-type of the element returned from operation (which has to be an algebraic element). 3. The type of the algebra over first type-parameter. -The middle type is needed in case algebra members do not store context. For example, it is not possible to add -a context to regular `Double`. The element performs automatic conversions from context types and back. -One should used context operations in all important places. The performance of element operations is not guaranteed. +The middle type is needed for of algebra members do not store context. For example, it is impossible to add a context +to regular `Double`. The element performs automatic conversions from context types and back. One should use context +operations in all performance-critical places. The performance of element operations is not guaranteed. -## Spaces and fields +## Spaces and Fields -An obvious first choice of mathematical objects to implement in a context-oriented style are algebraic elements like spaces, -rings and fields. Those are located in the `scientifik.kmath.operations.Algebra.kt` file. Alongside common contexts, the file includes definitions for algebra elements like `FieldElement`. A `FieldElement` object -stores a reference to the `Field` which contains additive and multiplicative operations, meaning -it has one fixed context attached and does not require explicit external context. So those `MathElements` can be operated without context: +KMath submits both contexts and elements for builtin algebraic structures: ```kotlin +import scientifik.kmath.operations.* + val c1 = Complex(1.0, 2.0) val c2 = ComplexField.i + val c3 = c1 + c2 +// or +val c3 = ComplexField { c1 + c2 } ``` -`ComplexField` also features special operations to mix complex and real numbers, for example: +Also, `ComplexField` features special operations to mix complex and real numbers, for example: ```kotlin +import scientifik.kmath.operations.* + val c1 = Complex(1.0, 2.0) -val c2 = ComplexField.run{ c1 - 1.0} // Returns: [re:0.0, im: 2.0] -val c3 = ComplexField.run{ c1 - i*2.0} +val c2 = ComplexField { c1 - 1.0 } // Returns: Complex(re=0.0, im=2.0) +val c3 = ComplexField { c1 - i * 2.0 } ``` -**Note**: In theory it is possible to add behaviors directly to the context, but currently kotlin syntax does not support -that. Watch [KT-10468](https://youtrack.jetbrains.com/issue/KT-10468) and [KEEP-176](https://github.com/Kotlin/KEEP/pull/176) for updates. +**Note**: In theory it is possible to add behaviors directly to the context, but as for now Kotlin does not support +that. Watch [KT-10468](https://youtrack.jetbrains.com/issue/KT-10468) and +[KEEP-176](https://github.com/Kotlin/KEEP/pull/176) for updates. ## Nested fields -Contexts allow one to build more complex structures. For example, it is possible to create a `Matrix` from complex elements like so: +Contexts allow one to build more complex structures. For example, it is possible to create a `Matrix` from complex +elements like so: ```kotlin -val element = NDElement.complex(shape = intArrayOf(2,2)){ index: IntArray -> +val element = NDElement.complex(shape = intArrayOf(2, 2)) { index: IntArray -> Complex(index[0].toDouble() - index[1].toDouble(), index[0].toDouble() + index[1].toDouble()) } ``` -The `element` in this example is a member of the `Field` of 2-d structures, each element of which is a member of its own -`ComplexField`. The important thing is one does not need to create a special n-d class to hold complex +The `element` in this example is a member of the `Field` of 2D structures, each element of which is a member of its own +`ComplexField`. It is important one does not need to create a special n-d class to hold complex numbers and implement operations on it, one just needs to provide a field for its elements. **Note**: Fields themselves do not solve the problem of JVM boxing, but it is possible to solve with special contexts like diff --git a/doc/buffers.md b/doc/buffers.md index 52a9df86e..679bd4e78 100644 --- a/doc/buffers.md +++ b/doc/buffers.md @@ -1,4 +1,5 @@ # Buffers + Buffer is one of main building blocks of kmath. It is a basic interface allowing random-access read and write (with `MutableBuffer`). There are different types of buffers: @@ -12,4 +13,5 @@ Some kmath features require a `BufferFactory` class to operate properly. A gener buffer for given reified type (for types with custom memory buffer it still better to use their own `MemoryBuffer.create()` factory). ## Buffer performance -One should avoid using default boxing buffer wherever it is possible. Try to use primitive buffers or memory buffers instead \ No newline at end of file + +One should avoid using default boxing buffer wherever it is possible. Try to use primitive buffers or memory buffers instead diff --git a/doc/codestyle.md b/doc/codestyle.md index 53789f7b2..541dc4973 100644 --- a/doc/codestyle.md +++ b/doc/codestyle.md @@ -1,22 +1,34 @@ -# Local coding conventions +# Coding Conventions -Kmath and other `scientifik` projects use general [kotlin code conventions](https://kotlinlang.org/docs/reference/coding-conventions.html), but with a number of small changes and clarifications. +KMath code follows general [Kotlin conventions](https://kotlinlang.org/docs/reference/coding-conventions.html), but +with a number of small changes and clarifications. -## Utility class names -File name should coincide with a name of one of the classes contained in the file or start with small letter and describe its contents. +## Utility Class Naming -The code convention [here](https://kotlinlang.org/docs/reference/coding-conventions.html#source-file-names) says that file names should start with capital letter even if file does not contain classes. Yet starting utility classes and aggregators with a small letter seems to be a good way to clearly visually separate those files. +Filename should coincide with a name of one of the classes contained in the file or start with small letter and +describe its contents. + +The code convention [here](https://kotlinlang.org/docs/reference/coding-conventions.html#source-file-names) says that +file names should start with a capital letter even if file does not contain classes. Yet starting utility classes and +aggregators with a small letter seems to be a good way to visually separate those files. This convention could be changed in future in a non-breaking way. -## Private variable names -Private variable names could start with underscore `_` in case the private mutable variable is shadowed by the public read-only value with the same meaning. +## Private Variable Naming -Code convention do not permit underscores in names, but is is sometimes useful to "underscore" the fact that public and private versions define the same entity. It is allowed only for private variables. +Private variables' names may start with underscore `_` for of the private mutable variable is shadowed by the public +read-only value with the same meaning. + +This rule does not permit underscores in names, but it is sometimes useful to "underscore" the fact that public and +private versions draw up the same entity. It is allowed only for private variables. This convention could be changed in future in a non-breaking way. -## Functions and properties one-liners -Use one-liners when they occupy single code window line both for functions and properties with getters like `val b: String get() = "fff"`. The same should be done with multiline expressions when they could be cleanly separated. +## Functions and Properties One-liners -There is not general consensus whenever use `fun a() = {}` or `fun a(){return}`. Yet from reader perspective one-lines seem to better show that the property or function is easily calculated. \ No newline at end of file +Use one-liners when they occupy single code window line both for functions and properties with getters like +`val b: String get() = "fff"`. The same should be performed with multiline expressions when they could be +cleanly separated. + +There is no universal consensus whenever use `fun a() = ...` or `fun a() { return ... }`. Yet from reader outlook +one-lines seem to better show that the property or function is easily calculated. diff --git a/doc/linear.md b/doc/linear.md index bbcc435ba..883df275e 100644 --- a/doc/linear.md +++ b/doc/linear.md @@ -1,6 +1,6 @@ ## Basic linear algebra layout -Kmath support for linear algebra organized in a context-oriented way. Meaning that operations are in most cases declared +KMath support for linear algebra organized in a context-oriented way. Meaning that operations are in most cases declared in context classes, and are not the members of classes that store data. This allows more flexible approach to maintain multiple back-ends. The new operations added as extensions to contexts instead of being member functions of data structures. diff --git a/doc/nd-structure.md b/doc/nd-structure.md index cf13c6a29..835304b9f 100644 --- a/doc/nd-structure.md +++ b/doc/nd-structure.md @@ -1,4 +1,4 @@ -# Nd-structure generation and operations +# ND-structure generation and operations **TODO** diff --git a/kmath-ast/README.md b/kmath-ast/README.md index a59688c8e..2339d0426 100644 --- a/kmath-ast/README.md +++ b/kmath-ast/README.md @@ -1,4 +1,4 @@ -# Abstract syntax tree expression representation and operations (`kmath-ast`) +# Abstract Syntax Tree Expression Representation and Operations (`kmath-ast`) This subproject implements the following features: @@ -38,7 +38,7 @@ This subproject implements the following features: > ``` > -## Dynamic expression code generation with ObjectWeb ASM +## Dynamic Expression Code Generation with ObjectWeb ASM `kmath-ast` JVM module supports runtime code generation to eliminate overhead of tree traversal. Code generator builds a special implementation of `Expression` with implemented `invoke` function. @@ -46,7 +46,7 @@ a special implementation of `Expression` with implemented `invoke` function. For example, the following builder: ```kotlin - RealField.mstInField { symbol("x") + 2 }.compile() +RealField.mstInField { symbol("x") + 2 }.compile() ``` … leads to generation of bytecode, which can be decompiled to the following Java class: @@ -75,7 +75,7 @@ public final class AsmCompiledExpression_1073786867_0 implements Expression #### Artifact: +> This module is distributed in the artifact `scientifik:kmath-core:0.1.4-dev-8`. +> +> **Gradle:** +> +> ```gradle +> repositories { +> maven { url 'https://dl.bintray.com/mipt-npm/scientifik' } +> maven { url 'https://dl.bintray.com/mipt-npm/dev' } +> maven { url https://dl.bintray.com/hotkeytlt/maven' } +> } +> +> dependencies { +> implementation 'scientifik:kmath-core:0.1.4-dev-8' +> } +> ``` +> **Gradle Kotlin DSL:** +> +> ```kotlin +> repositories { +> maven("https://dl.bintray.com/mipt-npm/scientifik") +> maven("https://dl.bintray.com/mipt-npm/dev") +> maven("https://dl.bintray.com/hotkeytlt/maven") +> } +> +> dependencies {`` +> implementation("scientifik:kmath-core:0.1.4-dev-8") +> } +> ``` diff --git a/kmath-core/build.gradle.kts b/kmath-core/build.gradle.kts index 18c0cc771..bea0fbf42 100644 --- a/kmath-core/build.gradle.kts +++ b/kmath-core/build.gradle.kts @@ -1,11 +1,7 @@ -plugins { - id("scientifik.mpp") -} +plugins { id("scientifik.mpp") } kotlin.sourceSets { commonMain { - dependencies { - api(project(":kmath-memory")) - } + dependencies { api(project(":kmath-memory")) } } } 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 aa190b31f..eb44cf7a7 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Algebra.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Algebra.kt @@ -58,7 +58,8 @@ interface NumericAlgebra : Algebra { inline operator fun , R> A.invoke(block: A.() -> R): R = run(block) /** - * Represents semigroup, i.e. algebraic structure with associative binary operation called "addition". + * Represents semispace, i.e. algebraic structure with associative binary operation called "addition" as well as + * multiplication by scalars. * * In KMath groups are called spaces, and also define multiplication of element by [Number]. * @@ -174,10 +175,8 @@ interface SpaceOperations : Algebra { } /** - * Represents group, i.e. algebraic structure with associative binary operation called "addition" and its neutral - * element. - * - * In KMath groups are called spaces, and also define multiplication of element by [Number]. + * Represents linear space, i.e. algebraic structure with associative binary operation called "addition" and its neutral + * element as well as multiplication by scalars. * * @param T the type of element of this group. */ diff --git a/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/BlockingIntChain.kt b/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/BlockingIntChain.kt index 6ec84d5c7..e9b499d71 100644 --- a/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/BlockingIntChain.kt +++ b/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/BlockingIntChain.kt @@ -9,4 +9,4 @@ abstract class BlockingIntChain : Chain { override suspend fun next(): Int = nextInt() fun nextBlock(size: Int): IntArray = IntArray(size) { nextInt() } -} \ No newline at end of file +} diff --git a/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/BlockingRealChain.kt b/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/BlockingRealChain.kt index 6b69d2734..ab819d327 100644 --- a/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/BlockingRealChain.kt +++ b/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/BlockingRealChain.kt @@ -9,4 +9,4 @@ abstract class BlockingRealChain : Chain { override suspend fun next(): Double = nextDouble() fun nextBlock(size: Int): DoubleArray = DoubleArray(size) { nextDouble() } -} \ No newline at end of file +} diff --git a/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/Chain.kt b/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/Chain.kt index 5635499e5..6cc9770af 100644 --- a/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/Chain.kt +++ b/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/Chain.kt @@ -22,12 +22,11 @@ import kotlinx.coroutines.flow.FlowCollector import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock - /** * A not-necessary-Markov chain of some type * @param R - the chain element type */ -interface Chain: Flow { +interface Chain : Flow { /** * Generate next value, changing state if needed */ @@ -41,7 +40,7 @@ interface Chain: Flow { @OptIn(InternalCoroutinesApi::class) override suspend fun collect(collector: FlowCollector) { kotlinx.coroutines.flow.flow { - while (true){ + while (true) { emit(next()) } }.collect(collector) @@ -71,7 +70,7 @@ class MarkovChain(private val seed: suspend () -> R, private val ge private var value: R? = null - fun value() = value + fun value(): R? = value override suspend fun next(): R { mutex.withLock { @@ -97,12 +96,11 @@ class StatefulChain( private val forkState: ((S) -> S), private val gen: suspend S.(R) -> R ) : Chain { - - private val mutex = Mutex() + private val mutex: Mutex = Mutex() private var value: R? = null - fun value() = value + fun value(): R? = value override suspend fun next(): R { mutex.withLock { @@ -112,9 +110,7 @@ class StatefulChain( } } - override fun fork(): Chain { - return StatefulChain(forkState(state), seed, forkState, gen) - } + override fun fork(): Chain = StatefulChain(forkState(state), seed, forkState, gen) } /** @@ -163,7 +159,8 @@ fun Chain.collect(mapper: suspend (Chain) -> R): Chain = object fun Chain.collectWithState(state: S, stateFork: (S) -> S, mapper: suspend S.(Chain) -> R): Chain = object : Chain { override suspend fun next(): R = state.mapper(this@collectWithState) - override fun fork(): Chain = this@collectWithState.fork().collectWithState(stateFork(state), stateFork, mapper) + override fun fork(): Chain = + this@collectWithState.fork().collectWithState(stateFork(state), stateFork, mapper) } /** @@ -173,4 +170,4 @@ fun Chain.zip(other: Chain, block: suspend (T, U) -> R): Chain = this@zip.fork().zip(other.fork(), block) -} \ No newline at end of file +} diff --git a/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/flowExtra.kt b/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/flowExtra.kt index bfd16d763..e8537304c 100644 --- a/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/flowExtra.kt +++ b/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/flowExtra.kt @@ -24,4 +24,4 @@ fun Flow.mean(space: Space): Flow = with(space) { this.num += 1 } }.map { it.sum / it.num } -} \ No newline at end of file +} diff --git a/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/coroutines/coroutinesExtra.kt b/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/coroutines/coroutinesExtra.kt index fdde62304..7e00b30a1 100644 --- a/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/coroutines/coroutinesExtra.kt +++ b/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/coroutines/coroutinesExtra.kt @@ -4,7 +4,8 @@ import kotlinx.coroutines.* import kotlinx.coroutines.channels.produce import kotlinx.coroutines.flow.* -val Dispatchers.Math: CoroutineDispatcher get() = Dispatchers.Default +val Dispatchers.Math: CoroutineDispatcher + get() = Default /** * An imitator of [Deferred] which holds a suspended function block and dispatcher @@ -42,7 +43,7 @@ fun Flow.async( } @FlowPreview -fun AsyncFlow.map(action: (T) -> R) = +fun AsyncFlow.map(action: (T) -> R): AsyncFlow = AsyncFlow(deferredFlow.map { input -> //TODO add function composition LazyDeferred(input.dispatcher) { @@ -82,9 +83,9 @@ suspend fun AsyncFlow.collect(concurrency: Int, collector: FlowCollector< @ExperimentalCoroutinesApi @FlowPreview -suspend fun AsyncFlow.collect(concurrency: Int, action: suspend (value: T) -> Unit): Unit { +suspend fun AsyncFlow.collect(concurrency: Int, action: suspend (value: T) -> Unit) { collect(concurrency, object : FlowCollector { - override suspend fun emit(value: T) = action(value) + override suspend fun emit(value: T): Unit = action(value) }) } @@ -94,9 +95,7 @@ fun Flow.mapParallel( dispatcher: CoroutineDispatcher = Dispatchers.Default, transform: suspend (T) -> R ): Flow { - return flatMapMerge{ value -> + return flatMapMerge { value -> flow { emit(transform(value)) } }.flowOn(dispatcher) } - - diff --git a/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/streaming/BufferFlow.kt b/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/streaming/BufferFlow.kt index 54da66bb7..9b7e82da5 100644 --- a/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/streaming/BufferFlow.kt +++ b/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/streaming/BufferFlow.kt @@ -11,7 +11,7 @@ import scientifik.kmath.structures.asBuffer /** * Create a [Flow] from buffer */ -fun Buffer.asFlow() = iterator().asFlow() +fun Buffer.asFlow(): Flow = iterator().asFlow() /** * Flat map a [Flow] of [Buffer] into continuous [Flow] of elements @@ -83,4 +83,4 @@ fun Flow.windowed(window: Int): Flow> = flow { ringBuffer.push(element) emit(ringBuffer.snapshot()) } -} \ No newline at end of file +} diff --git a/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/streaming/RingBuffer.kt b/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/streaming/RingBuffer.kt index fca9ffb9f..245d003b3 100644 --- a/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/streaming/RingBuffer.kt +++ b/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/streaming/RingBuffer.kt @@ -5,7 +5,6 @@ import kotlinx.coroutines.sync.withLock import scientifik.kmath.structures.Buffer import scientifik.kmath.structures.MutableBuffer import scientifik.kmath.structures.VirtualBuffer -import kotlin.reflect.KClass /** * Thread-safe ring buffer @@ -16,8 +15,7 @@ class RingBuffer( private var startIndex: Int = 0, size: Int = 0 ) : Buffer { - - private val mutex = Mutex() + private val mutex: Mutex = Mutex() override var size: Int = size private set @@ -28,7 +26,7 @@ class RingBuffer( return buffer[startIndex.forward(index)] as T } - fun isFull() = size == buffer.size + fun isFull(): Boolean = size == buffer.size /** * Iterator could provide wrong results if buffer is changed in initialization (iteration is safe) @@ -90,4 +88,4 @@ class RingBuffer( return RingBuffer(buffer) } } -} \ No newline at end of file +} diff --git a/kmath-coroutines/src/jvmMain/kotlin/scientifik/kmath/chains/ChainExt.kt b/kmath-coroutines/src/jvmMain/kotlin/scientifik/kmath/chains/ChainExt.kt index 013ea2922..0a3c67e00 100644 --- a/kmath-coroutines/src/jvmMain/kotlin/scientifik/kmath/chains/ChainExt.kt +++ b/kmath-coroutines/src/jvmMain/kotlin/scientifik/kmath/chains/ChainExt.kt @@ -6,7 +6,7 @@ import kotlin.sequences.Sequence /** * Represent a chain as regular iterator (uses blocking calls) */ -operator fun Chain.iterator() = object : Iterator { +operator fun Chain.iterator(): Iterator = object : Iterator { override fun hasNext(): Boolean = true override fun next(): R = runBlocking { next() } diff --git a/kmath-coroutines/src/jvmMain/kotlin/scientifik/kmath/structures/LazyNDStructure.kt b/kmath-coroutines/src/jvmMain/kotlin/scientifik/kmath/structures/LazyNDStructure.kt index e65940739..8d5145976 100644 --- a/kmath-coroutines/src/jvmMain/kotlin/scientifik/kmath/structures/LazyNDStructure.kt +++ b/kmath-coroutines/src/jvmMain/kotlin/scientifik/kmath/structures/LazyNDStructure.kt @@ -8,10 +8,9 @@ class LazyNDStructure( override val shape: IntArray, val function: suspend (IntArray) -> T ) : NDStructure { + private val cache: MutableMap> = hashMapOf() - private val cache = HashMap>() - - fun deferred(index: IntArray) = cache.getOrPut(index) { + fun deferred(index: IntArray): Deferred = cache.getOrPut(index) { scope.async(context = Dispatchers.Math) { function(index) } @@ -42,21 +41,21 @@ class LazyNDStructure( result = 31 * result + cache.hashCode() return result } - - } -fun NDStructure.deferred(index: IntArray) = +fun NDStructure.deferred(index: IntArray): Deferred = if (this is LazyNDStructure) this.deferred(index) else CompletableDeferred(get(index)) -suspend fun NDStructure.await(index: IntArray) = +suspend fun NDStructure.await(index: IntArray): T = if (this is LazyNDStructure) this.await(index) else get(index) /** - * PENDING would benifit from KEEP-176 + * PENDING would benefit from KEEP-176 */ -fun NDStructure.mapAsyncIndexed(scope: CoroutineScope, function: suspend (T, index: IntArray) -> R) = - LazyNDStructure(scope, shape) { index -> function(get(index), index) } +fun NDStructure.mapAsyncIndexed( + scope: CoroutineScope, + function: suspend (T, index: IntArray) -> R +): LazyNDStructure = LazyNDStructure(scope, shape) { index -> function(get(index), index) } -fun NDStructure.mapAsync(scope: CoroutineScope, function: suspend (T) -> R) = +fun NDStructure.mapAsync(scope: CoroutineScope, function: suspend (T) -> R): LazyNDStructure = LazyNDStructure(scope, shape) { index -> function(get(index)) } \ No newline at end of file diff --git a/kmath-coroutines/src/jvmTest/kotlin/scientifik/kmath/streaming/BufferFlowTest.kt b/kmath-coroutines/src/jvmTest/kotlin/scientifik/kmath/streaming/BufferFlowTest.kt index 147f687f0..427349072 100644 --- a/kmath-coroutines/src/jvmTest/kotlin/scientifik/kmath/streaming/BufferFlowTest.kt +++ b/kmath-coroutines/src/jvmTest/kotlin/scientifik/kmath/streaming/BufferFlowTest.kt @@ -15,14 +15,13 @@ import kotlin.test.Test @InternalCoroutinesApi @FlowPreview class BufferFlowTest { - - val dispatcher = Executors.newFixedThreadPool(4).asCoroutineDispatcher() + val dispatcher: CoroutineDispatcher = Executors.newFixedThreadPool(4).asCoroutineDispatcher() @Test @Timeout(2000) fun map() { runBlocking { - (1..20).asFlow().mapParallel( dispatcher) { + (1..20).asFlow().mapParallel(dispatcher) { println("Started $it on ${Thread.currentThread().name}") @Suppress("BlockingMethodInNonBlockingContext") Thread.sleep(200) diff --git a/kmath-coroutines/src/jvmTest/kotlin/scientifik/kmath/streaming/RingBufferTest.kt b/kmath-coroutines/src/jvmTest/kotlin/scientifik/kmath/streaming/RingBufferTest.kt index c14d1a26c..c84ef89ef 100644 --- a/kmath-coroutines/src/jvmTest/kotlin/scientifik/kmath/streaming/RingBufferTest.kt +++ b/kmath-coroutines/src/jvmTest/kotlin/scientifik/kmath/streaming/RingBufferTest.kt @@ -19,17 +19,17 @@ class RingBufferTest { } @Test - fun windowed(){ - val flow = flow{ + fun windowed() { + val flow = flow { var i = 0 - while(true){ - emit(i++) - } + while (true) emit(i++) } + val windowed = flow.windowed(10) + runBlocking { val first = windowed.take(1).single() - val res = windowed.take(15).map { it -> it.asSequence().average() }.toList() + val res = windowed.take(15).map { it.asSequence().average() }.toList() assertEquals(0.0, res[0]) assertEquals(4.5, res[9]) assertEquals(9.5, res[14]) -- 2.34.1 From 6329722131e10d3922b917ff856c5a0713cdca10 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Mon, 10 Aug 2020 01:30:32 +0700 Subject: [PATCH 153/156] Fix typos --- .../kotlin/scientifik/kmath/operations/Algebra.kt | 6 ++---- .../kotlin/scientifik/kmath/structures/ShortBuffer.kt | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) 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 eb44cf7a7..f18bde597 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Algebra.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Algebra.kt @@ -58,12 +58,10 @@ interface NumericAlgebra : Algebra { inline operator fun , R> A.invoke(block: A.() -> R): R = run(block) /** - * Represents semispace, i.e. algebraic structure with associative binary operation called "addition" as well as + * Represents "semispace", i.e. algebraic structure with associative binary operation called "addition" as well as * multiplication by scalars. * - * In KMath groups are called spaces, and also define multiplication of element by [Number]. - * - * @param T the type of element of this semigroup. + * @param T the type of element of this semispace. */ interface SpaceOperations : Algebra { /** diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ShortBuffer.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ShortBuffer.kt index a67cbec62..c6f19feaf 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ShortBuffer.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ShortBuffer.kt @@ -1,7 +1,7 @@ package scientifik.kmath.structures /** - * Specialized [MutableBuffer] implementation over [ShortBuffer]. + * Specialized [MutableBuffer] implementation over [ShortArray]. * * @property array the underlying array. */ -- 2.34.1 From 71c4ee065563acea5e8f917b519c2ff5c34b1005 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Mon, 10 Aug 2020 01:42:11 +0700 Subject: [PATCH 154/156] Add average and averageWith space extensions --- .../kmath/operations/AlgebraExtensions.kt | 51 +++++++++++++++++-- 1 file changed, 48 insertions(+), 3 deletions(-) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/AlgebraExtensions.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/AlgebraExtensions.kt index 6712d3baa..00b16dc98 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/AlgebraExtensions.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/AlgebraExtensions.kt @@ -4,7 +4,7 @@ package scientifik.kmath.operations * Returns the sum of all elements in the iterable in this [Space]. * * @receiver the algebra that provides addition. - * @param data the collection to sum up. + * @param data the iterable to sum up. * @return the sum. */ fun Space.sum(data: Iterable): T = data.fold(zero) { left, right -> add(left, right) } @@ -13,11 +13,29 @@ fun Space.sum(data: Iterable): T = data.fold(zero) { left, right -> ad * Returns the sum of all elements in the sequence in this [Space]. * * @receiver the algebra that provides addition. - * @param data the collection to sum up. + * @param data the sequence to sum up. * @return the sum. */ fun Space.sum(data: Sequence): T = data.fold(zero) { left, right -> add(left, right) } +/** + * Returns an average value of elements in the iterable in this [Space]. + * + * @receiver the algebra that provides addition and division. + * @param data the iterable to find average. + * @return the average value. + */ +fun Space.average(data: Iterable): T = sum(data) / data.count() + +/** + * Returns an average value of elements in the sequence in this [Space]. + * + * @receiver the algebra that provides addition and division. + * @param data the sequence to find average. + * @return the average value. + */ +fun Space.average(data: Sequence): T = sum(data) / data.count() + /** * Returns the sum of all elements in the iterable in provided space. * @@ -25,7 +43,34 @@ fun Space.sum(data: Sequence): T = data.fold(zero) { left, right -> ad * @param space the algebra that provides addition. * @return the sum. */ -fun > Iterable.sumWith(space: S): T = space.sum(this) +fun Iterable.sumWith(space: Space): T = space.sum(this) + +/** + * Returns the sum of all elements in the sequence in provided space. + * + * @receiver the collection to sum up. + * @param space the algebra that provides addition. + * @return the sum. + */ +fun Sequence.sumWith(space: Space): T = space.sum(this) + +/** + * Returns an average value of elements in the iterable in this [Space]. + * + * @receiver the iterable to find average. + * @param space the algebra that provides addition and division. + * @return the average value. + */ +fun Iterable.averageWith(space: Space): T = space.average(this) + +/** + * Returns an average value of elements in the sequence in this [Space]. + * + * @receiver the sequence to find average. + * @param space the algebra that provides addition and division. + * @return the average value. + */ +fun Sequence.averageWith(space: Space): T = space.average(this) //TODO optimized power operation -- 2.34.1 From 53a54300fa073ba39b9d161423e65973e1387204 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Tue, 11 Aug 2020 02:36:08 +0700 Subject: [PATCH 155/156] Add changelog --- CHANGELOG.md | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 000000000..91c4098cd --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,30 @@ +## [Unreleased] + +### Added +- Functional Expressions API +- Mathematical Syntax Tree, its interpreter and API +- String to MST parser (https://github.com/mipt-npm/kmath/pull/120) +- MST to JVM bytecode translator (https://github.com/mipt-npm/kmath/pull/94) +- FloatBuffer (specialized MutableBuffer over FloatArray) +- FlaggedBuffer to associate primitive numbers buffer with flags (to mark values infinite or missing, etc.) +- Specialized builder functions for all primitive buffers like `IntBuffer(25) { it + 1 }` (https://github.com/mipt-npm/kmath/pull/125) +- Interface `NumericAlgebra` where `number` operation is available to convert numbers to algebraic elements +- Inverse trigonometric functions support in ExtendedField (`asin`, `acos`, `atan`) (https://github.com/mipt-npm/kmath/pull/114) +- New space extensions: `average` and `averageWith` +- Local coding conventions +- Geometric Domains API in `kmath-core` +- Blocking chains in `kmath-coroutines` + +### Changed +- BigInteger and BigDecimal algebra: JBigDecimalField has companion object with default math context; minor optimizations +- `power(T, Int)` extension function has preconditions and supports `Field` +- Memory objects have more preconditions (overflow checking) +- `tg` function is renamed to `tan` (https://github.com/mipt-npm/kmath/pull/114) +- Gradle version: 6.3 -> 6.5.1 +- Moved probability distributions to commons-rng and to `kmath-prob`. + +### Fixed +- Missing copy method in Memory implementation on JS (https://github.com/mipt-npm/kmath/pull/106) +- D3.dim value in `kmath-dimensions` +- Multiplication in integer rings in `kmath-core` (https://github.com/mipt-npm/kmath/pull/101) +- Commons RNG compatibility (https://github.com/mipt-npm/kmath/issues/93) -- 2.34.1 From b65641ddbb8e5c423c0b1c4e749e8062aa438886 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Tue, 11 Aug 2020 02:39:01 +0700 Subject: [PATCH 156/156] Add header to changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 91c4098cd..2c99c7bc1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,5 @@ +# KMath + ## [Unreleased] ### Added -- 2.34.1