Syncing with dev

This commit is contained in:
Roland Grinis 2021-01-20 20:30:19 +00:00
commit 40895e5936
47 changed files with 635 additions and 545 deletions

View File

@ -4,27 +4,28 @@
### Added ### Added
- `fun` annotation for SAM interfaces in library - `fun` annotation for SAM interfaces in library
- Explicit `public` visibility for all public APIs - Explicit `public` visibility for all public APIs
- Better trigonometric and hyperbolic functions for `AutoDiffField` (https://github.com/mipt-npm/kmath/pull/140). - Better trigonometric and hyperbolic functions for `AutoDiffField` (https://github.com/mipt-npm/kmath/pull/140)
- Automatic README generation for features (#139) - Automatic README generation for features (#139)
- Native support for `memory`, `core` and `dimensions` - Native support for `memory`, `core` and `dimensions`
- `kmath-ejml` to supply EJML SimpleMatrix wrapper (https://github.com/mipt-npm/kmath/pull/136). - `kmath-ejml` to supply EJML SimpleMatrix wrapper (https://github.com/mipt-npm/kmath/pull/136)
- A separate `Symbol` entity, which is used for global unbound symbol. - A separate `Symbol` entity, which is used for global unbound symbol.
- A `Symbol` indexing scope. - A `Symbol` indexing scope.
- Basic optimization API for Commons-math. - Basic optimization API for Commons-math.
- Chi squared optimization for array-like data in CM - Chi squared optimization for array-like data in CM
- `Fitting` utility object in prob/stat - `Fitting` utility object in prob/stat
- ND4J support module submitting `NDStructure` and `NDAlgebra` over `INDArray`. - ND4J support module submitting `NDStructure` and `NDAlgebra` over `INDArray`
- Coroutine-deterministic Monte-Carlo scope with a random number generator. - Coroutine-deterministic Monte-Carlo scope with a random number generator
- Some minor utilities to `kmath-for-real`. - Some minor utilities to `kmath-for-real`
- Generic operation result parameter to `MatrixContext` - Generic operation result parameter to `MatrixContext`
- New `MatrixFeature` interfaces for matrix decompositions
### Changed ### Changed
- Package changed from `scientifik` to `kscience.kmath`. - Package changed from `scientifik` to `kscience.kmath`
- Gradle version: 6.6 -> 6.7.1 - Gradle version: 6.6 -> 6.8
- Minor exceptions refactor (throwing `IllegalArgumentException` by argument checks instead of `IllegalStateException`) - Minor exceptions refactor (throwing `IllegalArgumentException` by argument checks instead of `IllegalStateException`)
- `Polynomial` secondary constructor made function. - `Polynomial` secondary constructor made function
- Kotlin version: 1.3.72 -> 1.4.20 - Kotlin version: 1.3.72 -> 1.4.21
- `kmath-ast` doesn't depend on heavy `kotlin-reflect` library. - `kmath-ast` doesn't depend on heavy `kotlin-reflect` library
- Full autodiff refactoring based on `Symbol` - Full autodiff refactoring based on `Symbol`
- `kmath-prob` renamed to `kmath-stat` - `kmath-prob` renamed to `kmath-stat`
- Grid generators moved to `kmath-for-real` - Grid generators moved to `kmath-for-real`
@ -32,6 +33,8 @@
- Optimized dot product for buffer matrices moved to `kmath-for-real` - Optimized dot product for buffer matrices moved to `kmath-for-real`
- EjmlMatrix context is an object - EjmlMatrix context is an object
- Matrix LUP `inverse` renamed to `inverseWithLUP` - Matrix LUP `inverse` renamed to `inverseWithLUP`
- `NumericAlgebra` moved outside of regular algebra chain (`Ring` no longer implements it).
- Features moved to NDStructure and became transparent.
### Deprecated ### Deprecated

View File

@ -4,7 +4,7 @@ plugins {
id("ru.mipt.npm.project") 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 bintrayRepo: String by extra("kscience")
internal val githubProject: String by extra("kmath") internal val githubProject: String by extra("kmath")

View File

@ -28,7 +28,7 @@ internal class ExpressionsInterpretersBenchmark {
@Benchmark @Benchmark
fun mstExpression() { fun mstExpression() {
val expr = algebra.mstInField { val expr = algebra.mstInField {
symbol("x") * number(2.0) + number(2.0) / symbol("x") - number(16.0) symbol("x") * 2.0 + 2.0 / symbol("x") - 16.0
} }
invokeAndSum(expr) invokeAndSum(expr)
@ -37,7 +37,7 @@ internal class ExpressionsInterpretersBenchmark {
@Benchmark @Benchmark
fun asmExpression() { fun asmExpression() {
val expr = algebra.mstInField { val expr = algebra.mstInField {
symbol("x") * number(2.0) + number(2.0) / symbol("x") - number(16.0) symbol("x") * 2.0 + 2.0 / symbol("x") - 16.0
}.compile() }.compile()
invokeAndSum(expr) invokeAndSum(expr)

View File

@ -2,9 +2,8 @@ package kscience.kmath.benchmarks
import kotlinx.benchmark.Benchmark import kotlinx.benchmark.Benchmark
import kscience.kmath.commons.linear.CMMatrixContext import kscience.kmath.commons.linear.CMMatrixContext
import kscience.kmath.commons.linear.toCM
import kscience.kmath.ejml.EjmlMatrixContext import kscience.kmath.ejml.EjmlMatrixContext
import kscience.kmath.ejml.toEjml
import kscience.kmath.linear.BufferMatrixContext import kscience.kmath.linear.BufferMatrixContext
import kscience.kmath.linear.RealMatrixContext import kscience.kmath.linear.RealMatrixContext
import kscience.kmath.linear.real import kscience.kmath.linear.real
@ -26,11 +25,11 @@ class DotBenchmark {
val matrix1 = Matrix.real(dim, dim) { i, j -> if (i <= j) random.nextDouble() else 0.0 } val matrix1 = Matrix.real(dim, dim) { i, j -> if (i <= j) random.nextDouble() else 0.0 }
val matrix2 = Matrix.real(dim, dim) { i, j -> if (i <= j) random.nextDouble() else 0.0 } val matrix2 = Matrix.real(dim, dim) { i, j -> if (i <= j) random.nextDouble() else 0.0 }
val cmMatrix1 = matrix1.toCM() val cmMatrix1 = CMMatrixContext { matrix1.toCM() }
val cmMatrix2 = matrix2.toCM() val cmMatrix2 = CMMatrixContext { matrix2.toCM() }
val ejmlMatrix1 = matrix1.toEjml() val ejmlMatrix1 = EjmlMatrixContext { matrix1.toEjml() }
val ejmlMatrix2 = matrix2.toEjml() val ejmlMatrix2 = EjmlMatrixContext { matrix2.toEjml() }
} }
@Benchmark @Benchmark
@ -49,9 +48,10 @@ class DotBenchmark {
@Benchmark @Benchmark
fun ejmlMultiplicationwithConversion() { fun ejmlMultiplicationwithConversion() {
EjmlMatrixContext {
val ejmlMatrix1 = matrix1.toEjml() val ejmlMatrix1 = matrix1.toEjml()
val ejmlMatrix2 = matrix2.toEjml() val ejmlMatrix2 = matrix2.toEjml()
EjmlMatrixContext {
ejmlMatrix1 dot ejmlMatrix2 ejmlMatrix1 dot ejmlMatrix2
} }
} }

View File

@ -5,10 +5,8 @@ import kotlinx.benchmark.Benchmark
import kscience.kmath.commons.linear.CMMatrixContext import kscience.kmath.commons.linear.CMMatrixContext
import kscience.kmath.commons.linear.CMMatrixContext.dot import kscience.kmath.commons.linear.CMMatrixContext.dot
import kscience.kmath.commons.linear.inverse import kscience.kmath.commons.linear.inverse
import kscience.kmath.commons.linear.toCM
import kscience.kmath.ejml.EjmlMatrixContext import kscience.kmath.ejml.EjmlMatrixContext
import kscience.kmath.ejml.inverse import kscience.kmath.ejml.inverse
import kscience.kmath.ejml.toEjml
import kscience.kmath.operations.invoke import kscience.kmath.operations.invoke
import kscience.kmath.structures.Matrix import kscience.kmath.structures.Matrix
import org.openjdk.jmh.annotations.Scope import org.openjdk.jmh.annotations.Scope
@ -35,16 +33,14 @@ class LinearAlgebraBenchmark {
@Benchmark @Benchmark
fun cmLUPInversion() { fun cmLUPInversion() {
CMMatrixContext { CMMatrixContext {
val cm = matrix.toCM() //avoid overhead on conversion inverse(matrix)
inverse(cm)
} }
} }
@Benchmark @Benchmark
fun ejmlInverse() { fun ejmlInverse() {
EjmlMatrixContext { EjmlMatrixContext {
val km = matrix.toEjml() //avoid overhead on conversion inverse(matrix)
inverse(km)
} }
} }
} }

View File

@ -3,7 +3,6 @@ package kscience.kmath.commons.prob
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async import kotlinx.coroutines.async
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import kscience.kmath.chains.BlockingRealChain
import kscience.kmath.stat.* import kscience.kmath.stat.*
import org.apache.commons.rng.sampling.distribution.ZigguratNormalizedGaussianSampler import org.apache.commons.rng.sampling.distribution.ZigguratNormalizedGaussianSampler
import org.apache.commons.rng.simple.RandomSource import org.apache.commons.rng.simple.RandomSource
@ -13,7 +12,7 @@ import java.time.Instant
private fun runChain(): Duration { private fun runChain(): Duration {
val generator = RandomGenerator.fromSource(RandomSource.MT, 123L) val generator = RandomGenerator.fromSource(RandomSource.MT, 123L)
val normal = Distribution.normal(NormalSamplerMethod.Ziggurat) val normal = Distribution.normal(NormalSamplerMethod.Ziggurat)
val chain = normal.sample(generator) as BlockingRealChain val chain = normal.sample(generator)
val startTime = Instant.now() val startTime = Instant.now()
var sum = 0.0 var sum = 0.0

View File

@ -11,7 +11,7 @@ fun main() {
val n = 1000 val n = 1000
val realField = NDField.real(dim, dim) val realField = NDField.real(dim, dim)
val complexField = NDField.complex(dim, dim) val complexField: ComplexNDField = NDField.complex(dim, dim)
val realTime = measureTimeMillis { val realTime = measureTimeMillis {
realField { realField {

View File

@ -33,7 +33,7 @@ fun main() {
measureAndPrint("Automatic field addition") { measureAndPrint("Automatic field addition") {
autoField { autoField {
var res: NDBuffer<Double> = one var res: NDBuffer<Double> = one
repeat(n) { res += number(1.0) } repeat(n) { res += 1.0 }
} }
} }
@ -52,7 +52,7 @@ fun main() {
measureAndPrint("Nd4j specialized addition") { measureAndPrint("Nd4j specialized addition") {
nd4jField { nd4jField {
var res = one var res = one
repeat(n) { res += 1.0 as Number } repeat(n) { res += 1.0 }
} }
} }
@ -73,7 +73,7 @@ fun main() {
genericField { genericField {
var res: NDBuffer<Double> = one var res: NDBuffer<Double> = one
repeat(n) { 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 }
} }
} }
} }

View File

@ -1,5 +1,6 @@
package kscience.kmath.ast package kscience.kmath.ast
import kscience.kmath.misc.UnstableKMathAPI
import kscience.kmath.operations.* import kscience.kmath.operations.*
/** /**
@ -25,8 +26,11 @@ public object MstSpace : Space<MST>, NumericAlgebra<MST> {
public override fun number(value: Number): MST.Numeric = MstAlgebra.number(value) 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 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 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.unaryPlus(): MST.Unary =
public override operator fun MST.unaryMinus(): MST.Unary = unaryOperationFunction(SpaceOperations.MINUS_OPERATION)(this) 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 = public override operator fun MST.minus(b: MST): MST.Binary =
binaryOperationFunction(SpaceOperations.MINUS_OPERATION)(this, b) binaryOperationFunction(SpaceOperations.MINUS_OPERATION)(this, b)
@ -44,7 +48,8 @@ public object MstSpace : Space<MST>, NumericAlgebra<MST> {
/** /**
* [Ring] over [MST] nodes. * [Ring] over [MST] nodes.
*/ */
public object MstRing : Ring<MST>, NumericAlgebra<MST> { @OptIn(UnstableKMathAPI::class)
public object MstRing : Ring<MST>, RingWithNumbers<MST> {
public override val zero: MST.Numeric public override val zero: MST.Numeric
get() = MstSpace.zero get() = MstSpace.zero
@ -54,7 +59,9 @@ public object MstRing : Ring<MST>, NumericAlgebra<MST> {
public override fun symbol(value: String): MST.Symbolic = MstSpace.symbol(value) 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 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, 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.unaryPlus(): MST.Unary = MstSpace { +this@unaryPlus }
public override operator fun MST.unaryMinus(): MST.Unary = MstSpace { -this@unaryMinus } 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 } public override operator fun MST.minus(b: MST): MST.Binary = MstSpace { this@minus - b }
@ -69,7 +76,8 @@ public object MstRing : Ring<MST>, NumericAlgebra<MST> {
/** /**
* [Field] over [MST] nodes. * [Field] over [MST] nodes.
*/ */
public object MstField : Field<MST> { @OptIn(UnstableKMathAPI::class)
public object MstField : Field<MST>, RingWithNumbers<MST> {
public override val zero: MST.Numeric public override val zero: MST.Numeric
get() = MstRing.zero get() = MstRing.zero
@ -81,7 +89,9 @@ public object MstField : Field<MST> {
public override fun add(a: MST, b: MST): MST.Binary = MstRing.add(a, b) 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, 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 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.unaryPlus(): MST.Unary = MstRing { +this@unaryPlus }
public override operator fun MST.unaryMinus(): MST.Unary = MstRing { -this@unaryMinus } 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 } public override operator fun MST.minus(b: MST): MST.Binary = MstRing { this@minus - b }
@ -89,13 +99,14 @@ public object MstField : Field<MST> {
public override fun binaryOperationFunction(operation: String): (left: MST, right: MST) -> MST.Binary = public override fun binaryOperationFunction(operation: String): (left: MST, right: MST) -> MST.Binary =
MstRing.binaryOperationFunction(operation) 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. * [ExtendedField] over [MST] nodes.
*/ */
public object MstExtendedField : ExtendedField<MST> { public object MstExtendedField : ExtendedField<MST>, NumericAlgebra<MST> {
public override val zero: MST.Numeric public override val zero: MST.Numeric
get() = MstField.zero get() = MstField.zero
@ -103,6 +114,7 @@ public object MstExtendedField : ExtendedField<MST> {
get() = MstField.one get() = MstField.one
public override fun symbol(value: String): MST.Symbolic = MstField.symbol(value) 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 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 cos(arg: MST): MST.Unary = unaryOperationFunction(TrigonometricOperations.COS_OPERATION)(arg)
public override fun tan(arg: MST): MST.Unary = unaryOperationFunction(TrigonometricOperations.TAN_OPERATION)(arg) public override fun tan(arg: MST): MST.Unary = unaryOperationFunction(TrigonometricOperations.TAN_OPERATION)(arg)
@ -132,5 +144,6 @@ public object MstExtendedField : ExtendedField<MST> {
public override fun binaryOperationFunction(operation: String): (left: MST, right: MST) -> MST.Binary = public override fun binaryOperationFunction(operation: String): (left: MST, right: MST) -> MST.Binary =
MstField.binaryOperationFunction(operation) 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)
} }

View File

@ -1,7 +1,9 @@
package kscience.kmath.commons.expressions package kscience.kmath.commons.expressions
import kscience.kmath.expressions.* import kscience.kmath.expressions.*
import kscience.kmath.misc.UnstableKMathAPI
import kscience.kmath.operations.ExtendedField import kscience.kmath.operations.ExtendedField
import kscience.kmath.operations.RingWithNumbers
import org.apache.commons.math3.analysis.differentiation.DerivativeStructure 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 order The derivation order.
* @property bindings The map of bindings values. All bindings are considered free parameters * @property bindings The map of bindings values. All bindings are considered free parameters
*/ */
@OptIn(UnstableKMathAPI::class)
public class DerivativeStructureField( public class DerivativeStructureField(
public val order: Int, public val order: Int,
bindings: Map<Symbol, Double>, bindings: Map<Symbol, Double>,
) : ExtendedField<DerivativeStructure>, ExpressionAlgebra<Double, DerivativeStructure> { ) : ExtendedField<DerivativeStructure>, ExpressionAlgebra<Double, DerivativeStructure>, RingWithNumbers<DerivativeStructure> {
public val numberOfVariables: Int = bindings.size public val numberOfVariables: Int = bindings.size
public override val zero: DerivativeStructure by lazy { DerivativeStructure(numberOfVariables, order) } public override val zero: DerivativeStructure by lazy { DerivativeStructure(numberOfVariables, order) }
public override val one: DerivativeStructure by lazy { DerivativeStructure(numberOfVariables, order, 1.0) } 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] * A class that implements both [DerivativeStructure] and a [Symbol]
*/ */

View File

@ -1,42 +1,28 @@
package kscience.kmath.commons.linear package kscience.kmath.commons.linear
import kscience.kmath.linear.* import kscience.kmath.linear.DiagonalFeature
import kscience.kmath.linear.MatrixContext
import kscience.kmath.linear.Point
import kscience.kmath.linear.origin
import kscience.kmath.misc.UnstableKMathAPI
import kscience.kmath.structures.Matrix import kscience.kmath.structures.Matrix
import kscience.kmath.structures.NDStructure
import org.apache.commons.math3.linear.* import org.apache.commons.math3.linear.*
import kotlin.reflect.KClass
import kotlin.reflect.cast
public class CMMatrix(public val origin: RealMatrix, features: Set<MatrixFeature>? = null) : FeaturedMatrix<Double> { public inline class CMMatrix(public val origin: RealMatrix) : Matrix<Double> {
public override val rowNum: Int get() = origin.rowDimension public override val rowNum: Int get() = origin.rowDimension
public override val colNum: Int get() = origin.columnDimension public override val colNum: Int get() = origin.columnDimension
public override val features: Set<MatrixFeature> = features ?: sequence<MatrixFeature> { @UnstableKMathAPI
if (origin is DiagonalMatrix) yield(DiagonalFeature) override fun <T : Any> getFeature(type: KClass<T>): T? = when (type) {
}.toHashSet() DiagonalFeature::class -> if (origin is DiagonalMatrix) DiagonalFeature else null
else -> null
public override fun suggestFeature(vararg features: MatrixFeature): CMMatrix = }?.let { type.cast(it) }
CMMatrix(origin, this.features + features)
public override operator fun get(i: Int, j: Int): Double = origin.getEntry(i, j) public override operator fun get(i: Int, j: Int): Double = origin.getEntry(i, j)
public override fun equals(other: Any?): Boolean {
return NDStructure.equals(this, other as? NDStructure<*> ?: return false)
} }
public override fun hashCode(): Int {
var result = origin.hashCode()
result = 31 * result + features.hashCode()
return result
}
}
//TODO move inside context
public fun Matrix<Double>.toCM(): CMMatrix = if (this is CMMatrix) {
this
} else {
//TODO add feature analysis
val array = Array(rowNum) { i -> DoubleArray(colNum) { j -> get(i, j) } }
CMMatrix(Array2DRowRealMatrix(array))
}
public fun RealMatrix.asMatrix(): CMMatrix = CMMatrix(this) public fun RealMatrix.asMatrix(): CMMatrix = CMMatrix(this)
@ -61,6 +47,16 @@ public object CMMatrixContext : MatrixContext<Double, CMMatrix> {
return CMMatrix(Array2DRowRealMatrix(array)) return CMMatrix(Array2DRowRealMatrix(array))
} }
@OptIn(UnstableKMathAPI::class)
public fun Matrix<Double>.toCM(): CMMatrix = when (val matrix = origin) {
is CMMatrix -> matrix
else -> {
//TODO add feature analysis
val array = Array(rowNum) { i -> DoubleArray(colNum) { j -> get(i, j) } }
CMMatrix(Array2DRowRealMatrix(array))
}
}
public override fun Matrix<Double>.dot(other: Matrix<Double>): CMMatrix = public override fun Matrix<Double>.dot(other: Matrix<Double>): CMMatrix =
CMMatrix(toCM().origin.multiply(other.toCM().origin)) CMMatrix(toCM().origin.multiply(other.toCM().origin))

View File

@ -7,8 +7,9 @@ import kscience.kmath.operations.*
* *
* @param algebra The algebra to provide for Expressions built. * @param algebra The algebra to provide for Expressions built.
*/ */
public abstract class FunctionalExpressionAlgebra<T, A : Algebra<T>>(public val algebra: A) : public abstract class FunctionalExpressionAlgebra<T, A : Algebra<T>>(
ExpressionAlgebra<T, Expression<T>> { public val algebra: A,
) : ExpressionAlgebra<T, Expression<T>> {
/** /**
* Builds an Expression of constant expression which does not depend on arguments. * Builds an Expression of constant expression which does not depend on arguments.
*/ */
@ -42,8 +43,9 @@ public abstract class FunctionalExpressionAlgebra<T, A : Algebra<T>>(public val
/** /**
* A context class for [Expression] construction for [Space] algebras. * A context class for [Expression] construction for [Space] algebras.
*/ */
public open class FunctionalExpressionSpace<T, A : Space<T>>(algebra: A) : public open class FunctionalExpressionSpace<T, A : Space<T>>(
FunctionalExpressionAlgebra<T, A>(algebra), Space<Expression<T>> { algebra: A,
) : FunctionalExpressionAlgebra<T, A>(algebra), Space<Expression<T>> {
public override val zero: Expression<T> get() = const(algebra.zero) public override val zero: Expression<T> get() = const(algebra.zero)
/** /**
@ -71,8 +73,9 @@ public open class FunctionalExpressionSpace<T, A : Space<T>>(algebra: A) :
super<FunctionalExpressionAlgebra>.binaryOperationFunction(operation) super<FunctionalExpressionAlgebra>.binaryOperationFunction(operation)
} }
public open class FunctionalExpressionRing<T, A>(algebra: A) : FunctionalExpressionSpace<T, A>(algebra), public open class FunctionalExpressionRing<T, A : Ring<T>>(
Ring<Expression<T>> where A : Ring<T>, A : NumericAlgebra<T> { algebra: A,
) : FunctionalExpressionSpace<T, A>(algebra), Ring<Expression<T>> {
public override val one: Expression<T> public override val one: Expression<T>
get() = const(algebra.one) get() = const(algebra.one)
@ -92,9 +95,9 @@ public open class FunctionalExpressionRing<T, A>(algebra: A) : FunctionalExpress
super<FunctionalExpressionSpace>.binaryOperationFunction(operation) super<FunctionalExpressionSpace>.binaryOperationFunction(operation)
} }
public open class FunctionalExpressionField<T, A>(algebra: A) : public open class FunctionalExpressionField<T, A : Field<T>>(
FunctionalExpressionRing<T, A>(algebra), Field<Expression<T>> algebra: A,
where A : Field<T>, A : NumericAlgebra<T> { ) : FunctionalExpressionRing<T, A>(algebra), Field<Expression<T>> {
/** /**
* Builds an Expression of division an expression by another one. * Builds an Expression of division an expression by another one.
*/ */
@ -111,9 +114,12 @@ public open class FunctionalExpressionField<T, A>(algebra: A) :
super<FunctionalExpressionRing>.binaryOperationFunction(operation) super<FunctionalExpressionRing>.binaryOperationFunction(operation)
} }
public open class FunctionalExpressionExtendedField<T, A>(algebra: A) : public open class FunctionalExpressionExtendedField<T, A : ExtendedField<T>>(
FunctionalExpressionField<T, A>(algebra), algebra: A,
ExtendedField<Expression<T>> where A : ExtendedField<T>, A : NumericAlgebra<T> { ) : FunctionalExpressionField<T, A>(algebra), ExtendedField<Expression<T>> {
override fun number(value: Number): Expression<T> = const(algebra.number(value))
public override fun sin(arg: Expression<T>): Expression<T> = public override fun sin(arg: Expression<T>): Expression<T> =
unaryOperationFunction(TrigonometricOperations.SIN_OPERATION)(arg) unaryOperationFunction(TrigonometricOperations.SIN_OPERATION)(arg)
@ -135,7 +141,8 @@ public open class FunctionalExpressionExtendedField<T, A>(algebra: A) :
public override fun exp(arg: Expression<T>): Expression<T> = public override fun exp(arg: Expression<T>): Expression<T> =
unaryOperationFunction(ExponentialOperations.EXP_OPERATION)(arg) unaryOperationFunction(ExponentialOperations.EXP_OPERATION)(arg)
public override fun ln(arg: Expression<T>): Expression<T> = unaryOperationFunction(ExponentialOperations.LN_OPERATION)(arg) public override fun ln(arg: Expression<T>): Expression<T> =
unaryOperationFunction(ExponentialOperations.LN_OPERATION)(arg)
public override fun unaryOperationFunction(operation: String): (arg: Expression<T>) -> Expression<T> = public override fun unaryOperationFunction(operation: String): (arg: Expression<T>) -> Expression<T> =
super<FunctionalExpressionField>.unaryOperationFunction(operation) super<FunctionalExpressionField>.unaryOperationFunction(operation)

View File

@ -1,6 +1,7 @@
package kscience.kmath.expressions package kscience.kmath.expressions
import kscience.kmath.linear.Point import kscience.kmath.linear.Point
import kscience.kmath.misc.UnstableKMathAPI
import kscience.kmath.operations.* import kscience.kmath.operations.*
import kscience.kmath.structures.asBuffer import kscience.kmath.structures.asBuffer
import kotlin.contracts.InvocationKind import kotlin.contracts.InvocationKind
@ -79,10 +80,11 @@ public fun <T : Any, F : Field<T>> F.simpleAutoDiff(
/** /**
* Represents field in context of which functions can be derived. * Represents field in context of which functions can be derived.
*/ */
@OptIn(UnstableKMathAPI::class)
public open class SimpleAutoDiffField<T : Any, F : Field<T>>( public open class SimpleAutoDiffField<T : Any, F : Field<T>>(
public val context: F, public val context: F,
bindings: Map<Symbol, T>, bindings: Map<Symbol, T>,
) : Field<AutoDiffValue<T>>, ExpressionAlgebra<T, AutoDiffValue<T>> { ) : Field<AutoDiffValue<T>>, ExpressionAlgebra<T, AutoDiffValue<T>>, RingWithNumbers<AutoDiffValue<T>> {
public override val zero: AutoDiffValue<T> public override val zero: AutoDiffValue<T>
get() = const(context.zero) get() = const(context.zero)

View File

@ -1,10 +1,7 @@
package kscience.kmath.linear package kscience.kmath.linear
import kscience.kmath.operations.Ring import kscience.kmath.operations.Ring
import kscience.kmath.structures.Buffer import kscience.kmath.structures.*
import kscience.kmath.structures.BufferFactory
import kscience.kmath.structures.NDStructure
import kscience.kmath.structures.asSequence
/** /**
* Basic implementation of Matrix space based on [NDStructure] * Basic implementation of Matrix space based on [NDStructure]
@ -27,8 +24,7 @@ public class BufferMatrix<T : Any>(
public override val rowNum: Int, public override val rowNum: Int,
public override val colNum: Int, public override val colNum: Int,
public val buffer: Buffer<out T>, public val buffer: Buffer<out T>,
public override val features: Set<MatrixFeature> = emptySet(), ) : Matrix<T> {
) : FeaturedMatrix<T> {
init { init {
require(buffer.size == rowNum * colNum) { "Dimension mismatch for matrix structure" } require(buffer.size == rowNum * colNum) { "Dimension mismatch for matrix structure" }
@ -36,9 +32,6 @@ public class BufferMatrix<T : Any>(
override val shape: IntArray get() = intArrayOf(rowNum, colNum) override val shape: IntArray get() = intArrayOf(rowNum, colNum)
public override fun suggestFeature(vararg features: MatrixFeature): BufferMatrix<T> =
BufferMatrix(rowNum, colNum, buffer, this.features + features)
public override operator fun get(index: IntArray): T = get(index[0], index[1]) public override operator fun get(index: IntArray): T = get(index[0], index[1])
public override operator fun get(i: Int, j: Int): T = buffer[i * colNum + j] public override operator fun get(i: Int, j: Int): T = buffer[i * colNum + j]
@ -50,23 +43,26 @@ public class BufferMatrix<T : Any>(
if (this === other) return true if (this === other) return true
return when (other) { return when (other) {
is NDStructure<*> -> return NDStructure.equals(this, other) is NDStructure<*> -> NDStructure.contentEquals(this, other)
else -> false else -> false
} }
} }
public override fun hashCode(): Int { override fun hashCode(): Int {
var result = buffer.hashCode() var result = rowNum
result = 31 * result + features.hashCode() result = 31 * result + colNum
result = 31 * result + buffer.hashCode()
return result return result
} }
public override fun toString(): String { public override fun toString(): String {
return if (rowNum <= 5 && colNum <= 5) return if (rowNum <= 5 && colNum <= 5)
"Matrix(rowsNum = $rowNum, colNum = $colNum, features=$features)\n" + "Matrix(rowsNum = $rowNum, colNum = $colNum)\n" +
rows.asSequence().joinToString(prefix = "(", postfix = ")", separator = "\n ") { buffer -> rows.asSequence().joinToString(prefix = "(", postfix = ")", separator = "\n ") { buffer ->
buffer.asSequence().joinToString(separator = "\t") { it.toString() } buffer.asSequence().joinToString(separator = "\t") { it.toString() }
} }
else "Matrix(rowsNum = $rowNum, colNum = $colNum, features=$features)" else "Matrix(rowsNum = $rowNum, colNum = $colNum)"
} }
} }

View File

@ -1,89 +0,0 @@
package kscience.kmath.linear
import kscience.kmath.operations.Ring
import kscience.kmath.structures.Matrix
import kscience.kmath.structures.Structure2D
import kscience.kmath.structures.asBuffer
import kotlin.math.sqrt
/**
* A [Matrix] that holds [MatrixFeature] objects.
*
* @param T the type of items.
*/
public interface FeaturedMatrix<T : Any> : Matrix<T> {
public override val shape: IntArray get() = intArrayOf(rowNum, colNum)
/**
* The set of features this matrix possesses.
*/
public val features: Set<MatrixFeature>
/**
* Suggest new feature for this matrix. The result is the new matrix that may or may not reuse existing data structure.
*
* The implementation does not guarantee to check that matrix actually have the feature, so one should be careful to
* add only those features that are valid.
*/
public fun suggestFeature(vararg features: MatrixFeature): FeaturedMatrix<T>
public companion object
}
public inline fun Structure2D.Companion.real(
rows: Int,
columns: Int,
initializer: (Int, Int) -> Double,
): BufferMatrix<Double> = MatrixContext.real.produce(rows, columns, initializer)
/**
* Build a square matrix from given elements.
*/
public fun <T : Any> Structure2D.Companion.square(vararg elements: T): FeaturedMatrix<T> {
val size: Int = sqrt(elements.size.toDouble()).toInt()
require(size * size == elements.size) { "The number of elements ${elements.size} is not a full square" }
val buffer = elements.asBuffer()
return BufferMatrix(size, size, buffer)
}
public val Matrix<*>.features: Set<MatrixFeature> get() = (this as? FeaturedMatrix)?.features ?: emptySet()
/**
* Check if matrix has the given feature class
*/
public inline fun <reified T : Any> Matrix<*>.hasFeature(): Boolean =
features.find { it is T } != null
/**
* Get the first feature matching given class. Does not guarantee that matrix has only one feature matching the criteria
*/
public inline fun <reified T : Any> Matrix<*>.getFeature(): T? =
features.filterIsInstance<T>().firstOrNull()
/**
* Diagonal matrix of ones. The matrix is virtual no actual matrix is created
*/
public fun <T : Any, R : Ring<T>> GenericMatrixContext<T, R, *>.one(rows: Int, columns: Int): FeaturedMatrix<T> =
VirtualMatrix(rows, columns, DiagonalFeature) { i, j ->
if (i == j) elementContext.one else elementContext.zero
}
/**
* A virtual matrix of zeroes
*/
public fun <T : Any, R : Ring<T>> GenericMatrixContext<T, R, *>.zero(rows: Int, columns: Int): FeaturedMatrix<T> =
VirtualMatrix(rows, columns) { _, _ -> elementContext.zero }
public class TransposedFeature<T : Any>(public val original: Matrix<T>) : MatrixFeature
/**
* Create a virtual transposed matrix without copying anything. `A.transpose().transpose() === A`
*/
public fun <T : Any> Matrix<T>.transpose(): Matrix<T> {
return getFeature<TransposedFeature<T>>()?.original ?: VirtualMatrix(
colNum,
rowNum,
setOf(TransposedFeature(this))
) { i, j -> get(j, i) }
}

View File

@ -1,13 +1,14 @@
package kscience.kmath.linear package kscience.kmath.linear
import kscience.kmath.misc.UnstableKMathAPI
import kscience.kmath.operations.* import kscience.kmath.operations.*
import kscience.kmath.structures.* import kscience.kmath.structures.*
/** /**
* Common implementation of [LupDecompositionFeature]. * Common implementation of [LupDecompositionFeature].
*/ */
public class LUPDecomposition<T : Any>( public class LupDecomposition<T : Any>(
public val context: MatrixContext<T, FeaturedMatrix<T>>, public val context: MatrixContext<T, Matrix<T>>,
public val elementContext: Field<T>, public val elementContext: Field<T>,
public val lu: Matrix<T>, public val lu: Matrix<T>,
public val pivot: IntArray, public val pivot: IntArray,
@ -18,13 +19,13 @@ public class LUPDecomposition<T : Any>(
* *
* L is a lower-triangular matrix with [Ring.one] in diagonal * L is a lower-triangular matrix with [Ring.one] in diagonal
*/ */
override val l: FeaturedMatrix<T> = VirtualMatrix(lu.shape[0], lu.shape[1], setOf(LFeature)) { i, j -> override val l: Matrix<T> = VirtualMatrix(lu.shape[0], lu.shape[1]) { i, j ->
when { when {
j < i -> lu[i, j] j < i -> lu[i, j]
j == i -> elementContext.one j == i -> elementContext.one
else -> elementContext.zero else -> elementContext.zero
} }
} } + LFeature
/** /**
@ -32,9 +33,9 @@ public class LUPDecomposition<T : Any>(
* *
* U is an upper-triangular matrix including the diagonal * U is an upper-triangular matrix including the diagonal
*/ */
override val u: FeaturedMatrix<T> = VirtualMatrix(lu.shape[0], lu.shape[1], setOf(UFeature)) { i, j -> override val u: Matrix<T> = VirtualMatrix(lu.shape[0], lu.shape[1]) { i, j ->
if (j >= i) lu[i, j] else elementContext.zero if (j >= i) lu[i, j] else elementContext.zero
} } + UFeature
/** /**
* Returns the P rows permutation matrix. * Returns the P rows permutation matrix.
@ -42,7 +43,7 @@ public class LUPDecomposition<T : Any>(
* P is a sparse matrix with exactly one element set to [Ring.one] in * P is a sparse matrix with exactly one element set to [Ring.one] in
* each row and each column, all other elements being set to [Ring.zero]. * each row and each column, all other elements being set to [Ring.zero].
*/ */
override val p: FeaturedMatrix<T> = VirtualMatrix(lu.shape[0], lu.shape[1]) { i, j -> override val p: Matrix<T> = VirtualMatrix(lu.shape[0], lu.shape[1]) { i, j ->
if (j == pivot[i]) elementContext.one else elementContext.zero if (j == pivot[i]) elementContext.one else elementContext.zero
} }
@ -63,12 +64,12 @@ internal fun <T : Comparable<T>, F : Field<T>> GenericMatrixContext<T, F, *>.abs
/** /**
* Create a lup decomposition of generic matrix. * Create a lup decomposition of generic matrix.
*/ */
public fun <T : Comparable<T>> MatrixContext<T, FeaturedMatrix<T>>.lup( public fun <T : Comparable<T>> MatrixContext<T, Matrix<T>>.lup(
factory: MutableBufferFactory<T>, factory: MutableBufferFactory<T>,
elementContext: Field<T>, elementContext: Field<T>,
matrix: Matrix<T>, matrix: Matrix<T>,
checkSingular: (T) -> Boolean, checkSingular: (T) -> Boolean,
): LUPDecomposition<T> { ): LupDecomposition<T> {
require(matrix.rowNum == matrix.colNum) { "LU decomposition supports only square matrices" } require(matrix.rowNum == matrix.colNum) { "LU decomposition supports only square matrices" }
val m = matrix.colNum val m = matrix.colNum
val pivot = IntArray(matrix.rowNum) val pivot = IntArray(matrix.rowNum)
@ -137,23 +138,23 @@ public fun <T : Comparable<T>> MatrixContext<T, FeaturedMatrix<T>>.lup(
for (row in col + 1 until m) lu[row, col] /= luDiag for (row in col + 1 until m) lu[row, col] /= luDiag
} }
return LUPDecomposition(this@lup, elementContext, lu.collect(), pivot, even) return LupDecomposition(this@lup, elementContext, lu.collect(), pivot, even)
} }
} }
} }
public inline fun <reified T : Comparable<T>, F : Field<T>> GenericMatrixContext<T, F, FeaturedMatrix<T>>.lup( public inline fun <reified T : Comparable<T>, F : Field<T>> GenericMatrixContext<T, F, Matrix<T>>.lup(
matrix: Matrix<T>, matrix: Matrix<T>,
noinline checkSingular: (T) -> Boolean, noinline checkSingular: (T) -> Boolean,
): LUPDecomposition<T> = lup(MutableBuffer.Companion::auto, elementContext, matrix, checkSingular) ): LupDecomposition<T> = lup(MutableBuffer.Companion::auto, elementContext, matrix, checkSingular)
public fun MatrixContext<Double, FeaturedMatrix<Double>>.lup(matrix: Matrix<Double>): LUPDecomposition<Double> = public fun MatrixContext<Double, Matrix<Double>>.lup(matrix: Matrix<Double>): LupDecomposition<Double> =
lup(Buffer.Companion::real, RealField, matrix) { it < 1e-11 } lup(Buffer.Companion::real, RealField, matrix) { it < 1e-11 }
public fun <T : Any> LUPDecomposition<T>.solveWithLUP( public fun <T : Any> LupDecomposition<T>.solveWithLUP(
factory: MutableBufferFactory<T>, factory: MutableBufferFactory<T>,
matrix: Matrix<T> matrix: Matrix<T>,
): FeaturedMatrix<T> { ): Matrix<T> {
require(matrix.rowNum == pivot.size) { "Matrix dimension mismatch. Expected ${pivot.size}, but got ${matrix.colNum}" } require(matrix.rowNum == pivot.size) { "Matrix dimension mismatch. Expected ${pivot.size}, but got ${matrix.colNum}" }
BufferAccessor2D(matrix.rowNum, matrix.colNum, factory).run { BufferAccessor2D(matrix.rowNum, matrix.colNum, factory).run {
@ -198,25 +199,41 @@ public fun <T : Any> LUPDecomposition<T>.solveWithLUP(
} }
} }
public inline fun <reified T : Any> LUPDecomposition<T>.solveWithLUP(matrix: Matrix<T>): Matrix<T> = public inline fun <reified T : Any> LupDecomposition<T>.solveWithLUP(matrix: Matrix<T>): Matrix<T> =
solveWithLUP(MutableBuffer.Companion::auto, matrix) solveWithLUP(MutableBuffer.Companion::auto, matrix)
/** /**
* Solve a linear equation **a*x = b** using LUP decomposition * Solve a linear equation **a*x = b** using LUP decomposition
*/ */
public inline fun <reified T : Comparable<T>, F : Field<T>> GenericMatrixContext<T, F, FeaturedMatrix<T>>.solveWithLUP( @OptIn(UnstableKMathAPI::class)
public inline fun <reified T : Comparable<T>, F : Field<T>> GenericMatrixContext<T, F, Matrix<T>>.solveWithLUP(
a: Matrix<T>, a: Matrix<T>,
b: Matrix<T>, b: Matrix<T>,
noinline bufferFactory: MutableBufferFactory<T> = MutableBuffer.Companion::auto, noinline bufferFactory: MutableBufferFactory<T> = MutableBuffer.Companion::auto,
noinline checkSingular: (T) -> Boolean, noinline checkSingular: (T) -> Boolean,
): FeaturedMatrix<T> { ): Matrix<T> {
// Use existing decomposition if it is provided by matrix // Use existing decomposition if it is provided by matrix
val decomposition = a.getFeature() ?: lup(bufferFactory, elementContext, a, checkSingular) val decomposition = a.getFeature() ?: lup(bufferFactory, elementContext, a, checkSingular)
return decomposition.solveWithLUP(bufferFactory, b) return decomposition.solveWithLUP(bufferFactory, b)
} }
public inline fun <reified T : Comparable<T>, F : Field<T>> GenericMatrixContext<T, F, FeaturedMatrix<T>>.inverseWithLUP( public inline fun <reified T : Comparable<T>, F : Field<T>> GenericMatrixContext<T, F, Matrix<T>>.inverseWithLUP(
matrix: Matrix<T>, matrix: Matrix<T>,
noinline bufferFactory: MutableBufferFactory<T> = MutableBuffer.Companion::auto, noinline bufferFactory: MutableBufferFactory<T> = MutableBuffer.Companion::auto,
noinline checkSingular: (T) -> Boolean, noinline checkSingular: (T) -> Boolean,
): FeaturedMatrix<T> = solveWithLUP(matrix, one(matrix.rowNum, matrix.colNum), bufferFactory, checkSingular) ): Matrix<T> = solveWithLUP(matrix, one(matrix.rowNum, matrix.colNum), bufferFactory, checkSingular)
@OptIn(UnstableKMathAPI::class)
public fun RealMatrixContext.solveWithLUP(a: Matrix<Double>, b: Matrix<Double>): Matrix<Double> {
// Use existing decomposition if it is provided by matrix
val bufferFactory: MutableBufferFactory<Double> = MutableBuffer.Companion::real
val decomposition: LupDecomposition<Double> = a.getFeature() ?: lup(bufferFactory, RealField, a) { it < 1e-11 }
return decomposition.solveWithLUP(bufferFactory, b)
}
/**
* Inverses a square matrix using LUP decomposition. Non square matrix will throw a error.
*/
public fun RealMatrixContext.inverseWithLUP(matrix: Matrix<Double>): Matrix<Double> =
solveWithLUP(matrix, one(matrix.rowNum, matrix.colNum))

View File

@ -1,12 +1,9 @@
package kscience.kmath.linear package kscience.kmath.linear
import kscience.kmath.structures.Buffer import kscience.kmath.structures.*
import kscience.kmath.structures.BufferFactory
import kscience.kmath.structures.Structure2D
import kscience.kmath.structures.asBuffer
public class MatrixBuilder(public val rows: Int, public val columns: Int) { public class MatrixBuilder(public val rows: Int, public val columns: Int) {
public operator fun <T : Any> invoke(vararg elements: T): FeaturedMatrix<T> { public operator fun <T : Any> invoke(vararg elements: T): Matrix<T> {
require(rows * columns == elements.size) { "The number of elements ${elements.size} is not equal $rows * $columns" } require(rows * columns == elements.size) { "The number of elements ${elements.size} is not equal $rows * $columns" }
val buffer = elements.asBuffer() val buffer = elements.asBuffer()
return BufferMatrix(rows, columns, buffer) return BufferMatrix(rows, columns, buffer)
@ -17,7 +14,7 @@ public class MatrixBuilder(public val rows: Int, public val columns: Int) {
public fun Structure2D.Companion.build(rows: Int, columns: Int): MatrixBuilder = MatrixBuilder(rows, columns) public fun Structure2D.Companion.build(rows: Int, columns: Int): MatrixBuilder = MatrixBuilder(rows, columns)
public fun <T : Any> Structure2D.Companion.row(vararg values: T): FeaturedMatrix<T> { public fun <T : Any> Structure2D.Companion.row(vararg values: T): Matrix<T> {
val buffer = values.asBuffer() val buffer = values.asBuffer()
return BufferMatrix(1, values.size, buffer) return BufferMatrix(1, values.size, buffer)
} }
@ -26,12 +23,12 @@ public inline fun <reified T : Any> Structure2D.Companion.row(
size: Int, size: Int,
factory: BufferFactory<T> = Buffer.Companion::auto, factory: BufferFactory<T> = Buffer.Companion::auto,
noinline builder: (Int) -> T noinline builder: (Int) -> T
): FeaturedMatrix<T> { ): Matrix<T> {
val buffer = factory(size, builder) val buffer = factory(size, builder)
return BufferMatrix(1, size, buffer) return BufferMatrix(1, size, buffer)
} }
public fun <T : Any> Structure2D.Companion.column(vararg values: T): FeaturedMatrix<T> { public fun <T : Any> Structure2D.Companion.column(vararg values: T): Matrix<T> {
val buffer = values.asBuffer() val buffer = values.asBuffer()
return BufferMatrix(values.size, 1, buffer) return BufferMatrix(values.size, 1, buffer)
} }
@ -40,7 +37,7 @@ public inline fun <reified T : Any> Structure2D.Companion.column(
size: Int, size: Int,
factory: BufferFactory<T> = Buffer.Companion::auto, factory: BufferFactory<T> = Buffer.Companion::auto,
noinline builder: (Int) -> T noinline builder: (Int) -> T
): FeaturedMatrix<T> { ): Matrix<T> {
val buffer = factory(size, builder) val buffer = factory(size, builder)
return BufferMatrix(size, 1, buffer) return BufferMatrix(size, 1, buffer)
} }

View File

@ -133,8 +133,6 @@ public interface GenericMatrixContext<T : Any, R : Ring<T>, out M : Matrix<T>> :
public override fun multiply(a: Matrix<T>, k: Number): M = public override fun multiply(a: Matrix<T>, k: Number): M =
produce(a.rowNum, a.colNum) { i, j -> elementContext { a[i, j] * k } } produce(a.rowNum, a.colNum) { i, j -> elementContext { a[i, j] * k } }
public operator fun Number.times(matrix: FeaturedMatrix<T>): M = multiply(matrix, this)
public override operator fun Matrix<T>.times(value: T): M = public override operator fun Matrix<T>.times(value: T): M =
produce(rowNum, colNum) { i, j -> elementContext { get(i, j) * value } } produce(rowNum, colNum) { i, j -> elementContext { get(i, j) * value } }
} }

View File

@ -1,5 +1,7 @@
package kscience.kmath.linear 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 * 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. * to optimize matrix operations performance in some cases or retrieve the APIs.
@ -9,17 +11,19 @@ public interface MatrixFeature
/** /**
* Matrices with this feature are considered to have only diagonal non-null elements. * Matrices with this feature are considered to have only diagonal non-null elements.
*/ */
public object DiagonalFeature : MatrixFeature public interface DiagonalFeature : MatrixFeature{
public companion object: DiagonalFeature
}
/** /**
* Matrices with this feature have all zero elements. * Matrices with this feature have all zero elements.
*/ */
public object ZeroFeature : MatrixFeature public object ZeroFeature : DiagonalFeature
/** /**
* Matrices with this feature have unit elements on diagonal and zero elements in all other places. * Matrices with this feature have unit elements on diagonal and zero elements in all other places.
*/ */
public object UnitFeature : MatrixFeature public object UnitFeature : DiagonalFeature
/** /**
* Matrices with this feature can be inverted: [inverse] = `a`<sup>-1</sup> where `a` is the owning matrix. * Matrices with this feature can be inverted: [inverse] = `a`<sup>-1</sup> where `a` is the owning matrix.
@ -30,7 +34,7 @@ public interface InverseMatrixFeature<T : Any> : MatrixFeature {
/** /**
* The inverse matrix of the matrix that owns this feature. * The inverse matrix of the matrix that owns this feature.
*/ */
public val inverse: FeaturedMatrix<T> public val inverse: Matrix<T>
} }
/** /**
@ -74,17 +78,17 @@ public interface LupDecompositionFeature<T : Any> : MatrixFeature {
/** /**
* The lower triangular matrix in this decomposition. It may have [LFeature]. * The lower triangular matrix in this decomposition. It may have [LFeature].
*/ */
public val l: FeaturedMatrix<T> public val l: Matrix<T>
/** /**
* The upper triangular matrix in this decomposition. It may have [UFeature]. * The upper triangular matrix in this decomposition. It may have [UFeature].
*/ */
public val u: FeaturedMatrix<T> public val u: Matrix<T>
/** /**
* The permutation matrix in this decomposition. * The permutation matrix in this decomposition.
*/ */
public val p: FeaturedMatrix<T> public val p: Matrix<T>
} }
/** /**
@ -102,12 +106,12 @@ public interface QRDecompositionFeature<T : Any> : MatrixFeature {
/** /**
* The orthogonal matrix in this decomposition. It may have [OrthogonalFeature]. * The orthogonal matrix in this decomposition. It may have [OrthogonalFeature].
*/ */
public val q: FeaturedMatrix<T> public val q: Matrix<T>
/** /**
* The upper triangular matrix in this decomposition. It may have [UFeature]. * The upper triangular matrix in this decomposition. It may have [UFeature].
*/ */
public val r: FeaturedMatrix<T> public val r: Matrix<T>
} }
/** /**
@ -120,7 +124,7 @@ public interface CholeskyDecompositionFeature<T : Any> : MatrixFeature {
/** /**
* The triangular matrix in this decomposition. It may have either [UFeature] or [LFeature]. * The triangular matrix in this decomposition. It may have either [UFeature] or [LFeature].
*/ */
public val l: FeaturedMatrix<T> public val l: Matrix<T>
} }
/** /**
@ -133,17 +137,17 @@ public interface SingularValueDecompositionFeature<T : Any> : MatrixFeature {
/** /**
* The matrix in this decomposition. It is unitary, and it consists from left singular vectors. * The matrix in this decomposition. It is unitary, and it consists from left singular vectors.
*/ */
public val u: FeaturedMatrix<T> public val u: Matrix<T>
/** /**
* The matrix in this decomposition. Its main diagonal elements are singular values. * The matrix in this decomposition. Its main diagonal elements are singular values.
*/ */
public val s: FeaturedMatrix<T> public val s: Matrix<T>
/** /**
* The matrix in this decomposition. It is unitary, and it consists from right singular vectors. * The matrix in this decomposition. It is unitary, and it consists from right singular vectors.
*/ */
public val v: FeaturedMatrix<T> public val v: Matrix<T>
/** /**
* The buffer of singular values of this SVD. * The buffer of singular values of this SVD.

View File

@ -0,0 +1,105 @@
package kscience.kmath.linear
import kscience.kmath.misc.UnstableKMathAPI
import kscience.kmath.operations.Ring
import kscience.kmath.structures.Matrix
import kscience.kmath.structures.Structure2D
import kscience.kmath.structures.asBuffer
import kscience.kmath.structures.getFeature
import kotlin.math.sqrt
import kotlin.reflect.KClass
import kotlin.reflect.safeCast
/**
* A [Matrix] that holds [MatrixFeature] objects.
*
* @param T the type of items.
*/
public class MatrixWrapper<T : Any> internal constructor(
public val origin: Matrix<T>,
public val features: Set<MatrixFeature>,
) : Matrix<T> by origin {
/**
* Get the first feature matching given class. Does not guarantee that matrix has only one feature matching the criteria
*/
@UnstableKMathAPI
override fun <T : Any> getFeature(type: KClass<T>): T? = type.safeCast(features.find { type.isInstance(it) })
?: origin.getFeature(type)
override fun equals(other: Any?): Boolean = origin == other
override fun hashCode(): Int = origin.hashCode()
override fun toString(): String {
return "MatrixWrapper(matrix=$origin, features=$features)"
}
}
/**
* Return the original matrix. If this is a wrapper, return its origin. If not, this matrix.
* Origin does not necessary store all features.
*/
@UnstableKMathAPI
public val <T : Any> Matrix<T>.origin: Matrix<T> get() = (this as? MatrixWrapper)?.origin ?: this
/**
* Add a single feature to a [Matrix]
*/
public operator fun <T : Any> Matrix<T>.plus(newFeature: MatrixFeature): MatrixWrapper<T> = if (this is MatrixWrapper) {
MatrixWrapper(origin, features + newFeature)
} else {
MatrixWrapper(this, setOf(newFeature))
}
/**
* Add a collection of features to a [Matrix]
*/
public operator fun <T : Any> Matrix<T>.plus(newFeatures: Collection<MatrixFeature>): MatrixWrapper<T> =
if (this is MatrixWrapper) {
MatrixWrapper(origin, features + newFeatures)
} else {
MatrixWrapper(this, newFeatures.toSet())
}
public inline fun Structure2D.Companion.real(
rows: Int,
columns: Int,
initializer: (Int, Int) -> Double,
): BufferMatrix<Double> = MatrixContext.real.produce(rows, columns, initializer)
/**
* Build a square matrix from given elements.
*/
public fun <T : Any> Structure2D.Companion.square(vararg elements: T): Matrix<T> {
val size: Int = sqrt(elements.size.toDouble()).toInt()
require(size * size == elements.size) { "The number of elements ${elements.size} is not a full square" }
val buffer = elements.asBuffer()
return BufferMatrix(size, size, buffer)
}
/**
* Diagonal matrix of ones. The matrix is virtual no actual matrix is created
*/
public fun <T : Any, R : Ring<T>> GenericMatrixContext<T, R, *>.one(rows: Int, columns: Int): Matrix<T> =
VirtualMatrix(rows, columns) { i, j ->
if (i == j) elementContext.one else elementContext.zero
} + UnitFeature
/**
* A virtual matrix of zeroes
*/
public fun <T : Any, R : Ring<T>> GenericMatrixContext<T, R, *>.zero(rows: Int, columns: Int): Matrix<T> =
VirtualMatrix(rows, columns) { _, _ -> elementContext.zero } + ZeroFeature
public class TransposedFeature<T : Any>(public val original: Matrix<T>) : MatrixFeature
/**
* Create a virtual transposed matrix without copying anything. `A.transpose().transpose() === A`
*/
@OptIn(UnstableKMathAPI::class)
public fun <T : Any> Matrix<T>.transpose(): Matrix<T> {
return getFeature<TransposedFeature<T>>()?.original ?: VirtualMatrix(
colNum,
rowNum,
) { i, j -> get(j, i) } + TransposedFeature(this)
}

View File

@ -1,9 +1,6 @@
package kscience.kmath.linear package kscience.kmath.linear
import kscience.kmath.operations.RealField
import kscience.kmath.structures.Matrix import kscience.kmath.structures.Matrix
import kscience.kmath.structures.MutableBuffer
import kscience.kmath.structures.MutableBufferFactory
import kscience.kmath.structures.RealBuffer import kscience.kmath.structures.RealBuffer
@Suppress("OVERRIDE_BY_INLINE") @Suppress("OVERRIDE_BY_INLINE")
@ -22,9 +19,9 @@ public object RealMatrixContext : MatrixContext<Double, BufferMatrix<Double>> {
produce(rowNum, colNum) { i, j -> get(i, j) } produce(rowNum, colNum) { i, j -> get(i, j) }
} }
public fun one(rows: Int, columns: Int): FeaturedMatrix<Double> = VirtualMatrix(rows, columns, DiagonalFeature) { i, j -> public fun one(rows: Int, columns: Int): Matrix<Double> = VirtualMatrix(rows, columns) { i, j ->
if (i == j) 1.0 else 0.0 if (i == j) 1.0 else 0.0
} } + DiagonalFeature
public override infix fun Matrix<Double>.dot(other: Matrix<Double>): BufferMatrix<Double> { public override infix fun Matrix<Double>.dot(other: Matrix<Double>): BufferMatrix<Double> {
require(colNum == other.rowNum) { "Matrix dot operation dimension mismatch: ($rowNum, $colNum) x (${other.rowNum}, ${other.colNum})" } require(colNum == other.rowNum) { "Matrix dot operation dimension mismatch: ($rowNum, $colNum) x (${other.rowNum}, ${other.colNum})" }
@ -61,7 +58,7 @@ public object RealMatrixContext : MatrixContext<Double, BufferMatrix<Double>> {
override fun multiply(a: Matrix<Double>, k: Number): BufferMatrix<Double> = override fun multiply(a: Matrix<Double>, k: Number): BufferMatrix<Double> =
produce(a.rowNum, a.colNum) { i, j -> a.get(i, j) * k.toDouble() } produce(a.rowNum, a.colNum) { i, j -> a[i, j] * k.toDouble() }
} }
@ -69,16 +66,3 @@ public object RealMatrixContext : MatrixContext<Double, BufferMatrix<Double>> {
* Partially optimized real-valued matrix * Partially optimized real-valued matrix
*/ */
public val MatrixContext.Companion.real: RealMatrixContext get() = RealMatrixContext public val MatrixContext.Companion.real: RealMatrixContext get() = RealMatrixContext
public fun RealMatrixContext.solveWithLUP(a: Matrix<Double>, b: Matrix<Double>): FeaturedMatrix<Double> {
// Use existing decomposition if it is provided by matrix
val bufferFactory: MutableBufferFactory<Double> = MutableBuffer.Companion::real
val decomposition = a.getFeature() ?: lup(bufferFactory, RealField, a) { it < 1e-11 }
return decomposition.solveWithLUP(bufferFactory, b)
}
/**
* Inverses a square matrix using LUP decomposition. Non square matrix will throw a error.
*/
public fun RealMatrixContext.inverseWithLUP(matrix: Matrix<Double>): FeaturedMatrix<Double> =
solveWithLUP(matrix, one(matrix.rowNum, matrix.colNum))

View File

@ -5,31 +5,16 @@ import kscience.kmath.structures.Matrix
public class VirtualMatrix<T : Any>( public class VirtualMatrix<T : Any>(
override val rowNum: Int, override val rowNum: Int,
override val colNum: Int, override val colNum: Int,
override val features: Set<MatrixFeature> = emptySet(),
public val generator: (i: Int, j: Int) -> T public val generator: (i: Int, j: Int) -> T
) : FeaturedMatrix<T> { ) : Matrix<T> {
public constructor(
rowNum: Int,
colNum: Int,
vararg features: MatrixFeature,
generator: (i: Int, j: Int) -> T
) : this(
rowNum,
colNum,
setOf(*features),
generator
)
override val shape: IntArray get() = intArrayOf(rowNum, colNum) override val shape: IntArray get() = intArrayOf(rowNum, colNum)
override operator fun get(i: Int, j: Int): T = generator(i, j) override operator fun get(i: Int, j: Int): T = generator(i, j)
override fun suggestFeature(vararg features: MatrixFeature): VirtualMatrix<T> =
VirtualMatrix(rowNum, colNum, this.features + features, generator)
override fun equals(other: Any?): Boolean { override fun equals(other: Any?): Boolean {
if (this === other) return true if (this === other) return true
if (other !is FeaturedMatrix<*>) return false if (other !is Matrix<*>) return false
if (rowNum != other.rowNum) return false if (rowNum != other.rowNum) return false
if (colNum != other.colNum) return false if (colNum != other.colNum) return false
@ -40,21 +25,9 @@ public class VirtualMatrix<T : Any>(
override fun hashCode(): Int { override fun hashCode(): Int {
var result = rowNum var result = rowNum
result = 31 * result + colNum result = 31 * result + colNum
result = 31 * result + features.hashCode()
result = 31 * result + generator.hashCode() result = 31 * result + generator.hashCode()
return result return result
} }
public companion object {
/**
* Wrap a matrix adding additional features to it
*/
public fun <T : Any> wrap(matrix: Matrix<T>, vararg features: MatrixFeature): FeaturedMatrix<T> {
return if (matrix is VirtualMatrix)
VirtualMatrix(matrix.rowNum, matrix.colNum, matrix.features + features, matrix.generator)
else
VirtualMatrix(matrix.rowNum, matrix.colNum, matrix.features + features) { i, j -> matrix[i, j] }
}
}
} }

View File

@ -88,90 +88,11 @@ public interface Algebra<T> {
public fun binaryOperation(operation: String, left: T, right: T): T = binaryOperationFunction(operation)(left, right) 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<T> : Algebra<T> {
/**
* 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. * Call a block with an [Algebra] as receiver.
*/ */
// TODO add contract when KT-32313 is fixed // TODO add contract when KT-32313 is fixed
public inline operator fun <A : Algebra<*>, R> A.invoke(block: A.() -> R): R = block() public inline operator fun <A : Algebra<*>, 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
@ -341,47 +262,11 @@ public interface RingOperations<T> : SpaceOperations<T> {
* *
* @param T the type of element of this ring. * @param T the type of element of this ring.
*/ */
public interface Ring<T> : Space<T>, RingOperations<T>, NumericAlgebra<T> { public interface Ring<T> : Space<T>, RingOperations<T> {
/** /**
* neutral operation for multiplication * neutral operation for multiplication
*/ */
public val one: T 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
} }
/** /**

View File

@ -1,5 +1,6 @@
package kscience.kmath.operations package kscience.kmath.operations
import kscience.kmath.misc.UnstableKMathAPI
import kscience.kmath.operations.BigInt.Companion.BASE import kscience.kmath.operations.BigInt.Companion.BASE
import kscience.kmath.operations.BigInt.Companion.BASE_SIZE import kscience.kmath.operations.BigInt.Companion.BASE_SIZE
import kscience.kmath.structures.* 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) * @author Robert Drynkin (https://github.com/robdrynkin) and Peter Klimai (https://github.com/pklimai)
*/ */
public object BigIntField : Field<BigInt> { @OptIn(UnstableKMathAPI::class)
public object BigIntField : Field<BigInt>, RingWithNumbers<BigInt> {
override val zero: BigInt = BigInt.ZERO override val zero: BigInt = BigInt.ZERO
override val one: BigInt = BigInt.ONE override val one: BigInt = BigInt.ONE

View File

@ -3,6 +3,7 @@ package kscience.kmath.operations
import kscience.kmath.memory.MemoryReader import kscience.kmath.memory.MemoryReader
import kscience.kmath.memory.MemorySpec import kscience.kmath.memory.MemorySpec
import kscience.kmath.memory.MemoryWriter import kscience.kmath.memory.MemoryWriter
import kscience.kmath.misc.UnstableKMathAPI
import kscience.kmath.structures.Buffer import kscience.kmath.structures.Buffer
import kscience.kmath.structures.MemoryBuffer import kscience.kmath.structures.MemoryBuffer
import kscience.kmath.structures.MutableBuffer import kscience.kmath.structures.MutableBuffer
@ -41,7 +42,8 @@ private val PI_DIV_2 = Complex(PI / 2, 0)
/** /**
* A field of [Complex]. * A field of [Complex].
*/ */
public object ComplexField : ExtendedField<Complex>, Norm<Complex, Complex> { @OptIn(UnstableKMathAPI::class)
public object ComplexField : ExtendedField<Complex>, Norm<Complex, Complex>, RingWithNumbers<Complex> {
override val zero: Complex = 0.0.toComplex() override val zero: Complex = 0.0.toComplex()
override val one: Complex = 1.0.toComplex() override val one: Complex = 1.0.toComplex()
@ -156,7 +158,7 @@ public object ComplexField : ExtendedField<Complex>, Norm<Complex, Complex> {
override fun norm(arg: Complex): Complex = sqrt(arg.conjugate * arg) 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<ExtendedField>.symbol(value)
} }
/** /**

View File

@ -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<T> : Algebra<T> {
/**
* 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<T>: Ring<T>, NumericAlgebra<T>{
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
}

View File

@ -37,7 +37,7 @@ public interface ExtendedFieldOperations<T> :
/** /**
* Advanced Number-like field that implements basic operations. * Advanced Number-like field that implements basic operations.
*/ */
public interface ExtendedField<T> : ExtendedFieldOperations<T>, Field<T> { public interface ExtendedField<T> : ExtendedFieldOperations<T>, Field<T>, NumericAlgebra<T> {
public override fun sinh(arg: T): T = (exp(arg) - exp(-arg)) / 2 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 cosh(arg: T): T = (exp(arg) + exp(-arg)) / 2
public override fun tanh(arg: T): T = (exp(arg) - exp(-arg)) / (exp(-arg) + exp(arg)) public override fun tanh(arg: T): T = (exp(arg) - exp(-arg)) / (exp(-arg) + exp(arg))
@ -80,6 +80,8 @@ public object RealField : ExtendedField<Double>, Norm<Double, Double> {
public override val one: Double public override val one: Double
get() = 1.0 get() = 1.0
override fun number(value: Number): Double = value.toDouble()
public override fun binaryOperationFunction(operation: String): (left: Double, right: Double) -> Double = public override fun binaryOperationFunction(operation: String): (left: Double, right: Double) -> Double =
when (operation) { when (operation) {
PowerOperations.POW_OPERATION -> ::power PowerOperations.POW_OPERATION -> ::power
@ -131,7 +133,10 @@ public object FloatField : ExtendedField<Float>, Norm<Float, Float> {
public override val one: Float public override val one: Float
get() = 1.0f get() = 1.0f
public override fun binaryOperationFunction(operation: String): (left: Float, right: Float) -> Float = when (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 PowerOperations.POW_OPERATION -> ::power
else -> super.binaryOperationFunction(operation) else -> super.binaryOperationFunction(operation)
} }
@ -174,13 +179,15 @@ public object FloatField : ExtendedField<Float>, Norm<Float, Float> {
* A field for [Int] without boxing. Does not produce corresponding ring element. * A field for [Int] without boxing. Does not produce corresponding ring element.
*/ */
@Suppress("EXTENSION_SHADOWED_BY_MEMBER", "OVERRIDE_BY_INLINE", "NOTHING_TO_INLINE") @Suppress("EXTENSION_SHADOWED_BY_MEMBER", "OVERRIDE_BY_INLINE", "NOTHING_TO_INLINE")
public object IntRing : Ring<Int>, Norm<Int, Int> { public object IntRing : Ring<Int>, Norm<Int, Int>, NumericAlgebra<Int> {
public override val zero: Int public override val zero: Int
get() = 0 get() = 0
public override val one: Int public override val one: Int
get() = 1 get() = 1
override fun number(value: Number): Int = value.toInt()
public override inline fun add(a: Int, b: Int): Int = a + b public override inline fun add(a: Int, b: Int): Int = a + b
public override inline fun multiply(a: Int, k: Number): Int = k.toInt() * a public override inline fun multiply(a: Int, k: Number): Int = k.toInt() * a
@ -198,13 +205,15 @@ public object IntRing : Ring<Int>, Norm<Int, Int> {
* A field for [Short] without boxing. Does not produce appropriate ring element. * A field for [Short] without boxing. Does not produce appropriate ring element.
*/ */
@Suppress("EXTENSION_SHADOWED_BY_MEMBER", "OVERRIDE_BY_INLINE", "NOTHING_TO_INLINE") @Suppress("EXTENSION_SHADOWED_BY_MEMBER", "OVERRIDE_BY_INLINE", "NOTHING_TO_INLINE")
public object ShortRing : Ring<Short>, Norm<Short, Short> { public object ShortRing : Ring<Short>, Norm<Short, Short>, NumericAlgebra<Short> {
public override val zero: Short public override val zero: Short
get() = 0 get() = 0
public override val one: Short public override val one: Short
get() = 1 get() = 1
override fun number(value: Number): Short = value.toShort()
public override inline fun add(a: Short, b: Short): Short = (a + b).toShort() public override inline fun add(a: Short, b: Short): Short = (a + b).toShort()
public override inline fun multiply(a: Short, k: Number): Short = (a * k.toShort()).toShort() public override inline fun multiply(a: Short, k: Number): Short = (a * k.toShort()).toShort()
@ -222,13 +231,15 @@ public object ShortRing : Ring<Short>, Norm<Short, Short> {
* A field for [Byte] without boxing. Does not produce appropriate ring element. * A field for [Byte] without boxing. Does not produce appropriate ring element.
*/ */
@Suppress("EXTENSION_SHADOWED_BY_MEMBER", "OVERRIDE_BY_INLINE", "NOTHING_TO_INLINE") @Suppress("EXTENSION_SHADOWED_BY_MEMBER", "OVERRIDE_BY_INLINE", "NOTHING_TO_INLINE")
public object ByteRing : Ring<Byte>, Norm<Byte, Byte> { public object ByteRing : Ring<Byte>, Norm<Byte, Byte>, NumericAlgebra<Byte> {
public override val zero: Byte public override val zero: Byte
get() = 0 get() = 0
public override val one: Byte public override val one: Byte
get() = 1 get() = 1
override fun number(value: Number): Byte = value.toByte()
public override inline fun add(a: Byte, b: Byte): Byte = (a + b).toByte() public override inline fun add(a: Byte, b: Byte): Byte = (a + b).toByte()
public override inline fun multiply(a: Byte, k: Number): Byte = (a * k.toByte()).toByte() public override inline fun multiply(a: Byte, k: Number): Byte = (a * k.toByte()).toByte()
@ -246,13 +257,15 @@ public object ByteRing : Ring<Byte>, Norm<Byte, Byte> {
* A field for [Double] without boxing. Does not produce appropriate ring element. * A field for [Double] without boxing. Does not produce appropriate ring element.
*/ */
@Suppress("EXTENSION_SHADOWED_BY_MEMBER", "OVERRIDE_BY_INLINE", "NOTHING_TO_INLINE") @Suppress("EXTENSION_SHADOWED_BY_MEMBER", "OVERRIDE_BY_INLINE", "NOTHING_TO_INLINE")
public object LongRing : Ring<Long>, Norm<Long, Long> { public object LongRing : Ring<Long>, Norm<Long, Long>, NumericAlgebra<Long> {
public override val zero: Long public override val zero: Long
get() = 0L get() = 0L
public override val one: Long public override val one: Long
get() = 1L get() = 1L
override fun number(value: Number): Long = value.toLong()
public override inline fun add(a: Long, b: Long): Long = a + b public override inline fun add(a: Long, b: Long): Long = a + b
public override inline fun multiply(a: Long, k: Number): Long = a * k.toLong() public override inline fun multiply(a: Long, k: Number): Long = a * k.toLong()

View File

@ -1,9 +1,7 @@
package kscience.kmath.structures package kscience.kmath.structures
import kscience.kmath.operations.Complex import kscience.kmath.misc.UnstableKMathAPI
import kscience.kmath.operations.ComplexField import kscience.kmath.operations.*
import kscience.kmath.operations.FieldElement
import kscience.kmath.operations.complex
import kotlin.contracts.InvocationKind import kotlin.contracts.InvocationKind
import kotlin.contracts.contract import kotlin.contracts.contract
@ -12,15 +10,22 @@ public typealias ComplexNDElement = BufferedNDFieldElement<Complex, ComplexField
/** /**
* An optimized nd-field for complex numbers * An optimized nd-field for complex numbers
*/ */
@OptIn(UnstableKMathAPI::class)
public class ComplexNDField(override val shape: IntArray) : public class ComplexNDField(override val shape: IntArray) :
BufferedNDField<Complex, ComplexField>, BufferedNDField<Complex, ComplexField>,
ExtendedNDField<Complex, ComplexField, NDBuffer<Complex>> { ExtendedNDField<Complex, ComplexField, NDBuffer<Complex>>,
RingWithNumbers<NDBuffer<Complex>>{
override val strides: Strides = DefaultStrides(shape) override val strides: Strides = DefaultStrides(shape)
override val elementContext: ComplexField get() = ComplexField override val elementContext: ComplexField get() = ComplexField
override val zero: ComplexNDElement by lazy { produce { zero } } override val zero: ComplexNDElement by lazy { produce { zero } }
override val one: ComplexNDElement by lazy { produce { one } } override val one: ComplexNDElement by lazy { produce { one } }
override fun number(value: Number): NDBuffer<Complex> {
val c = value.toComplex()
return produce { c }
}
public inline fun buildBuffer(size: Int, crossinline initializer: (Int) -> Complex): Buffer<Complex> = public inline fun buildBuffer(size: Int, crossinline initializer: (Int) -> Complex): Buffer<Complex> =
Buffer.complex(size) { initializer(it) } Buffer.complex(size) { initializer(it) }
@ -29,7 +34,7 @@ public class ComplexNDField(override val shape: IntArray) :
*/ */
override fun map( override fun map(
arg: NDBuffer<Complex>, arg: NDBuffer<Complex>,
transform: ComplexField.(Complex) -> Complex transform: ComplexField.(Complex) -> Complex,
): ComplexNDElement { ): ComplexNDElement {
check(arg) check(arg)
val array = buildBuffer(arg.strides.linearSize) { offset -> ComplexField.transform(arg.buffer[offset]) } 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( override fun mapIndexed(
arg: NDBuffer<Complex>, arg: NDBuffer<Complex>,
transform: ComplexField.(index: IntArray, Complex) -> Complex transform: ComplexField.(index: IntArray, Complex) -> Complex,
): ComplexNDElement { ): ComplexNDElement {
check(arg) check(arg)
@ -60,7 +65,7 @@ public class ComplexNDField(override val shape: IntArray) :
override fun combine( override fun combine(
a: NDBuffer<Complex>, a: NDBuffer<Complex>,
b: NDBuffer<Complex>, b: NDBuffer<Complex>,
transform: ComplexField.(Complex, Complex) -> Complex transform: ComplexField.(Complex, Complex) -> Complex,
): ComplexNDElement { ): ComplexNDElement {
check(a, b) check(a, b)
@ -141,7 +146,7 @@ public fun NDField.Companion.complex(vararg shape: Int): ComplexNDField = Comple
public fun NDElement.Companion.complex( public fun NDElement.Companion.complex(
vararg shape: Int, vararg shape: Int,
initializer: ComplexField.(IntArray) -> Complex initializer: ComplexField.(IntArray) -> Complex,
): ComplexNDElement = NDField.complex(*shape).produce(initializer) ): ComplexNDElement = NDField.complex(*shape).produce(initializer)
/** /**

View File

@ -1,5 +1,6 @@
package kscience.kmath.structures package kscience.kmath.structures
import kscience.kmath.misc.UnstableKMathAPI
import kotlin.jvm.JvmName import kotlin.jvm.JvmName
import kotlin.native.concurrent.ThreadLocal import kotlin.native.concurrent.ThreadLocal
import kotlin.reflect.KClass import kotlin.reflect.KClass
@ -38,14 +39,22 @@ public interface NDStructure<T> {
*/ */
public fun elements(): Sequence<Pair<IntArray, T>> public fun elements(): Sequence<Pair<IntArray, T>>
//force override equality and hash code
public override fun equals(other: Any?): Boolean public override fun equals(other: Any?): Boolean
public override fun hashCode(): Int public override fun hashCode(): Int
/**
* Feature is additional property or hint that does not directly affect the structure, but could in some cases help
* optimize operations and performance. If the feature is not present, null is defined.
*/
@UnstableKMathAPI
public fun <T : Any> getFeature(type: KClass<T>): T? = null
public companion object { public companion object {
/** /**
* Indicates whether some [NDStructure] is equal to another one. * Indicates whether some [NDStructure] is equal to another one.
*/ */
public fun equals(st1: NDStructure<*>, st2: NDStructure<*>): Boolean { public fun contentEquals(st1: NDStructure<*>, st2: NDStructure<*>): Boolean {
if (st1 === st2) return true if (st1 === st2) return true
// fast comparison of buffers if possible // fast comparison of buffers if possible
@ -120,6 +129,9 @@ public interface NDStructure<T> {
*/ */
public operator fun <T> NDStructure<T>.get(vararg index: Int): T = get(index) public operator fun <T> NDStructure<T>.get(vararg index: Int): T = get(index)
@UnstableKMathAPI
public inline fun <reified T : Any> NDStructure<*>.getFeature(): T? = getFeature(T::class)
/** /**
* Represents mutable [NDStructure]. * Represents mutable [NDStructure].
*/ */
@ -133,6 +145,9 @@ public interface MutableNDStructure<T> : NDStructure<T> {
public operator fun set(index: IntArray, value: T) public operator fun set(index: IntArray, value: T)
} }
/**
* Transform a structure element-by element in place.
*/
public inline fun <T> MutableNDStructure<T>.mapInPlace(action: (IntArray, T) -> T): Unit = public inline fun <T> MutableNDStructure<T>.mapInPlace(action: (IntArray, T) -> T): Unit =
elements().forEach { (index, oldValue) -> this[index] = action(index, oldValue) } elements().forEach { (index, oldValue) -> this[index] = action(index, oldValue) }
@ -259,16 +274,17 @@ public abstract class NDBuffer<T> : NDStructure<T> {
override fun elements(): Sequence<Pair<IntArray, T>> = strides.indices().map { it to this[it] } override fun elements(): Sequence<Pair<IntArray, T>> = strides.indices().map { it to this[it] }
override fun equals(other: Any?): Boolean {
return NDStructure.contentEquals(this, other as? NDStructure<*> ?: return false)
}
override fun hashCode(): Int { override fun hashCode(): Int {
var result = strides.hashCode() var result = strides.hashCode()
result = 31 * result + buffer.hashCode() result = 31 * result + buffer.hashCode()
return result return result
} }
override fun equals(other: Any?): Boolean {
return NDStructure.equals(this, other as? NDStructure<*> ?: return false)
}
override fun toString(): String { override fun toString(): String {
val bufferRepr: String = when (shape.size) { val bufferRepr: String = when (shape.size) {
1 -> buffer.asSequence().joinToString(prefix = "[", postfix = "]", separator = ", ") 1 -> buffer.asSequence().joinToString(prefix = "[", postfix = "]", separator = ", ")

View File

@ -150,6 +150,8 @@ public class RealBufferField(public val size: Int) : ExtendedField<Buffer<Double
public override val zero: Buffer<Double> by lazy { RealBuffer(size) { 0.0 } } public override val zero: Buffer<Double> by lazy { RealBuffer(size) { 0.0 } }
public override val one: Buffer<Double> by lazy { RealBuffer(size) { 1.0 } } public override val one: Buffer<Double> by lazy { RealBuffer(size) { 1.0 } }
override fun number(value: Number): Buffer<Double> = RealBuffer(size) { value.toDouble() }
public override fun add(a: Buffer<Double>, b: Buffer<Double>): RealBuffer { public override fun add(a: Buffer<Double>, b: Buffer<Double>): RealBuffer {
require(a.size == size) { "The buffer size ${a.size} does not match context size $size" } require(a.size == size) { "The buffer size ${a.size} does not match context size $size" }
return RealBufferFieldOperations.add(a, b) return RealBufferFieldOperations.add(a, b)

View File

@ -1,13 +1,17 @@
package kscience.kmath.structures package kscience.kmath.structures
import kscience.kmath.misc.UnstableKMathAPI
import kscience.kmath.operations.FieldElement import kscience.kmath.operations.FieldElement
import kscience.kmath.operations.RealField import kscience.kmath.operations.RealField
import kscience.kmath.operations.RingWithNumbers
public typealias RealNDElement = BufferedNDFieldElement<Double, RealField> public typealias RealNDElement = BufferedNDFieldElement<Double, RealField>
@OptIn(UnstableKMathAPI::class)
public class RealNDField(override val shape: IntArray) : public class RealNDField(override val shape: IntArray) :
BufferedNDField<Double, RealField>, BufferedNDField<Double, RealField>,
ExtendedNDField<Double, RealField, NDBuffer<Double>> { ExtendedNDField<Double, RealField, NDBuffer<Double>>,
RingWithNumbers<NDBuffer<Double>> {
override val strides: Strides = DefaultStrides(shape) override val strides: Strides = DefaultStrides(shape)
@ -15,35 +19,36 @@ public class RealNDField(override val shape: IntArray) :
override val zero: RealNDElement by lazy { produce { zero } } override val zero: RealNDElement by lazy { produce { zero } }
override val one: RealNDElement by lazy { produce { one } } override val one: RealNDElement by lazy { produce { one } }
public inline fun buildBuffer(size: Int, crossinline initializer: (Int) -> Double): Buffer<Double> = override fun number(value: Number): NDBuffer<Double> {
RealBuffer(DoubleArray(size) { initializer(it) }) val d = value.toDouble()
return produce { d }
}
/** @Suppress("OVERRIDE_BY_INLINE")
* Inline transform an NDStructure to override inline fun map(
*/
override fun map(
arg: NDBuffer<Double>, arg: NDBuffer<Double>,
transform: RealField.(Double) -> Double transform: RealField.(Double) -> Double,
): RealNDElement { ): RealNDElement {
check(arg) check(arg)
val array = buildBuffer(arg.strides.linearSize) { offset -> RealField.transform(arg.buffer[offset]) } val array = RealBuffer(arg.strides.linearSize) { offset -> RealField.transform(arg.buffer[offset]) }
return BufferedNDFieldElement(this, array) return BufferedNDFieldElement(this, array)
} }
override fun produce(initializer: RealField.(IntArray) -> Double): RealNDElement { @Suppress("OVERRIDE_BY_INLINE")
val array = buildBuffer(strides.linearSize) { offset -> elementContext.initializer(strides.index(offset)) } override inline fun produce(initializer: RealField.(IntArray) -> Double): RealNDElement {
val array = RealBuffer(strides.linearSize) { offset -> elementContext.initializer(strides.index(offset)) }
return BufferedNDFieldElement(this, array) return BufferedNDFieldElement(this, array)
} }
override fun mapIndexed( @Suppress("OVERRIDE_BY_INLINE")
override inline fun mapIndexed(
arg: NDBuffer<Double>, arg: NDBuffer<Double>,
transform: RealField.(index: IntArray, Double) -> Double transform: RealField.(index: IntArray, Double) -> Double,
): RealNDElement { ): RealNDElement {
check(arg) check(arg)
return BufferedNDFieldElement( return BufferedNDFieldElement(
this, this,
buildBuffer(arg.strides.linearSize) { offset -> RealBuffer(arg.strides.linearSize) { offset ->
elementContext.transform( elementContext.transform(
arg.strides.index(offset), arg.strides.index(offset),
arg.buffer[offset] arg.buffer[offset]
@ -51,15 +56,17 @@ public class RealNDField(override val shape: IntArray) :
}) })
} }
override fun combine( @Suppress("OVERRIDE_BY_INLINE")
override inline fun combine(
a: NDBuffer<Double>, a: NDBuffer<Double>,
b: NDBuffer<Double>, b: NDBuffer<Double>,
transform: RealField.(Double, Double) -> Double transform: RealField.(Double, Double) -> Double,
): RealNDElement { ): RealNDElement {
check(a, b) check(a, b)
return BufferedNDFieldElement( val buffer = RealBuffer(strides.linearSize) { offset ->
this, elementContext.transform(a.buffer[offset], b.buffer[offset])
buildBuffer(strides.linearSize) { offset -> elementContext.transform(a.buffer[offset], b.buffer[offset]) }) }
return BufferedNDFieldElement(this, buffer)
} }
override fun NDBuffer<Double>.toElement(): FieldElement<NDBuffer<Double>, *, out BufferedNDField<Double, RealField>> = override fun NDBuffer<Double>.toElement(): FieldElement<NDBuffer<Double>, *, out BufferedNDField<Double, RealField>> =

View File

@ -9,12 +9,14 @@ public interface Structure2D<T> : NDStructure<T> {
/** /**
* The number of rows in this structure. * The number of rows in this structure.
*/ */
public val rowNum: Int get() = shape[0] public val rowNum: Int
/** /**
* The number of columns in this structure. * The number of columns in this structure.
*/ */
public val colNum: Int get() = shape[1] public val colNum: Int
public override val shape: IntArray get() = intArrayOf(rowNum, colNum)
/** /**
* The buffer of rows of this structure. It gets elements from the structure dynamically. * The buffer of rows of this structure. It gets elements from the structure dynamically.
@ -56,6 +58,9 @@ public interface Structure2D<T> : NDStructure<T> {
private inline class Structure2DWrapper<T>(val structure: NDStructure<T>) : Structure2D<T> { private inline class Structure2DWrapper<T>(val structure: NDStructure<T>) : Structure2D<T> {
override val shape: IntArray get() = structure.shape override val shape: IntArray get() = structure.shape
override val rowNum: Int get() = shape[0]
override val colNum: Int get() = shape[1]
override operator fun get(i: Int, j: Int): T = structure[i, j] override operator fun get(i: Int, j: Int): T = structure[i, j]
override fun elements(): Sequence<Pair<IntArray, T>> = structure.elements() override fun elements(): Sequence<Pair<IntArray, T>> = structure.elements()

View File

@ -7,6 +7,7 @@ import kscience.kmath.structures.as2D
import kotlin.test.Test import kotlin.test.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals
@Suppress("UNUSED_VARIABLE")
class MatrixTest { class MatrixTest {
@Test @Test
fun testTranspose() { fun testTranspose() {

View File

@ -1,14 +1,13 @@
package kscience.kmath.structures package kscience.kmath.structures
import kscience.kmath.operations.internal.FieldVerifier import kscience.kmath.operations.internal.FieldVerifier
import kscience.kmath.operations.invoke
import kotlin.test.Test import kotlin.test.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals
internal class NDFieldTest { internal class NDFieldTest {
@Test @Test
fun verify() { 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 @Test

View File

@ -8,6 +8,7 @@ import kotlin.math.pow
import kotlin.test.Test import kotlin.test.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals
@Suppress("UNUSED_VARIABLE")
class NumberNDFieldTest { class NumberNDFieldTest {
val array1: RealNDElement = 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() } val array2: RealNDElement = real2D(3, 3) { i, j -> (i - j).toDouble() }

View File

@ -7,7 +7,7 @@ import java.math.MathContext
/** /**
* A field over [BigInteger]. * A field over [BigInteger].
*/ */
public object JBigIntegerField : Field<BigInteger> { public object JBigIntegerField : Field<BigInteger>, NumericAlgebra<BigInteger> {
public override val zero: BigInteger public override val zero: BigInteger
get() = BigInteger.ZERO get() = BigInteger.ZERO
@ -28,9 +28,9 @@ public object JBigIntegerField : Field<BigInteger> {
* *
* @property mathContext the [MathContext] to use. * @property mathContext the [MathContext] to use.
*/ */
public abstract class JBigDecimalFieldBase internal constructor(public val mathContext: MathContext = MathContext.DECIMAL64) : public abstract class JBigDecimalFieldBase internal constructor(
Field<BigDecimal>, private val mathContext: MathContext = MathContext.DECIMAL64,
PowerOperations<BigDecimal> { ) : Field<BigDecimal>, PowerOperations<BigDecimal>, NumericAlgebra<BigDecimal> {
public override val zero: BigDecimal public override val zero: BigDecimal
get() = BigDecimal.ZERO get() = BigDecimal.ZERO

View File

@ -24,7 +24,7 @@ public class LazyNDStructure<T>(
} }
public override fun equals(other: Any?): Boolean { public override fun equals(other: Any?): Boolean {
return NDStructure.equals(this, other as? NDStructure<*> ?: return false) return NDStructure.contentEquals(this, other as? NDStructure<*> ?: return false)
} }
public override fun hashCode(): Int { public override fun hashCode(): Int {

View File

@ -40,6 +40,8 @@ public inline class DMatrixWrapper<T, R : Dimension, C : Dimension>(
private val structure: Structure2D<T>, private val structure: Structure2D<T>,
) : DMatrix<T, R, C> { ) : DMatrix<T, R, C> {
override val shape: IntArray get() = structure.shape override val shape: IntArray get() = structure.shape
override val rowNum: Int get() = shape[0]
override val colNum: Int get() = shape[1]
override operator fun get(i: Int, j: Int): T = structure[i, j] override operator fun get(i: Int, j: Int): T = structure[i, j]
} }
@ -147,6 +149,7 @@ public inline fun <reified D : Dimension> DMatrixContext<Double>.one(): DMatrix<
if (i == j) 1.0 else 0.0 if (i == j) 1.0 else 0.0
} }
public inline fun <reified R : Dimension, reified C : Dimension> DMatrixContext<Double>.zero(): DMatrix<Double, R, C> = produce { _, _ -> public inline fun <reified R : Dimension, reified C : Dimension> DMatrixContext<Double>.zero(): DMatrix<Double, R, C> =
produce { _, _ ->
0.0 0.0
} }

View File

@ -6,6 +6,7 @@ import kscience.kmath.dimensions.DMatrixContext
import kscience.kmath.dimensions.one import kscience.kmath.dimensions.one
import kotlin.test.Test import kotlin.test.Test
@Suppress("UNUSED_VARIABLE")
internal class DMatrixContextTest { internal class DMatrixContextTest {
@Test @Test
fun testDimensionSafeMatrix() { fun testDimensionSafeMatrix() {

View File

@ -1,10 +1,14 @@
package kscience.kmath.ejml package kscience.kmath.ejml
import kscience.kmath.linear.* import kscience.kmath.linear.*
import kscience.kmath.misc.UnstableKMathAPI
import kscience.kmath.structures.Matrix
import kscience.kmath.structures.NDStructure import kscience.kmath.structures.NDStructure
import kscience.kmath.structures.RealBuffer import kscience.kmath.structures.RealBuffer
import org.ejml.dense.row.factory.DecompositionFactory_DDRM import org.ejml.dense.row.factory.DecompositionFactory_DDRM
import org.ejml.simple.SimpleMatrix import org.ejml.simple.SimpleMatrix
import kotlin.reflect.KClass
import kotlin.reflect.cast
/** /**
* Represents featured matrix over EJML [SimpleMatrix]. * Represents featured matrix over EJML [SimpleMatrix].
@ -12,87 +16,75 @@ import org.ejml.simple.SimpleMatrix
* @property origin the underlying [SimpleMatrix]. * @property origin the underlying [SimpleMatrix].
* @author Iaroslav Postovalov * @author Iaroslav Postovalov
*/ */
public class EjmlMatrix(public val origin: SimpleMatrix, features: Set<MatrixFeature> = emptySet()) : public class EjmlMatrix(
FeaturedMatrix<Double> { public val origin: SimpleMatrix,
public override val rowNum: Int ) : Matrix<Double> {
get() = origin.numRows() public override val rowNum: Int get() = origin.numRows()
public override val colNum: Int public override val colNum: Int get() = origin.numCols()
get() = origin.numCols()
public override val shape: IntArray by lazy { intArrayOf(rowNum, colNum) } @UnstableKMathAPI
override fun <T : Any> getFeature(type: KClass<T>): T? = when (type) {
public override val features: Set<MatrixFeature> = hashSetOf( InverseMatrixFeature::class -> object : InverseMatrixFeature<Double> {
object : InverseMatrixFeature<Double> { override val inverse: Matrix<Double> by lazy { EjmlMatrix(origin.invert()) }
override val inverse: FeaturedMatrix<Double> by lazy { EjmlMatrix(origin.invert()) } }
}, DeterminantFeature::class -> object : DeterminantFeature<Double> {
object : DeterminantFeature<Double> {
override val determinant: Double by lazy(origin::determinant) override val determinant: Double by lazy(origin::determinant)
}, }
SingularValueDecompositionFeature::class -> object : SingularValueDecompositionFeature<Double> {
object : SingularValueDecompositionFeature<Double> {
private val svd by lazy { private val svd by lazy {
DecompositionFactory_DDRM.svd(origin.numRows(), origin.numCols(), true, true, false) DecompositionFactory_DDRM.svd(origin.numRows(), origin.numCols(), true, true, false)
.apply { decompose(origin.ddrm.copy()) } .apply { decompose(origin.ddrm.copy()) }
} }
override val u: FeaturedMatrix<Double> by lazy { EjmlMatrix(SimpleMatrix(svd.getU(null, false))) } override val u: Matrix<Double> by lazy { EjmlMatrix(SimpleMatrix(svd.getU(null, false))) }
override val s: FeaturedMatrix<Double> by lazy { EjmlMatrix(SimpleMatrix(svd.getW(null))) } override val s: Matrix<Double> by lazy { EjmlMatrix(SimpleMatrix(svd.getW(null))) }
override val v: FeaturedMatrix<Double> by lazy { EjmlMatrix(SimpleMatrix(svd.getV(null, false))) } override val v: Matrix<Double> by lazy { EjmlMatrix(SimpleMatrix(svd.getV(null, false))) }
override val singularValues: Point<Double> by lazy { RealBuffer(svd.singularValues) } override val singularValues: Point<Double> by lazy { RealBuffer(svd.singularValues) }
}, }
QRDecompositionFeature::class -> object : QRDecompositionFeature<Double> {
object : QRDecompositionFeature<Double> {
private val qr by lazy { private val qr by lazy {
DecompositionFactory_DDRM.qr().apply { decompose(origin.ddrm.copy()) } DecompositionFactory_DDRM.qr().apply { decompose(origin.ddrm.copy()) }
} }
override val q: FeaturedMatrix<Double> by lazy { EjmlMatrix(SimpleMatrix(qr.getQ(null, false))) } override val q: Matrix<Double> by lazy { EjmlMatrix(SimpleMatrix(qr.getQ(null, false))) }
override val r: FeaturedMatrix<Double> by lazy { EjmlMatrix(SimpleMatrix(qr.getR(null, false))) } override val r: Matrix<Double> by lazy { EjmlMatrix(SimpleMatrix(qr.getR(null, false))) }
}, }
CholeskyDecompositionFeature::class -> object : CholeskyDecompositionFeature<Double> {
object : CholeskyDecompositionFeature<Double> { override val l: Matrix<Double> by lazy {
override val l: FeaturedMatrix<Double> by lazy {
val cholesky = val cholesky =
DecompositionFactory_DDRM.chol(rowNum, true).apply { decompose(origin.ddrm.copy()) } DecompositionFactory_DDRM.chol(rowNum, true).apply { decompose(origin.ddrm.copy()) }
EjmlMatrix(SimpleMatrix(cholesky.getT(null)), setOf(LFeature)) EjmlMatrix(SimpleMatrix(cholesky.getT(null))) + LFeature
} }
}, }
LupDecompositionFeature::class -> object : LupDecompositionFeature<Double> {
object : LupDecompositionFeature<Double> {
private val lup by lazy { private val lup by lazy {
DecompositionFactory_DDRM.lu(origin.numRows(), origin.numCols()).apply { decompose(origin.ddrm.copy()) } DecompositionFactory_DDRM.lu(origin.numRows(), origin.numCols()).apply { decompose(origin.ddrm.copy()) }
} }
override val l: FeaturedMatrix<Double> by lazy { override val l: Matrix<Double> by lazy {
EjmlMatrix(SimpleMatrix(lup.getLower(null)), setOf(LFeature)) EjmlMatrix(SimpleMatrix(lup.getLower(null))) + LFeature
} }
override val u: FeaturedMatrix<Double> by lazy { override val u: Matrix<Double> by lazy {
EjmlMatrix(SimpleMatrix(lup.getUpper(null)), setOf(UFeature)) EjmlMatrix(SimpleMatrix(lup.getUpper(null))) + UFeature
} }
override val p: FeaturedMatrix<Double> by lazy { EjmlMatrix(SimpleMatrix(lup.getRowPivot(null))) } override val p: Matrix<Double> by lazy { EjmlMatrix(SimpleMatrix(lup.getRowPivot(null))) }
}, }
) union features else -> null
}?.let { type.cast(it) }
public override fun suggestFeature(vararg features: MatrixFeature): EjmlMatrix =
EjmlMatrix(origin, this.features + features)
public override operator fun get(i: Int, j: Int): Double = origin[i, j] public override operator fun get(i: Int, j: Int): Double = origin[i, j]
public override fun equals(other: Any?): Boolean { override fun equals(other: Any?): Boolean {
if (other is EjmlMatrix) return origin.isIdentical(other.origin, 0.0) if (this === other) return true
return NDStructure.equals(this, other as? NDStructure<*> ?: return false) if (other !is Matrix<*>) return false
return NDStructure.contentEquals(this, other)
} }
public override fun hashCode(): Int { override fun hashCode(): Int = origin.hashCode()
var result = origin.hashCode()
result = 31 * result + features.hashCode()
return result
}
public override fun toString(): String = "EjmlMatrix(origin=$origin, features=$features)"
} }

View File

@ -1,22 +1,30 @@
package kscience.kmath.ejml package kscience.kmath.ejml
import kscience.kmath.linear.InverseMatrixFeature
import kscience.kmath.linear.MatrixContext import kscience.kmath.linear.MatrixContext
import kscience.kmath.linear.Point import kscience.kmath.linear.Point
import kscience.kmath.linear.origin
import kscience.kmath.misc.UnstableKMathAPI
import kscience.kmath.structures.Matrix import kscience.kmath.structures.Matrix
import kscience.kmath.structures.getFeature
import org.ejml.simple.SimpleMatrix import org.ejml.simple.SimpleMatrix
/**
* Converts this matrix to EJML one.
*/
public fun Matrix<Double>.toEjml(): EjmlMatrix =
if (this is EjmlMatrix) this else EjmlMatrixContext.produce(rowNum, colNum) { i, j -> get(i, j) }
/** /**
* Represents context of basic operations operating with [EjmlMatrix]. * Represents context of basic operations operating with [EjmlMatrix].
* *
* @author Iaroslav Postovalov * @author Iaroslav Postovalov
*/ */
public object EjmlMatrixContext : MatrixContext<Double, EjmlMatrix> { public object EjmlMatrixContext : MatrixContext<Double, EjmlMatrix> {
/**
* Converts this matrix to EJML one.
*/
@OptIn(UnstableKMathAPI::class)
public fun Matrix<Double>.toEjml(): EjmlMatrix = when (val matrix = origin) {
is EjmlMatrix -> matrix
else -> produce(rowNum, colNum) { i, j -> get(i, j) }
}
/** /**
* Converts this vector to EJML one. * Converts this vector to EJML one.
*/ */
@ -77,3 +85,8 @@ public fun EjmlMatrixContext.solve(a: Matrix<Double>, b: Matrix<Double>): EjmlMa
*/ */
public fun EjmlMatrixContext.solve(a: Matrix<Double>, b: Point<Double>): EjmlVector = public fun EjmlMatrixContext.solve(a: Matrix<Double>, b: Point<Double>): EjmlVector =
EjmlVector(a.toEjml().origin.solve(b.toEjml().origin)) EjmlVector(a.toEjml().origin.solve(b.toEjml().origin))
@OptIn(UnstableKMathAPI::class)
public fun EjmlMatrix.inverted(): EjmlMatrix = getFeature<InverseMatrixFeature<Double>>()!!.inverse as EjmlMatrix
public fun EjmlMatrixContext.inverse(matrix: Matrix<Double>): Matrix<Double> = matrix.toEjml().inverted()

View File

@ -3,7 +3,9 @@ package kscience.kmath.ejml
import kscience.kmath.linear.DeterminantFeature import kscience.kmath.linear.DeterminantFeature
import kscience.kmath.linear.LupDecompositionFeature import kscience.kmath.linear.LupDecompositionFeature
import kscience.kmath.linear.MatrixFeature import kscience.kmath.linear.MatrixFeature
import kscience.kmath.linear.getFeature import kscience.kmath.linear.plus
import kscience.kmath.misc.UnstableKMathAPI
import kscience.kmath.structures.getFeature
import org.ejml.dense.row.factory.DecompositionFactory_DDRM import org.ejml.dense.row.factory.DecompositionFactory_DDRM
import org.ejml.simple.SimpleMatrix import org.ejml.simple.SimpleMatrix
import kotlin.random.Random import kotlin.random.Random
@ -38,6 +40,7 @@ internal class EjmlMatrixTest {
assertEquals(listOf(m.numRows(), m.numCols()), w.shape.toList()) assertEquals(listOf(m.numRows(), m.numCols()), w.shape.toList())
} }
@OptIn(UnstableKMathAPI::class)
@Test @Test
fun features() { fun features() {
val m = randomMatrix val m = randomMatrix
@ -56,9 +59,10 @@ internal class EjmlMatrixTest {
private object SomeFeature : MatrixFeature {} private object SomeFeature : MatrixFeature {}
@OptIn(UnstableKMathAPI::class)
@Test @Test
fun suggestFeature() { fun suggestFeature() {
assertNotNull(EjmlMatrix(randomMatrix).suggestFeature(SomeFeature).getFeature<SomeFeature>()) assertNotNull((EjmlMatrix(randomMatrix) + SomeFeature).getFeature<SomeFeature>())
} }
@Test @Test

View File

@ -1,8 +1,12 @@
package kscience.kmath.real package kscience.kmath.real
import kscience.kmath.linear.* import kscience.kmath.linear.MatrixContext
import kscience.kmath.linear.VirtualMatrix
import kscience.kmath.linear.inverseWithLUP
import kscience.kmath.linear.real
import kscience.kmath.misc.UnstableKMathAPI import kscience.kmath.misc.UnstableKMathAPI
import kscience.kmath.structures.Buffer import kscience.kmath.structures.Buffer
import kscience.kmath.structures.Matrix
import kscience.kmath.structures.RealBuffer import kscience.kmath.structures.RealBuffer
import kscience.kmath.structures.asIterable import kscience.kmath.structures.asIterable
import kotlin.math.pow import kotlin.math.pow
@ -19,7 +23,7 @@ import kotlin.math.pow
* Functions that help create a real (Double) matrix * Functions that help create a real (Double) matrix
*/ */
public typealias RealMatrix = FeaturedMatrix<Double> public typealias RealMatrix = Matrix<Double>
public fun realMatrix(rowNum: Int, colNum: Int, initializer: (i: Int, j: Int) -> Double): RealMatrix = public fun realMatrix(rowNum: Int, colNum: Int, initializer: (i: Int, j: Int) -> Double): RealMatrix =
MatrixContext.real.produce(rowNum, colNum, initializer) MatrixContext.real.produce(rowNum, colNum, initializer)

View File

@ -1,6 +1,5 @@
package kaceince.kmath.real package kaceince.kmath.real
import kscience.kmath.linear.VirtualMatrix
import kscience.kmath.linear.build import kscience.kmath.linear.build
import kscience.kmath.real.* import kscience.kmath.real.*
import kscience.kmath.structures.Matrix import kscience.kmath.structures.Matrix
@ -42,7 +41,7 @@ internal class RealMatrixTest {
1.0, 0.0, 0.0, 1.0, 0.0, 0.0,
0.0, 1.0, 2.0 0.0, 1.0, 2.0
) )
assertEquals(VirtualMatrix.wrap(matrix2), matrix1.repeatStackVertical(3)) assertEquals(matrix2, matrix1.repeatStackVertical(3))
} }
@Test @Test

View File

@ -1,5 +1,6 @@
package kscience.kmath.nd4j package kscience.kmath.nd4j
import kscience.kmath.misc.UnstableKMathAPI
import kscience.kmath.operations.* import kscience.kmath.operations.*
import kscience.kmath.structures.NDAlgebra import kscience.kmath.structures.NDAlgebra
import kscience.kmath.structures.NDField import kscience.kmath.structures.NDField
@ -35,7 +36,7 @@ public interface Nd4jArrayAlgebra<T, C> : NDAlgebra<T, C, Nd4jArrayStructure<T>>
public override fun mapIndexed( public override fun mapIndexed(
arg: Nd4jArrayStructure<T>, arg: Nd4jArrayStructure<T>,
transform: C.(index: IntArray, T) -> T transform: C.(index: IntArray, T) -> T,
): Nd4jArrayStructure<T> { ): Nd4jArrayStructure<T> {
check(arg) check(arg)
val new = Nd4j.create(*shape).wrap() val new = Nd4j.create(*shape).wrap()
@ -46,7 +47,7 @@ public interface Nd4jArrayAlgebra<T, C> : NDAlgebra<T, C, Nd4jArrayStructure<T>>
public override fun combine( public override fun combine(
a: Nd4jArrayStructure<T>, a: Nd4jArrayStructure<T>,
b: Nd4jArrayStructure<T>, b: Nd4jArrayStructure<T>,
transform: C.(T, T) -> T transform: C.(T, T) -> T,
): Nd4jArrayStructure<T> { ): Nd4jArrayStructure<T> {
check(a, b) check(a, b)
val new = Nd4j.create(*shape).wrap() val new = Nd4j.create(*shape).wrap()
@ -61,8 +62,8 @@ public interface Nd4jArrayAlgebra<T, C> : NDAlgebra<T, C, Nd4jArrayStructure<T>>
* @param T the type of the element contained in ND structure. * @param T the type of the element contained in ND structure.
* @param S the type of space of structure elements. * @param S the type of space of structure elements.
*/ */
public interface Nd4jArraySpace<T, S> : NDSpace<T, S, Nd4jArrayStructure<T>>, public interface Nd4jArraySpace<T, S : Space<T>> : NDSpace<T, S, Nd4jArrayStructure<T>>, Nd4jArrayAlgebra<T, S> {
Nd4jArrayAlgebra<T, S> where S : Space<T> {
public override val zero: Nd4jArrayStructure<T> public override val zero: Nd4jArrayStructure<T>
get() = Nd4j.zeros(*shape).wrap() get() = Nd4j.zeros(*shape).wrap()
@ -103,7 +104,9 @@ public interface Nd4jArraySpace<T, S> : NDSpace<T, S, Nd4jArrayStructure<T>>,
* @param T the type of the element contained in ND structure. * @param T the type of the element contained in ND structure.
* @param R the type of ring of structure elements. * @param R the type of ring of structure elements.
*/ */
public interface Nd4jArrayRing<T, R> : NDRing<T, R, Nd4jArrayStructure<T>>, Nd4jArraySpace<T, R> where R : Ring<T> { @OptIn(UnstableKMathAPI::class)
public interface Nd4jArrayRing<T, R : Ring<T>> : NDRing<T, R, Nd4jArrayStructure<T>>, Nd4jArraySpace<T, R> {
public override val one: Nd4jArrayStructure<T> public override val one: Nd4jArrayStructure<T>
get() = Nd4j.ones(*shape).wrap() get() = Nd4j.ones(*shape).wrap()
@ -111,21 +114,21 @@ public interface Nd4jArrayRing<T, R> : NDRing<T, R, Nd4jArrayStructure<T>>, Nd4j
check(a, b) check(a, b)
return a.ndArray.mul(b.ndArray).wrap() return a.ndArray.mul(b.ndArray).wrap()
} }
//
public override operator fun Nd4jArrayStructure<T>.minus(b: Number): Nd4jArrayStructure<T> { // public override operator fun Nd4jArrayStructure<T>.minus(b: Number): Nd4jArrayStructure<T> {
check(this) // check(this)
return ndArray.sub(b).wrap() // return ndArray.sub(b).wrap()
} // }
//
public override operator fun Nd4jArrayStructure<T>.plus(b: Number): Nd4jArrayStructure<T> { // public override operator fun Nd4jArrayStructure<T>.plus(b: Number): Nd4jArrayStructure<T> {
check(this) // check(this)
return ndArray.add(b).wrap() // return ndArray.add(b).wrap()
} // }
//
public override operator fun Number.minus(b: Nd4jArrayStructure<T>): Nd4jArrayStructure<T> { // public override operator fun Number.minus(b: Nd4jArrayStructure<T>): Nd4jArrayStructure<T> {
check(b) // check(b)
return b.ndArray.rsub(this).wrap() // return b.ndArray.rsub(this).wrap()
} // }
public companion object { public companion object {
private val intNd4jArrayRingCache: ThreadLocal<MutableMap<IntArray, IntNd4jArrayRing>> = private val intNd4jArrayRingCache: ThreadLocal<MutableMap<IntArray, IntNd4jArrayRing>> =
@ -165,7 +168,8 @@ public interface Nd4jArrayRing<T, R> : NDRing<T, R, Nd4jArrayStructure<T>>, Nd4j
* @param N the type of ND structure. * @param N the type of ND structure.
* @param F the type field of structure elements. * @param F the type field of structure elements.
*/ */
public interface Nd4jArrayField<T, F> : NDField<T, F, Nd4jArrayStructure<T>>, Nd4jArrayRing<T, F> where F : Field<T> { public interface Nd4jArrayField<T, F : Field<T>> : NDField<T, F, Nd4jArrayStructure<T>>, Nd4jArrayRing<T, F> {
public override fun divide(a: Nd4jArrayStructure<T>, b: Nd4jArrayStructure<T>): Nd4jArrayStructure<T> { public override fun divide(a: Nd4jArrayStructure<T>, b: Nd4jArrayStructure<T>): Nd4jArrayStructure<T> {
check(a, b) check(a, b)
return a.ndArray.div(b.ndArray).wrap() return a.ndArray.div(b.ndArray).wrap()

View File

@ -62,6 +62,7 @@ class MCScopeTest {
} }
@OptIn(ObsoleteCoroutinesApi::class)
fun compareResult(test: ATest) { fun compareResult(test: ATest) {
val res1 = runBlocking(Dispatchers.Default) { test() } val res1 = runBlocking(Dispatchers.Default) { test() }
val res2 = runBlocking(newSingleThreadContext("test")) { test() } val res2 = runBlocking(newSingleThreadContext("test")) { test() }

View File

@ -8,8 +8,8 @@ pluginManagement {
maven("https://dl.bintray.com/kotlin/kotlinx") maven("https://dl.bintray.com/kotlin/kotlinx")
} }
val toolsVersion = "0.7.1" val toolsVersion = "0.7.3-1.4.30-RC"
val kotlinVersion = "1.4.21" val kotlinVersion = "1.4.30-RC"
plugins { plugins {
id("kotlinx.benchmark") version "0.2.0-dev-20" id("kotlinx.benchmark") version "0.2.0-dev-20"