From ab32cd95616adfb409c28cc0fb13754f69031081 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Tue, 19 Jan 2021 17:16:43 +0300 Subject: [PATCH] Numeric operations are decoupled from Ring --- CHANGELOG.md | 1 + build.gradle.kts | 2 +- .../kmath/benchmarks/LargeNDBenchmark.kt | 25 ++++ .../kmath/stat/DistributionBenchmark.kt | 3 +- .../kscience/kmath/structures/ComplexND.kt | 2 +- .../kscience/kmath/structures/NDField.kt | 8 +- .../kotlin/kscience/kmath/ast/MstAlgebra.kt | 31 +++-- .../DerivativeStructureExpression.kt | 7 +- .../FunctionalExpressionAlgebra.kt | 32 +++-- .../kmath/expressions/SimpleAutoDiff.kt | 4 +- .../kscience/kmath/linear/MatrixFeatures.kt | 4 +- .../kscience/kmath/operations/Algebra.kt | 117 +--------------- .../kscience/kmath/operations/BigInt.kt | 4 +- .../kscience/kmath/operations/Complex.kt | 4 +- .../kmath/operations/NumericAlgebra.kt | 125 ++++++++++++++++++ .../{NumberAlgebra.kt => numbers.kt} | 15 ++- .../kmath/structures/ComplexNDField.kt | 23 ++-- .../kmath/structures/RealBufferField.kt | 2 + .../kscience/kmath/structures/RealNDField.kt | 16 ++- .../kscience/kmath/structures/NDFieldTest.kt | 3 +- .../kscience/kmath/operations/BigNumbers.kt | 8 +- .../kotlin/kscience/kmath/ejml/EjmlMatrix.kt | 14 +- .../kscience/kmath/ejml/EjmlMatrixContext.kt | 6 + .../kscience.kmath.nd4j/Nd4jArrayAlgebra.kt | 46 ++++--- settings.gradle.kts | 4 +- 25 files changed, 300 insertions(+), 206 deletions(-) create mode 100644 examples/src/benchmarks/kotlin/kscience/kmath/benchmarks/LargeNDBenchmark.kt create mode 100644 kmath-core/src/commonMain/kotlin/kscience/kmath/operations/NumericAlgebra.kt rename kmath-core/src/commonMain/kotlin/kscience/kmath/operations/{NumberAlgebra.kt => numbers.kt} (97%) diff --git a/CHANGELOG.md b/CHANGELOG.md index e542d210c..840733d92 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,6 +32,7 @@ - Optimized dot product for buffer matrices moved to `kmath-for-real` - EjmlMatrix context is an object - Matrix LUP `inverse` renamed to `inverseWithLUP` +- `NumericAlgebra` moved outside of regular algebra chain (`Ring` no longer implements it). ### Deprecated diff --git a/build.gradle.kts b/build.gradle.kts index 90a39c531..d171bd608 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -4,7 +4,7 @@ plugins { id("ru.mipt.npm.project") } -internal val kmathVersion: String by extra("0.2.0-dev-4") +internal val kmathVersion: String by extra("0.2.0-dev-5") internal val bintrayRepo: String by extra("kscience") internal val githubProject: String by extra("kmath") diff --git a/examples/src/benchmarks/kotlin/kscience/kmath/benchmarks/LargeNDBenchmark.kt b/examples/src/benchmarks/kotlin/kscience/kmath/benchmarks/LargeNDBenchmark.kt new file mode 100644 index 000000000..395fde619 --- /dev/null +++ b/examples/src/benchmarks/kotlin/kscience/kmath/benchmarks/LargeNDBenchmark.kt @@ -0,0 +1,25 @@ +package kscience.kmath.benchmarks + +import kscience.kmath.structures.NDField +import org.openjdk.jmh.annotations.Benchmark +import org.openjdk.jmh.annotations.Scope +import org.openjdk.jmh.annotations.State +import org.openjdk.jmh.infra.Blackhole +import kotlin.random.Random + +@State(Scope.Benchmark) +class LargeNDBenchmark { + val arraySize = 10000 + val RANDOM = Random(222) + val src1 = DoubleArray(arraySize) { RANDOM.nextDouble() } + val src2 = DoubleArray(arraySize) { RANDOM.nextDouble() } + val field = NDField.real(arraySize) + val kmathArray1 = field.produce { (a) -> src1[a] } + val kmathArray2 = field.produce { (a) -> src2[a] } + + @Benchmark + fun test10000(bh: Blackhole) { + bh.consume(field.add(kmathArray1, kmathArray2)) + } + +} \ No newline at end of file diff --git a/examples/src/main/kotlin/kscience/kmath/stat/DistributionBenchmark.kt b/examples/src/main/kotlin/kscience/kmath/stat/DistributionBenchmark.kt index ef554aeff..99d3cd504 100644 --- a/examples/src/main/kotlin/kscience/kmath/stat/DistributionBenchmark.kt +++ b/examples/src/main/kotlin/kscience/kmath/stat/DistributionBenchmark.kt @@ -3,7 +3,6 @@ package kscience.kmath.commons.prob import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.async import kotlinx.coroutines.runBlocking -import kscience.kmath.chains.BlockingRealChain import kscience.kmath.stat.* import org.apache.commons.rng.sampling.distribution.ZigguratNormalizedGaussianSampler import org.apache.commons.rng.simple.RandomSource @@ -13,7 +12,7 @@ import java.time.Instant private fun runChain(): Duration { val generator = RandomGenerator.fromSource(RandomSource.MT, 123L) val normal = Distribution.normal(NormalSamplerMethod.Ziggurat) - val chain = normal.sample(generator) as BlockingRealChain + val chain = normal.sample(generator) val startTime = Instant.now() var sum = 0.0 diff --git a/examples/src/main/kotlin/kscience/kmath/structures/ComplexND.kt b/examples/src/main/kotlin/kscience/kmath/structures/ComplexND.kt index aa4b10ef2..b69590473 100644 --- a/examples/src/main/kotlin/kscience/kmath/structures/ComplexND.kt +++ b/examples/src/main/kotlin/kscience/kmath/structures/ComplexND.kt @@ -11,7 +11,7 @@ fun main() { val n = 1000 val realField = NDField.real(dim, dim) - val complexField = NDField.complex(dim, dim) + val complexField: ComplexNDField = NDField.complex(dim, dim) val realTime = measureTimeMillis { realField { diff --git a/examples/src/main/kotlin/kscience/kmath/structures/NDField.kt b/examples/src/main/kotlin/kscience/kmath/structures/NDField.kt index 778d811fd..b5130c92b 100644 --- a/examples/src/main/kotlin/kscience/kmath/structures/NDField.kt +++ b/examples/src/main/kotlin/kscience/kmath/structures/NDField.kt @@ -33,7 +33,7 @@ fun main() { measureAndPrint("Automatic field addition") { autoField { var res: NDBuffer = one - repeat(n) { res += number(1.0) } + repeat(n) { res += 1.0 } } } @@ -45,14 +45,14 @@ fun main() { measureAndPrint("Specialized addition") { specializedField { var res: NDBuffer = one - repeat(n) { res += one } + repeat(n) { res += 1.0 } } } measureAndPrint("Nd4j specialized addition") { nd4jField { var res = one - repeat(n) { res += one } + repeat(n) { res += 1.0 } } } @@ -73,7 +73,7 @@ fun main() { genericField { var res: NDBuffer = one repeat(n) { - res += one // couldn't avoid using `one` due to resolution ambiguity } + res += 1.0 // couldn't avoid using `one` due to resolution ambiguity } } } } diff --git a/kmath-ast/src/commonMain/kotlin/kscience/kmath/ast/MstAlgebra.kt b/kmath-ast/src/commonMain/kotlin/kscience/kmath/ast/MstAlgebra.kt index 80b164a7c..eadbc85ee 100644 --- a/kmath-ast/src/commonMain/kotlin/kscience/kmath/ast/MstAlgebra.kt +++ b/kmath-ast/src/commonMain/kotlin/kscience/kmath/ast/MstAlgebra.kt @@ -1,5 +1,6 @@ package kscience.kmath.ast +import kscience.kmath.misc.UnstableKMathAPI import kscience.kmath.operations.* /** @@ -25,8 +26,11 @@ public object MstSpace : Space, NumericAlgebra { public override fun number(value: Number): MST.Numeric = MstAlgebra.number(value) public override fun symbol(value: String): MST.Symbolic = MstAlgebra.symbol(value) public override fun add(a: MST, b: MST): MST.Binary = binaryOperationFunction(SpaceOperations.PLUS_OPERATION)(a, b) - public override operator fun MST.unaryPlus(): MST.Unary = unaryOperationFunction(SpaceOperations.PLUS_OPERATION)(this) - public override operator fun MST.unaryMinus(): MST.Unary = unaryOperationFunction(SpaceOperations.MINUS_OPERATION)(this) + public override operator fun MST.unaryPlus(): MST.Unary = + unaryOperationFunction(SpaceOperations.PLUS_OPERATION)(this) + + public override operator fun MST.unaryMinus(): MST.Unary = + unaryOperationFunction(SpaceOperations.MINUS_OPERATION)(this) public override operator fun MST.minus(b: MST): MST.Binary = binaryOperationFunction(SpaceOperations.MINUS_OPERATION)(this, b) @@ -44,7 +48,8 @@ public object MstSpace : Space, NumericAlgebra { /** * [Ring] over [MST] nodes. */ -public object MstRing : Ring, NumericAlgebra { +@OptIn(UnstableKMathAPI::class) +public object MstRing : Ring, RingWithNumbers { public override val zero: MST.Numeric get() = MstSpace.zero @@ -54,7 +59,9 @@ public object MstRing : Ring, NumericAlgebra { public override fun symbol(value: String): MST.Symbolic = MstSpace.symbol(value) public override fun add(a: MST, b: MST): MST.Binary = MstSpace.add(a, b) public override fun multiply(a: MST, k: Number): MST.Binary = MstSpace.multiply(a, k) - public override fun multiply(a: MST, b: MST): MST.Binary = binaryOperationFunction(RingOperations.TIMES_OPERATION)(a, b) + public override fun multiply(a: MST, b: MST): MST.Binary = + binaryOperationFunction(RingOperations.TIMES_OPERATION)(a, b) + public override operator fun MST.unaryPlus(): MST.Unary = MstSpace { +this@unaryPlus } public override operator fun MST.unaryMinus(): MST.Unary = MstSpace { -this@unaryMinus } public override operator fun MST.minus(b: MST): MST.Binary = MstSpace { this@minus - b } @@ -69,7 +76,8 @@ public object MstRing : Ring, NumericAlgebra { /** * [Field] over [MST] nodes. */ -public object MstField : Field { +@OptIn(UnstableKMathAPI::class) +public object MstField : Field, RingWithNumbers { public override val zero: MST.Numeric get() = MstRing.zero @@ -81,7 +89,9 @@ public object MstField : Field { public override fun add(a: MST, b: MST): MST.Binary = MstRing.add(a, b) public override fun multiply(a: MST, k: Number): MST.Binary = MstRing.multiply(a, k) public override fun multiply(a: MST, b: MST): MST.Binary = MstRing.multiply(a, b) - public override fun divide(a: MST, b: MST): MST.Binary = binaryOperationFunction(FieldOperations.DIV_OPERATION)(a, b) + public override fun divide(a: MST, b: MST): MST.Binary = + binaryOperationFunction(FieldOperations.DIV_OPERATION)(a, b) + public override operator fun MST.unaryPlus(): MST.Unary = MstRing { +this@unaryPlus } public override operator fun MST.unaryMinus(): MST.Unary = MstRing { -this@unaryMinus } public override operator fun MST.minus(b: MST): MST.Binary = MstRing { this@minus - b } @@ -89,13 +99,14 @@ public object MstField : Field { public override fun binaryOperationFunction(operation: String): (left: MST, right: MST) -> MST.Binary = MstRing.binaryOperationFunction(operation) - public override fun unaryOperationFunction(operation: String): (arg: MST) -> MST.Unary = MstRing.unaryOperationFunction(operation) + public override fun unaryOperationFunction(operation: String): (arg: MST) -> MST.Unary = + MstRing.unaryOperationFunction(operation) } /** * [ExtendedField] over [MST] nodes. */ -public object MstExtendedField : ExtendedField { +public object MstExtendedField : ExtendedField, NumericAlgebra { public override val zero: MST.Numeric get() = MstField.zero @@ -103,6 +114,7 @@ public object MstExtendedField : ExtendedField { get() = MstField.one public override fun symbol(value: String): MST.Symbolic = MstField.symbol(value) + public override fun number(value: Number): MST.Numeric = MstRing.number(value) public override fun sin(arg: MST): MST.Unary = unaryOperationFunction(TrigonometricOperations.SIN_OPERATION)(arg) public override fun cos(arg: MST): MST.Unary = unaryOperationFunction(TrigonometricOperations.COS_OPERATION)(arg) public override fun tan(arg: MST): MST.Unary = unaryOperationFunction(TrigonometricOperations.TAN_OPERATION)(arg) @@ -132,5 +144,6 @@ public object MstExtendedField : ExtendedField { public override fun binaryOperationFunction(operation: String): (left: MST, right: MST) -> MST.Binary = MstField.binaryOperationFunction(operation) - public override fun unaryOperationFunction(operation: String): (arg: MST) -> MST.Unary = MstField.unaryOperationFunction(operation) + public override fun unaryOperationFunction(operation: String): (arg: MST) -> MST.Unary = + MstField.unaryOperationFunction(operation) } diff --git a/kmath-commons/src/main/kotlin/kscience/kmath/commons/expressions/DerivativeStructureExpression.kt b/kmath-commons/src/main/kotlin/kscience/kmath/commons/expressions/DerivativeStructureExpression.kt index 345babe8b..2912ddc4c 100644 --- a/kmath-commons/src/main/kotlin/kscience/kmath/commons/expressions/DerivativeStructureExpression.kt +++ b/kmath-commons/src/main/kotlin/kscience/kmath/commons/expressions/DerivativeStructureExpression.kt @@ -1,7 +1,9 @@ package kscience.kmath.commons.expressions import kscience.kmath.expressions.* +import kscience.kmath.misc.UnstableKMathAPI import kscience.kmath.operations.ExtendedField +import kscience.kmath.operations.RingWithNumbers import org.apache.commons.math3.analysis.differentiation.DerivativeStructure /** @@ -10,15 +12,18 @@ import org.apache.commons.math3.analysis.differentiation.DerivativeStructure * @property order The derivation order. * @property bindings The map of bindings values. All bindings are considered free parameters */ +@OptIn(UnstableKMathAPI::class) public class DerivativeStructureField( public val order: Int, bindings: Map, -) : ExtendedField, ExpressionAlgebra { +) : ExtendedField, ExpressionAlgebra, RingWithNumbers { public val numberOfVariables: Int = bindings.size public override val zero: DerivativeStructure by lazy { DerivativeStructure(numberOfVariables, order) } public override val one: DerivativeStructure by lazy { DerivativeStructure(numberOfVariables, order, 1.0) } + override fun number(value: Number): DerivativeStructure = const(value.toDouble()) + /** * A class that implements both [DerivativeStructure] and a [Symbol] */ diff --git a/kmath-core/src/commonMain/kotlin/kscience/kmath/expressions/FunctionalExpressionAlgebra.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/expressions/FunctionalExpressionAlgebra.kt index afbaf16e1..880a4e34c 100644 --- a/kmath-core/src/commonMain/kotlin/kscience/kmath/expressions/FunctionalExpressionAlgebra.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/expressions/FunctionalExpressionAlgebra.kt @@ -7,8 +7,9 @@ import kscience.kmath.operations.* * * @param algebra The algebra to provide for Expressions built. */ -public abstract class FunctionalExpressionAlgebra>(public val algebra: A) : - ExpressionAlgebra> { +public abstract class FunctionalExpressionAlgebra>( + public val algebra: A, +) : ExpressionAlgebra> { /** * Builds an Expression of constant expression which does not depend on arguments. */ @@ -42,8 +43,9 @@ public abstract class FunctionalExpressionAlgebra>(public val /** * A context class for [Expression] construction for [Space] algebras. */ -public open class FunctionalExpressionSpace>(algebra: A) : - FunctionalExpressionAlgebra(algebra), Space> { +public open class FunctionalExpressionSpace>( + algebra: A, +) : FunctionalExpressionAlgebra(algebra), Space> { public override val zero: Expression get() = const(algebra.zero) /** @@ -71,8 +73,9 @@ public open class FunctionalExpressionSpace>(algebra: A) : super.binaryOperationFunction(operation) } -public open class FunctionalExpressionRing(algebra: A) : FunctionalExpressionSpace(algebra), - Ring> where A : Ring, A : NumericAlgebra { +public open class FunctionalExpressionRing>( + algebra: A, +) : FunctionalExpressionSpace(algebra), Ring> { public override val one: Expression get() = const(algebra.one) @@ -92,9 +95,8 @@ public open class FunctionalExpressionRing(algebra: A) : FunctionalExpress super.binaryOperationFunction(operation) } -public open class FunctionalExpressionField(algebra: A) : - FunctionalExpressionRing(algebra), Field> - where A : Field, A : NumericAlgebra { +public open class FunctionalExpressionField>(algebra: A) : + FunctionalExpressionRing(algebra), Field> { /** * Builds an Expression of division an expression by another one. */ @@ -111,9 +113,12 @@ public open class FunctionalExpressionField(algebra: A) : super.binaryOperationFunction(operation) } -public open class FunctionalExpressionExtendedField(algebra: A) : - FunctionalExpressionField(algebra), - ExtendedField> where A : ExtendedField, A : NumericAlgebra { +public open class FunctionalExpressionExtendedField>( + algebra: A, +) : FunctionalExpressionField(algebra), ExtendedField> { + + override fun number(value: Number): Expression = const(algebra.number(value)) + public override fun sin(arg: Expression): Expression = unaryOperationFunction(TrigonometricOperations.SIN_OPERATION)(arg) @@ -135,7 +140,8 @@ public open class FunctionalExpressionExtendedField(algebra: A) : public override fun exp(arg: Expression): Expression = unaryOperationFunction(ExponentialOperations.EXP_OPERATION)(arg) - public override fun ln(arg: Expression): Expression = unaryOperationFunction(ExponentialOperations.LN_OPERATION)(arg) + public override fun ln(arg: Expression): Expression = + unaryOperationFunction(ExponentialOperations.LN_OPERATION)(arg) public override fun unaryOperationFunction(operation: String): (arg: Expression) -> Expression = super.unaryOperationFunction(operation) diff --git a/kmath-core/src/commonMain/kotlin/kscience/kmath/expressions/SimpleAutoDiff.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/expressions/SimpleAutoDiff.kt index e8a894d23..0621e82bd 100644 --- a/kmath-core/src/commonMain/kotlin/kscience/kmath/expressions/SimpleAutoDiff.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/expressions/SimpleAutoDiff.kt @@ -1,6 +1,7 @@ package kscience.kmath.expressions import kscience.kmath.linear.Point +import kscience.kmath.misc.UnstableKMathAPI import kscience.kmath.operations.* import kscience.kmath.structures.asBuffer import kotlin.contracts.InvocationKind @@ -79,10 +80,11 @@ public fun > F.simpleAutoDiff( /** * Represents field in context of which functions can be derived. */ +@OptIn(UnstableKMathAPI::class) public open class SimpleAutoDiffField>( public val context: F, bindings: Map, -) : Field>, ExpressionAlgebra> { +) : Field>, ExpressionAlgebra>, RingWithNumbers> { public override val zero: AutoDiffValue get() = const(context.zero) diff --git a/kmath-core/src/commonMain/kotlin/kscience/kmath/linear/MatrixFeatures.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/linear/MatrixFeatures.kt index 767b58eba..1f93309a6 100644 --- a/kmath-core/src/commonMain/kotlin/kscience/kmath/linear/MatrixFeatures.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/linear/MatrixFeatures.kt @@ -1,5 +1,7 @@ package kscience.kmath.linear +import kscience.kmath.structures.Matrix + /** * A marker interface representing some properties of matrices or additional transformations of them. Features are used * to optimize matrix operations performance in some cases or retrieve the APIs. @@ -30,7 +32,7 @@ public interface InverseMatrixFeature : MatrixFeature { /** * The inverse matrix of the matrix that owns this feature. */ - public val inverse: FeaturedMatrix + public val inverse: Matrix } /** diff --git a/kmath-core/src/commonMain/kotlin/kscience/kmath/operations/Algebra.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/operations/Algebra.kt index 24b417860..2bafd377e 100644 --- a/kmath-core/src/commonMain/kotlin/kscience/kmath/operations/Algebra.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/operations/Algebra.kt @@ -88,85 +88,6 @@ public interface Algebra { public fun binaryOperation(operation: String, left: T, right: T): T = binaryOperationFunction(operation)(left, right) } -/** - * An algebraic structure where elements can have numeric representation. - * - * @param T the type of element of this structure. - */ -public interface NumericAlgebra : Algebra { - /** - * Wraps a number to [T] object. - * - * @param value the number to wrap. - * @return an object. - */ - public fun number(value: Number): T - - /** - * Dynamically dispatches a binary operation with the certain name with numeric first argument. - * - * This function must follow two properties: - * - * 1. In case if operation is not defined in the structure, the function throws [kotlin.IllegalStateException]. - * 2. This function is symmetric with the other [leftSideNumberOperation] overload: - * i.e. `leftSideNumberOperationFunction(a)(b, c) == leftSideNumberOperation(a, b)`. - * - * @param operation the name of operation. - * @return an operation. - */ - public fun leftSideNumberOperationFunction(operation: String): (left: Number, right: T) -> T = - { l, r -> binaryOperationFunction(operation)(number(l), r) } - - /** - * Dynamically invokes a binary operation with the certain name with numeric first argument. - * - * This function must follow two properties: - * - * 1. In case if operation is not defined in the structure, the function throws [kotlin.IllegalStateException]. - * 2. This function is symmetric with second [leftSideNumberOperation] overload: - * i.e. `leftSideNumberOperationFunction(a)(b, c) == leftSideNumberOperation(a, b, c)`. - * - * @param operation the name of operation. - * @param left the first argument of operation. - * @param right the second argument of operation. - * @return a result of operation. - */ - public fun leftSideNumberOperation(operation: String, left: Number, right: T): T = - leftSideNumberOperationFunction(operation)(left, right) - - /** - * Dynamically dispatches a binary operation with the certain name with numeric first argument. - * - * This function must follow two properties: - * - * 1. In case if operation is not defined in the structure, the function throws [kotlin.IllegalStateException]. - * 2. This function is symmetric with the other [rightSideNumberOperationFunction] overload: - * i.e. `rightSideNumberOperationFunction(a)(b, c) == leftSideNumberOperation(a, b, c)`. - * - * @param operation the name of operation. - * @return an operation. - */ - public fun rightSideNumberOperationFunction(operation: String): (left: T, right: Number) -> T = - { l, r -> binaryOperationFunction(operation)(l, number(r)) } - - /** - * Dynamically invokes a binary operation with the certain name with numeric second argument. - * - * This function must follow two properties: - * - * 1. In case if operation is not defined in the structure, the function throws [kotlin.IllegalStateException]. - * 2. This function is symmetric with the other [rightSideNumberOperationFunction] overload: - * i.e. `rightSideNumberOperationFunction(a)(b, c) == rightSideNumberOperation(a, b, c)`. - * - * @param operation the name of operation. - * @param left the first argument of operation. - * @param right the second argument of operation. - * @return a result of operation. - */ - public fun rightSideNumberOperation(operation: String, left: T, right: Number): T = - rightSideNumberOperationFunction(operation)(left, right) -} - /** * Call a block with an [Algebra] as receiver. */ @@ -341,47 +262,11 @@ public interface RingOperations : SpaceOperations { * * @param T the type of element of this ring. */ -public interface Ring : Space, RingOperations, NumericAlgebra { +public interface Ring : Space, RingOperations { /** * neutral operation for multiplication */ public val one: T - - public override fun number(value: Number): T = one * value.toDouble() - - /** - * Addition of element and scalar. - * - * @receiver the addend. - * @param b the augend. - */ - public operator fun T.plus(b: Number): T = this + number(b) - - /** - * Addition of scalar and element. - * - * @receiver the addend. - * @param b the augend. - */ - public operator fun Number.plus(b: T): T = b + this - - /** - * Subtraction of element from number. - * - * @receiver the minuend. - * @param b the subtrahend. - * @receiver the difference. - */ - public 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. - */ - public operator fun Number.minus(b: T): T = -b + this } /** diff --git a/kmath-core/src/commonMain/kotlin/kscience/kmath/operations/BigInt.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/operations/BigInt.kt index 20f289596..0be72e80c 100644 --- a/kmath-core/src/commonMain/kotlin/kscience/kmath/operations/BigInt.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/operations/BigInt.kt @@ -1,5 +1,6 @@ package kscience.kmath.operations +import kscience.kmath.misc.UnstableKMathAPI import kscience.kmath.operations.BigInt.Companion.BASE import kscience.kmath.operations.BigInt.Companion.BASE_SIZE import kscience.kmath.structures.* @@ -16,7 +17,8 @@ public typealias TBase = ULong * * @author Robert Drynkin (https://github.com/robdrynkin) and Peter Klimai (https://github.com/pklimai) */ -public object BigIntField : Field { +@OptIn(UnstableKMathAPI::class) +public object BigIntField : Field, RingWithNumbers { override val zero: BigInt = BigInt.ZERO override val one: BigInt = BigInt.ONE diff --git a/kmath-core/src/commonMain/kotlin/kscience/kmath/operations/Complex.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/operations/Complex.kt index 703931c7c..5695e6696 100644 --- a/kmath-core/src/commonMain/kotlin/kscience/kmath/operations/Complex.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/operations/Complex.kt @@ -41,7 +41,7 @@ private val PI_DIV_2 = Complex(PI / 2, 0) /** * A field of [Complex]. */ -public object ComplexField : ExtendedField, Norm { +public object ComplexField : ExtendedField, Norm, RingWithNumbers { override val zero: Complex = 0.0.toComplex() override val one: Complex = 1.0.toComplex() @@ -156,7 +156,7 @@ public object ComplexField : ExtendedField, Norm { override fun norm(arg: Complex): Complex = sqrt(arg.conjugate * arg) - 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) } /** diff --git a/kmath-core/src/commonMain/kotlin/kscience/kmath/operations/NumericAlgebra.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/operations/NumericAlgebra.kt new file mode 100644 index 000000000..26f93fae8 --- /dev/null +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/operations/NumericAlgebra.kt @@ -0,0 +1,125 @@ +package kscience.kmath.operations + +import kscience.kmath.misc.UnstableKMathAPI + +/** + * An algebraic structure where elements can have numeric representation. + * + * @param T the type of element of this structure. + */ +public interface NumericAlgebra : Algebra { + /** + * Wraps a number to [T] object. + * + * @param value the number to wrap. + * @return an object. + */ + public fun number(value: Number): T + + /** + * Dynamically dispatches a binary operation with the certain name with numeric first argument. + * + * This function must follow two properties: + * + * 1. In case if operation is not defined in the structure, the function throws [kotlin.IllegalStateException]. + * 2. This function is symmetric with the other [leftSideNumberOperation] overload: + * i.e. `leftSideNumberOperationFunction(a)(b, c) == leftSideNumberOperation(a, b)`. + * + * @param operation the name of operation. + * @return an operation. + */ + public fun leftSideNumberOperationFunction(operation: String): (left: Number, right: T) -> T = + { l, r -> binaryOperationFunction(operation)(number(l), r) } + + /** + * Dynamically invokes a binary operation with the certain name with numeric first argument. + * + * This function must follow two properties: + * + * 1. In case if operation is not defined in the structure, the function throws [kotlin.IllegalStateException]. + * 2. This function is symmetric with second [leftSideNumberOperation] overload: + * i.e. `leftSideNumberOperationFunction(a)(b, c) == leftSideNumberOperation(a, b, c)`. + * + * @param operation the name of operation. + * @param left the first argument of operation. + * @param right the second argument of operation. + * @return a result of operation. + */ + public fun leftSideNumberOperation(operation: String, left: Number, right: T): T = + leftSideNumberOperationFunction(operation)(left, right) + + /** + * Dynamically dispatches a binary operation with the certain name with numeric first argument. + * + * This function must follow two properties: + * + * 1. In case if operation is not defined in the structure, the function throws [kotlin.IllegalStateException]. + * 2. This function is symmetric with the other [rightSideNumberOperationFunction] overload: + * i.e. `rightSideNumberOperationFunction(a)(b, c) == leftSideNumberOperation(a, b, c)`. + * + * @param operation the name of operation. + * @return an operation. + */ + public fun rightSideNumberOperationFunction(operation: String): (left: T, right: Number) -> T = + { l, r -> binaryOperationFunction(operation)(l, number(r)) } + + /** + * Dynamically invokes a binary operation with the certain name with numeric second argument. + * + * This function must follow two properties: + * + * 1. In case if operation is not defined in the structure, the function throws [kotlin.IllegalStateException]. + * 2. This function is symmetric with the other [rightSideNumberOperationFunction] overload: + * i.e. `rightSideNumberOperationFunction(a)(b, c) == rightSideNumberOperation(a, b, c)`. + * + * @param operation the name of operation. + * @param left the first argument of operation. + * @param right the second argument of operation. + * @return a result of operation. + */ + public fun rightSideNumberOperation(operation: String, left: T, right: Number): T = + rightSideNumberOperationFunction(operation)(left, right) +} + +/** + * A combination of [NumericAlgebra] and [Ring] that adds intrinsic simple operations on numbers like `T+1` + * TODO to be removed and replaced by extensions after multiple receivers are there + */ +@UnstableKMathAPI +public interface RingWithNumbers: Ring, NumericAlgebra{ + public override fun number(value: Number): T = one * value + + /** + * Addition of element and scalar. + * + * @receiver the addend. + * @param b the augend. + */ + public operator fun T.plus(b: Number): T = this + number(b) + + /** + * Addition of scalar and element. + * + * @receiver the addend. + * @param b the augend. + */ + public operator fun Number.plus(b: T): T = b + this + + /** + * Subtraction of element from number. + * + * @receiver the minuend. + * @param b the subtrahend. + * @receiver the difference. + */ + public 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. + */ + public operator fun Number.minus(b: T): T = -b + this +} \ No newline at end of file diff --git a/kmath-core/src/commonMain/kotlin/kscience/kmath/operations/NumberAlgebra.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/operations/numbers.kt similarity index 97% rename from kmath-core/src/commonMain/kotlin/kscience/kmath/operations/NumberAlgebra.kt rename to kmath-core/src/commonMain/kotlin/kscience/kmath/operations/numbers.kt index a7b73d5e0..de3818aa6 100644 --- a/kmath-core/src/commonMain/kotlin/kscience/kmath/operations/NumberAlgebra.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/operations/numbers.kt @@ -37,7 +37,7 @@ public interface ExtendedFieldOperations : /** * Advanced Number-like field that implements basic operations. */ -public interface ExtendedField : ExtendedFieldOperations, Field { +public interface ExtendedField : ExtendedFieldOperations, Field, NumericAlgebra { public override fun sinh(arg: T): T = (exp(arg) - exp(-arg)) / 2 public override fun cosh(arg: T): T = (exp(arg) + exp(-arg)) / 2 public override fun tanh(arg: T): T = (exp(arg) - exp(-arg)) / (exp(-arg) + exp(arg)) @@ -80,6 +80,8 @@ public object RealField : ExtendedField, Norm { public override val one: Double get() = 1.0 + override fun number(value: Number): Double = value.toDouble() + public override fun binaryOperationFunction(operation: String): (left: Double, right: Double) -> Double = when (operation) { PowerOperations.POW_OPERATION -> ::power @@ -131,10 +133,13 @@ public object FloatField : ExtendedField, Norm { public override val one: Float get() = 1.0f - public override fun binaryOperationFunction(operation: String): (left: Float, right: Float) -> Float = when (operation) { - PowerOperations.POW_OPERATION -> ::power - else -> super.binaryOperationFunction(operation) - } + override fun number(value: Number): Float = value.toFloat() + + public override fun binaryOperationFunction(operation: String): (left: Float, right: Float) -> Float = + when (operation) { + PowerOperations.POW_OPERATION -> ::power + else -> super.binaryOperationFunction(operation) + } public override inline fun add(a: Float, b: Float): Float = a + b public override inline fun multiply(a: Float, k: Number): Float = a * k.toFloat() diff --git a/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/ComplexNDField.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/ComplexNDField.kt index f1f1074e5..6de69cabe 100644 --- a/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/ComplexNDField.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/ComplexNDField.kt @@ -1,9 +1,7 @@ package kscience.kmath.structures -import kscience.kmath.operations.Complex -import kscience.kmath.operations.ComplexField -import kscience.kmath.operations.FieldElement -import kscience.kmath.operations.complex +import kscience.kmath.misc.UnstableKMathAPI +import kscience.kmath.operations.* import kotlin.contracts.InvocationKind import kotlin.contracts.contract @@ -12,15 +10,22 @@ public typealias ComplexNDElement = BufferedNDFieldElement, - ExtendedNDField> { + ExtendedNDField>, + RingWithNumbers>{ override val strides: Strides = DefaultStrides(shape) override val elementContext: ComplexField get() = ComplexField override val zero: ComplexNDElement by lazy { produce { zero } } override val one: ComplexNDElement by lazy { produce { one } } + override fun number(value: Number): NDBuffer { + val c = value.toComplex() + return produce { c } + } + public inline fun buildBuffer(size: Int, crossinline initializer: (Int) -> Complex): Buffer = Buffer.complex(size) { initializer(it) } @@ -29,7 +34,7 @@ public class ComplexNDField(override val shape: IntArray) : */ override fun map( arg: NDBuffer, - transform: ComplexField.(Complex) -> Complex + transform: ComplexField.(Complex) -> Complex, ): ComplexNDElement { check(arg) val array = buildBuffer(arg.strides.linearSize) { offset -> ComplexField.transform(arg.buffer[offset]) } @@ -43,7 +48,7 @@ public class ComplexNDField(override val shape: IntArray) : override fun mapIndexed( arg: NDBuffer, - transform: ComplexField.(index: IntArray, Complex) -> Complex + transform: ComplexField.(index: IntArray, Complex) -> Complex, ): ComplexNDElement { check(arg) @@ -60,7 +65,7 @@ public class ComplexNDField(override val shape: IntArray) : override fun combine( a: NDBuffer, b: NDBuffer, - transform: ComplexField.(Complex, Complex) -> Complex + transform: ComplexField.(Complex, Complex) -> Complex, ): ComplexNDElement { check(a, b) @@ -141,7 +146,7 @@ public fun NDField.Companion.complex(vararg shape: Int): ComplexNDField = Comple public fun NDElement.Companion.complex( vararg shape: Int, - initializer: ComplexField.(IntArray) -> Complex + initializer: ComplexField.(IntArray) -> Complex, ): ComplexNDElement = NDField.complex(*shape).produce(initializer) /** diff --git a/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/RealBufferField.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/RealBufferField.kt index f7b2ee31e..3f4d15c4d 100644 --- a/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/RealBufferField.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/RealBufferField.kt @@ -150,6 +150,8 @@ public class RealBufferField(public val size: Int) : ExtendedField by lazy { RealBuffer(size) { 0.0 } } public override val one: Buffer by lazy { RealBuffer(size) { 1.0 } } + override fun number(value: Number): Buffer = RealBuffer(size) { value.toDouble() } + public 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) diff --git a/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/RealNDField.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/RealNDField.kt index ed28fb9f2..3eb1dc4ca 100644 --- a/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/RealNDField.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/RealNDField.kt @@ -1,13 +1,17 @@ package kscience.kmath.structures +import kscience.kmath.misc.UnstableKMathAPI import kscience.kmath.operations.FieldElement import kscience.kmath.operations.RealField +import kscience.kmath.operations.RingWithNumbers public typealias RealNDElement = BufferedNDFieldElement +@OptIn(UnstableKMathAPI::class) public class RealNDField(override val shape: IntArray) : BufferedNDField, - ExtendedNDField> { + ExtendedNDField>, + RingWithNumbers>{ override val strides: Strides = DefaultStrides(shape) @@ -15,7 +19,12 @@ public class RealNDField(override val shape: IntArray) : override val zero: RealNDElement by lazy { produce { zero } } override val one: RealNDElement by lazy { produce { one } } - public inline fun buildBuffer(size: Int, crossinline initializer: (Int) -> Double): Buffer = + override fun number(value: Number): NDBuffer { + val d = value.toDouble() + return produce { d } + } + + private inline fun buildBuffer(size: Int, crossinline initializer: (Int) -> Double): Buffer = RealBuffer(DoubleArray(size) { initializer(it) }) /** @@ -59,7 +68,8 @@ public class RealNDField(override val shape: IntArray) : check(a, b) return BufferedNDFieldElement( this, - buildBuffer(strides.linearSize) { offset -> elementContext.transform(a.buffer[offset], b.buffer[offset]) }) + buildBuffer(strides.linearSize) { offset -> elementContext.transform(a.buffer[offset], b.buffer[offset]) } + ) } override fun NDBuffer.toElement(): FieldElement, *, out BufferedNDField> = diff --git a/kmath-core/src/commonTest/kotlin/kscience/kmath/structures/NDFieldTest.kt b/kmath-core/src/commonTest/kotlin/kscience/kmath/structures/NDFieldTest.kt index b763ec7de..1129a8a36 100644 --- a/kmath-core/src/commonTest/kotlin/kscience/kmath/structures/NDFieldTest.kt +++ b/kmath-core/src/commonTest/kotlin/kscience/kmath/structures/NDFieldTest.kt @@ -1,14 +1,13 @@ package kscience.kmath.structures import kscience.kmath.operations.internal.FieldVerifier -import kscience.kmath.operations.invoke import kotlin.test.Test import kotlin.test.assertEquals internal class NDFieldTest { @Test fun verify() { - (NDField.real(12, 32)) { FieldVerifier(this, one + 3, one - 23, one * 12, 6.66) } + NDField.real(12, 32).run { FieldVerifier(this, one + 3, one - 23, one * 12, 6.66) } } @Test diff --git a/kmath-core/src/jvmMain/kotlin/kscience/kmath/operations/BigNumbers.kt b/kmath-core/src/jvmMain/kotlin/kscience/kmath/operations/BigNumbers.kt index 2f0978237..9bd6a9fc4 100644 --- a/kmath-core/src/jvmMain/kotlin/kscience/kmath/operations/BigNumbers.kt +++ b/kmath-core/src/jvmMain/kotlin/kscience/kmath/operations/BigNumbers.kt @@ -7,7 +7,7 @@ import java.math.MathContext /** * A field over [BigInteger]. */ -public object JBigIntegerField : Field { +public object JBigIntegerField : Field, NumericAlgebra { public override val zero: BigInteger get() = BigInteger.ZERO @@ -28,9 +28,9 @@ public object JBigIntegerField : Field { * * @property mathContext the [MathContext] to use. */ -public abstract class JBigDecimalFieldBase internal constructor(public val mathContext: MathContext = MathContext.DECIMAL64) : - Field, - PowerOperations { +public abstract class JBigDecimalFieldBase internal constructor( + private val mathContext: MathContext = MathContext.DECIMAL64, +) : Field, PowerOperations, NumericAlgebra { public override val zero: BigDecimal get() = BigDecimal.ZERO diff --git a/kmath-ejml/src/main/kotlin/kscience/kmath/ejml/EjmlMatrix.kt b/kmath-ejml/src/main/kotlin/kscience/kmath/ejml/EjmlMatrix.kt index 5b7d0a01b..a7d571b58 100644 --- a/kmath-ejml/src/main/kotlin/kscience/kmath/ejml/EjmlMatrix.kt +++ b/kmath-ejml/src/main/kotlin/kscience/kmath/ejml/EjmlMatrix.kt @@ -12,8 +12,10 @@ import org.ejml.simple.SimpleMatrix * @property origin the underlying [SimpleMatrix]. * @author Iaroslav Postovalov */ -public class EjmlMatrix(public val origin: SimpleMatrix, features: Set = emptySet()) : - FeaturedMatrix { +public class EjmlMatrix( + public val origin: SimpleMatrix, + features: Set = emptySet() +) : FeaturedMatrix { public override val rowNum: Int get() = origin.numRows() @@ -88,11 +90,7 @@ public class EjmlMatrix(public val origin: SimpleMatrix, features: Set ?: return false) } - public override fun hashCode(): Int { - var result = origin.hashCode() - result = 31 * result + features.hashCode() - return result - } + public override fun hashCode(): Int = origin.hashCode() - public override fun toString(): String = "EjmlMatrix(origin=$origin, features=$features)" + public override fun toString(): String = "EjmlMatrix($origin)" } diff --git a/kmath-ejml/src/main/kotlin/kscience/kmath/ejml/EjmlMatrixContext.kt b/kmath-ejml/src/main/kotlin/kscience/kmath/ejml/EjmlMatrixContext.kt index f8791b72c..c8789a4a7 100644 --- a/kmath-ejml/src/main/kotlin/kscience/kmath/ejml/EjmlMatrixContext.kt +++ b/kmath-ejml/src/main/kotlin/kscience/kmath/ejml/EjmlMatrixContext.kt @@ -1,7 +1,9 @@ package kscience.kmath.ejml +import kscience.kmath.linear.InverseMatrixFeature import kscience.kmath.linear.MatrixContext import kscience.kmath.linear.Point +import kscience.kmath.linear.getFeature import kscience.kmath.structures.Matrix import org.ejml.simple.SimpleMatrix @@ -77,3 +79,7 @@ public fun EjmlMatrixContext.solve(a: Matrix, b: Matrix): EjmlMa */ public fun EjmlMatrixContext.solve(a: Matrix, b: Point): EjmlVector = EjmlVector(a.toEjml().origin.solve(b.toEjml().origin)) + +public fun EjmlMatrix.inverted(): EjmlMatrix = getFeature>()!!.inverse as EjmlMatrix + +public fun EjmlMatrixContext.inverse(matrix: Matrix): Matrix = matrix.toEjml().inverted() \ No newline at end of file diff --git a/kmath-nd4j/src/main/kotlin/kscience.kmath.nd4j/Nd4jArrayAlgebra.kt b/kmath-nd4j/src/main/kotlin/kscience.kmath.nd4j/Nd4jArrayAlgebra.kt index a8c874fc3..db2a44861 100644 --- a/kmath-nd4j/src/main/kotlin/kscience.kmath.nd4j/Nd4jArrayAlgebra.kt +++ b/kmath-nd4j/src/main/kotlin/kscience.kmath.nd4j/Nd4jArrayAlgebra.kt @@ -1,5 +1,6 @@ package kscience.kmath.nd4j +import kscience.kmath.misc.UnstableKMathAPI import kscience.kmath.operations.* import kscience.kmath.structures.NDAlgebra import kscience.kmath.structures.NDField @@ -35,7 +36,7 @@ public interface Nd4jArrayAlgebra : NDAlgebra> public override fun mapIndexed( arg: Nd4jArrayStructure, - transform: C.(index: IntArray, T) -> T + transform: C.(index: IntArray, T) -> T, ): Nd4jArrayStructure { check(arg) val new = Nd4j.create(*shape).wrap() @@ -46,7 +47,7 @@ public interface Nd4jArrayAlgebra : NDAlgebra> public override fun combine( a: Nd4jArrayStructure, b: Nd4jArrayStructure, - transform: C.(T, T) -> T + transform: C.(T, T) -> T, ): Nd4jArrayStructure { check(a, b) val new = Nd4j.create(*shape).wrap() @@ -61,8 +62,8 @@ public interface Nd4jArrayAlgebra : NDAlgebra> * @param T the type of the element contained in ND structure. * @param S the type of space of structure elements. */ -public interface Nd4jArraySpace : NDSpace>, - Nd4jArrayAlgebra where S : Space { +public interface Nd4jArraySpace> : NDSpace>, Nd4jArrayAlgebra { + public override val zero: Nd4jArrayStructure get() = Nd4j.zeros(*shape).wrap() @@ -103,7 +104,9 @@ public interface Nd4jArraySpace : NDSpace>, * @param T the type of the element contained in ND structure. * @param R the type of ring of structure elements. */ -public interface Nd4jArrayRing : NDRing>, Nd4jArraySpace where R : Ring { +@OptIn(UnstableKMathAPI::class) +public interface Nd4jArrayRing> : NDRing>, Nd4jArraySpace { + public override val one: Nd4jArrayStructure get() = Nd4j.ones(*shape).wrap() @@ -111,21 +114,21 @@ public interface Nd4jArrayRing : NDRing>, Nd4j check(a, b) return a.ndArray.mul(b.ndArray).wrap() } - - public override operator fun Nd4jArrayStructure.minus(b: Number): Nd4jArrayStructure { - check(this) - return ndArray.sub(b).wrap() - } - - public override operator fun Nd4jArrayStructure.plus(b: Number): Nd4jArrayStructure { - check(this) - return ndArray.add(b).wrap() - } - - public override operator fun Number.minus(b: Nd4jArrayStructure): Nd4jArrayStructure { - check(b) - return b.ndArray.rsub(this).wrap() - } +// +// public override operator fun Nd4jArrayStructure.minus(b: Number): Nd4jArrayStructure { +// check(this) +// return ndArray.sub(b).wrap() +// } +// +// public override operator fun Nd4jArrayStructure.plus(b: Number): Nd4jArrayStructure { +// check(this) +// return ndArray.add(b).wrap() +// } +// +// public override operator fun Number.minus(b: Nd4jArrayStructure): Nd4jArrayStructure { +// check(b) +// return b.ndArray.rsub(this).wrap() +// } public companion object { private val intNd4jArrayRingCache: ThreadLocal> = @@ -165,7 +168,8 @@ public interface Nd4jArrayRing : NDRing>, Nd4j * @param N the type of ND structure. * @param F the type field of structure elements. */ -public interface Nd4jArrayField : NDField>, Nd4jArrayRing where F : Field { +public interface Nd4jArrayField> : NDField>, Nd4jArrayRing { + public override fun divide(a: Nd4jArrayStructure, b: Nd4jArrayStructure): Nd4jArrayStructure { check(a, b) return a.ndArray.div(b.ndArray).wrap() diff --git a/settings.gradle.kts b/settings.gradle.kts index 025e4a3c6..a1ea40148 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -8,8 +8,8 @@ pluginManagement { maven("https://dl.bintray.com/kotlin/kotlinx") } - val toolsVersion = "0.7.2-dev-2" - val kotlinVersion = "1.4.21" + val toolsVersion = "0.7.3-1.4.30-RC" + val kotlinVersion = "1.4.30-RC" plugins { id("kotlinx.benchmark") version "0.2.0-dev-20"