From 415d11749aee1caa8aefbf102b0b89baa8976195 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Sun, 28 Jun 2020 22:06:50 +0700 Subject: [PATCH 01/54] Improve big arithmetics algebra in JVM module of kmath-core --- .../kmath/asm/internal/AsmBuilder.kt | 2 +- .../scientifik/kmath/operations/BigNumbers.kt | 45 ++++++++++++++----- 2 files changed, 35 insertions(+), 12 deletions(-) diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt index 5531fd5dc..f3ee81c1c 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt @@ -340,7 +340,7 @@ internal class AsmBuilder internal constructor( checkcast(type) } - fun loadNumeric(value: Number) { + internal fun loadNumeric(value: Number) { if (expectationStack.peek() == NUMBER_TYPE) { loadNumberConstant(value, true) expectationStack.pop() diff --git a/kmath-core/src/jvmMain/kotlin/scientifik/kmath/operations/BigNumbers.kt b/kmath-core/src/jvmMain/kotlin/scientifik/kmath/operations/BigNumbers.kt index e6f09c040..1c1c0a5bf 100644 --- a/kmath-core/src/jvmMain/kotlin/scientifik/kmath/operations/BigNumbers.kt +++ b/kmath-core/src/jvmMain/kotlin/scientifik/kmath/operations/BigNumbers.kt @@ -7,31 +7,54 @@ import java.math.MathContext /** * A field wrapper for Java [BigInteger] */ -object JBigIntegerField : Field { - override val zero: BigInteger = BigInteger.ZERO - override val one: BigInteger = BigInteger.ONE +object JBigIntegerRing : Ring, PowerOperations { + override val zero: BigInteger + get() = BigInteger.ZERO + + override val one: BigInteger + get() = BigInteger.ONE override fun add(a: BigInteger, b: BigInteger): BigInteger = a.add(b) - + override fun BigInteger.minus(b: BigInteger): BigInteger = this.subtract(b) override fun multiply(a: BigInteger, k: Number): BigInteger = a.multiply(k.toInt().toBigInteger()) - override fun multiply(a: BigInteger, b: BigInteger): BigInteger = a.multiply(b) - - override fun divide(a: BigInteger, b: BigInteger): BigInteger = a.div(b) + override fun power(arg: BigInteger, pow: Number): BigInteger = arg.pow(pow.toInt()) + override fun sqrt(arg: BigInteger): BigInteger = arg.sqrt() + override fun BigInteger.unaryMinus(): BigInteger = negate() } /** * A Field wrapper for Java [BigDecimal] */ -class JBigDecimalField(val mathContext: MathContext = MathContext.DECIMAL64) : Field { - override val zero: BigDecimal = BigDecimal.ZERO - override val one: BigDecimal = BigDecimal.ONE +abstract class JBigDecimalFieldBase internal constructor(val mathContext: MathContext = MathContext.DECIMAL64) : + Field, + PowerOperations { + override val zero: BigDecimal + get() = BigDecimal.ZERO + + override val one: BigDecimal + get() = BigDecimal.ONE override fun add(a: BigDecimal, b: BigDecimal): BigDecimal = a.add(b) + override fun BigDecimal.minus(b: BigDecimal): BigDecimal { + return subtract(b) + } override fun multiply(a: BigDecimal, k: Number): BigDecimal = a.multiply(k.toDouble().toBigDecimal(mathContext), mathContext) override fun multiply(a: BigDecimal, b: BigDecimal): BigDecimal = a.multiply(b, mathContext) override fun divide(a: BigDecimal, b: BigDecimal): BigDecimal = a.divide(b, mathContext) -} \ No newline at end of file + override fun power(arg: BigDecimal, pow: Number): BigDecimal = arg.pow(pow.toInt(), mathContext) + override fun sqrt(arg: BigDecimal): BigDecimal = arg.sqrt(mathContext) + override fun BigDecimal.unaryMinus(): BigDecimal = negate(mathContext) + +} + +class JBigDecimalField(mathContext: MathContext = MathContext.DECIMAL64) : JBigDecimalFieldBase(mathContext) { + companion object : JBigDecimalFieldBase() +} + +fun main() { + JBigDecimalField { one pow 2 } +} From 1614eef4526066dcab71220db2929f19f32fe5f9 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Sun, 28 Jun 2020 22:10:39 +0700 Subject: [PATCH 02/54] Revert change of JBigIntegerField to Ring, delete unused psvm function --- .../kotlin/scientifik/kmath/operations/BigNumbers.kt | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/kmath-core/src/jvmMain/kotlin/scientifik/kmath/operations/BigNumbers.kt b/kmath-core/src/jvmMain/kotlin/scientifik/kmath/operations/BigNumbers.kt index 1c1c0a5bf..32feeb0d1 100644 --- a/kmath-core/src/jvmMain/kotlin/scientifik/kmath/operations/BigNumbers.kt +++ b/kmath-core/src/jvmMain/kotlin/scientifik/kmath/operations/BigNumbers.kt @@ -7,13 +7,14 @@ import java.math.MathContext /** * A field wrapper for Java [BigInteger] */ -object JBigIntegerRing : Ring, PowerOperations { +object JBigIntegerField : Field, PowerOperations { override val zero: BigInteger get() = BigInteger.ZERO override val one: BigInteger get() = BigInteger.ONE + override fun divide(a: BigInteger, b: BigInteger): BigInteger = a.div(b) override fun add(a: BigInteger, b: BigInteger): BigInteger = a.add(b) override fun BigInteger.minus(b: BigInteger): BigInteger = this.subtract(b) override fun multiply(a: BigInteger, k: Number): BigInteger = a.multiply(k.toInt().toBigInteger()) @@ -54,7 +55,3 @@ abstract class JBigDecimalFieldBase internal constructor(val mathContext: MathCo class JBigDecimalField(mathContext: MathContext = MathContext.DECIMAL64) : JBigDecimalFieldBase(mathContext) { companion object : JBigDecimalFieldBase() } - -fun main() { - JBigDecimalField { one pow 2 } -} From e96ecaddcfab5c8ab659c2b302d8ff7630b8cb95 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Sun, 28 Jun 2020 22:21:18 +0700 Subject: [PATCH 03/54] Revert implementing PowerOperations for JBigIntegerField --- .../jvmMain/kotlin/scientifik/kmath/operations/BigNumbers.kt | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/kmath-core/src/jvmMain/kotlin/scientifik/kmath/operations/BigNumbers.kt b/kmath-core/src/jvmMain/kotlin/scientifik/kmath/operations/BigNumbers.kt index 32feeb0d1..48d3c8c59 100644 --- a/kmath-core/src/jvmMain/kotlin/scientifik/kmath/operations/BigNumbers.kt +++ b/kmath-core/src/jvmMain/kotlin/scientifik/kmath/operations/BigNumbers.kt @@ -7,7 +7,7 @@ import java.math.MathContext /** * A field wrapper for Java [BigInteger] */ -object JBigIntegerField : Field, PowerOperations { +object JBigIntegerField : Field { override val zero: BigInteger get() = BigInteger.ZERO @@ -19,8 +19,6 @@ object JBigIntegerField : Field, PowerOperations { override fun BigInteger.minus(b: BigInteger): BigInteger = this.subtract(b) override fun multiply(a: BigInteger, k: Number): BigInteger = a.multiply(k.toInt().toBigInteger()) override fun multiply(a: BigInteger, b: BigInteger): BigInteger = a.multiply(b) - override fun power(arg: BigInteger, pow: Number): BigInteger = arg.pow(pow.toInt()) - override fun sqrt(arg: BigInteger): BigInteger = arg.sqrt() override fun BigInteger.unaryMinus(): BigInteger = negate() } From e64a6796ea9c1250136f88abfaa83b1ab2f7ab4f Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Fri, 3 Jul 2020 00:46:48 +0700 Subject: [PATCH 04/54] Fix division of Complex, minor reformat and rearrangement of NumberAlgebra, implement hyperbolic functions --- .../commons/expressions/DiffExpression.kt | 11 +- .../scientifik/kmath/operations/Complex.kt | 101 ++++---- .../kmath/operations/NumberAlgebra.kt | 221 ++++++++++-------- .../kmath/operations/OptionalOperations.kt | 104 +++++++-- .../kmath/structures/ComplexNDField.kt | 33 +-- .../kmath/structures/RealBufferField.kt | 109 ++++++--- .../kmath/structures/RealNDField.kt | 39 ++-- 7 files changed, 388 insertions(+), 230 deletions(-) diff --git a/kmath-commons/src/main/kotlin/scientifik/kmath/commons/expressions/DiffExpression.kt b/kmath-commons/src/main/kotlin/scientifik/kmath/commons/expressions/DiffExpression.kt index 54c404f57..64ebe8da3 100644 --- a/kmath-commons/src/main/kotlin/scientifik/kmath/commons/expressions/DiffExpression.kt +++ b/kmath-commons/src/main/kotlin/scientifik/kmath/commons/expressions/DiffExpression.kt @@ -17,7 +17,6 @@ class DerivativeStructureField( ) : ExtendedField { override val zero: DerivativeStructure by lazy { DerivativeStructure(order, parameters.size) } - override val one: DerivativeStructure by lazy { DerivativeStructure(order, parameters.size, 1.0) } private val variables: Map = parameters.mapValues { (key, value) -> @@ -60,10 +59,18 @@ class DerivativeStructureField( override fun sin(arg: DerivativeStructure): DerivativeStructure = arg.sin() override fun cos(arg: DerivativeStructure): DerivativeStructure = arg.cos() + override fun tan(arg: DerivativeStructure): DerivativeStructure = arg.tan() override fun asin(arg: DerivativeStructure): DerivativeStructure = arg.asin() override fun acos(arg: DerivativeStructure): DerivativeStructure = arg.acos() override fun atan(arg: DerivativeStructure): DerivativeStructure = arg.atan() + override fun sinh(arg: DerivativeStructure): DerivativeStructure = arg.sinh() + override fun cosh(arg: DerivativeStructure): DerivativeStructure = arg.cosh() + override fun tanh(arg: DerivativeStructure): DerivativeStructure = arg.tanh() + override fun asinh(arg: DerivativeStructure): DerivativeStructure = arg.asinh() + override fun acosh(arg: DerivativeStructure): DerivativeStructure = arg.acosh() + override fun atanh(arg: DerivativeStructure): DerivativeStructure = arg.atanh() + override fun power(arg: DerivativeStructure, pow: Number): DerivativeStructure = when (pow) { is Double -> arg.pow(pow) is Int -> arg.pow(pow) @@ -71,9 +78,7 @@ class DerivativeStructureField( } fun power(arg: DerivativeStructure, pow: DerivativeStructure): DerivativeStructure = arg.pow(pow) - override fun exp(arg: DerivativeStructure): DerivativeStructure = arg.exp() - override fun ln(arg: DerivativeStructure): DerivativeStructure = arg.log() override operator fun DerivativeStructure.plus(b: Number): DerivativeStructure = add(b.toDouble()) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Complex.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Complex.kt index 398ea4395..95cfc1b1d 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Complex.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Complex.kt @@ -8,28 +8,47 @@ import scientifik.memory.MemorySpec import scientifik.memory.MemoryWriter import kotlin.math.* +/** + * A complex conjugate. + */ +val Complex.conjugate: Complex + get() = Complex(re, -im) + +/** + * Absolute value of complex number. + */ +val Complex.r: Double + get() = sqrt(re * re + im * im) + +/** + * An angle between vector represented by complex number and X axis. + */ +val Complex.theta: Double + get() = atan(im / re) + private val PI_DIV_2 = Complex(PI / 2, 0) /** - * A field for complex numbers + * A field for complex numbers. */ object ComplexField : ExtendedField { - override val zero: Complex = Complex(0.0, 0.0) + override val zero: Complex = Complex(0, 0) + override val one: Complex = Complex(1, 0) - override val one: Complex = Complex(1.0, 0.0) - - val i = Complex(0.0, 1.0) + /** + * The imaginary unit constant. + */ + val i = Complex(0, 1) override fun add(a: Complex, b: Complex): Complex = Complex(a.re + b.re, a.im + b.im) - override fun multiply(a: Complex, k: Number): Complex = Complex(a.re * k.toDouble(), a.im * k.toDouble()) override fun multiply(a: Complex, b: Complex): Complex = Complex(a.re * b.re - a.im * b.im, a.re * b.im + a.im * b.re) override fun divide(a: Complex, b: Complex): Complex { - val norm = b.re * b.re + b.im * b.im - return Complex((a.re * b.re + a.im * b.im) / norm, (a.re * b.im - a.im * b.re) / norm) + val scale = b.re * b.re + b.im * b.im + return a * Complex(b.re / scale, -b.im / scale) } override fun sin(arg: Complex): Complex = i * (exp(-i * arg) - exp(i * arg)) / 2 @@ -38,42 +57,40 @@ object ComplexField : ExtendedField { override fun acos(arg: Complex): Complex = PI_DIV_2 + i * ln(sqrt(one - arg pow 2) + i * arg) override fun atan(arg: Complex): Complex = i * (ln(one - i * arg) - ln(one + i * arg)) / 2 + override fun sinh(arg: Complex): Complex = (exp(arg) - exp(-arg)) / 2 + override fun cosh(arg: Complex): Complex = (exp(arg) + exp(-arg)) / 2 + override fun tanh(arg: Complex): Complex = (exp(arg) - exp(-arg)) / (exp(-arg) + exp(arg)) + override fun asinh(arg: Complex): Complex = ln(sqrt(arg pow 2) + arg) + override fun acosh(arg: Complex): Complex = ln(arg + sqrt((arg - 1) * (arg + 1))) + override fun atanh(arg: Complex): Complex = (ln(arg + 1) - ln(1 - arg)) / 2 + override fun power(arg: Complex, pow: Number): Complex = arg.r.pow(pow.toDouble()) * (cos(pow.toDouble() * arg.theta) + i * sin(pow.toDouble() * arg.theta)) override fun exp(arg: Complex): Complex = exp(arg.re) * (cos(arg.im) + i * sin(arg.im)) - override fun ln(arg: Complex): Complex = ln(arg.r) + i * atan2(arg.im, arg.re) - operator fun Double.plus(c: Complex) = add(this.toComplex(), c) - - operator fun Double.minus(c: Complex) = add(this.toComplex(), -c) - - operator fun Complex.plus(d: Double) = d + this - - operator fun Complex.minus(d: Double) = add(this, -d.toComplex()) - - operator fun Double.times(c: Complex) = Complex(c.re * this, c.im * this) - - override fun symbol(value: String): Complex = if (value == "i") { - i - } else { - super.symbol(value) - } + operator fun Double.plus(c: Complex): Complex = add(toComplex(), c) + operator fun Double.minus(c: Complex): Complex = add(toComplex(), -c) + operator fun Complex.plus(d: Double): Complex = d + this + operator fun Complex.minus(d: Double): Complex = add(this, -d.toComplex()) + operator fun Double.times(c: Complex): Complex = Complex(c.re * this, c.im * this) + override fun symbol(value: String): Complex = if (value == "i") i else super.symbol(value) } /** - * Complex number class + * Complex number class. + * + * @property re the real part of the number. + * @property im the imaginary part of the number. */ data class Complex(val re: Double, val im: Double) : FieldElement, Comparable { constructor(re: Number, im: Number) : this(re.toDouble(), im.toDouble()) - override fun unwrap(): Complex = this - - override fun Complex.wrap(): Complex = this - override val context: ComplexField get() = ComplexField + override fun unwrap(): Complex = this + override fun Complex.wrap(): Complex = this override fun compareTo(other: Complex): Int = r.compareTo(other.r) companion object : MemorySpec { @@ -90,26 +107,12 @@ data class Complex(val re: Double, val im: Double) : FieldElement Complex): Buffer = + MemoryBuffer.create(Complex, size, init) -/** - * An angle between vector represented by complex number and X axis - */ -val Complex.theta: Double get() = atan(im / re) - -fun Double.toComplex() = Complex(this, 0.0) - -inline fun Buffer.Companion.complex(size: Int, crossinline init: (Int) -> Complex): Buffer { - return MemoryBuffer.create(Complex, size, init) -} - -inline fun MutableBuffer.Companion.complex(size: Int, crossinline init: (Int) -> Complex): Buffer { - return MemoryBuffer.create(Complex, size, init) -} +inline fun MutableBuffer.Companion.complex(size: Int, crossinline init: (Int) -> Complex): Buffer = + MemoryBuffer.create(Complex, size, init) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/NumberAlgebra.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/NumberAlgebra.kt index 953c5a112..9f137788c 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/NumberAlgebra.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/NumberAlgebra.kt @@ -7,23 +7,31 @@ import kotlin.math.pow as kpow * Advanced Number-like field that implements basic operations */ interface ExtendedFieldOperations : - InverseTrigonometricOperations, + TrigonometricOperations, + HyperbolicTrigonometricOperations, PowerOperations, ExponentialOperations { override fun tan(arg: T): T = sin(arg) / cos(arg) + override fun tanh(arg: T): T = sinh(arg) / cosh(arg) override fun unaryOperation(operation: String, arg: T): T = when (operation) { TrigonometricOperations.COS_OPERATION -> cos(arg) TrigonometricOperations.SIN_OPERATION -> sin(arg) TrigonometricOperations.TAN_OPERATION -> tan(arg) - InverseTrigonometricOperations.ACOS_OPERATION -> acos(arg) - InverseTrigonometricOperations.ASIN_OPERATION -> asin(arg) - InverseTrigonometricOperations.ATAN_OPERATION -> atan(arg) + TrigonometricOperations.ACOS_OPERATION -> acos(arg) + TrigonometricOperations.ASIN_OPERATION -> asin(arg) + TrigonometricOperations.ATAN_OPERATION -> atan(arg) + HyperbolicTrigonometricOperations.COSH_OPERATION -> cos(arg) + HyperbolicTrigonometricOperations.SINH_OPERATION -> sin(arg) + HyperbolicTrigonometricOperations.TANH_OPERATION -> tan(arg) + HyperbolicTrigonometricOperations.ACOSH_OPERATION -> acos(arg) + HyperbolicTrigonometricOperations.ASINH_OPERATION -> asin(arg) + HyperbolicTrigonometricOperations.ATANH_OPERATION -> atan(arg) PowerOperations.SQRT_OPERATION -> sqrt(arg) ExponentialOperations.EXP_OPERATION -> exp(arg) ExponentialOperations.LN_OPERATION -> ln(arg) - else -> super.unaryOperation(operation, arg) + else -> super.unaryOperation(operation, arg) } } @@ -40,12 +48,13 @@ interface ExtendedField : ExtendedFieldOperations, Field { * TODO inline does not work due to compiler bug. Waiting for fix for KT-27586 */ inline class Real(val value: Double) : FieldElement { + override val context: RealField + get() = RealField + override fun unwrap(): Double = value override fun Double.wrap(): Real = Real(value) - override val context get() = RealField - companion object } @@ -54,72 +63,86 @@ inline class Real(val value: Double) : FieldElement { */ @Suppress("EXTENSION_SHADOWED_BY_MEMBER", "OVERRIDE_BY_INLINE", "NOTHING_TO_INLINE") object RealField : ExtendedField, Norm { - override val zero: Double = 0.0 - override inline fun add(a: Double, b: Double) = a + b - override inline fun multiply(a: Double, b: Double) = a * b - override inline fun multiply(a: Double, k: Number) = a * k.toDouble() + override val zero: Double + get() = 0.0 - override val one: Double = 1.0 - override inline fun divide(a: Double, b: Double) = a / b + override val one: Double + get() = 1.0 - override inline fun sin(arg: Double) = kotlin.math.sin(arg) - override inline fun cos(arg: Double) = kotlin.math.cos(arg) + override inline fun add(a: Double, b: Double): Double = a + b + override inline fun multiply(a: Double, k: Number): Double = a * k.toDouble() + + override inline fun multiply(a: Double, b: Double): Double = a * b + + override inline fun divide(a: Double, b: Double): Double = a / b + + override inline fun sin(arg: Double): Double = kotlin.math.sin(arg) + override inline fun cos(arg: Double): Double = kotlin.math.cos(arg) override inline fun tan(arg: Double): Double = kotlin.math.tan(arg) override inline fun acos(arg: Double): Double = kotlin.math.acos(arg) override inline fun asin(arg: Double): Double = kotlin.math.asin(arg) override inline fun atan(arg: Double): Double = kotlin.math.atan(arg) - override inline fun power(arg: Double, pow: Number) = arg.kpow(pow.toDouble()) + override inline fun sinh(arg: Double): Double = kotlin.math.sinh(arg) + override inline fun cosh(arg: Double): Double = kotlin.math.cosh(arg) + override inline fun tanh(arg: Double): Double = kotlin.math.tanh(arg) + override inline fun asinh(arg: Double): Double = kotlin.math.asinh(arg) + override inline fun acosh(arg: Double): Double = kotlin.math.acosh(arg) + override inline fun atanh(arg: Double): Double = kotlin.math.atanh(arg) - override inline fun exp(arg: Double) = kotlin.math.exp(arg) - override inline fun ln(arg: Double) = kotlin.math.ln(arg) + override inline fun power(arg: Double, pow: Number): Double = arg.kpow(pow.toDouble()) + override inline fun exp(arg: Double): Double = kotlin.math.exp(arg) + override inline fun ln(arg: Double): Double = kotlin.math.ln(arg) - override inline fun norm(arg: Double) = abs(arg) + override inline fun norm(arg: Double): Double = abs(arg) - override inline fun Double.unaryMinus() = -this - - override inline fun Double.plus(b: Double) = this + b - - override inline fun Double.minus(b: Double) = this - b - - override inline fun Double.times(b: Double) = this * b - - override inline fun Double.div(b: Double) = this / b + override inline fun Double.unaryMinus(): Double = -this + override inline fun Double.plus(b: Double): Double = this + b + override inline fun Double.minus(b: Double): Double = this - b + override inline fun Double.times(b: Double): Double = this * b + override inline fun Double.div(b: Double): Double = this / b } @Suppress("EXTENSION_SHADOWED_BY_MEMBER", "OVERRIDE_BY_INLINE", "NOTHING_TO_INLINE") object FloatField : ExtendedField, Norm { - override val zero: Float = 0f - override inline fun add(a: Float, b: Float) = a + b - override inline fun multiply(a: Float, b: Float) = a * b - override inline fun multiply(a: Float, k: Number) = a * k.toFloat() + override val zero: Float + get() = 0.0f - override val one: Float = 1f - override inline fun divide(a: Float, b: Float) = a / b + override val one: Float + get() = 1.0f - override inline fun sin(arg: Float) = kotlin.math.sin(arg) - override inline fun cos(arg: Float) = kotlin.math.cos(arg) - override inline fun tan(arg: Float) = kotlin.math.tan(arg) - override inline fun acos(arg: Float) = kotlin.math.acos(arg) - override inline fun asin(arg: Float) = kotlin.math.asin(arg) - override inline fun atan(arg: Float) = kotlin.math.atan(arg) + override inline fun add(a: Float, b: Float): Float = a + b + override inline fun multiply(a: Float, k: Number): Float = a * k.toFloat() - override inline fun power(arg: Float, pow: Number) = arg.pow(pow.toFloat()) + override inline fun multiply(a: Float, b: Float): Float = a * b - override inline fun exp(arg: Float) = kotlin.math.exp(arg) - override inline fun ln(arg: Float) = kotlin.math.ln(arg) + override inline fun divide(a: Float, b: Float): Float = a / b - override inline fun norm(arg: Float) = abs(arg) + override inline fun sin(arg: Float): Float = kotlin.math.sin(arg) + override inline fun cos(arg: Float): Float = kotlin.math.cos(arg) + override inline fun tan(arg: Float): Float = kotlin.math.tan(arg) + override inline fun acos(arg: Float): Float = kotlin.math.acos(arg) + override inline fun asin(arg: Float): Float = kotlin.math.asin(arg) + override inline fun atan(arg: Float): Float = kotlin.math.atan(arg) - override inline fun Float.unaryMinus() = -this + override inline fun sinh(arg: Float): Float = kotlin.math.sinh(arg) + override inline fun cosh(arg: Float): Float = kotlin.math.cosh(arg) + override inline fun tanh(arg: Float): Float = kotlin.math.tanh(arg) + override inline fun asinh(arg: Float): Float = kotlin.math.asinh(arg) + override inline fun acosh(arg: Float): Float = kotlin.math.acosh(arg) + override inline fun atanh(arg: Float): Float = kotlin.math.atanh(arg) - override inline fun Float.plus(b: Float) = this + b + override inline fun power(arg: Float, pow: Number): Float = arg.kpow(pow.toFloat()) + override inline fun exp(arg: Float): Float = kotlin.math.exp(arg) + override inline fun ln(arg: Float): Float = kotlin.math.ln(arg) - override inline fun Float.minus(b: Float) = this - b + override inline fun norm(arg: Float): Float = abs(arg) - override inline fun Float.times(b: Float) = this * b - - override inline fun Float.div(b: Float) = this / b + override inline fun Float.unaryMinus(): Float = -this + override inline fun Float.plus(b: Float): Float = this + b + override inline fun Float.minus(b: Float): Float = this - b + override inline fun Float.times(b: Float): Float = this * b + override inline fun Float.div(b: Float): Float = this / b } /** @@ -127,20 +150,22 @@ object FloatField : ExtendedField, Norm { */ @Suppress("EXTENSION_SHADOWED_BY_MEMBER", "OVERRIDE_BY_INLINE", "NOTHING_TO_INLINE") object IntRing : Ring, Norm { - override val zero: Int = 0 - override inline fun add(a: Int, b: Int) = a + b - override inline fun multiply(a: Int, b: Int) = a * b - override inline fun multiply(a: Int, k: Number) = k.toInt() * a - override val one: Int = 1 + override val zero: Int + get() = 0 - override inline fun norm(arg: Int) = abs(arg) + override val one: Int + get() = 1 - override inline fun Int.unaryMinus() = -this + override inline fun add(a: Int, b: Int): Int = a + b + override inline fun multiply(a: Int, k: Number): Int = k.toInt() * a + override inline fun multiply(a: Int, b: Int): Int = a * b + + override inline fun norm(arg: Int): Int = abs(arg) + + override inline fun Int.unaryMinus(): Int = -this override inline fun Int.plus(b: Int): Int = this + b - override inline fun Int.minus(b: Int): Int = this - b - override inline fun Int.times(b: Int): Int = this * b } @@ -149,21 +174,23 @@ object IntRing : Ring, Norm { */ @Suppress("EXTENSION_SHADOWED_BY_MEMBER", "OVERRIDE_BY_INLINE", "NOTHING_TO_INLINE") object ShortRing : Ring, Norm { - override val zero: Short = 0 - override inline fun add(a: Short, b: Short) = (a + b).toShort() - override inline fun multiply(a: Short, b: Short) = (a * b).toShort() - override inline fun multiply(a: Short, k: Number) = (a * k.toShort()).toShort() - override val one: Short = 1 + override val zero: Short + get() = 0 + + override val one: Short + get() = 1 + + override inline fun add(a: Short, b: Short): Short = (a + b).toShort() + override inline fun multiply(a: Short, k: Number): Short = (a * k.toShort()).toShort() + + override inline fun multiply(a: Short, b: Short): Short = (a * b).toShort() override fun norm(arg: Short): Short = if (arg > 0) arg else (-arg).toShort() - override inline fun Short.unaryMinus() = (-this).toShort() - - override inline fun Short.plus(b: Short) = (this + b).toShort() - - override inline fun Short.minus(b: Short) = (this - b).toShort() - - override inline fun Short.times(b: Short) = (this * b).toShort() + override inline fun Short.unaryMinus(): Short = (-this).toShort() + override inline fun Short.plus(b: Short): Short = (this + b).toShort() + override inline fun Short.minus(b: Short): Short = (this - b).toShort() + override inline fun Short.times(b: Short): Short = (this * b).toShort() } /** @@ -171,21 +198,23 @@ object ShortRing : Ring, Norm { */ @Suppress("EXTENSION_SHADOWED_BY_MEMBER", "OVERRIDE_BY_INLINE", "NOTHING_TO_INLINE") object ByteRing : Ring, Norm { - override val zero: Byte = 0 - override inline fun add(a: Byte, b: Byte) = (a + b).toByte() - override inline fun multiply(a: Byte, b: Byte) = (a * b).toByte() - override inline fun multiply(a: Byte, k: Number) = (a * k.toByte()).toByte() - override val one: Byte = 1 + override val zero: Byte + get() = 0 + + override val one: Byte + get() = 1 + + override inline fun add(a: Byte, b: Byte): Byte = (a + b).toByte() + override inline fun multiply(a: Byte, k: Number): Byte = (a * k.toByte()).toByte() + + override inline fun multiply(a: Byte, b: Byte): Byte = (a * b).toByte() override fun norm(arg: Byte): Byte = if (arg > 0) arg else (-arg).toByte() - override inline fun Byte.unaryMinus() = (-this).toByte() - - override inline fun Byte.plus(b: Byte) = (this + b).toByte() - - override inline fun Byte.minus(b: Byte) = (this - b).toByte() - - override inline fun Byte.times(b: Byte) = (this * b).toByte() + override inline fun Byte.unaryMinus(): Byte = (-this).toByte() + override inline fun Byte.plus(b: Byte): Byte = (this + b).toByte() + override inline fun Byte.minus(b: Byte): Byte = (this - b).toByte() + override inline fun Byte.times(b: Byte): Byte = (this * b).toByte() } /** @@ -193,19 +222,21 @@ object ByteRing : Ring, Norm { */ @Suppress("EXTENSION_SHADOWED_BY_MEMBER", "OVERRIDE_BY_INLINE", "NOTHING_TO_INLINE") object LongRing : Ring, Norm { - override val zero: Long = 0 - override inline fun add(a: Long, b: Long) = (a + b) - override inline fun multiply(a: Long, b: Long) = (a * b) - override inline fun multiply(a: Long, k: Number) = a * k.toLong() - override val one: Long = 1 + override val zero: Long + get() = 0 + + override val one: Long + get() = 1 + + override inline fun add(a: Long, b: Long): Long = a + b + override inline fun multiply(a: Long, k: Number): Long = a * k.toLong() + + override inline fun multiply(a: Long, b: Long): Long = a * b override fun norm(arg: Long): Long = abs(arg) - override inline fun Long.unaryMinus() = (-this) - - override inline fun Long.plus(b: Long) = (this + b) - - override inline fun Long.minus(b: Long) = (this - b) - - override inline fun Long.times(b: Long) = (this * b) + override inline fun Long.unaryMinus(): Long = (-this) + override inline fun Long.plus(b: Long): Long = (this + b) + override inline fun Long.minus(b: Long): Long = (this - b) + override inline fun Long.times(b: Long): Long = (this * b) } diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/OptionalOperations.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/OptionalOperations.kt index 709f0260f..bd1b8efab 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/OptionalOperations.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/OptionalOperations.kt @@ -1,33 +1,46 @@ package scientifik.kmath.operations - -/* Trigonometric operations */ - /** * A container for trigonometric operations for specific type. Trigonometric operations are limited to fields. * * The operations are not exposed to class directly to avoid method bloat but instead are declared in the field. - * It also allows to override behavior for optional operations - * + * It also allows to override behavior for optional operations. */ interface TrigonometricOperations : FieldOperations { + /** + * Computes the sine of [arg] . + */ fun sin(arg: T): T + + /** + * Computes the cosine of [arg]. + */ fun cos(arg: T): T + + /** + * Computes the tangent of [arg]. + */ fun tan(arg: T): T + /** + * Computes the inverse sine of [arg]. + */ + fun asin(arg: T): T + + /** + * Computes the inverse cosine of [arg]. + */ + fun acos(arg: T): T + + /** + * Computes the inverse tangent of [arg]. + */ + fun atan(arg: T): T + companion object { const val SIN_OPERATION = "sin" const val COS_OPERATION = "cos" const val TAN_OPERATION = "tan" - } -} - -interface InverseTrigonometricOperations : TrigonometricOperations { - fun asin(arg: T): T - fun acos(arg: T): T - fun atan(arg: T): T - - companion object { const val ASIN_OPERATION = "asin" const val ACOS_OPERATION = "acos" const val ATAN_OPERATION = "atan" @@ -37,11 +50,64 @@ interface InverseTrigonometricOperations : TrigonometricOperations { fun >> sin(arg: T): T = arg.context.sin(arg) fun >> cos(arg: T): T = arg.context.cos(arg) fun >> tan(arg: T): T = arg.context.tan(arg) -fun >> asin(arg: T): T = arg.context.asin(arg) -fun >> acos(arg: T): T = arg.context.acos(arg) -fun >> atan(arg: T): T = arg.context.atan(arg) +fun >> asin(arg: T): T = arg.context.asin(arg) +fun >> acos(arg: T): T = arg.context.acos(arg) +fun >> atan(arg: T): T = arg.context.atan(arg) -/* Power and roots */ +/** + * A container for hyperbolic trigonometric operations for specific type. Trigonometric operations are limited to + * fields. + * + * The operations are not exposed to class directly to avoid method bloat but instead are declared in the field. It + * also allows to override behavior for optional operations. + */ +interface HyperbolicTrigonometricOperations : FieldOperations { + /** + * Computes the hyperbolic sine of [arg]. + */ + fun sinh(arg: T): T + + /** + * Computes the hyperbolic cosine of [arg]. + */ + fun cosh(arg: T): T + + /** + * Computes the hyperbolic tangent of [arg]. + */ + fun tanh(arg: T): T + + /** + * Computes the inverse hyperbolic sine of [arg]. + */ + fun asinh(arg: T): T + + /** + * Computes the inverse hyperbolic cosine of [arg]. + */ + fun acosh(arg: T): T + + /** + * Computes the inverse hyperbolic tangent of [arg]. + */ + fun atanh(arg: T): T + + companion object { + const val SINH_OPERATION = "sinh" + const val COSH_OPERATION = "cosh" + const val TANH_OPERATION = "tanh" + const val ASINH_OPERATION = "asinh" + const val ACOSH_OPERATION = "acosh" + const val ATANH_OPERATION = "atanh" + } +} + +fun >> sinh(arg: T): T = arg.context.sinh(arg) +fun >> cosh(arg: T): T = arg.context.cosh(arg) +fun >> tanh(arg: T): T = arg.context.tanh(arg) +fun >> asinh(arg: T): T = arg.context.asinh(arg) +fun >> acosh(arg: T): T = arg.context.acosh(arg) +fun >> atanh(arg: T): T = arg.context.atanh(arg) /** * A context extension to include power operations like square roots, etc @@ -62,8 +128,6 @@ infix fun >> T.pow(power: Double): T = co fun >> sqrt(arg: T): T = arg pow 0.5 fun >> sqr(arg: T): T = arg pow 2.0 -/* Exponential */ - interface ExponentialOperations : Algebra { fun exp(arg: T): T fun ln(arg: T): T diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ComplexNDField.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ComplexNDField.kt index c7e672c28..85c997c13 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ComplexNDField.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ComplexNDField.kt @@ -15,7 +15,6 @@ class ComplexNDField(override val shape: IntArray) : ExtendedNDField> { override val strides: Strides = DefaultStrides(shape) - override val elementContext: ComplexField get() = ComplexField override val zero by lazy { produce { zero } } override val one by lazy { produce { one } } @@ -45,6 +44,7 @@ class ComplexNDField(override val shape: IntArray) : transform: ComplexField.(index: IntArray, Complex) -> Complex ): ComplexNDElement { check(arg) + return BufferedNDFieldElement( this, buildBuffer(arg.strides.linearSize) { offset -> @@ -61,6 +61,7 @@ class ComplexNDField(override val shape: IntArray) : transform: ComplexField.(Complex, Complex) -> Complex ): ComplexNDElement { check(a, b) + return BufferedNDFieldElement( this, buildBuffer(strides.linearSize) { offset -> elementContext.transform(a.buffer[offset], b.buffer[offset]) }) @@ -69,23 +70,25 @@ class ComplexNDField(override val shape: IntArray) : override fun NDBuffer.toElement(): FieldElement, *, out BufferedNDField> = BufferedNDFieldElement(this@ComplexNDField, buffer) - override fun power(arg: NDBuffer, pow: Number) = map(arg) { power(it, pow) } + override fun power(arg: NDBuffer, pow: Number): BufferedNDFieldElement = + map(arg) { power(it, pow) } - override fun exp(arg: NDBuffer) = map(arg) { exp(it) } + override fun exp(arg: NDBuffer): BufferedNDFieldElement = map(arg) { exp(it) } + override fun ln(arg: NDBuffer): BufferedNDFieldElement = map(arg) { ln(it) } - override fun ln(arg: NDBuffer) = map(arg) { ln(it) } + override fun sin(arg: NDBuffer): BufferedNDFieldElement = map(arg) { sin(it) } + override fun cos(arg: NDBuffer): BufferedNDFieldElement = map(arg) { cos(it) } + override fun tan(arg: NDBuffer): BufferedNDFieldElement = map(arg) { tan(it) } + override fun asin(arg: NDBuffer): BufferedNDFieldElement = map(arg) { asin(it) } + override fun acos(arg: NDBuffer): BufferedNDFieldElement = map(arg) { acos(it) } + override fun atan(arg: NDBuffer): BufferedNDFieldElement = map(arg) { atan(it) } - override fun sin(arg: NDBuffer) = map(arg) { sin(it) } - - override fun cos(arg: NDBuffer) = map(arg) { cos(it) } - - override fun tan(arg: NDBuffer): NDBuffer = map(arg) { tan(it) } - - override fun asin(arg: NDBuffer): NDBuffer = map(arg) { asin(it) } - - override fun acos(arg: NDBuffer): NDBuffer = map(arg) {acos(it)} - - override fun atan(arg: NDBuffer): NDBuffer = map(arg) {atan(it)} + override fun sinh(arg: NDBuffer): BufferedNDFieldElement = map(arg) { sinh(it) } + override fun cosh(arg: NDBuffer): BufferedNDFieldElement = map(arg) { cosh(it) } + override fun tanh(arg: NDBuffer): BufferedNDFieldElement = map(arg) { tanh(it) } + override fun asinh(arg: NDBuffer): BufferedNDFieldElement = map(arg) { asinh(it) } + override fun acosh(arg: NDBuffer): BufferedNDFieldElement = map(arg) { acosh(it) } + override fun atanh(arg: NDBuffer): BufferedNDFieldElement = map(arg) { atanh(it) } } diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/RealBufferField.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/RealBufferField.kt index 826203d1f..62a92fdb8 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/RealBufferField.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/RealBufferField.kt @@ -6,18 +6,19 @@ import kotlin.math.* /** - * A simple field over linear buffers of [Double] + * A simple field over linear buffers of [Double]. */ object RealBufferFieldOperations : ExtendedFieldOperations> { override fun add(a: Buffer, b: Buffer): RealBuffer { - require(b.size == a.size) { "The size of the first buffer ${a.size} should be the same as for second one: ${b.size} " } + require(b.size == a.size) { + "The size of the first buffer ${a.size} should be the same as for second one: ${b.size} " + } return if (a is RealBuffer && b is RealBuffer) { val aArray = a.array val bArray = b.array RealBuffer(DoubleArray(a.size) { aArray[it] + bArray[it] }) - } else - RealBuffer(DoubleArray(a.size) { a[it] + b[it] }) + } else RealBuffer(DoubleArray(a.size) { a[it] + b[it] }) } override fun multiply(a: Buffer, k: Number): RealBuffer { @@ -26,57 +27,52 @@ object RealBufferFieldOperations : ExtendedFieldOperations> { return if (a is RealBuffer) { val aArray = a.array RealBuffer(DoubleArray(a.size) { aArray[it] * kValue }) - } else - RealBuffer(DoubleArray(a.size) { a[it] * kValue }) + } else RealBuffer(DoubleArray(a.size) { a[it] * kValue }) } override fun multiply(a: Buffer, b: Buffer): RealBuffer { - require(b.size == a.size) { "The size of the first buffer ${a.size} should be the same as for second one: ${b.size} " } + require(b.size == a.size) { + "The size of the first buffer ${a.size} should be the same as for second one: ${b.size} " + } return if (a is RealBuffer && b is RealBuffer) { val aArray = a.array val bArray = b.array RealBuffer(DoubleArray(a.size) { aArray[it] * bArray[it] }) - } else - RealBuffer(DoubleArray(a.size) { a[it] * b[it] }) + } else RealBuffer(DoubleArray(a.size) { a[it] * b[it] }) } override fun divide(a: Buffer, b: Buffer): RealBuffer { - require(b.size == a.size) { "The size of the first buffer ${a.size} should be the same as for second one: ${b.size} " } + require(b.size == a.size) { + "The size of the first buffer ${a.size} should be the same as for second one: ${b.size} " + } return if (a is RealBuffer && b is RealBuffer) { val aArray = a.array val bArray = b.array RealBuffer(DoubleArray(a.size) { aArray[it] / bArray[it] }) - } else - RealBuffer(DoubleArray(a.size) { a[it] / b[it] }) + } else RealBuffer(DoubleArray(a.size) { a[it] / b[it] }) } override fun sin(arg: Buffer): RealBuffer = if (arg is RealBuffer) { val array = arg.array RealBuffer(DoubleArray(arg.size) { sin(array[it]) }) - } else { - RealBuffer(DoubleArray(arg.size) { sin(arg[it]) }) - } + } else RealBuffer(DoubleArray(arg.size) { sin(arg[it]) }) override fun cos(arg: Buffer): RealBuffer = if (arg is RealBuffer) { val array = arg.array RealBuffer(DoubleArray(arg.size) { cos(array[it]) }) - } else - RealBuffer(DoubleArray(arg.size) { cos(arg[it]) }) + } else RealBuffer(DoubleArray(arg.size) { cos(arg[it]) }) override fun tan(arg: Buffer): RealBuffer = if (arg is RealBuffer) { val array = arg.array RealBuffer(DoubleArray(arg.size) { tan(array[it]) }) - } else - RealBuffer(DoubleArray(arg.size) { tan(arg[it]) }) + } else RealBuffer(DoubleArray(arg.size) { tan(arg[it]) }) override fun asin(arg: Buffer): RealBuffer = if (arg is RealBuffer) { val array = arg.array RealBuffer(DoubleArray(arg.size) { asin(array[it]) }) - } else { - RealBuffer(DoubleArray(arg.size) { asin(arg[it]) }) - } + } else RealBuffer(DoubleArray(arg.size) { asin(arg[it]) }) override fun acos(arg: Buffer): RealBuffer = if (arg is RealBuffer) { val array = arg.array @@ -90,23 +86,50 @@ object RealBufferFieldOperations : ExtendedFieldOperations> { } else RealBuffer(DoubleArray(arg.size) { atan(arg[it]) }) + override fun sinh(arg: Buffer): RealBuffer = if (arg is RealBuffer) { + val array = arg.array + RealBuffer(DoubleArray(arg.size) { sinh(array[it]) }) + } else RealBuffer(DoubleArray(arg.size) { sinh(arg[it]) }) + + override fun cosh(arg: Buffer): RealBuffer = if (arg is RealBuffer) { + val array = arg.array + RealBuffer(DoubleArray(arg.size) { cosh(array[it]) }) + } else RealBuffer(DoubleArray(arg.size) { cosh(arg[it]) }) + + override fun tanh(arg: Buffer): RealBuffer = if (arg is RealBuffer) { + val array = arg.array + RealBuffer(DoubleArray(arg.size) { tanh(array[it]) }) + } else RealBuffer(DoubleArray(arg.size) { tanh(arg[it]) }) + + override fun asinh(arg: Buffer): RealBuffer = if (arg is RealBuffer) { + val array = arg.array + RealBuffer(DoubleArray(arg.size) { asinh(array[it]) }) + } else RealBuffer(DoubleArray(arg.size) { asinh(arg[it]) }) + + override fun acosh(arg: Buffer): RealBuffer = if (arg is RealBuffer) { + val array = arg.array + RealBuffer(DoubleArray(arg.size) { acosh(array[it]) }) + } else RealBuffer(DoubleArray(arg.size) { acosh(arg[it]) }) + + override fun atanh(arg: Buffer): RealBuffer = if (arg is RealBuffer) { + val array = arg.array + RealBuffer(DoubleArray(arg.size) { atanh(array[it]) }) + } else RealBuffer(DoubleArray(arg.size) { atanh(arg[it]) }) + override fun power(arg: Buffer, pow: Number): RealBuffer = if (arg is RealBuffer) { val array = arg.array RealBuffer(DoubleArray(arg.size) { array[it].pow(pow.toDouble()) }) - } else - RealBuffer(DoubleArray(arg.size) { arg[it].pow(pow.toDouble()) }) + } else RealBuffer(DoubleArray(arg.size) { arg[it].pow(pow.toDouble()) }) override fun exp(arg: Buffer): RealBuffer = if (arg is RealBuffer) { val array = arg.array RealBuffer(DoubleArray(arg.size) { exp(array[it]) }) - } else - RealBuffer(DoubleArray(arg.size) { exp(arg[it]) }) + } else RealBuffer(DoubleArray(arg.size) { exp(arg[it]) }) override fun ln(arg: Buffer): RealBuffer = if (arg is RealBuffer) { val array = arg.array RealBuffer(DoubleArray(arg.size) { ln(array[it]) }) - } else - RealBuffer(DoubleArray(arg.size) { ln(arg[it]) }) + } else RealBuffer(DoubleArray(arg.size) { ln(arg[it]) }) } class RealBufferField(val size: Int) : ExtendedField> { @@ -163,6 +186,36 @@ class RealBufferField(val size: Int) : ExtendedField> { return RealBufferFieldOperations.atan(arg) } + override fun sinh(arg: Buffer): RealBuffer { + require(arg.size == size) { "The buffer size ${arg.size} does not match context size $size" } + return RealBufferFieldOperations.sinh(arg) + } + + override fun cosh(arg: Buffer): RealBuffer { + require(arg.size == size) { "The buffer size ${arg.size} does not match context size $size" } + return RealBufferFieldOperations.cosh(arg) + } + + override fun tanh(arg: Buffer): RealBuffer { + require(arg.size == size) { "The buffer size ${arg.size} does not match context size $size" } + return RealBufferFieldOperations.tanh(arg) + } + + override fun asinh(arg: Buffer): RealBuffer { + require(arg.size == size) { "The buffer size ${arg.size} does not match context size $size" } + return RealBufferFieldOperations.asinh(arg) + } + + override fun acosh(arg: Buffer): RealBuffer { + require(arg.size == size) { "The buffer size ${arg.size} does not match context size $size" } + return RealBufferFieldOperations.acosh(arg) + } + + override fun atanh(arg: Buffer): RealBuffer { + require(arg.size == size) { "The buffer size ${arg.size} does not match context size $size" } + return RealBufferFieldOperations.atanh(arg) + } + override fun power(arg: Buffer, pow: Number): RealBuffer { require(arg.size == size) { "The buffer size ${arg.size} does not match context size $size" } return RealBufferFieldOperations.power(arg, pow) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/RealNDField.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/RealNDField.kt index 8c90f90c7..26588b7b1 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/RealNDField.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/RealNDField.kt @@ -12,8 +12,8 @@ class RealNDField(override val shape: IntArray) : override val strides: Strides = DefaultStrides(shape) override val elementContext: RealField get() = RealField - override val zero by lazy { produce { zero } } - override val one by lazy { produce { one } } + override val zero: BufferedNDFieldElement by lazy { produce { zero } } + override val one: RealNDElement by lazy { produce { one } } inline fun buildBuffer(size: Int, crossinline initializer: (Int) -> Double): Buffer = RealBuffer(DoubleArray(size) { initializer(it) }) @@ -40,6 +40,7 @@ class RealNDField(override val shape: IntArray) : transform: RealField.(index: IntArray, Double) -> Double ): RealNDElement { check(arg) + return BufferedNDFieldElement( this, buildBuffer(arg.strides.linearSize) { offset -> @@ -64,23 +65,25 @@ class RealNDField(override val shape: IntArray) : override fun NDBuffer.toElement(): FieldElement, *, out BufferedNDField> = BufferedNDFieldElement(this@RealNDField, buffer) - override fun power(arg: NDBuffer, pow: Number) = map(arg) { power(it, pow) } + override fun power(arg: NDBuffer, pow: Number): RealNDElement = map(arg) { power(it, pow) } override fun exp(arg: NDBuffer) = map(arg) { exp(it) } override fun ln(arg: NDBuffer) = map(arg) { ln(it) } - override fun sin(arg: NDBuffer) = map(arg) { sin(it) } + override fun sin(arg: NDBuffer): RealNDElement = map(arg) { sin(it) } + override fun cos(arg: NDBuffer): RealNDElement = map(arg) { cos(it) } + override fun tan(arg: NDBuffer): RealNDElement = map(arg) { tan(it) } + override fun asin(arg: NDBuffer): RealNDElement = map(arg) { asin(it) } + override fun acos(arg: NDBuffer): RealNDElement = map(arg) { acos(it) } + override fun atan(arg: NDBuffer): RealNDElement = map(arg) { atan(it) } - override fun cos(arg: NDBuffer) = map(arg) { cos(it) } - - override fun tan(arg: NDBuffer): NDBuffer = map(arg) { tan(it) } - - override fun asin(arg: NDBuffer): NDBuffer = map(arg) { asin(it) } - - override fun acos(arg: NDBuffer): NDBuffer = map(arg) { acos(it) } - - override fun atan(arg: NDBuffer): NDBuffer = map(arg) { atan(it) } + override fun sinh(arg: NDBuffer): RealNDElement = map(arg) { sinh(it) } + override fun cosh(arg: NDBuffer): RealNDElement = map(arg) { cosh(it) } + override fun tanh(arg: NDBuffer): RealNDElement = map(arg) { tanh(it) } + override fun asinh(arg: NDBuffer): RealNDElement = map(arg) { asinh(it) } + override fun acosh(arg: NDBuffer): RealNDElement = map(arg) { acosh(it) } + override fun atanh(arg: NDBuffer): RealNDElement = map(arg) { atanh(it) } } @@ -118,18 +121,14 @@ operator fun Function1.invoke(ndElement: RealNDElement) = /** * Summation operation for [BufferedNDElement] and single element */ -operator fun RealNDElement.plus(arg: Double) = - map { it + arg } +operator fun RealNDElement.plus(arg: Double): RealNDElement = map { it + arg } /** * Subtraction operation between [BufferedNDElement] and single element */ -operator fun RealNDElement.minus(arg: Double) = - map { it - arg } +operator fun RealNDElement.minus(arg: Double): RealNDElement = map { it - arg } /** * Produce a context for n-dimensional operations inside this real field */ -inline fun RealField.nd(vararg shape: Int, action: RealNDField.() -> R): R { - return NDField.real(*shape).run(action) -} \ No newline at end of file +inline fun RealField.nd(vararg shape: Int, action: RealNDField.() -> R): R = NDField.real(*shape).run(action) From 73005f715a4980ebd207d52035c575b32b5780f6 Mon Sep 17 00:00:00 2001 From: Commander Tvis Date: Fri, 3 Jul 2020 15:50:14 +0700 Subject: [PATCH 05/54] Implement optimized complete Complex division, add tests class for it --- .../scientifik/kmath/operations/Complex.kt | 51 +++++++++++++++++-- .../kmath/operations/ComplexTest.kt | 49 ++++++++++++++++++ 2 files changed, 95 insertions(+), 5 deletions(-) create mode 100644 kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/ComplexTest.kt diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Complex.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Complex.kt index 95cfc1b1d..0d0593d1b 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Complex.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Complex.kt @@ -9,11 +9,20 @@ import scientifik.memory.MemoryWriter import kotlin.math.* /** - * A complex conjugate. + * This complex's conjugate. */ val Complex.conjugate: Complex get() = Complex(re, -im) +/** + * This complex's reciprocal. + */ +val Complex.reciprocal: Complex + get() { + val scale = re * re + im * im + return Complex(re / scale, -im / scale) + } + /** * Absolute value of complex number. */ @@ -46,16 +55,48 @@ object ComplexField : ExtendedField { override fun multiply(a: Complex, b: Complex): Complex = Complex(a.re * b.re - a.im * b.im, a.re * b.im + a.im * b.re) - override fun divide(a: Complex, b: Complex): Complex { - val scale = b.re * b.re + b.im * b.im - return a * Complex(b.re / scale, -b.im / scale) + override fun divide(a: Complex, b: Complex): Complex = when { + b.re.isNaN() || b.im.isNaN() -> Complex(Double.NaN, Double.NaN) + + (if (b.im < 0) -b.im else +b.im) < (if (b.re < 0) -b.re else +b.re) -> { + val wr = b.im / b.re + val wd = b.re + wr * b.im + + if (wd.isNaN() || wd == 0.0) + Complex(Double.NaN, Double.NaN) + else + Complex((a.re + a.im * wr) / wd, (a.im - a.re * wr) / wd) + } + + b.im == 0.0 -> Complex(Double.NaN, Double.NaN) + + else -> { + val wr = b.re / b.im + val wd = b.im + wr * b.re + + if (wd.isNaN() || wd == 0.0) + Complex(Double.NaN, Double.NaN) + else + Complex((a.re * wr + a.im) / wd, (a.im * wr - a.re) / wd) + } } override fun sin(arg: Complex): Complex = i * (exp(-i * arg) - exp(i * arg)) / 2 override fun cos(arg: Complex): Complex = (exp(-i * arg) + exp(i * arg)) / 2 + + override fun tan(arg: Complex): Complex { + val e1 = exp(-i * arg) + val e2 = exp(i * arg) + return i * (e1 - e2) / (e1 + e2) + } + override fun asin(arg: Complex): Complex = -i * ln(sqrt(one - arg pow 2) + i * arg) override fun acos(arg: Complex): Complex = PI_DIV_2 + i * ln(sqrt(one - arg pow 2) + i * arg) - override fun atan(arg: Complex): Complex = i * (ln(one - i * arg) - ln(one + i * arg)) / 2 + + override fun atan(arg: Complex): Complex { + val iArg = i * arg + return i * (ln(one - iArg) - ln(one + iArg)) / 2 + } override fun sinh(arg: Complex): Complex = (exp(arg) - exp(-arg)) / 2 override fun cosh(arg: Complex): Complex = (exp(arg) + exp(-arg)) / 2 diff --git a/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/ComplexTest.kt b/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/ComplexTest.kt new file mode 100644 index 000000000..ad85fa9aa --- /dev/null +++ b/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/ComplexTest.kt @@ -0,0 +1,49 @@ +package scientifik.kmath.operations + +import kotlin.math.PI +import kotlin.test.Test +import kotlin.test.assertEquals + +internal class ComplexFieldTest { + @Test + fun testAddition() { + assertEquals(Complex(42, 42), ComplexField { Complex(16, 16) + Complex(26, 26) }) + assertEquals(Complex(42, 16), ComplexField { Complex(16, 16) + 26 }) + assertEquals(Complex(42, 16), ComplexField { 26 + Complex(16, 16) }) + } + + @Test + fun testSubtraction() { + assertEquals(Complex(42, 42), ComplexField { Complex(86, 55) - Complex(44, 13) }) + assertEquals(Complex(42, 56), ComplexField { Complex(86, 56) - 44 }) + assertEquals(Complex(42, 56), ComplexField { 86 - Complex(44, -56) }) + } + + @Test + fun testMultiplication() { + assertEquals(Complex(42, 42), ComplexField { Complex(4.2, 0) * Complex(10, 10) }) + assertEquals(Complex(42, 21), ComplexField { Complex(4.2, 2.1) * 10 }) + assertEquals(Complex(42, 21), ComplexField { 10 * Complex(4.2, 2.1) }) + } + + @Test + fun testDivision() { + assertEquals(Complex(42, 42), ComplexField { Complex(0, 168) / Complex(2, 2) }) + assertEquals(Complex(42, 56), ComplexField { Complex(86, 56) - 44 }) + assertEquals(Complex(42, 56), ComplexField { 86 - Complex(44, -56) }) + assertEquals(Complex(Double.NaN, Double.NaN), ComplexField { Complex(1, 1) / Complex(Double.NaN, Double.NaN) }) + assertEquals(Complex(Double.NaN, Double.NaN), ComplexField { Complex(1, 1) / Complex(0, 0) }) + } + + @Test + fun testSine() { + assertEquals(Complex(1.2246467991473532E-16, 0), ComplexField { sin(PI.toComplex()) }) + assertEquals(Complex(0, 11.548739357257748), ComplexField { sin(i * PI.toComplex()) }) + assertEquals(Complex(0, 1.1752011936438014), ComplexField { sin(i) }) + } + + @Test + fun testArcsine() { + assertEquals(Complex(0, -0.0), ComplexField { asin(zero) }) + } +} From badb7b1365897d930789f818a863b70b221f994f Mon Sep 17 00:00:00 2001 From: Commander Tvis Date: Fri, 3 Jul 2020 17:14:50 +0700 Subject: [PATCH 06/54] Update implementation of Complex Division and Exponentiation --- .../scientifik/kmath/operations/Complex.kt | 17 +++++------- .../{ComplexTest.kt => ComplexFieldTest.kt} | 27 ++++++++++++++++--- 2 files changed, 30 insertions(+), 14 deletions(-) rename kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/{ComplexTest.kt => ComplexFieldTest.kt} (62%) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Complex.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Complex.kt index 0d0593d1b..3dd6f5157 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Complex.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Complex.kt @@ -41,8 +41,8 @@ private val PI_DIV_2 = Complex(PI / 2, 0) * A field for complex numbers. */ object ComplexField : ExtendedField { - override val zero: Complex = Complex(0, 0) - override val one: Complex = Complex(1, 0) + override val zero: Complex = 0.0.toComplex() + override val one: Complex = 1.0.toComplex() /** * The imaginary unit constant. @@ -90,24 +90,21 @@ object ComplexField : ExtendedField { return i * (e1 - e2) / (e1 + e2) } - override fun asin(arg: Complex): Complex = -i * ln(sqrt(one - arg pow 2) + i * arg) - override fun acos(arg: Complex): Complex = PI_DIV_2 + i * ln(sqrt(one - arg pow 2) + i * arg) + override fun asin(arg: Complex): Complex = -i * ln(sqrt(1 - (arg pow 2)) + i * arg) + override fun acos(arg: Complex): Complex = PI_DIV_2 + i * ln(sqrt(1 - (arg pow 2)) + i * arg) override fun atan(arg: Complex): Complex { val iArg = i * arg - return i * (ln(one - iArg) - ln(one + iArg)) / 2 + return i * (ln(1 - iArg) - ln(1 + iArg)) / 2 } override fun sinh(arg: Complex): Complex = (exp(arg) - exp(-arg)) / 2 override fun cosh(arg: Complex): Complex = (exp(arg) + exp(-arg)) / 2 override fun tanh(arg: Complex): Complex = (exp(arg) - exp(-arg)) / (exp(-arg) + exp(arg)) - override fun asinh(arg: Complex): Complex = ln(sqrt(arg pow 2) + arg) + override fun asinh(arg: Complex): Complex = ln(sqrt((arg pow 2) + 1) + arg) override fun acosh(arg: Complex): Complex = ln(arg + sqrt((arg - 1) * (arg + 1))) override fun atanh(arg: Complex): Complex = (ln(arg + 1) - ln(1 - arg)) / 2 - - override fun power(arg: Complex, pow: Number): Complex = - arg.r.pow(pow.toDouble()) * (cos(pow.toDouble() * arg.theta) + i * sin(pow.toDouble() * arg.theta)) - + override fun power(arg: Complex, pow: Number): Complex = exp(ln(arg) * pow) override fun exp(arg: Complex): Complex = exp(arg.re) * (cos(arg.im) + i * sin(arg.im)) override fun ln(arg: Complex): Complex = ln(arg.r) + i * atan2(arg.im, arg.re) diff --git a/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/ComplexTest.kt b/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/ComplexFieldTest.kt similarity index 62% rename from kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/ComplexTest.kt rename to kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/ComplexFieldTest.kt index ad85fa9aa..2986efd5f 100644 --- a/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/ComplexTest.kt +++ b/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/ComplexFieldTest.kt @@ -37,13 +37,32 @@ internal class ComplexFieldTest { @Test fun testSine() { - assertEquals(Complex(1.2246467991473532E-16, 0), ComplexField { sin(PI.toComplex()) }) - assertEquals(Complex(0, 11.548739357257748), ComplexField { sin(i * PI.toComplex()) }) - assertEquals(Complex(0, 1.1752011936438014), ComplexField { sin(i) }) + assertEquals(ComplexField { i * sinh(one) }, ComplexField { sin(i) }) + assertEquals(ComplexField { i * sinh(PI.toComplex()) }, ComplexField { sin(i * PI.toComplex()) }) } @Test - fun testArcsine() { + fun testInverseSine() { assertEquals(Complex(0, -0.0), ComplexField { asin(zero) }) + + assertEquals(ComplexField { i * asinh(one) }.let { it.im.toInt() to it.re.toInt() }, + ComplexField { asin(i) }.let { it.im.toInt() to it.re.toInt() }) + } + + @Test + fun testInverseHyperbolicSine() { + assertEquals( + ComplexField { i * PI.toComplex() / 2 }.let { it.im.toInt() to it.re.toInt() }, + ComplexField { asinh(i) }.let { it.im.toInt() to it.re.toInt() }) + } + + @Test + fun testPower() { + assertEquals(ComplexField.zero, ComplexField { zero pow 2 }) + assertEquals(ComplexField.zero, ComplexField { zero pow 2 }) + + assertEquals( + ComplexField { i * 8 }.let { it.im.toInt() to it.re.toInt() }, + ComplexField { Complex(2, 2) pow 2 }.let { it.im.toInt() to it.re.toInt() }) } } From 77625cca2b8b807e39fb7e8853e4774149c90e81 Mon Sep 17 00:00:00 2001 From: Commander Tvis Date: Fri, 3 Jul 2020 17:38:13 +0700 Subject: [PATCH 07/54] Update typealiases usages; add optimization for exponentiation real complex numbers --- .../scientifik/kmath/operations/Complex.kt | 11 +++++-- .../kmath/structures/ComplexNDField.kt | 30 +++++++++---------- 2 files changed, 23 insertions(+), 18 deletions(-) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Complex.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Complex.kt index 3dd6f5157..e45dcb1ea 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Complex.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Complex.kt @@ -104,7 +104,12 @@ object ComplexField : ExtendedField { override fun asinh(arg: Complex): Complex = ln(sqrt((arg pow 2) + 1) + arg) override fun acosh(arg: Complex): Complex = ln(arg + sqrt((arg - 1) * (arg + 1))) override fun atanh(arg: Complex): Complex = (ln(arg + 1) - ln(1 - arg)) / 2 - override fun power(arg: Complex, pow: Number): Complex = exp(ln(arg) * pow) + + override fun power(arg: Complex, pow: Number): Complex = if (arg.im == 0.0) + arg.re.pow(pow.toDouble()).toComplex() + else + exp(pow * ln(arg)) + override fun exp(arg: Complex): Complex = exp(arg.re) * (cos(arg.im) + i * sin(arg.im)) override fun ln(arg: Complex): Complex = ln(arg.r) + i * atan2(arg.im, arg.re) @@ -145,9 +150,9 @@ data class Complex(val re: Double, val im: Double) : FieldElement Complex): Buffer = MemoryBuffer.create(Complex, size, init) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ComplexNDField.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ComplexNDField.kt index 85c997c13..f979707f4 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ComplexNDField.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ComplexNDField.kt @@ -70,25 +70,25 @@ class ComplexNDField(override val shape: IntArray) : override fun NDBuffer.toElement(): FieldElement, *, out BufferedNDField> = BufferedNDFieldElement(this@ComplexNDField, buffer) - override fun power(arg: NDBuffer, pow: Number): BufferedNDFieldElement = + override fun power(arg: NDBuffer, pow: Number): ComplexNDElement = map(arg) { power(it, pow) } - override fun exp(arg: NDBuffer): BufferedNDFieldElement = map(arg) { exp(it) } - override fun ln(arg: NDBuffer): BufferedNDFieldElement = map(arg) { ln(it) } + override fun exp(arg: NDBuffer): ComplexNDElement = map(arg) { exp(it) } + override fun ln(arg: NDBuffer): ComplexNDElement = map(arg) { ln(it) } - override fun sin(arg: NDBuffer): BufferedNDFieldElement = map(arg) { sin(it) } - override fun cos(arg: NDBuffer): BufferedNDFieldElement = map(arg) { cos(it) } - override fun tan(arg: NDBuffer): BufferedNDFieldElement = map(arg) { tan(it) } - override fun asin(arg: NDBuffer): BufferedNDFieldElement = map(arg) { asin(it) } - override fun acos(arg: NDBuffer): BufferedNDFieldElement = map(arg) { acos(it) } - override fun atan(arg: NDBuffer): BufferedNDFieldElement = map(arg) { atan(it) } + override fun sin(arg: NDBuffer): ComplexNDElement = map(arg) { sin(it) } + override fun cos(arg: NDBuffer): ComplexNDElement = map(arg) { cos(it) } + override fun tan(arg: NDBuffer): ComplexNDElement = map(arg) { tan(it) } + override fun asin(arg: NDBuffer): ComplexNDElement = map(arg) { asin(it) } + override fun acos(arg: NDBuffer): ComplexNDElement = map(arg) { acos(it) } + override fun atan(arg: NDBuffer): ComplexNDElement = map(arg) { atan(it) } - override fun sinh(arg: NDBuffer): BufferedNDFieldElement = map(arg) { sinh(it) } - override fun cosh(arg: NDBuffer): BufferedNDFieldElement = map(arg) { cosh(it) } - override fun tanh(arg: NDBuffer): BufferedNDFieldElement = map(arg) { tanh(it) } - override fun asinh(arg: NDBuffer): BufferedNDFieldElement = map(arg) { asinh(it) } - override fun acosh(arg: NDBuffer): BufferedNDFieldElement = map(arg) { acosh(it) } - override fun atanh(arg: NDBuffer): BufferedNDFieldElement = map(arg) { atanh(it) } + override fun sinh(arg: NDBuffer): ComplexNDElement = map(arg) { sinh(it) } + override fun cosh(arg: NDBuffer): ComplexNDElement = map(arg) { cosh(it) } + override fun tanh(arg: NDBuffer): ComplexNDElement = map(arg) { tanh(it) } + override fun asinh(arg: NDBuffer): ComplexNDElement = map(arg) { asinh(it) } + override fun acosh(arg: NDBuffer): ComplexNDElement = map(arg) { acosh(it) } + override fun atanh(arg: NDBuffer): ComplexNDElement = map(arg) { atanh(it) } } From 995a0f916be2c4989cc0df1897a71da84ce5c83c Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Fri, 3 Jul 2020 19:20:44 +0700 Subject: [PATCH 08/54] Replace x pow 2 with multiplication x by x because of precision --- .../kotlin/scientifik/kmath/operations/Complex.kt | 6 +++--- .../scientifik/kmath/operations/ComplexFieldTest.kt | 11 +++++------ 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Complex.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Complex.kt index e45dcb1ea..3e1f53ddc 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Complex.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Complex.kt @@ -90,8 +90,8 @@ object ComplexField : ExtendedField { return i * (e1 - e2) / (e1 + e2) } - override fun asin(arg: Complex): Complex = -i * ln(sqrt(1 - (arg pow 2)) + i * arg) - override fun acos(arg: Complex): Complex = PI_DIV_2 + i * ln(sqrt(1 - (arg pow 2)) + i * arg) + override fun asin(arg: Complex): Complex = -i * ln(sqrt(1 - (arg * arg)) + i * arg) + override fun acos(arg: Complex): Complex = PI_DIV_2 + i * ln(sqrt(1 - (arg * arg)) + i * arg) override fun atan(arg: Complex): Complex { val iArg = i * arg @@ -101,7 +101,7 @@ object ComplexField : ExtendedField { override fun sinh(arg: Complex): Complex = (exp(arg) - exp(-arg)) / 2 override fun cosh(arg: Complex): Complex = (exp(arg) + exp(-arg)) / 2 override fun tanh(arg: Complex): Complex = (exp(arg) - exp(-arg)) / (exp(-arg) + exp(arg)) - override fun asinh(arg: Complex): Complex = ln(sqrt((arg pow 2) + 1) + arg) + override fun asinh(arg: Complex): Complex = ln(sqrt(arg * arg + 1) + arg) override fun acosh(arg: Complex): Complex = ln(arg + sqrt((arg - 1) * (arg + 1))) override fun atanh(arg: Complex): Complex = (ln(arg + 1) - ln(1 - arg)) / 2 diff --git a/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/ComplexFieldTest.kt b/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/ComplexFieldTest.kt index 2986efd5f..1091a8de1 100644 --- a/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/ComplexFieldTest.kt +++ b/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/ComplexFieldTest.kt @@ -1,8 +1,9 @@ package scientifik.kmath.operations -import kotlin.math.PI +import kotlin.math.* import kotlin.test.Test import kotlin.test.assertEquals +import kotlin.test.assertTrue internal class ComplexFieldTest { @Test @@ -44,16 +45,14 @@ internal class ComplexFieldTest { @Test fun testInverseSine() { assertEquals(Complex(0, -0.0), ComplexField { asin(zero) }) - - assertEquals(ComplexField { i * asinh(one) }.let { it.im.toInt() to it.re.toInt() }, - ComplexField { asin(i) }.let { it.im.toInt() to it.re.toInt() }) + assertTrue(abs(ComplexField { i * asinh(one) }.r - ComplexField { asin(i) }.r) < 0.000000000000001) } @Test fun testInverseHyperbolicSine() { assertEquals( - ComplexField { i * PI.toComplex() / 2 }.let { it.im.toInt() to it.re.toInt() }, - ComplexField { asinh(i) }.let { it.im.toInt() to it.re.toInt() }) + ComplexField { i * PI.toComplex() / 2 }, + ComplexField { asinh(i) }) } @Test From bce2a8460e22e85b7a9d38f2c8d40da475e1983b Mon Sep 17 00:00:00 2001 From: Iaroslav Postovalov Date: Mon, 20 Jul 2020 20:40:03 +0700 Subject: [PATCH 09/54] Add reference ANTLR grammar, implement missing rules and operations in parser, also add support for symbols in ASM --- kmath-ast/reference/ArithmeticsEvaluator.g4 | 59 +++++++++++++++ .../kotlin/scientifik/kmath/ast/MST.kt | 38 +++++----- .../kotlin/scientifik/kmath/ast/parser.kt | 71 ++++++++++++------- .../kotlin/scientifik/kmath/asm/asm.kt | 15 +++- .../kmath/asm/internal/AsmBuilder.kt | 2 +- .../kotlin/scietifik/kmath/ast/ParserTest.kt | 35 +++++++++ 6 files changed, 173 insertions(+), 47 deletions(-) create mode 100644 kmath-ast/reference/ArithmeticsEvaluator.g4 diff --git a/kmath-ast/reference/ArithmeticsEvaluator.g4 b/kmath-ast/reference/ArithmeticsEvaluator.g4 new file mode 100644 index 000000000..684a9de44 --- /dev/null +++ b/kmath-ast/reference/ArithmeticsEvaluator.g4 @@ -0,0 +1,59 @@ +grammar ArithmeticsEvaluator; + +fragment DIGIT: '0'..'9'; +fragment LETTER: 'a'..'z'; +fragment UNDERSCORE: '_'; + +ID: (LETTER | UNDERSCORE) (LETTER | UNDERSCORE | DIGIT)*; +NUM: (DIGIT | '.')+ ([eE] MINUS? DIGIT+)?; +MUL: '*'; +DIV: '/'; +PLUS: '+'; +MINUS: '-'; +POW: '^'; +COMMA: ','; +LPAR: '('; +RPAR: ')'; +WS: [ \n\t\r]+ -> skip; + +number + : NUM + ; + +singular + : ID + ; + +unaryFunction + : ID LPAR subSumChain RPAR + ; + +binaryFunction + : ID LPAR subSumChain COMMA subSumChain RPAR + ; + +term + : number + | singular + | unaryFunction + | binaryFunction + | MINUS term + | LPAR subSumChain RPAR + ; + +powChain + : term (POW term)* + ; + + +divMulChain + : powChain ((PLUS | MINUS) powChain)* + ; + +subSumChain + : divMulChain ((DIV | MUL) divMulChain)* + ; + +rootParser + : subSumChain EOF + ; diff --git a/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MST.kt b/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MST.kt index 142d27f93..e87df882f 100644 --- a/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MST.kt +++ b/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MST.kt @@ -41,27 +41,25 @@ sealed class MST { //TODO add a function with named arguments -fun Algebra.evaluate(node: MST): T { - return when (node) { - is MST.Numeric -> (this as? NumericAlgebra)?.number(node.value) - ?: error("Numeric nodes are not supported by $this") - is MST.Symbolic -> symbol(node.value) - is MST.Unary -> unaryOperation(node.operation, evaluate(node.value)) - is MST.Binary -> when { - this !is NumericAlgebra -> binaryOperation(node.operation, evaluate(node.left), evaluate(node.right)) - node.left is MST.Numeric && node.right is MST.Numeric -> { - val number = RealField.binaryOperation( - node.operation, - node.left.value.toDouble(), - node.right.value.toDouble() - ) - number(number) - } - node.left is MST.Numeric -> leftSideNumberOperation(node.operation, node.left.value, evaluate(node.right)) - node.right is MST.Numeric -> rightSideNumberOperation(node.operation, evaluate(node.left), node.right.value) - else -> binaryOperation(node.operation, evaluate(node.left), evaluate(node.right)) +fun Algebra.evaluate(node: MST): T = when (node) { + is MST.Numeric -> (this as? NumericAlgebra)?.number(node.value) + ?: error("Numeric nodes are not supported by $this") + is MST.Symbolic -> symbol(node.value) + is MST.Unary -> unaryOperation(node.operation, evaluate(node.value)) + is MST.Binary -> when { + this !is NumericAlgebra -> binaryOperation(node.operation, evaluate(node.left), evaluate(node.right)) + node.left is MST.Numeric && node.right is MST.Numeric -> { + val number = RealField.binaryOperation( + node.operation, + node.left.value.toDouble(), + node.right.value.toDouble() + ) + number(number) } + node.left is MST.Numeric -> leftSideNumberOperation(node.operation, node.left.value, evaluate(node.right)) + node.right is MST.Numeric -> rightSideNumberOperation(node.operation, evaluate(node.left), node.right.value) + else -> binaryOperation(node.operation, evaluate(node.left), evaluate(node.right)) } } -fun MST.compile(algebra: Algebra): T = algebra.evaluate(this) \ No newline at end of file +fun MST.compile(algebra: Algebra): T = algebra.evaluate(this) diff --git a/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/parser.kt b/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/parser.kt index 30a92c5ae..ae0b6656d 100644 --- a/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/parser.kt +++ b/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/parser.kt @@ -5,6 +5,8 @@ import com.github.h0tk3y.betterParse.grammar.Grammar import com.github.h0tk3y.betterParse.grammar.parseToEnd import com.github.h0tk3y.betterParse.grammar.parser import com.github.h0tk3y.betterParse.grammar.tryParseToEnd +import com.github.h0tk3y.betterParse.lexer.Token +import com.github.h0tk3y.betterParse.lexer.TokenMatch import com.github.h0tk3y.betterParse.parser.ParseResult import com.github.h0tk3y.betterParse.parser.Parser import scientifik.kmath.operations.FieldOperations @@ -15,45 +17,66 @@ import scientifik.kmath.operations.SpaceOperations /** * TODO move to common */ -private object ArithmeticsEvaluator : Grammar() { - val num by token("-?[\\d.]+(?:[eE]-?\\d+)?".toRegex()) - val lpar by token("\\(".toRegex()) - val rpar by token("\\)".toRegex()) - val mul by token("\\*".toRegex()) - val pow by token("\\^".toRegex()) - val div by token("/".toRegex()) - val minus by token("-".toRegex()) - val plus by token("\\+".toRegex()) - val ws by token("\\s+".toRegex(), ignore = true) +object ArithmeticsEvaluator : Grammar() { + private val num: Token by token("[\\d.]+(?:[eE]-?\\d+)?".toRegex()) + private val id: Token by token("[a-z_][\\da-z_]*".toRegex()) + private val lpar: Token by token("\\(".toRegex()) + private val rpar: Token by token("\\)".toRegex()) + private val comma: Token by token(",".toRegex()) + private val mul: Token by token("\\*".toRegex()) + private val pow: Token by token("\\^".toRegex()) + private val div: Token by token("/".toRegex()) + private val minus: Token by token("-".toRegex()) + private val plus: Token by token("\\+".toRegex()) + private val ws: Token by token("\\s+".toRegex(), ignore = true) - val number: Parser by num use { MST.Numeric(text.toDouble()) } + private val number: Parser by num use { MST.Numeric(text.toDouble()) } + private val singular: Parser by id use { MST.Symbolic(text) } - val term: Parser by number or - (skip(minus) and parser(this::term) map { MST.Unary(SpaceOperations.MINUS_OPERATION, it) }) or - (skip(lpar) and parser(this::rootParser) and skip(rpar)) + private val unaryFunction: Parser by (id and skip(lpar) and parser(::subSumChain) and skip(rpar)) + .map { (id, term) -> MST.Unary(id.text, term) } - val powChain by leftAssociative(term, pow) { a, _, b -> + private val binaryFunction: Parser by id + .and(skip(lpar)) + .and(parser(::subSumChain)) + .and(skip(comma)) + .and(parser(::subSumChain)) + .and(skip(rpar)) + .map { (id, left, right) -> MST.Binary(id.text, left, right) } + + private val term: Parser by number + .or(binaryFunction) + .or(unaryFunction) + .or(singular) + .or(skip(minus) and parser(::term) map { MST.Unary(SpaceOperations.MINUS_OPERATION, it) }) + .or(skip(lpar) and parser(::subSumChain) and skip(rpar)) + + private val powChain: Parser by leftAssociative(term = term, operator = pow) { a, _, b -> MST.Binary(PowerOperations.POW_OPERATION, a, b) } - val divMulChain: Parser by leftAssociative(powChain, div or mul use { type }) { a, op, b -> - if (op == div) { + private val divMulChain: Parser by leftAssociative( + term = powChain, + operator = div or mul use TokenMatch::type + ) { a, op, b -> + if (op == div) MST.Binary(FieldOperations.DIV_OPERATION, a, b) - } else { + else MST.Binary(RingOperations.TIMES_OPERATION, a, b) - } } - val subSumChain: Parser by leftAssociative(divMulChain, plus or minus use { type }) { a, op, b -> - if (op == plus) { + private val subSumChain: Parser by leftAssociative( + term = divMulChain, + operator = plus or minus use TokenMatch::type + ) { a, op, b -> + if (op == plus) MST.Binary(SpaceOperations.PLUS_OPERATION, a, b) - } else { + else MST.Binary(SpaceOperations.MINUS_OPERATION, a, b) - } } override val rootParser: Parser by subSumChain } fun String.tryParseMath(): ParseResult = ArithmeticsEvaluator.tryParseToEnd(this) -fun String.parseMath(): MST = ArithmeticsEvaluator.parseToEnd(this) \ No newline at end of file +fun String.parseMath(): MST = ArithmeticsEvaluator.parseToEnd(this) diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/asm.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/asm.kt index f0022a43a..ee0ea15ff 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/asm.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/asm.kt @@ -8,7 +8,6 @@ import scientifik.kmath.ast.MST import scientifik.kmath.ast.MstExpression import scientifik.kmath.expressions.Expression import scientifik.kmath.operations.Algebra -import scientifik.kmath.operations.NumericAlgebra import kotlin.reflect.KClass /** @@ -17,7 +16,19 @@ import kotlin.reflect.KClass fun MST.compileWith(type: KClass, algebra: Algebra): Expression { fun AsmBuilder.visit(node: MST) { when (node) { - is MST.Symbolic -> loadVariable(node.value) + is MST.Symbolic -> { + val symbol = try { + algebra.symbol(node.value) + } catch (ignored: Throwable) { + null + } + + if (symbol != null) + loadTConstant(symbol) + else + loadVariable(node.value) + } + is MST.Numeric -> loadNumeric(node.value) is MST.Unary -> buildAlgebraOperationCall( diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt index 5531fd5dc..a947b3478 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt @@ -288,7 +288,7 @@ internal class AsmBuilder internal constructor( /** * Loads a [T] constant from [constants]. */ - private fun loadTConstant(value: T) { + internal fun loadTConstant(value: T) { if (classOfT in INLINABLE_NUMBERS) { val expectedType = expectationStack.pop() val mustBeBoxed = expectedType.sort == Type.OBJECT diff --git a/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/ParserTest.kt b/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/ParserTest.kt index 5394a4b00..9179c3428 100644 --- a/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/ParserTest.kt +++ b/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/ParserTest.kt @@ -4,8 +4,10 @@ import scientifik.kmath.ast.evaluate import scientifik.kmath.ast.mstInField import scientifik.kmath.ast.parseMath import scientifik.kmath.expressions.invoke +import scientifik.kmath.operations.Algebra import scientifik.kmath.operations.Complex import scientifik.kmath.operations.ComplexField +import scientifik.kmath.operations.RealField import kotlin.test.Test import kotlin.test.assertEquals @@ -22,4 +24,37 @@ internal class ParserTest { val res = ComplexField.mstInField { number(2) + number(2) * (number(2) + number(2)) }() assertEquals(Complex(10.0, 0.0), res) } + + @Test + fun `evaluate MST with singular`() { + val mst = "i".parseMath() + val res = ComplexField.evaluate(mst) + assertEquals(ComplexField.i, res) + } + + + @Test + fun `evaluate MST with unary function`() { + val mst = "sin(0)".parseMath() + val res = RealField.evaluate(mst) + assertEquals(0.0, res) + } + + @Test + fun `evaluate MST with binary function`() { + val magicalAlgebra = object : Algebra { + override fun symbol(value: String): String = value + + override fun unaryOperation(operation: String, arg: String): String = throw NotImplementedError() + + override fun binaryOperation(operation: String, left: String, right: String): String = when (operation) { + "magic" -> "$left ★ $right" + else -> throw NotImplementedError() + } + } + + val mst = "magic(a, b)".parseMath() + val res = magicalAlgebra.evaluate(mst) + assertEquals("a ★ b", res) + } } From 2864b2d12e4ad7100b2978a5f4378fd4cb5b846b Mon Sep 17 00:00:00 2001 From: Iaroslav Postovalov Date: Sun, 26 Jul 2020 11:17:47 +0700 Subject: [PATCH 10/54] Move https://dl.bintray.com/hotkeytlt/maven to another RepositoryHandler --- build.gradle.kts | 1 + kmath-ast/build.gradle.kts | 12 ++---------- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 052b457c5..8a2ba3617 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -11,6 +11,7 @@ allprojects { repositories { jcenter() maven("https://dl.bintray.com/kotlin/kotlinx") + maven("https://dl.bintray.com/hotkeytlt/maven") } group = "scientifik" diff --git a/kmath-ast/build.gradle.kts b/kmath-ast/build.gradle.kts index 511571fc9..4f67d57eb 100644 --- a/kmath-ast/build.gradle.kts +++ b/kmath-ast/build.gradle.kts @@ -1,10 +1,4 @@ -plugins { - id("scientifik.mpp") -} - -repositories { - maven("https://dl.bintray.com/hotkeytlt/maven") -} +plugins { id("scientifik.mpp") } kotlin.sourceSets { // all { @@ -30,8 +24,6 @@ kotlin.sourceSets { } jsMain { - dependencies { - implementation("com.github.h0tk3y.betterParse:better-parse-js:0.4.0-alpha-3") - } + dependencies { implementation("com.github.h0tk3y.betterParse:better-parse-js:0.4.0-alpha-3") } } } \ No newline at end of file From 186575d8b335128eabbe701bbcaf237b883f9c8d Mon Sep 17 00:00:00 2001 From: Iaroslav Postovalov Date: Sun, 26 Jul 2020 11:28:19 +0700 Subject: [PATCH 11/54] Override number function for JBigIntegerField and JBigDecimalField --- .../kotlin/scientifik/kmath/operations/BigNumbers.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/kmath-core/src/jvmMain/kotlin/scientifik/kmath/operations/BigNumbers.kt b/kmath-core/src/jvmMain/kotlin/scientifik/kmath/operations/BigNumbers.kt index 48d3c8c59..eb268bb5e 100644 --- a/kmath-core/src/jvmMain/kotlin/scientifik/kmath/operations/BigNumbers.kt +++ b/kmath-core/src/jvmMain/kotlin/scientifik/kmath/operations/BigNumbers.kt @@ -14,6 +14,7 @@ object JBigIntegerField : Field { override val one: BigInteger get() = BigInteger.ONE + override fun number(value: Number): BigInteger = BigInteger.valueOf(value.toLong()) override fun divide(a: BigInteger, b: BigInteger): BigInteger = a.div(b) override fun add(a: BigInteger, b: BigInteger): BigInteger = a.add(b) override fun BigInteger.minus(b: BigInteger): BigInteger = this.subtract(b) @@ -35,9 +36,8 @@ abstract class JBigDecimalFieldBase internal constructor(val mathContext: MathCo get() = BigDecimal.ONE override fun add(a: BigDecimal, b: BigDecimal): BigDecimal = a.add(b) - override fun BigDecimal.minus(b: BigDecimal): BigDecimal { - return subtract(b) - } + override fun BigDecimal.minus(b: BigDecimal): BigDecimal = subtract(b) + override fun number(value: Number): BigDecimal = BigDecimal.valueOf(value.toDouble()) override fun multiply(a: BigDecimal, k: Number): BigDecimal = a.multiply(k.toDouble().toBigDecimal(mathContext), mathContext) From 10e3c4965125f3238f0335e9c32d2d074d533802 Mon Sep 17 00:00:00 2001 From: breandan Date: Sun, 26 Jul 2020 17:03:22 -0400 Subject: [PATCH 12/54] fix bintray-dev link URL --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 08600113d..a081ceedd 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ Bintray: [ ![Download](https://api.bintray.com/packages/mipt-npm/scientifik/kmath-core/images/download.svg) ](https://bintray.com/mipt-npm/scientifik/kmath-core/_latestVersion) -Bintray-dev: [ ![Download](https://api.bintray.com/packages/mipt-npm/dev/kmath-core/images/download.svg) ](https://bintray.com/mipt-npm/scientifik/kmath-core/_latestVersion) +Bintray-dev: [ ![Download](https://api.bintray.com/packages/mipt-npm/dev/kmath-core/images/download.svg) ](https://bintray.com/mipt-npm/dev/kmath-core/_latestVersion) # KMath Could be pronounced as `key-math`. From 45087f8b2de6b615ea627400b8dd12edc8e0918d Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Mon, 27 Jul 2020 15:37:18 +0700 Subject: [PATCH 13/54] Add capital latin letters support --- kmath-ast/reference/ArithmeticsEvaluator.g4 | 3 ++- kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/parser.kt | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/kmath-ast/reference/ArithmeticsEvaluator.g4 b/kmath-ast/reference/ArithmeticsEvaluator.g4 index 684a9de44..137d42860 100644 --- a/kmath-ast/reference/ArithmeticsEvaluator.g4 +++ b/kmath-ast/reference/ArithmeticsEvaluator.g4 @@ -2,9 +2,10 @@ grammar ArithmeticsEvaluator; fragment DIGIT: '0'..'9'; fragment LETTER: 'a'..'z'; +fragment CAPITAL_LETTER: 'A'..'Z' fragment UNDERSCORE: '_'; -ID: (LETTER | UNDERSCORE) (LETTER | UNDERSCORE | DIGIT)*; +ID: (LETTER | UNDERSCORE | CAPITAL_LETTER) (LETTER | UNDERSCORE | DIGIT | CAPITAL_LETTER)*; NUM: (DIGIT | '.')+ ([eE] MINUS? DIGIT+)?; MUL: '*'; DIV: '/'; diff --git a/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/parser.kt b/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/parser.kt index ae0b6656d..27f133d6a 100644 --- a/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/parser.kt +++ b/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/parser.kt @@ -19,7 +19,7 @@ import scientifik.kmath.operations.SpaceOperations */ object ArithmeticsEvaluator : Grammar() { private val num: Token by token("[\\d.]+(?:[eE]-?\\d+)?".toRegex()) - private val id: Token by token("[a-z_][\\da-z_]*".toRegex()) + private val id: Token by token("[a-z_A-Z][\\da-z_A-Z]*".toRegex()) private val lpar: Token by token("\\(".toRegex()) private val rpar: Token by token("\\)".toRegex()) private val comma: Token by token(",".toRegex()) From a5499260fae7e412da6ca6fcd9769382d7d9357f Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Mon, 27 Jul 2020 15:45:05 +0700 Subject: [PATCH 14/54] Update num rule to match 1e+1 like numbers --- kmath-ast/reference/ArithmeticsEvaluator.g4 | 8 ++++---- .../src/commonMain/kotlin/scientifik/kmath/ast/parser.kt | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/kmath-ast/reference/ArithmeticsEvaluator.g4 b/kmath-ast/reference/ArithmeticsEvaluator.g4 index 137d42860..ba8166bc8 100644 --- a/kmath-ast/reference/ArithmeticsEvaluator.g4 +++ b/kmath-ast/reference/ArithmeticsEvaluator.g4 @@ -2,11 +2,11 @@ grammar ArithmeticsEvaluator; fragment DIGIT: '0'..'9'; fragment LETTER: 'a'..'z'; -fragment CAPITAL_LETTER: 'A'..'Z' +fragment CAPITAL_LETTER: 'A'..'Z'; fragment UNDERSCORE: '_'; ID: (LETTER | UNDERSCORE | CAPITAL_LETTER) (LETTER | UNDERSCORE | DIGIT | CAPITAL_LETTER)*; -NUM: (DIGIT | '.')+ ([eE] MINUS? DIGIT+)?; +NUM: (DIGIT | '.')+ ([eE] (MINUS? | PLUS?) DIGIT+)?; MUL: '*'; DIV: '/'; PLUS: '+'; @@ -17,7 +17,7 @@ LPAR: '('; RPAR: ')'; WS: [ \n\t\r]+ -> skip; -number +num : NUM ; @@ -34,7 +34,7 @@ binaryFunction ; term - : number + : num | singular | unaryFunction | binaryFunction diff --git a/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/parser.kt b/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/parser.kt index 27f133d6a..a0b729ffe 100644 --- a/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/parser.kt +++ b/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/parser.kt @@ -18,7 +18,7 @@ import scientifik.kmath.operations.SpaceOperations * TODO move to common */ object ArithmeticsEvaluator : Grammar() { - private val num: Token by token("[\\d.]+(?:[eE]-?\\d+)?".toRegex()) + private val num: Token by token("[\\d.]+(?:[eE][-+]?\\d+)?".toRegex()) private val id: Token by token("[a-z_A-Z][\\da-z_A-Z]*".toRegex()) private val lpar: Token by token("\\(".toRegex()) private val rpar: Token by token("\\)".toRegex()) From f8383deb708e0b88a89ff76ddb0021c1facf5464 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Mon, 27 Jul 2020 15:49:36 +0700 Subject: [PATCH 15/54] Fix operators in reference --- kmath-ast/reference/ArithmeticsEvaluator.g4 | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/kmath-ast/reference/ArithmeticsEvaluator.g4 b/kmath-ast/reference/ArithmeticsEvaluator.g4 index ba8166bc8..9835ec7f7 100644 --- a/kmath-ast/reference/ArithmeticsEvaluator.g4 +++ b/kmath-ast/reference/ArithmeticsEvaluator.g4 @@ -46,13 +46,12 @@ powChain : term (POW term)* ; - divMulChain - : powChain ((PLUS | MINUS) powChain)* + : powChain ((DIV | MUL) powChain)* ; subSumChain - : divMulChain ((DIV | MUL) divMulChain)* + : divMulChain ((PLUS | MINUS) divMulChain)* ; rootParser From 1ebd3626969e07b3ee16fe4f77a03b2315563fcd Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Mon, 27 Jul 2020 15:58:09 +0700 Subject: [PATCH 16/54] Update num token in reference --- kmath-ast/reference/ArithmeticsEvaluator.g4 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kmath-ast/reference/ArithmeticsEvaluator.g4 b/kmath-ast/reference/ArithmeticsEvaluator.g4 index 9835ec7f7..dc47b23fb 100644 --- a/kmath-ast/reference/ArithmeticsEvaluator.g4 +++ b/kmath-ast/reference/ArithmeticsEvaluator.g4 @@ -6,7 +6,7 @@ fragment CAPITAL_LETTER: 'A'..'Z'; fragment UNDERSCORE: '_'; ID: (LETTER | UNDERSCORE | CAPITAL_LETTER) (LETTER | UNDERSCORE | DIGIT | CAPITAL_LETTER)*; -NUM: (DIGIT | '.')+ ([eE] (MINUS? | PLUS?) DIGIT+)?; +NUM: (DIGIT | '.')+ ([eE] [-+]? DIGIT+)?; MUL: '*'; DIV: '/'; PLUS: '+'; From c64a89c6b6296517d7c7b9690e7ae319ae54fcd8 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Mon, 27 Jul 2020 19:27:59 +0700 Subject: [PATCH 17/54] Implement power as binary operation with unchecked cast, add tests on parser precedence --- .../kotlin/scientifik/kmath/ast/MST.kt | 8 ++--- .../kmath/ast/ParserPrecedenceTest.kt | 36 +++++++++++++++++++ .../kmath/operations/NumberAlgebra.kt | 11 ++++-- 3 files changed, 48 insertions(+), 7 deletions(-) create mode 100644 kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/ParserPrecedenceTest.kt diff --git a/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MST.kt b/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MST.kt index e87df882f..1127c18aa 100644 --- a/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MST.kt +++ b/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MST.kt @@ -2,7 +2,6 @@ package scientifik.kmath.ast import scientifik.kmath.operations.Algebra import scientifik.kmath.operations.NumericAlgebra -import scientifik.kmath.operations.RealField /** * A Mathematical Syntax Tree node for mathematical expressions @@ -49,12 +48,11 @@ fun Algebra.evaluate(node: MST): T = when (node) { is MST.Binary -> when { this !is NumericAlgebra -> binaryOperation(node.operation, evaluate(node.left), evaluate(node.right)) node.left is MST.Numeric && node.right is MST.Numeric -> { - val number = RealField.binaryOperation( + binaryOperation( node.operation, - node.left.value.toDouble(), - node.right.value.toDouble() + number(node.left.value), + number(node.right.value) ) - number(number) } node.left is MST.Numeric -> leftSideNumberOperation(node.operation, node.left.value, evaluate(node.right)) node.right is MST.Numeric -> rightSideNumberOperation(node.operation, evaluate(node.left), node.right.value) diff --git a/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/ParserPrecedenceTest.kt b/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/ParserPrecedenceTest.kt new file mode 100644 index 000000000..9bdbb12c9 --- /dev/null +++ b/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/ParserPrecedenceTest.kt @@ -0,0 +1,36 @@ +package scietifik.kmath.ast + +import scientifik.kmath.ast.evaluate +import scientifik.kmath.ast.parseMath +import scientifik.kmath.operations.Field +import scientifik.kmath.operations.RealField +import kotlin.test.Test +import kotlin.test.assertEquals + +internal class ParserPrecedenceTest { + private val f: Field = RealField + + @Test + fun test1(): Unit = assertEquals(6.0, f.evaluate("2*2+2".parseMath())) + + @Test + fun test2(): Unit = assertEquals(6.0, f.evaluate("2+2*2".parseMath())) + + @Test + fun test3(): Unit = assertEquals(10.0, f.evaluate("2^3+2".parseMath())) + + @Test + fun test4(): Unit = assertEquals(10.0, f.evaluate("2+2^3".parseMath())) + + @Test + fun test5(): Unit = assertEquals(16.0, f.evaluate("2^3*2".parseMath())) + + @Test + fun test6(): Unit = assertEquals(16.0, f.evaluate("2*2^3".parseMath())) + + @Test + fun test7(): Unit = assertEquals(18.0, f.evaluate("2+2^3*2".parseMath())) + + @Test + fun test8(): Unit = assertEquals(18.0, f.evaluate("2*2^3+2".parseMath())) +} diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/NumberAlgebra.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/NumberAlgebra.kt index 953c5a112..ca5fa68f0 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/NumberAlgebra.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/NumberAlgebra.kt @@ -34,6 +34,13 @@ interface ExtendedField : ExtendedFieldOperations, Field { } } +interface NumberExtendedField : ExtendedField { + override fun binaryOperation(operation: String, left: T, right: T): T = when (operation) { + PowerOperations.POW_OPERATION -> power(left, right as Number) + else -> super.binaryOperation(operation, left, right) + } +} + /** * Real field element wrapping double. * @@ -53,7 +60,7 @@ inline class Real(val value: Double) : FieldElement { * A field for double without boxing. Does not produce appropriate field element */ @Suppress("EXTENSION_SHADOWED_BY_MEMBER", "OVERRIDE_BY_INLINE", "NOTHING_TO_INLINE") -object RealField : ExtendedField, Norm { +object RealField : NumberExtendedField, Norm { override val zero: Double = 0.0 override inline fun add(a: Double, b: Double) = a + b override inline fun multiply(a: Double, b: Double) = a * b @@ -88,7 +95,7 @@ object RealField : ExtendedField, Norm { } @Suppress("EXTENSION_SHADOWED_BY_MEMBER", "OVERRIDE_BY_INLINE", "NOTHING_TO_INLINE") -object FloatField : ExtendedField, Norm { +object FloatField : NumberExtendedField, Norm { override val zero: Float = 0f override inline fun add(a: Float, b: Float) = a + b override inline fun multiply(a: Float, b: Float) = a * b From 0995dca8b857d07d5739dc34e8b75e8df78703f6 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Mon, 27 Jul 2020 20:46:57 +0700 Subject: [PATCH 18/54] Delete unchecked cast, revert evaluate changes, add RealField handling "pow" operation --- .../kotlin/scientifik/kmath/ast/MST.kt | 11 ++++++++--- .../kmath/operations/NumberAlgebra.kt | 17 ++++++++--------- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MST.kt b/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MST.kt index 1127c18aa..46558cbfb 100644 --- a/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MST.kt +++ b/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MST.kt @@ -2,6 +2,7 @@ package scientifik.kmath.ast import scientifik.kmath.operations.Algebra import scientifik.kmath.operations.NumericAlgebra +import scientifik.kmath.operations.RealField /** * A Mathematical Syntax Tree node for mathematical expressions @@ -47,13 +48,17 @@ fun Algebra.evaluate(node: MST): T = when (node) { is MST.Unary -> unaryOperation(node.operation, evaluate(node.value)) is MST.Binary -> when { this !is NumericAlgebra -> binaryOperation(node.operation, evaluate(node.left), evaluate(node.right)) + node.left is MST.Numeric && node.right is MST.Numeric -> { - binaryOperation( + val number = RealField.binaryOperation( node.operation, - number(node.left.value), - number(node.right.value) + node.left.value.toDouble(), + node.right.value.toDouble() ) + + number(number) } + node.left is MST.Numeric -> leftSideNumberOperation(node.operation, node.left.value, evaluate(node.right)) node.right is MST.Numeric -> rightSideNumberOperation(node.operation, evaluate(node.left), node.right.value) else -> binaryOperation(node.operation, evaluate(node.left), evaluate(node.right)) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/NumberAlgebra.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/NumberAlgebra.kt index ca5fa68f0..2c57d0098 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/NumberAlgebra.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/NumberAlgebra.kt @@ -1,5 +1,6 @@ package scientifik.kmath.operations +import scientifik.kmath.operations.RealField.pow import kotlin.math.abs import kotlin.math.pow as kpow @@ -34,13 +35,6 @@ interface ExtendedField : ExtendedFieldOperations, Field { } } -interface NumberExtendedField : ExtendedField { - override fun binaryOperation(operation: String, left: T, right: T): T = when (operation) { - PowerOperations.POW_OPERATION -> power(left, right as Number) - else -> super.binaryOperation(operation, left, right) - } -} - /** * Real field element wrapping double. * @@ -60,7 +54,7 @@ inline class Real(val value: Double) : FieldElement { * A field for double without boxing. Does not produce appropriate field element */ @Suppress("EXTENSION_SHADOWED_BY_MEMBER", "OVERRIDE_BY_INLINE", "NOTHING_TO_INLINE") -object RealField : NumberExtendedField, Norm { +object RealField : ExtendedField, Norm { override val zero: Double = 0.0 override inline fun add(a: Double, b: Double) = a + b override inline fun multiply(a: Double, b: Double) = a * b @@ -92,10 +86,15 @@ object RealField : NumberExtendedField, Norm { override inline fun Double.times(b: Double) = this * b override inline fun Double.div(b: Double) = this / b + + override fun binaryOperation(operation: String, left: Double, right: Double): Double = when (operation) { + PowerOperations.POW_OPERATION -> left pow right + else -> super.binaryOperation(operation, left, right) + } } @Suppress("EXTENSION_SHADOWED_BY_MEMBER", "OVERRIDE_BY_INLINE", "NOTHING_TO_INLINE") -object FloatField : NumberExtendedField, Norm { +object FloatField : ExtendedField, Norm { override val zero: Float = 0f override inline fun add(a: Float, b: Float) = a + b override inline fun multiply(a: Float, b: Float) = a * b From 11c98d6acfaf955f632e24d9ab77a54d002aefec Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Mon, 27 Jul 2020 23:06:28 +0700 Subject: [PATCH 19/54] Upgrade better-parse to 0.4.0 --- kmath-ast/build.gradle.kts | 8 +----- .../kotlin/scientifik/kmath/ast/parser.kt | 26 ++++++++++--------- 2 files changed, 15 insertions(+), 19 deletions(-) diff --git a/kmath-ast/build.gradle.kts b/kmath-ast/build.gradle.kts index 4f67d57eb..d13a7712d 100644 --- a/kmath-ast/build.gradle.kts +++ b/kmath-ast/build.gradle.kts @@ -9,21 +9,15 @@ kotlin.sourceSets { commonMain { dependencies { api(project(":kmath-core")) - implementation("com.github.h0tk3y.betterParse:better-parse-multiplatform:0.4.0-alpha-3") - implementation("com.github.h0tk3y.betterParse:better-parse-multiplatform-metadata:0.4.0-alpha-3") + implementation("com.github.h0tk3y.betterParse:better-parse:0.4.0") } } jvmMain { dependencies { - implementation("com.github.h0tk3y.betterParse:better-parse-jvm:0.4.0-alpha-3") implementation("org.ow2.asm:asm:8.0.1") implementation("org.ow2.asm:asm-commons:8.0.1") implementation(kotlin("reflect")) } } - - jsMain { - dependencies { implementation("com.github.h0tk3y.betterParse:better-parse-js:0.4.0-alpha-3") } - } } \ No newline at end of file diff --git a/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/parser.kt b/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/parser.kt index a0b729ffe..11797d79f 100644 --- a/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/parser.kt +++ b/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/parser.kt @@ -7,6 +7,7 @@ import com.github.h0tk3y.betterParse.grammar.parser import com.github.h0tk3y.betterParse.grammar.tryParseToEnd import com.github.h0tk3y.betterParse.lexer.Token import com.github.h0tk3y.betterParse.lexer.TokenMatch +import com.github.h0tk3y.betterParse.lexer.regexToken import com.github.h0tk3y.betterParse.parser.ParseResult import com.github.h0tk3y.betterParse.parser.Parser import scientifik.kmath.operations.FieldOperations @@ -15,20 +16,21 @@ import scientifik.kmath.operations.RingOperations import scientifik.kmath.operations.SpaceOperations /** - * TODO move to common + * TODO move to core */ object ArithmeticsEvaluator : Grammar() { - private val num: Token by token("[\\d.]+(?:[eE][-+]?\\d+)?".toRegex()) - private val id: Token by token("[a-z_A-Z][\\da-z_A-Z]*".toRegex()) - private val lpar: Token by token("\\(".toRegex()) - private val rpar: Token by token("\\)".toRegex()) - private val comma: Token by token(",".toRegex()) - private val mul: Token by token("\\*".toRegex()) - private val pow: Token by token("\\^".toRegex()) - private val div: Token by token("/".toRegex()) - private val minus: Token by token("-".toRegex()) - private val plus: Token by token("\\+".toRegex()) - private val ws: Token by token("\\s+".toRegex(), ignore = true) + // TODO replace with "...".toRegex() when better-parse 0.4.1 is released + private val num: Token by regexToken("[\\d.]+(?:[eE][-+]?\\d+)?") + private val id: Token by regexToken("[a-z_A-Z][\\da-z_A-Z]*") + private val lpar: Token by regexToken("\\(") + private val rpar: Token by regexToken("\\)") + private val comma: Token by regexToken(",") + private val mul: Token by regexToken("\\*") + private val pow: Token by regexToken("\\^") + private val div: Token by regexToken("/") + private val minus: Token by regexToken("-") + private val plus: Token by regexToken("\\+") + private val ws: Token by regexToken("\\s+", ignore = true) private val number: Parser by num use { MST.Numeric(text.toDouble()) } private val singular: Parser by id use { MST.Symbolic(text) } From 87ff735a3119df44451dba2b52507b84b264521f Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Tue, 28 Jul 2020 09:02:03 +0300 Subject: [PATCH 20/54] Update Dimensions.kt Fix D3 typo. --- .../kotlin/scientifik/kmath/dimensions/Dimensions.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kmath-dimensions/src/commonMain/kotlin/scientifik/kmath/dimensions/Dimensions.kt b/kmath-dimensions/src/commonMain/kotlin/scientifik/kmath/dimensions/Dimensions.kt index 37e89c111..f40483cfd 100644 --- a/kmath-dimensions/src/commonMain/kotlin/scientifik/kmath/dimensions/Dimensions.kt +++ b/kmath-dimensions/src/commonMain/kotlin/scientifik/kmath/dimensions/Dimensions.kt @@ -31,5 +31,5 @@ object D2 : Dimension { } object D3 : Dimension { - override val dim: UInt get() = 31U -} \ No newline at end of file + override val dim: UInt get() = 3U +} From b9f871ae7686b86e6c2047a3171e22f186669803 Mon Sep 17 00:00:00 2001 From: Iaroslav Postovalov Date: Thu, 30 Jul 2020 03:43:57 +0700 Subject: [PATCH 21/54] Upgrade Gradle from 6.5 to 6.5.1 --- gradle/wrapper/gradle-wrapper.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 622ab64a3..bb8b2fc26 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.5.1-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists From 9fded79af09b08ccc39e7dedf6b7b34979ba4d45 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Fri, 31 Jul 2020 16:46:32 +0700 Subject: [PATCH 22/54] Move codestyle.md --- {docs => doc}/codestyle.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {docs => doc}/codestyle.md (100%) diff --git a/docs/codestyle.md b/doc/codestyle.md similarity index 100% rename from docs/codestyle.md rename to doc/codestyle.md From ae7aefeb6aa58100721144dfeec0aebdbe87f878 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Wed, 5 Aug 2020 03:58:00 +0700 Subject: [PATCH 23/54] Specify type explicitly EVERYWHERE in kmath-core, add newlines at ends of files, refactor minor problems, improve documentation --- .../kotlin/scientifik/kmath/domains/Domain.kt | 11 +- .../kmath/domains/HyperSquareDomain.kt | 5 +- .../scientifik/kmath/domains/RealDomain.kt | 6 +- .../kmath/domains/UnconstrainedDomain.kt | 4 +- .../kmath/domains/UnivariateDomain.kt | 7 +- .../kmath/expressions/Expression.kt | 9 +- .../FunctionalExpressionAlgebra.kt | 2 +- .../scientifik/kmath/linear/BufferMatrix.kt | 16 +- .../scientifik/kmath/linear/FeaturedMatrix.kt | 12 +- .../kmath/linear/LUPDecomposition.kt | 24 +- .../scientifik/kmath/linear/LinearAlgebra.kt | 2 +- .../scientifik/kmath/linear/MatrixContext.kt | 8 +- .../scientifik/kmath/linear/MatrixFeatures.kt | 10 +- .../scientifik/kmath/linear/VectorSpace.kt | 6 +- .../scientifik/kmath/linear/VirtualMatrix.kt | 4 +- .../kotlin/scientifik/kmath/misc/AutoDiff.kt | 10 +- .../kotlin/scientifik/kmath/misc/Grids.kt | 2 +- .../scientifik/kmath/misc/cumulative.kt | 35 +-- .../scientifik/kmath/operations/Algebra.kt | 225 +++++++++++++++--- .../kmath/operations/AlgebraElements.kt | 22 +- .../kmath/operations/AlgebraExtensions.kt | 2 +- .../scientifik/kmath/operations/BigInt.kt | 24 +- .../scientifik/kmath/operations/Complex.kt | 14 +- .../kmath/operations/NumberAlgebra.kt | 125 +++++----- .../kmath/operations/OptionalOperations.kt | 186 ++++++++++++--- .../kmath/structures/BoxingNDField.kt | 8 +- .../kmath/structures/BoxingNDRing.kt | 8 +- .../kmath/structures/BufferAccessor2D.kt | 8 +- .../kmath/structures/BufferedNDAlgebra.kt | 4 +- .../kmath/structures/BufferedNDElement.kt | 16 +- .../scientifik/kmath/structures/Buffers.kt | 21 +- .../kmath/structures/ComplexNDField.kt | 37 ++- .../kmath/structures/ExtendedNDField.kt | 3 - .../kmath/structures/FlaggedBuffer.kt | 12 +- .../scientifik/kmath/structures/IntBuffer.kt | 5 +- .../scientifik/kmath/structures/LongBuffer.kt | 4 +- .../kmath/structures/MemoryBuffer.kt | 14 +- .../scientifik/kmath/structures/NDAlgebra.kt | 26 +- .../scientifik/kmath/structures/NDElement.kt | 29 ++- .../kmath/structures/NDStructure.kt | 41 +++- .../scientifik/kmath/structures/RealBuffer.kt | 4 +- .../kmath/structures/RealNDField.kt | 26 +- .../kmath/structures/ShortBuffer.kt | 5 +- .../kmath/structures/ShortNDRing.kt | 21 +- .../kmath/structures/Structure1D.kt | 8 +- .../kmath/structures/Structure2D.kt | 6 +- .../kmath/expressions/ExpressionFieldTest.kt | 6 +- .../scientifik/kmath/linear/MatrixTest.kt | 12 +- .../kmath/linear/RealLUSolverTest.kt | 2 +- .../scientifik/kmath/misc/AutoDiffTest.kt | 9 +- .../scientifik/kmath/misc/CumulativeKtTest.kt | 2 +- .../kmath/operations/BigIntAlgebraTest.kt | 1 - .../kmath/operations/BigIntConstructorTest.kt | 4 +- .../kmath/operations/BigIntConversionsTest.kt | 4 +- .../kmath/operations/BigIntOperationsTest.kt | 24 +- .../kmath/operations/RealFieldTest.kt | 2 +- .../kmath/structures/ComplexBufferSpecTest.kt | 2 +- .../kmath/structures/NDFieldTest.kt | 2 +- .../kmath/structures/NumberNDFieldTest.kt | 4 +- 59 files changed, 722 insertions(+), 429 deletions(-) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/domains/Domain.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/domains/Domain.kt index 333b77cb4..341383bfb 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/domains/Domain.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/domains/Domain.kt @@ -3,13 +3,18 @@ package scientifik.kmath.domains import scientifik.kmath.linear.Point /** - * A simple geometric domain + * A simple geometric domain. + * + * @param T the type of element of this domain. */ interface Domain { + /** + * Checks if the specified point is contained in this domain. + */ operator fun contains(point: Point): Boolean /** - * Number of hyperspace dimensions + * Number of hyperspace dimensions. */ val dimension: Int -} \ No newline at end of file +} diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/domains/HyperSquareDomain.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/domains/HyperSquareDomain.kt index e0019c96b..66798c42f 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/domains/HyperSquareDomain.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/domains/HyperSquareDomain.kt @@ -42,13 +42,14 @@ class HyperSquareDomain(private val lower: RealBuffer, private val upper: RealBu override fun getUpperBound(num: Int): Double? = upper[num] override fun nearestInDomain(point: Point): Point { - val res: DoubleArray = DoubleArray(point.size) { i -> + val res = DoubleArray(point.size) { i -> when { point[i] < lower[i] -> lower[i] point[i] > upper[i] -> upper[i] else -> point[i] } } + return RealBuffer(*res) } @@ -64,4 +65,4 @@ class HyperSquareDomain(private val lower: RealBuffer, private val upper: RealBu } return res } -} \ No newline at end of file +} diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/domains/RealDomain.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/domains/RealDomain.kt index 89115887e..7507ccd59 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/domains/RealDomain.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/domains/RealDomain.kt @@ -22,8 +22,7 @@ import scientifik.kmath.linear.Point * * @author Alexander Nozik */ -interface RealDomain: Domain { - +interface RealDomain : Domain { fun nearestInDomain(point: Point): Point /** @@ -61,5 +60,4 @@ interface RealDomain: Domain { * @return */ fun volume(): Double - -} \ No newline at end of file +} diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/domains/UnconstrainedDomain.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/domains/UnconstrainedDomain.kt index e49fd3b37..595a3dbe7 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/domains/UnconstrainedDomain.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/domains/UnconstrainedDomain.kt @@ -18,7 +18,6 @@ package scientifik.kmath.domains import scientifik.kmath.linear.Point class UnconstrainedDomain(override val dimension: Int) : RealDomain { - override operator fun contains(point: Point): Boolean = true override fun getLowerBound(num: Int, point: Point): Double? = Double.NEGATIVE_INFINITY @@ -32,5 +31,4 @@ class UnconstrainedDomain(override val dimension: Int) : RealDomain { override fun nearestInDomain(point: Point): Point = point override fun volume(): Double = Double.POSITIVE_INFINITY - -} \ No newline at end of file +} diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/domains/UnivariateDomain.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/domains/UnivariateDomain.kt index ef521d5ea..280dc7d66 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/domains/UnivariateDomain.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/domains/UnivariateDomain.kt @@ -4,7 +4,6 @@ import scientifik.kmath.linear.Point import scientifik.kmath.structures.asBuffer inline class UnivariateDomain(val range: ClosedFloatingPointRange) : RealDomain { - operator fun contains(d: Double): Boolean = range.contains(d) override operator fun contains(point: Point): Boolean { @@ -15,10 +14,10 @@ inline class UnivariateDomain(val range: ClosedFloatingPointRange) : Rea override fun nearestInDomain(point: Point): Point { require(point.size == 1) val value = point[0] - return when{ + return when { value in range -> point value >= range.endInclusive -> doubleArrayOf(range.endInclusive).asBuffer() - else -> doubleArrayOf(range.start).asBuffer() + else -> doubleArrayOf(range.start).asBuffer() } } @@ -45,4 +44,4 @@ inline class UnivariateDomain(val range: ClosedFloatingPointRange) : Rea override fun volume(): Double = range.endInclusive - range.start override val dimension: Int get() = 1 -} \ No newline at end of file +} diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/Expression.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/Expression.kt index e512b1cd8..b21a414f5 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/Expression.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/Expression.kt @@ -14,9 +14,10 @@ interface Expression { /** * Create simple lazily evaluated expression inside given algebra */ -fun Algebra.expression(block: Algebra.(arguments: Map) -> T): Expression = object: Expression { - override fun invoke(arguments: Map): T = block(arguments) -} +fun Algebra.expression(block: Algebra.(arguments: Map) -> T): Expression = + object : Expression { + override fun invoke(arguments: Map): T = block(arguments) + } operator fun Expression.invoke(vararg pairs: Pair): T = invoke(mapOf(*pairs)) @@ -33,4 +34,4 @@ interface ExpressionAlgebra : Algebra { * A constant expression which does not depend on arguments */ fun const(value: T): E -} \ No newline at end of file +} diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressionAlgebra.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressionAlgebra.kt index a8a26aa33..5a8a10717 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressionAlgebra.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressionAlgebra.kt @@ -143,4 +143,4 @@ inline fun > A.expressionInRing(block: FunctionalExpressionRing> A.expressionInField(block: FunctionalExpressionField.() -> Expression): Expression = - FunctionalExpressionField(this).block() \ No newline at end of file + FunctionalExpressionField(this).block() diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/BufferMatrix.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/BufferMatrix.kt index c4c38284b..2e1f32501 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/BufferMatrix.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/BufferMatrix.kt @@ -19,22 +19,20 @@ class BufferMatrixContext>( override fun point(size: Int, initializer: (Int) -> T): Point = bufferFactory(size, initializer) - companion object { - - } + companion object } @Suppress("OVERRIDE_BY_INLINE") object RealMatrixContext : GenericMatrixContext { - override val elementContext get() = RealField + override val elementContext: RealField get() = RealField override inline fun produce(rows: Int, columns: Int, initializer: (i: Int, j: Int) -> Double): Matrix { val buffer = RealBuffer(rows * columns) { offset -> initializer(offset / columns, offset % columns) } return BufferMatrix(rows, columns, buffer) } - override inline fun point(size: Int, initializer: (Int) -> Double): Point = RealBuffer(size,initializer) + override inline fun point(size: Int, initializer: (Int) -> Double): Point = RealBuffer(size, initializer) } class BufferMatrix( @@ -52,7 +50,7 @@ class BufferMatrix( override val shape: IntArray get() = intArrayOf(rowNum, colNum) - override fun suggestFeature(vararg features: MatrixFeature) = + override fun suggestFeature(vararg features: MatrixFeature): BufferMatrix = BufferMatrix(rowNum, colNum, buffer, this.features + features) override fun get(index: IntArray): T = get(index[0], index[1]) @@ -84,8 +82,8 @@ class BufferMatrix( override fun toString(): String { return if (rowNum <= 5 && colNum <= 5) { "Matrix(rowsNum = $rowNum, colNum = $colNum, features=$features)\n" + - rows.asSequence().joinToString(prefix = "(", postfix = ")", separator = "\n ") { - it.asSequence().joinToString(separator = "\t") { it.toString() } + rows.asSequence().joinToString(prefix = "(", postfix = ")", separator = "\n ") { buffer -> + buffer.asSequence().joinToString(separator = "\t") { it.toString() } } } else { "Matrix(rowsNum = $rowNum, colNum = $colNum, features=$features)" @@ -121,4 +119,4 @@ infix fun BufferMatrix.dot(other: BufferMatrix): BufferMatrix : Matrix { */ fun suggestFeature(vararg features: MatrixFeature): FeaturedMatrix - companion object { - - } + companion object } -fun Structure2D.Companion.real(rows: Int, columns: Int, initializer: (Int, Int) -> Double) = +fun Structure2D.Companion.real(rows: Int, columns: Int, initializer: (Int, Int) -> Double): Matrix = MatrixContext.real.produce(rows, columns, initializer) /** @@ -41,7 +39,7 @@ fun Structure2D.Companion.square(vararg elements: T): FeaturedMatrix.features get() = (this as? FeaturedMatrix)?.features?: emptySet() +val Matrix<*>.features: Set get() = (this as? FeaturedMatrix)?.features ?: emptySet() /** * Check if matrix has the given feature class @@ -68,7 +66,7 @@ fun > GenericMatrixContext.one(rows: Int, columns: In * A virtual matrix of zeroes */ fun > GenericMatrixContext.zero(rows: Int, columns: Int): FeaturedMatrix = - VirtualMatrix(rows, columns) { _, _ -> elementContext.zero } + VirtualMatrix(rows, columns) { _, _ -> elementContext.zero } class TransposedFeature(val original: Matrix) : MatrixFeature @@ -83,4 +81,4 @@ fun Matrix.transpose(): Matrix { ) { i, j -> get(j, i) } } -infix fun Matrix.dot(other: Matrix): Matrix = with(MatrixContext.real) { dot(other) } \ No newline at end of file +infix fun Matrix.dot(other: Matrix): Matrix = with(MatrixContext.real) { dot(other) } diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/LUPDecomposition.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/LUPDecomposition.kt index 75d7da169..d04a99fbb 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/LUPDecomposition.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/LUPDecomposition.kt @@ -18,7 +18,7 @@ class LUPDecomposition( private val even: Boolean ) : LUPDecompositionFeature, DeterminantFeature { - val elementContext get() = context.elementContext + val elementContext: Field get() = context.elementContext /** * Returns the matrix L of the decomposition. @@ -67,7 +67,7 @@ class LUPDecomposition( } -fun , F : Field> GenericMatrixContext.abs(value: T) = +fun , F : Field> GenericMatrixContext.abs(value: T): T = if (value > elementContext.zero) value else with(elementContext) { -value } @@ -169,9 +169,10 @@ fun , F : Field> GenericMatrixContext.lup( inline fun , F : Field> GenericMatrixContext.lup( matrix: Matrix, noinline checkSingular: (T) -> Boolean -) = lup(T::class, matrix, checkSingular) +): LUPDecomposition = lup(T::class, matrix, checkSingular) -fun GenericMatrixContext.lup(matrix: Matrix) = lup(Double::class, matrix) { it < 1e-11 } +fun GenericMatrixContext.lup(matrix: Matrix): LUPDecomposition = + lup(Double::class, matrix) { it < 1e-11 } fun LUPDecomposition.solve(type: KClass, matrix: Matrix): Matrix { @@ -185,7 +186,7 @@ fun LUPDecomposition.solve(type: KClass, matrix: Matrix): Mat // Apply permutations to b val bp = create { _, _ -> zero } - for (row in 0 until pivot.size) { + for (row in pivot.indices) { val bpRow = bp.row(row) val pRow = pivot[row] for (col in 0 until matrix.colNum) { @@ -194,7 +195,7 @@ fun LUPDecomposition.solve(type: KClass, matrix: Matrix): Mat } // Solve LY = b - for (col in 0 until pivot.size) { + for (col in pivot.indices) { val bpCol = bp.row(col) for (i in col + 1 until pivot.size) { val bpI = bp.row(i) @@ -225,7 +226,7 @@ fun LUPDecomposition.solve(type: KClass, matrix: Matrix): Mat } } -inline fun LUPDecomposition.solve(matrix: Matrix) = solve(T::class, matrix) +inline fun LUPDecomposition.solve(matrix: Matrix): Matrix = solve(T::class, matrix) /** * Solve a linear equation **a*x = b** @@ -240,13 +241,12 @@ inline fun , F : Field> GenericMatrixContext. return decomposition.solve(T::class, b) } -fun RealMatrixContext.solve(a: Matrix, b: Matrix) = - solve(a, b) { it < 1e-11 } +fun RealMatrixContext.solve(a: Matrix, b: Matrix): Matrix = solve(a, b) { it < 1e-11 } inline fun , F : Field> GenericMatrixContext.inverse( matrix: Matrix, noinline checkSingular: (T) -> Boolean -) = solve(matrix, one(matrix.rowNum, matrix.colNum), checkSingular) +): Matrix = solve(matrix, one(matrix.rowNum, matrix.colNum), checkSingular) -fun RealMatrixContext.inverse(matrix: Matrix) = - solve(matrix, one(matrix.rowNum, matrix.colNum)) { it < 1e-11 } \ No newline at end of file +fun RealMatrixContext.inverse(matrix: Matrix): Matrix = + solve(matrix, one(matrix.rowNum, matrix.colNum)) { it < 1e-11 } diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/LinearAlgebra.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/LinearAlgebra.kt index abd8603f4..fb49d18ed 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/LinearAlgebra.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/LinearAlgebra.kt @@ -25,4 +25,4 @@ fun Matrix.asPoint(): Point = error("Can't convert matrix with more than one column to vector") } -fun Point.asMatrix() = VirtualMatrix(size, 1) { i, _ -> get(i) } \ No newline at end of file +fun Point.asMatrix(): VirtualMatrix = VirtualMatrix(size, 1) { i, _ -> get(i) } diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/MatrixContext.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/MatrixContext.kt index 7797fdadf..5dc86a7dd 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/MatrixContext.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/MatrixContext.kt @@ -29,7 +29,7 @@ interface MatrixContext : SpaceOperations> { /** * Non-boxing double matrix */ - val real = RealMatrixContext + val real: RealMatrixContext = RealMatrixContext /** * A structured matrix with custom buffer @@ -82,12 +82,12 @@ interface GenericMatrixContext> : MatrixContext { } } - override operator fun Matrix.unaryMinus() = + override operator fun Matrix.unaryMinus(): Matrix = produce(rowNum, colNum) { i, j -> elementContext.run { -get(i, j) } } override fun add(a: Matrix, b: Matrix): Matrix { if (a.rowNum != b.rowNum || a.colNum != b.colNum) error("Matrix operation dimension mismatch. [${a.rowNum},${a.colNum}] + [${b.rowNum},${b.colNum}]") - return produce(a.rowNum, a.colNum) { i, j -> elementContext.run { a.get(i, j) + b[i, j] } } + return produce(a.rowNum, a.colNum) { i, j -> elementContext.run { a[i, j] + b[i, j] } } } override operator fun Matrix.minus(b: Matrix): Matrix { @@ -96,7 +96,7 @@ interface GenericMatrixContext> : MatrixContext { } override fun multiply(a: Matrix, k: Number): Matrix = - produce(a.rowNum, a.colNum) { i, j -> elementContext.run { a.get(i, j) * k } } + produce(a.rowNum, a.colNum) { i, j -> elementContext.run { a[i, j] * k } } operator fun Number.times(matrix: FeaturedMatrix): Matrix = matrix * this diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/MatrixFeatures.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/MatrixFeatures.kt index de315071f..87cfe21b0 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/MatrixFeatures.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/MatrixFeatures.kt @@ -1,7 +1,7 @@ package scientifik.kmath.linear /** - * A marker interface representing some matrix feature like diagonal, sparce, zero, etc. Features used to optimize matrix + * A marker interface representing some matrix feature like diagonal, sparse, zero, etc. Features used to optimize matrix * operations performance in some cases. */ interface MatrixFeature @@ -36,19 +36,19 @@ interface DeterminantFeature : MatrixFeature { } @Suppress("FunctionName") -fun DeterminantFeature(determinant: T) = object: DeterminantFeature{ +fun DeterminantFeature(determinant: T): DeterminantFeature = object : DeterminantFeature { override val determinant: T = determinant } /** * Lower triangular matrix */ -object LFeature: MatrixFeature +object LFeature : MatrixFeature /** * Upper triangular feature */ -object UFeature: MatrixFeature +object UFeature : MatrixFeature /** * TODO add documentation @@ -59,4 +59,4 @@ interface LUPDecompositionFeature : MatrixFeature { val p: FeaturedMatrix } -//TODO add sparse matrix feature \ No newline at end of file +//TODO add sparse matrix feature diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/VectorSpace.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/VectorSpace.kt index 8e14e2882..691b464fc 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/VectorSpace.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/VectorSpace.kt @@ -54,7 +54,7 @@ interface VectorSpace> : Space> { size: Int, space: S, bufferFactory: BufferFactory = Buffer.Companion::boxing - ) = BufferVectorSpace(size, space, bufferFactory) + ): BufferVectorSpace = BufferVectorSpace(size, space, bufferFactory) /** * Automatic buffered vector, unboxed if it is possible @@ -70,6 +70,6 @@ class BufferVectorSpace>( override val space: S, val bufferFactory: BufferFactory ) : VectorSpace { - override fun produce(initializer: (Int) -> T) = bufferFactory(size, initializer) + override fun produce(initializer: (Int) -> T): Buffer = bufferFactory(size, initializer) //override fun produceElement(initializer: (Int) -> T): Vector = BufferVector(this, produce(initializer)) -} \ No newline at end of file +} diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/VirtualMatrix.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/VirtualMatrix.kt index 0806cabea..207151d57 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/VirtualMatrix.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/VirtualMatrix.kt @@ -20,7 +20,7 @@ class VirtualMatrix( override fun get(i: Int, j: Int): T = generator(i, j) - override fun suggestFeature(vararg features: MatrixFeature) = + override fun suggestFeature(vararg features: MatrixFeature): VirtualMatrix = VirtualMatrix(rowNum, colNum, this.features + features, generator) override fun equals(other: Any?): Boolean { @@ -56,4 +56,4 @@ class VirtualMatrix( } } } -} \ No newline at end of file +} diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/misc/AutoDiff.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/misc/AutoDiff.kt index 076701a4f..db8863ae8 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/misc/AutoDiff.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/misc/AutoDiff.kt @@ -22,12 +22,12 @@ class DerivationResult( val deriv: Map, T>, val context: Field ) : Variable(value) { - fun deriv(variable: Variable) = deriv[variable] ?: context.zero + fun deriv(variable: Variable): T = deriv[variable] ?: context.zero /** * compute divergence */ - fun div() = context.run { sum(deriv.values) } + fun div(): T = context.run { sum(deriv.values) } /** * Compute a gradient for variables in given order @@ -53,7 +53,7 @@ class DerivationResult( * ``` */ fun > F.deriv(body: AutoDiffField.() -> Variable): DerivationResult = - AutoDiffContext(this).run { + AutoDiffContext(this).run { val result = body() result.d = context.one// computing derivative w.r.t result runBackwardPass() @@ -86,7 +86,7 @@ abstract class AutoDiffField> : Field> { abstract fun variable(value: T): Variable - inline fun variable(block: F.() -> T) = variable(context.block()) + inline fun variable(block: F.() -> T): Variable = variable(context.block()) // Overloads for Double constants @@ -236,4 +236,4 @@ fun > AutoDiffField.sin(x: Variable): Var fun > AutoDiffField.cos(x: Variable): Variable = derive(variable { cos(x.value) }) { z -> x.d -= z.d * sin(x.value) - } \ No newline at end of file + } diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/misc/Grids.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/misc/Grids.kt index f040fb8d4..d3bf0891f 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/misc/Grids.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/misc/Grids.kt @@ -43,4 +43,4 @@ fun ClosedFloatingPointRange.toSequenceWithPoints(numPoints: Int): Seque fun ClosedFloatingPointRange.toGrid(numPoints: Int): DoubleArray { if (numPoints < 2) error("Can't create generic grid with less than two points") return DoubleArray(numPoints) { i -> start + (endInclusive - start) / (numPoints - 1) * i } -} \ No newline at end of file +} diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/misc/cumulative.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/misc/cumulative.kt index c3cfc448a..a0f4525cc 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/misc/cumulative.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/misc/cumulative.kt @@ -1,14 +1,15 @@ package scientifik.kmath.misc import scientifik.kmath.operations.Space +import scientifik.kmath.operations.invoke import kotlin.jvm.JvmName - /** - * Generic cumulative operation on iterator - * @param T type of initial iterable - * @param R type of resulting iterable - * @param initial lazy evaluated + * Generic cumulative operation on iterator. + * + * @param T the type of initial iterable. + * @param R the type of resulting iterable. + * @param initial lazy evaluated. */ fun Iterator.cumulative(initial: R, operation: (R, T) -> R): Iterator = object : Iterator { var state: R = initial @@ -36,41 +37,41 @@ fun List.cumulative(initial: R, operation: (R, T) -> R): List = /** * Cumulative sum with custom space */ -fun Iterable.cumulativeSum(space: Space) = with(space) { +fun Iterable.cumulativeSum(space: Space): Iterable = space { cumulative(zero) { element: T, sum: T -> sum + element } } @JvmName("cumulativeSumOfDouble") -fun Iterable.cumulativeSum() = this.cumulative(0.0) { element, sum -> sum + element } +fun Iterable.cumulativeSum(): Iterable = this.cumulative(0.0) { element, sum -> sum + element } @JvmName("cumulativeSumOfInt") -fun Iterable.cumulativeSum() = this.cumulative(0) { element, sum -> sum + element } +fun Iterable.cumulativeSum(): Iterable = this.cumulative(0) { element, sum -> sum + element } @JvmName("cumulativeSumOfLong") -fun Iterable.cumulativeSum() = this.cumulative(0L) { element, sum -> sum + element } +fun Iterable.cumulativeSum(): Iterable = this.cumulative(0L) { element, sum -> sum + element } -fun Sequence.cumulativeSum(space: Space) = with(space) { +fun Sequence.cumulativeSum(space: Space): Sequence = with(space) { cumulative(zero) { element: T, sum: T -> sum + element } } @JvmName("cumulativeSumOfDouble") -fun Sequence.cumulativeSum() = this.cumulative(0.0) { element, sum -> sum + element } +fun Sequence.cumulativeSum(): Sequence = this.cumulative(0.0) { element, sum -> sum + element } @JvmName("cumulativeSumOfInt") -fun Sequence.cumulativeSum() = this.cumulative(0) { element, sum -> sum + element } +fun Sequence.cumulativeSum(): Sequence = this.cumulative(0) { element, sum -> sum + element } @JvmName("cumulativeSumOfLong") -fun Sequence.cumulativeSum() = this.cumulative(0L) { element, sum -> sum + element } +fun Sequence.cumulativeSum(): Sequence = this.cumulative(0L) { element, sum -> sum + element } -fun List.cumulativeSum(space: Space) = with(space) { +fun List.cumulativeSum(space: Space): List = with(space) { cumulative(zero) { element: T, sum: T -> sum + element } } @JvmName("cumulativeSumOfDouble") -fun List.cumulativeSum() = this.cumulative(0.0) { element, sum -> sum + element } +fun List.cumulativeSum(): List = this.cumulative(0.0) { element, sum -> sum + element } @JvmName("cumulativeSumOfInt") -fun List.cumulativeSum() = this.cumulative(0) { element, sum -> sum + element } +fun List.cumulativeSum(): List = this.cumulative(0) { element, sum -> sum + element } @JvmName("cumulativeSumOfLong") -fun List.cumulativeSum() = this.cumulative(0L) { element, sum -> sum + element } \ No newline at end of file +fun List.cumulativeSum(): List = this.cumulative(0L) { element, sum -> sum + element } diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Algebra.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Algebra.kt index 52b6bba02..aa190b31f 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Algebra.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Algebra.kt @@ -1,10 +1,15 @@ package scientifik.kmath.operations +/** + * Stub for DSL the [Algebra] is. + */ @DslMarker annotation class KMathContext /** - * Marker interface for any algebra + * Represents an algebraic structure. + * + * @param T the type of element of this structure. */ interface Algebra { /** @@ -24,50 +29,122 @@ interface Algebra { } /** - * An algebra with numeric representation of members + * An algebraic structure where elements can have numeric representation. + * + * @param T the type of element of this structure. */ interface NumericAlgebra : Algebra { /** - * Wrap a number + * Wraps a number. */ fun number(value: Number): T + /** + * Dynamic call of binary operation [operation] on [left] and [right] where left element is [Number]. + */ fun leftSideNumberOperation(operation: String, left: Number, right: T): T = binaryOperation(operation, number(left), right) + /** + * Dynamic call of binary operation [operation] on [left] and [right] where right element is [Number]. + */ fun rightSideNumberOperation(operation: String, left: T, right: Number): T = leftSideNumberOperation(operation, right, left) } /** - * Call a block with an [Algebra] as receiver + * Call a block with an [Algebra] as receiver. */ inline operator fun , R> A.invoke(block: A.() -> R): R = run(block) /** - * Space-like operations without neutral element + * Represents semigroup, i.e. algebraic structure with associative binary operation called "addition". + * + * In KMath groups are called spaces, and also define multiplication of element by [Number]. + * + * @param T the type of element of this semigroup. */ interface SpaceOperations : Algebra { /** - * Addition operation for two context elements + * Addition of two elements. + * + * @param a the addend. + * @param b the augend. + * @return the sum. */ fun add(a: T, b: T): T /** - * Multiplication operation for context element and real number + * Multiplication of element by scalar. + * + * @param a the multiplier. + * @param k the multiplicand. + * @return the produce. */ fun multiply(a: T, k: Number): T - //Operation to be performed in this context. Could be moved to extensions in case of KEEP-176 + // Operations to be performed in this context. Could be moved to extensions in case of KEEP-176 + + /** + * The negation of this element. + * + * @receiver this value. + * @return the additive inverse of this value. + */ operator fun T.unaryMinus(): T = multiply(this, -1.0) + /** + * Returns this value. + * + * @receiver this value. + * @return this value. + */ operator fun T.unaryPlus(): T = this + /** + * Addition of two elements. + * + * @receiver the addend. + * @param b the augend. + * @return the sum. + */ operator fun T.plus(b: T): T = add(this, b) + + /** + * Subtraction of two elements. + * + * @receiver the minuend. + * @param b the subtrahend. + * @return the difference. + */ operator fun T.minus(b: T): T = add(this, -b) - operator fun T.times(k: Number) = multiply(this, k.toDouble()) - operator fun T.div(k: Number) = multiply(this, 1.0 / k.toDouble()) - operator fun Number.times(b: T) = b * this + + /** + * Multiplication of this element by a scalar. + * + * @receiver the multiplier. + * @param k the multiplicand. + * @return the product. + */ + operator fun T.times(k: Number): T = multiply(this, k.toDouble()) + + /** + * Division of this element by scalar. + * + * @receiver the dividend. + * @param k the divisor. + * @return the quotient. + */ + operator fun T.div(k: Number): T = multiply(this, 1.0 / k.toDouble()) + + /** + * Multiplication of this number by element. + * + * @receiver the multiplier. + * @param b the multiplicand. + * @return the product. + */ + operator fun Number.times(b: T): T = b * this override fun unaryOperation(operation: String, arg: T): T = when (operation) { PLUS_OPERATION -> arg @@ -82,37 +159,56 @@ interface SpaceOperations : Algebra { } companion object { - const val PLUS_OPERATION = "+" - const val MINUS_OPERATION = "-" - const val NOT_OPERATION = "!" + /** + * The identifier of addition. + */ + const val PLUS_OPERATION: String = "+" + + /** + * The identifier of subtraction (and negation). + */ + const val MINUS_OPERATION: String = "-" + + const val NOT_OPERATION: String = "!" } } - /** - * A general interface representing linear context of some kind. - * The context defines sum operation for its elements and multiplication by real value. - * One must note that in some cases context is a singleton class, but in some cases it - * works as a context for operations inside it. + * Represents group, i.e. algebraic structure with associative binary operation called "addition" and its neutral + * element. * - * TODO do we need non-commutative context? + * In KMath groups are called spaces, and also define multiplication of element by [Number]. + * + * @param T the type of element of this group. */ interface Space : SpaceOperations { /** - * Neutral element for sum operation + * The neutral element of addition. */ val zero: T } /** - * Operations on ring without multiplication neutral element + * Represents semiring, i.e. algebraic structure with two associative binary operations called "addition" and + * "multiplication". + * + * @param T the type of element of this semiring. */ interface RingOperations : SpaceOperations { /** - * Multiplication for two field elements + * Multiplies two elements. + * + * @param a the multiplier. + * @param b the multiplicand. */ fun multiply(a: T, b: T): T + /** + * Multiplies this element by scalar. + * + * @receiver the multiplier. + * @param b the multiplicand. + */ operator fun T.times(b: T): T = multiply(this, b) override fun binaryOperation(operation: String, left: T, right: T): T = when (operation) { @@ -121,12 +217,18 @@ interface RingOperations : SpaceOperations { } companion object { - const val TIMES_OPERATION = "*" + /** + * The identifier of multiplication. + */ + const val TIMES_OPERATION: String = "*" } } /** - * The same as {@link Space} but with additional multiplication operation + * Represents ring, i.e. algebraic structure with two associative binary operations called "addition" and + * "multiplication" and their neutral elements. + * + * @param T the type of element of this ring. */ interface Ring : Space, RingOperations, NumericAlgebra { /** @@ -150,20 +252,64 @@ interface Ring : Space, RingOperations, NumericAlgebra { else -> super.rightSideNumberOperation(operation, left, right) } + /** + * Addition of element and scalar. + * + * @receiver the addend. + * @param b the augend. + */ + operator fun T.plus(b: Number): T = this + number(b) - operator fun T.plus(b: Number) = this.plus(number(b)) - operator fun Number.plus(b: T) = b + this + /** + * Addition of scalar and element. + * + * @receiver the addend. + * @param b the augend. + */ + operator fun Number.plus(b: T): T = b + this - operator fun T.minus(b: Number) = this.minus(number(b)) - operator fun Number.minus(b: T) = -b + this + /** + * Subtraction of element from number. + * + * @receiver the minuend. + * @param b the subtrahend. + * @receiver the difference. + */ + operator fun T.minus(b: Number): T = this - number(b) + + /** + * Subtraction of number from element. + * + * @receiver the minuend. + * @param b the subtrahend. + * @receiver the difference. + */ + operator fun Number.minus(b: T): T = -b + this } /** - * All ring operations but without neutral elements + * Represents semifield, i.e. algebraic structure with three operations: associative "addition" and "multiplication", + * and "division". + * + * @param T the type of element of this semifield. */ interface FieldOperations : RingOperations { + /** + * Division of two elements. + * + * @param a the dividend. + * @param b the divisor. + * @return the quotient. + */ fun divide(a: T, b: T): T + /** + * Division of two elements. + * + * @receiver the dividend. + * @param b the divisor. + * @return the quotient. + */ operator fun T.div(b: T): T = divide(this, b) override fun binaryOperation(operation: String, left: T, right: T): T = when (operation) { @@ -172,13 +318,26 @@ interface FieldOperations : RingOperations { } companion object { - const val DIV_OPERATION = "/" + /** + * The identifier of division. + */ + const val DIV_OPERATION: String = "/" } } /** - * Four operations algebra + * Represents field, i.e. algebraic structure with three operations: associative "addition" and "multiplication", + * and "division" and their neutral elements. + * + * @param T the type of element of this semifield. */ interface Field : Ring, FieldOperations { - operator fun Number.div(b: T) = this * divide(one, b) + /** + * Division of element by scalar. + * + * @receiver the dividend. + * @param b the divisor. + * @return the quotient. + */ + operator fun Number.div(b: T): T = this * divide(one, b) } diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/AlgebraElements.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/AlgebraElements.kt index 093021ae3..9b430af39 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/AlgebraElements.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/AlgebraElements.kt @@ -2,13 +2,12 @@ package scientifik.kmath.operations /** * The generic mathematics elements which is able to store its context - * @param T the type of space operation results - * @param I self type of the element. Needed for static type checking - * @param C the type of mathematical context for this element + * + * @param C the type of mathematical context for this element. */ interface MathElement { /** - * The context this element belongs to + * The context this element belongs to. */ val context: C } @@ -25,18 +24,17 @@ interface MathWrapper { * @param S the type of space */ interface SpaceElement, S : Space> : MathElement, MathWrapper { - - operator fun plus(b: T) = context.add(unwrap(), b).wrap() - operator fun minus(b: T) = context.add(unwrap(), context.multiply(b, -1.0)).wrap() - operator fun times(k: Number) = context.multiply(unwrap(), k.toDouble()).wrap() - operator fun div(k: Number) = context.multiply(unwrap(), 1.0 / k.toDouble()).wrap() + operator fun plus(b: T): I = context.add(unwrap(), b).wrap() + operator fun minus(b: T): I = context.add(unwrap(), context.multiply(b, -1.0)).wrap() + operator fun times(k: Number): I = context.multiply(unwrap(), k.toDouble()).wrap() + operator fun div(k: Number): I = context.multiply(unwrap(), 1.0 / k.toDouble()).wrap() } /** * Ring element */ interface RingElement, R : Ring> : SpaceElement { - operator fun times(b: T) = context.multiply(unwrap(), b).wrap() + operator fun times(b: T): I = context.multiply(unwrap(), b).wrap() } /** @@ -44,5 +42,5 @@ interface RingElement, R : Ring> : SpaceElement, F : Field> : RingElement { override val context: F - operator fun div(b: T) = context.divide(unwrap(), b).wrap() -} \ No newline at end of file + operator fun div(b: T): I = context.divide(unwrap(), b).wrap() +} diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/AlgebraExtensions.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/AlgebraExtensions.kt index bfb4199a3..a6d508ee4 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/AlgebraExtensions.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/AlgebraExtensions.kt @@ -12,4 +12,4 @@ fun RingOperations.power(arg: T, power: Int): T { res *= arg } return res -} \ No newline at end of file +} diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/BigInt.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/BigInt.kt index 5e7d49b1c..fd7719157 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/BigInt.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/BigInt.kt @@ -194,8 +194,8 @@ class BigInt internal constructor( } infix fun or(other: BigInt): BigInt { - if (this == ZERO) return other; - if (other == ZERO) return this; + if (this == ZERO) return other + if (other == ZERO) return this val resSize = max(this.magnitude.size, other.magnitude.size) val newMagnitude: Magnitude = Magnitude(resSize) for (i in 0 until resSize) { @@ -210,7 +210,7 @@ class BigInt internal constructor( } infix fun and(other: BigInt): BigInt { - if ((this == ZERO) or (other == ZERO)) return ZERO; + if ((this == ZERO) or (other == ZERO)) return ZERO val resSize = min(this.magnitude.size, other.magnitude.size) val newMagnitude: Magnitude = Magnitude(resSize) for (i in 0 until resSize) { @@ -260,7 +260,7 @@ class BigInt internal constructor( } companion object { - const val BASE = 0xffffffffUL + const val BASE: ULong = 0xffffffffUL const val BASE_SIZE: Int = 32 val ZERO: BigInt = BigInt(0, uintArrayOf()) val ONE: BigInt = BigInt(1, uintArrayOf(1u)) @@ -394,12 +394,12 @@ fun abs(x: BigInt): BigInt = x.abs() /** * Convert this [Int] to [BigInt] */ -fun Int.toBigInt() = BigInt(sign.toByte(), uintArrayOf(kotlin.math.abs(this).toUInt())) +fun Int.toBigInt(): BigInt = BigInt(sign.toByte(), uintArrayOf(kotlin.math.abs(this).toUInt())) /** * Convert this [Long] to [BigInt] */ -fun Long.toBigInt() = BigInt( +fun Long.toBigInt(): BigInt = BigInt( sign.toByte(), stripLeadingZeros( uintArrayOf( (kotlin.math.abs(this).toULong() and BASE).toUInt(), @@ -411,17 +411,17 @@ fun Long.toBigInt() = BigInt( /** * Convert UInt to [BigInt] */ -fun UInt.toBigInt() = BigInt(1, uintArrayOf(this)) +fun UInt.toBigInt(): BigInt = BigInt(1, uintArrayOf(this)) /** * Convert ULong to [BigInt] */ -fun ULong.toBigInt() = BigInt( +fun ULong.toBigInt(): BigInt = BigInt( 1, stripLeadingZeros( uintArrayOf( - (this and BigInt.BASE).toUInt(), - ((this shr BigInt.BASE_SIZE) and BigInt.BASE).toUInt() + (this and BASE).toUInt(), + ((this shr BASE_SIZE) and BASE).toUInt() ) ) ) @@ -434,7 +434,7 @@ fun UIntArray.toBigInt(sign: Byte): BigInt { return BigInt(sign, this.copyOf()) } -val hexChToInt = hashMapOf( +val hexChToInt: MutableMap = hashMapOf( '0' to 0, '1' to 1, '2' to 2, '3' to 3, '4' to 4, '5' to 5, '6' to 6, '7' to 7, '8' to 8, '9' to 9, 'A' to 10, 'B' to 11, @@ -497,4 +497,4 @@ fun NDElement.Companion.bigInt( vararg shape: Int, initializer: BigIntField.(IntArray) -> BigInt ): BufferedNDRingElement = - NDAlgebra.bigInt(*shape).produce(initializer) \ No newline at end of file + NDAlgebra.bigInt(*shape).produce(initializer) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Complex.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Complex.kt index 398ea4395..ffd6c8dec 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Complex.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Complex.kt @@ -18,7 +18,7 @@ object ComplexField : ExtendedField { override val one: Complex = Complex(1.0, 0.0) - val i = Complex(0.0, 1.0) + val i: Complex = Complex(0.0, 1.0) override fun add(a: Complex, b: Complex): Complex = Complex(a.re + b.re, a.im + b.im) @@ -45,15 +45,15 @@ object ComplexField : ExtendedField { override fun ln(arg: Complex): Complex = ln(arg.r) + i * atan2(arg.im, arg.re) - operator fun Double.plus(c: Complex) = add(this.toComplex(), c) + operator fun Double.plus(c: Complex): Complex = add(this.toComplex(), c) - operator fun Double.minus(c: Complex) = add(this.toComplex(), -c) + operator fun Double.minus(c: Complex): Complex = add(this.toComplex(), -c) - operator fun Complex.plus(d: Double) = d + this + operator fun Complex.plus(d: Double): Complex = d + this - operator fun Complex.minus(d: Double) = add(this, -d.toComplex()) + operator fun Complex.minus(d: Double): Complex = add(this, -d.toComplex()) - operator fun Double.times(c: Complex) = Complex(c.re * this, c.im * this) + operator fun Double.times(c: Complex): Complex = Complex(c.re * this, c.im * this) override fun symbol(value: String): Complex = if (value == "i") { i @@ -104,7 +104,7 @@ val Complex.r: Double get() = sqrt(re * re + im * im) */ val Complex.theta: Double get() = atan(im / re) -fun Double.toComplex() = Complex(this, 0.0) +fun Double.toComplex(): Complex = Complex(this, 0.0) inline fun Buffer.Companion.complex(size: Int, crossinline init: (Int) -> Complex): Buffer { return MemoryBuffer.create(Complex, size, init) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/NumberAlgebra.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/NumberAlgebra.kt index 2c57d0098..2145eb4b5 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/NumberAlgebra.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/NumberAlgebra.kt @@ -1,6 +1,5 @@ package scientifik.kmath.operations -import scientifik.kmath.operations.RealField.pow import kotlin.math.abs import kotlin.math.pow as kpow @@ -38,6 +37,8 @@ interface ExtendedField : ExtendedFieldOperations, Field { /** * Real field element wrapping double. * + * @property value the [Double] value wrapped by this [Real]. + * * TODO inline does not work due to compiler bug. Waiting for fix for KT-27586 */ inline class Real(val value: Double) : FieldElement { @@ -45,7 +46,7 @@ inline class Real(val value: Double) : FieldElement { override fun Double.wrap(): Real = Real(value) - override val context get() = RealField + override val context: RealField get() = RealField companion object } @@ -56,36 +57,36 @@ inline class Real(val value: Double) : FieldElement { @Suppress("EXTENSION_SHADOWED_BY_MEMBER", "OVERRIDE_BY_INLINE", "NOTHING_TO_INLINE") object RealField : ExtendedField, Norm { override val zero: Double = 0.0 - override inline fun add(a: Double, b: Double) = a + b - override inline fun multiply(a: Double, b: Double) = a * b - override inline fun multiply(a: Double, k: Number) = a * k.toDouble() + override inline fun add(a: Double, b: Double): Double = a + b + override inline fun multiply(a: Double, b: Double): Double = a * b + override inline fun multiply(a: Double, k: Number): Double = a * k.toDouble() override val one: Double = 1.0 - override inline fun divide(a: Double, b: Double) = a / b + override inline fun divide(a: Double, b: Double): Double = a / b - override inline fun sin(arg: Double) = kotlin.math.sin(arg) - override inline fun cos(arg: Double) = kotlin.math.cos(arg) + override inline fun sin(arg: Double): Double = kotlin.math.sin(arg) + override inline fun cos(arg: Double): Double = kotlin.math.cos(arg) override inline fun tan(arg: Double): Double = kotlin.math.tan(arg) override inline fun acos(arg: Double): Double = kotlin.math.acos(arg) override inline fun asin(arg: Double): Double = kotlin.math.asin(arg) override inline fun atan(arg: Double): Double = kotlin.math.atan(arg) - override inline fun power(arg: Double, pow: Number) = arg.kpow(pow.toDouble()) + override inline fun power(arg: Double, pow: Number): Double = arg.kpow(pow.toDouble()) - override inline fun exp(arg: Double) = kotlin.math.exp(arg) - override inline fun ln(arg: Double) = kotlin.math.ln(arg) + override inline fun exp(arg: Double): Double = kotlin.math.exp(arg) + override inline fun ln(arg: Double): Double = kotlin.math.ln(arg) - override inline fun norm(arg: Double) = abs(arg) + override inline fun norm(arg: Double): Double = abs(arg) - override inline fun Double.unaryMinus() = -this + override inline fun Double.unaryMinus(): Double = -this - override inline fun Double.plus(b: Double) = this + b + override inline fun Double.plus(b: Double): Double = this + b - override inline fun Double.minus(b: Double) = this - b + override inline fun Double.minus(b: Double): Double = this - b - override inline fun Double.times(b: Double) = this * b + override inline fun Double.times(b: Double): Double = this * b - override inline fun Double.div(b: Double) = this / b + override inline fun Double.div(b: Double): Double = this / b override fun binaryOperation(operation: String, left: Double, right: Double): Double = when (operation) { PowerOperations.POW_OPERATION -> left pow right @@ -96,36 +97,36 @@ object RealField : ExtendedField, Norm { @Suppress("EXTENSION_SHADOWED_BY_MEMBER", "OVERRIDE_BY_INLINE", "NOTHING_TO_INLINE") object FloatField : ExtendedField, Norm { override val zero: Float = 0f - override inline fun add(a: Float, b: Float) = a + b - override inline fun multiply(a: Float, b: Float) = a * b - override inline fun multiply(a: Float, k: Number) = a * k.toFloat() + override inline fun add(a: Float, b: Float): Float = a + b + override inline fun multiply(a: Float, b: Float): Float = a * b + override inline fun multiply(a: Float, k: Number): Float = a * k.toFloat() override val one: Float = 1f - override inline fun divide(a: Float, b: Float) = a / b + override inline fun divide(a: Float, b: Float): Float = a / b - override inline fun sin(arg: Float) = kotlin.math.sin(arg) - override inline fun cos(arg: Float) = kotlin.math.cos(arg) - override inline fun tan(arg: Float) = kotlin.math.tan(arg) - override inline fun acos(arg: Float) = kotlin.math.acos(arg) - override inline fun asin(arg: Float) = kotlin.math.asin(arg) - override inline fun atan(arg: Float) = kotlin.math.atan(arg) + override inline fun sin(arg: Float): Float = kotlin.math.sin(arg) + override inline fun cos(arg: Float): Float = kotlin.math.cos(arg) + override inline fun tan(arg: Float): Float = kotlin.math.tan(arg) + override inline fun acos(arg: Float): Float = kotlin.math.acos(arg) + override inline fun asin(arg: Float): Float = kotlin.math.asin(arg) + override inline fun atan(arg: Float): Float = kotlin.math.atan(arg) - override inline fun power(arg: Float, pow: Number) = arg.pow(pow.toFloat()) + override inline fun power(arg: Float, pow: Number): Float = arg.pow(pow.toFloat()) - override inline fun exp(arg: Float) = kotlin.math.exp(arg) - override inline fun ln(arg: Float) = kotlin.math.ln(arg) + override inline fun exp(arg: Float): Float = kotlin.math.exp(arg) + override inline fun ln(arg: Float): Float = kotlin.math.ln(arg) - override inline fun norm(arg: Float) = abs(arg) + override inline fun norm(arg: Float): Float = abs(arg) - override inline fun Float.unaryMinus() = -this + override inline fun Float.unaryMinus(): Float = -this - override inline fun Float.plus(b: Float) = this + b + override inline fun Float.plus(b: Float): Float = this + b - override inline fun Float.minus(b: Float) = this - b + override inline fun Float.minus(b: Float): Float = this - b - override inline fun Float.times(b: Float) = this * b + override inline fun Float.times(b: Float): Float = this * b - override inline fun Float.div(b: Float) = this / b + override inline fun Float.div(b: Float): Float = this / b } /** @@ -134,14 +135,14 @@ object FloatField : ExtendedField, Norm { @Suppress("EXTENSION_SHADOWED_BY_MEMBER", "OVERRIDE_BY_INLINE", "NOTHING_TO_INLINE") object IntRing : Ring, Norm { override val zero: Int = 0 - override inline fun add(a: Int, b: Int) = a + b - override inline fun multiply(a: Int, b: Int) = a * b - override inline fun multiply(a: Int, k: Number) = k.toInt() * a + override inline fun add(a: Int, b: Int): Int = a + b + override inline fun multiply(a: Int, b: Int): Int = a * b + override inline fun multiply(a: Int, k: Number): Int = k.toInt() * a override val one: Int = 1 - override inline fun norm(arg: Int) = abs(arg) + override inline fun norm(arg: Int): Int = abs(arg) - override inline fun Int.unaryMinus() = -this + override inline fun Int.unaryMinus(): Int = -this override inline fun Int.plus(b: Int): Int = this + b @@ -156,20 +157,20 @@ object IntRing : Ring, Norm { @Suppress("EXTENSION_SHADOWED_BY_MEMBER", "OVERRIDE_BY_INLINE", "NOTHING_TO_INLINE") object ShortRing : Ring, Norm { override val zero: Short = 0 - override inline fun add(a: Short, b: Short) = (a + b).toShort() - override inline fun multiply(a: Short, b: Short) = (a * b).toShort() - override inline fun multiply(a: Short, k: Number) = (a * k.toShort()).toShort() + override inline fun add(a: Short, b: Short): Short = (a + b).toShort() + override inline fun multiply(a: Short, b: Short): Short = (a * b).toShort() + override inline fun multiply(a: Short, k: Number): Short = (a * k.toShort()).toShort() override val one: Short = 1 override fun norm(arg: Short): Short = if (arg > 0) arg else (-arg).toShort() - override inline fun Short.unaryMinus() = (-this).toShort() + override inline fun Short.unaryMinus(): Short = (-this).toShort() - override inline fun Short.plus(b: Short) = (this + b).toShort() + override inline fun Short.plus(b: Short): Short = (this + b).toShort() - override inline fun Short.minus(b: Short) = (this - b).toShort() + override inline fun Short.minus(b: Short): Short = (this - b).toShort() - override inline fun Short.times(b: Short) = (this * b).toShort() + override inline fun Short.times(b: Short): Short = (this * b).toShort() } /** @@ -178,20 +179,20 @@ object ShortRing : Ring, Norm { @Suppress("EXTENSION_SHADOWED_BY_MEMBER", "OVERRIDE_BY_INLINE", "NOTHING_TO_INLINE") object ByteRing : Ring, Norm { override val zero: Byte = 0 - override inline fun add(a: Byte, b: Byte) = (a + b).toByte() - override inline fun multiply(a: Byte, b: Byte) = (a * b).toByte() - override inline fun multiply(a: Byte, k: Number) = (a * k.toByte()).toByte() + override inline fun add(a: Byte, b: Byte): Byte = (a + b).toByte() + override inline fun multiply(a: Byte, b: Byte): Byte = (a * b).toByte() + override inline fun multiply(a: Byte, k: Number): Byte = (a * k.toByte()).toByte() override val one: Byte = 1 override fun norm(arg: Byte): Byte = if (arg > 0) arg else (-arg).toByte() - override inline fun Byte.unaryMinus() = (-this).toByte() + override inline fun Byte.unaryMinus(): Byte = (-this).toByte() - override inline fun Byte.plus(b: Byte) = (this + b).toByte() + override inline fun Byte.plus(b: Byte): Byte = (this + b).toByte() - override inline fun Byte.minus(b: Byte) = (this - b).toByte() + override inline fun Byte.minus(b: Byte): Byte = (this - b).toByte() - override inline fun Byte.times(b: Byte) = (this * b).toByte() + override inline fun Byte.times(b: Byte): Byte = (this * b).toByte() } /** @@ -200,18 +201,18 @@ object ByteRing : Ring, Norm { @Suppress("EXTENSION_SHADOWED_BY_MEMBER", "OVERRIDE_BY_INLINE", "NOTHING_TO_INLINE") object LongRing : Ring, Norm { override val zero: Long = 0 - override inline fun add(a: Long, b: Long) = (a + b) - override inline fun multiply(a: Long, b: Long) = (a * b) - override inline fun multiply(a: Long, k: Number) = a * k.toLong() + override inline fun add(a: Long, b: Long): Long = (a + b) + override inline fun multiply(a: Long, b: Long): Long = (a * b) + override inline fun multiply(a: Long, k: Number): Long = a * k.toLong() override val one: Long = 1 override fun norm(arg: Long): Long = abs(arg) - override inline fun Long.unaryMinus() = (-this) + override inline fun Long.unaryMinus(): Long = (-this) - override inline fun Long.plus(b: Long) = (this + b) + override inline fun Long.plus(b: Long): Long = (this + b) - override inline fun Long.minus(b: Long) = (this - b) + override inline fun Long.minus(b: Long): Long = (this - b) - override inline fun Long.times(b: Long) = (this * b) + override inline fun Long.times(b: Long): Long = (this * b) } diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/OptionalOperations.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/OptionalOperations.kt index 709f0260f..75adb1dee 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/OptionalOperations.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/OptionalOperations.kt @@ -1,84 +1,210 @@ package scientifik.kmath.operations - -/* Trigonometric operations */ - /** - * A container for trigonometric operations for specific type. Trigonometric operations are limited to fields. + * A container for trigonometric operations for specific type. They are limited to semifields. * * The operations are not exposed to class directly to avoid method bloat but instead are declared in the field. - * It also allows to override behavior for optional operations - * + * It also allows to override behavior for optional operations. */ interface TrigonometricOperations : FieldOperations { + /** + * Computes the sine of [arg]. + */ fun sin(arg: T): T + + /** + * Computes the cosine of [arg]. + */ fun cos(arg: T): T + + /** + * Computes the tangent of [arg]. + */ fun tan(arg: T): T companion object { - const val SIN_OPERATION = "sin" - const val COS_OPERATION = "cos" - const val TAN_OPERATION = "tan" + /** + * The identifier of sine. + */ + const val SIN_OPERATION: String = "sin" + + /** + * The identifier of cosine. + */ + const val COS_OPERATION: String = "cos" + + /** + * The identifier of tangent. + */ + const val TAN_OPERATION: String = "tan" } } +/** + * A container for inverse trigonometric operations for specific type. They are limited to semifields. + * + * The operations are not exposed to class directly to avoid method bloat but instead are declared in the field. + * It also allows to override behavior for optional operations. + */ interface InverseTrigonometricOperations : TrigonometricOperations { + /** + * Computes the inverse sine of [arg]. + */ fun asin(arg: T): T + + /** + * Computes the inverse cosine of [arg]. + */ fun acos(arg: T): T + + /** + * Computes the inverse tangent of [arg]. + */ fun atan(arg: T): T companion object { - const val ASIN_OPERATION = "asin" - const val ACOS_OPERATION = "acos" - const val ATAN_OPERATION = "atan" + /** + * The identifier of inverse sine. + */ + const val ASIN_OPERATION: String = "asin" + + /** + * The identifier of inverse cosine. + */ + const val ACOS_OPERATION: String = "acos" + + /** + * The identifier of inverse tangent. + */ + const val ATAN_OPERATION: String = "atan" } } -fun >> sin(arg: T): T = arg.context.sin(arg) -fun >> cos(arg: T): T = arg.context.cos(arg) -fun >> tan(arg: T): T = arg.context.tan(arg) -fun >> asin(arg: T): T = arg.context.asin(arg) -fun >> acos(arg: T): T = arg.context.acos(arg) -fun >> atan(arg: T): T = arg.context.atan(arg) - -/* Power and roots */ - /** - * A context extension to include power operations like square roots, etc + * Computes the sine of [arg]. + */ +fun >> sin(arg: T): T = arg.context.sin(arg) + +/** + * Computes the cosine of [arg]. + */ +fun >> cos(arg: T): T = arg.context.cos(arg) + +/** + * Computes the tangent of [arg]. + */ +fun >> tan(arg: T): T = arg.context.tan(arg) + +/** + * Computes the inverse sine of [arg]. + */ +fun >> asin(arg: T): T = arg.context.asin(arg) + +/** + * Computes the inverse cosine of [arg]. + */ +fun >> acos(arg: T): T = arg.context.acos(arg) + +/** + * Computes the inverse tangent of [arg]. + */ +fun >> atan(arg: T): T = arg.context.atan(arg) + +/** + * A context extension to include power operations based on exponentiation. */ interface PowerOperations : Algebra { + /** + * Raises [arg] to the power [pow]. + */ fun power(arg: T, pow: Number): T - fun sqrt(arg: T) = power(arg, 0.5) - infix fun T.pow(pow: Number) = power(this, pow) + /** + * Computes the square root of the value [arg]. + */ + fun sqrt(arg: T): T = power(arg, 0.5) + + /** + * Raises this value to the power [pow]. + */ + infix fun T.pow(pow: Number): T = power(this, pow) companion object { - const val POW_OPERATION = "pow" - const val SQRT_OPERATION = "sqrt" + /** + * The identifier of exponentiation. + */ + const val POW_OPERATION: String = "pow" + + /** + * The identifier of square root. + */ + const val SQRT_OPERATION: String = "sqrt" } } +/** + * Raises [arg] to the power [pow]. + */ infix fun >> T.pow(power: Double): T = context.power(this, power) + +/** + * Computes the square root of the value [arg]. + */ fun >> sqrt(arg: T): T = arg pow 0.5 + +/** + * Computes the square of the value [arg]. + */ fun >> sqr(arg: T): T = arg pow 2.0 -/* Exponential */ - +/** + * A container for operations related to `exp` and `ln` functions. + */ interface ExponentialOperations : Algebra { + /** + * Computes Euler's number `e` raised to the power of the value [arg]. + */ fun exp(arg: T): T + + /** + * Computes the natural logarithm (base `e`) of the value [arg]. + */ fun ln(arg: T): T companion object { - const val EXP_OPERATION = "exp" - const val LN_OPERATION = "ln" + /** + * The identifier of exponential function. + */ + const val EXP_OPERATION: String = "exp" + + /** + * The identifier of natural logarithm. + */ + const val LN_OPERATION: String = "ln" } } +/** + * The identifier of exponential function. + */ fun >> exp(arg: T): T = arg.context.exp(arg) + +/** + * The identifier of natural logarithm. + */ fun >> ln(arg: T): T = arg.context.ln(arg) +/** + * A container for norm functional on element. + */ interface Norm { + /** + * Computes the norm of [arg] (i.e. absolute value or vector length). + */ fun norm(arg: T): R } +/** + * Computes the norm of [arg] (i.e. absolute value or vector length). + */ fun >, R> norm(arg: T): R = arg.context.norm(arg) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BoxingNDField.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BoxingNDField.kt index e6d4b226d..516f9a8cd 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BoxingNDField.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BoxingNDField.kt @@ -19,10 +19,10 @@ class BoxingNDField>( if (!elements.all { it.strides == this.strides }) error("Element strides are not the same as context strides") } - override val zero by lazy { produce { zero } } - override val one by lazy { produce { one } } + override val zero: BufferedNDFieldElement by lazy { produce { zero } } + override val one: BufferedNDFieldElement by lazy { produce { one } } - override fun produce(initializer: F.(IntArray) -> T) = + override fun produce(initializer: F.(IntArray) -> T): BufferedNDFieldElement = BufferedNDFieldElement( this, buildBuffer(strides.linearSize) { offset -> elementContext.initializer(strides.index(offset)) }) @@ -79,4 +79,4 @@ inline fun , R> F.nd( ): R { val ndfield: BoxingNDField = NDField.boxing(this, *shape, bufferFactory = bufferFactory) return ndfield.action() -} \ No newline at end of file +} diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BoxingNDRing.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BoxingNDRing.kt index 39fc555e8..f7be95736 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BoxingNDRing.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BoxingNDRing.kt @@ -18,10 +18,10 @@ class BoxingNDRing>( if (!elements.all { it.strides == this.strides }) error("Element strides are not the same as context strides") } - override val zero by lazy { produce { zero } } - override val one by lazy { produce { one } } + override val zero: BufferedNDRingElement by lazy { produce { zero } } + override val one: BufferedNDRingElement by lazy { produce { one } } - override fun produce(initializer: R.(IntArray) -> T) = + override fun produce(initializer: R.(IntArray) -> T): BufferedNDRingElement = BufferedNDRingElement( this, buildBuffer(strides.linearSize) { offset -> elementContext.initializer(strides.index(offset)) }) @@ -69,4 +69,4 @@ class BoxingNDRing>( override fun NDBuffer.toElement(): RingElement, *, out BufferedNDRing> = BufferedNDRingElement(this@BoxingNDRing, buffer) -} \ No newline at end of file +} diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BufferAccessor2D.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BufferAccessor2D.kt index b14da5d99..00832b69c 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BufferAccessor2D.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BufferAccessor2D.kt @@ -7,16 +7,16 @@ import kotlin.reflect.KClass */ class BufferAccessor2D(val type: KClass, val rowNum: Int, val colNum: Int) { - operator fun Buffer.get(i: Int, j: Int) = get(i + colNum * j) + operator fun Buffer.get(i: Int, j: Int): T = get(i + colNum * j) operator fun MutableBuffer.set(i: Int, j: Int, value: T) { set(i + colNum * j, value) } - inline fun create(init: (i: Int, j: Int) -> T) = + inline fun create(init: (i: Int, j: Int) -> T): MutableBuffer = MutableBuffer.auto(type, rowNum * colNum) { offset -> init(offset / colNum, offset % colNum) } - fun create(mat: Structure2D) = create { i, j -> mat[i, j] } + fun create(mat: Structure2D): MutableBuffer = create { i, j -> mat[i, j] } //TODO optimize wrapper fun MutableBuffer.collect(): Structure2D = @@ -41,5 +41,5 @@ class BufferAccessor2D(val type: KClass, val rowNum: Int, val colNum /** * Get row */ - fun MutableBuffer.row(i: Int) = Row(this, i) + fun MutableBuffer.row(i: Int): Row = Row(this, i) } diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BufferedNDAlgebra.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BufferedNDAlgebra.kt index 9742f3662..baf1a05a6 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BufferedNDAlgebra.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BufferedNDAlgebra.kt @@ -2,7 +2,7 @@ package scientifik.kmath.structures import scientifik.kmath.operations.* -interface BufferedNDAlgebra: NDAlgebra>{ +interface BufferedNDAlgebra : NDAlgebra> { val strides: Strides override fun check(vararg elements: NDBuffer) { @@ -30,7 +30,7 @@ interface BufferedNDAlgebra: NDAlgebra>{ } -interface BufferedNDSpace> : NDSpace>, BufferedNDAlgebra { +interface BufferedNDSpace> : NDSpace>, BufferedNDAlgebra { override fun NDBuffer.toElement(): SpaceElement, *, out BufferedNDSpace> } diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BufferedNDElement.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BufferedNDElement.kt index be80f66bf..d1d622b23 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BufferedNDElement.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BufferedNDElement.kt @@ -8,7 +8,7 @@ import scientifik.kmath.operations.* abstract class BufferedNDElement : NDBuffer(), NDElement> { abstract override val context: BufferedNDAlgebra - override val strides get() = context.strides + override val strides: Strides get() = context.strides override val shape: IntArray get() = context.shape } @@ -54,9 +54,9 @@ class BufferedNDFieldElement>( /** - * Element by element application of any operation on elements to the whole array. Just like in numpy + * Element by element application of any operation on elements to the whole array. Just like in numpy. */ -operator fun > Function1.invoke(ndElement: BufferedNDElement) = +operator fun > Function1.invoke(ndElement: BufferedNDElement): MathElement> = ndElement.context.run { map(ndElement) { invoke(it) }.toElement() } /* plus and minus */ @@ -64,13 +64,13 @@ operator fun > Function1.invoke(ndElement: BufferedN /** * Summation operation for [BufferedNDElement] and single element */ -operator fun > BufferedNDElement.plus(arg: T) = +operator fun > BufferedNDElement.plus(arg: T): NDElement> = context.map(this) { it + arg }.wrap() /** * Subtraction operation between [BufferedNDElement] and single element */ -operator fun > BufferedNDElement.minus(arg: T) = +operator fun > BufferedNDElement.minus(arg: T): NDElement> = context.map(this) { it - arg }.wrap() /* prod and div */ @@ -78,11 +78,11 @@ operator fun > BufferedNDElement.minus(arg: T) = /** * Product operation for [BufferedNDElement] and single element */ -operator fun > BufferedNDElement.times(arg: T) = +operator fun > BufferedNDElement.times(arg: T): NDElement> = context.map(this) { it * arg }.wrap() /** * Division operation between [BufferedNDElement] and single element */ -operator fun > BufferedNDElement.div(arg: T) = - context.map(this) { it / arg }.wrap() \ No newline at end of file +operator fun > BufferedNDElement.div(arg: T): NDElement> = + context.map(this) { it / arg }.wrap() diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/Buffers.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/Buffers.kt index 5789de9ee..723348e3f 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/Buffers.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/Buffers.kt @@ -15,22 +15,22 @@ typealias MutableBufferFactory = (Int, (Int) -> T) -> MutableBuffer interface Buffer { /** - * The size of the buffer + * The size of this buffer. */ val size: Int /** - * Get element at given index + * Gets element at given index. */ operator fun get(index: Int): T /** - * Iterate over all elements + * Iterates over all elements. */ operator fun iterator(): Iterator /** - * Check content eqiality with another buffer + * Checks content equality with another buffer. */ fun contentEquals(other: Buffer<*>): Boolean = asSequence().mapIndexed { index, value -> value == other[index] }.all { it } @@ -124,10 +124,9 @@ inline class ListBuffer(val list: List) : Buffer { override fun iterator(): Iterator = list.iterator() } -fun List.asBuffer() = ListBuffer(this) +fun List.asBuffer(): ListBuffer = ListBuffer(this) -@Suppress("FunctionName") -inline fun ListBuffer(size: Int, init: (Int) -> T) = List(size, init).asBuffer() +inline fun ListBuffer(size: Int, init: (Int) -> T): ListBuffer = List(size, init).asBuffer() inline class MutableListBuffer(val list: MutableList) : MutableBuffer { @@ -165,13 +164,13 @@ fun Array.asBuffer(): ArrayBuffer = ArrayBuffer(this) inline class ReadOnlyBuffer(val buffer: MutableBuffer) : Buffer { override val size: Int get() = buffer.size - override fun get(index: Int): T = buffer.get(index) + override fun get(index: Int): T = buffer[index] - override fun iterator() = buffer.iterator() + override fun iterator(): Iterator = buffer.iterator() } /** - * A buffer with content calculated on-demand. The calculated contect is not stored, so it is recalculated on each call. + * A buffer with content calculated on-demand. The calculated content is not stored, so it is recalculated on each call. * Useful when one needs single element from the buffer. */ class VirtualBuffer(override val size: Int, private val generator: (Int) -> T) : Buffer { @@ -205,4 +204,4 @@ fun Buffer.asReadOnly(): Buffer = if (this is MutableBuffer) { */ typealias BufferTransform = (Buffer) -> Buffer -typealias SuspendBufferTransform = suspend (Buffer) -> Buffer \ No newline at end of file +typealias SuspendBufferTransform = suspend (Buffer) -> Buffer diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ComplexNDField.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ComplexNDField.kt index c7e672c28..2f7d20b6d 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ComplexNDField.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ComplexNDField.kt @@ -17,8 +17,8 @@ class ComplexNDField(override val shape: IntArray) : override val strides: Strides = DefaultStrides(shape) override val elementContext: ComplexField get() = ComplexField - override val zero by lazy { produce { zero } } - override val one by lazy { produce { one } } + override val zero: ComplexNDElement by lazy { produce { zero } } + override val one: ComplexNDElement by lazy { produce { one } } inline fun buildBuffer(size: Int, crossinline initializer: (Int) -> Complex): Buffer = Buffer.complex(size) { initializer(it) } @@ -69,23 +69,23 @@ class ComplexNDField(override val shape: IntArray) : override fun NDBuffer.toElement(): FieldElement, *, out BufferedNDField> = BufferedNDFieldElement(this@ComplexNDField, buffer) - override fun power(arg: NDBuffer, pow: Number) = map(arg) { power(it, pow) } + override fun power(arg: NDBuffer, pow: Number): ComplexNDElement = map(arg) { power(it, pow) } - override fun exp(arg: NDBuffer) = map(arg) { exp(it) } + override fun exp(arg: NDBuffer): ComplexNDElement = map(arg) { exp(it) } - override fun ln(arg: NDBuffer) = map(arg) { ln(it) } + override fun ln(arg: NDBuffer): ComplexNDElement = map(arg) { ln(it) } - override fun sin(arg: NDBuffer) = map(arg) { sin(it) } + override fun sin(arg: NDBuffer): ComplexNDElement = map(arg) { sin(it) } - override fun cos(arg: NDBuffer) = map(arg) { cos(it) } + override fun cos(arg: NDBuffer): ComplexNDElement = map(arg) { cos(it) } - override fun tan(arg: NDBuffer): NDBuffer = map(arg) { tan(it) } + override fun tan(arg: NDBuffer): ComplexNDElement = map(arg) { tan(it) } - override fun asin(arg: NDBuffer): NDBuffer = map(arg) { asin(it) } + override fun asin(arg: NDBuffer): ComplexNDElement = map(arg) { asin(it) } - override fun acos(arg: NDBuffer): NDBuffer = map(arg) {acos(it)} + override fun acos(arg: NDBuffer): ComplexNDElement = map(arg) { acos(it) } - override fun atan(arg: NDBuffer): NDBuffer = map(arg) {atan(it)} + override fun atan(arg: NDBuffer): ComplexNDElement = map(arg) { atan(it) } } @@ -100,7 +100,7 @@ inline fun BufferedNDField.produceInline(crossinline init /** * Map one [ComplexNDElement] using function with indexes */ -inline fun ComplexNDElement.mapIndexed(crossinline transform: ComplexField.(index: IntArray, Complex) -> Complex) = +inline fun ComplexNDElement.mapIndexed(crossinline transform: ComplexField.(index: IntArray, Complex) -> Complex): ComplexNDElement = context.produceInline { offset -> transform(strides.index(offset), buffer[offset]) } /** @@ -114,7 +114,7 @@ inline fun ComplexNDElement.map(crossinline transform: ComplexField.(Complex) -> /** * Element by element application of any operation on elements to the whole array. Just like in numpy */ -operator fun Function1.invoke(ndElement: ComplexNDElement) = +operator fun Function1.invoke(ndElement: ComplexNDElement): ComplexNDElement = ndElement.map { this@invoke(it) } @@ -123,19 +123,18 @@ operator fun Function1.invoke(ndElement: ComplexNDElement) = /** * Summation operation for [BufferedNDElement] and single element */ -operator fun ComplexNDElement.plus(arg: Complex) = - map { it + arg } +operator fun ComplexNDElement.plus(arg: Complex): ComplexNDElement = map { it + arg } /** * Subtraction operation between [BufferedNDElement] and single element */ -operator fun ComplexNDElement.minus(arg: Complex) = +operator fun ComplexNDElement.minus(arg: Complex): ComplexNDElement = map { it - arg } -operator fun ComplexNDElement.plus(arg: Double) = +operator fun ComplexNDElement.plus(arg: Double): ComplexNDElement = map { it + arg } -operator fun ComplexNDElement.minus(arg: Double) = +operator fun ComplexNDElement.minus(arg: Double): ComplexNDElement = map { it - arg } fun NDField.Companion.complex(vararg shape: Int): ComplexNDField = ComplexNDField(shape) @@ -148,4 +147,4 @@ fun NDElement.Companion.complex(vararg shape: Int, initializer: ComplexField.(In */ inline fun ComplexField.nd(vararg shape: Int, action: ComplexNDField.() -> R): R { return NDField.complex(*shape).run(action) -} \ No newline at end of file +} diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ExtendedNDField.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ExtendedNDField.kt index 776cff880..1fe449632 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ExtendedNDField.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ExtendedNDField.kt @@ -4,7 +4,6 @@ import scientifik.kmath.operations.ExtendedField interface ExtendedNDField, N : NDStructure> : NDField, ExtendedField - ///** // * NDField that supports [ExtendedField] operations on its elements // */ @@ -36,5 +35,3 @@ interface ExtendedNDField, N : NDStructure> : N // return produce { with(elementContext) { cos(arg[it]) } } // } //} - - diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/FlaggedBuffer.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/FlaggedBuffer.kt index 749e4eeec..2382e2c57 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/FlaggedBuffer.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/FlaggedBuffer.kt @@ -19,11 +19,11 @@ interface FlaggedBuffer : Buffer { /** * The value is valid if all flags are down */ -fun FlaggedBuffer<*>.isValid(index: Int) = getFlag(index) != 0.toByte() +fun FlaggedBuffer<*>.isValid(index: Int): Boolean = getFlag(index) != 0.toByte() -fun FlaggedBuffer<*>.hasFlag(index: Int, flag: ValueFlag) = (getFlag(index) and flag.mask) != 0.toByte() +fun FlaggedBuffer<*>.hasFlag(index: Int, flag: ValueFlag): Boolean = (getFlag(index) and flag.mask) != 0.toByte() -fun FlaggedBuffer<*>.isMissing(index: Int) = hasFlag(index, ValueFlag.MISSING) +fun FlaggedBuffer<*>.isMissing(index: Int): Boolean = hasFlag(index, ValueFlag.MISSING) /** * A real buffer which supports flags for each value like NaN or Missing @@ -45,9 +45,9 @@ class FlaggedRealBuffer(val values: DoubleArray, val flags: ByteArray) : Flagged } inline fun FlaggedRealBuffer.forEachValid(block: (Double) -> Unit) { - for(i in indices){ - if(isValid(i)){ + for (i in indices) { + if (isValid(i)) { block(values[i]) } } -} \ No newline at end of file +} diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/IntBuffer.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/IntBuffer.kt index a354c5de0..b277374ab 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/IntBuffer.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/IntBuffer.kt @@ -9,12 +9,11 @@ inline class IntBuffer(val array: IntArray) : MutableBuffer { array[index] = value } - override fun iterator() = array.iterator() + override fun iterator(): IntIterator = array.iterator() override fun copy(): MutableBuffer = IntBuffer(array.copyOf()) } - -fun IntArray.asBuffer() = IntBuffer(this) \ No newline at end of file +fun IntArray.asBuffer(): IntBuffer = IntBuffer(this) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/LongBuffer.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/LongBuffer.kt index fa6229a71..3ee2e75a2 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/LongBuffer.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/LongBuffer.kt @@ -9,11 +9,11 @@ inline class LongBuffer(val array: LongArray) : MutableBuffer { array[index] = value } - override fun iterator() = array.iterator() + override fun iterator(): LongIterator = array.iterator() override fun copy(): MutableBuffer = LongBuffer(array.copyOf()) } -fun LongArray.asBuffer() = LongBuffer(this) \ No newline at end of file +fun LongArray.asBuffer(): LongBuffer = LongBuffer(this) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/MemoryBuffer.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/MemoryBuffer.kt index a09f09165..b15487114 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/MemoryBuffer.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/MemoryBuffer.kt @@ -3,7 +3,7 @@ package scientifik.kmath.structures import scientifik.memory.* /** - * A non-boxing buffer based on [ByteBuffer] storage + * A non-boxing buffer over [Memory] object. */ open class MemoryBuffer(protected val memory: Memory, protected val spec: MemorySpec) : Buffer { @@ -17,7 +17,7 @@ open class MemoryBuffer(protected val memory: Memory, protected val spe companion object { - fun create(spec: MemorySpec, size: Int) = + fun create(spec: MemorySpec, size: Int): MemoryBuffer = MemoryBuffer(Memory.allocate(size * spec.objectSize), spec) inline fun create( @@ -36,25 +36,25 @@ open class MemoryBuffer(protected val memory: Memory, protected val spe class MutableMemoryBuffer(memory: Memory, spec: MemorySpec) : MemoryBuffer(memory, spec), MutableBuffer { - private val writer = memory.writer() + private val writer: MemoryWriter = memory.writer() - override fun set(index: Int, value: T) = writer.write(spec, spec.objectSize * index, value) + override fun set(index: Int, value: T): Unit = writer.write(spec, spec.objectSize * index, value) override fun copy(): MutableBuffer = MutableMemoryBuffer(memory.copy(), spec) companion object { - fun create(spec: MemorySpec, size: Int) = + fun create(spec: MemorySpec, size: Int): MutableMemoryBuffer = MutableMemoryBuffer(Memory.allocate(size * spec.objectSize), spec) inline fun create( spec: MemorySpec, size: Int, crossinline initializer: (Int) -> T - ) = + ): MutableMemoryBuffer = MutableMemoryBuffer(Memory.allocate(size * spec.objectSize), spec).also { buffer -> (0 until size).forEach { buffer[it] = initializer(it) } } } -} \ No newline at end of file +} diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/NDAlgebra.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/NDAlgebra.kt index c826565cf..750133351 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/NDAlgebra.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/NDAlgebra.kt @@ -56,7 +56,7 @@ interface NDAlgebra> { /** * element-by-element invoke a function working on [T] on a [NDStructure] */ - operator fun Function1.invoke(structure: N) = map(structure) { value -> this@invoke(value) } + operator fun Function1.invoke(structure: N): N = map(structure) { value -> this@invoke(value) } companion object } @@ -76,12 +76,12 @@ interface NDSpace, N : NDStructure> : Space, NDAlgebra add(arg, value) } + operator fun N.plus(arg: T): N = map(this) { value -> add(arg, value) } - operator fun N.minus(arg: T) = map(this) { value -> add(arg, -value) } + operator fun N.minus(arg: T): N = map(this) { value -> add(arg, -value) } - operator fun T.plus(arg: N) = map(arg) { value -> add(this@plus, value) } - operator fun T.minus(arg: N) = map(arg) { value -> add(-this@minus, value) } + operator fun T.plus(arg: N): N = map(arg) { value -> add(this@plus, value) } + operator fun T.minus(arg: N): N = map(arg) { value -> add(-this@minus, value) } companion object } @@ -97,20 +97,18 @@ interface NDRing, N : NDStructure> : Ring, NDSpace override fun multiply(a: N, b: N): N = combine(a, b) { aValue, bValue -> multiply(aValue, bValue) } //TODO move to extensions after KEEP-176 - operator fun N.times(arg: T) = map(this) { value -> multiply(arg, value) } + operator fun N.times(arg: T): N = map(this) { value -> multiply(arg, value) } - operator fun T.times(arg: N) = map(arg) { value -> multiply(this@times, value) } + operator fun T.times(arg: N): N = map(arg) { value -> multiply(this@times, value) } companion object } /** * Field for n-dimensional structures. - * @param shape - the list of dimensions of the array - * @param elementField - operations field defined on individual array element + * * @param T - the type of the element contained in ND structure * @param F - field of structure elements - * @param R - actual nd-element type of this field */ interface NDField, N : NDStructure> : Field, NDRing { @@ -120,9 +118,9 @@ interface NDField, N : NDStructure> : Field, NDRing divide(aValue, bValue) } //TODO move to extensions after KEEP-176 - operator fun N.div(arg: T) = map(this) { value -> divide(arg, value) } + operator fun N.div(arg: T): N = map(this) { value -> divide(arg, value) } - operator fun T.div(arg: N) = map(arg) { divide(it, this@div) } + operator fun T.div(arg: N): N = map(arg) { divide(it, this@div) } companion object { @@ -131,7 +129,7 @@ interface NDField, N : NDStructure> : Field, NDRing, N : NDStructure> : Field, NDRing = Buffer.Companion::boxing - ) = BoxingNDField(shape, field, bufferFactory) + ): BoxingNDField = BoxingNDField(shape, field, bufferFactory) /** * Create a most suitable implementation for nd-field using reified class. diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/NDElement.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/NDElement.kt index a18a03364..9dfe2b5a8 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/NDElement.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/NDElement.kt @@ -23,19 +23,23 @@ interface NDElement> : NDStructure { /** * Create a optimized NDArray of doubles */ - fun real(shape: IntArray, initializer: RealField.(IntArray) -> Double = { 0.0 }) = + fun real(shape: IntArray, initializer: RealField.(IntArray) -> Double = { 0.0 }): RealNDElement = NDField.real(*shape).produce(initializer) - fun real1D(dim: Int, initializer: (Int) -> Double = { _ -> 0.0 }) = + fun real1D(dim: Int, initializer: (Int) -> Double = { _ -> 0.0 }): RealNDElement = real(intArrayOf(dim)) { initializer(it[0]) } - fun real2D(dim1: Int, dim2: Int, initializer: (Int, Int) -> Double = { _, _ -> 0.0 }) = + fun real2D(dim1: Int, dim2: Int, initializer: (Int, Int) -> Double = { _, _ -> 0.0 }): RealNDElement = real(intArrayOf(dim1, dim2)) { initializer(it[0], it[1]) } - fun real3D(dim1: Int, dim2: Int, dim3: Int, initializer: (Int, Int, Int) -> Double = { _, _, _ -> 0.0 }) = - real(intArrayOf(dim1, dim2, dim3)) { initializer(it[0], it[1], it[2]) } + fun real3D( + dim1: Int, + dim2: Int, + dim3: Int, + initializer: (Int, Int, Int) -> Double = { _, _, _ -> 0.0 } + ): RealNDElement = real(intArrayOf(dim1, dim2, dim3)) { initializer(it[0], it[1], it[2]) } /** @@ -62,16 +66,17 @@ interface NDElement> : NDStructure { } -fun > NDElement.mapIndexed(transform: C.(index: IntArray, T) -> T) = +fun > NDElement.mapIndexed(transform: C.(index: IntArray, T) -> T): NDElement = context.mapIndexed(unwrap(), transform).wrap() -fun > NDElement.map(transform: C.(T) -> T) = context.map(unwrap(), transform).wrap() +fun > NDElement.map(transform: C.(T) -> T): NDElement = + context.map(unwrap(), transform).wrap() /** * Element by element application of any operation on elements to the whole [NDElement] */ -operator fun > Function1.invoke(ndElement: NDElement) = +operator fun > Function1.invoke(ndElement: NDElement): NDElement = ndElement.map { value -> this@invoke(value) } /* plus and minus */ @@ -79,13 +84,13 @@ operator fun > Function1.invoke(ndElement: NDElem /** * Summation operation for [NDElement] and single element */ -operator fun , N : NDStructure> NDElement.plus(arg: T) = +operator fun , N : NDStructure> NDElement.plus(arg: T): NDElement = map { value -> arg + value } /** * Subtraction operation between [NDElement] and single element */ -operator fun , N : NDStructure> NDElement.minus(arg: T) = +operator fun , N : NDStructure> NDElement.minus(arg: T): NDElement = map { value -> arg - value } /* prod and div */ @@ -93,13 +98,13 @@ operator fun , N : NDStructure> NDElement.minus(arg: /** * Product operation for [NDElement] and single element */ -operator fun , N : NDStructure> NDElement.times(arg: T) = +operator fun , N : NDStructure> NDElement.times(arg: T): NDElement = map { value -> arg * value } /** * Division operation between [NDElement] and single element */ -operator fun , N : NDStructure> NDElement.div(arg: T) = +operator fun , N : NDStructure> NDElement.div(arg: T): NDElement = map { value -> arg / value } diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/NDStructure.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/NDStructure.kt index 236a6f366..bcd74ed2a 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/NDStructure.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/NDStructure.kt @@ -8,7 +8,7 @@ interface NDStructure { val shape: IntArray - val dimension get() = shape.size + val dimension: Int get() = shape.size operator fun get(index: IntArray): T @@ -44,32 +44,49 @@ interface NDStructure { strides: Strides, bufferFactory: BufferFactory = Buffer.Companion::boxing, initializer: (IntArray) -> T - ) = + ): BufferNDStructure = BufferNDStructure(strides, bufferFactory(strides.linearSize) { i -> initializer(strides.index(i)) }) /** * Inline create NDStructure with non-boxing buffer implementation if it is possible */ - inline fun auto(strides: Strides, crossinline initializer: (IntArray) -> T) = + inline fun auto( + strides: Strides, + crossinline initializer: (IntArray) -> T + ): BufferNDStructure = BufferNDStructure(strides, Buffer.auto(strides.linearSize) { i -> initializer(strides.index(i)) }) - inline fun auto(type: KClass, strides: Strides, crossinline initializer: (IntArray) -> T) = + inline fun auto( + type: KClass, + strides: Strides, + crossinline initializer: (IntArray) -> T + ): BufferNDStructure = BufferNDStructure(strides, Buffer.auto(type, strides.linearSize) { i -> initializer(strides.index(i)) }) fun build( shape: IntArray, bufferFactory: BufferFactory = Buffer.Companion::boxing, initializer: (IntArray) -> T - ) = build(DefaultStrides(shape), bufferFactory, initializer) + ): BufferNDStructure = build(DefaultStrides(shape), bufferFactory, initializer) - inline fun auto(shape: IntArray, crossinline initializer: (IntArray) -> T) = + inline fun auto( + shape: IntArray, + crossinline initializer: (IntArray) -> T + ): BufferNDStructure = auto(DefaultStrides(shape), initializer) @JvmName("autoVarArg") - inline fun auto(vararg shape: Int, crossinline initializer: (IntArray) -> T) = + inline fun auto( + vararg shape: Int, + crossinline initializer: (IntArray) -> T + ): BufferNDStructure = auto(DefaultStrides(shape), initializer) - inline fun auto(type: KClass, vararg shape: Int, crossinline initializer: (IntArray) -> T) = + inline fun auto( + type: KClass, + vararg shape: Int, + crossinline initializer: (IntArray) -> T + ): BufferNDStructure = auto(type, DefaultStrides(shape), initializer) } } @@ -128,7 +145,7 @@ class DefaultStrides private constructor(override val shape: IntArray) : Strides /** * Strides for memory access */ - override val strides by lazy { + override val strides: List by lazy { sequence { var current = 1 yield(1) @@ -238,7 +255,7 @@ inline fun NDStructure.mapToBuffer( } /** - * Mutable ND buffer based on linear [autoBuffer] + * Mutable ND buffer based on linear [MutableBuffer]. */ class MutableBufferNDStructure( override val strides: Strides, @@ -251,7 +268,7 @@ class MutableBufferNDStructure( } } - override fun set(index: IntArray, value: T) = buffer.set(strides.offset(index), value) + override fun set(index: IntArray, value: T): Unit = buffer.set(strides.offset(index), value) } inline fun NDStructure.combine( @@ -260,4 +277,4 @@ inline fun NDStructure.combine( ): NDStructure { if (!this.shape.contentEquals(struct.shape)) error("Shape mismatch in structure combination") return NDStructure.auto(shape) { block(this[it], struct[it]) } -} \ No newline at end of file +} diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/RealBuffer.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/RealBuffer.kt index f48ace3a9..20187fe19 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/RealBuffer.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/RealBuffer.kt @@ -9,7 +9,7 @@ inline class RealBuffer(val array: DoubleArray) : MutableBuffer { array[index] = value } - override fun iterator() = array.iterator() + override fun iterator(): DoubleIterator = array.iterator() override fun copy(): MutableBuffer = RealBuffer(array.copyOf()) @@ -31,4 +31,4 @@ val MutableBuffer.array: DoubleArray DoubleArray(size) { get(it) } } -fun DoubleArray.asBuffer() = RealBuffer(this) \ No newline at end of file +fun DoubleArray.asBuffer(): RealBuffer = RealBuffer(this) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/RealNDField.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/RealNDField.kt index 8c90f90c7..b89f28233 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/RealNDField.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/RealNDField.kt @@ -12,8 +12,8 @@ class RealNDField(override val shape: IntArray) : override val strides: Strides = DefaultStrides(shape) override val elementContext: RealField get() = RealField - override val zero by lazy { produce { zero } } - override val one by lazy { produce { one } } + override val zero: RealNDElement by lazy { produce { zero } } + override val one: RealNDElement by lazy { produce { one } } inline fun buildBuffer(size: Int, crossinline initializer: (Int) -> Double): Buffer = RealBuffer(DoubleArray(size) { initializer(it) }) @@ -64,15 +64,15 @@ class RealNDField(override val shape: IntArray) : override fun NDBuffer.toElement(): FieldElement, *, out BufferedNDField> = BufferedNDFieldElement(this@RealNDField, buffer) - override fun power(arg: NDBuffer, pow: Number) = map(arg) { power(it, pow) } + override fun power(arg: NDBuffer, pow: Number): RealNDElement = map(arg) { power(it, pow) } - override fun exp(arg: NDBuffer) = map(arg) { exp(it) } + override fun exp(arg: NDBuffer): RealNDElement = map(arg) { exp(it) } - override fun ln(arg: NDBuffer) = map(arg) { ln(it) } + override fun ln(arg: NDBuffer): RealNDElement = map(arg) { ln(it) } - override fun sin(arg: NDBuffer) = map(arg) { sin(it) } + override fun sin(arg: NDBuffer): RealNDElement = map(arg) { sin(it) } - override fun cos(arg: NDBuffer) = map(arg) { cos(it) } + override fun cos(arg: NDBuffer): RealNDElement = map(arg) { cos(it) } override fun tan(arg: NDBuffer): NDBuffer = map(arg) { tan(it) } @@ -95,7 +95,7 @@ inline fun BufferedNDField.produceInline(crossinline initiali /** * Map one [RealNDElement] using function with indexes */ -inline fun RealNDElement.mapIndexed(crossinline transform: RealField.(index: IntArray, Double) -> Double) = +inline fun RealNDElement.mapIndexed(crossinline transform: RealField.(index: IntArray, Double) -> Double): RealNDElement = context.produceInline { offset -> transform(strides.index(offset), buffer[offset]) } /** @@ -107,9 +107,9 @@ inline fun RealNDElement.map(crossinline transform: RealField.(Double) -> Double } /** - * Element by element application of any operation on elements to the whole array. Just like in numpy + * Element by element application of any operation on elements to the whole array. Just like in numpy. */ -operator fun Function1.invoke(ndElement: RealNDElement) = +operator fun Function1.invoke(ndElement: RealNDElement): RealNDElement = ndElement.map { this@invoke(it) } @@ -118,13 +118,13 @@ operator fun Function1.invoke(ndElement: RealNDElement) = /** * Summation operation for [BufferedNDElement] and single element */ -operator fun RealNDElement.plus(arg: Double) = +operator fun RealNDElement.plus(arg: Double): RealNDElement = map { it + arg } /** * Subtraction operation between [BufferedNDElement] and single element */ -operator fun RealNDElement.minus(arg: Double) = +operator fun RealNDElement.minus(arg: Double): RealNDElement = map { it - arg } /** @@ -132,4 +132,4 @@ operator fun RealNDElement.minus(arg: Double) = */ inline fun RealField.nd(vararg shape: Int, action: RealNDField.() -> R): R { return NDField.real(*shape).run(action) -} \ No newline at end of file +} diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ShortBuffer.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ShortBuffer.kt index f4b2f7d13..24c94a885 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ShortBuffer.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ShortBuffer.kt @@ -9,12 +9,11 @@ inline class ShortBuffer(val array: ShortArray) : MutableBuffer { array[index] = value } - override fun iterator() = array.iterator() + override fun iterator(): ShortIterator = array.iterator() override fun copy(): MutableBuffer = ShortBuffer(array.copyOf()) } - -fun ShortArray.asBuffer() = ShortBuffer(this) \ No newline at end of file +fun ShortArray.asBuffer(): ShortBuffer = ShortBuffer(this) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ShortNDRing.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ShortNDRing.kt index 6b09c91de..f404a2a27 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ShortNDRing.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ShortNDRing.kt @@ -12,8 +12,8 @@ class ShortNDRing(override val shape: IntArray) : override val strides: Strides = DefaultStrides(shape) override val elementContext: ShortRing get() = ShortRing - override val zero by lazy { produce { ShortRing.zero } } - override val one by lazy { produce { ShortRing.one } } + override val zero: ShortNDElement by lazy { produce { zero } } + override val one: ShortNDElement by lazy { produce { one } } inline fun buildBuffer(size: Int, crossinline initializer: (Int) -> Short): Buffer = ShortBuffer(ShortArray(size) { initializer(it) }) @@ -40,6 +40,7 @@ class ShortNDRing(override val shape: IntArray) : transform: ShortRing.(index: IntArray, Short) -> Short ): ShortNDElement { check(arg) + return BufferedNDRingElement( this, buildBuffer(arg.strides.linearSize) { offset -> @@ -67,7 +68,7 @@ class ShortNDRing(override val shape: IntArray) : /** - * Fast element production using function inlining + * Fast element production using function inlining. */ inline fun BufferedNDRing.produceInline(crossinline initializer: ShortRing.(Int) -> Short): ShortNDElement { val array = ShortArray(strides.linearSize) { offset -> ShortRing.initializer(offset) } @@ -75,22 +76,22 @@ inline fun BufferedNDRing.produceInline(crossinline initialize } /** - * Element by element application of any operation on elements to the whole array. Just like in numpy + * Element by element application of any operation on elements to the whole array. */ -operator fun Function1.invoke(ndElement: ShortNDElement) = +operator fun Function1.invoke(ndElement: ShortNDElement): ShortNDElement = ndElement.context.produceInline { i -> invoke(ndElement.buffer[i]) } /* plus and minus */ /** - * Summation operation for [StridedNDFieldElement] and single element + * Summation operation for [ShortNDElement] and single element. */ -operator fun ShortNDElement.plus(arg: Short) = +operator fun ShortNDElement.plus(arg: Short): ShortNDElement = context.produceInline { i -> (buffer[i] + arg).toShort() } /** - * Subtraction operation between [StridedNDFieldElement] and single element + * Subtraction operation between [ShortNDElement] and single element. */ -operator fun ShortNDElement.minus(arg: Short) = - context.produceInline { i -> (buffer[i] - arg).toShort() } \ No newline at end of file +operator fun ShortNDElement.minus(arg: Short): ShortNDElement = + context.produceInline { i -> (buffer[i] - arg).toShort() } diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/Structure1D.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/Structure1D.kt index 9974538ec..faf022367 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/Structure1D.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/Structure1D.kt @@ -17,7 +17,7 @@ interface Structure1D : NDStructure, Buffer { /** * A 1D wrapper for nd-structure */ -private inline class Structure1DWrapper(val structure: NDStructure) : Structure1D{ +private inline class Structure1DWrapper(val structure: NDStructure) : Structure1D { override val shape: IntArray get() = structure.shape override val size: Int get() = structure.shape[0] @@ -39,14 +39,14 @@ private inline class Buffer1DWrapper(val buffer: Buffer) : Structure1D override fun elements(): Sequence> = asSequence().mapIndexed { index, value -> intArrayOf(index) to value } - override fun get(index: Int): T = buffer.get(index) + override fun get(index: Int): T = buffer[index] } /** * Represent a [NDStructure] as [Structure1D]. Throw error in case of dimension mismatch */ fun NDStructure.as1D(): Structure1D = if (shape.size == 1) { - if( this is NDBuffer){ + if (this is NDBuffer) { Buffer1DWrapper(this.buffer) } else { Structure1DWrapper(this) @@ -59,4 +59,4 @@ fun NDStructure.as1D(): Structure1D = if (shape.size == 1) { /** * Represent this buffer as 1D structure */ -fun Buffer.asND(): Structure1D = Buffer1DWrapper(this) \ No newline at end of file +fun Buffer.asND(): Structure1D = Buffer1DWrapper(this) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/Structure2D.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/Structure2D.kt index bfaf8338d..30fd556d3 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/Structure2D.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/Structure2D.kt @@ -32,9 +32,7 @@ interface Structure2D : NDStructure { } } - companion object { - - } + companion object } /** @@ -57,4 +55,4 @@ fun NDStructure.as2D(): Structure2D = if (shape.size == 2) { error("Can't create 2d-structure from ${shape.size}d-structure") } -typealias Matrix = Structure2D \ No newline at end of file +typealias Matrix = Structure2D diff --git a/kmath-core/src/commonTest/kotlin/scientifik/kmath/expressions/ExpressionFieldTest.kt b/kmath-core/src/commonTest/kotlin/scientifik/kmath/expressions/ExpressionFieldTest.kt index 9eae60efc..22b924ef9 100644 --- a/kmath-core/src/commonTest/kotlin/scientifik/kmath/expressions/ExpressionFieldTest.kt +++ b/kmath-core/src/commonTest/kotlin/scientifik/kmath/expressions/ExpressionFieldTest.kt @@ -31,7 +31,7 @@ class ExpressionFieldTest { @Test fun separateContext() { - fun FunctionalExpressionField.expression(): Expression { + fun FunctionalExpressionField.expression(): Expression { val x = variable("x") return x * x + 2 * x + one } @@ -42,7 +42,7 @@ class ExpressionFieldTest { @Test fun valueExpression() { - val expressionBuilder: FunctionalExpressionField.() -> Expression = { + val expressionBuilder: FunctionalExpressionField.() -> Expression = { val x = variable("x") x * x + 2 * x + one } @@ -50,4 +50,4 @@ class ExpressionFieldTest { val expression = FunctionalExpressionField(RealField).expressionBuilder() assertEquals(expression("x" to 1.0), 4.0) } -} \ No newline at end of file +} diff --git a/kmath-core/src/commonTest/kotlin/scientifik/kmath/linear/MatrixTest.kt b/kmath-core/src/commonTest/kotlin/scientifik/kmath/linear/MatrixTest.kt index dcd510e32..987426250 100644 --- a/kmath-core/src/commonTest/kotlin/scientifik/kmath/linear/MatrixTest.kt +++ b/kmath-core/src/commonTest/kotlin/scientifik/kmath/linear/MatrixTest.kt @@ -49,17 +49,17 @@ class MatrixTest { @Test fun test2DDot() { - val firstMatrix = NDStructure.auto(2,3){ (i, j) -> (i + j).toDouble() }.as2D() - val secondMatrix = NDStructure.auto(3,2){ (i, j) -> (i + j).toDouble() }.as2D() + val firstMatrix = NDStructure.auto(2, 3) { (i, j) -> (i + j).toDouble() }.as2D() + val secondMatrix = NDStructure.auto(3, 2) { (i, j) -> (i + j).toDouble() }.as2D() MatrixContext.real.run { // val firstMatrix = produce(2, 3) { i, j -> (i + j).toDouble() } // val secondMatrix = produce(3, 2) { i, j -> (i + j).toDouble() } val result = firstMatrix dot secondMatrix assertEquals(2, result.rowNum) assertEquals(2, result.colNum) - assertEquals(8.0, result[0,1]) - assertEquals(8.0, result[1,0]) - assertEquals(14.0, result[1,1]) + assertEquals(8.0, result[0, 1]) + assertEquals(8.0, result[1, 0]) + assertEquals(14.0, result[1, 1]) } } -} \ No newline at end of file +} diff --git a/kmath-core/src/commonTest/kotlin/scientifik/kmath/linear/RealLUSolverTest.kt b/kmath-core/src/commonTest/kotlin/scientifik/kmath/linear/RealLUSolverTest.kt index 56a0b7aad..34bd8a0e3 100644 --- a/kmath-core/src/commonTest/kotlin/scientifik/kmath/linear/RealLUSolverTest.kt +++ b/kmath-core/src/commonTest/kotlin/scientifik/kmath/linear/RealLUSolverTest.kt @@ -48,4 +48,4 @@ class RealLUSolverTest { assertEquals(expected, inverted) } -} \ No newline at end of file +} diff --git a/kmath-core/src/commonTest/kotlin/scientifik/kmath/misc/AutoDiffTest.kt b/kmath-core/src/commonTest/kotlin/scientifik/kmath/misc/AutoDiffTest.kt index 9dc8f5ef7..c08a63ccb 100644 --- a/kmath-core/src/commonTest/kotlin/scientifik/kmath/misc/AutoDiffTest.kt +++ b/kmath-core/src/commonTest/kotlin/scientifik/kmath/misc/AutoDiffTest.kt @@ -8,10 +8,10 @@ import kotlin.test.assertEquals import kotlin.test.assertTrue class AutoDiffTest { + fun Variable(int: Int): Variable = Variable(int.toDouble()) - fun Variable(int: Int) = Variable(int.toDouble()) - - fun deriv(body: AutoDiffField.() -> Variable) = RealField.deriv(body) + fun deriv(body: AutoDiffField.() -> Variable): DerivationResult = + RealField.deriv(body) @Test fun testPlusX2() { @@ -178,5 +178,4 @@ class AutoDiffTest { private fun assertApprox(a: Double, b: Double) { if ((a - b) > 1e-10) assertEquals(a, b) } - -} \ No newline at end of file +} diff --git a/kmath-core/src/commonTest/kotlin/scientifik/kmath/misc/CumulativeKtTest.kt b/kmath-core/src/commonTest/kotlin/scientifik/kmath/misc/CumulativeKtTest.kt index e7c99e7d0..82ea5318f 100644 --- a/kmath-core/src/commonTest/kotlin/scientifik/kmath/misc/CumulativeKtTest.kt +++ b/kmath-core/src/commonTest/kotlin/scientifik/kmath/misc/CumulativeKtTest.kt @@ -10,4 +10,4 @@ class CumulativeKtTest { val cumulative = initial.cumulativeSum() assertEquals(listOf(-1.0, 1.0, 2.0, 3.0), cumulative) } -} \ No newline at end of file +} diff --git a/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/BigIntAlgebraTest.kt b/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/BigIntAlgebraTest.kt index 5ae977196..c22d2f27b 100644 --- a/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/BigIntAlgebraTest.kt +++ b/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/BigIntAlgebraTest.kt @@ -47,4 +47,3 @@ class BigIntAlgebraTest { } } - diff --git a/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/BigIntConstructorTest.kt b/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/BigIntConstructorTest.kt index 2af1b7e50..5e3f6d1b0 100644 --- a/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/BigIntConstructorTest.kt +++ b/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/BigIntConstructorTest.kt @@ -19,8 +19,8 @@ class BigIntConstructorTest { @Test fun testConstructor_0xffffffffaL() { - val x = -0xffffffffaL.toBigInt() + val x = (-0xffffffffaL).toBigInt() val y = uintArrayOf(0xfffffffaU, 0xfU).toBigInt(-1) assertEquals(x, y) } -} \ No newline at end of file +} diff --git a/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/BigIntConversionsTest.kt b/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/BigIntConversionsTest.kt index 51b9509e0..41df1968d 100644 --- a/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/BigIntConversionsTest.kt +++ b/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/BigIntConversionsTest.kt @@ -19,7 +19,7 @@ class BigIntConversionsTest { @Test fun testToString_0x17ead2ffffd() { - val x = -0x17ead2ffffdL.toBigInt() + val x = (-0x17ead2ffffdL).toBigInt() assertEquals("-0x17ead2ffffd", x.toString()) } @@ -40,4 +40,4 @@ class BigIntConversionsTest { val x = "-7059135710711894913860".parseBigInteger() assertEquals("-0x17ead2ffffd11223344", x.toString()) } -} \ No newline at end of file +} diff --git a/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/BigIntOperationsTest.kt b/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/BigIntOperationsTest.kt index 72ac9f229..b7f4cf43b 100644 --- a/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/BigIntOperationsTest.kt +++ b/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/BigIntOperationsTest.kt @@ -31,7 +31,7 @@ class BigIntOperationsTest { @Test fun testUnaryMinus() { val x = 1234.toBigInt() - val y = -1234.toBigInt() + val y = (-1234).toBigInt() assertEquals(-x, y) } @@ -48,18 +48,18 @@ class BigIntOperationsTest { @Test fun testMinus__2_1() { - val x = -2.toBigInt() + val x = (-2).toBigInt() val y = 1.toBigInt() val res = x - y - val sum = -3.toBigInt() + val sum = (-3).toBigInt() assertEquals(sum, res) } @Test fun testMinus___2_1() { - val x = -2.toBigInt() + val x = (-2).toBigInt() val y = 1.toBigInt() val res = -x - y @@ -74,7 +74,7 @@ class BigIntOperationsTest { val y = 0xffffffffaL.toBigInt() val res = x - y - val sum = -0xfffffcfc1L.toBigInt() + val sum = (-0xfffffcfc1L).toBigInt() assertEquals(sum, res) } @@ -92,11 +92,11 @@ class BigIntOperationsTest { @Test fun testMultiply__2_3() { - val x = -2.toBigInt() + val x = (-2).toBigInt() val y = 3.toBigInt() val res = x * y - val prod = -6.toBigInt() + val prod = (-6).toBigInt() assertEquals(prod, res) } @@ -129,7 +129,7 @@ class BigIntOperationsTest { val y = -0xfff456 val res = x * y - val prod = -0xffe579ad5dc2L.toBigInt() + val prod = (-0xffe579ad5dc2L).toBigInt() assertEquals(prod, res) } @@ -259,7 +259,7 @@ class BigIntOperationsTest { val y = -3 val res = x / y - val div = -6.toBigInt() + val div = (-6).toBigInt() assertEquals(div, res) } @@ -267,10 +267,10 @@ class BigIntOperationsTest { @Test fun testBigDivision_20__3() { val x = 20.toBigInt() - val y = -3.toBigInt() + val y = (-3).toBigInt() val res = x / y - val div = -6.toBigInt() + val div = (-6).toBigInt() assertEquals(div, res) } @@ -378,4 +378,4 @@ class BigIntOperationsTest { return assertEquals(res, x % mod) } -} \ No newline at end of file +} diff --git a/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/RealFieldTest.kt b/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/RealFieldTest.kt index 779cfc4b8..9dfa3bdd1 100644 --- a/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/RealFieldTest.kt +++ b/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/RealFieldTest.kt @@ -11,4 +11,4 @@ class RealFieldTest { } assertEquals(5.0, sqrt) } -} \ No newline at end of file +} diff --git a/kmath-core/src/commonTest/kotlin/scientifik/kmath/structures/ComplexBufferSpecTest.kt b/kmath-core/src/commonTest/kotlin/scientifik/kmath/structures/ComplexBufferSpecTest.kt index 454683dac..cbbe6f0f4 100644 --- a/kmath-core/src/commonTest/kotlin/scientifik/kmath/structures/ComplexBufferSpecTest.kt +++ b/kmath-core/src/commonTest/kotlin/scientifik/kmath/structures/ComplexBufferSpecTest.kt @@ -11,4 +11,4 @@ class ComplexBufferSpecTest { val buffer = Buffer.complex(20) { Complex(it.toDouble(), -it.toDouble()) } assertEquals(Complex(5.0, -5.0), buffer[5]) } -} \ No newline at end of file +} diff --git a/kmath-core/src/commonTest/kotlin/scientifik/kmath/structures/NDFieldTest.kt b/kmath-core/src/commonTest/kotlin/scientifik/kmath/structures/NDFieldTest.kt index 39cce5c67..7abeefca6 100644 --- a/kmath-core/src/commonTest/kotlin/scientifik/kmath/structures/NDFieldTest.kt +++ b/kmath-core/src/commonTest/kotlin/scientifik/kmath/structures/NDFieldTest.kt @@ -10,4 +10,4 @@ class NDFieldTest { val ndArray = NDElement.real(intArrayOf(10, 10)) { (it[0] + it[1]).toDouble() } assertEquals(ndArray[5, 5], 10.0) } -} \ No newline at end of file +} diff --git a/kmath-core/src/commonTest/kotlin/scientifik/kmath/structures/NumberNDFieldTest.kt b/kmath-core/src/commonTest/kotlin/scientifik/kmath/structures/NumberNDFieldTest.kt index 60f1f9979..d48aabfd0 100644 --- a/kmath-core/src/commonTest/kotlin/scientifik/kmath/structures/NumberNDFieldTest.kt +++ b/kmath-core/src/commonTest/kotlin/scientifik/kmath/structures/NumberNDFieldTest.kt @@ -8,8 +8,8 @@ import kotlin.test.Test import kotlin.test.assertEquals class NumberNDFieldTest { - val array1 = real2D(3, 3) { i, j -> (i + j).toDouble() } - val array2 = real2D(3, 3) { i, j -> (i - j).toDouble() } + val array1: RealNDElement = real2D(3, 3) { i, j -> (i + j).toDouble() } + val array2: RealNDElement = real2D(3, 3) { i, j -> (i - j).toDouble() } @Test fun testSum() { From 8debed704873df4bf9af77151bbce81dbb920057 Mon Sep 17 00:00:00 2001 From: Iaroslav Postovalov Date: Wed, 5 Aug 2020 17:25:56 +0700 Subject: [PATCH 24/54] Improve documentation --- .../kmath/operations/AlgebraElements.kt | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/AlgebraElements.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/AlgebraElements.kt index 9b430af39..0de2109c4 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/AlgebraElements.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/AlgebraElements.kt @@ -24,9 +24,36 @@ interface MathWrapper { * @param S the type of space */ interface SpaceElement, S : Space> : MathElement, MathWrapper { + /** + * Adds element to this one. + * + * @param b the augend. + * @return the sum. + */ operator fun plus(b: T): I = context.add(unwrap(), b).wrap() + + /** + * Subtracts element from this one. + * + * @param b the subtrahend. + * @return the difference. + */ operator fun minus(b: T): I = context.add(unwrap(), context.multiply(b, -1.0)).wrap() + + /** + * Multiplies this element by number. + * + * @param k the multiplicand. + * @return the product. + */ operator fun times(k: Number): I = context.multiply(unwrap(), k.toDouble()).wrap() + + /** + * Divides this element by number. + * + * @param k the divisor. + * @return the quotient. + */ operator fun div(k: Number): I = context.multiply(unwrap(), 1.0 / k.toDouble()).wrap() } @@ -34,6 +61,12 @@ interface SpaceElement, S : Space> : MathElement * Ring element */ interface RingElement, R : Ring> : SpaceElement { + /** + * Multiplies this element by another one. + * + * @param b the multiplicand. + * @return the product. + */ operator fun times(b: T): I = context.multiply(unwrap(), b).wrap() } @@ -42,5 +75,12 @@ interface RingElement, R : Ring> : SpaceElement, F : Field> : RingElement { override val context: F + + /** + * Divides this element by another one. + * + * @param b the divisor. + * @return the quotient. + */ operator fun div(b: T): I = context.divide(unwrap(), b).wrap() } From aed10329ebb6ea7bdedad469fbaa161ee41fba6a Mon Sep 17 00:00:00 2001 From: Iaroslav Postovalov Date: Wed, 5 Aug 2020 17:30:11 +0700 Subject: [PATCH 25/54] Improve documentation of elements --- .../kmath/operations/AlgebraElements.kt | 21 +++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/AlgebraElements.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/AlgebraElements.kt index 0de2109c4..534f56e0d 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/AlgebraElements.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/AlgebraElements.kt @@ -18,10 +18,11 @@ interface MathWrapper { } /** - * The element of linear context - * @param T the type of space operation results - * @param I self type of the element. Needed for static type checking - * @param S the type of space + * The element of [Space]. + * + * @param T the type of space operation results. + * @param I self type of the element. Needed for static type checking. + * @param S the type of space. */ interface SpaceElement, S : Space> : MathElement, MathWrapper { /** @@ -58,7 +59,11 @@ interface SpaceElement, S : Space> : MathElement } /** - * Ring element + * The element of [Ring]. + * + * @param T the type of space operation results. + * @param I self type of the element. Needed for static type checking. + * @param R the type of space. */ interface RingElement, R : Ring> : SpaceElement { /** @@ -71,7 +76,11 @@ interface RingElement, R : Ring> : SpaceElement, F : Field> : RingElement { override val context: F From 8367e13057fb19df3b6c356f6d5846bb92ffc6bd Mon Sep 17 00:00:00 2001 From: Iaroslav Postovalov Date: Wed, 5 Aug 2020 18:41:58 +0700 Subject: [PATCH 26/54] Improve documentation of complex and memoryspec --- .../scientifik/kmath/operations/Complex.kt | 57 ++++++++++++++++--- .../kotlin/scientifik/memory/MemorySpec.kt | 29 ++++++---- 2 files changed, 67 insertions(+), 19 deletions(-) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Complex.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Complex.kt index ffd6c8dec..0ce144a33 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Complex.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Complex.kt @@ -11,13 +11,16 @@ import kotlin.math.* private val PI_DIV_2 = Complex(PI / 2, 0) /** - * A field for complex numbers + * A field of [Complex]. */ object ComplexField : ExtendedField { override val zero: Complex = Complex(0.0, 0.0) override val one: Complex = Complex(1.0, 0.0) + /** + * The imaginary unit. + */ val i: Complex = Complex(0.0, 1.0) override fun add(a: Complex, b: Complex): Complex = Complex(a.re + b.re, a.im + b.im) @@ -45,25 +48,59 @@ object ComplexField : ExtendedField { override fun ln(arg: Complex): Complex = ln(arg.r) + i * atan2(arg.im, arg.re) + /** + * Adds complex number to real one. + * + * @receiver the addend. + * @param c the augend. + * @return the sum. + */ operator fun Double.plus(c: Complex): Complex = add(this.toComplex(), c) + /** + * Subtracts complex number from real one. + * + * @receiver the minuend. + * @param c the subtrahend. + * @return the difference. + */ operator fun Double.minus(c: Complex): Complex = add(this.toComplex(), -c) + /** + * Adds real number to complex one. + * + * @receiver the addend. + * @param d the augend. + * @return the sum. + */ operator fun Complex.plus(d: Double): Complex = d + this + /** + * Subtracts real number from complex one. + * + * @receiver the minuend. + * @param d the subtrahend. + * @return the difference. + */ operator fun Complex.minus(d: Double): Complex = add(this, -d.toComplex()) + /** + * Multiplies real number by complex one. + * + * @receiver the multiplier. + * @param c the multiplicand. + * @receiver the product. + */ operator fun Double.times(c: Complex): Complex = Complex(c.re * this, c.im * this) - override fun symbol(value: String): Complex = if (value == "i") { - i - } else { - super.symbol(value) - } + override fun symbol(value: String): Complex = if (value == "i") i else super.symbol(value) } /** - * Complex number class + * Represents complex number. + * + * @property re The real part. + * @property im The imaginary part. */ data class Complex(val re: Double, val im: Double) : FieldElement, Comparable { constructor(re: Number, im: Number) : this(re.toDouble(), im.toDouble()) @@ -104,6 +141,12 @@ val Complex.r: Double get() = sqrt(re * re + im * im) */ val Complex.theta: Double get() = atan(im / re) +/** + * Creates a complex number with real part equal to this real. + * + * @receiver the real part. + * @return the new complex number. + */ fun Double.toComplex(): Complex = Complex(this, 0.0) inline fun Buffer.Companion.complex(size: Int, crossinline init: (Int) -> Complex): Buffer { diff --git a/kmath-memory/src/commonMain/kotlin/scientifik/memory/MemorySpec.kt b/kmath-memory/src/commonMain/kotlin/scientifik/memory/MemorySpec.kt index 7999aa2ab..4d0035d09 100644 --- a/kmath-memory/src/commonMain/kotlin/scientifik/memory/MemorySpec.kt +++ b/kmath-memory/src/commonMain/kotlin/scientifik/memory/MemorySpec.kt @@ -1,7 +1,9 @@ package scientifik.memory /** - * A specification to read or write custom objects with fixed size in bytes + * A specification to read or write custom objects with fixed size in bytes. + * + * @param T the type of object this spec manages. */ interface MemorySpec { /** @@ -9,27 +11,30 @@ interface MemorySpec { */ val objectSize: Int + /** + * Reads the object starting from [offset]. + */ fun MemoryReader.read(offset: Int): T - //TODO consider thread safety + + // TODO consider thread safety + + /** + * Writes the object [value] starting from [offset]. + */ fun MemoryWriter.write(offset: Int, value: T) } -fun MemoryReader.read(spec: MemorySpec, offset: Int): T = spec.run { read(offset) } -fun MemoryWriter.write(spec: MemorySpec, offset: Int, value: T) = spec.run { write(offset, value) } +fun MemoryReader.read(spec: MemorySpec, offset: Int): T = with(spec) { read(offset) } +fun MemoryWriter.write(spec: MemorySpec, offset: Int, value: T): Unit = with(spec) { write(offset, value) } -inline fun MemoryReader.readArray(spec: MemorySpec, offset: Int, size: Int) = +inline fun MemoryReader.readArray(spec: MemorySpec, offset: Int, size: Int): Array = Array(size) { i -> spec.run { read(offset + i * objectSize) } } -fun MemoryWriter.writeArray(spec: MemorySpec, offset: Int, array: Array) { - spec.run { - for (i in array.indices) { - write(offset + i * objectSize, array[i]) - } - } -} +fun MemoryWriter.writeArray(spec: MemorySpec, offset: Int, array: Array): Unit = + with(spec) { array.indices.forEach { i -> write(offset + i * objectSize, array[i]) } } //TODO It is possible to add elastic MemorySpec with unknown object size \ No newline at end of file From 7d32fe0af82c4133b8173ff0f6228d417104760c Mon Sep 17 00:00:00 2001 From: Iaroslav Postovalov Date: Wed, 5 Aug 2020 18:43:20 +0700 Subject: [PATCH 27/54] Update documentation of NumberAlgebra --- .../kotlin/scientifik/kmath/operations/NumberAlgebra.kt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/NumberAlgebra.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/NumberAlgebra.kt index 2145eb4b5..f99a6bc0f 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/NumberAlgebra.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/NumberAlgebra.kt @@ -52,7 +52,7 @@ inline class Real(val value: Double) : FieldElement { } /** - * A field for double without boxing. Does not produce appropriate field element + * A field for [Double] without boxing. Does not produce appropriate field element. */ @Suppress("EXTENSION_SHADOWED_BY_MEMBER", "OVERRIDE_BY_INLINE", "NOTHING_TO_INLINE") object RealField : ExtendedField, Norm { @@ -94,6 +94,9 @@ object RealField : ExtendedField, Norm { } } +/** + * A field for [Float] without boxing. Does not produce appropriate field element. + */ @Suppress("EXTENSION_SHADOWED_BY_MEMBER", "OVERRIDE_BY_INLINE", "NOTHING_TO_INLINE") object FloatField : ExtendedField, Norm { override val zero: Float = 0f From 4c39b9801747d5016e0e1e04414ce868d6fdce43 Mon Sep 17 00:00:00 2001 From: Iaroslav Postovalov Date: Wed, 5 Aug 2020 18:44:28 +0700 Subject: [PATCH 28/54] Update documentation of NumberAlgebra --- .../kotlin/scientifik/kmath/operations/NumberAlgebra.kt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/NumberAlgebra.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/NumberAlgebra.kt index f99a6bc0f..b113e07a1 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/NumberAlgebra.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/NumberAlgebra.kt @@ -4,7 +4,7 @@ import kotlin.math.abs import kotlin.math.pow as kpow /** - * Advanced Number-like field that implements basic operations + * Advanced Number-like semifield that implements basic operations. */ interface ExtendedFieldOperations : InverseTrigonometricOperations, @@ -27,6 +27,10 @@ interface ExtendedFieldOperations : } } + +/** + * Advanced Number-like field that implements basic operations. + */ interface ExtendedField : ExtendedFieldOperations, Field { override fun rightSideNumberOperation(operation: String, left: T, right: Number): T = when (operation) { PowerOperations.POW_OPERATION -> power(left, right) From eae218ff5f0c46bbab8a0d425f54467944a0d71b Mon Sep 17 00:00:00 2001 From: Iaroslav Postovalov Date: Wed, 5 Aug 2020 20:55:50 +0700 Subject: [PATCH 29/54] Update algebra extensions, improve docs --- .../kmath/operations/AlgebraExtensions.kt | 55 +++++++++++++++++-- .../kmath/operations/OptionalOperations.kt | 6 +- .../scientifik/kmath/structures/Buffers.kt | 31 +++++++++-- .../scientifik/kmath/structures/IntBuffer.kt | 11 ++++ .../scientifik/kmath/operations/BigNumbers.kt | 9 ++- 5 files changed, 100 insertions(+), 12 deletions(-) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/AlgebraExtensions.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/AlgebraExtensions.kt index a6d508ee4..6712d3baa 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/AlgebraExtensions.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/AlgebraExtensions.kt @@ -1,15 +1,62 @@ package scientifik.kmath.operations +/** + * Returns the sum of all elements in the iterable in this [Space]. + * + * @receiver the algebra that provides addition. + * @param data the collection to sum up. + * @return the sum. + */ fun Space.sum(data: Iterable): T = data.fold(zero) { left, right -> add(left, right) } + +/** + * Returns the sum of all elements in the sequence in this [Space]. + * + * @receiver the algebra that provides addition. + * @param data the collection to sum up. + * @return the sum. + */ fun Space.sum(data: Sequence): T = data.fold(zero) { left, right -> add(left, right) } +/** + * Returns the sum of all elements in the iterable in provided space. + * + * @receiver the collection to sum up. + * @param space the algebra that provides addition. + * @return the sum. + */ fun > Iterable.sumWith(space: S): T = space.sum(this) //TODO optimized power operation -fun RingOperations.power(arg: T, power: Int): T { + +/** + * Raises [arg] to the natural power [power]. + * + * @receiver the algebra to provide multiplication. + * @param arg the base. + * @param power the exponent. + * @return the base raised to the power. + */ +fun Ring.power(arg: T, power: Int): T { + require(power >= 0) { "The power can't be negative." } + require(power != 0 || arg != zero) { "The $zero raised to $power is not defined." } + if (power == 0) return one var res = arg - repeat(power - 1) { - res *= arg - } + repeat(power - 1) { res *= arg } return res } + +/** + * Raises [arg] to the integer power [power]. + * + * @receiver the algebra to provide multiplication and division. + * @param arg the base. + * @param power the exponent. + * @return the base raised to the power. + */ +fun Field.power(arg: T, power: Int): T { + require(power != 0 || arg != zero) { "The $zero raised to $power is not defined." } + if (power == 0) return one + if (power < 0) return one / (this as Ring).power(arg, -power) + return (this as Ring).power(arg, power) +} diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/OptionalOperations.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/OptionalOperations.kt index 75adb1dee..dea45a145 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/OptionalOperations.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/OptionalOperations.kt @@ -143,7 +143,11 @@ interface PowerOperations : Algebra { } /** - * Raises [arg] to the power [pow]. + * Raises this element to the power [pow]. + * + * @receiver the base. + * @param power the exponent. + * @return the base raised to the power. */ infix fun >> T.pow(power: Double): T = context.power(this, power) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/Buffers.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/Buffers.kt index 723348e3f..294549c31 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/Buffers.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/Buffers.kt @@ -4,13 +4,22 @@ import scientifik.kmath.operations.Complex import scientifik.kmath.operations.complex import kotlin.reflect.KClass - +/** + * Function that produces [Buffer] from its size and function that supplies values. + * + * @param T the type of buffer. + */ typealias BufferFactory = (Int, (Int) -> T) -> Buffer -typealias MutableBufferFactory = (Int, (Int) -> T) -> MutableBuffer - /** - * A generic random access structure for both primitives and objects + * Function that produces [MutableBuffer] from its size and function that supplies values. + * + * @param T the type of buffer. + */ +typealias MutableBufferFactory = (Int, (Int) -> T) -> MutableBuffer + +/** + * A generic random-access structure for both primitives and objects. */ interface Buffer { @@ -69,12 +78,24 @@ interface Buffer { } } +/** + * Creates a sequence that returns all elements from this [Buffer]. + */ fun Buffer.asSequence(): Sequence = Sequence(::iterator) +/** + * Creates an iterable that returns all elements from this [Buffer]. + */ fun Buffer.asIterable(): Iterable = Iterable(::iterator) -val Buffer<*>.indices: IntRange get() = IntRange(0, size - 1) +/** + * Returns an [IntRange] of the valid indices for this [Buffer]. + */ +val Buffer<*>.indices: IntRange get() = 0 until size +/** + * A generic mutable random-access structure for both primitives and objects. + */ interface MutableBuffer : Buffer { operator fun set(index: Int, value: T) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/IntBuffer.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/IntBuffer.kt index b277374ab..99fd052aa 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/IntBuffer.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/IntBuffer.kt @@ -1,5 +1,10 @@ package scientifik.kmath.structures +/** + * Specialized [MutableBuffer] implementation over [IntArray]. + * + * @property array the underlying array. + */ inline class IntBuffer(val array: IntArray) : MutableBuffer { override val size: Int get() = array.size @@ -16,4 +21,10 @@ inline class IntBuffer(val array: IntArray) : MutableBuffer { } +/** + * Returns [IntBuffer] over this array. + * + * @receiver the array. + * @return the new buffer. + */ fun IntArray.asBuffer(): IntBuffer = IntBuffer(this) diff --git a/kmath-core/src/jvmMain/kotlin/scientifik/kmath/operations/BigNumbers.kt b/kmath-core/src/jvmMain/kotlin/scientifik/kmath/operations/BigNumbers.kt index eb268bb5e..06f2b31ad 100644 --- a/kmath-core/src/jvmMain/kotlin/scientifik/kmath/operations/BigNumbers.kt +++ b/kmath-core/src/jvmMain/kotlin/scientifik/kmath/operations/BigNumbers.kt @@ -5,7 +5,7 @@ import java.math.BigInteger import java.math.MathContext /** - * A field wrapper for Java [BigInteger] + * A field over [BigInteger]. */ object JBigIntegerField : Field { override val zero: BigInteger @@ -24,7 +24,9 @@ object JBigIntegerField : Field { } /** - * A Field wrapper for Java [BigDecimal] + * An abstract field over [BigDecimal]. + * + * @property mathContext the [MathContext] to use. */ abstract class JBigDecimalFieldBase internal constructor(val mathContext: MathContext = MathContext.DECIMAL64) : Field, @@ -50,6 +52,9 @@ abstract class JBigDecimalFieldBase internal constructor(val mathContext: MathCo } +/** + * A field over [BigDecimal]. + */ class JBigDecimalField(mathContext: MathContext = MathContext.DECIMAL64) : JBigDecimalFieldBase(mathContext) { companion object : JBigDecimalFieldBase() } From 9f6bd116f660003c36d6655c2930a8a62a4e8770 Mon Sep 17 00:00:00 2001 From: Iaroslav Postovalov Date: Thu, 6 Aug 2020 12:39:50 +0700 Subject: [PATCH 30/54] Document and make more consistent buffers API --- .../scientifik/kmath/structures/Buffers.kt | 5 +++- .../scientifik/kmath/structures/IntBuffer.kt | 22 +++++++++++++++++ .../scientifik/kmath/structures/RealBuffer.kt | 24 ++++++++++++++----- 3 files changed, 44 insertions(+), 7 deletions(-) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/Buffers.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/Buffers.kt index 294549c31..73c3f96f4 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/Buffers.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/Buffers.kt @@ -97,10 +97,13 @@ val Buffer<*>.indices: IntRange get() = 0 until size * A generic mutable random-access structure for both primitives and objects. */ interface MutableBuffer : Buffer { + /** + * Sets the array element at the specified [index] to the specified [value]. + */ operator fun set(index: Int, value: T) /** - * A shallow copy of the buffer + * Returns a shallow copy of the buffer. */ fun copy(): MutableBuffer diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/IntBuffer.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/IntBuffer.kt index 99fd052aa..3bf8ef6ab 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/IntBuffer.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/IntBuffer.kt @@ -21,6 +21,28 @@ inline class IntBuffer(val array: IntArray) : MutableBuffer { } +/** + * Creates a new [IntBuffer] with the specified [size], where each element is calculated by calling the specified + * [init] function. + * + * The function [init] is called for each array element sequentially starting from the first one. + * It should return the value for an array element given its index. + */ +@Suppress("FunctionName") +inline fun IntBuffer(size: Int, init: (Int) -> Int): IntBuffer = IntBuffer(IntArray(size) { init(it) }) + +/** + * Returns a new [IntBuffer] of given elements. + */ +@Suppress("FunctionName") +fun IntBuffer(vararg ints: Int): IntBuffer = IntBuffer(ints) + +/** + * Returns a [IntArray] containing all of the elements of this [MutableBuffer]. + */ +val MutableBuffer.array: IntArray + get() = (if (this is IntBuffer) array else IntArray(size) { get(it) }) + /** * Returns [IntBuffer] over this array. * diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/RealBuffer.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/RealBuffer.kt index 20187fe19..715f3a017 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/RealBuffer.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/RealBuffer.kt @@ -15,20 +15,32 @@ inline class RealBuffer(val array: DoubleArray) : MutableBuffer { RealBuffer(array.copyOf()) } +/** + * Creates a new array with the specified [size], where each element is calculated by calling the specified + * [init] function. + * + * The function [init] is called for each array element sequentially starting from the first one. + * It should return the value for an array element given its index. + */ @Suppress("FunctionName") inline fun RealBuffer(size: Int, init: (Int) -> Double): RealBuffer = RealBuffer(DoubleArray(size) { init(it) }) +/** + * Returns a new [RealBuffer] of given elements. + */ @Suppress("FunctionName") fun RealBuffer(vararg doubles: Double): RealBuffer = RealBuffer(doubles) /** - * Transform buffer of doubles into array for high performance operations + * Returns a [DoubleArray] containing all of the elements of this [MutableBuffer]. */ val MutableBuffer.array: DoubleArray - get() = if (this is RealBuffer) { - array - } else { - DoubleArray(size) { get(it) } - } + get() = (if (this is RealBuffer) array else DoubleArray(size) { get(it) }) +/** + * Returns [RealBuffer] over this array. + * + * @receiver the array. + * @return the new buffer. + */ fun DoubleArray.asBuffer(): RealBuffer = RealBuffer(this) From 0a8044ddb3cf4fd6fc38431106daea4aa4ff5302 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Fri, 7 Aug 2020 15:20:26 +0700 Subject: [PATCH 31/54] Update buffers documentation, make API more consistent, minor changes --- .../scientifik/kmath/expressions/Builders.kt | 6 +- .../kmath/operations/AlgebraElements.kt | 13 ++++ .../scientifik/kmath/structures/Buffers.kt | 63 +++++++++++++++---- .../kmath/structures/FlaggedBuffer.kt | 22 ++++++- .../kmath/structures/FloatBuffer.kt | 49 +++++++++++++++ .../scientifik/kmath/structures/IntBuffer.kt | 4 +- .../scientifik/kmath/structures/LongBuffer.kt | 31 +++++++++ .../kmath/structures/MemoryBuffer.kt | 13 +++- .../scientifik/kmath/structures/NDAlgebra.kt | 4 +- .../scientifik/kmath/structures/RealBuffer.kt | 11 ++-- .../kmath/structures/RealBufferField.kt | 7 ++- .../kmath/structures/ShortBuffer.kt | 31 +++++++++ 12 files changed, 227 insertions(+), 27 deletions(-) create mode 100644 kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/FloatBuffer.kt diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/Builders.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/Builders.kt index 9f1503285..834ef9e24 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/Builders.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/Builders.kt @@ -5,19 +5,19 @@ import scientifik.kmath.operations.Ring import scientifik.kmath.operations.Space /** - * Create a functional expression on this [Space] + * Creates a functional expression with this [Space]. */ fun Space.spaceExpression(block: FunctionalExpressionSpace>.() -> Expression): Expression = FunctionalExpressionSpace(this).run(block) /** - * Create a functional expression on this [Ring] + * Creates a functional expression with this [Ring]. */ fun Ring.ringExpression(block: FunctionalExpressionRing>.() -> Expression): Expression = FunctionalExpressionRing(this).run(block) /** - * Create a functional expression on this [Field] + * Creates a functional expression with this [Field]. */ fun Field.fieldExpression(block: FunctionalExpressionField>.() -> Expression): Expression = FunctionalExpressionField(this).run(block) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/AlgebraElements.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/AlgebraElements.kt index 534f56e0d..197897c14 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/AlgebraElements.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/AlgebraElements.kt @@ -12,8 +12,21 @@ interface MathElement { val context: C } +/** + * Represents element that can be wrapped to its "primitive" value. + * + * @param T the type wrapped by this wrapper. + * @param I the type of this wrapper. + */ interface MathWrapper { + /** + * Unwraps [I] to [T]. + */ fun unwrap(): T + + /** + * Wraps [T] to [I]. + */ fun T.wrap(): I } diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/Buffers.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/Buffers.kt index 73c3f96f4..5fdf79e88 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/Buffers.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/Buffers.kt @@ -19,10 +19,11 @@ typealias BufferFactory = (Int, (Int) -> T) -> Buffer typealias MutableBufferFactory = (Int, (Int) -> T) -> MutableBuffer /** - * A generic random-access structure for both primitives and objects. + * A generic immutable random-access structure for both primitives and objects. + * + * @param T the type of elements contained in the buffer. */ interface Buffer { - /** * The size of this buffer. */ @@ -45,7 +46,6 @@ interface Buffer { asSequence().mapIndexed { index, value -> value == other[index] }.all { it } companion object { - inline fun real(size: Int, initializer: (Int) -> Double): RealBuffer { val array = DoubleArray(size) { initializer(it) } return RealBuffer(array) @@ -95,6 +95,8 @@ val Buffer<*>.indices: IntRange get() = 0 until size /** * A generic mutable random-access structure for both primitives and objects. + * + * @param T the type of elements contained in the buffer. */ interface MutableBuffer : Buffer { /** @@ -138,8 +140,13 @@ interface MutableBuffer : Buffer { } } +/** + * [Buffer] implementation over [List]. + * + * @param T the type of elements contained in the buffer. + * @property list The underlying list. + */ inline class ListBuffer(val list: List) : Buffer { - override val size: Int get() = list.size @@ -148,10 +155,26 @@ inline class ListBuffer(val list: List) : Buffer { override fun iterator(): Iterator = list.iterator() } +/** + * Returns an [ListBuffer] that wraps the original list. + */ fun List.asBuffer(): ListBuffer = ListBuffer(this) +/** + * Creates a new [ListBuffer] with the specified [size], where each element is calculated by calling the specified + * [init] function. + * + * The function [init] is called for each array element sequentially starting from the first one. + * It should return the value for an array element given its index. + */ inline fun ListBuffer(size: Int, init: (Int) -> T): ListBuffer = List(size, init).asBuffer() +/** + * [MutableBuffer] implementation over [MutableList]. + * + * @param T the type of elements contained in the buffer. + * @property list The underlying list. + */ inline class MutableListBuffer(val list: MutableList) : MutableBuffer { override val size: Int @@ -167,8 +190,14 @@ inline class MutableListBuffer(val list: MutableList) : MutableBuffer { override fun copy(): MutableBuffer = MutableListBuffer(ArrayList(list)) } +/** + * [MutableBuffer] implementation over [Array]. + * + * @param T the type of elements contained in the buffer. + * @property array The underlying array. + */ class ArrayBuffer(private val array: Array) : MutableBuffer { - //Can't inline because array is invariant + // Can't inline because array is invariant override val size: Int get() = array.size @@ -183,8 +212,17 @@ class ArrayBuffer(private val array: Array) : MutableBuffer { override fun copy(): MutableBuffer = ArrayBuffer(array.copyOf()) } +/** + * Returns an [ArrayBuffer] that wraps the original array. + */ fun Array.asBuffer(): ArrayBuffer = ArrayBuffer(this) +/** + * Immutable wrapper for [MutableBuffer]. + * + * @param T the type of elements contained in the buffer. + * @property buffer The underlying buffer. + */ inline class ReadOnlyBuffer(val buffer: MutableBuffer) : Buffer { override val size: Int get() = buffer.size @@ -196,6 +234,8 @@ inline class ReadOnlyBuffer(val buffer: MutableBuffer) : Buffer { /** * A buffer with content calculated on-demand. The calculated content is not stored, so it is recalculated on each call. * Useful when one needs single element from the buffer. + * + * @param T the type of elements provided by the buffer. */ class VirtualBuffer(override val size: Int, private val generator: (Int) -> T) : Buffer { override fun get(index: Int): T { @@ -215,17 +255,16 @@ class VirtualBuffer(override val size: Int, private val generator: (Int) -> T } /** - * Convert this buffer to read-only buffer + * Convert this buffer to read-only buffer. */ -fun Buffer.asReadOnly(): Buffer = if (this is MutableBuffer) { - ReadOnlyBuffer(this) -} else { - this -} +fun Buffer.asReadOnly(): Buffer = if (this is MutableBuffer) ReadOnlyBuffer(this) else this /** - * Typealias for buffer transformations + * Typealias for buffer transformations. */ typealias BufferTransform = (Buffer) -> Buffer +/** + * Typealias for buffer transformations with suspend function. + */ typealias SuspendBufferTransform = suspend (Buffer) -> Buffer diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/FlaggedBuffer.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/FlaggedBuffer.kt index 2382e2c57..a2d0a71b3 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/FlaggedBuffer.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/FlaggedBuffer.kt @@ -2,15 +2,35 @@ package scientifik.kmath.structures import kotlin.experimental.and +/** + * Represents flags to supply additional info about values of buffer. + * + * @property mask bit mask value of this flag. + */ enum class ValueFlag(val mask: Byte) { + /** + * Reports the value is NaN. + */ NAN(0b0000_0001), + + /** + * Reports the value doesn't present in the buffer (when the type of value doesn't support `null`). + */ MISSING(0b0000_0010), + + /** + * Reports the value is negative infinity. + */ NEGATIVE_INFINITY(0b0000_0100), + + /** + * Reports the value is positive infinity + */ POSITIVE_INFINITY(0b0000_1000) } /** - * A buffer with flagged values + * A buffer with flagged values. */ interface FlaggedBuffer : Buffer { fun getFlag(index: Int): Byte diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/FloatBuffer.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/FloatBuffer.kt new file mode 100644 index 000000000..e42df8c14 --- /dev/null +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/FloatBuffer.kt @@ -0,0 +1,49 @@ +package scientifik.kmath.structures + +/** + * Specialized [MutableBuffer] implementation over [FloatArray]. + * + * @property array the underlying array. + */ +inline class FloatBuffer(val array: FloatArray) : MutableBuffer { + override val size: Int get() = array.size + + override fun get(index: Int): Float = array[index] + + override fun set(index: Int, value: Float) { + array[index] = value + } + + override fun iterator(): FloatIterator = array.iterator() + + override fun copy(): MutableBuffer = + FloatBuffer(array.copyOf()) +} + +/** + * Creates a new [FloatBuffer] with the specified [size], where each element is calculated by calling the specified + * [init] function. + * + * The function [init] is called for each array element sequentially starting from the first one. + * It should return the value for an buffer element given its index. + */ +inline fun FloatBuffer(size: Int, init: (Int) -> Float): FloatBuffer = FloatBuffer(FloatArray(size) { init(it) }) + +/** + * Returns a new [FloatBuffer] of given elements. + */ +fun FloatBuffer(vararg floats: Float): FloatBuffer = FloatBuffer(floats) + +/** + * Returns a [FloatArray] containing all of the elements of this [MutableBuffer]. + */ +val MutableBuffer.array: FloatArray + get() = (if (this is FloatBuffer) array else FloatArray(size) { get(it) }) + +/** + * Returns [FloatBuffer] over this array. + * + * @receiver the array. + * @return the new buffer. + */ +fun FloatArray.asBuffer(): FloatBuffer = FloatBuffer(this) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/IntBuffer.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/IntBuffer.kt index 3bf8ef6ab..a3f0f3c3e 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/IntBuffer.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/IntBuffer.kt @@ -26,15 +26,13 @@ inline class IntBuffer(val array: IntArray) : MutableBuffer { * [init] function. * * The function [init] is called for each array element sequentially starting from the first one. - * It should return the value for an array element given its index. + * It should return the value for an buffer element given its index. */ -@Suppress("FunctionName") inline fun IntBuffer(size: Int, init: (Int) -> Int): IntBuffer = IntBuffer(IntArray(size) { init(it) }) /** * Returns a new [IntBuffer] of given elements. */ -@Suppress("FunctionName") fun IntBuffer(vararg ints: Int): IntBuffer = IntBuffer(ints) /** diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/LongBuffer.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/LongBuffer.kt index 3ee2e75a2..912656c68 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/LongBuffer.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/LongBuffer.kt @@ -1,5 +1,10 @@ package scientifik.kmath.structures +/** + * Specialized [MutableBuffer] implementation over [LongArray]. + * + * @property array the underlying array. + */ inline class LongBuffer(val array: LongArray) : MutableBuffer { override val size: Int get() = array.size @@ -16,4 +21,30 @@ inline class LongBuffer(val array: LongArray) : MutableBuffer { } +/** + * Creates a new [LongBuffer] with the specified [size], where each element is calculated by calling the specified + * [init] function. + * + * The function [init] is called for each array element sequentially starting from the first one. + * It should return the value for an buffer element given its index. + */ +inline fun LongBuffer(size: Int, init: (Int) -> Long): LongBuffer = LongBuffer(LongArray(size) { init(it) }) + +/** + * Returns a new [LongBuffer] of given elements. + */ +fun LongBuffer(vararg longs: Long): LongBuffer = LongBuffer(longs) + +/** + * Returns a [IntArray] containing all of the elements of this [MutableBuffer]. + */ +val MutableBuffer.array: LongArray + get() = (if (this is LongBuffer) array else LongArray(size) { get(it) }) + +/** + * Returns [LongBuffer] over this array. + * + * @receiver the array. + * @return the new buffer. + */ fun LongArray.asBuffer(): LongBuffer = LongBuffer(this) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/MemoryBuffer.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/MemoryBuffer.kt index b15487114..70e4a8f0f 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/MemoryBuffer.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/MemoryBuffer.kt @@ -4,12 +4,16 @@ import scientifik.memory.* /** * A non-boxing buffer over [Memory] object. + * + * @param T the type of elements contained in the buffer. + * @property memory the underlying memory segment. + * @property spec the spec of [T] type. */ open class MemoryBuffer(protected val memory: Memory, protected val spec: MemorySpec) : Buffer { override val size: Int get() = memory.size / spec.objectSize - private val reader = memory.reader() + private val reader: MemoryReader = memory.reader() override fun get(index: Int): T = reader.read(spec, spec.objectSize * index) @@ -33,6 +37,13 @@ open class MemoryBuffer(protected val memory: Memory, protected val spe } } +/** + * A mutable non-boxing buffer over [Memory] object. + * + * @param T the type of elements contained in the buffer. + * @property memory the underlying memory segment. + * @property spec the spec of [T] type. + */ class MutableMemoryBuffer(memory: Memory, spec: MemorySpec) : MemoryBuffer(memory, spec), MutableBuffer { diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/NDAlgebra.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/NDAlgebra.kt index 750133351..f2805529c 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/NDAlgebra.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/NDAlgebra.kt @@ -107,8 +107,8 @@ interface NDRing, N : NDStructure> : Ring, NDSpace /** * Field for n-dimensional structures. * - * @param T - the type of the element contained in ND structure - * @param F - field of structure elements + * @param T the type of the element contained in ND structure + * @param F field of structure elements */ interface NDField, N : NDStructure> : Field, NDRing { diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/RealBuffer.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/RealBuffer.kt index 715f3a017..e999e12b2 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/RealBuffer.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/RealBuffer.kt @@ -1,5 +1,10 @@ package scientifik.kmath.structures +/** + * Specialized [MutableBuffer] implementation over [DoubleArray]. + * + * @property array the underlying array. + */ inline class RealBuffer(val array: DoubleArray) : MutableBuffer { override val size: Int get() = array.size @@ -16,19 +21,17 @@ inline class RealBuffer(val array: DoubleArray) : MutableBuffer { } /** - * Creates a new array with the specified [size], where each element is calculated by calling the specified + * Creates a new [RealBuffer] with the specified [size], where each element is calculated by calling the specified * [init] function. * * The function [init] is called for each array element sequentially starting from the first one. - * It should return the value for an array element given its index. + * It should return the value for an buffer element given its index. */ -@Suppress("FunctionName") inline fun RealBuffer(size: Int, init: (Int) -> Double): RealBuffer = RealBuffer(DoubleArray(size) { init(it) }) /** * Returns a new [RealBuffer] of given elements. */ -@Suppress("FunctionName") fun RealBuffer(vararg doubles: Double): RealBuffer = RealBuffer(doubles) /** diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/RealBufferField.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/RealBufferField.kt index 826203d1f..33198aac1 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/RealBufferField.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/RealBufferField.kt @@ -6,7 +6,7 @@ import kotlin.math.* /** - * A simple field over linear buffers of [Double] + * [ExtendedFieldOperations] over [RealBuffer]. */ object RealBufferFieldOperations : ExtendedFieldOperations> { override fun add(a: Buffer, b: Buffer): RealBuffer { @@ -109,6 +109,11 @@ object RealBufferFieldOperations : ExtendedFieldOperations> { RealBuffer(DoubleArray(arg.size) { ln(arg[it]) }) } +/** + * [ExtendedField] over [RealBuffer]. + * + * @property size the size of buffers to operate on. + */ class RealBufferField(val size: Int) : ExtendedField> { override val zero: Buffer by lazy { RealBuffer(size) { 0.0 } } override val one: Buffer by lazy { RealBuffer(size) { 1.0 } } diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ShortBuffer.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ShortBuffer.kt index 24c94a885..a67cbec62 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ShortBuffer.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ShortBuffer.kt @@ -1,5 +1,10 @@ package scientifik.kmath.structures +/** + * Specialized [MutableBuffer] implementation over [ShortBuffer]. + * + * @property array the underlying array. + */ inline class ShortBuffer(val array: ShortArray) : MutableBuffer { override val size: Int get() = array.size @@ -16,4 +21,30 @@ inline class ShortBuffer(val array: ShortArray) : MutableBuffer { } +/** + * Creates a new [ShortBuffer] with the specified [size], where each element is calculated by calling the specified + * [init] function. + * + * The function [init] is called for each array element sequentially starting from the first one. + * It should return the value for an buffer element given its index. + */ +inline fun ShortBuffer(size: Int, init: (Int) -> Short): ShortBuffer = ShortBuffer(ShortArray(size) { init(it) }) + +/** + * Returns a new [ShortBuffer] of given elements. + */ +fun ShortBuffer(vararg shorts: Short): ShortBuffer = ShortBuffer(shorts) + +/** + * Returns a [ShortArray] containing all of the elements of this [MutableBuffer]. + */ +val MutableBuffer.array: ShortArray + get() = (if (this is ShortBuffer) array else ShortArray(size) { get(it) }) + +/** + * Returns [ShortBuffer] over this array. + * + * @receiver the array. + * @return the new buffer. + */ fun ShortArray.asBuffer(): ShortBuffer = ShortBuffer(this) From c8cd6cd28857b14199ee99683f789eb11a28794e Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Sat, 8 Aug 2020 04:21:59 +0700 Subject: [PATCH 32/54] Document memory module and several ND objects --- .../kmath/structures/BoxingNDField.kt | 1 - .../kmath/structures/BufferedNDAlgebra.kt | 3 +- .../kmath/structures/ComplexNDField.kt | 4 +- .../kmath/structures/ExtendedNDField.kt | 7 ++ .../kmath/structures/MemoryBuffer.kt | 1 - .../scientifik/kmath/structures/NDAlgebra.kt | 7 +- .../kmath/structures/NDStructure.kt | 77 ++++++++++++--- .../kmath/structures/RealNDField.kt | 4 +- .../kotlin/scientifik/memory/Memory.kt | 97 ++++++++++++++++--- .../kotlin/scientifik/memory/MemorySpec.kt | 17 +++- .../scientifik/memory/DataViewMemory.kt | 18 ++-- .../scientifik/memory/ByteBufferMemory.kt | 48 ++++----- 12 files changed, 217 insertions(+), 67 deletions(-) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BoxingNDField.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BoxingNDField.kt index 516f9a8cd..4cbb565c1 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BoxingNDField.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BoxingNDField.kt @@ -3,7 +3,6 @@ package scientifik.kmath.structures import scientifik.kmath.operations.Field import scientifik.kmath.operations.FieldElement - class BoxingNDField>( override val shape: IntArray, override val elementContext: F, diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BufferedNDAlgebra.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BufferedNDAlgebra.kt index baf1a05a6..06922c56f 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BufferedNDAlgebra.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BufferedNDAlgebra.kt @@ -11,7 +11,8 @@ interface BufferedNDAlgebra : NDAlgebra> { /** * Convert any [NDStructure] to buffered structure using strides from this context. - * If the structure is already [NDBuffer], conversion is free. If not, it could be expensive because iteration over indexes + * If the structure is already [NDBuffer], conversion is free. If not, it could be expensive because iteration over + * indices. * * If the argument is [NDBuffer] with different strides structure, the new element will be produced. */ diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ComplexNDField.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ComplexNDField.kt index 2f7d20b6d..be0b9e5c6 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ComplexNDField.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ComplexNDField.kt @@ -98,13 +98,13 @@ inline fun BufferedNDField.produceInline(crossinline init } /** - * Map one [ComplexNDElement] using function with indexes + * Map one [ComplexNDElement] using function with indices. */ inline fun ComplexNDElement.mapIndexed(crossinline transform: ComplexField.(index: IntArray, Complex) -> Complex): ComplexNDElement = context.produceInline { offset -> transform(strides.index(offset), buffer[offset]) } /** - * Map one [ComplexNDElement] using function without indexes + * Map one [ComplexNDElement] using function without indices. */ inline fun ComplexNDElement.map(crossinline transform: ComplexField.(Complex) -> Complex): ComplexNDElement { val buffer = Buffer.complex(strides.linearSize) { offset -> ComplexField.transform(buffer[offset]) } diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ExtendedNDField.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ExtendedNDField.kt index 1fe449632..24aa48c6b 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ExtendedNDField.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ExtendedNDField.kt @@ -2,6 +2,13 @@ package scientifik.kmath.structures import scientifik.kmath.operations.ExtendedField +/** + * [ExtendedField] over [NDStructure]. + * + * @param T the type of the element contained in ND structure. + * @param N the type of ND structure. + * @param F the extended field of structure elements. + */ interface ExtendedNDField, N : NDStructure> : NDField, ExtendedField ///** diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/MemoryBuffer.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/MemoryBuffer.kt index 70e4a8f0f..1d0c87580 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/MemoryBuffer.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/MemoryBuffer.kt @@ -10,7 +10,6 @@ import scientifik.memory.* * @property spec the spec of [T] type. */ open class MemoryBuffer(protected val memory: Memory, protected val spec: MemorySpec) : Buffer { - override val size: Int get() = memory.size / spec.objectSize private val reader: MemoryReader = memory.reader() diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/NDAlgebra.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/NDAlgebra.kt index f2805529c..f09db3c72 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/NDAlgebra.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/NDAlgebra.kt @@ -105,10 +105,11 @@ interface NDRing, N : NDStructure> : Ring, NDSpace } /** - * Field for n-dimensional structures. + * Field of [NDStructure]. * - * @param T the type of the element contained in ND structure - * @param F field of structure elements + * @param T the type of the element contained in ND structure. + * @param N the type of ND structure. + * @param F field of structure elements. */ interface NDField, N : NDStructure> : Field, NDRing { diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/NDStructure.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/NDStructure.kt index bcd74ed2a..9d7735053 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/NDStructure.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/NDStructure.kt @@ -3,15 +3,38 @@ package scientifik.kmath.structures import kotlin.jvm.JvmName import kotlin.reflect.KClass - +/** + * Represents n-dimensional structure, i.e. multidimensional container of items of the same type and size. The number + * of dimensions and items in an array is defined by its shape, which is a sequence of non-negative integers that + * specify the sizes of each dimension. + * + * @param T the type of items. + */ interface NDStructure { - + /** + * The shape of structure, i.e. non-empty sequence of non-negative integers that specify sizes of dimensions of + * this structure. + */ val shape: IntArray + /** + * The count of dimensions in this structure. It should be equal to size of [shape]. + */ val dimension: Int get() = shape.size + /** + * Returns the value at the specified indices. + * + * @param index the indices. + * @return the value. + */ operator fun get(index: IntArray): T + /** + * Returns the sequence of all the elements associated by their indices. + * + * @return the lazy sequence of pairs of indices to values. + */ fun elements(): Sequence> override fun equals(other: Any?): Boolean @@ -19,6 +42,9 @@ interface NDStructure { override fun hashCode(): Int companion object { + /** + * Indicates whether some [NDStructure] is equal to another one. + */ fun equals(st1: NDStructure<*>, st2: NDStructure<*>): Boolean { if (st1 === st2) return true @@ -36,9 +62,9 @@ interface NDStructure { } /** - * Create a NDStructure with explicit buffer factory + * Creates a NDStructure with explicit buffer factory. * - * Strides should be reused if possible + * Strides should be reused if possible. */ fun build( strides: Strides, @@ -91,9 +117,24 @@ interface NDStructure { } } +/** + * Returns the value at the specified indices. + * + * @param index the indices. + * @return the value. + */ operator fun NDStructure.get(vararg index: Int): T = get(index) +/** + * Represents mutable [NDStructure]. + */ interface MutableNDStructure : NDStructure { + /** + * Inserts an item at the specified indices. + * + * @param index the indices. + * @param value the value. + */ operator fun set(index: IntArray, value: T) } @@ -104,7 +145,7 @@ inline fun MutableNDStructure.mapInPlace(action: (IntArray, T) -> T) { } /** - * A way to convert ND index to linear one and back + * A way to convert ND index to linear one and back. */ interface Strides { /** @@ -141,6 +182,9 @@ interface Strides { } } +/** + * Simple implementation of [Strides]. + */ class DefaultStrides private constructor(override val shape: IntArray) : Strides { /** * Strides for memory access @@ -180,19 +224,14 @@ class DefaultStrides private constructor(override val shape: IntArray) : Strides override val linearSize: Int get() = strides[shape.size] - override fun equals(other: Any?): Boolean { if (this === other) return true if (other !is DefaultStrides) return false - if (!shape.contentEquals(other.shape)) return false - return true } - override fun hashCode(): Int { - return shape.contentHashCode() - } + override fun hashCode(): Int = shape.contentHashCode() companion object { private val defaultStridesCache = HashMap() @@ -204,8 +243,20 @@ class DefaultStrides private constructor(override val shape: IntArray) : Strides } } +/** + * Represents [NDStructure] over [Buffer]. + * + * @param T the type of items. + */ abstract class NDBuffer : NDStructure { + /** + * The underlying buffer. + */ abstract val buffer: Buffer + + /** + * The strides to access elements of [Buffer] by linear indices. + */ abstract val strides: Strides override fun get(index: IntArray): T = buffer[strides.offset(index)] @@ -263,8 +314,8 @@ class MutableBufferNDStructure( ) : NDBuffer(), MutableNDStructure { init { - if (strides.linearSize != buffer.size) { - error("Expected buffer side of ${strides.linearSize}, but found ${buffer.size}") + require(strides.linearSize == buffer.size) { + "Expected buffer side of ${strides.linearSize}, but found ${buffer.size}" } } diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/RealNDField.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/RealNDField.kt index b89f28233..e2a1a33df 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/RealNDField.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/RealNDField.kt @@ -93,13 +93,13 @@ inline fun BufferedNDField.produceInline(crossinline initiali } /** - * Map one [RealNDElement] using function with indexes + * Map one [RealNDElement] using function with indices. */ inline fun RealNDElement.mapIndexed(crossinline transform: RealField.(index: IntArray, Double) -> Double): RealNDElement = context.produceInline { offset -> transform(strides.index(offset), buffer[offset]) } /** - * Map one [RealNDElement] using function without indexes + * Map one [RealNDElement] using function without indices. */ inline fun RealNDElement.map(crossinline transform: RealField.(Double) -> Double): RealNDElement { val array = DoubleArray(strides.linearSize) { offset -> RealField.transform(buffer[offset]) } diff --git a/kmath-memory/src/commonMain/kotlin/scientifik/memory/Memory.kt b/kmath-memory/src/commonMain/kotlin/scientifik/memory/Memory.kt index 7c6886202..a749a7074 100644 --- a/kmath-memory/src/commonMain/kotlin/scientifik/memory/Memory.kt +++ b/kmath-memory/src/commonMain/kotlin/scientifik/memory/Memory.kt @@ -1,77 +1,148 @@ package scientifik.memory +/** + * Represents a display of certain memory structure. + */ interface Memory { + /** + * The length of this memory in bytes. + */ val size: Int /** - * Get a projection of this memory (it reflects the changes in the parent memory block) + * Get a projection of this memory (it reflects the changes in the parent memory block). */ fun view(offset: Int, length: Int): Memory /** - * Create a copy of this memory, which does not know anything about this memory + * Creates an independent copy of this memory. */ fun copy(): Memory /** - * Create and possibly register a new reader + * Gets or creates a reader of this memory. */ fun reader(): MemoryReader + /** + * Gets or creates a writer of this memory. + */ fun writer(): MemoryWriter - companion object { - - } + companion object } +/** + * The interface to read primitive types in this memory. + */ interface MemoryReader { + /** + * The underlying memory. + */ val memory: Memory + /** + * Reads [Double] at certain [offset]. + */ fun readDouble(offset: Int): Double + + /** + * Reads [Float] at certain [offset]. + */ fun readFloat(offset: Int): Float + + /** + * Reads [Byte] at certain [offset]. + */ fun readByte(offset: Int): Byte + + /** + * Reads [Short] at certain [offset]. + */ fun readShort(offset: Int): Short + + /** + * Reads [Int] at certain [offset]. + */ fun readInt(offset: Int): Int + + /** + * Reads [Long] at certain [offset]. + */ fun readLong(offset: Int): Long + /** + * Disposes this reader if needed. + */ fun release() } /** - * Use the memory for read then release the reader + * Uses the memory for read then releases the reader. */ inline fun Memory.read(block: MemoryReader.() -> Unit) { - reader().apply(block).apply { release() } + reader().apply(block).release() } +/** + * The interface to write primitive types into this memory. + */ interface MemoryWriter { + /** + * The underlying memory. + */ val memory: Memory + /** + * Writes [Double] at certain [offset]. + */ fun writeDouble(offset: Int, value: Double) + + /** + * Writes [Float] at certain [offset]. + */ fun writeFloat(offset: Int, value: Float) + + /** + * Writes [Byte] at certain [offset]. + */ fun writeByte(offset: Int, value: Byte) + + /** + * Writes [Short] at certain [offset]. + */ fun writeShort(offset: Int, value: Short) + + /** + * Writes [Int] at certain [offset]. + */ fun writeInt(offset: Int, value: Int) + + /** + * Writes [Long] at certain [offset]. + */ fun writeLong(offset: Int, value: Long) + /** + * Disposes this writer if needed. + */ fun release() } /** - * Use the memory for write then release the writer + * Uses the memory for write then releases the writer. */ inline fun Memory.write(block: MemoryWriter.() -> Unit) { - writer().apply(block).apply { release() } + writer().apply(block).release() } /** - * Allocate the most effective platform-specific memory + * Allocates the most effective platform-specific memory. */ expect fun Memory.Companion.allocate(length: Int): Memory /** - * Wrap a [Memory] around existing [ByteArray]. This operation is unsafe since the array is not copied - * and could be mutated independently from the resulting [Memory] + * Wraps a [Memory] around existing [ByteArray]. This operation is unsafe since the array is not copied + * and could be mutated independently from the resulting [Memory]. */ expect fun Memory.Companion.wrap(array: ByteArray): Memory diff --git a/kmath-memory/src/commonMain/kotlin/scientifik/memory/MemorySpec.kt b/kmath-memory/src/commonMain/kotlin/scientifik/memory/MemorySpec.kt index 4d0035d09..59a93f290 100644 --- a/kmath-memory/src/commonMain/kotlin/scientifik/memory/MemorySpec.kt +++ b/kmath-memory/src/commonMain/kotlin/scientifik/memory/MemorySpec.kt @@ -7,7 +7,7 @@ package scientifik.memory */ interface MemorySpec { /** - * Size of [T] in bytes after serialization + * Size of [T] in bytes after serialization. */ val objectSize: Int @@ -24,9 +24,19 @@ interface MemorySpec { fun MemoryWriter.write(offset: Int, value: T) } +/** + * Reads the object with [spec] starting from [offset]. + */ fun MemoryReader.read(spec: MemorySpec, offset: Int): T = with(spec) { read(offset) } + +/** + * Writes the object [value] with [spec] starting from [offset]. + */ fun MemoryWriter.write(spec: MemorySpec, offset: Int, value: T): Unit = with(spec) { write(offset, value) } +/** + * Reads array of [size] objects mapped by [spec] at certain [offset]. + */ inline fun MemoryReader.readArray(spec: MemorySpec, offset: Int, size: Int): Array = Array(size) { i -> spec.run { @@ -34,7 +44,10 @@ inline fun MemoryReader.readArray(spec: MemorySpec, offset: } } +/** + * Writes [array] of objects mapped by [spec] at certain [offset]. + */ fun MemoryWriter.writeArray(spec: MemorySpec, offset: Int, array: Array): Unit = with(spec) { array.indices.forEach { i -> write(offset + i * objectSize, array[i]) } } -//TODO It is possible to add elastic MemorySpec with unknown object size \ No newline at end of file +// TODO It is possible to add elastic MemorySpec with unknown object size diff --git a/kmath-memory/src/jsMain/kotlin/scientifik/memory/DataViewMemory.kt b/kmath-memory/src/jsMain/kotlin/scientifik/memory/DataViewMemory.kt index 38ec14824..974750502 100644 --- a/kmath-memory/src/jsMain/kotlin/scientifik/memory/DataViewMemory.kt +++ b/kmath-memory/src/jsMain/kotlin/scientifik/memory/DataViewMemory.kt @@ -4,13 +4,13 @@ import org.khronos.webgl.ArrayBuffer import org.khronos.webgl.DataView import org.khronos.webgl.Int8Array -class DataViewMemory(val view: DataView) : Memory { - +private class DataViewMemory(val view: DataView) : Memory { override val size: Int get() = view.byteLength override fun view(offset: Int, length: Int): Memory { require(offset >= 0) { "offset shouldn't be negative: $offset" } require(length >= 0) { "length shouldn't be negative: $length" } + require(offset + length <= size) { "Can't view memory outside the parent region." } if (offset + length > size) throw IndexOutOfBoundsException("offset + length > size: $offset + $length > $size") @@ -33,11 +33,11 @@ class DataViewMemory(val view: DataView) : Memory { override fun readInt(offset: Int): Int = view.getInt32(offset, false) - override fun readLong(offset: Int): Long = (view.getInt32(offset, false).toLong() shl 32) or - view.getInt32(offset + 4, false).toLong() + override fun readLong(offset: Int): Long = + view.getInt32(offset, false).toLong() shl 32 or view.getInt32(offset + 4, false).toLong() override fun release() { - // does nothing on JS because of GC + // does nothing on JS } } @@ -72,7 +72,7 @@ class DataViewMemory(val view: DataView) : Memory { } override fun release() { - //does nothing on JS + // does nothing on JS } } @@ -81,13 +81,17 @@ class DataViewMemory(val view: DataView) : Memory { } /** - * Allocate the most effective platform-specific memory + * Allocates memory based on a [DataView]. */ actual fun Memory.Companion.allocate(length: Int): Memory { val buffer = ArrayBuffer(length) return DataViewMemory(DataView(buffer, 0, length)) } +/** + * Wraps a [Memory] around existing [ByteArray]. This operation is unsafe since the array is not copied + * and could be mutated independently from the resulting [Memory]. + */ actual fun Memory.Companion.wrap(array: ByteArray): Memory { @Suppress("CAST_NEVER_SUCCEEDS") val int8Array = array as Int8Array return DataViewMemory(DataView(int8Array.buffer, int8Array.byteOffset, int8Array.length)) diff --git a/kmath-memory/src/jvmMain/kotlin/scientifik/memory/ByteBufferMemory.kt b/kmath-memory/src/jvmMain/kotlin/scientifik/memory/ByteBufferMemory.kt index 9ec2b3a09..b5a0dd51b 100644 --- a/kmath-memory/src/jvmMain/kotlin/scientifik/memory/ByteBufferMemory.kt +++ b/kmath-memory/src/jvmMain/kotlin/scientifik/memory/ByteBufferMemory.kt @@ -6,19 +6,18 @@ import java.nio.file.Files import java.nio.file.Path import java.nio.file.StandardOpenOption - private class ByteBufferMemory( val buffer: ByteBuffer, val startOffset: Int = 0, override val size: Int = buffer.limit() ) : Memory { - - @Suppress("NOTHING_TO_INLINE") private inline fun position(o: Int): Int = startOffset + o override fun view(offset: Int, length: Int): Memory { - if (offset + length > size) error("Selecting a Memory view outside of memory range") + require(offset >= 0) { "offset shouldn't be negative: $offset" } + require(length >= 0) { "length shouldn't be negative: $length" } + require(offset + length <= size) { "Can't view memory outside the parent region." } return ByteBufferMemory(buffer, position(offset), length) } @@ -28,10 +27,9 @@ private class ByteBufferMemory( copy.put(buffer) copy.flip() return ByteBufferMemory(copy) - } - private val reader = object : MemoryReader { + private val reader: MemoryReader = object : MemoryReader { override val memory: Memory get() = this@ByteBufferMemory override fun readDouble(offset: Int) = buffer.getDouble(position(offset)) @@ -47,13 +45,13 @@ private class ByteBufferMemory( override fun readLong(offset: Int) = buffer.getLong(position(offset)) override fun release() { - //does nothing on JVM + // does nothing on JVM } } override fun reader(): MemoryReader = reader - private val writer = object : MemoryWriter { + private val writer: MemoryWriter = object : MemoryWriter { override val memory: Memory get() = this@ByteBufferMemory override fun writeDouble(offset: Int, value: Double) { @@ -81,7 +79,7 @@ private class ByteBufferMemory( } override fun release() { - //does nothing on JVM + // does nothing on JVM } } @@ -89,26 +87,32 @@ private class ByteBufferMemory( } /** - * Allocate the most effective platform-specific memory + * Allocates memory based on a [ByteBuffer]. */ -actual fun Memory.Companion.allocate(length: Int): Memory { - val buffer = ByteBuffer.allocate(length) - return ByteBufferMemory(buffer) -} +actual fun Memory.Companion.allocate(length: Int): Memory = + ByteBufferMemory(checkNotNull(ByteBuffer.allocate(length))) -actual fun Memory.Companion.wrap(array: ByteArray): Memory { - val buffer = ByteBuffer.wrap(array) - return ByteBufferMemory(buffer) -} +/** + * Wraps a [Memory] around existing [ByteArray]. This operation is unsafe since the array is not copied + * and could be mutated independently from the resulting [Memory]. + */ +actual fun Memory.Companion.wrap(array: ByteArray): Memory = ByteBufferMemory(checkNotNull(ByteBuffer.wrap(array))) +/** + * Wraps this [ByteBuffer] to [Memory] object. + * + * @receiver the byte buffer. + * @param startOffset the start offset. + * @param size the size of memory to map. + * @return the [Memory] object. + */ fun ByteBuffer.asMemory(startOffset: Int = 0, size: Int = limit()): Memory = ByteBufferMemory(this, startOffset, size) /** - * Use direct memory-mapped buffer from file to read something and close it afterwards. + * Uses direct memory-mapped buffer from file to read something and close it afterwards. */ -fun Path.readAsMemory(position: Long = 0, size: Long = Files.size(this), block: Memory.() -> R): R { - return FileChannel.open(this, StandardOpenOption.READ).use { +fun Path.readAsMemory(position: Long = 0, size: Long = Files.size(this), block: Memory.() -> R): R = + FileChannel.open(this, StandardOpenOption.READ).use { ByteBufferMemory(it.map(FileChannel.MapMode.READ_ONLY, position, size)).block() } -} \ No newline at end of file From cb5234a334d2ffe07403d6ac3b553fd388f54619 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Sat, 8 Aug 2020 04:57:20 +0700 Subject: [PATCH 33/54] Document AST and expressions API, implement ExtendedField over MST and Expression --- .../kotlin/scientifik/kmath/ast/MST.kt | 47 ++++++++---- .../kotlin/scientifik/kmath/ast/MstAlgebra.kt | 74 +++++++++++++------ .../scientifik/kmath/ast/MstExpression.kt | 65 ++++++++++++---- .../kotlin/scientifik/kmath/ast/parser.kt | 13 ++++ .../scientifik/kmath/expressions/Builders.kt | 8 ++ .../kmath/expressions/Expression.kt | 12 +++ .../FunctionalExpressionAlgebra.kt | 39 ++++++++-- .../scientifik/kmath/streaming/RingBuffer.kt | 2 +- 8 files changed, 202 insertions(+), 58 deletions(-) diff --git a/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MST.kt b/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MST.kt index 46558cbfb..0e8151c04 100644 --- a/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MST.kt +++ b/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MST.kt @@ -5,42 +5,54 @@ import scientifik.kmath.operations.NumericAlgebra import scientifik.kmath.operations.RealField /** - * A Mathematical Syntax Tree node for mathematical expressions + * A Mathematical Syntax Tree node for mathematical expressions. */ sealed class MST { - /** - * A node containing unparsed string + * A node containing raw string. + * + * @property value the value of this node. */ data class Symbolic(val value: String) : MST() /** - * A node containing a number + * A node containing a numeric value or scalar. + * + * @property value the value of this number. */ data class Numeric(val value: Number) : MST() /** - * A node containing an unary operation + * A node containing an unary operation. + * + * @property operation the identifier of operation. + * @property value the argument of this operation. */ data class Unary(val operation: String, val value: MST) : MST() { - companion object { - const val ABS_OPERATION = "abs" - //TODO add operations - } + companion object } /** - * A node containing binary operation + * A node containing binary operation. + * + * @property operation the identifier operation. + * @property left the left operand. + * @property right the right operand. */ data class Binary(val operation: String, val left: MST, val right: MST) : MST() { companion object } } -//TODO add a function with positional arguments - -//TODO add a function with named arguments +// TODO add a function with named arguments +/** + * Interprets the [MST] node with this [Algebra]. + * + * @receiver the algebra that provides operations. + * @param node the node to evaluate. + * @return the value of expression. + */ fun Algebra.evaluate(node: MST): T = when (node) { is MST.Numeric -> (this as? NumericAlgebra)?.number(node.value) ?: error("Numeric nodes are not supported by $this") @@ -65,4 +77,11 @@ fun Algebra.evaluate(node: MST): T = when (node) { } } -fun MST.compile(algebra: Algebra): T = algebra.evaluate(this) +/** + * Interprets the [MST] node with this [Algebra]. + * + * @receiver the node to evaluate. + * @param algebra the algebra that provides operations. + * @return the value of expression. + */ +fun MST.interpret(algebra: Algebra): T = algebra.evaluate(this) diff --git a/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MstAlgebra.kt b/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MstAlgebra.kt index 007cf57c4..b47c7cae8 100644 --- a/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MstAlgebra.kt +++ b/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MstAlgebra.kt @@ -2,6 +2,9 @@ package scientifik.kmath.ast import scientifik.kmath.operations.* +/** + * [Algebra] over [MST] nodes. + */ object MstAlgebra : NumericAlgebra { override fun number(value: Number): MST = MST.Numeric(value) @@ -14,17 +17,16 @@ object MstAlgebra : NumericAlgebra { MST.Binary(operation, left, right) } +/** + * [Space] over [MST] nodes. + */ object MstSpace : Space, NumericAlgebra { override val zero: MST = number(0.0) override fun number(value: Number): MST = MstAlgebra.number(value) override fun symbol(value: String): MST = MstAlgebra.symbol(value) - - override fun add(a: MST, b: MST): MST = - binaryOperation(SpaceOperations.PLUS_OPERATION, a, b) - - override fun multiply(a: MST, k: Number): MST = - binaryOperation(RingOperations.TIMES_OPERATION, a, number(k)) + override fun add(a: MST, b: MST): MST = binaryOperation(SpaceOperations.PLUS_OPERATION, a, b) + override fun multiply(a: MST, k: Number): MST = binaryOperation(RingOperations.TIMES_OPERATION, a, number(k)) override fun binaryOperation(operation: String, left: MST, right: MST): MST = MstAlgebra.binaryOperation(operation, left, right) @@ -32,41 +34,69 @@ object MstSpace : Space, NumericAlgebra { override fun unaryOperation(operation: String, arg: MST): MST = MstAlgebra.unaryOperation(operation, arg) } +/** + * [Ring] over [MST] nodes. + */ object MstRing : Ring, NumericAlgebra { override val zero: MST = number(0.0) override val one: MST = number(1.0) - override fun number(value: Number): MST = MstAlgebra.number(value) - override fun symbol(value: String): MST = MstAlgebra.symbol(value) - override fun add(a: MST, b: MST): MST = binaryOperation(SpaceOperations.PLUS_OPERATION, a, b) + override fun number(value: Number): MST = MstSpace.number(value) + override fun symbol(value: String): MST = MstSpace.symbol(value) + override fun add(a: MST, b: MST): MST = MstSpace.add(a, b) - override fun multiply(a: MST, k: Number): MST = - binaryOperation(RingOperations.TIMES_OPERATION, a, MstSpace.number(k)) + override fun multiply(a: MST, k: Number): MST = MstSpace.multiply(a, k) override fun multiply(a: MST, b: MST): MST = binaryOperation(RingOperations.TIMES_OPERATION, a, b) override fun binaryOperation(operation: String, left: MST, right: MST): MST = - MstAlgebra.binaryOperation(operation, left, right) + MstSpace.binaryOperation(operation, left, right) override fun unaryOperation(operation: String, arg: MST): MST = MstAlgebra.unaryOperation(operation, arg) } +/** + * [Field] over [MST] nodes. + */ object MstField : Field { override val zero: MST = number(0.0) override val one: MST = number(1.0) - override fun symbol(value: String): MST = MstAlgebra.symbol(value) - override fun number(value: Number): MST = MstAlgebra.number(value) - override fun add(a: MST, b: MST): MST = binaryOperation(SpaceOperations.PLUS_OPERATION, a, b) - - override fun multiply(a: MST, k: Number): MST = - binaryOperation(RingOperations.TIMES_OPERATION, a, MstSpace.number(k)) - - override fun multiply(a: MST, b: MST): MST = binaryOperation(RingOperations.TIMES_OPERATION, a, b) + override fun symbol(value: String): MST = MstRing.symbol(value) + override fun number(value: Number): MST = MstRing.number(value) + override fun add(a: MST, b: MST): MST = MstRing.add(a, b) + override fun multiply(a: MST, k: Number): MST = MstRing.multiply(a, k) + override fun multiply(a: MST, b: MST): MST = MstRing.multiply(a, b) override fun divide(a: MST, b: MST): MST = binaryOperation(FieldOperations.DIV_OPERATION, a, b) override fun binaryOperation(operation: String, left: MST, right: MST): MST = - MstAlgebra.binaryOperation(operation, left, right) + MstRing.binaryOperation(operation, left, right) - override fun unaryOperation(operation: String, arg: MST): MST = MstAlgebra.unaryOperation(operation, arg) + override fun unaryOperation(operation: String, arg: MST): MST = MstRing.unaryOperation(operation, arg) +} + +/** + * [ExtendedField] over [MST] nodes. + */ +object MstExtendedField : ExtendedField { + override val zero: MST = number(0.0) + override val one: MST = number(1.0) + + override fun sin(arg: MST): MST = unaryOperation(TrigonometricOperations.SIN_OPERATION, arg) + override fun cos(arg: MST): MST = unaryOperation(TrigonometricOperations.COS_OPERATION, arg) + override fun asin(arg: MST): MST = unaryOperation(InverseTrigonometricOperations.ASIN_OPERATION, arg) + override fun acos(arg: MST): MST = unaryOperation(InverseTrigonometricOperations.ACOS_OPERATION, arg) + override fun atan(arg: MST): MST = unaryOperation(InverseTrigonometricOperations.ATAN_OPERATION, arg) + override fun add(a: MST, b: MST): MST = MstField.add(a, b) + override fun multiply(a: MST, k: Number): MST = MstField.multiply(a, k) + override fun multiply(a: MST, b: MST): MST = MstField.multiply(a, b) + override fun divide(a: MST, b: MST): MST = MstField.divide(a, b) + override fun power(arg: MST, pow: Number): MST = binaryOperation(PowerOperations.POW_OPERATION, arg, number(pow)) + override fun exp(arg: MST): MST = unaryOperation(ExponentialOperations.EXP_OPERATION, arg) + override fun ln(arg: MST): MST = unaryOperation(ExponentialOperations.LN_OPERATION, arg) + + override fun binaryOperation(operation: String, left: MST, right: MST): MST = + MstField.binaryOperation(operation, left, right) + + override fun unaryOperation(operation: String, arg: MST): MST = MstField.unaryOperation(operation, arg) } diff --git a/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MstExpression.kt b/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MstExpression.kt index 1468c3ad4..59f3f15d8 100644 --- a/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MstExpression.kt +++ b/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MstExpression.kt @@ -1,19 +1,16 @@ package scientifik.kmath.ast -import scientifik.kmath.expressions.Expression -import scientifik.kmath.expressions.FunctionalExpressionField -import scientifik.kmath.expressions.FunctionalExpressionRing -import scientifik.kmath.expressions.FunctionalExpressionSpace +import scientifik.kmath.expressions.* import scientifik.kmath.operations.* /** - * The expression evaluates MST on-flight. Should be much faster than functional expression, but slower than ASM-generated expressions. + * The expression evaluates MST on-flight. Should be much faster than functional expression, but slower than + * ASM-generated expressions. + * + * @property algebra the algebra that provides operations. + * @property mst the [MST] node. */ class MstExpression(val algebra: Algebra, val mst: MST) : Expression { - - /** - * Substitute algebra raw value - */ private inner class InnerAlgebra(val arguments: Map) : NumericAlgebra { override fun symbol(value: String): T = arguments[value] ?: algebra.symbol(value) override fun unaryOperation(operation: String, arg: T): T = algebra.unaryOperation(operation, arg) @@ -30,26 +27,62 @@ class MstExpression(val algebra: Algebra, val mst: MST) : Expression { override fun invoke(arguments: Map): T = InnerAlgebra(arguments).evaluate(mst) } - +/** + * Builds [MstExpression] over [Algebra]. + */ inline fun , E : Algebra> A.mst( mstAlgebra: E, block: E.() -> MST ): MstExpression = MstExpression(this, mstAlgebra.block()) +/** + * Builds [MstExpression] over [Space]. + */ inline fun Space.mstInSpace(block: MstSpace.() -> MST): MstExpression = MstExpression(this, MstSpace.block()) +/** + * Builds [MstExpression] over [Ring]. + */ inline fun Ring.mstInRing(block: MstRing.() -> MST): MstExpression = MstExpression(this, MstRing.block()) +/** + * Builds [MstExpression] over [Field]. + */ inline fun Field.mstInField(block: MstField.() -> MST): MstExpression = MstExpression(this, MstField.block()) -inline fun > FunctionalExpressionSpace.mstInSpace(block: MstSpace.() -> MST): MstExpression = - algebra.mstInSpace(block) +/** + * Builds [MstExpression] over [ExtendedField]. + */ +inline fun Field.mstInExtendedField(block: MstExtendedField.() -> MST): MstExpression = + MstExpression(this, MstExtendedField.block()) -inline fun > FunctionalExpressionRing.mstInRing(block: MstRing.() -> MST): MstExpression = - algebra.mstInRing(block) +/** + * Builds [MstExpression] over [FunctionalExpressionSpace]. + */ +inline fun > FunctionalExpressionSpace.mstInSpace( + block: MstSpace.() -> MST +): MstExpression = algebra.mstInSpace(block) -inline fun > FunctionalExpressionField.mstInField(block: MstField.() -> MST): MstExpression = - algebra.mstInField(block) \ No newline at end of file +/** + * Builds [MstExpression] over [FunctionalExpressionRing]. + */ +inline fun > FunctionalExpressionRing.mstInRing( + block: MstRing.() -> MST +): MstExpression = algebra.mstInRing(block) + +/** + * Builds [MstExpression] over [FunctionalExpressionField]. + */ +inline fun > FunctionalExpressionField.mstInField( + block: MstField.() -> MST +): MstExpression = algebra.mstInField(block) + +/** + * Builds [MstExpression] over [FunctionalExpressionExtendedField]. + */ +inline fun > FunctionalExpressionExtendedField.mstInExtendedField( + block: MstExtendedField.() -> MST +): MstExpression = algebra.mstInExtendedField(block) diff --git a/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/parser.kt b/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/parser.kt index 11797d79f..cba335a8d 100644 --- a/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/parser.kt +++ b/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/parser.kt @@ -80,5 +80,18 @@ object ArithmeticsEvaluator : Grammar() { override val rootParser: Parser by subSumChain } +/** + * Tries to parse the string into [MST]. + * + * @receiver the string to parse. + * @return the [MST] node. + */ fun String.tryParseMath(): ParseResult = ArithmeticsEvaluator.tryParseToEnd(this) + +/** + * Parses the string into [MST]. + * + * @receiver the string to parse. + * @return the [MST] node. + */ fun String.parseMath(): MST = ArithmeticsEvaluator.parseToEnd(this) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/Builders.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/Builders.kt index 834ef9e24..8cd6e28f8 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/Builders.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/Builders.kt @@ -1,5 +1,6 @@ package scientifik.kmath.expressions +import scientifik.kmath.operations.ExtendedField import scientifik.kmath.operations.Field import scientifik.kmath.operations.Ring import scientifik.kmath.operations.Space @@ -21,3 +22,10 @@ fun Ring.ringExpression(block: FunctionalExpressionRing>.() -> */ fun Field.fieldExpression(block: FunctionalExpressionField>.() -> Expression): Expression = FunctionalExpressionField(this).run(block) + +/** + * Creates a functional expression with this [ExtendedField]. + */ +fun ExtendedField.fieldExpression( + block: FunctionalExpressionExtendedField>.() -> Expression +): Expression = FunctionalExpressionExtendedField(this).run(block) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/Expression.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/Expression.kt index b21a414f5..380822f78 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/Expression.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/Expression.kt @@ -6,6 +6,12 @@ import scientifik.kmath.operations.Algebra * An elementary function that could be invoked on a map of arguments */ interface Expression { + /** + * Calls this expression from arguments. + * + * @param arguments the map of arguments. + * @return the value. + */ operator fun invoke(arguments: Map): T companion object @@ -19,6 +25,12 @@ fun Algebra.expression(block: Algebra.(arguments: Map) -> T override fun invoke(arguments: Map): T = block(arguments) } +/** + * Calls this expression from arguments. + * + * @param pairs the pair of arguments' names to values. + * @return the value. + */ operator fun Expression.invoke(vararg pairs: Pair): T = invoke(mapOf(*pairs)) /** diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressionAlgebra.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressionAlgebra.kt index 5a8a10717..dd5fb572a 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressionAlgebra.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressionAlgebra.kt @@ -40,7 +40,6 @@ internal class FunctionalConstProductExpression( * @param algebra The algebra to provide for Expressions built. */ abstract class FunctionalExpressionAlgebra>(val algebra: A) : ExpressionAlgebra> { - /** * Builds an Expression of constant expression which does not depend on arguments. */ @@ -69,14 +68,13 @@ abstract class FunctionalExpressionAlgebra>(val algebra: A) : */ open class FunctionalExpressionSpace>(algebra: A) : FunctionalExpressionAlgebra(algebra), Space> { - override val zero: Expression get() = const(algebra.zero) /** * Builds an Expression of addition of two another expressions. */ override fun add(a: Expression, b: Expression): Expression = - FunctionalBinaryOperation(algebra, SpaceOperations.PLUS_OPERATION, a, b) + binaryOperation(SpaceOperations.PLUS_OPERATION, a, b) /** * Builds an Expression of multiplication of expression by number. @@ -105,7 +103,7 @@ open class FunctionalExpressionRing(algebra: A) : FunctionalExpressionSpac * Builds an Expression of multiplication of two expressions. */ override fun multiply(a: Expression, b: Expression): Expression = - FunctionalBinaryOperation(algebra, RingOperations.TIMES_OPERATION, a, b) + binaryOperation(RingOperations.TIMES_OPERATION, a, b) operator fun Expression.times(arg: T): Expression = this * const(arg) operator fun T.times(arg: Expression): Expression = arg * this @@ -124,7 +122,7 @@ open class FunctionalExpressionField(algebra: A) : * Builds an Expression of division an expression by another one. */ override fun divide(a: Expression, b: Expression): Expression = - FunctionalBinaryOperation(algebra, FieldOperations.DIV_OPERATION, a, b) + binaryOperation(FieldOperations.DIV_OPERATION, a, b) operator fun Expression.div(arg: T): Expression = this / const(arg) operator fun T.div(arg: Expression): Expression = arg / this @@ -136,6 +134,34 @@ open class FunctionalExpressionField(algebra: A) : super.binaryOperation(operation, left, right) } +open class FunctionalExpressionExtendedField(algebra: A) : + FunctionalExpressionField(algebra), + ExtendedField> where A : ExtendedField, A : NumericAlgebra { + override fun sin(arg: Expression): Expression = unaryOperation(TrigonometricOperations.SIN_OPERATION, arg) + override fun cos(arg: Expression): Expression = unaryOperation(TrigonometricOperations.COS_OPERATION, arg) + + override fun asin(arg: Expression): Expression = + unaryOperation(InverseTrigonometricOperations.ASIN_OPERATION, arg) + + override fun acos(arg: Expression): Expression = + unaryOperation(InverseTrigonometricOperations.ACOS_OPERATION, arg) + + override fun atan(arg: Expression): Expression = + unaryOperation(InverseTrigonometricOperations.ATAN_OPERATION, arg) + + override fun power(arg: Expression, pow: Number): Expression = + binaryOperation(PowerOperations.POW_OPERATION, arg, number(pow)) + + override fun exp(arg: Expression): Expression = unaryOperation(ExponentialOperations.EXP_OPERATION, arg) + override fun ln(arg: Expression): Expression = unaryOperation(ExponentialOperations.LN_OPERATION, arg) + + override fun unaryOperation(operation: String, arg: Expression): Expression = + super.unaryOperation(operation, arg) + + override fun binaryOperation(operation: String, left: Expression, right: Expression): Expression = + super.binaryOperation(operation, left, right) +} + inline fun > A.expressionInSpace(block: FunctionalExpressionSpace.() -> Expression): Expression = FunctionalExpressionSpace(this).block() @@ -144,3 +170,6 @@ inline fun > A.expressionInRing(block: FunctionalExpressionRing> A.expressionInField(block: FunctionalExpressionField.() -> Expression): Expression = FunctionalExpressionField(this).block() + +inline fun > A.expressionInExtendedField(block: FunctionalExpressionExtendedField.() -> Expression): Expression = + FunctionalExpressionExtendedField(this).block() diff --git a/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/streaming/RingBuffer.kt b/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/streaming/RingBuffer.kt index 6b99e34ff..fca9ffb9f 100644 --- a/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/streaming/RingBuffer.kt +++ b/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/streaming/RingBuffer.kt @@ -11,7 +11,7 @@ import kotlin.reflect.KClass * Thread-safe ring buffer */ @Suppress("UNCHECKED_CAST") -internal class RingBuffer( +class RingBuffer( private val buffer: MutableBuffer, private var startIndex: Int = 0, size: Int = 0 From 6114099e68fad71e169ec86ce4dac8925fe9e6da Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Sat, 8 Aug 2020 15:51:04 +0700 Subject: [PATCH 34/54] Edit doc files, update readmes, document coroutines API --- doc/algebra.md | 124 ++++++++++-------- doc/buffers.md | 4 +- doc/codestyle.md | 34 +++-- doc/linear.md | 2 +- doc/nd-structure.md | 2 +- kmath-ast/README.md | 8 +- kmath-core/README.md | 40 ++++++ kmath-core/build.gradle.kts | 8 +- .../scientifik/kmath/operations/Algebra.kt | 9 +- .../kmath/chains/BlockingIntChain.kt | 2 +- .../kmath/chains/BlockingRealChain.kt | 2 +- .../kotlin/scientifik/kmath/chains/Chain.kt | 21 ++- .../scientifik/kmath/chains/flowExtra.kt | 2 +- .../kmath/coroutines/coroutinesExtra.kt | 13 +- .../scientifik/kmath/streaming/BufferFlow.kt | 4 +- .../scientifik/kmath/streaming/RingBuffer.kt | 8 +- .../scientifik/kmath/chains/ChainExt.kt | 2 +- .../kmath/structures/LazyNDStructure.kt | 21 ++- .../kmath/streaming/BufferFlowTest.kt | 5 +- .../kmath/streaming/RingBufferTest.kt | 12 +- 20 files changed, 189 insertions(+), 134 deletions(-) create mode 100644 kmath-core/README.md diff --git a/doc/algebra.md b/doc/algebra.md index 015f4fc82..b1b77a31f 100644 --- a/doc/algebra.md +++ b/doc/algebra.md @@ -1,110 +1,124 @@ -# Algebra and algebra elements +# Algebraic Structures and Algebraic Elements -The mathematical operations in `kmath` are generally separated from mathematical objects. -This means that in order to perform an operation, say `+`, one needs two objects of a type `T` and -and algebra context which defines appropriate operation, say `Space`. Next one needs to run actual operation -in the context: +The mathematical operations in KMath are generally separated from mathematical objects. This means that to perform an +operation, say `+`, one needs two objects of a type `T` and an algebra context, which draws appropriate operation up, +say `Space`. Next one needs to run the actual operation in the context: ```kotlin -val a: T -val b: T -val space: Space +import scientifik.kmath.operations.* -val c = space.run{a + b} +val a: T = ... +val b: T = ... +val space: Space = ... + +val c = space { a + b } ``` -From the first glance, this distinction seems to be a needless complication, but in fact one needs -to remember that in mathematics, one could define different operations on the same objects. For example, -one could use different types of geometry for vectors. +At first glance, this distinction seems to be a needless complication, but in fact one needs to remember that in +mathematics, one could draw up different operations on same objects. For example, one could use different types of +geometry for vectors. -## Algebra hierarchy +## Algebraic Structures Mathematical contexts have the following hierarchy: -**Space** <- **Ring** <- **Field** +**Algebra** ← **Space** ← **Ring** ← **Field** -All classes follow abstract mathematical constructs. -[Space](http://mathworld.wolfram.com/Space.html) defines `zero` element, addition operation and multiplication by constant, -[Ring](http://mathworld.wolfram.com/Ring.html) adds multiplication and unit `one` element, -[Field](http://mathworld.wolfram.com/Field.html) adds division operation. +These interfaces follow real algebraic structures: -Typical case of `Field` is the `RealField` which works on doubles. And typical case of `Space` is a `VectorSpace`. +- [Space](https://mathworld.wolfram.com/VectorSpace.html) defines addition, its neutral element (i.e. 0) and scalar +multiplication; +- [Ring](http://mathworld.wolfram.com/Ring.html) adds multiplication and its neutral element (i.e. 1); +- [Field](http://mathworld.wolfram.com/Field.html) adds division operation. -In some cases algebra context could hold additional operation like `exp` or `sin`, in this case it inherits appropriate -interface. Also a context could have an operation which produces an element outside of its context. For example -`Matrix` `dot` operation produces a matrix with new dimensions which can be incompatible with initial matrix in -terms of linear operations. +A typical implementation of `Field` is the `RealField` which works on doubles, and `VectorSpace` for `Space`. -## Algebra element +In some cases algebra context can hold additional operations like `exp` or `sin`, and then it inherits appropriate +interface. Also, contexts may have operations, which produce elements outside of the context. For example, `Matrix.dot` +operation produces a matrix with new dimensions, which can be incompatible with initial matrix in terms of linear +operations. -In order to achieve more familiar behavior (where you apply operations directly to mathematical objects), without involving contexts -`kmath` introduces special type objects called `MathElement`. A `MathElement` is basically some object coupled to +## Algebraic Element + +To achieve more familiar behavior (where you apply operations directly to mathematical objects), without involving +contexts KMath submits special type objects called `MathElement`. A `MathElement` is basically some object coupled to a mathematical context. For example `Complex` is the pair of real numbers representing real and imaginary parts, -but it also holds reference to the `ComplexField` singleton which allows to perform direct operations on `Complex` +but it also holds reference to the `ComplexField` singleton, which allows performing direct operations on `Complex` numbers without explicit involving the context like: ```kotlin - val c1 = Complex(1.0, 1.0) - val c2 = Complex(1.0, -1.0) - val c3 = c1 + c2 + 3.0.toComplex() - //or with field notation: - val c4 = ComplexField.run{c1 + i - 2.0} +import scientifik.kmath.operations.* + +// Using elements +val c1 = Complex(1.0, 1.0) +val c2 = Complex(1.0, -1.0) +val c3 = c1 + c2 + 3.0.toComplex() + +// Using context +val c4 = ComplexField { c1 + i - 2.0 } ``` Both notations have their pros and cons. -The hierarchy for algebra elements follows the hierarchy for the corresponding algebra. +The hierarchy for algebraic elements follows the hierarchy for the corresponding algebraic structures. -**MathElement** <- **SpaceElement** <- **RingElement** <- **FieldElement** +**MathElement** ← **SpaceElement** ← **RingElement** ← **FieldElement** -**MathElement** is the generic common ancestor of the class with context. +`MathElement` is the generic common ancestor of the class with context. -One important distinction between algebra elements and algebra contexts is that algebra element has three type parameters: +One major distinction between algebraic elements and algebraic contexts is that elements have three type +parameters: -1. The type of elements, field operates on. -2. The self-type of the element returned from operation (must be algebra element). +1. The type of elements, the field operates on. +2. The self-type of the element returned from operation (which has to be an algebraic element). 3. The type of the algebra over first type-parameter. -The middle type is needed in case algebra members do not store context. For example, it is not possible to add -a context to regular `Double`. The element performs automatic conversions from context types and back. -One should used context operations in all important places. The performance of element operations is not guaranteed. +The middle type is needed for of algebra members do not store context. For example, it is impossible to add a context +to regular `Double`. The element performs automatic conversions from context types and back. One should use context +operations in all performance-critical places. The performance of element operations is not guaranteed. -## Spaces and fields +## Spaces and Fields -An obvious first choice of mathematical objects to implement in a context-oriented style are algebraic elements like spaces, -rings and fields. Those are located in the `scientifik.kmath.operations.Algebra.kt` file. Alongside common contexts, the file includes definitions for algebra elements like `FieldElement`. A `FieldElement` object -stores a reference to the `Field` which contains additive and multiplicative operations, meaning -it has one fixed context attached and does not require explicit external context. So those `MathElements` can be operated without context: +KMath submits both contexts and elements for builtin algebraic structures: ```kotlin +import scientifik.kmath.operations.* + val c1 = Complex(1.0, 2.0) val c2 = ComplexField.i + val c3 = c1 + c2 +// or +val c3 = ComplexField { c1 + c2 } ``` -`ComplexField` also features special operations to mix complex and real numbers, for example: +Also, `ComplexField` features special operations to mix complex and real numbers, for example: ```kotlin +import scientifik.kmath.operations.* + val c1 = Complex(1.0, 2.0) -val c2 = ComplexField.run{ c1 - 1.0} // Returns: [re:0.0, im: 2.0] -val c3 = ComplexField.run{ c1 - i*2.0} +val c2 = ComplexField { c1 - 1.0 } // Returns: Complex(re=0.0, im=2.0) +val c3 = ComplexField { c1 - i * 2.0 } ``` -**Note**: In theory it is possible to add behaviors directly to the context, but currently kotlin syntax does not support -that. Watch [KT-10468](https://youtrack.jetbrains.com/issue/KT-10468) and [KEEP-176](https://github.com/Kotlin/KEEP/pull/176) for updates. +**Note**: In theory it is possible to add behaviors directly to the context, but as for now Kotlin does not support +that. Watch [KT-10468](https://youtrack.jetbrains.com/issue/KT-10468) and +[KEEP-176](https://github.com/Kotlin/KEEP/pull/176) for updates. ## Nested fields -Contexts allow one to build more complex structures. For example, it is possible to create a `Matrix` from complex elements like so: +Contexts allow one to build more complex structures. For example, it is possible to create a `Matrix` from complex +elements like so: ```kotlin -val element = NDElement.complex(shape = intArrayOf(2,2)){ index: IntArray -> +val element = NDElement.complex(shape = intArrayOf(2, 2)) { index: IntArray -> Complex(index[0].toDouble() - index[1].toDouble(), index[0].toDouble() + index[1].toDouble()) } ``` -The `element` in this example is a member of the `Field` of 2-d structures, each element of which is a member of its own -`ComplexField`. The important thing is one does not need to create a special n-d class to hold complex +The `element` in this example is a member of the `Field` of 2D structures, each element of which is a member of its own +`ComplexField`. It is important one does not need to create a special n-d class to hold complex numbers and implement operations on it, one just needs to provide a field for its elements. **Note**: Fields themselves do not solve the problem of JVM boxing, but it is possible to solve with special contexts like diff --git a/doc/buffers.md b/doc/buffers.md index 52a9df86e..679bd4e78 100644 --- a/doc/buffers.md +++ b/doc/buffers.md @@ -1,4 +1,5 @@ # Buffers + Buffer is one of main building blocks of kmath. It is a basic interface allowing random-access read and write (with `MutableBuffer`). There are different types of buffers: @@ -12,4 +13,5 @@ Some kmath features require a `BufferFactory` class to operate properly. A gener buffer for given reified type (for types with custom memory buffer it still better to use their own `MemoryBuffer.create()` factory). ## Buffer performance -One should avoid using default boxing buffer wherever it is possible. Try to use primitive buffers or memory buffers instead \ No newline at end of file + +One should avoid using default boxing buffer wherever it is possible. Try to use primitive buffers or memory buffers instead diff --git a/doc/codestyle.md b/doc/codestyle.md index 53789f7b2..541dc4973 100644 --- a/doc/codestyle.md +++ b/doc/codestyle.md @@ -1,22 +1,34 @@ -# Local coding conventions +# Coding Conventions -Kmath and other `scientifik` projects use general [kotlin code conventions](https://kotlinlang.org/docs/reference/coding-conventions.html), but with a number of small changes and clarifications. +KMath code follows general [Kotlin conventions](https://kotlinlang.org/docs/reference/coding-conventions.html), but +with a number of small changes and clarifications. -## Utility class names -File name should coincide with a name of one of the classes contained in the file or start with small letter and describe its contents. +## Utility Class Naming -The code convention [here](https://kotlinlang.org/docs/reference/coding-conventions.html#source-file-names) says that file names should start with capital letter even if file does not contain classes. Yet starting utility classes and aggregators with a small letter seems to be a good way to clearly visually separate those files. +Filename should coincide with a name of one of the classes contained in the file or start with small letter and +describe its contents. + +The code convention [here](https://kotlinlang.org/docs/reference/coding-conventions.html#source-file-names) says that +file names should start with a capital letter even if file does not contain classes. Yet starting utility classes and +aggregators with a small letter seems to be a good way to visually separate those files. This convention could be changed in future in a non-breaking way. -## Private variable names -Private variable names could start with underscore `_` in case the private mutable variable is shadowed by the public read-only value with the same meaning. +## Private Variable Naming -Code convention do not permit underscores in names, but is is sometimes useful to "underscore" the fact that public and private versions define the same entity. It is allowed only for private variables. +Private variables' names may start with underscore `_` for of the private mutable variable is shadowed by the public +read-only value with the same meaning. + +This rule does not permit underscores in names, but it is sometimes useful to "underscore" the fact that public and +private versions draw up the same entity. It is allowed only for private variables. This convention could be changed in future in a non-breaking way. -## Functions and properties one-liners -Use one-liners when they occupy single code window line both for functions and properties with getters like `val b: String get() = "fff"`. The same should be done with multiline expressions when they could be cleanly separated. +## Functions and Properties One-liners -There is not general consensus whenever use `fun a() = {}` or `fun a(){return}`. Yet from reader perspective one-lines seem to better show that the property or function is easily calculated. \ No newline at end of file +Use one-liners when they occupy single code window line both for functions and properties with getters like +`val b: String get() = "fff"`. The same should be performed with multiline expressions when they could be +cleanly separated. + +There is no universal consensus whenever use `fun a() = ...` or `fun a() { return ... }`. Yet from reader outlook +one-lines seem to better show that the property or function is easily calculated. diff --git a/doc/linear.md b/doc/linear.md index bbcc435ba..883df275e 100644 --- a/doc/linear.md +++ b/doc/linear.md @@ -1,6 +1,6 @@ ## Basic linear algebra layout -Kmath support for linear algebra organized in a context-oriented way. Meaning that operations are in most cases declared +KMath support for linear algebra organized in a context-oriented way. Meaning that operations are in most cases declared in context classes, and are not the members of classes that store data. This allows more flexible approach to maintain multiple back-ends. The new operations added as extensions to contexts instead of being member functions of data structures. diff --git a/doc/nd-structure.md b/doc/nd-structure.md index cf13c6a29..835304b9f 100644 --- a/doc/nd-structure.md +++ b/doc/nd-structure.md @@ -1,4 +1,4 @@ -# Nd-structure generation and operations +# ND-structure generation and operations **TODO** diff --git a/kmath-ast/README.md b/kmath-ast/README.md index a59688c8e..2339d0426 100644 --- a/kmath-ast/README.md +++ b/kmath-ast/README.md @@ -1,4 +1,4 @@ -# Abstract syntax tree expression representation and operations (`kmath-ast`) +# Abstract Syntax Tree Expression Representation and Operations (`kmath-ast`) This subproject implements the following features: @@ -38,7 +38,7 @@ This subproject implements the following features: > ``` > -## Dynamic expression code generation with ObjectWeb ASM +## Dynamic Expression Code Generation with ObjectWeb ASM `kmath-ast` JVM module supports runtime code generation to eliminate overhead of tree traversal. Code generator builds a special implementation of `Expression` with implemented `invoke` function. @@ -46,7 +46,7 @@ a special implementation of `Expression` with implemented `invoke` function. For example, the following builder: ```kotlin - RealField.mstInField { symbol("x") + 2 }.compile() +RealField.mstInField { symbol("x") + 2 }.compile() ``` … leads to generation of bytecode, which can be decompiled to the following Java class: @@ -75,7 +75,7 @@ public final class AsmCompiledExpression_1073786867_0 implements Expression #### Artifact: +> This module is distributed in the artifact `scientifik:kmath-core:0.1.4-dev-8`. +> +> **Gradle:** +> +> ```gradle +> repositories { +> maven { url 'https://dl.bintray.com/mipt-npm/scientifik' } +> maven { url 'https://dl.bintray.com/mipt-npm/dev' } +> maven { url https://dl.bintray.com/hotkeytlt/maven' } +> } +> +> dependencies { +> implementation 'scientifik:kmath-core:0.1.4-dev-8' +> } +> ``` +> **Gradle Kotlin DSL:** +> +> ```kotlin +> repositories { +> maven("https://dl.bintray.com/mipt-npm/scientifik") +> maven("https://dl.bintray.com/mipt-npm/dev") +> maven("https://dl.bintray.com/hotkeytlt/maven") +> } +> +> dependencies {`` +> implementation("scientifik:kmath-core:0.1.4-dev-8") +> } +> ``` diff --git a/kmath-core/build.gradle.kts b/kmath-core/build.gradle.kts index 18c0cc771..bea0fbf42 100644 --- a/kmath-core/build.gradle.kts +++ b/kmath-core/build.gradle.kts @@ -1,11 +1,7 @@ -plugins { - id("scientifik.mpp") -} +plugins { id("scientifik.mpp") } kotlin.sourceSets { commonMain { - dependencies { - api(project(":kmath-memory")) - } + dependencies { api(project(":kmath-memory")) } } } diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Algebra.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Algebra.kt index aa190b31f..eb44cf7a7 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Algebra.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Algebra.kt @@ -58,7 +58,8 @@ interface NumericAlgebra : Algebra { inline operator fun , R> A.invoke(block: A.() -> R): R = run(block) /** - * Represents semigroup, i.e. algebraic structure with associative binary operation called "addition". + * Represents semispace, i.e. algebraic structure with associative binary operation called "addition" as well as + * multiplication by scalars. * * In KMath groups are called spaces, and also define multiplication of element by [Number]. * @@ -174,10 +175,8 @@ interface SpaceOperations : Algebra { } /** - * Represents group, i.e. algebraic structure with associative binary operation called "addition" and its neutral - * element. - * - * In KMath groups are called spaces, and also define multiplication of element by [Number]. + * Represents linear space, i.e. algebraic structure with associative binary operation called "addition" and its neutral + * element as well as multiplication by scalars. * * @param T the type of element of this group. */ diff --git a/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/BlockingIntChain.kt b/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/BlockingIntChain.kt index 6ec84d5c7..e9b499d71 100644 --- a/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/BlockingIntChain.kt +++ b/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/BlockingIntChain.kt @@ -9,4 +9,4 @@ abstract class BlockingIntChain : Chain { override suspend fun next(): Int = nextInt() fun nextBlock(size: Int): IntArray = IntArray(size) { nextInt() } -} \ No newline at end of file +} diff --git a/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/BlockingRealChain.kt b/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/BlockingRealChain.kt index 6b69d2734..ab819d327 100644 --- a/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/BlockingRealChain.kt +++ b/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/BlockingRealChain.kt @@ -9,4 +9,4 @@ abstract class BlockingRealChain : Chain { override suspend fun next(): Double = nextDouble() fun nextBlock(size: Int): DoubleArray = DoubleArray(size) { nextDouble() } -} \ No newline at end of file +} diff --git a/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/Chain.kt b/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/Chain.kt index 5635499e5..6cc9770af 100644 --- a/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/Chain.kt +++ b/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/Chain.kt @@ -22,12 +22,11 @@ import kotlinx.coroutines.flow.FlowCollector import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock - /** * A not-necessary-Markov chain of some type * @param R - the chain element type */ -interface Chain: Flow { +interface Chain : Flow { /** * Generate next value, changing state if needed */ @@ -41,7 +40,7 @@ interface Chain: Flow { @OptIn(InternalCoroutinesApi::class) override suspend fun collect(collector: FlowCollector) { kotlinx.coroutines.flow.flow { - while (true){ + while (true) { emit(next()) } }.collect(collector) @@ -71,7 +70,7 @@ class MarkovChain(private val seed: suspend () -> R, private val ge private var value: R? = null - fun value() = value + fun value(): R? = value override suspend fun next(): R { mutex.withLock { @@ -97,12 +96,11 @@ class StatefulChain( private val forkState: ((S) -> S), private val gen: suspend S.(R) -> R ) : Chain { - - private val mutex = Mutex() + private val mutex: Mutex = Mutex() private var value: R? = null - fun value() = value + fun value(): R? = value override suspend fun next(): R { mutex.withLock { @@ -112,9 +110,7 @@ class StatefulChain( } } - override fun fork(): Chain { - return StatefulChain(forkState(state), seed, forkState, gen) - } + override fun fork(): Chain = StatefulChain(forkState(state), seed, forkState, gen) } /** @@ -163,7 +159,8 @@ fun Chain.collect(mapper: suspend (Chain) -> R): Chain = object fun Chain.collectWithState(state: S, stateFork: (S) -> S, mapper: suspend S.(Chain) -> R): Chain = object : Chain { override suspend fun next(): R = state.mapper(this@collectWithState) - override fun fork(): Chain = this@collectWithState.fork().collectWithState(stateFork(state), stateFork, mapper) + override fun fork(): Chain = + this@collectWithState.fork().collectWithState(stateFork(state), stateFork, mapper) } /** @@ -173,4 +170,4 @@ fun Chain.zip(other: Chain, block: suspend (T, U) -> R): Chain = this@zip.fork().zip(other.fork(), block) -} \ No newline at end of file +} diff --git a/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/flowExtra.kt b/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/flowExtra.kt index bfd16d763..e8537304c 100644 --- a/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/flowExtra.kt +++ b/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/flowExtra.kt @@ -24,4 +24,4 @@ fun Flow.mean(space: Space): Flow = with(space) { this.num += 1 } }.map { it.sum / it.num } -} \ No newline at end of file +} diff --git a/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/coroutines/coroutinesExtra.kt b/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/coroutines/coroutinesExtra.kt index fdde62304..7e00b30a1 100644 --- a/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/coroutines/coroutinesExtra.kt +++ b/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/coroutines/coroutinesExtra.kt @@ -4,7 +4,8 @@ import kotlinx.coroutines.* import kotlinx.coroutines.channels.produce import kotlinx.coroutines.flow.* -val Dispatchers.Math: CoroutineDispatcher get() = Dispatchers.Default +val Dispatchers.Math: CoroutineDispatcher + get() = Default /** * An imitator of [Deferred] which holds a suspended function block and dispatcher @@ -42,7 +43,7 @@ fun Flow.async( } @FlowPreview -fun AsyncFlow.map(action: (T) -> R) = +fun AsyncFlow.map(action: (T) -> R): AsyncFlow = AsyncFlow(deferredFlow.map { input -> //TODO add function composition LazyDeferred(input.dispatcher) { @@ -82,9 +83,9 @@ suspend fun AsyncFlow.collect(concurrency: Int, collector: FlowCollector< @ExperimentalCoroutinesApi @FlowPreview -suspend fun AsyncFlow.collect(concurrency: Int, action: suspend (value: T) -> Unit): Unit { +suspend fun AsyncFlow.collect(concurrency: Int, action: suspend (value: T) -> Unit) { collect(concurrency, object : FlowCollector { - override suspend fun emit(value: T) = action(value) + override suspend fun emit(value: T): Unit = action(value) }) } @@ -94,9 +95,7 @@ fun Flow.mapParallel( dispatcher: CoroutineDispatcher = Dispatchers.Default, transform: suspend (T) -> R ): Flow { - return flatMapMerge{ value -> + return flatMapMerge { value -> flow { emit(transform(value)) } }.flowOn(dispatcher) } - - diff --git a/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/streaming/BufferFlow.kt b/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/streaming/BufferFlow.kt index 54da66bb7..9b7e82da5 100644 --- a/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/streaming/BufferFlow.kt +++ b/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/streaming/BufferFlow.kt @@ -11,7 +11,7 @@ import scientifik.kmath.structures.asBuffer /** * Create a [Flow] from buffer */ -fun Buffer.asFlow() = iterator().asFlow() +fun Buffer.asFlow(): Flow = iterator().asFlow() /** * Flat map a [Flow] of [Buffer] into continuous [Flow] of elements @@ -83,4 +83,4 @@ fun Flow.windowed(window: Int): Flow> = flow { ringBuffer.push(element) emit(ringBuffer.snapshot()) } -} \ No newline at end of file +} diff --git a/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/streaming/RingBuffer.kt b/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/streaming/RingBuffer.kt index fca9ffb9f..245d003b3 100644 --- a/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/streaming/RingBuffer.kt +++ b/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/streaming/RingBuffer.kt @@ -5,7 +5,6 @@ import kotlinx.coroutines.sync.withLock import scientifik.kmath.structures.Buffer import scientifik.kmath.structures.MutableBuffer import scientifik.kmath.structures.VirtualBuffer -import kotlin.reflect.KClass /** * Thread-safe ring buffer @@ -16,8 +15,7 @@ class RingBuffer( private var startIndex: Int = 0, size: Int = 0 ) : Buffer { - - private val mutex = Mutex() + private val mutex: Mutex = Mutex() override var size: Int = size private set @@ -28,7 +26,7 @@ class RingBuffer( return buffer[startIndex.forward(index)] as T } - fun isFull() = size == buffer.size + fun isFull(): Boolean = size == buffer.size /** * Iterator could provide wrong results if buffer is changed in initialization (iteration is safe) @@ -90,4 +88,4 @@ class RingBuffer( return RingBuffer(buffer) } } -} \ No newline at end of file +} diff --git a/kmath-coroutines/src/jvmMain/kotlin/scientifik/kmath/chains/ChainExt.kt b/kmath-coroutines/src/jvmMain/kotlin/scientifik/kmath/chains/ChainExt.kt index 013ea2922..0a3c67e00 100644 --- a/kmath-coroutines/src/jvmMain/kotlin/scientifik/kmath/chains/ChainExt.kt +++ b/kmath-coroutines/src/jvmMain/kotlin/scientifik/kmath/chains/ChainExt.kt @@ -6,7 +6,7 @@ import kotlin.sequences.Sequence /** * Represent a chain as regular iterator (uses blocking calls) */ -operator fun Chain.iterator() = object : Iterator { +operator fun Chain.iterator(): Iterator = object : Iterator { override fun hasNext(): Boolean = true override fun next(): R = runBlocking { next() } diff --git a/kmath-coroutines/src/jvmMain/kotlin/scientifik/kmath/structures/LazyNDStructure.kt b/kmath-coroutines/src/jvmMain/kotlin/scientifik/kmath/structures/LazyNDStructure.kt index e65940739..8d5145976 100644 --- a/kmath-coroutines/src/jvmMain/kotlin/scientifik/kmath/structures/LazyNDStructure.kt +++ b/kmath-coroutines/src/jvmMain/kotlin/scientifik/kmath/structures/LazyNDStructure.kt @@ -8,10 +8,9 @@ class LazyNDStructure( override val shape: IntArray, val function: suspend (IntArray) -> T ) : NDStructure { + private val cache: MutableMap> = hashMapOf() - private val cache = HashMap>() - - fun deferred(index: IntArray) = cache.getOrPut(index) { + fun deferred(index: IntArray): Deferred = cache.getOrPut(index) { scope.async(context = Dispatchers.Math) { function(index) } @@ -42,21 +41,21 @@ class LazyNDStructure( result = 31 * result + cache.hashCode() return result } - - } -fun NDStructure.deferred(index: IntArray) = +fun NDStructure.deferred(index: IntArray): Deferred = if (this is LazyNDStructure) this.deferred(index) else CompletableDeferred(get(index)) -suspend fun NDStructure.await(index: IntArray) = +suspend fun NDStructure.await(index: IntArray): T = if (this is LazyNDStructure) this.await(index) else get(index) /** - * PENDING would benifit from KEEP-176 + * PENDING would benefit from KEEP-176 */ -fun NDStructure.mapAsyncIndexed(scope: CoroutineScope, function: suspend (T, index: IntArray) -> R) = - LazyNDStructure(scope, shape) { index -> function(get(index), index) } +fun NDStructure.mapAsyncIndexed( + scope: CoroutineScope, + function: suspend (T, index: IntArray) -> R +): LazyNDStructure = LazyNDStructure(scope, shape) { index -> function(get(index), index) } -fun NDStructure.mapAsync(scope: CoroutineScope, function: suspend (T) -> R) = +fun NDStructure.mapAsync(scope: CoroutineScope, function: suspend (T) -> R): LazyNDStructure = LazyNDStructure(scope, shape) { index -> function(get(index)) } \ No newline at end of file diff --git a/kmath-coroutines/src/jvmTest/kotlin/scientifik/kmath/streaming/BufferFlowTest.kt b/kmath-coroutines/src/jvmTest/kotlin/scientifik/kmath/streaming/BufferFlowTest.kt index 147f687f0..427349072 100644 --- a/kmath-coroutines/src/jvmTest/kotlin/scientifik/kmath/streaming/BufferFlowTest.kt +++ b/kmath-coroutines/src/jvmTest/kotlin/scientifik/kmath/streaming/BufferFlowTest.kt @@ -15,14 +15,13 @@ import kotlin.test.Test @InternalCoroutinesApi @FlowPreview class BufferFlowTest { - - val dispatcher = Executors.newFixedThreadPool(4).asCoroutineDispatcher() + val dispatcher: CoroutineDispatcher = Executors.newFixedThreadPool(4).asCoroutineDispatcher() @Test @Timeout(2000) fun map() { runBlocking { - (1..20).asFlow().mapParallel( dispatcher) { + (1..20).asFlow().mapParallel(dispatcher) { println("Started $it on ${Thread.currentThread().name}") @Suppress("BlockingMethodInNonBlockingContext") Thread.sleep(200) diff --git a/kmath-coroutines/src/jvmTest/kotlin/scientifik/kmath/streaming/RingBufferTest.kt b/kmath-coroutines/src/jvmTest/kotlin/scientifik/kmath/streaming/RingBufferTest.kt index c14d1a26c..c84ef89ef 100644 --- a/kmath-coroutines/src/jvmTest/kotlin/scientifik/kmath/streaming/RingBufferTest.kt +++ b/kmath-coroutines/src/jvmTest/kotlin/scientifik/kmath/streaming/RingBufferTest.kt @@ -19,17 +19,17 @@ class RingBufferTest { } @Test - fun windowed(){ - val flow = flow{ + fun windowed() { + val flow = flow { var i = 0 - while(true){ - emit(i++) - } + while (true) emit(i++) } + val windowed = flow.windowed(10) + runBlocking { val first = windowed.take(1).single() - val res = windowed.take(15).map { it -> it.asSequence().average() }.toList() + val res = windowed.take(15).map { it.asSequence().average() }.toList() assertEquals(0.0, res[0]) assertEquals(4.5, res[9]) assertEquals(9.5, res[14]) From 6ead822cc306e79c948d392fda29cd2749fcbf51 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Sun, 9 Aug 2020 23:31:49 +0700 Subject: [PATCH 35/54] Fix after-merge issues, provide default implementations for hyperbolic functions, remove their interface --- .../kotlin/scientifik/kmath/ast/MstAlgebra.kt | 6 +- .../FunctionalExpressionAlgebra.kt | 12 +--- .../scientifik/kmath/operations/Complex.kt | 9 +-- .../kmath/operations/NumberAlgebra.kt | 32 ++++++---- .../kmath/operations/OptionalOperations.kt | 63 ++++++++++++++++--- 5 files changed, 82 insertions(+), 40 deletions(-) diff --git a/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MstAlgebra.kt b/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MstAlgebra.kt index b47c7cae8..23deae24b 100644 --- a/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MstAlgebra.kt +++ b/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MstAlgebra.kt @@ -84,9 +84,9 @@ object MstExtendedField : ExtendedField { override fun sin(arg: MST): MST = unaryOperation(TrigonometricOperations.SIN_OPERATION, arg) override fun cos(arg: MST): MST = unaryOperation(TrigonometricOperations.COS_OPERATION, arg) - override fun asin(arg: MST): MST = unaryOperation(InverseTrigonometricOperations.ASIN_OPERATION, arg) - override fun acos(arg: MST): MST = unaryOperation(InverseTrigonometricOperations.ACOS_OPERATION, arg) - override fun atan(arg: MST): MST = unaryOperation(InverseTrigonometricOperations.ATAN_OPERATION, arg) + override fun asin(arg: MST): MST = unaryOperation(TrigonometricOperations.ASIN_OPERATION, arg) + override fun acos(arg: MST): MST = unaryOperation(TrigonometricOperations.ACOS_OPERATION, arg) + override fun atan(arg: MST): MST = unaryOperation(TrigonometricOperations.ATAN_OPERATION, arg) override fun add(a: MST, b: MST): MST = MstField.add(a, b) override fun multiply(a: MST, k: Number): MST = MstField.multiply(a, k) override fun multiply(a: MST, b: MST): MST = MstField.multiply(a, b) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressionAlgebra.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressionAlgebra.kt index dd5fb572a..9fe8aaf93 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressionAlgebra.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressionAlgebra.kt @@ -139,15 +139,9 @@ open class FunctionalExpressionExtendedField(algebra: A) : ExtendedField> where A : ExtendedField, A : NumericAlgebra { override fun sin(arg: Expression): Expression = unaryOperation(TrigonometricOperations.SIN_OPERATION, arg) override fun cos(arg: Expression): Expression = unaryOperation(TrigonometricOperations.COS_OPERATION, arg) - - override fun asin(arg: Expression): Expression = - unaryOperation(InverseTrigonometricOperations.ASIN_OPERATION, arg) - - override fun acos(arg: Expression): Expression = - unaryOperation(InverseTrigonometricOperations.ACOS_OPERATION, arg) - - override fun atan(arg: Expression): Expression = - unaryOperation(InverseTrigonometricOperations.ATAN_OPERATION, arg) + override fun asin(arg: Expression): Expression = unaryOperation(TrigonometricOperations.ASIN_OPERATION, arg) + override fun acos(arg: Expression): Expression = unaryOperation(TrigonometricOperations.ACOS_OPERATION, arg) + override fun atan(arg: Expression): Expression = unaryOperation(TrigonometricOperations.ATAN_OPERATION, arg) override fun power(arg: Expression, pow: Number): Expression = binaryOperation(PowerOperations.POW_OPERATION, arg, number(pow)) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Complex.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Complex.kt index aea0c59ec..f1535e212 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Complex.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Complex.kt @@ -99,13 +99,6 @@ object ComplexField : ExtendedField { return i * (ln(1 - iArg) - ln(1 + iArg)) / 2 } - override fun sinh(arg: Complex): Complex = (exp(arg) - exp(-arg)) / 2 - override fun cosh(arg: Complex): Complex = (exp(arg) + exp(-arg)) / 2 - override fun tanh(arg: Complex): Complex = (exp(arg) - exp(-arg)) / (exp(-arg) + exp(arg)) - override fun asinh(arg: Complex): Complex = ln(sqrt(arg * arg + 1) + arg) - override fun acosh(arg: Complex): Complex = ln(arg + sqrt((arg - 1) * (arg + 1))) - override fun atanh(arg: Complex): Complex = (ln(arg + 1) - ln(1 - arg)) / 2 - override fun power(arg: Complex, pow: Number): Complex = if (arg.im == 0.0) arg.re.pow(pow.toDouble()).toComplex() else @@ -199,7 +192,7 @@ data class Complex(val re: Double, val im: Double) : FieldElement Complex): Buffer { return MemoryBuffer.create(Complex, size, init) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/NumberAlgebra.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/NumberAlgebra.kt index 8b397208b..98cf912ac 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/NumberAlgebra.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/NumberAlgebra.kt @@ -7,8 +7,9 @@ import kotlin.math.pow as kpow * Advanced Number-like semifield that implements basic operations. */ interface ExtendedFieldOperations : + FieldOperations, TrigonometricOperations, - HyperbolicTrigonometricOperations, + HyperbolicOperations, PowerOperations, ExponentialOperations { @@ -22,16 +23,16 @@ interface ExtendedFieldOperations : TrigonometricOperations.ACOS_OPERATION -> acos(arg) TrigonometricOperations.ASIN_OPERATION -> asin(arg) TrigonometricOperations.ATAN_OPERATION -> atan(arg) - HyperbolicTrigonometricOperations.COSH_OPERATION -> cosh(arg) - HyperbolicTrigonometricOperations.SINH_OPERATION -> sinh(arg) - HyperbolicTrigonometricOperations.TANH_OPERATION -> tanh(arg) - HyperbolicTrigonometricOperations.ACOSH_OPERATION -> acosh(arg) - HyperbolicTrigonometricOperations.ASINH_OPERATION -> asinh(arg) - HyperbolicTrigonometricOperations.ATANH_OPERATION -> atanh(arg) + HyperbolicOperations.COSH_OPERATION -> cosh(arg) + HyperbolicOperations.SINH_OPERATION -> sinh(arg) + HyperbolicOperations.TANH_OPERATION -> tanh(arg) + HyperbolicOperations.ACOSH_OPERATION -> acosh(arg) + HyperbolicOperations.ASINH_OPERATION -> asinh(arg) + HyperbolicOperations.ATANH_OPERATION -> atanh(arg) PowerOperations.SQRT_OPERATION -> sqrt(arg) ExponentialOperations.EXP_OPERATION -> exp(arg) ExponentialOperations.LN_OPERATION -> ln(arg) - else -> super.unaryOperation(operation, arg) + else -> super.unaryOperation(operation, arg) } } @@ -40,6 +41,13 @@ interface ExtendedFieldOperations : * Advanced Number-like field that implements basic operations. */ interface ExtendedField : ExtendedFieldOperations, Field { + override fun sinh(arg: T): T = (exp(arg) - exp(-arg)) / 2 + override fun cosh(arg: T): T = (exp(arg) + exp(-arg)) / 2 + override fun tanh(arg: T): T = (exp(arg) - exp(-arg)) / (exp(-arg) + exp(arg)) + override fun asinh(arg: T): T = ln(sqrt(arg * arg + one) + arg) + override fun acosh(arg: T): T = ln(arg + sqrt((arg - one) * (arg + one))) + override fun atanh(arg: T): T = (ln(arg + one) - ln(one - arg)) / 2 + override fun rightSideNumberOperation(operation: String, left: T, right: Number): T = when (operation) { PowerOperations.POW_OPERATION -> power(left, right) else -> super.rightSideNumberOperation(operation, left, right) @@ -155,7 +163,7 @@ object FloatField : ExtendedField, Norm { } /** - * A field for [Int] without boxing. Does not produce corresponding field element + * A field for [Int] without boxing. Does not produce corresponding ring element. */ @Suppress("EXTENSION_SHADOWED_BY_MEMBER", "OVERRIDE_BY_INLINE", "NOTHING_TO_INLINE") object IntRing : Ring, Norm { @@ -179,7 +187,7 @@ object IntRing : Ring, Norm { } /** - * A field for [Short] without boxing. Does not produce appropriate field element + * A field for [Short] without boxing. Does not produce appropriate ring element. */ @Suppress("EXTENSION_SHADOWED_BY_MEMBER", "OVERRIDE_BY_INLINE", "NOTHING_TO_INLINE") object ShortRing : Ring, Norm { @@ -203,7 +211,7 @@ object ShortRing : Ring, Norm { } /** - * A field for [Byte] values + * A field for [Byte] without boxing. Does not produce appropriate ring element. */ @Suppress("EXTENSION_SHADOWED_BY_MEMBER", "OVERRIDE_BY_INLINE", "NOTHING_TO_INLINE") object ByteRing : Ring, Norm { @@ -227,7 +235,7 @@ object ByteRing : Ring, Norm { } /** - * A field for [Long] values + * A field for [Double] without boxing. Does not produce appropriate ring element. */ @Suppress("EXTENSION_SHADOWED_BY_MEMBER", "OVERRIDE_BY_INLINE", "NOTHING_TO_INLINE") object LongRing : Ring, Norm { diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/OptionalOperations.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/OptionalOperations.kt index 165d34dc4..e19acea3d 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/OptionalOperations.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/OptionalOperations.kt @@ -6,7 +6,7 @@ package scientifik.kmath.operations * The operations are not exposed to class directly to avoid method bloat but instead are declared in the field. * It also allows to override behavior for optional operations. */ -interface TrigonometricOperations : FieldOperations { +interface TrigonometricOperations : Algebra { /** * Computes the sine of [arg]. */ @@ -62,6 +62,7 @@ interface TrigonometricOperations : FieldOperations { * The identifier of inverse cosine. */ const val ACOS_OPERATION = "acos" + /** * The identifier of inverse tangent. */ @@ -106,7 +107,7 @@ fun >> atan(arg: T): T = arg.cont * The operations are not exposed to class directly to avoid method bloat but instead are declared in the field. It * also allows to override behavior for optional operations. */ -interface HyperbolicTrigonometricOperations : FieldOperations { +interface HyperbolicOperations : Algebra { /** * Computes the hyperbolic sine of [arg]. */ @@ -138,21 +139,67 @@ interface HyperbolicTrigonometricOperations : FieldOperations { fun atanh(arg: T): T companion object { + /** + * The identifier of hyperbolic sine. + */ const val SINH_OPERATION = "sinh" + + /** + * The identifier of hyperbolic cosine. + */ const val COSH_OPERATION = "cosh" + + /** + * The identifier of hyperbolic tangent. + */ const val TANH_OPERATION = "tanh" + + /** + * The identifier of inverse hyperbolic sine. + */ const val ASINH_OPERATION = "asinh" + + /** + * The identifier of inverse hyperbolic cosine. + */ const val ACOSH_OPERATION = "acosh" + + /** + * The identifier of inverse hyperbolic tangent. + */ const val ATANH_OPERATION = "atanh" } } -fun >> sinh(arg: T): T = arg.context.sinh(arg) -fun >> cosh(arg: T): T = arg.context.cosh(arg) -fun >> tanh(arg: T): T = arg.context.tanh(arg) -fun >> asinh(arg: T): T = arg.context.asinh(arg) -fun >> acosh(arg: T): T = arg.context.acosh(arg) -fun >> atanh(arg: T): T = arg.context.atanh(arg) +/** + * Computes the hyperbolic sine of [arg]. + */ +fun >> sinh(arg: T): T = arg.context.sinh(arg) + +/** + * Computes the hyperbolic cosine of [arg]. + */ +fun >> cosh(arg: T): T = arg.context.cosh(arg) + +/** + * Computes the hyperbolic tangent of [arg]. + */ +fun >> tanh(arg: T): T = arg.context.tanh(arg) + +/** + * Computes the inverse hyperbolic sine of [arg]. + */ +fun >> asinh(arg: T): T = arg.context.asinh(arg) + +/** + * Computes the inverse hyperbolic cosine of [arg]. + */ +fun >> acosh(arg: T): T = arg.context.acosh(arg) + +/** + * Computes the inverse hyperbolic tangent of [arg]. + */ +fun >> atanh(arg: T): T = arg.context.atanh(arg) /** * A context extension to include power operations based on exponentiation. From 895f788f756f1a2bbacc17c3f117b69c2c9853fd Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Sun, 9 Aug 2020 23:43:38 +0700 Subject: [PATCH 36/54] Cleanup doc mistakes, fix number fields corner cases --- .../scientifik/kmath/operations/Algebra.kt | 6 ++---- .../kmath/operations/NumberAlgebra.kt | 11 +++++++++++ .../kmath/operations/OptionalOperations.kt | 18 +++++++++++------- 3 files changed, 24 insertions(+), 11 deletions(-) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Algebra.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Algebra.kt index eb44cf7a7..9abd43c8f 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Algebra.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Algebra.kt @@ -58,12 +58,10 @@ interface NumericAlgebra : Algebra { inline operator fun , R> A.invoke(block: A.() -> R): R = run(block) /** - * Represents semispace, i.e. algebraic structure with associative binary operation called "addition" as well as + * Represents "semispace", i.e. algebraic structure with associative binary operation called "addition" as well as * multiplication by scalars. * - * In KMath groups are called spaces, and also define multiplication of element by [Number]. - * - * @param T the type of element of this semigroup. + * @param T the type of element of this "semispace". */ interface SpaceOperations : Algebra { /** diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/NumberAlgebra.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/NumberAlgebra.kt index 98cf912ac..0735a96da 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/NumberAlgebra.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/NumberAlgebra.kt @@ -1,5 +1,6 @@ package scientifik.kmath.operations +import scientifik.kmath.operations.RealField.pow import kotlin.math.abs import kotlin.math.pow as kpow @@ -83,6 +84,11 @@ object RealField : ExtendedField, Norm { override val one: Double get() = 1.0 + override fun binaryOperation(operation: String, left: Double, right: Double): Double = when (operation) { + PowerOperations.POW_OPERATION -> left pow right + else -> super.binaryOperation(operation, left, right) + } + override inline fun add(a: Double, b: Double): Double = a + b override inline fun multiply(a: Double, k: Number): Double = a * k.toDouble() @@ -128,6 +134,11 @@ object FloatField : ExtendedField, Norm { override val one: Float get() = 1.0f + override fun binaryOperation(operation: String, left: Float, right: Float): Float = when (operation) { + PowerOperations.POW_OPERATION -> left pow right + else -> super.binaryOperation(operation, left, right) + } + override inline fun add(a: Float, b: Float): Float = a + b override inline fun multiply(a: Float, k: Number): Float = a * k.toFloat() diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/OptionalOperations.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/OptionalOperations.kt index e19acea3d..9d901746a 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/OptionalOperations.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/OptionalOperations.kt @@ -1,10 +1,9 @@ package scientifik.kmath.operations /** - * A container for trigonometric operations for specific type. They are limited to semifields. + * A container for trigonometric operations for specific type. * - * The operations are not exposed to class directly to avoid method bloat but instead are declared in the field. - * It also allows to override behavior for optional operations. + * @param T the type of element of this structure. */ interface TrigonometricOperations : Algebra { /** @@ -101,11 +100,9 @@ fun >> acos(arg: T): T = arg.cont fun >> atan(arg: T): T = arg.context.atan(arg) /** - * A container for hyperbolic trigonometric operations for specific type. Trigonometric operations are limited to - * fields. + * A container for hyperbolic trigonometric operations for specific type. * - * The operations are not exposed to class directly to avoid method bloat but instead are declared in the field. It - * also allows to override behavior for optional operations. + * @param T the type of element of this structure. */ interface HyperbolicOperations : Algebra { /** @@ -203,6 +200,8 @@ fun >> atanh(arg: T): T = arg.contex /** * A context extension to include power operations based on exponentiation. + * + * @param T the type of element of this structure. */ interface PowerOperations : Algebra { /** @@ -254,6 +253,8 @@ fun >> sqr(arg: T): T = arg pow 2.0 /** * A container for operations related to `exp` and `ln` functions. + * + * @param T the type of element of this structure. */ interface ExponentialOperations : Algebra { /** @@ -291,6 +292,9 @@ fun >> ln(arg: T): T = arg.context. /** * A container for norm functional on element. + * + * @param T the type of element having norm defined. + * @param R the type of norm. */ interface Norm { /** From 6329722131e10d3922b917ff856c5a0713cdca10 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Mon, 10 Aug 2020 01:30:32 +0700 Subject: [PATCH 37/54] Fix typos --- .../kotlin/scientifik/kmath/operations/Algebra.kt | 6 ++---- .../kotlin/scientifik/kmath/structures/ShortBuffer.kt | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Algebra.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Algebra.kt index eb44cf7a7..f18bde597 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Algebra.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Algebra.kt @@ -58,12 +58,10 @@ interface NumericAlgebra : Algebra { inline operator fun , R> A.invoke(block: A.() -> R): R = run(block) /** - * Represents semispace, i.e. algebraic structure with associative binary operation called "addition" as well as + * Represents "semispace", i.e. algebraic structure with associative binary operation called "addition" as well as * multiplication by scalars. * - * In KMath groups are called spaces, and also define multiplication of element by [Number]. - * - * @param T the type of element of this semigroup. + * @param T the type of element of this semispace. */ interface SpaceOperations : Algebra { /** diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ShortBuffer.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ShortBuffer.kt index a67cbec62..c6f19feaf 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ShortBuffer.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ShortBuffer.kt @@ -1,7 +1,7 @@ package scientifik.kmath.structures /** - * Specialized [MutableBuffer] implementation over [ShortBuffer]. + * Specialized [MutableBuffer] implementation over [ShortArray]. * * @property array the underlying array. */ From 71c4ee065563acea5e8f917b519c2ff5c34b1005 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Mon, 10 Aug 2020 01:42:11 +0700 Subject: [PATCH 38/54] Add average and averageWith space extensions --- .../kmath/operations/AlgebraExtensions.kt | 51 +++++++++++++++++-- 1 file changed, 48 insertions(+), 3 deletions(-) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/AlgebraExtensions.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/AlgebraExtensions.kt index 6712d3baa..00b16dc98 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/AlgebraExtensions.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/AlgebraExtensions.kt @@ -4,7 +4,7 @@ package scientifik.kmath.operations * Returns the sum of all elements in the iterable in this [Space]. * * @receiver the algebra that provides addition. - * @param data the collection to sum up. + * @param data the iterable to sum up. * @return the sum. */ fun Space.sum(data: Iterable): T = data.fold(zero) { left, right -> add(left, right) } @@ -13,11 +13,29 @@ fun Space.sum(data: Iterable): T = data.fold(zero) { left, right -> ad * Returns the sum of all elements in the sequence in this [Space]. * * @receiver the algebra that provides addition. - * @param data the collection to sum up. + * @param data the sequence to sum up. * @return the sum. */ fun Space.sum(data: Sequence): T = data.fold(zero) { left, right -> add(left, right) } +/** + * Returns an average value of elements in the iterable in this [Space]. + * + * @receiver the algebra that provides addition and division. + * @param data the iterable to find average. + * @return the average value. + */ +fun Space.average(data: Iterable): T = sum(data) / data.count() + +/** + * Returns an average value of elements in the sequence in this [Space]. + * + * @receiver the algebra that provides addition and division. + * @param data the sequence to find average. + * @return the average value. + */ +fun Space.average(data: Sequence): T = sum(data) / data.count() + /** * Returns the sum of all elements in the iterable in provided space. * @@ -25,7 +43,34 @@ fun Space.sum(data: Sequence): T = data.fold(zero) { left, right -> ad * @param space the algebra that provides addition. * @return the sum. */ -fun > Iterable.sumWith(space: S): T = space.sum(this) +fun Iterable.sumWith(space: Space): T = space.sum(this) + +/** + * Returns the sum of all elements in the sequence in provided space. + * + * @receiver the collection to sum up. + * @param space the algebra that provides addition. + * @return the sum. + */ +fun Sequence.sumWith(space: Space): T = space.sum(this) + +/** + * Returns an average value of elements in the iterable in this [Space]. + * + * @receiver the iterable to find average. + * @param space the algebra that provides addition and division. + * @return the average value. + */ +fun Iterable.averageWith(space: Space): T = space.average(this) + +/** + * Returns an average value of elements in the sequence in this [Space]. + * + * @receiver the sequence to find average. + * @param space the algebra that provides addition and division. + * @return the average value. + */ +fun Sequence.averageWith(space: Space): T = space.average(this) //TODO optimized power operation From 53a54300fa073ba39b9d161423e65973e1387204 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Tue, 11 Aug 2020 02:36:08 +0700 Subject: [PATCH 39/54] Add changelog --- CHANGELOG.md | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 000000000..91c4098cd --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,30 @@ +## [Unreleased] + +### Added +- Functional Expressions API +- Mathematical Syntax Tree, its interpreter and API +- String to MST parser (https://github.com/mipt-npm/kmath/pull/120) +- MST to JVM bytecode translator (https://github.com/mipt-npm/kmath/pull/94) +- FloatBuffer (specialized MutableBuffer over FloatArray) +- FlaggedBuffer to associate primitive numbers buffer with flags (to mark values infinite or missing, etc.) +- Specialized builder functions for all primitive buffers like `IntBuffer(25) { it + 1 }` (https://github.com/mipt-npm/kmath/pull/125) +- Interface `NumericAlgebra` where `number` operation is available to convert numbers to algebraic elements +- Inverse trigonometric functions support in ExtendedField (`asin`, `acos`, `atan`) (https://github.com/mipt-npm/kmath/pull/114) +- New space extensions: `average` and `averageWith` +- Local coding conventions +- Geometric Domains API in `kmath-core` +- Blocking chains in `kmath-coroutines` + +### Changed +- BigInteger and BigDecimal algebra: JBigDecimalField has companion object with default math context; minor optimizations +- `power(T, Int)` extension function has preconditions and supports `Field` +- Memory objects have more preconditions (overflow checking) +- `tg` function is renamed to `tan` (https://github.com/mipt-npm/kmath/pull/114) +- Gradle version: 6.3 -> 6.5.1 +- Moved probability distributions to commons-rng and to `kmath-prob`. + +### Fixed +- Missing copy method in Memory implementation on JS (https://github.com/mipt-npm/kmath/pull/106) +- D3.dim value in `kmath-dimensions` +- Multiplication in integer rings in `kmath-core` (https://github.com/mipt-npm/kmath/pull/101) +- Commons RNG compatibility (https://github.com/mipt-npm/kmath/issues/93) From b65641ddbb8e5c423c0b1c4e749e8062aa438886 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Tue, 11 Aug 2020 02:39:01 +0700 Subject: [PATCH 40/54] Add header to changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 91c4098cd..2c99c7bc1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,5 @@ +# KMath + ## [Unreleased] ### Added From b77bfeb372cef83e7c4a62e4f2e79e7282b58df1 Mon Sep 17 00:00:00 2001 From: Iaroslav Postovalov Date: Tue, 11 Aug 2020 14:58:28 +0700 Subject: [PATCH 41/54] Provide basic tests for complex numbers, also fix complex division --- .../scientifik/kmath/operations/Complex.kt | 107 +++++++++++++----- .../kmath/operations/ComplexFieldTest.kt | 46 ++++++++ .../kmath/operations/ComplexTest.kt | 38 +++++++ 3 files changed, 161 insertions(+), 30 deletions(-) create mode 100644 kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/ComplexFieldTest.kt create mode 100644 kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/ComplexTest.kt diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Complex.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Complex.kt index 0ce144a33..ef4c9bcf1 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Complex.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Complex.kt @@ -8,15 +8,41 @@ import scientifik.memory.MemorySpec import scientifik.memory.MemoryWriter import kotlin.math.* +/** + * This complex's conjugate. + */ +val Complex.conjugate: Complex + get() = Complex(re, -im) + +/** + * This complex's reciprocal. + */ +val Complex.reciprocal: Complex + get() { + val scale = re * re + im * im + return Complex(re / scale, -im / scale) + } + +/** + * Absolute value of complex number. + */ +val Complex.r: Double + get() = sqrt(re * re + im * im) + +/** + * An angle between vector represented by complex number and X axis. + */ +val Complex.theta: Double + get() = atan(im / re) + private val PI_DIV_2 = Complex(PI / 2, 0) /** * A field of [Complex]. */ -object ComplexField : ExtendedField { - override val zero: Complex = Complex(0.0, 0.0) - - override val one: Complex = Complex(1.0, 0.0) +object ComplexField : ExtendedField, Norm { + override val zero: Complex = 0.0.toComplex() + override val one: Complex = 1.0.toComplex() /** * The imaginary unit. @@ -30,19 +56,53 @@ object ComplexField : ExtendedField { override fun multiply(a: Complex, b: Complex): Complex = Complex(a.re * b.re - a.im * b.im, a.re * b.im + a.im * b.re) - override fun divide(a: Complex, b: Complex): Complex { - val norm = b.re * b.re + b.im * b.im - return Complex((a.re * b.re + a.im * b.im) / norm, (a.re * b.im - a.im * b.re) / norm) + override fun divide(a: Complex, b: Complex): Complex = when { + b.re.isNaN() || b.im.isNaN() -> Complex(Double.NaN, Double.NaN) + + (if (b.im < 0) -b.im else +b.im) < (if (b.re < 0) -b.re else +b.re) -> { + val wr = b.im / b.re + val wd = b.re + wr * b.im + + if (wd.isNaN() || wd == 0.0) + Complex(Double.NaN, Double.NaN) + else + Complex((a.re + a.im * wr) / wd, (a.im - a.re * wr) / wd) + } + + b.im == 0.0 -> Complex(Double.NaN, Double.NaN) + + else -> { + val wr = b.re / b.im + val wd = b.im + wr * b.re + + if (wd.isNaN() || wd == 0.0) + Complex(Double.NaN, Double.NaN) + else + Complex((a.re * wr + a.im) / wd, (a.im * wr - a.re) / wd) + } } override fun sin(arg: Complex): Complex = i * (exp(-i * arg) - exp(i * arg)) / 2 override fun cos(arg: Complex): Complex = (exp(-i * arg) + exp(i * arg)) / 2 - override fun asin(arg: Complex): Complex = -i * ln(sqrt(one - arg pow 2) + i * arg) - override fun acos(arg: Complex): Complex = PI_DIV_2 + i * ln(sqrt(one - arg pow 2) + i * arg) - override fun atan(arg: Complex): Complex = i * (ln(one - i * arg) - ln(one + i * arg)) / 2 - override fun power(arg: Complex, pow: Number): Complex = - arg.r.pow(pow.toDouble()) * (cos(pow.toDouble() * arg.theta) + i * sin(pow.toDouble() * arg.theta)) + override fun tan(arg: Complex): Complex { + val e1 = exp(-i * arg) + val e2 = exp(i * arg) + return i * (e1 - e2) / (e1 + e2) + } + + override fun asin(arg: Complex): Complex = -i * ln(sqrt(1 - (arg * arg)) + i * arg) + override fun acos(arg: Complex): Complex = PI_DIV_2 + i * ln(sqrt(1 - (arg * arg)) + i * arg) + + override fun atan(arg: Complex): Complex { + val iArg = i * arg + return i * (ln(1 - iArg) - ln(1 + iArg)) / 2 + } + + override fun power(arg: Complex, pow: Number): Complex = if (arg.im == 0.0) + arg.re.pow(pow.toDouble()).toComplex() + else + exp(pow * ln(arg)) override fun exp(arg: Complex): Complex = exp(arg.re) * (cos(arg.im) + i * sin(arg.im)) @@ -93,6 +153,8 @@ object ComplexField : ExtendedField { */ operator fun Double.times(c: Complex): Complex = Complex(c.re * this, c.im * this) + override fun norm(arg: Complex): Complex = arg.conjugate * arg + override fun symbol(value: String): Complex = if (value == "i") i else super.symbol(value) } @@ -105,12 +167,12 @@ object ComplexField : ExtendedField { data class Complex(val re: Double, val im: Double) : FieldElement, Comparable { constructor(re: Number, im: Number) : this(re.toDouble(), im.toDouble()) + override val context: ComplexField get() = ComplexField + override fun unwrap(): Complex = this override fun Complex.wrap(): Complex = this - override val context: ComplexField get() = ComplexField - override fun compareTo(other: Complex): Int = r.compareTo(other.r) companion object : MemorySpec { @@ -126,28 +188,13 @@ data class Complex(val re: Double, val im: Double) : FieldElement Complex): Buffer { return MemoryBuffer.create(Complex, size, init) diff --git a/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/ComplexFieldTest.kt b/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/ComplexFieldTest.kt new file mode 100644 index 000000000..4722929cf --- /dev/null +++ b/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/ComplexFieldTest.kt @@ -0,0 +1,46 @@ +package scientifik.kmath.operations + +import kotlin.test.Test +import kotlin.test.assertEquals + +internal class ComplexFieldTest { + @Test + fun testAddition() { + assertEquals(Complex(42, 42), ComplexField { Complex(16, 16) + Complex(26, 26) }) + assertEquals(Complex(42, 16), ComplexField { Complex(16, 16) + 26 }) + assertEquals(Complex(42, 16), ComplexField { 26 + Complex(16, 16) }) + } + + @Test + fun testSubtraction() { + assertEquals(Complex(42, 42), ComplexField { Complex(86, 55) - Complex(44, 13) }) + assertEquals(Complex(42, 56), ComplexField { Complex(86, 56) - 44 }) + assertEquals(Complex(42, 56), ComplexField { 86 - Complex(44, -56) }) + } + + @Test + fun testMultiplication() { + assertEquals(Complex(42, 42), ComplexField { Complex(4.2, 0) * Complex(10, 10) }) + assertEquals(Complex(42, 21), ComplexField { Complex(4.2, 2.1) * 10 }) + assertEquals(Complex(42, 21), ComplexField { 10 * Complex(4.2, 2.1) }) + } + + @Test + fun testDivision() { + assertEquals(Complex(42, 42), ComplexField { Complex(0, 168) / Complex(2, 2) }) + assertEquals(Complex(42, 56), ComplexField { Complex(86, 56) - 44 }) + assertEquals(Complex(42, 56), ComplexField { 86 - Complex(44, -56) }) + assertEquals(Complex(Double.NaN, Double.NaN), ComplexField { Complex(1, 1) / Complex(Double.NaN, Double.NaN) }) + assertEquals(Complex(Double.NaN, Double.NaN), ComplexField { Complex(1, 1) / Complex(0, 0) }) + } + + @Test + fun testPower() { + assertEquals(ComplexField.zero, ComplexField { zero pow 2 }) + assertEquals(ComplexField.zero, ComplexField { zero pow 2 }) + + assertEquals( + ComplexField { i * 8 }.let { it.im.toInt() to it.re.toInt() }, + ComplexField { Complex(2, 2) pow 2 }.let { it.im.toInt() to it.re.toInt() }) + } +} diff --git a/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/ComplexTest.kt b/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/ComplexTest.kt new file mode 100644 index 000000000..e2132b75f --- /dev/null +++ b/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/ComplexTest.kt @@ -0,0 +1,38 @@ +package scientifik.kmath.operations + +import kotlin.test.Test +import kotlin.test.assertEquals + +internal class ComplexTest { + @Test + fun conjugate() { + assertEquals( + Complex(0, -42), (ComplexField.i * 42).conjugate + ) + } + + @Test + fun reciprocal() { + assertEquals(Complex(0.5, -0.0), 2.toComplex().reciprocal) + } + + @Test + fun r() { + assertEquals(kotlin.math.sqrt(2.0), (ComplexField.i + 1.0.toComplex()).r) + } + + @Test + fun theta() { + assertEquals(0.0, 1.toComplex().theta) + } + + @Test + fun toComplex() { + assertEquals(Complex(42, 0), 42.toComplex()) + assertEquals(Complex(42.0, 0), 42.0.toComplex()) + assertEquals(Complex(42f, 0), 42f.toComplex()) + assertEquals(Complex(42.0, 0), 42.0.toComplex()) + assertEquals(Complex(42.toByte(), 0), 42.toByte().toComplex()) + assertEquals(Complex(42.toShort(), 0), 42.toShort().toComplex()) + } +} \ No newline at end of file From 8ab88642276d4a8685c379406d3fa0ee609fc09d Mon Sep 17 00:00:00 2001 From: Iaroslav Postovalov Date: Tue, 11 Aug 2020 15:02:56 +0700 Subject: [PATCH 42/54] Add newline --- .../kotlin/scientifik/kmath/operations/ComplexTest.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/ComplexTest.kt b/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/ComplexTest.kt index e2132b75f..e8d698c70 100644 --- a/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/ComplexTest.kt +++ b/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/ComplexTest.kt @@ -35,4 +35,4 @@ internal class ComplexTest { assertEquals(Complex(42.toByte(), 0), 42.toByte().toComplex()) assertEquals(Complex(42.toShort(), 0), 42.toShort().toComplex()) } -} \ No newline at end of file +} From 837150b5eb0ce915c7795ec1c60e8289d71d3d1d Mon Sep 17 00:00:00 2001 From: Iaroslav Postovalov Date: Tue, 11 Aug 2020 15:06:53 +0700 Subject: [PATCH 43/54] Fix norm and add test of it --- .../kotlin/scientifik/kmath/operations/Complex.kt | 2 +- .../scientifik/kmath/operations/ComplexFieldTest.kt | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Complex.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Complex.kt index ef4c9bcf1..76df0f45d 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Complex.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Complex.kt @@ -153,7 +153,7 @@ object ComplexField : ExtendedField, Norm { */ operator fun Double.times(c: Complex): Complex = Complex(c.re * this, c.im * this) - override fun norm(arg: Complex): Complex = 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) } diff --git a/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/ComplexFieldTest.kt b/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/ComplexFieldTest.kt index 1091a8de1..40fb0434d 100644 --- a/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/ComplexFieldTest.kt +++ b/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/ComplexFieldTest.kt @@ -1,6 +1,7 @@ package scientifik.kmath.operations -import kotlin.math.* +import kotlin.math.PI +import kotlin.math.abs import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertTrue @@ -64,4 +65,9 @@ internal class ComplexFieldTest { ComplexField { i * 8 }.let { it.im.toInt() to it.re.toInt() }, ComplexField { Complex(2, 2) pow 2 }.let { it.im.toInt() to it.re.toInt() }) } + + @Test + fun testNorm() { + assertEquals(2.toComplex(), ComplexField { norm(2 * i) }) + } } From c7e6764ea2886e5a89de184b200d8acfc9636288 Mon Sep 17 00:00:00 2001 From: Iaroslav Postovalov Date: Tue, 11 Aug 2020 15:10:48 +0700 Subject: [PATCH 44/54] Specify constants' types for API stability --- .../kmath/operations/OptionalOperations.kt | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/OptionalOperations.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/OptionalOperations.kt index 9d901746a..09b170c15 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/OptionalOperations.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/OptionalOperations.kt @@ -40,32 +40,32 @@ interface TrigonometricOperations : Algebra { /** * The identifier of sine. */ - const val SIN_OPERATION = "sin" + const val SIN_OPERATION: String = "sin" /** * The identifier of cosine. */ - const val COS_OPERATION = "cos" + const val COS_OPERATION: String = "cos" /** * The identifier of tangent. */ - const val TAN_OPERATION = "tan" + const val TAN_OPERATION: String = "tan" /** * The identifier of inverse sine. */ - const val ASIN_OPERATION = "asin" + const val ASIN_OPERATION: String = "asin" /** * The identifier of inverse cosine. */ - const val ACOS_OPERATION = "acos" + const val ACOS_OPERATION: String = "acos" /** * The identifier of inverse tangent. */ - const val ATAN_OPERATION = "atan" + const val ATAN_OPERATION: String = "atan" } } @@ -139,27 +139,27 @@ interface HyperbolicOperations : Algebra { /** * The identifier of hyperbolic sine. */ - const val SINH_OPERATION = "sinh" + const val SINH_OPERATION: String = "sinh" /** * The identifier of hyperbolic cosine. */ - const val COSH_OPERATION = "cosh" + const val COSH_OPERATION: String = "cosh" /** * The identifier of hyperbolic tangent. */ - const val TANH_OPERATION = "tanh" + const val TANH_OPERATION: String = "tanh" /** * The identifier of inverse hyperbolic sine. */ - const val ASINH_OPERATION = "asinh" + const val ASINH_OPERATION: String = "asinh" /** * The identifier of inverse hyperbolic cosine. */ - const val ACOSH_OPERATION = "acosh" + const val ACOSH_OPERATION: String = "acosh" /** * The identifier of inverse hyperbolic tangent. From 3f0c2083551a5c932518568299ac9b587e1a9eff Mon Sep 17 00:00:00 2001 From: Iaroslav Postovalov Date: Tue, 11 Aug 2020 15:11:29 +0700 Subject: [PATCH 45/54] Specify ATANH_OPERATION type for API stability --- .../kotlin/scientifik/kmath/operations/OptionalOperations.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/OptionalOperations.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/OptionalOperations.kt index 09b170c15..1dac649aa 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/OptionalOperations.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/OptionalOperations.kt @@ -164,7 +164,7 @@ interface HyperbolicOperations : Algebra { /** * The identifier of inverse hyperbolic tangent. */ - const val ATANH_OPERATION = "atanh" + const val ATANH_OPERATION: String = "atanh" } } From 1e4692d5cf9ca97c5e9ad6add5d95d9d19b4c1a8 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Wed, 12 Aug 2020 21:18:47 +0700 Subject: [PATCH 46/54] Fix scalar multiplication in BigInt, implement testers for fields, rings and spaces and apply them to ComplexFieldTest, BigIntAlgebraTest and RealFieldTest --- .../scientifik/kmath/operations/BigInt.kt | 3 +- .../kmath/operations/BigIntAlgebraTest.kt | 6 +++- .../kmath/operations/ComplexFieldTest.kt | 4 +++ .../kmath/operations/RealFieldTest.kt | 10 +++--- .../operations/internal/AlgebraicVerifier.kt | 9 +++++ .../operations/internal/FieldVerifier.kt | 24 ++++++++++++++ .../kmath/operations/internal/RingVerifier.kt | 28 ++++++++++++++++ .../operations/internal/SpaceVerifier.kt | 33 +++++++++++++++++++ 8 files changed, 111 insertions(+), 6 deletions(-) create mode 100644 kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/internal/AlgebraicVerifier.kt create mode 100644 kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/internal/FieldVerifier.kt create mode 100644 kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/internal/RingVerifier.kt create mode 100644 kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/internal/SpaceVerifier.kt diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/BigInt.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/BigInt.kt index fd7719157..07163750b 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/BigInt.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/BigInt.kt @@ -22,8 +22,9 @@ object BigIntField : Field { override val one: BigInt = BigInt.ONE override fun add(a: BigInt, b: BigInt): BigInt = a.plus(b) + override fun number(value: Number): BigInt = value.toLong().toBigInt() - override fun multiply(a: BigInt, k: Number): BigInt = a.times(k.toLong()) + override fun multiply(a: BigInt, k: Number): BigInt = a.times(number(k)) override fun multiply(a: BigInt, b: BigInt): BigInt = a.times(b) diff --git a/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/BigIntAlgebraTest.kt b/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/BigIntAlgebraTest.kt index c22d2f27b..d140f1017 100644 --- a/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/BigIntAlgebraTest.kt +++ b/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/BigIntAlgebraTest.kt @@ -1,9 +1,13 @@ package scientifik.kmath.operations +import scientifik.kmath.operations.internal.RingVerifier import kotlin.test.Test import kotlin.test.assertEquals -class BigIntAlgebraTest { +internal class BigIntAlgebraTest { + @Test + fun verify() = BigIntField { RingVerifier(this, +"42", +"10", +"-12", 10).verify() } + @Test fun testKBigIntegerRingSum() { val res = BigIntField { diff --git a/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/ComplexFieldTest.kt b/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/ComplexFieldTest.kt index 40fb0434d..2c480ebea 100644 --- a/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/ComplexFieldTest.kt +++ b/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/ComplexFieldTest.kt @@ -1,5 +1,6 @@ package scientifik.kmath.operations +import scientifik.kmath.operations.internal.FieldVerifier import kotlin.math.PI import kotlin.math.abs import kotlin.test.Test @@ -7,6 +8,9 @@ import kotlin.test.assertEquals import kotlin.test.assertTrue internal class ComplexFieldTest { + @Test + fun verify() = ComplexField { FieldVerifier(this, 42.0 * i, 66.0 + 28 * i, 2.0 + 0 * i, 5).verify() } + @Test fun testAddition() { assertEquals(Complex(42, 42), ComplexField { Complex(16, 16) + Complex(26, 26) }) diff --git a/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/RealFieldTest.kt b/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/RealFieldTest.kt index 9dfa3bdd1..a168b4afd 100644 --- a/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/RealFieldTest.kt +++ b/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/RealFieldTest.kt @@ -1,14 +1,16 @@ package scientifik.kmath.operations +import scientifik.kmath.operations.internal.FieldVerifier import kotlin.test.Test import kotlin.test.assertEquals -class RealFieldTest { +internal class RealFieldTest { + @Test + fun verify() = FieldVerifier(RealField, 42.0, 66.0, 2.0, 5).verify() + @Test fun testSqrt() { - val sqrt = RealField { - sqrt(25 * one) - } + val sqrt = RealField { sqrt(25 * one) } assertEquals(5.0, sqrt) } } diff --git a/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/internal/AlgebraicVerifier.kt b/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/internal/AlgebraicVerifier.kt new file mode 100644 index 000000000..cb097d46e --- /dev/null +++ b/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/internal/AlgebraicVerifier.kt @@ -0,0 +1,9 @@ +package scientifik.kmath.operations.internal + +import scientifik.kmath.operations.Algebra + +internal interface AlgebraicVerifier where A : Algebra { + val algebra: A + + fun verify() +} diff --git a/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/internal/FieldVerifier.kt b/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/internal/FieldVerifier.kt new file mode 100644 index 000000000..973fd00b1 --- /dev/null +++ b/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/internal/FieldVerifier.kt @@ -0,0 +1,24 @@ +package scientifik.kmath.operations.internal + +import scientifik.kmath.operations.Field +import scientifik.kmath.operations.invoke +import kotlin.test.assertEquals +import kotlin.test.assertNotEquals + +internal class FieldVerifier(override val algebra: Field, a: T, b: T, c: T, x: Number) : + RingVerifier(algebra, a, b, c, x) { + + override fun verify() { + super.verify() + + algebra { + assertNotEquals(a / b, b / a, "Division in $algebra is not anti-commutative.") + assertNotEquals((a / b) / c, a / (b / c), "Division in $algebra is associative.") + assertEquals((a + b) / c, (a / c) + (b / c), "Division in $algebra is not right-distributive.") + assertEquals(a, a / one, "$one in $algebra is not neutral division element.") + assertEquals(one, one / a * a, "$algebra does not provide single reciprocal element.") + assertEquals(zero / a, zero, "$zero in $algebra is not left neutral element for division.") + assertEquals(-one, a / (-a), "Division by sign reversal element in $algebra does not give ${-one}.") + } + } +} diff --git a/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/internal/RingVerifier.kt b/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/internal/RingVerifier.kt new file mode 100644 index 000000000..047a213e9 --- /dev/null +++ b/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/internal/RingVerifier.kt @@ -0,0 +1,28 @@ +package scientifik.kmath.operations.internal + +import scientifik.kmath.operations.Ring +import scientifik.kmath.operations.invoke +import kotlin.test.assertEquals + +internal open class RingVerifier(override val algebra: Ring, a: T, b: T, c: T, x: Number) : + SpaceVerifier(algebra, a, b, c, x) { + override fun verify() { + super.verify() + + algebra { + assertEquals(a * b, a * b, "Multiplication in $algebra is not commutative.") + assertEquals(a * b * c, a * (b * c), "Multiplication in $algebra is not associative.") + assertEquals(c * (a + b), (c * a) + (c * b), "Multiplication in $algebra is not distributive.") + assertEquals(a * one, one * a, "$one in $algebra is not a neutral multiplication element.") + assertEquals(a, one * a, "$one in $algebra is not a neutral multiplication element.") + assertEquals(a, a * one, "$one in $algebra is not a neutral multiplication element.") + assertEquals(a, one * a, "$one in $algebra is not a neutral multiplication element.") + assertEquals(a, a * one * one, "Multiplication by $one in $algebra is not idempotent.") + assertEquals(a, a * one * one * one, "Multiplication by $one in $algebra is not idempotent.") + assertEquals(a, a * one * one * one * one, "Multiplication by $one in $algebra is not idempotent.") + assertEquals(zero, a * zero, "Multiplication by $zero in $algebra doesn't give $zero.") + assertEquals(zero, zero * a, "Multiplication by $zero in $algebra doesn't give $zero.") + assertEquals(a * zero, a * zero, "Multiplication by $zero in $algebra doesn't give $zero.") + } + } +} diff --git a/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/internal/SpaceVerifier.kt b/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/internal/SpaceVerifier.kt new file mode 100644 index 000000000..bc241c97d --- /dev/null +++ b/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/internal/SpaceVerifier.kt @@ -0,0 +1,33 @@ +package scientifik.kmath.operations.internal + +import scientifik.kmath.operations.Space +import scientifik.kmath.operations.invoke +import kotlin.test.assertEquals +import kotlin.test.assertNotEquals + +internal open class SpaceVerifier( + override val algebra: Space, + val a: T, + val b: T, + val c: T, + val x: Number +) : + AlgebraicVerifier> { + override fun verify() { + algebra { + assertEquals(a + b, b + a, "Addition in $algebra is not commutative.") + assertEquals(a + b + c, a + (b + c), "Addition in $algebra is not associative.") + assertEquals(x * (a + b), x * a + x * b, "Addition in $algebra is not distributive.") + assertEquals((a + b) * x, a * x + b * x, "Addition in $algebra is not distributive.") + assertEquals(a + zero, zero + a, "$zero in $algebra is not a neutral addition element.") + assertEquals(a, a + zero, "$zero in $algebra is not a neutral addition element.") + assertEquals(a, zero + a, "$zero in $algebra is not a neutral addition element.") + assertEquals(a - b, -(b - a), "Subtraction in $algebra is not anti-commutative.") + assertNotEquals(a - b - c, a - (b - c), "Subtraction in $algebra is associative.") + assertEquals(x * (a - b), x * a - x * b, "Subtraction in $algebra is not distributive.") + assertEquals(a, a - zero, "$zero in $algebra is not a neutral addition element.") + assertEquals(a * x, x * a, "Multiplication by scalar in $algebra is not commutative.") + assertEquals(x * (a + b), (x * a) + (x * b), "Multiplication by scalar in $algebra is not distributive.") + } + } +} From b1f52f3022171c28802bc9f7a08fff05e0fd40ec Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Sat, 15 Aug 2020 17:00:17 +0700 Subject: [PATCH 47/54] Update changelog --- CHANGELOG.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2c99c7bc1..26f9e33ec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,8 @@ - Local coding conventions - Geometric Domains API in `kmath-core` - Blocking chains in `kmath-coroutines` +- Full hyperbolic functions support and default implementations within `ExtendedField` +- Norm support for `Complex` ### Changed - BigInteger and BigDecimal algebra: JBigDecimalField has companion object with default math context; minor optimizations @@ -23,10 +25,11 @@ - Memory objects have more preconditions (overflow checking) - `tg` function is renamed to `tan` (https://github.com/mipt-npm/kmath/pull/114) - Gradle version: 6.3 -> 6.5.1 -- Moved probability distributions to commons-rng and to `kmath-prob`. +- Moved probability distributions to commons-rng and to `kmath-prob` ### Fixed - Missing copy method in Memory implementation on JS (https://github.com/mipt-npm/kmath/pull/106) - D3.dim value in `kmath-dimensions` - Multiplication in integer rings in `kmath-core` (https://github.com/mipt-npm/kmath/pull/101) - Commons RNG compatibility (https://github.com/mipt-npm/kmath/issues/93) +- Multiplication of BigInt by scalar From 1d18832aa6bfd50cd826c85a34e7d4f55c73bb11 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Sat, 22 Aug 2020 02:42:59 +0700 Subject: [PATCH 48/54] Provide contracts for many functions, make inline several functions, replace .run { and with(> { with {, add newlines at EOFs, specify operator modifier explicitly at many places, reformat files, replace if (...) error guards with require and check --- CHANGELOG.md | 5 +- examples/build.gradle.kts | 3 +- .../kmath/structures/NDFieldBenchmark.kt | 24 ++-- .../kmath/structures/ViktorBenchmark.kt | 15 +- .../kotlin/scientifik/kmath/utils/utils.kt | 7 +- .../kmath/linear/LinearAlgebraBenchmark.kt | 24 +--- .../kmath/linear/MultiplicationBenchmark.kt | 6 +- .../kmath/operations/ComplexDemo.kt | 6 +- .../scientifik/kmath/structures/ComplexND.kt | 15 +- .../scientifik/kmath/structures/NDField.kt | 30 ++-- .../kmath/structures/typeSafeDimensions.kt | 12 +- .../scientifik/kmath/ast/MstExpression.kt | 66 ++++++--- .../kmath/asm/internal/codegenUtils.kt | 32 ++++- .../commons/expressions/DiffExpression.kt | 48 +++---- .../kmath/commons/linear/CMMatrix.kt | 35 +++-- .../random/CMRandomGeneratorWrapper.kt | 9 +- .../kmath/commons/expressions/AutoDiffTest.kt | 10 +- .../scientifik/kmath/expressions/Builders.kt | 32 +++-- .../kmath/expressions/Expression.kt | 2 +- .../FunctionalExpressionAlgebra.kt | 10 +- .../scientifik/kmath/linear/BufferMatrix.kt | 12 +- .../scientifik/kmath/linear/FeaturedMatrix.kt | 11 +- .../kmath/linear/LUPDecomposition.kt | 61 +++----- .../scientifik/kmath/linear/MatrixBuilder.kt | 2 +- .../scientifik/kmath/linear/MatrixContext.kt | 40 +++--- .../scientifik/kmath/linear/VectorSpace.kt | 27 ++-- .../scientifik/kmath/linear/VirtualMatrix.kt | 2 +- .../kotlin/scientifik/kmath/misc/AutoDiff.kt | 135 +++++++----------- .../kotlin/scientifik/kmath/misc/Grids.kt | 2 +- .../scientifik/kmath/misc/cumulative.kt | 64 +++++---- .../scientifik/kmath/operations/BigInt.kt | 23 +-- .../scientifik/kmath/operations/Complex.kt | 6 + .../kmath/structures/BoxingNDField.kt | 9 +- .../kmath/structures/BoxingNDRing.kt | 15 +- .../kmath/structures/BufferAccessor2D.kt | 8 +- .../kmath/structures/BufferedNDAlgebra.kt | 5 +- .../kmath/structures/BufferedNDElement.kt | 2 - .../scientifik/kmath/structures/Buffers.kt | 40 +++--- .../kmath/structures/ComplexNDField.kt | 9 +- .../kmath/structures/FlaggedBuffer.kt | 18 ++- .../kmath/structures/FloatBuffer.kt | 15 +- .../scientifik/kmath/structures/IntBuffer.kt | 17 ++- .../scientifik/kmath/structures/LongBuffer.kt | 15 +- .../kmath/structures/MemoryBuffer.kt | 9 +- .../scientifik/kmath/structures/NDElement.kt | 17 ++- .../kmath/structures/NDStructure.kt | 28 ++-- .../scientifik/kmath/structures/RealBuffer.kt | 15 +- .../kmath/structures/ShortBuffer.kt | 16 ++- .../kmath/structures/Structure1D.kt | 10 +- .../kmath/structures/Structure2D.kt | 8 +- .../kmath/expressions/ExpressionFieldTest.kt | 9 +- .../scientifik/kmath/linear/MatrixTest.kt | 2 +- .../kmath/structures/NumberNDFieldTest.kt | 12 +- .../scientifik/kmath/operations/BigNumbers.kt | 9 +- .../kotlin/scientifik/kmath/chains/Chain.kt | 9 +- .../scientifik/kmath/chains/flowExtra.kt | 5 +- .../kmath/coroutines/coroutinesExtra.kt | 17 ++- .../scientifik/kmath/streaming/RingBuffer.kt | 8 +- .../scientifik/kmath/chains/ChainExt.kt | 5 +- .../kmath/structures/LazyNDStructure.kt | 12 +- .../scientifik/kmath/dimensions/Wrappers.kt | 64 ++++----- .../dimensions/DMatrixContextTest.kt | 5 +- .../scientifik/kmath/real/RealVector.kt | 15 +- .../scientifik/kmath/real/realMatrix.kt | 32 +++-- .../scientifik/kmath/linear/VectorTest.kt | 9 +- .../scientifik/kmath/functions/Polynomial.kt | 37 ++--- .../kmath/interpolation/LinearInterpolator.kt | 6 +- .../kmath/interpolation/SplineInterpolator.kt | 3 +- .../kmath/interpolation/XYPointSet.kt | 8 +- .../kmath/geometry/Euclidean2DSpace.kt | 12 +- .../kmath/geometry/Euclidean3DSpace.kt | 13 +- .../scientifik/kmath/histogram/Histogram.kt | 17 ++- .../kmath/histogram/RealHistogram.kt | 88 ++++-------- .../kmath/histogram/UnivariateHistogram.kt | 13 +- .../scientifik.kmath.linear/KomaMatrix.kt | 52 ++++--- .../kotlin/scientifik/memory/Memory.kt | 8 ++ .../kotlin/scientifik/memory/MemorySpec.kt | 6 +- .../scientifik/memory/ByteBufferMemory.kt | 20 ++- .../scientifik/kmath/prob/RandomChain.kt | 3 +- .../scientifik/kmath/prob/SamplerAlgebra.kt | 5 +- .../kotlin/scientifik/kmath/prob/Statistic.kt | 23 +-- .../scientifik/kmath/viktor/ViktorBuffer.kt | 2 +- 82 files changed, 787 insertions(+), 774 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 26f9e33ec..3944c673e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,11 +20,14 @@ - Norm support for `Complex` ### Changed +- `readAsMemory` now has `throws IOException` in JVM signature. +- Several functions taking functional types were made `inline`. +- Several functions taking functional types now have `callsInPlace` contracts. - BigInteger and BigDecimal algebra: JBigDecimalField has companion object with default math context; minor optimizations - `power(T, Int)` extension function has preconditions and supports `Field` - Memory objects have more preconditions (overflow checking) - `tg` function is renamed to `tan` (https://github.com/mipt-npm/kmath/pull/114) -- Gradle version: 6.3 -> 6.5.1 +- Gradle version: 6.3 -> 6.6 - Moved probability distributions to commons-rng and to `kmath-prob` ### Fixed diff --git a/examples/build.gradle.kts b/examples/build.gradle.kts index 73def3572..9f842024d 100644 --- a/examples/build.gradle.kts +++ b/examples/build.gradle.kts @@ -60,5 +60,6 @@ benchmark { tasks.withType { kotlinOptions { jvmTarget = Scientifik.JVM_TARGET.toString() + freeCompilerArgs = freeCompilerArgs + "-Xopt-in=kotlin.RequiresOptIn" } -} \ No newline at end of file +} diff --git a/examples/src/benchmarks/kotlin/scientifik/kmath/structures/NDFieldBenchmark.kt b/examples/src/benchmarks/kotlin/scientifik/kmath/structures/NDFieldBenchmark.kt index ae27620f7..46da6c6d8 100644 --- a/examples/src/benchmarks/kotlin/scientifik/kmath/structures/NDFieldBenchmark.kt +++ b/examples/src/benchmarks/kotlin/scientifik/kmath/structures/NDFieldBenchmark.kt @@ -4,46 +4,38 @@ import org.openjdk.jmh.annotations.Benchmark import org.openjdk.jmh.annotations.Scope import org.openjdk.jmh.annotations.State import scientifik.kmath.operations.RealField +import scientifik.kmath.operations.invoke @State(Scope.Benchmark) class NDFieldBenchmark { - @Benchmark fun autoFieldAdd() { - bufferedField.run { + bufferedField { var res: NDBuffer = one - repeat(n) { - res += one - } + repeat(n) { res += one } } } @Benchmark fun autoElementAdd() { var res = genericField.one - repeat(n) { - res += 1.0 - } + repeat(n) { res += 1.0 } } @Benchmark fun specializedFieldAdd() { - specializedField.run { + specializedField { var res: NDBuffer = one - repeat(n) { - res += 1.0 - } + repeat(n) { res += 1.0 } } } @Benchmark fun boxingFieldAdd() { - genericField.run { + genericField { var res: NDBuffer = one - repeat(n) { - res += one - } + repeat(n) { res += one } } } diff --git a/examples/src/benchmarks/kotlin/scientifik/kmath/structures/ViktorBenchmark.kt b/examples/src/benchmarks/kotlin/scientifik/kmath/structures/ViktorBenchmark.kt index f7b9661ef..9627743c9 100644 --- a/examples/src/benchmarks/kotlin/scientifik/kmath/structures/ViktorBenchmark.kt +++ b/examples/src/benchmarks/kotlin/scientifik/kmath/structures/ViktorBenchmark.kt @@ -5,23 +5,22 @@ import org.openjdk.jmh.annotations.Benchmark import org.openjdk.jmh.annotations.Scope import org.openjdk.jmh.annotations.State import scientifik.kmath.operations.RealField +import scientifik.kmath.operations.invoke import scientifik.kmath.viktor.ViktorNDField - @State(Scope.Benchmark) class ViktorBenchmark { final val dim = 1000 final val n = 100 // automatically build context most suited for given type. - final val autoField = NDField.auto(RealField, dim, dim) - final val realField = NDField.real(dim, dim) - - final val viktorField = ViktorNDField(intArrayOf(dim, dim)) + final val autoField: BufferedNDField = NDField.auto(RealField, dim, dim) + final val realField: RealNDField = NDField.real(dim, dim) + final val viktorField: ViktorNDField = ViktorNDField(intArrayOf(dim, dim)) @Benchmark fun automaticFieldAddition() { - autoField.run { + autoField { var res = one repeat(n) { res += one } } @@ -29,7 +28,7 @@ class ViktorBenchmark { @Benchmark fun viktorFieldAddition() { - viktorField.run { + viktorField { var res = one repeat(n) { res += one } } @@ -44,7 +43,7 @@ class ViktorBenchmark { @Benchmark fun realdFieldLog() { - realField.run { + realField { val fortyTwo = produce { 42.0 } var res = one repeat(n) { res = ln(fortyTwo) } diff --git a/examples/src/benchmarks/kotlin/scientifik/kmath/utils/utils.kt b/examples/src/benchmarks/kotlin/scientifik/kmath/utils/utils.kt index 6ec9e9c17..305f21d4f 100644 --- a/examples/src/benchmarks/kotlin/scientifik/kmath/utils/utils.kt +++ b/examples/src/benchmarks/kotlin/scientifik/kmath/utils/utils.kt @@ -1,8 +1,13 @@ package scientifik.kmath.utils +import kotlin.contracts.ExperimentalContracts +import kotlin.contracts.InvocationKind +import kotlin.contracts.contract import kotlin.system.measureTimeMillis +@OptIn(ExperimentalContracts::class) internal inline fun measureAndPrint(title: String, block: () -> Unit) { + contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } val time = measureTimeMillis(block) println("$title completed in $time millis") -} \ No newline at end of file +} diff --git a/examples/src/main/kotlin/scientifik/kmath/linear/LinearAlgebraBenchmark.kt b/examples/src/main/kotlin/scientifik/kmath/linear/LinearAlgebraBenchmark.kt index 960f03de3..6cc5411b8 100644 --- a/examples/src/main/kotlin/scientifik/kmath/linear/LinearAlgebraBenchmark.kt +++ b/examples/src/main/kotlin/scientifik/kmath/linear/LinearAlgebraBenchmark.kt @@ -5,6 +5,7 @@ import scientifik.kmath.commons.linear.CMMatrixContext import scientifik.kmath.commons.linear.inverse import scientifik.kmath.commons.linear.toCM import scientifik.kmath.operations.RealField +import scientifik.kmath.operations.invoke import scientifik.kmath.structures.Matrix import kotlin.contracts.ExperimentalContracts import kotlin.random.Random @@ -21,29 +22,18 @@ fun main() { val n = 5000 // iterations - MatrixContext.real.run { - - repeat(50) { - val res = inverse(matrix) - } - - val inverseTime = measureTimeMillis { - repeat(n) { - val res = inverse(matrix) - } - } - + MatrixContext.real { + repeat(50) { val res = inverse(matrix) } + val inverseTime = measureTimeMillis { repeat(n) { val res = inverse(matrix) } } println("[kmath] Inversion of $n matrices $dim x $dim finished in $inverseTime millis") } //commons-math val commonsTime = measureTimeMillis { - CMMatrixContext.run { + CMMatrixContext { val cm = matrix.toCM() //avoid overhead on conversion - repeat(n) { - val res = inverse(cm) - } + repeat(n) { val res = inverse(cm) } } } @@ -53,7 +43,7 @@ fun main() { //koma-ejml val komaTime = measureTimeMillis { - KomaMatrixContext(EJMLMatrixFactory(), RealField).run { + (KomaMatrixContext(EJMLMatrixFactory(), RealField)) { val km = matrix.toKoma() //avoid overhead on conversion repeat(n) { val res = inverse(km) diff --git a/examples/src/main/kotlin/scientifik/kmath/linear/MultiplicationBenchmark.kt b/examples/src/main/kotlin/scientifik/kmath/linear/MultiplicationBenchmark.kt index 03bd0001c..3ae550682 100644 --- a/examples/src/main/kotlin/scientifik/kmath/linear/MultiplicationBenchmark.kt +++ b/examples/src/main/kotlin/scientifik/kmath/linear/MultiplicationBenchmark.kt @@ -4,6 +4,7 @@ import koma.matrix.ejml.EJMLMatrixFactory import scientifik.kmath.commons.linear.CMMatrixContext import scientifik.kmath.commons.linear.toCM import scientifik.kmath.operations.RealField +import scientifik.kmath.operations.invoke import scientifik.kmath.structures.Matrix import kotlin.random.Random import kotlin.system.measureTimeMillis @@ -18,7 +19,7 @@ fun main() { // //warmup // matrix1 dot matrix2 - CMMatrixContext.run { + CMMatrixContext { val cmMatrix1 = matrix1.toCM() val cmMatrix2 = matrix2.toCM() @@ -29,8 +30,7 @@ fun main() { println("CM implementation time: $cmTime") } - - KomaMatrixContext(EJMLMatrixFactory(), RealField).run { + (KomaMatrixContext(EJMLMatrixFactory(), RealField)) { val komaMatrix1 = matrix1.toKoma() val komaMatrix2 = matrix2.toKoma() diff --git a/examples/src/main/kotlin/scientifik/kmath/operations/ComplexDemo.kt b/examples/src/main/kotlin/scientifik/kmath/operations/ComplexDemo.kt index 4841f9dd8..6dbfebce1 100644 --- a/examples/src/main/kotlin/scientifik/kmath/operations/ComplexDemo.kt +++ b/examples/src/main/kotlin/scientifik/kmath/operations/ComplexDemo.kt @@ -9,13 +9,11 @@ fun main() { Complex(index[0].toDouble() - index[1].toDouble(), index[0].toDouble() + index[1].toDouble()) } - - val compute = NDField.complex(8).run { + val compute = (NDField.complex(8)) { val a = produce { (it) -> i * it - it.toDouble() } val b = 3 val c = Complex(1.0, 1.0) (a pow b) + c } - -} \ No newline at end of file +} diff --git a/examples/src/main/kotlin/scientifik/kmath/structures/ComplexND.kt b/examples/src/main/kotlin/scientifik/kmath/structures/ComplexND.kt index 991cd34a1..2329f3fc3 100644 --- a/examples/src/main/kotlin/scientifik/kmath/structures/ComplexND.kt +++ b/examples/src/main/kotlin/scientifik/kmath/structures/ComplexND.kt @@ -13,9 +13,8 @@ fun main() { val realField = NDField.real(dim, dim) val complexField = NDField.complex(dim, dim) - val realTime = measureTimeMillis { - realField.run { + realField { var res: NDBuffer = one repeat(n) { res += 1.0 @@ -26,18 +25,15 @@ fun main() { println("Real addition completed in $realTime millis") val complexTime = measureTimeMillis { - complexField.run { + complexField { var res: NDBuffer = one - repeat(n) { - res += 1.0 - } + repeat(n) { res += 1.0 } } } println("Complex addition completed in $complexTime millis") } - fun complexExample() { //Create a context for 2-d structure with complex values ComplexField { @@ -46,10 +42,7 @@ fun complexExample() { val x = one * 2.5 operator fun Number.plus(other: Complex) = Complex(this.toDouble() + other.re, other.im) //a structure generator specific to this context - val matrix = produce { (k, l) -> - k + l * i - } - + val matrix = produce { (k, l) -> k + l * i } //Perform sum val sum = matrix + x + 1.0 diff --git a/examples/src/main/kotlin/scientifik/kmath/structures/NDField.kt b/examples/src/main/kotlin/scientifik/kmath/structures/NDField.kt index 2aafb504d..1ce74b2a3 100644 --- a/examples/src/main/kotlin/scientifik/kmath/structures/NDField.kt +++ b/examples/src/main/kotlin/scientifik/kmath/structures/NDField.kt @@ -2,14 +2,19 @@ package scientifik.kmath.structures import kotlinx.coroutines.GlobalScope import scientifik.kmath.operations.RealField +import scientifik.kmath.operations.invoke +import kotlin.contracts.ExperimentalContracts +import kotlin.contracts.InvocationKind +import kotlin.contracts.contract import kotlin.system.measureTimeMillis +@OptIn(ExperimentalContracts::class) internal inline fun measureAndPrint(title: String, block: () -> Unit) { + contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } val time = measureTimeMillis(block) println("$title completed in $time millis") } - fun main() { val dim = 1000 val n = 1000 @@ -22,27 +27,21 @@ fun main() { val genericField = NDField.boxing(RealField, dim, dim) measureAndPrint("Automatic field addition") { - autoField.run { + autoField { var res: NDBuffer = one - repeat(n) { - res += number(1.0) - } + repeat(n) { res += number(1.0) } } } measureAndPrint("Element addition") { var res = genericField.one - repeat(n) { - res += 1.0 - } + repeat(n) { res += 1.0 } } measureAndPrint("Specialized addition") { - specializedField.run { + specializedField { var res: NDBuffer = one - repeat(n) { - res += 1.0 - } + repeat(n) { res += 1.0 } } } @@ -60,12 +59,11 @@ fun main() { measureAndPrint("Generic addition") { //genericField.run(action) - genericField.run { + genericField { var res: NDBuffer = one repeat(n) { - res += one // con't avoid using `one` due to resolution ambiguity + res += one // couldn't avoid using `one` due to resolution ambiguity } } } } - -} \ No newline at end of file +} diff --git a/examples/src/main/kotlin/scientifik/kmath/structures/typeSafeDimensions.kt b/examples/src/main/kotlin/scientifik/kmath/structures/typeSafeDimensions.kt index fdc09ed5d..e627cecaa 100644 --- a/examples/src/main/kotlin/scientifik/kmath/structures/typeSafeDimensions.kt +++ b/examples/src/main/kotlin/scientifik/kmath/structures/typeSafeDimensions.kt @@ -16,6 +16,7 @@ fun DMatrixContext.simple() { object D5 : Dimension { + @OptIn(ExperimentalUnsignedTypes::class) override val dim: UInt = 5u } @@ -23,13 +24,10 @@ fun DMatrixContext.custom() { val m1 = produce { i, j -> (i + j).toDouble() } val m2 = produce { i, j -> (i - j).toDouble() } val m3 = produce { i, j -> (i - j).toDouble() } - (m1 dot m2) + m3 } -fun main() { - DMatrixContext.real.run { - simple() - custom() - } -} \ No newline at end of file +fun main(): Unit = with(DMatrixContext.real) { + simple() + custom() +} diff --git a/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MstExpression.kt b/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MstExpression.kt index 59f3f15d8..635dc940d 100644 --- a/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MstExpression.kt +++ b/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MstExpression.kt @@ -2,6 +2,9 @@ package scientifik.kmath.ast import scientifik.kmath.expressions.* import scientifik.kmath.operations.* +import kotlin.contracts.ExperimentalContracts +import kotlin.contracts.InvocationKind +import kotlin.contracts.contract /** * The expression evaluates MST on-flight. Should be much faster than functional expression, but slower than @@ -24,7 +27,7 @@ class MstExpression(val algebra: Algebra, val mst: MST) : Expression { error("Numeric nodes are not supported by $this") } - override fun invoke(arguments: Map): T = InnerAlgebra(arguments).evaluate(mst) + override operator fun invoke(arguments: Map): T = InnerAlgebra(arguments).evaluate(mst) } /** @@ -38,51 +41,72 @@ inline fun , E : Algebra> A.mst( /** * Builds [MstExpression] over [Space]. */ -inline fun Space.mstInSpace(block: MstSpace.() -> MST): MstExpression = - MstExpression(this, MstSpace.block()) +@OptIn(ExperimentalContracts::class) +inline fun Space.mstInSpace(block: MstSpace.() -> MST): MstExpression { + contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } + return MstExpression(this, MstSpace.block()) +} /** * Builds [MstExpression] over [Ring]. */ -inline fun Ring.mstInRing(block: MstRing.() -> MST): MstExpression = - MstExpression(this, MstRing.block()) +@OptIn(ExperimentalContracts::class) +inline fun Ring.mstInRing(block: MstRing.() -> MST): MstExpression { + contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } + return MstExpression(this, MstRing.block()) +} /** * Builds [MstExpression] over [Field]. */ -inline fun Field.mstInField(block: MstField.() -> MST): MstExpression = - MstExpression(this, MstField.block()) +@OptIn(ExperimentalContracts::class) +inline fun Field.mstInField(block: MstField.() -> MST): MstExpression { + contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } + return MstExpression(this, MstField.block()) +} /** * Builds [MstExpression] over [ExtendedField]. */ -inline fun Field.mstInExtendedField(block: MstExtendedField.() -> MST): MstExpression = - MstExpression(this, MstExtendedField.block()) +@OptIn(ExperimentalContracts::class) +inline fun Field.mstInExtendedField(block: MstExtendedField.() -> MST): MstExpression { + contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } + return MstExpression(this, MstExtendedField.block()) +} /** * Builds [MstExpression] over [FunctionalExpressionSpace]. */ -inline fun > FunctionalExpressionSpace.mstInSpace( - block: MstSpace.() -> MST -): MstExpression = algebra.mstInSpace(block) +@OptIn(ExperimentalContracts::class) +inline fun > FunctionalExpressionSpace.mstInSpace(block: MstSpace.() -> MST): MstExpression { + contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } + return algebra.mstInSpace(block) +} /** * Builds [MstExpression] over [FunctionalExpressionRing]. */ -inline fun > FunctionalExpressionRing.mstInRing( - block: MstRing.() -> MST -): MstExpression = algebra.mstInRing(block) + +@OptIn(ExperimentalContracts::class) +inline fun > FunctionalExpressionRing.mstInRing(block: MstRing.() -> MST): MstExpression { + contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } + return algebra.mstInRing(block) +} /** * Builds [MstExpression] over [FunctionalExpressionField]. */ -inline fun > FunctionalExpressionField.mstInField( - block: MstField.() -> MST -): MstExpression = algebra.mstInField(block) +@OptIn(ExperimentalContracts::class) +inline fun > FunctionalExpressionField.mstInField(block: MstField.() -> MST): MstExpression { + contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } + return algebra.mstInField(block) +} /** * Builds [MstExpression] over [FunctionalExpressionExtendedField]. */ -inline fun > FunctionalExpressionExtendedField.mstInExtendedField( - block: MstExtendedField.() -> MST -): MstExpression = algebra.mstInExtendedField(block) +@OptIn(ExperimentalContracts::class) +inline fun > FunctionalExpressionExtendedField.mstInExtendedField(block: MstExtendedField.() -> MST): MstExpression { + contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } + return algebra.mstInExtendedField(block) +} diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/codegenUtils.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/codegenUtils.kt index a637289b8..97bfba7f2 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/codegenUtils.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/codegenUtils.kt @@ -7,6 +7,9 @@ import scientifik.kmath.ast.MST import scientifik.kmath.expressions.Expression import scientifik.kmath.operations.Algebra import java.lang.reflect.Method +import kotlin.contracts.ExperimentalContracts +import kotlin.contracts.InvocationKind +import kotlin.contracts.contract import kotlin.reflect.KClass private val methodNameAdapters: Map, String> by lazy { @@ -26,8 +29,11 @@ internal val KClass<*>.asm: Type /** * Returns singleton array with this value if the [predicate] is true, returns empty array otherwise. */ -internal inline fun T.wrapToArrayIf(predicate: (T) -> Boolean): Array = - if (predicate(this)) arrayOf(this) else emptyArray() +@OptIn(ExperimentalContracts::class) +internal inline fun T.wrapToArrayIf(predicate: (T) -> Boolean): Array { + contract { callsInPlace(predicate, InvocationKind.EXACTLY_ONCE) } + return if (predicate(this)) arrayOf(this) else emptyArray() +} /** * Creates an [InstructionAdapter] from this [MethodVisitor]. @@ -37,8 +43,11 @@ private fun MethodVisitor.instructionAdapter(): InstructionAdapter = Instruction /** * Creates an [InstructionAdapter] from this [MethodVisitor] and applies [block] to it. */ -internal fun MethodVisitor.instructionAdapter(block: InstructionAdapter.() -> Unit): InstructionAdapter = - instructionAdapter().apply(block) +@OptIn(ExperimentalContracts::class) +internal inline fun MethodVisitor.instructionAdapter(block: InstructionAdapter.() -> Unit): InstructionAdapter { + contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } + return instructionAdapter().apply(block) +} /** * Constructs a [Label], then applies it to this visitor. @@ -63,10 +72,14 @@ internal tailrec fun buildName(mst: MST, collision: Int = 0): String { return buildName(mst, collision + 1) } +@OptIn(ExperimentalContracts::class) @Suppress("FunctionName") -internal inline fun ClassWriter(flags: Int, block: ClassWriter.() -> Unit): ClassWriter = - ClassWriter(flags).apply(block) +internal inline fun ClassWriter(flags: Int, block: ClassWriter.() -> Unit): ClassWriter { + contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } + return ClassWriter(flags).apply(block) +} +@OptIn(ExperimentalContracts::class) internal inline fun ClassWriter.visitField( access: Int, name: String, @@ -74,7 +87,10 @@ internal inline fun ClassWriter.visitField( signature: String?, value: Any?, block: FieldVisitor.() -> Unit -): FieldVisitor = visitField(access, name, descriptor, signature, value).apply(block) +): FieldVisitor { + contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } + return visitField(access, name, descriptor, signature, value).apply(block) +} private fun AsmBuilder.findSpecific(context: Algebra, name: String, parameterTypes: Array): Method? = context.javaClass.methods.find { method -> @@ -151,6 +167,7 @@ private fun AsmBuilder.tryInvokeSpecific( /** * Builds specialized algebra call with option to fallback to generic algebra operation accepting String. */ +@OptIn(ExperimentalContracts::class) internal inline fun AsmBuilder.buildAlgebraOperationCall( context: Algebra, name: String, @@ -158,6 +175,7 @@ internal inline fun AsmBuilder.buildAlgebraOperationCall( parameterTypes: Array, parameters: AsmBuilder.() -> Unit ) { + contract { callsInPlace(parameters, InvocationKind.EXACTLY_ONCE) } val arity = parameterTypes.size loadAlgebra() if (!buildExpectationStack(context, name, parameterTypes)) loadStringConstant(name) diff --git a/kmath-commons/src/main/kotlin/scientifik/kmath/commons/expressions/DiffExpression.kt b/kmath-commons/src/main/kotlin/scientifik/kmath/commons/expressions/DiffExpression.kt index 64ebe8da3..9119991e5 100644 --- a/kmath-commons/src/main/kotlin/scientifik/kmath/commons/expressions/DiffExpression.kt +++ b/kmath-commons/src/main/kotlin/scientifik/kmath/commons/expressions/DiffExpression.kt @@ -5,6 +5,7 @@ import scientifik.kmath.expressions.Expression import scientifik.kmath.expressions.ExpressionAlgebra import scientifik.kmath.operations.ExtendedField import scientifik.kmath.operations.Field +import scientifik.kmath.operations.invoke import kotlin.properties.ReadOnlyProperty import kotlin.reflect.KProperty @@ -15,7 +16,6 @@ class DerivativeStructureField( val order: Int, val parameters: Map ) : ExtendedField { - override val zero: DerivativeStructure by lazy { DerivativeStructure(order, parameters.size) } override val one: DerivativeStructure by lazy { DerivativeStructure(order, parameters.size, 1.0) } @@ -23,17 +23,15 @@ class DerivativeStructureField( DerivativeStructure(parameters.size, order, parameters.keys.indexOf(key), value) } - val variable = object : ReadOnlyProperty { - override fun getValue(thisRef: Any?, property: KProperty<*>): DerivativeStructure { - return variables[property.name] ?: error("A variable with name ${property.name} does not exist") - } + val variable: ReadOnlyProperty = object : ReadOnlyProperty { + override fun getValue(thisRef: Any?, property: KProperty<*>): DerivativeStructure = + variables[property.name] ?: error("A variable with name ${property.name} does not exist") } fun variable(name: String, default: DerivativeStructure? = null): DerivativeStructure = variables[name] ?: default ?: error("A variable with name $name does not exist") - - fun Number.const() = DerivativeStructure(order, parameters.size, toDouble()) + fun Number.const(): DerivativeStructure = DerivativeStructure(order, parameters.size, toDouble()) fun DerivativeStructure.deriv(parName: String, order: Int = 1): Double { return deriv(mapOf(parName to order)) @@ -83,16 +81,15 @@ class DerivativeStructureField( override operator fun DerivativeStructure.plus(b: Number): DerivativeStructure = add(b.toDouble()) override operator fun DerivativeStructure.minus(b: Number): DerivativeStructure = subtract(b.toDouble()) - override operator fun Number.plus(b: DerivativeStructure) = b + this - override operator fun Number.minus(b: DerivativeStructure) = b - this + override operator fun Number.plus(b: DerivativeStructure): DerivativeStructure = b + this + override operator fun Number.minus(b: DerivativeStructure): DerivativeStructure = b - this } /** * A constructs that creates a derivative structure with required order on-demand */ class DiffExpression(val function: DerivativeStructureField.() -> DerivativeStructure) : Expression { - - override fun invoke(arguments: Map): Double = DerivativeStructureField( + override operator fun invoke(arguments: Map): Double = DerivativeStructureField( 0, arguments ).run(function).value @@ -101,45 +98,40 @@ class DiffExpression(val function: DerivativeStructureField.() -> DerivativeStru * Get the derivative expression with given orders * TODO make result [DiffExpression] */ - fun derivative(orders: Map): Expression { - return object : Expression { - override fun invoke(arguments: Map): Double = - DerivativeStructureField(orders.values.max() ?: 0, arguments) - .run { - function().deriv(orders) - } - } + fun derivative(orders: Map): Expression = object : Expression { + override operator fun invoke(arguments: Map): Double = + (DerivativeStructureField(orders.values.max() ?: 0, arguments)) { function().deriv(orders) } } //TODO add gradient and maybe other vector operators } -fun DiffExpression.derivative(vararg orders: Pair) = derivative(mapOf(*orders)) -fun DiffExpression.derivative(name: String) = derivative(name to 1) +fun DiffExpression.derivative(vararg orders: Pair): Expression = derivative(mapOf(*orders)) +fun DiffExpression.derivative(name: String): Expression = derivative(name to 1) /** * A context for [DiffExpression] (not to be confused with [DerivativeStructure]) */ object DiffExpressionAlgebra : ExpressionAlgebra, Field { - override fun variable(name: String, default: Double?) = + override fun variable(name: String, default: Double?): DiffExpression = DiffExpression { variable(name, default?.const()) } override fun const(value: Double): DiffExpression = DiffExpression { value.const() } - override fun add(a: DiffExpression, b: DiffExpression) = + override fun add(a: DiffExpression, b: DiffExpression): DiffExpression = DiffExpression { a.function(this) + b.function(this) } - override val zero = DiffExpression { 0.0.const() } + override val zero: DiffExpression = DiffExpression { 0.0.const() } - override fun multiply(a: DiffExpression, k: Number) = + override fun multiply(a: DiffExpression, k: Number): DiffExpression = DiffExpression { a.function(this) * k } - override val one = DiffExpression { 1.0.const() } + override val one: DiffExpression = DiffExpression { 1.0.const() } - override fun multiply(a: DiffExpression, b: DiffExpression) = + override fun multiply(a: DiffExpression, b: DiffExpression): DiffExpression = DiffExpression { a.function(this) * b.function(this) } - override fun divide(a: DiffExpression, b: DiffExpression) = + override fun divide(a: DiffExpression, b: DiffExpression): DiffExpression = DiffExpression { a.function(this) / b.function(this) } } diff --git a/kmath-commons/src/main/kotlin/scientifik/kmath/commons/linear/CMMatrix.kt b/kmath-commons/src/main/kotlin/scientifik/kmath/commons/linear/CMMatrix.kt index a17effccc..f0bbdbe65 100644 --- a/kmath-commons/src/main/kotlin/scientifik/kmath/commons/linear/CMMatrix.kt +++ b/kmath-commons/src/main/kotlin/scientifik/kmath/commons/linear/CMMatrix.kt @@ -1,8 +1,6 @@ package scientifik.kmath.commons.linear import org.apache.commons.math3.linear.* -import org.apache.commons.math3.linear.RealMatrix -import org.apache.commons.math3.linear.RealVector import scientifik.kmath.linear.* import scientifik.kmath.structures.Matrix import scientifik.kmath.structures.NDStructure @@ -14,12 +12,12 @@ class CMMatrix(val origin: RealMatrix, features: Set? = null) : override val features: Set = features ?: sequence { if (origin is DiagonalMatrix) yield(DiagonalFeature) - }.toSet() + }.toHashSet() - override fun suggestFeature(vararg features: MatrixFeature) = + override fun suggestFeature(vararg features: MatrixFeature): CMMatrix = CMMatrix(origin, this.features + features) - override fun get(i: Int, j: Int): Double = origin.getEntry(i, j) + override operator fun get(i: Int, j: Int): Double = origin.getEntry(i, j) override fun equals(other: Any?): Boolean { return NDStructure.equals(this, other as? NDStructure<*> ?: return false) @@ -40,24 +38,22 @@ fun Matrix.toCM(): CMMatrix = if (this is CMMatrix) { CMMatrix(Array2DRowRealMatrix(array)) } -fun RealMatrix.asMatrix() = CMMatrix(this) +fun RealMatrix.asMatrix(): CMMatrix = CMMatrix(this) class CMVector(val origin: RealVector) : Point { override val size: Int get() = origin.dimension - override fun get(index: Int): Double = origin.getEntry(index) + override operator fun get(index: Int): Double = origin.getEntry(index) - override fun iterator(): Iterator = origin.toArray().iterator() + override operator fun iterator(): Iterator = origin.toArray().iterator() } -fun Point.toCM(): CMVector = if (this is CMVector) { - this -} else { +fun Point.toCM(): CMVector = if (this is CMVector) this else { val array = DoubleArray(size) { this[it] } CMVector(ArrayRealVector(array)) } -fun RealVector.toPoint() = CMVector(this) +fun RealVector.toPoint(): CMVector = CMVector(this) object CMMatrixContext : MatrixContext { override fun produce(rows: Int, columns: Int, initializer: (i: Int, j: Int) -> Double): CMMatrix { @@ -65,32 +61,33 @@ object CMMatrixContext : MatrixContext { return CMMatrix(Array2DRowRealMatrix(array)) } - override fun Matrix.dot(other: Matrix) = + override fun Matrix.dot(other: Matrix): CMMatrix = CMMatrix(this.toCM().origin.multiply(other.toCM().origin)) override fun Matrix.dot(vector: Point): CMVector = CMVector(this.toCM().origin.preMultiply(vector.toCM().origin)) - override fun Matrix.unaryMinus(): CMMatrix = + override operator fun Matrix.unaryMinus(): CMMatrix = produce(rowNum, colNum) { i, j -> -get(i, j) } - override fun add(a: Matrix, b: Matrix) = + override fun add(a: Matrix, b: Matrix): CMMatrix = CMMatrix(a.toCM().origin.multiply(b.toCM().origin)) - override fun Matrix.minus(b: Matrix) = + override operator fun Matrix.minus(b: Matrix): CMMatrix = CMMatrix(this.toCM().origin.subtract(b.toCM().origin)) - override fun multiply(a: Matrix, k: Number) = + override fun multiply(a: Matrix, k: Number): CMMatrix = CMMatrix(a.toCM().origin.scalarMultiply(k.toDouble())) - override fun Matrix.times(value: Double): Matrix = + override operator fun Matrix.times(value: Double): Matrix = produce(rowNum, colNum) { i, j -> get(i, j) * value } } operator fun CMMatrix.plus(other: CMMatrix): CMMatrix = CMMatrix(this.origin.add(other.origin)) + operator fun CMMatrix.minus(other: CMMatrix): CMMatrix = CMMatrix(this.origin.subtract(other.origin)) infix fun CMMatrix.dot(other: CMMatrix): CMMatrix = - CMMatrix(this.origin.multiply(other.origin)) \ No newline at end of file + CMMatrix(this.origin.multiply(other.origin)) diff --git a/kmath-commons/src/main/kotlin/scientifik/kmath/commons/random/CMRandomGeneratorWrapper.kt b/kmath-commons/src/main/kotlin/scientifik/kmath/commons/random/CMRandomGeneratorWrapper.kt index 13e79d60e..cb2b5dd9c 100644 --- a/kmath-commons/src/main/kotlin/scientifik/kmath/commons/random/CMRandomGeneratorWrapper.kt +++ b/kmath-commons/src/main/kotlin/scientifik/kmath/commons/random/CMRandomGeneratorWrapper.kt @@ -4,10 +4,9 @@ import scientifik.kmath.prob.RandomGenerator class CMRandomGeneratorWrapper(val factory: (IntArray) -> RandomGenerator) : org.apache.commons.math3.random.RandomGenerator { - private var generator = factory(intArrayOf()) + private var generator: RandomGenerator = factory(intArrayOf()) override fun nextBoolean(): Boolean = generator.nextBoolean() - override fun nextFloat(): Float = generator.nextDouble().toFloat() override fun setSeed(seed: Int) { @@ -27,12 +26,8 @@ class CMRandomGeneratorWrapper(val factory: (IntArray) -> RandomGenerator) : } override fun nextInt(): Int = generator.nextInt() - override fun nextInt(n: Int): Int = generator.nextInt(n) - override fun nextGaussian(): Double = TODO() - override fun nextDouble(): Double = generator.nextDouble() - override fun nextLong(): Long = generator.nextLong() -} \ No newline at end of file +} diff --git a/kmath-commons/src/test/kotlin/scientifik/kmath/commons/expressions/AutoDiffTest.kt b/kmath-commons/src/test/kotlin/scientifik/kmath/commons/expressions/AutoDiffTest.kt index c77f30eb7..ae4bb755a 100644 --- a/kmath-commons/src/test/kotlin/scientifik/kmath/commons/expressions/AutoDiffTest.kt +++ b/kmath-commons/src/test/kotlin/scientifik/kmath/commons/expressions/AutoDiffTest.kt @@ -1,11 +1,17 @@ package scientifik.kmath.commons.expressions import scientifik.kmath.expressions.invoke +import kotlin.contracts.ExperimentalContracts +import kotlin.contracts.InvocationKind +import kotlin.contracts.contract import kotlin.test.Test import kotlin.test.assertEquals -inline fun diff(order: Int, vararg parameters: Pair, block: DerivativeStructureField.() -> R) = - DerivativeStructureField(order, mapOf(*parameters)).run(block) +@OptIn(ExperimentalContracts::class) +inline fun diff(order: Int, vararg parameters: Pair, block: DerivativeStructureField.() -> R): R { + contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } + return DerivativeStructureField(order, mapOf(*parameters)).run(block) +} class AutoDiffTest { @Test diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/Builders.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/Builders.kt index 8cd6e28f8..3bbc86ce5 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/Builders.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/Builders.kt @@ -4,28 +4,42 @@ import scientifik.kmath.operations.ExtendedField import scientifik.kmath.operations.Field import scientifik.kmath.operations.Ring import scientifik.kmath.operations.Space +import kotlin.contracts.ExperimentalContracts +import kotlin.contracts.InvocationKind +import kotlin.contracts.contract /** * Creates a functional expression with this [Space]. */ -fun Space.spaceExpression(block: FunctionalExpressionSpace>.() -> Expression): Expression = - FunctionalExpressionSpace(this).run(block) +@OptIn(ExperimentalContracts::class) +inline fun Space.spaceExpression(block: FunctionalExpressionSpace>.() -> Expression): Expression { + contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } + return FunctionalExpressionSpace(this).block() +} /** * Creates a functional expression with this [Ring]. */ -fun Ring.ringExpression(block: FunctionalExpressionRing>.() -> Expression): Expression = - FunctionalExpressionRing(this).run(block) +@OptIn(ExperimentalContracts::class) +inline fun Ring.ringExpression(block: FunctionalExpressionRing>.() -> Expression): Expression { + contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } + return FunctionalExpressionRing(this).block() +} /** * Creates a functional expression with this [Field]. */ -fun Field.fieldExpression(block: FunctionalExpressionField>.() -> Expression): Expression = - FunctionalExpressionField(this).run(block) +@OptIn(ExperimentalContracts::class) +inline fun Field.fieldExpression(block: FunctionalExpressionField>.() -> Expression): Expression { + contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } + return FunctionalExpressionField(this).block() +} /** * Creates a functional expression with this [ExtendedField]. */ -fun ExtendedField.fieldExpression( - block: FunctionalExpressionExtendedField>.() -> Expression -): Expression = FunctionalExpressionExtendedField(this).run(block) +@OptIn(ExperimentalContracts::class) +inline fun ExtendedField.extendedFieldExpression(block: FunctionalExpressionExtendedField>.() -> Expression): Expression { + contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } + return FunctionalExpressionExtendedField(this).block() +} diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/Expression.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/Expression.kt index 380822f78..fd11c246d 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/Expression.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/Expression.kt @@ -22,7 +22,7 @@ interface Expression { */ fun Algebra.expression(block: Algebra.(arguments: Map) -> T): Expression = object : Expression { - override fun invoke(arguments: Map): T = block(arguments) + override operator fun invoke(arguments: Map): T = block(arguments) } /** diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressionAlgebra.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressionAlgebra.kt index 9fe8aaf93..d36c31a0d 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressionAlgebra.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/FunctionalExpressionAlgebra.kt @@ -4,7 +4,7 @@ import scientifik.kmath.operations.* internal class FunctionalUnaryOperation(val context: Algebra, val name: String, private val expr: Expression) : Expression { - override fun invoke(arguments: Map): T = context.unaryOperation(name, expr.invoke(arguments)) + override operator fun invoke(arguments: Map): T = context.unaryOperation(name, expr.invoke(arguments)) } internal class FunctionalBinaryOperation( @@ -13,17 +13,17 @@ internal class FunctionalBinaryOperation( val first: Expression, val second: Expression ) : Expression { - override fun invoke(arguments: Map): T = + override operator fun invoke(arguments: Map): T = context.binaryOperation(name, first.invoke(arguments), second.invoke(arguments)) } internal class FunctionalVariableExpression(val name: String, val default: T? = null) : Expression { - override fun invoke(arguments: Map): T = + override operator fun invoke(arguments: Map): T = arguments[name] ?: default ?: error("Parameter not found: $name") } internal class FunctionalConstantExpression(val value: T) : Expression { - override fun invoke(arguments: Map): T = value + override operator fun invoke(arguments: Map): T = value } internal class FunctionalConstProductExpression( @@ -31,7 +31,7 @@ internal class FunctionalConstProductExpression( private val expr: Expression, val const: Number ) : Expression { - override fun invoke(arguments: Map): T = context.multiply(expr.invoke(arguments), const) + override operator fun invoke(arguments: Map): T = context.multiply(expr.invoke(arguments), const) } /** diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/BufferMatrix.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/BufferMatrix.kt index 2e1f32501..343b8287e 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/BufferMatrix.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/BufferMatrix.kt @@ -53,16 +53,12 @@ class BufferMatrix( override fun suggestFeature(vararg features: MatrixFeature): BufferMatrix = BufferMatrix(rowNum, colNum, buffer, this.features + features) - override fun get(index: IntArray): T = get(index[0], index[1]) + override operator fun get(index: IntArray): T = get(index[0], index[1]) - override fun get(i: Int, j: Int): T = buffer[i * colNum + j] + override operator fun get(i: Int, j: Int): T = buffer[i * colNum + j] override fun elements(): Sequence> = sequence { - for (i in 0 until rowNum) { - for (j in 0 until colNum) { - yield(intArrayOf(i, j) to get(i, j)) - } - } + for (i in 0 until rowNum) for (j in 0 until colNum) yield(intArrayOf(i, j) to get(i, j)) } override fun equals(other: Any?): Boolean { @@ -95,7 +91,7 @@ class BufferMatrix( * Optimized dot product for real matrices */ infix fun BufferMatrix.dot(other: BufferMatrix): BufferMatrix { - if (this.colNum != other.rowNum) error("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})" } val array = DoubleArray(this.rowNum * other.colNum) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/FeaturedMatrix.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/FeaturedMatrix.kt index dd17c4fe7..39256f4ac 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/FeaturedMatrix.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/FeaturedMatrix.kt @@ -4,6 +4,8 @@ import scientifik.kmath.operations.Ring import scientifik.kmath.structures.Matrix import scientifik.kmath.structures.Structure2D import scientifik.kmath.structures.asBuffer +import kotlin.contracts.ExperimentalContracts +import kotlin.contracts.contract import kotlin.math.sqrt /** @@ -26,15 +28,18 @@ interface FeaturedMatrix : Matrix { companion object } -fun Structure2D.Companion.real(rows: Int, columns: Int, initializer: (Int, Int) -> Double): Matrix = - MatrixContext.real.produce(rows, columns, initializer) +@OptIn(ExperimentalContracts::class) +inline fun Structure2D.Companion.real(rows: Int, columns: Int, initializer: (Int, Int) -> Double): Matrix { + contract { callsInPlace(initializer) } + return MatrixContext.real.produce(rows, columns, initializer) +} /** * Build a square matrix from given elements. */ fun Structure2D.Companion.square(vararg elements: T): FeaturedMatrix { val size: Int = sqrt(elements.size.toDouble()).toInt() - if (size * size != elements.size) error("The number of elements ${elements.size} is not a full square") + 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) } diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/LUPDecomposition.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/LUPDecomposition.kt index d04a99fbb..f3e4f648f 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/LUPDecomposition.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/LUPDecomposition.kt @@ -3,6 +3,7 @@ package scientifik.kmath.linear import scientifik.kmath.operations.Field import scientifik.kmath.operations.RealField import scientifik.kmath.operations.Ring +import scientifik.kmath.operations.invoke import scientifik.kmath.structures.BufferAccessor2D import scientifik.kmath.structures.Matrix import scientifik.kmath.structures.Structure2D @@ -60,15 +61,13 @@ class LUPDecomposition( * @return determinant of the matrix */ override val determinant: T by lazy { - with(elementContext) { - (0 until lu.shape[0]).fold(if (even) one else -one) { value, i -> value * lu[i, i] } - } + elementContext { (0 until lu.shape[0]).fold(if (even) one else -one) { value, i -> value * lu[i, i] } } } } fun , F : Field> GenericMatrixContext.abs(value: T): T = - if (value > elementContext.zero) value else with(elementContext) { -value } + if (value > elementContext.zero) value else elementContext { -value } /** @@ -88,43 +87,34 @@ fun , F : Field> GenericMatrixContext.lup( //TODO just waits for KEEP-176 BufferAccessor2D(type, matrix.rowNum, matrix.colNum).run { - elementContext.run { - + elementContext { val lu = create(matrix) // Initialize permutation array and parity - for (row in 0 until m) { - pivot[row] = row - } + for (row in 0 until m) pivot[row] = row var even = true // Initialize permutation array and parity - for (row in 0 until m) { - pivot[row] = row - } + for (row in 0 until m) pivot[row] = row // Loop over columns for (col in 0 until m) { - // upper for (row in 0 until col) { val luRow = lu.row(row) var sum = luRow[col] - for (i in 0 until row) { - sum -= luRow[i] * lu[i, col] - } + for (i in 0 until row) sum -= luRow[i] * lu[i, col] luRow[col] = sum } // lower var max = col // permutation row var largest = -one + for (row in col until m) { val luRow = lu.row(row) var sum = luRow[col] - for (i in 0 until col) { - sum -= luRow[i] * lu[i, col] - } + for (i in 0 until col) sum -= luRow[i] * lu[i, col] luRow[col] = sum // maintain best permutation choice @@ -135,19 +125,19 @@ fun , F : Field> GenericMatrixContext.lup( } // Singularity check - if (checkSingular(this@lup.abs(lu[max, col]))) { - error("The matrix is singular") - } + check(!checkSingular(this@lup.abs(lu[max, col]))) { "The matrix is singular" } // Pivot if necessary if (max != col) { val luMax = lu.row(max) val luCol = lu.row(col) + for (i in 0 until m) { val tmp = luMax[i] luMax[i] = luCol[i] luCol[i] = tmp } + val temp = pivot[max] pivot[max] = pivot[col] pivot[col] = temp @@ -156,9 +146,7 @@ fun , F : Field> GenericMatrixContext.lup( // Divide the lower elements by the "winning" diagonal elt. val luDiag = lu[col, col] - 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, lu.collect(), pivot, even) @@ -175,28 +163,23 @@ fun GenericMatrixContext.lup(matrix: Matrix): LUPDeco lup(Double::class, matrix) { it < 1e-11 } fun LUPDecomposition.solve(type: KClass, matrix: Matrix): Matrix { - - if (matrix.rowNum != pivot.size) { - error("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(type, matrix.rowNum, matrix.colNum).run { - elementContext.run { - + elementContext { // Apply permutations to b val bp = create { _, _ -> zero } for (row in pivot.indices) { val bpRow = bp.row(row) val pRow = pivot[row] - for (col in 0 until matrix.colNum) { - bpRow[col] = matrix[pRow, col] - } + for (col in 0 until matrix.colNum) bpRow[col] = matrix[pRow, col] } // Solve LY = b for (col in pivot.indices) { val bpCol = bp.row(col) + for (i in col + 1 until pivot.size) { val bpI = bp.row(i) val luICol = lu[i, col] @@ -210,17 +193,15 @@ fun LUPDecomposition.solve(type: KClass, matrix: Matrix): Mat for (col in pivot.size - 1 downTo 0) { val bpCol = bp.row(col) val luDiag = lu[col, col] - for (j in 0 until matrix.colNum) { - bpCol[j] /= luDiag - } + for (j in 0 until matrix.colNum) bpCol[j] /= luDiag + for (i in 0 until col) { val bpI = bp.row(i) val luICol = lu[i, col] - for (j in 0 until matrix.colNum) { - bpI[j] -= bpCol[j] * luICol - } + for (j in 0 until matrix.colNum) bpI[j] -= bpCol[j] * luICol } } + return context.produce(pivot.size, matrix.colNum) { i, j -> bp[i, j] } } } diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/MatrixBuilder.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/MatrixBuilder.kt index 516f65bb8..390362f8c 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/MatrixBuilder.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/MatrixBuilder.kt @@ -7,7 +7,7 @@ import scientifik.kmath.structures.asBuffer class MatrixBuilder(val rows: Int, val columns: Int) { operator fun invoke(vararg elements: T): FeaturedMatrix { - if (rows * columns != elements.size) error("The number of elements ${elements.size} is not equal $rows * $columns") + require(rows * columns == elements.size) { "The number of elements ${elements.size} is not equal $rows * $columns" } val buffer = elements.asBuffer() return BufferMatrix(rows, columns, buffer) } diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/MatrixContext.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/MatrixContext.kt index 5dc86a7dd..763bb1615 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/MatrixContext.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/MatrixContext.kt @@ -2,6 +2,7 @@ package scientifik.kmath.linear import scientifik.kmath.operations.Ring import scientifik.kmath.operations.SpaceOperations +import scientifik.kmath.operations.invoke import scientifik.kmath.operations.sum import scientifik.kmath.structures.Buffer import scientifik.kmath.structures.BufferFactory @@ -37,8 +38,7 @@ interface MatrixContext : SpaceOperations> { fun > buffered( ring: R, bufferFactory: BufferFactory = Buffer.Companion::boxing - ): GenericMatrixContext = - BufferMatrixContext(ring, bufferFactory) + ): GenericMatrixContext = BufferMatrixContext(ring, bufferFactory) /** * Automatic buffered matrix, unboxed if it is possible @@ -61,45 +61,49 @@ interface GenericMatrixContext> : MatrixContext { override infix fun Matrix.dot(other: Matrix): Matrix { //TODO add typed error - if (this.colNum != other.rowNum) error("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})" } + return produce(rowNum, other.colNum) { i, j -> val row = rows[i] val column = other.columns[j] - with(elementContext) { - sum(row.asSequence().zip(column.asSequence(), ::multiply)) - } + elementContext { sum(row.asSequence().zip(column.asSequence(), ::multiply)) } } } override infix fun Matrix.dot(vector: Point): Point { //TODO add typed error - if (this.colNum != vector.size) error("Matrix dot vector operation dimension mismatch: ($rowNum, $colNum) x (${vector.size})") + require(colNum == vector.size) { "Matrix dot vector operation dimension mismatch: ($rowNum, $colNum) x (${vector.size})" } + return point(rowNum) { i -> val row = rows[i] - with(elementContext) { - sum(row.asSequence().zip(vector.asSequence(), ::multiply)) - } + elementContext { sum(row.asSequence().zip(vector.asSequence(), ::multiply)) } } } override operator fun Matrix.unaryMinus(): Matrix = - produce(rowNum, colNum) { i, j -> elementContext.run { -get(i, j) } } + produce(rowNum, colNum) { i, j -> elementContext { -get(i, j) } } override fun add(a: Matrix, b: Matrix): Matrix { - if (a.rowNum != b.rowNum || a.colNum != b.colNum) error("Matrix operation dimension mismatch. [${a.rowNum},${a.colNum}] + [${b.rowNum},${b.colNum}]") - return produce(a.rowNum, a.colNum) { i, j -> elementContext.run { a[i, j] + b[i, j] } } + require(a.rowNum == b.rowNum && a.colNum == b.colNum) { + "Matrix operation dimension mismatch. [${a.rowNum},${a.colNum}] + [${b.rowNum},${b.colNum}]" + } + + return produce(a.rowNum, a.colNum) { i, j -> elementContext { a[i, j] + b[i, j] } } } override operator fun Matrix.minus(b: Matrix): Matrix { - if (rowNum != b.rowNum || colNum != b.colNum) error("Matrix operation dimension mismatch. [$rowNum,$colNum] - [${b.rowNum},${b.colNum}]") - return produce(rowNum, colNum) { i, j -> elementContext.run { get(i, j) + b[i, j] } } + require(rowNum == b.rowNum && colNum == b.colNum) { + "Matrix operation dimension mismatch. [$rowNum,$colNum] - [${b.rowNum},${b.colNum}]" + } + + return produce(rowNum, colNum) { i, j -> elementContext { get(i, j) + b[i, j] } } } override fun multiply(a: Matrix, k: Number): Matrix = - produce(a.rowNum, a.colNum) { i, j -> elementContext.run { a[i, j] * k } } + produce(a.rowNum, a.colNum) { i, j -> elementContext { a[i, j] * k } } operator fun Number.times(matrix: FeaturedMatrix): Matrix = matrix * this - override fun Matrix.times(value: T): Matrix = - produce(rowNum, colNum) { i, j -> elementContext.run { get(i, j) * value } } + override operator fun Matrix.times(value: T): Matrix = + produce(rowNum, colNum) { i, j -> elementContext { get(i, j) * value } } } diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/VectorSpace.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/VectorSpace.kt index 691b464fc..82e5c7ef6 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/VectorSpace.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/VectorSpace.kt @@ -2,6 +2,7 @@ package scientifik.kmath.linear import scientifik.kmath.operations.RealField import scientifik.kmath.operations.Space +import scientifik.kmath.operations.invoke import scientifik.kmath.structures.Buffer import scientifik.kmath.structures.BufferFactory @@ -10,10 +11,9 @@ import scientifik.kmath.structures.BufferFactory * Could be used on any point-like structure */ interface VectorSpace> : Space> { - val size: Int - val space: S + override val zero: Point get() = produce { space.zero } fun produce(initializer: (Int) -> T): Point @@ -22,29 +22,24 @@ interface VectorSpace> : Space> { */ //fun produceElement(initializer: (Int) -> T): Vector - override val zero: Point get() = produce { space.zero } + override fun add(a: Point, b: Point): Point = produce { space { a[it] + b[it] } } - override fun add(a: Point, b: Point): Point = produce { with(space) { a[it] + b[it] } } - - override fun multiply(a: Point, k: Number): Point = produce { with(space) { a[it] * k } } + override fun multiply(a: Point, k: Number): Point = produce { space { a[it] * k } } //TODO add basis companion object { - - private val realSpaceCache = HashMap>() + private val realSpaceCache: MutableMap> = hashMapOf() /** * Non-boxing double vector space */ - fun real(size: Int): BufferVectorSpace { - return realSpaceCache.getOrPut(size) { - BufferVectorSpace( - size, - RealField, - Buffer.Companion::auto - ) - } + fun real(size: Int): BufferVectorSpace = realSpaceCache.getOrPut(size) { + BufferVectorSpace( + size, + RealField, + Buffer.Companion::auto + ) } /** diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/VirtualMatrix.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/VirtualMatrix.kt index 207151d57..5266dc884 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/VirtualMatrix.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/VirtualMatrix.kt @@ -18,7 +18,7 @@ class VirtualMatrix( override val shape: IntArray get() = intArrayOf(rowNum, colNum) - override 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 = VirtualMatrix(rowNum, colNum, this.features + features, generator) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/misc/AutoDiff.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/misc/AutoDiff.kt index db8863ae8..1609f063a 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/misc/AutoDiff.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/misc/AutoDiff.kt @@ -3,8 +3,12 @@ package scientifik.kmath.misc import scientifik.kmath.linear.Point import scientifik.kmath.operations.ExtendedField import scientifik.kmath.operations.Field +import scientifik.kmath.operations.invoke import scientifik.kmath.operations.sum import scientifik.kmath.structures.asBuffer +import kotlin.contracts.ExperimentalContracts +import kotlin.contracts.InvocationKind +import kotlin.contracts.contract /* * Implementation of backward-mode automatic differentiation. @@ -27,15 +31,14 @@ class DerivationResult( /** * compute divergence */ - fun div(): T = context.run { sum(deriv.values) } + fun div(): T = context { sum(deriv.values) } /** * Compute a gradient for variables in given order */ - fun grad(vararg variables: Variable): Point = if (variables.isEmpty()) { - error("Variable order is not provided for gradient construction") - } else { - variables.map(::deriv).asBuffer() + fun grad(vararg variables: Variable): Point { + check(variables.isNotEmpty()) { "Variable order is not provided for gradient construction" } + return variables.map(::deriv).asBuffer() } } @@ -52,19 +55,28 @@ class DerivationResult( * assertEquals(9.0, x.d) // dy/dx * ``` */ -fun > F.deriv(body: AutoDiffField.() -> Variable): DerivationResult = - AutoDiffContext(this).run { +@OptIn(ExperimentalContracts::class) +inline fun > F.deriv(body: AutoDiffField.() -> Variable): DerivationResult { + contract { callsInPlace(body, InvocationKind.EXACTLY_ONCE) } + + return (AutoDiffContext(this)) { val result = body() - result.d = context.one// computing derivative w.r.t result + result.d = context.one // computing derivative w.r.t result runBackwardPass() DerivationResult(result.value, derivatives, this@deriv) } +} abstract class AutoDiffField> : Field> { - abstract val context: F + /** + * A variable accessing inner state of derivatives. + * Use this function in inner builders to avoid creating additional derivative bindings + */ + abstract var Variable.d: T + /** * Performs update of derivative after the rest of the formula in the back-pass. * @@ -78,12 +90,6 @@ abstract class AutoDiffField> : Field> { */ abstract fun derive(value: R, block: F.(R) -> Unit): R - /** - * A variable accessing inner state of derivatives. - * Use this function in inner builders to avoid creating additional derivative bindings - */ - abstract var Variable.d: T - abstract fun variable(value: T): Variable inline fun variable(block: F.() -> T): Variable = variable(context.block()) @@ -98,46 +104,35 @@ abstract class AutoDiffField> : Field> { override operator fun Variable.plus(b: Number): Variable = b.plus(this) override operator fun Number.minus(b: Variable): Variable = - derive(variable { this@minus.toDouble() * one - b.value }) { z -> - b.d -= z.d - } + derive(variable { this@minus.toDouble() * one - b.value }) { z -> b.d -= z.d } override operator fun Variable.minus(b: Number): Variable = - derive(variable { this@minus.value - one * b.toDouble() }) { z -> - this@minus.d += z.d - } + derive(variable { this@minus.value - one * b.toDouble() }) { z -> this@minus.d += z.d } } /** * Automatic Differentiation context class. */ -private class AutoDiffContext>(override val context: F) : AutoDiffField() { - +@PublishedApi +internal class AutoDiffContext>(override val context: F) : AutoDiffField() { // this stack contains pairs of blocks and values to apply them to - private var stack = arrayOfNulls(8) - private var sp = 0 - - internal val derivatives = HashMap, T>() - + private var stack: Array = arrayOfNulls(8) + private var sp: Int = 0 + val derivatives: MutableMap, T> = hashMapOf() + override val zero: Variable get() = Variable(context.zero) + override val one: Variable get() = Variable(context.one) /** * A variable coupled with its derivative. For internal use only */ private class VariableWithDeriv(x: T, var d: T) : Variable(x) - override fun variable(value: T): Variable = VariableWithDeriv(value, context.zero) override var Variable.d: T get() = (this as? VariableWithDeriv)?.d ?: derivatives[this] ?: context.zero - set(value) { - if (this is VariableWithDeriv) { - d = value - } else { - derivatives[this] = value - } - } + set(value) = if (this is VariableWithDeriv) d = value else derivatives[this] = value @Suppress("UNCHECKED_CAST") override fun derive(value: R, block: F.(R) -> Unit): R { @@ -160,67 +155,49 @@ private class AutoDiffContext>(override val context: F) : // Basic math (+, -, *, /) - override fun add(a: Variable, b: Variable): Variable = - derive(variable { a.value + b.value }) { z -> - a.d += z.d - b.d += z.d - } + override fun add(a: Variable, b: Variable): Variable = derive(variable { a.value + b.value }) { z -> + a.d += z.d + b.d += z.d + } - override fun multiply(a: Variable, b: Variable): Variable = - derive(variable { a.value * b.value }) { z -> - a.d += z.d * b.value - b.d += z.d * a.value - } + override fun multiply(a: Variable, b: Variable): Variable = derive(variable { a.value * b.value }) { z -> + a.d += z.d * b.value + b.d += z.d * a.value + } - override fun divide(a: Variable, b: Variable): Variable = - derive(variable { a.value / b.value }) { z -> - a.d += z.d / b.value - b.d -= z.d * a.value / (b.value * b.value) - } + override fun divide(a: Variable, b: Variable): Variable = derive(variable { a.value / b.value }) { z -> + a.d += z.d / b.value + b.d -= z.d * a.value / (b.value * b.value) + } - override fun multiply(a: Variable, k: Number): Variable = - derive(variable { k.toDouble() * a.value }) { z -> - a.d += z.d * k.toDouble() - } - - override val zero: Variable get() = Variable(context.zero) - override val one: Variable get() = Variable(context.one) + override fun multiply(a: Variable, k: Number): Variable = derive(variable { k.toDouble() * a.value }) { z -> + a.d += z.d * k.toDouble() + } } // Extensions for differentiation of various basic mathematical functions // x ^ 2 fun > AutoDiffField.sqr(x: Variable): Variable = - derive(variable { x.value * x.value }) { z -> - x.d += z.d * 2 * x.value - } + derive(variable { x.value * x.value }) { z -> x.d += z.d * 2 * x.value } // x ^ 1/2 fun > AutoDiffField.sqrt(x: Variable): Variable = - derive(variable { sqrt(x.value) }) { z -> - x.d += z.d * 0.5 / z.value - } + derive(variable { sqrt(x.value) }) { z -> x.d += z.d * 0.5 / z.value } // x ^ y (const) fun > AutoDiffField.pow(x: Variable, y: Double): Variable = - derive(variable { power(x.value, y) }) { z -> - x.d += z.d * y * power(x.value, y - 1) - } + derive(variable { power(x.value, y) }) { z -> x.d += z.d * y * power(x.value, y - 1) } fun > AutoDiffField.pow(x: Variable, y: Int): Variable = pow(x, y.toDouble()) // exp(x) fun > AutoDiffField.exp(x: Variable): Variable = - derive(variable { exp(x.value) }) { z -> - x.d += z.d * z.value - } + derive(variable { exp(x.value) }) { z -> x.d += z.d * z.value } // ln(x) -fun > AutoDiffField.ln(x: Variable): Variable = derive( - variable { ln(x.value) } -) { z -> - x.d += z.d / x.value -} +fun > AutoDiffField.ln(x: Variable): Variable = + derive(variable { ln(x.value) }) { z -> x.d += z.d / x.value } // x ^ y (any) fun > AutoDiffField.pow(x: Variable, y: Variable): Variable = @@ -228,12 +205,8 @@ fun > AutoDiffField.pow(x: Variable, y: V // sin(x) fun > AutoDiffField.sin(x: Variable): Variable = - derive(variable { sin(x.value) }) { z -> - x.d += z.d * cos(x.value) - } + derive(variable { sin(x.value) }) { z -> x.d += z.d * cos(x.value) } // cos(x) fun > AutoDiffField.cos(x: Variable): Variable = - derive(variable { cos(x.value) }) { z -> - x.d -= z.d * sin(x.value) - } + derive(variable { cos(x.value) }) { z -> x.d -= z.d * sin(x.value) } diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/misc/Grids.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/misc/Grids.kt index d3bf0891f..1272ddd1c 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/misc/Grids.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/misc/Grids.kt @@ -41,6 +41,6 @@ fun ClosedFloatingPointRange.toSequenceWithPoints(numPoints: Int): Seque */ @Deprecated("Replace by 'toSequenceWithPoints'") fun ClosedFloatingPointRange.toGrid(numPoints: Int): DoubleArray { - if (numPoints < 2) error("Can't create generic grid with less than two points") + require(numPoints >= 2) { "Can't create generic grid with less than two points" } return DoubleArray(numPoints) { i -> start + (endInclusive - start) / (numPoints - 1) * i } } diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/misc/cumulative.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/misc/cumulative.kt index a0f4525cc..dc7a4bf1f 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/misc/cumulative.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/misc/cumulative.kt @@ -2,6 +2,8 @@ package scientifik.kmath.misc import scientifik.kmath.operations.Space import scientifik.kmath.operations.invoke +import kotlin.contracts.ExperimentalContracts +import kotlin.contracts.contract import kotlin.jvm.JvmName /** @@ -11,67 +13,69 @@ import kotlin.jvm.JvmName * @param R the type of resulting iterable. * @param initial lazy evaluated. */ -fun Iterator.cumulative(initial: R, operation: (R, T) -> R): Iterator = object : Iterator { - var state: R = initial - override fun hasNext(): Boolean = this@cumulative.hasNext() +@OptIn(ExperimentalContracts::class) +inline fun Iterator.cumulative(initial: R, crossinline operation: (R, T) -> R): Iterator { + contract { callsInPlace(operation) } - override fun next(): R { - state = operation(state, this@cumulative.next()) - return state + return object : Iterator { + var state: R = initial + + override fun hasNext(): Boolean = this@cumulative.hasNext() + + override fun next(): R { + state = operation(state, this@cumulative.next()) + return state + } } } -fun Iterable.cumulative(initial: R, operation: (R, T) -> R): Iterable = object : Iterable { - override fun iterator(): Iterator = this@cumulative.iterator().cumulative(initial, operation) -} +inline fun Iterable.cumulative(initial: R, crossinline operation: (R, T) -> R): Iterable = + Iterable { this@cumulative.iterator().cumulative(initial, operation) } -fun Sequence.cumulative(initial: R, operation: (R, T) -> R): Sequence = object : Sequence { - override fun iterator(): Iterator = this@cumulative.iterator().cumulative(initial, operation) +inline fun Sequence.cumulative(initial: R, crossinline operation: (R, T) -> R): Sequence = Sequence { + this@cumulative.iterator().cumulative(initial, operation) } fun List.cumulative(initial: R, operation: (R, T) -> R): List = - this.iterator().cumulative(initial, operation).asSequence().toList() + iterator().cumulative(initial, operation).asSequence().toList() //Cumulative sum /** * Cumulative sum with custom space */ -fun Iterable.cumulativeSum(space: Space): Iterable = space { - cumulative(zero) { element: T, sum: T -> sum + element } -} +fun Iterable.cumulativeSum(space: Space): Iterable = + space { cumulative(zero) { element: T, sum: T -> sum + element } } @JvmName("cumulativeSumOfDouble") -fun Iterable.cumulativeSum(): Iterable = this.cumulative(0.0) { element, sum -> sum + element } +fun Iterable.cumulativeSum(): Iterable = cumulative(0.0) { element, sum -> sum + element } @JvmName("cumulativeSumOfInt") -fun Iterable.cumulativeSum(): Iterable = this.cumulative(0) { element, sum -> sum + element } +fun Iterable.cumulativeSum(): Iterable = cumulative(0) { element, sum -> sum + element } @JvmName("cumulativeSumOfLong") -fun Iterable.cumulativeSum(): Iterable = this.cumulative(0L) { element, sum -> sum + element } +fun Iterable.cumulativeSum(): Iterable = cumulative(0L) { element, sum -> sum + element } -fun Sequence.cumulativeSum(space: Space): Sequence = with(space) { - cumulative(zero) { element: T, sum: T -> sum + element } -} +fun Sequence.cumulativeSum(space: Space): Sequence = + space { cumulative(zero) { element: T, sum: T -> sum + element } } @JvmName("cumulativeSumOfDouble") -fun Sequence.cumulativeSum(): Sequence = this.cumulative(0.0) { element, sum -> sum + element } +fun Sequence.cumulativeSum(): Sequence = cumulative(0.0) { element, sum -> sum + element } @JvmName("cumulativeSumOfInt") -fun Sequence.cumulativeSum(): Sequence = this.cumulative(0) { element, sum -> sum + element } +fun Sequence.cumulativeSum(): Sequence = cumulative(0) { element, sum -> sum + element } @JvmName("cumulativeSumOfLong") -fun Sequence.cumulativeSum(): Sequence = this.cumulative(0L) { element, sum -> sum + element } +fun Sequence.cumulativeSum(): Sequence = cumulative(0L) { element, sum -> sum + element } -fun List.cumulativeSum(space: Space): List = with(space) { - cumulative(zero) { element: T, sum: T -> sum + element } -} +fun List.cumulativeSum(space: Space): List = + space { cumulative(zero) { element: T, sum: T -> sum + element } } @JvmName("cumulativeSumOfDouble") -fun List.cumulativeSum(): List = this.cumulative(0.0) { element, sum -> sum + element } +fun List.cumulativeSum(): List = cumulative(0.0) { element, sum -> sum + element } @JvmName("cumulativeSumOfInt") -fun List.cumulativeSum(): List = this.cumulative(0) { element, sum -> sum + element } +fun List.cumulativeSum(): List = cumulative(0) { element, sum -> sum + element } @JvmName("cumulativeSumOfLong") -fun List.cumulativeSum(): List = this.cumulative(0L) { element, sum -> sum + element } +fun List.cumulativeSum(): List = cumulative(0L) { element, sum -> sum + element } diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/BigInt.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/BigInt.kt index 07163750b..d0c4989d8 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/BigInt.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/BigInt.kt @@ -3,6 +3,8 @@ package scientifik.kmath.operations import scientifik.kmath.operations.BigInt.Companion.BASE import scientifik.kmath.operations.BigInt.Companion.BASE_SIZE import scientifik.kmath.structures.* +import kotlin.contracts.ExperimentalContracts +import kotlin.contracts.contract import kotlin.math.log2 import kotlin.math.max import kotlin.math.min @@ -431,8 +433,8 @@ fun ULong.toBigInt(): BigInt = BigInt( * Create a [BigInt] with this array of magnitudes with protective copy */ fun UIntArray.toBigInt(sign: Byte): BigInt { - if (sign == 0.toByte() && isNotEmpty()) error("") - return BigInt(sign, this.copyOf()) + require(sign != 0.toByte() || !isNotEmpty()) + return BigInt(sign, copyOf()) } val hexChToInt: MutableMap = hashMapOf( @@ -485,11 +487,17 @@ fun String.parseBigInteger(): BigInt? { return res * sign } -inline fun Buffer.Companion.bigInt(size: Int, initializer: (Int) -> BigInt): Buffer = - boxing(size, initializer) +@OptIn(ExperimentalContracts::class) +inline fun Buffer.Companion.bigInt(size: Int, initializer: (Int) -> BigInt): Buffer { + contract { callsInPlace(initializer) } + return boxing(size, initializer) +} -inline fun MutableBuffer.Companion.bigInt(size: Int, initializer: (Int) -> BigInt): MutableBuffer = - boxing(size, initializer) +@OptIn(ExperimentalContracts::class) +inline fun MutableBuffer.Companion.bigInt(size: Int, initializer: (Int) -> BigInt): MutableBuffer { + contract { callsInPlace(initializer) } + return boxing(size, initializer) +} fun NDAlgebra.Companion.bigInt(vararg shape: Int): BoxingNDRing = BoxingNDRing(shape, BigIntField, Buffer.Companion::bigInt) @@ -497,5 +505,4 @@ fun NDAlgebra.Companion.bigInt(vararg shape: Int): BoxingNDRing BigInt -): BufferedNDRingElement = - NDAlgebra.bigInt(*shape).produce(initializer) +): BufferedNDRingElement = NDAlgebra.bigInt(*shape).produce(initializer) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Complex.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Complex.kt index 76df0f45d..e80e6983a 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Complex.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Complex.kt @@ -6,6 +6,8 @@ import scientifik.kmath.structures.MutableBuffer import scientifik.memory.MemoryReader import scientifik.memory.MemorySpec import scientifik.memory.MemoryWriter +import kotlin.contracts.ExperimentalContracts +import kotlin.contracts.contract import kotlin.math.* /** @@ -196,10 +198,14 @@ data class Complex(val re: Double, val im: Double) : FieldElement Complex): Buffer { + contract { callsInPlace(init) } return MemoryBuffer.create(Complex, size, init) } +@OptIn(ExperimentalContracts::class) inline fun MutableBuffer.Companion.complex(size: Int, crossinline init: (Int) -> Complex): Buffer { + contract { callsInPlace(init) } return MemoryBuffer.create(Complex, size, init) } diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BoxingNDField.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BoxingNDField.kt index 4cbb565c1..be71645d1 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BoxingNDField.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BoxingNDField.kt @@ -8,19 +8,17 @@ class BoxingNDField>( override val elementContext: F, val bufferFactory: BufferFactory ) : BufferedNDField { - + override val zero: BufferedNDFieldElement by lazy { produce { zero } } + override val one: BufferedNDFieldElement by lazy { produce { one } } override val strides: Strides = DefaultStrides(shape) fun buildBuffer(size: Int, initializer: (Int) -> T): Buffer = bufferFactory(size, initializer) override fun check(vararg elements: NDBuffer) { - if (!elements.all { it.strides == this.strides }) error("Element strides are not the same as context strides") + check(elements.all { it.strides == strides }) { "Element strides are not the same as context strides" } } - override val zero: BufferedNDFieldElement by lazy { produce { zero } } - override val one: BufferedNDFieldElement by lazy { produce { one } } - override fun produce(initializer: F.(IntArray) -> T): BufferedNDFieldElement = BufferedNDFieldElement( this, @@ -28,6 +26,7 @@ class BoxingNDField>( override fun map(arg: NDBuffer, transform: F.(T) -> T): BufferedNDFieldElement { check(arg) + return BufferedNDFieldElement( this, buildBuffer(arg.strides.linearSize) { offset -> elementContext.transform(arg.buffer[offset]) }) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BoxingNDRing.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BoxingNDRing.kt index f7be95736..91b945e79 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BoxingNDRing.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BoxingNDRing.kt @@ -8,19 +8,16 @@ class BoxingNDRing>( override val elementContext: R, val bufferFactory: BufferFactory ) : BufferedNDRing { - override val strides: Strides = DefaultStrides(shape) - - fun buildBuffer(size: Int, initializer: (Int) -> T): Buffer = - bufferFactory(size, initializer) - - override fun check(vararg elements: NDBuffer) { - if (!elements.all { it.strides == this.strides }) error("Element strides are not the same as context strides") - } - override val zero: BufferedNDRingElement by lazy { produce { zero } } override val one: BufferedNDRingElement by lazy { produce { one } } + fun buildBuffer(size: Int, initializer: (Int) -> T): Buffer = bufferFactory(size, initializer) + + override fun check(vararg elements: NDBuffer) { + require(elements.all { it.strides == strides }) { "Element strides are not the same as context strides" } + } + override fun produce(initializer: R.(IntArray) -> T): BufferedNDRingElement = BufferedNDRingElement( this, diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BufferAccessor2D.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BufferAccessor2D.kt index 00832b69c..2c3d69094 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BufferAccessor2D.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BufferAccessor2D.kt @@ -6,7 +6,6 @@ import kotlin.reflect.KClass * A context that allows to operate on a [MutableBuffer] as on 2d array */ class BufferAccessor2D(val type: KClass, val rowNum: Int, val colNum: Int) { - operator fun Buffer.get(i: Int, j: Int): T = get(i + colNum * j) operator fun MutableBuffer.set(i: Int, j: Int, value: T) { @@ -26,15 +25,14 @@ class BufferAccessor2D(val type: KClass, val rowNum: Int, val colNum inner class Row(val buffer: MutableBuffer, val rowIndex: Int) : MutableBuffer { override val size: Int get() = colNum - override fun get(index: Int): T = buffer[rowIndex, index] + override operator fun get(index: Int): T = buffer[rowIndex, index] - override fun set(index: Int, value: T) { + override operator fun set(index: Int, value: T) { buffer[rowIndex, index] = value } override fun copy(): MutableBuffer = MutableBuffer.auto(type, colNum) { get(it) } - - override fun iterator(): Iterator = (0 until colNum).map(::get).iterator() + override operator fun iterator(): Iterator = (0 until colNum).map(::get).iterator() } diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BufferedNDAlgebra.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BufferedNDAlgebra.kt index 06922c56f..2c0c2021f 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BufferedNDAlgebra.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BufferedNDAlgebra.kt @@ -5,9 +5,8 @@ import scientifik.kmath.operations.* interface BufferedNDAlgebra : NDAlgebra> { val strides: Strides - override fun check(vararg elements: NDBuffer) { - if (!elements.all { it.strides == this.strides }) error("Strides mismatch") - } + override fun check(vararg elements: NDBuffer): Unit = + require(elements.all { it.strides == strides }) { ("Strides mismatch") } /** * Convert any [NDStructure] to buffered structure using strides from this context. diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BufferedNDElement.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BufferedNDElement.kt index d1d622b23..20e34fadd 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BufferedNDElement.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BufferedNDElement.kt @@ -30,7 +30,6 @@ class BufferedNDRingElement>( override val context: BufferedNDRing, override val buffer: Buffer ) : BufferedNDElement(), RingElement, BufferedNDRingElement, BufferedNDRing> { - override fun unwrap(): NDBuffer = this override fun NDBuffer.wrap(): BufferedNDRingElement { @@ -43,7 +42,6 @@ class BufferedNDFieldElement>( override val context: BufferedNDField, override val buffer: Buffer ) : BufferedNDElement(), FieldElement, BufferedNDFieldElement, BufferedNDField> { - override fun unwrap(): NDBuffer = this override fun NDBuffer.wrap(): BufferedNDFieldElement { diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/Buffers.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/Buffers.kt index 5fdf79e88..e065f4990 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/Buffers.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/Buffers.kt @@ -2,6 +2,8 @@ package scientifik.kmath.structures import scientifik.kmath.operations.Complex import scientifik.kmath.operations.complex +import kotlin.contracts.ExperimentalContracts +import kotlin.contracts.contract import kotlin.reflect.KClass /** @@ -117,15 +119,14 @@ interface MutableBuffer : Buffer { MutableListBuffer(MutableList(size, initializer)) @Suppress("UNCHECKED_CAST") - inline fun auto(type: KClass, size: Int, initializer: (Int) -> T): MutableBuffer { - return when (type) { + inline fun auto(type: KClass, size: Int, initializer: (Int) -> T): MutableBuffer = + when (type) { Double::class -> RealBuffer(DoubleArray(size) { initializer(it) as Double }) as MutableBuffer Short::class -> ShortBuffer(ShortArray(size) { initializer(it) as Short }) as MutableBuffer Int::class -> IntBuffer(IntArray(size) { initializer(it) as Int }) as MutableBuffer Long::class -> LongBuffer(LongArray(size) { initializer(it) as Long }) as MutableBuffer else -> boxing(size, initializer) } - } /** * Create most appropriate mutable buffer for given type avoiding boxing wherever possible @@ -150,9 +151,8 @@ inline class ListBuffer(val list: List) : Buffer { override val size: Int get() = list.size - override fun get(index: Int): T = list[index] - - override fun iterator(): Iterator = list.iterator() + override operator fun get(index: Int): T = list[index] + override operator fun iterator(): Iterator = list.iterator() } /** @@ -167,7 +167,11 @@ fun List.asBuffer(): ListBuffer = ListBuffer(this) * The function [init] is called for each array element sequentially starting from the first one. * It should return the value for an array element given its index. */ -inline fun ListBuffer(size: Int, init: (Int) -> T): ListBuffer = List(size, init).asBuffer() +@OptIn(ExperimentalContracts::class) +inline fun ListBuffer(size: Int, init: (Int) -> T): ListBuffer { + contract { callsInPlace(init) } + return List(size, init).asBuffer() +} /** * [MutableBuffer] implementation over [MutableList]. @@ -176,17 +180,16 @@ inline fun ListBuffer(size: Int, init: (Int) -> T): ListBuffer = List(siz * @property list The underlying list. */ inline class MutableListBuffer(val list: MutableList) : MutableBuffer { - override val size: Int get() = list.size - override fun get(index: Int): T = list[index] + override operator fun get(index: Int): T = list[index] - override fun set(index: Int, value: T) { + override operator fun set(index: Int, value: T) { list[index] = value } - override fun iterator(): Iterator = list.iterator() + override operator fun iterator(): Iterator = list.iterator() override fun copy(): MutableBuffer = MutableListBuffer(ArrayList(list)) } @@ -201,14 +204,13 @@ class ArrayBuffer(private val array: Array) : MutableBuffer { override val size: Int get() = array.size - override fun get(index: Int): T = array[index] + override operator fun get(index: Int): T = array[index] - override fun set(index: Int, value: T) { + override operator fun set(index: Int, value: T) { array[index] = value } - override fun iterator(): Iterator = array.iterator() - + override operator fun iterator(): Iterator = array.iterator() override fun copy(): MutableBuffer = ArrayBuffer(array.copyOf()) } @@ -226,9 +228,9 @@ fun Array.asBuffer(): ArrayBuffer = ArrayBuffer(this) inline class ReadOnlyBuffer(val buffer: MutableBuffer) : Buffer { override val size: Int get() = buffer.size - override fun get(index: Int): T = buffer[index] + override operator fun get(index: Int): T = buffer[index] - override fun iterator(): Iterator = buffer.iterator() + override operator fun iterator(): Iterator = buffer.iterator() } /** @@ -238,12 +240,12 @@ inline class ReadOnlyBuffer(val buffer: MutableBuffer) : Buffer { * @param T the type of elements provided by the buffer. */ class VirtualBuffer(override val size: Int, private val generator: (Int) -> T) : Buffer { - override fun get(index: Int): T { + override operator fun get(index: Int): T { if (index < 0 || index >= size) throw IndexOutOfBoundsException("Expected index from 0 to ${size - 1}, but found $index") return generator(index) } - override fun iterator(): Iterator = (0 until size).asSequence().map(generator).iterator() + override operator fun iterator(): Iterator = (0 until size).asSequence().map(generator).iterator() override fun contentEquals(other: Buffer<*>): Boolean { return if (other is VirtualBuffer) { diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ComplexNDField.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ComplexNDField.kt index 370d8ff4d..a4855ca7c 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ComplexNDField.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ComplexNDField.kt @@ -4,6 +4,9 @@ import scientifik.kmath.operations.Complex import scientifik.kmath.operations.ComplexField import scientifik.kmath.operations.FieldElement import scientifik.kmath.operations.complex +import kotlin.contracts.ExperimentalContracts +import kotlin.contracts.InvocationKind +import kotlin.contracts.contract typealias ComplexNDElement = BufferedNDFieldElement @@ -109,7 +112,9 @@ inline fun ComplexNDElement.mapIndexed(crossinline transform: ComplexField.(inde /** * Map one [ComplexNDElement] using function without indices. */ +@OptIn(ExperimentalContracts::class) inline fun ComplexNDElement.map(crossinline transform: ComplexField.(Complex) -> Complex): ComplexNDElement { + contract { callsInPlace(transform) } val buffer = Buffer.complex(strides.linearSize) { offset -> ComplexField.transform(buffer[offset]) } return BufferedNDFieldElement(context, buffer) } @@ -148,6 +153,8 @@ fun NDElement.Companion.complex(vararg shape: Int, initializer: ComplexField.(In /** * Produce a context for n-dimensional operations inside this real field */ +@OptIn(ExperimentalContracts::class) inline fun ComplexField.nd(vararg shape: Int, action: ComplexNDField.() -> R): R { - return NDField.complex(*shape).run(action) + contract { callsInPlace(action, InvocationKind.EXACTLY_ONCE) } + return NDField.complex(*shape).action() } diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/FlaggedBuffer.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/FlaggedBuffer.kt index a2d0a71b3..b78ac0beb 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/FlaggedBuffer.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/FlaggedBuffer.kt @@ -1,5 +1,7 @@ package scientifik.kmath.structures +import kotlin.contracts.ExperimentalContracts +import kotlin.contracts.contract import kotlin.experimental.and /** @@ -57,17 +59,19 @@ class FlaggedRealBuffer(val values: DoubleArray, val flags: ByteArray) : Flagged override val size: Int get() = values.size - override fun get(index: Int): Double? = if (isValid(index)) values[index] else null + override operator fun get(index: Int): Double? = if (isValid(index)) values[index] else null - override fun iterator(): Iterator = values.indices.asSequence().map { + override operator fun iterator(): Iterator = values.indices.asSequence().map { if (isValid(it)) values[it] else null }.iterator() } +@OptIn(ExperimentalContracts::class) inline fun FlaggedRealBuffer.forEachValid(block: (Double) -> Unit) { - for (i in indices) { - if (isValid(i)) { - block(values[i]) - } - } + contract { callsInPlace(block) } + + indices + .asSequence() + .filter(::isValid) + .forEach { block(values[it]) } } diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/FloatBuffer.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/FloatBuffer.kt index e42df8c14..24822056b 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/FloatBuffer.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/FloatBuffer.kt @@ -1,5 +1,8 @@ package scientifik.kmath.structures +import kotlin.contracts.ExperimentalContracts +import kotlin.contracts.contract + /** * Specialized [MutableBuffer] implementation over [FloatArray]. * @@ -8,13 +11,13 @@ package scientifik.kmath.structures inline class FloatBuffer(val array: FloatArray) : MutableBuffer { override val size: Int get() = array.size - override fun get(index: Int): Float = array[index] + override operator fun get(index: Int): Float = array[index] - override fun set(index: Int, value: Float) { + override operator fun set(index: Int, value: Float) { array[index] = value } - override fun iterator(): FloatIterator = array.iterator() + override operator fun iterator(): FloatIterator = array.iterator() override fun copy(): MutableBuffer = FloatBuffer(array.copyOf()) @@ -27,7 +30,11 @@ inline class FloatBuffer(val array: FloatArray) : MutableBuffer { * The function [init] is called for each array element sequentially starting from the first one. * It should return the value for an buffer element given its index. */ -inline fun FloatBuffer(size: Int, init: (Int) -> Float): FloatBuffer = FloatBuffer(FloatArray(size) { init(it) }) +@OptIn(ExperimentalContracts::class) +inline fun FloatBuffer(size: Int, init: (Int) -> Float): FloatBuffer { + contract { callsInPlace(init) } + return FloatBuffer(FloatArray(size) { init(it) }) +} /** * Returns a new [FloatBuffer] of given elements. diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/IntBuffer.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/IntBuffer.kt index a3f0f3c3e..229b62b48 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/IntBuffer.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/IntBuffer.kt @@ -1,5 +1,9 @@ package scientifik.kmath.structures +import kotlin.contracts.ExperimentalContracts +import kotlin.contracts.InvocationKind +import kotlin.contracts.contract + /** * Specialized [MutableBuffer] implementation over [IntArray]. * @@ -8,17 +12,16 @@ package scientifik.kmath.structures inline class IntBuffer(val array: IntArray) : MutableBuffer { override val size: Int get() = array.size - override fun get(index: Int): Int = array[index] + override operator fun get(index: Int): Int = array[index] - override fun set(index: Int, value: Int) { + override operator fun set(index: Int, value: Int) { array[index] = value } - override fun iterator(): IntIterator = array.iterator() + override operator fun iterator(): IntIterator = array.iterator() override fun copy(): MutableBuffer = IntBuffer(array.copyOf()) - } /** @@ -28,7 +31,11 @@ inline class IntBuffer(val array: IntArray) : MutableBuffer { * The function [init] is called for each array element sequentially starting from the first one. * It should return the value for an buffer element given its index. */ -inline fun IntBuffer(size: Int, init: (Int) -> Int): IntBuffer = IntBuffer(IntArray(size) { init(it) }) +@OptIn(ExperimentalContracts::class) +inline fun IntBuffer(size: Int, init: (Int) -> Int): IntBuffer { + contract { callsInPlace(init) } + return IntBuffer(IntArray(size) { init(it) }) +} /** * Returns a new [IntBuffer] of given elements. diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/LongBuffer.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/LongBuffer.kt index 912656c68..cfedb5f35 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/LongBuffer.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/LongBuffer.kt @@ -1,5 +1,8 @@ package scientifik.kmath.structures +import kotlin.contracts.ExperimentalContracts +import kotlin.contracts.contract + /** * Specialized [MutableBuffer] implementation over [LongArray]. * @@ -8,13 +11,13 @@ package scientifik.kmath.structures inline class LongBuffer(val array: LongArray) : MutableBuffer { override val size: Int get() = array.size - override fun get(index: Int): Long = array[index] + override operator fun get(index: Int): Long = array[index] - override fun set(index: Int, value: Long) { + override operator fun set(index: Int, value: Long) { array[index] = value } - override fun iterator(): LongIterator = array.iterator() + override operator fun iterator(): LongIterator = array.iterator() override fun copy(): MutableBuffer = LongBuffer(array.copyOf()) @@ -28,7 +31,11 @@ inline class LongBuffer(val array: LongArray) : MutableBuffer { * The function [init] is called for each array element sequentially starting from the first one. * It should return the value for an buffer element given its index. */ -inline fun LongBuffer(size: Int, init: (Int) -> Long): LongBuffer = LongBuffer(LongArray(size) { init(it) }) +@OptIn(ExperimentalContracts::class) +inline fun LongBuffer(size: Int, init: (Int) -> Long): LongBuffer { + contract { callsInPlace(init) } + return LongBuffer(LongArray(size) { init(it) }) +} /** * Returns a new [LongBuffer] of given elements. diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/MemoryBuffer.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/MemoryBuffer.kt index 1d0c87580..83c50b14b 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/MemoryBuffer.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/MemoryBuffer.kt @@ -14,10 +14,8 @@ open class MemoryBuffer(protected val memory: Memory, protected val spe private val reader: MemoryReader = memory.reader() - override fun get(index: Int): T = reader.read(spec, spec.objectSize * index) - - override fun iterator(): Iterator = (0 until size).asSequence().map { get(it) }.iterator() - + override operator fun get(index: Int): T = reader.read(spec, spec.objectSize * index) + override operator fun iterator(): Iterator = (0 until size).asSequence().map { get(it) }.iterator() companion object { fun create(spec: MemorySpec, size: Int): MemoryBuffer = @@ -48,8 +46,7 @@ class MutableMemoryBuffer(memory: Memory, spec: MemorySpec) : Memory private val writer: MemoryWriter = memory.writer() - override fun set(index: Int, value: T): Unit = writer.write(spec, spec.objectSize * index, value) - + override operator fun set(index: Int, value: T): Unit = writer.write(spec, spec.objectSize * index, value) override fun copy(): MutableBuffer = MutableMemoryBuffer(memory.copy(), spec) companion object { diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/NDElement.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/NDElement.kt index 9dfe2b5a8..6cc0a72c0 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/NDElement.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/NDElement.kt @@ -26,19 +26,20 @@ interface NDElement> : NDStructure { fun real(shape: IntArray, initializer: RealField.(IntArray) -> Double = { 0.0 }): RealNDElement = NDField.real(*shape).produce(initializer) - - fun real1D(dim: Int, initializer: (Int) -> Double = { _ -> 0.0 }): RealNDElement = + inline fun real1D(dim: Int, crossinline initializer: (Int) -> Double = { _ -> 0.0 }): RealNDElement = real(intArrayOf(dim)) { initializer(it[0]) } + inline fun real2D( + dim1: Int, + dim2: Int, + crossinline initializer: (Int, Int) -> Double = { _, _ -> 0.0 } + ): RealNDElement = real(intArrayOf(dim1, dim2)) { initializer(it[0], it[1]) } - fun real2D(dim1: Int, dim2: Int, initializer: (Int, Int) -> Double = { _, _ -> 0.0 }): RealNDElement = - real(intArrayOf(dim1, dim2)) { initializer(it[0], it[1]) } - - fun real3D( + inline fun real3D( dim1: Int, dim2: Int, dim3: Int, - initializer: (Int, Int, Int) -> Double = { _, _, _ -> 0.0 } + crossinline initializer: (Int, Int, Int) -> Double = { _, _, _ -> 0.0 } ): RealNDElement = real(intArrayOf(dim1, dim2, dim3)) { initializer(it[0], it[1], it[2]) } @@ -72,7 +73,6 @@ fun > NDElement.mapIndexed(transform: C.(index fun > NDElement.map(transform: C.(T) -> T): NDElement = context.map(unwrap(), transform).wrap() - /** * Element by element application of any operation on elements to the whole [NDElement] */ @@ -107,7 +107,6 @@ operator fun , N : NDStructure> NDElement.times(arg: operator fun , N : NDStructure> NDElement.div(arg: T): NDElement = map { value -> arg / value } - // /** // * Reverse sum operation // */ diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/NDStructure.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/NDStructure.kt index 9d7735053..a16caab68 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/NDStructure.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/NDStructure.kt @@ -1,5 +1,7 @@ package scientifik.kmath.structures +import kotlin.contracts.ExperimentalContracts +import kotlin.contracts.contract import kotlin.jvm.JvmName import kotlin.reflect.KClass @@ -138,10 +140,10 @@ interface MutableNDStructure : NDStructure { operator fun set(index: IntArray, value: T) } +@OptIn(ExperimentalContracts::class) inline fun MutableNDStructure.mapInPlace(action: (IntArray, T) -> T) { - elements().forEach { (index, oldValue) -> - this[index] = action(index, oldValue) - } + contract { callsInPlace(action) } + elements().forEach { (index, oldValue) -> this[index] = action(index, oldValue) } } /** @@ -200,14 +202,12 @@ class DefaultStrides private constructor(override val shape: IntArray) : Strides }.toList() } - override fun offset(index: IntArray): Int { - return index.mapIndexed { i, value -> - if (value < 0 || value >= this.shape[i]) { - throw RuntimeException("Index $value out of shape bounds: (0,${this.shape[i]})") - } - value * strides[i] - }.sum() - } + override fun offset(index: IntArray): Int = index.mapIndexed { i, value -> + if (value < 0 || value >= this.shape[i]) + throw IndexOutOfBoundsException("Index $value out of shape bounds: (0,${this.shape[i]})") + + value * strides[i] + }.sum() override fun index(offset: Int): IntArray { val res = IntArray(shape.size) @@ -259,7 +259,7 @@ abstract class NDBuffer : NDStructure { */ abstract val strides: Strides - override fun get(index: IntArray): T = buffer[strides.offset(index)] + override operator fun get(index: IntArray): T = buffer[strides.offset(index)] override val shape: IntArray get() = strides.shape @@ -319,13 +319,13 @@ class MutableBufferNDStructure( } } - override fun set(index: IntArray, value: T): Unit = buffer.set(strides.offset(index), value) + override operator fun set(index: IntArray, value: T): Unit = buffer.set(strides.offset(index), value) } inline fun NDStructure.combine( struct: NDStructure, crossinline block: (T, T) -> T ): NDStructure { - if (!this.shape.contentEquals(struct.shape)) error("Shape mismatch in structure combination") + require(shape.contentEquals(struct.shape)) { "Shape mismatch in structure combination" } return NDStructure.auto(shape) { block(this[it], struct[it]) } } diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/RealBuffer.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/RealBuffer.kt index e999e12b2..f897134db 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/RealBuffer.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/RealBuffer.kt @@ -1,5 +1,8 @@ package scientifik.kmath.structures +import kotlin.contracts.ExperimentalContracts +import kotlin.contracts.contract + /** * Specialized [MutableBuffer] implementation over [DoubleArray]. * @@ -8,13 +11,13 @@ package scientifik.kmath.structures inline class RealBuffer(val array: DoubleArray) : MutableBuffer { override val size: Int get() = array.size - override fun get(index: Int): Double = array[index] + override operator fun get(index: Int): Double = array[index] - override fun set(index: Int, value: Double) { + override operator fun set(index: Int, value: Double) { array[index] = value } - override fun iterator(): DoubleIterator = array.iterator() + override operator fun iterator(): DoubleIterator = array.iterator() override fun copy(): MutableBuffer = RealBuffer(array.copyOf()) @@ -27,7 +30,11 @@ inline class RealBuffer(val array: DoubleArray) : MutableBuffer { * The function [init] is called for each array element sequentially starting from the first one. * It should return the value for an buffer element given its index. */ -inline fun RealBuffer(size: Int, init: (Int) -> Double): RealBuffer = RealBuffer(DoubleArray(size) { init(it) }) +@OptIn(ExperimentalContracts::class) +inline fun RealBuffer(size: Int, init: (Int) -> Double): RealBuffer { + contract { callsInPlace(init) } + return RealBuffer(DoubleArray(size) { init(it) }) +} /** * Returns a new [RealBuffer] of given elements. diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ShortBuffer.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ShortBuffer.kt index c6f19feaf..08d4a4376 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ShortBuffer.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ShortBuffer.kt @@ -1,5 +1,8 @@ package scientifik.kmath.structures +import kotlin.contracts.ExperimentalContracts +import kotlin.contracts.contract + /** * Specialized [MutableBuffer] implementation over [ShortArray]. * @@ -8,17 +11,16 @@ package scientifik.kmath.structures inline class ShortBuffer(val array: ShortArray) : MutableBuffer { override val size: Int get() = array.size - override fun get(index: Int): Short = array[index] + override operator fun get(index: Int): Short = array[index] - override fun set(index: Int, value: Short) { + override operator fun set(index: Int, value: Short) { array[index] = value } - override fun iterator(): ShortIterator = array.iterator() + override operator fun iterator(): ShortIterator = array.iterator() override fun copy(): MutableBuffer = ShortBuffer(array.copyOf()) - } /** @@ -28,7 +30,11 @@ inline class ShortBuffer(val array: ShortArray) : MutableBuffer { * The function [init] is called for each array element sequentially starting from the first one. * It should return the value for an buffer element given its index. */ -inline fun ShortBuffer(size: Int, init: (Int) -> Short): ShortBuffer = ShortBuffer(ShortArray(size) { init(it) }) +@OptIn(ExperimentalContracts::class) +inline fun ShortBuffer(size: Int, init: (Int) -> Short): ShortBuffer { + contract { callsInPlace(init) } + return ShortBuffer(ShortArray(size) { init(it) }) +} /** * Returns a new [ShortBuffer] of given elements. diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/Structure1D.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/Structure1D.kt index faf022367..a796c2037 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/Structure1D.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/Structure1D.kt @@ -6,12 +6,12 @@ package scientifik.kmath.structures interface Structure1D : NDStructure, Buffer { override val dimension: Int get() = 1 - override fun get(index: IntArray): T { - if (index.size != 1) error("Index dimension mismatch. Expected 1 but found ${index.size}") + override operator fun get(index: IntArray): T { + require(index.size == 1) { "Index dimension mismatch. Expected 1 but found ${index.size}" } return get(index[0]) } - override fun iterator(): Iterator = (0 until size).asSequence().map { get(it) }.iterator() + override operator fun iterator(): Iterator = (0 until size).asSequence().map { get(it) }.iterator() } /** @@ -22,7 +22,7 @@ private inline class Structure1DWrapper(val structure: NDStructure) : Stru override val shape: IntArray get() = structure.shape override val size: Int get() = structure.shape[0] - override fun get(index: Int): T = structure[index] + override operator fun get(index: Int): T = structure[index] override fun elements(): Sequence> = structure.elements() } @@ -39,7 +39,7 @@ private inline class Buffer1DWrapper(val buffer: Buffer) : Structure1D override fun elements(): Sequence> = asSequence().mapIndexed { index, value -> intArrayOf(index) to value } - override fun get(index: Int): T = buffer[index] + override operator fun get(index: Int): T = buffer[index] } /** diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/Structure2D.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/Structure2D.kt index 30fd556d3..eeb6bd3dc 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/Structure2D.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/Structure2D.kt @@ -9,8 +9,8 @@ interface Structure2D : NDStructure { operator fun get(i: Int, j: Int): T - override fun get(index: IntArray): T { - if (index.size != 2) error("Index dimension mismatch. Expected 2 but found ${index.size}") + override operator fun get(index: IntArray): T { + require(index.size == 2) { "Index dimension mismatch. Expected 2 but found ${index.size}" } return get(index[0], index[1]) } @@ -39,10 +39,10 @@ interface Structure2D : NDStructure { * A 2D wrapper for nd-structure */ private inline class Structure2DWrapper(val structure: NDStructure) : Structure2D { - override fun get(i: Int, j: Int): T = structure[i, j] - override val shape: IntArray get() = structure.shape + override operator fun get(i: Int, j: Int): T = structure[i, j] + override fun elements(): Sequence> = structure.elements() } diff --git a/kmath-core/src/commonTest/kotlin/scientifik/kmath/expressions/ExpressionFieldTest.kt b/kmath-core/src/commonTest/kotlin/scientifik/kmath/expressions/ExpressionFieldTest.kt index 22b924ef9..485de08b4 100644 --- a/kmath-core/src/commonTest/kotlin/scientifik/kmath/expressions/ExpressionFieldTest.kt +++ b/kmath-core/src/commonTest/kotlin/scientifik/kmath/expressions/ExpressionFieldTest.kt @@ -3,6 +3,7 @@ package scientifik.kmath.expressions import scientifik.kmath.operations.Complex import scientifik.kmath.operations.ComplexField import scientifik.kmath.operations.RealField +import scientifik.kmath.operations.invoke import kotlin.test.Test import kotlin.test.assertEquals @@ -10,10 +11,12 @@ class ExpressionFieldTest { @Test fun testExpression() { val context = FunctionalExpressionField(RealField) - val expression = with(context) { + + val expression = context { val x = variable("x", 2.0) x * x + 2 * x + one } + assertEquals(expression("x" to 1.0), 4.0) assertEquals(expression(), 9.0) } @@ -21,10 +24,12 @@ class ExpressionFieldTest { @Test fun testComplex() { val context = FunctionalExpressionField(ComplexField) - val expression = with(context) { + + val expression = context { val x = variable("x", Complex(2.0, 0.0)) x * x + 2 * x + one } + assertEquals(expression("x" to Complex(1.0, 0.0)), Complex(4.0, 0.0)) assertEquals(expression(), Complex(9.0, 0.0)) } diff --git a/kmath-core/src/commonTest/kotlin/scientifik/kmath/linear/MatrixTest.kt b/kmath-core/src/commonTest/kotlin/scientifik/kmath/linear/MatrixTest.kt index 987426250..52a2f80a6 100644 --- a/kmath-core/src/commonTest/kotlin/scientifik/kmath/linear/MatrixTest.kt +++ b/kmath-core/src/commonTest/kotlin/scientifik/kmath/linear/MatrixTest.kt @@ -7,7 +7,6 @@ import kotlin.test.Test import kotlin.test.assertEquals class MatrixTest { - @Test fun testTranspose() { val matrix = MatrixContext.real.one(3, 3) @@ -51,6 +50,7 @@ class MatrixTest { fun test2DDot() { val firstMatrix = NDStructure.auto(2, 3) { (i, j) -> (i + j).toDouble() }.as2D() val secondMatrix = NDStructure.auto(3, 2) { (i, j) -> (i + j).toDouble() }.as2D() + MatrixContext.real.run { // val firstMatrix = produce(2, 3) { i, j -> (i + j).toDouble() } // val secondMatrix = produce(3, 2) { i, j -> (i + j).toDouble() } diff --git a/kmath-core/src/commonTest/kotlin/scientifik/kmath/structures/NumberNDFieldTest.kt b/kmath-core/src/commonTest/kotlin/scientifik/kmath/structures/NumberNDFieldTest.kt index d48aabfd0..b7e2594ec 100644 --- a/kmath-core/src/commonTest/kotlin/scientifik/kmath/structures/NumberNDFieldTest.kt +++ b/kmath-core/src/commonTest/kotlin/scientifik/kmath/structures/NumberNDFieldTest.kt @@ -1,6 +1,7 @@ package scientifik.kmath.structures import scientifik.kmath.operations.Norm +import scientifik.kmath.operations.invoke import scientifik.kmath.structures.NDElement.Companion.real2D import kotlin.math.abs import kotlin.math.pow @@ -56,17 +57,12 @@ class NumberNDFieldTest { } object L2Norm : Norm, Double> { - override fun norm(arg: NDStructure): Double { - return kotlin.math.sqrt(arg.elements().sumByDouble { it.second.toDouble() }) - } + override fun norm(arg: NDStructure): Double = + kotlin.math.sqrt(arg.elements().sumByDouble { it.second.toDouble() }) } @Test fun testInternalContext() { - NDField.real(*array1.shape).run { - with(L2Norm) { - 1 + norm(array1) + exp(array2) - } - } + (NDField.real(*array1.shape)) { with(L2Norm) { 1 + norm(array1) + exp(array2) } } } } diff --git a/kmath-core/src/jvmMain/kotlin/scientifik/kmath/operations/BigNumbers.kt b/kmath-core/src/jvmMain/kotlin/scientifik/kmath/operations/BigNumbers.kt index 06f2b31ad..f10ef24da 100644 --- a/kmath-core/src/jvmMain/kotlin/scientifik/kmath/operations/BigNumbers.kt +++ b/kmath-core/src/jvmMain/kotlin/scientifik/kmath/operations/BigNumbers.kt @@ -17,10 +17,10 @@ object JBigIntegerField : Field { override fun number(value: Number): BigInteger = BigInteger.valueOf(value.toLong()) override fun divide(a: BigInteger, b: BigInteger): BigInteger = a.div(b) override fun add(a: BigInteger, b: BigInteger): BigInteger = a.add(b) - override fun BigInteger.minus(b: BigInteger): BigInteger = this.subtract(b) + override operator fun BigInteger.minus(b: BigInteger): BigInteger = subtract(b) override fun multiply(a: BigInteger, k: Number): BigInteger = a.multiply(k.toInt().toBigInteger()) override fun multiply(a: BigInteger, b: BigInteger): BigInteger = a.multiply(b) - override fun BigInteger.unaryMinus(): BigInteger = negate() + override operator fun BigInteger.unaryMinus(): BigInteger = negate() } /** @@ -38,7 +38,7 @@ abstract class JBigDecimalFieldBase internal constructor(val mathContext: MathCo get() = BigDecimal.ONE override fun add(a: BigDecimal, b: BigDecimal): BigDecimal = a.add(b) - override fun BigDecimal.minus(b: BigDecimal): BigDecimal = subtract(b) + override operator fun BigDecimal.minus(b: BigDecimal): BigDecimal = subtract(b) override fun number(value: Number): BigDecimal = BigDecimal.valueOf(value.toDouble()) override fun multiply(a: BigDecimal, k: Number): BigDecimal = @@ -48,8 +48,7 @@ abstract class JBigDecimalFieldBase internal constructor(val mathContext: MathCo override fun divide(a: BigDecimal, b: BigDecimal): BigDecimal = a.divide(b, mathContext) override fun power(arg: BigDecimal, pow: Number): BigDecimal = arg.pow(pow.toInt(), mathContext) override fun sqrt(arg: BigDecimal): BigDecimal = arg.sqrt(mathContext) - override fun BigDecimal.unaryMinus(): BigDecimal = negate(mathContext) - + override operator fun BigDecimal.unaryMinus(): BigDecimal = negate(mathContext) } /** diff --git a/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/Chain.kt b/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/Chain.kt index 6cc9770af..aac4f2534 100644 --- a/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/Chain.kt +++ b/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/Chain.kt @@ -139,9 +139,10 @@ fun Chain.map(func: suspend (T) -> R): Chain = object : Chain { fun Chain.filter(block: (T) -> Boolean): Chain = object : Chain { override suspend fun next(): T { var next: T - do { - next = this@filter.next() - } while (!block(next)) + + do next = this@filter.next() + while (!block(next)) + return next } @@ -159,6 +160,7 @@ fun Chain.collect(mapper: suspend (Chain) -> R): Chain = object fun Chain.collectWithState(state: S, stateFork: (S) -> S, mapper: suspend S.(Chain) -> R): Chain = object : Chain { override suspend fun next(): R = state.mapper(this@collectWithState) + override fun fork(): Chain = this@collectWithState.fork().collectWithState(stateFork(state), stateFork, mapper) } @@ -168,6 +170,5 @@ fun Chain.collectWithState(state: S, stateFork: (S) -> S, mapper: s */ fun Chain.zip(other: Chain, block: suspend (T, U) -> R): Chain = object : Chain { override suspend fun next(): R = block(this@zip.next(), other.next()) - override fun fork(): Chain = this@zip.fork().zip(other.fork(), block) } diff --git a/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/flowExtra.kt b/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/flowExtra.kt index e8537304c..6624722ce 100644 --- a/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/flowExtra.kt +++ b/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/flowExtra.kt @@ -7,15 +7,16 @@ import kotlinx.coroutines.flow.scan import kotlinx.coroutines.flow.scanReduce import scientifik.kmath.operations.Space import scientifik.kmath.operations.SpaceOperations +import scientifik.kmath.operations.invoke @ExperimentalCoroutinesApi -fun Flow.cumulativeSum(space: SpaceOperations): Flow = with(space) { +fun Flow.cumulativeSum(space: SpaceOperations): Flow = space { scanReduce { sum: T, element: T -> sum + element } } @ExperimentalCoroutinesApi -fun Flow.mean(space: Space): Flow = with(space) { +fun Flow.mean(space: Space): Flow = space { class Accumulator(var sum: T, var num: Int) scan(Accumulator(zero, 0)) { sum, element -> diff --git a/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/coroutines/coroutinesExtra.kt b/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/coroutines/coroutinesExtra.kt index 7e00b30a1..8ee6c52ad 100644 --- a/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/coroutines/coroutinesExtra.kt +++ b/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/coroutines/coroutinesExtra.kt @@ -3,6 +3,8 @@ package scientifik.kmath.coroutines import kotlinx.coroutines.* import kotlinx.coroutines.channels.produce import kotlinx.coroutines.flow.* +import kotlin.contracts.ExperimentalContracts +import kotlin.contracts.contract val Dispatchers.Math: CoroutineDispatcher get() = Default @@ -81,21 +83,24 @@ suspend fun AsyncFlow.collect(concurrency: Int, collector: FlowCollector< } } +@OptIn(ExperimentalContracts::class) @ExperimentalCoroutinesApi @FlowPreview -suspend fun AsyncFlow.collect(concurrency: Int, action: suspend (value: T) -> Unit) { +suspend inline fun AsyncFlow.collect(concurrency: Int, crossinline action: suspend (value: T) -> Unit) { + contract { callsInPlace(action) } + collect(concurrency, object : FlowCollector { override suspend fun emit(value: T): Unit = action(value) }) } +@OptIn(ExperimentalContracts::class) @ExperimentalCoroutinesApi @FlowPreview -fun Flow.mapParallel( +inline fun Flow.mapParallel( dispatcher: CoroutineDispatcher = Dispatchers.Default, - transform: suspend (T) -> R + crossinline transform: suspend (T) -> R ): Flow { - return flatMapMerge { value -> - flow { emit(transform(value)) } - }.flowOn(dispatcher) + contract { callsInPlace(transform) } + return flatMapMerge { value -> flow { emit(transform(value)) } }.flowOn(dispatcher) } diff --git a/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/streaming/RingBuffer.kt b/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/streaming/RingBuffer.kt index 245d003b3..f1c0bfc6a 100644 --- a/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/streaming/RingBuffer.kt +++ b/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/streaming/RingBuffer.kt @@ -20,7 +20,7 @@ class RingBuffer( override var size: Int = size private set - override fun get(index: Int): T { + override operator fun get(index: Int): T { require(index >= 0) { "Index must be positive" } require(index < size) { "Index $index is out of circular buffer size $size" } return buffer[startIndex.forward(index)] as T @@ -31,15 +31,13 @@ class RingBuffer( /** * Iterator could provide wrong results if buffer is changed in initialization (iteration is safe) */ - override fun iterator(): Iterator = object : AbstractIterator() { + override operator fun iterator(): Iterator = object : AbstractIterator() { private var count = size private var index = startIndex val copy = buffer.copy() override fun computeNext() { - if (count == 0) { - done() - } else { + if (count == 0) done() else { setNext(copy[index] as T) index = index.forward(1) count-- diff --git a/kmath-coroutines/src/jvmMain/kotlin/scientifik/kmath/chains/ChainExt.kt b/kmath-coroutines/src/jvmMain/kotlin/scientifik/kmath/chains/ChainExt.kt index 0a3c67e00..5686b0ac0 100644 --- a/kmath-coroutines/src/jvmMain/kotlin/scientifik/kmath/chains/ChainExt.kt +++ b/kmath-coroutines/src/jvmMain/kotlin/scientifik/kmath/chains/ChainExt.kt @@ -1,7 +1,6 @@ package scientifik.kmath.chains import kotlinx.coroutines.runBlocking -import kotlin.sequences.Sequence /** * Represent a chain as regular iterator (uses blocking calls) @@ -15,6 +14,4 @@ operator fun Chain.iterator(): Iterator = object : Iterator { /** * Represent a chain as a sequence */ -fun Chain.asSequence(): Sequence = object : Sequence { - override fun iterator(): Iterator = this@asSequence.iterator() -} \ No newline at end of file +fun Chain.asSequence(): Sequence = Sequence { this@asSequence.iterator() } \ No newline at end of file diff --git a/kmath-coroutines/src/jvmMain/kotlin/scientifik/kmath/structures/LazyNDStructure.kt b/kmath-coroutines/src/jvmMain/kotlin/scientifik/kmath/structures/LazyNDStructure.kt index 8d5145976..ff732a06b 100644 --- a/kmath-coroutines/src/jvmMain/kotlin/scientifik/kmath/structures/LazyNDStructure.kt +++ b/kmath-coroutines/src/jvmMain/kotlin/scientifik/kmath/structures/LazyNDStructure.kt @@ -18,7 +18,7 @@ class LazyNDStructure( suspend fun await(index: IntArray): T = deferred(index).await() - override fun get(index: IntArray): T = runBlocking { + override operator fun get(index: IntArray): T = runBlocking { deferred(index).await() } @@ -52,10 +52,12 @@ suspend fun NDStructure.await(index: IntArray): T = /** * PENDING would benefit from KEEP-176 */ -fun NDStructure.mapAsyncIndexed( +inline fun NDStructure.mapAsyncIndexed( scope: CoroutineScope, - function: suspend (T, index: IntArray) -> R + crossinline function: suspend (T, index: IntArray) -> R ): LazyNDStructure = LazyNDStructure(scope, shape) { index -> function(get(index), index) } -fun NDStructure.mapAsync(scope: CoroutineScope, function: suspend (T) -> R): LazyNDStructure = - LazyNDStructure(scope, shape) { index -> function(get(index)) } \ No newline at end of file +inline fun NDStructure.mapAsync( + scope: CoroutineScope, + crossinline function: suspend (T) -> R +): LazyNDStructure = LazyNDStructure(scope, shape) { index -> function(get(index)) } diff --git a/kmath-dimensions/src/commonMain/kotlin/scientifik/kmath/dimensions/Wrappers.kt b/kmath-dimensions/src/commonMain/kotlin/scientifik/kmath/dimensions/Wrappers.kt index f447866c0..7b0244bdf 100644 --- a/kmath-dimensions/src/commonMain/kotlin/scientifik/kmath/dimensions/Wrappers.kt +++ b/kmath-dimensions/src/commonMain/kotlin/scientifik/kmath/dimensions/Wrappers.kt @@ -4,7 +4,9 @@ import scientifik.kmath.linear.GenericMatrixContext import scientifik.kmath.linear.MatrixContext import scientifik.kmath.linear.Point import scientifik.kmath.linear.transpose +import scientifik.kmath.operations.RealField import scientifik.kmath.operations.Ring +import scientifik.kmath.operations.invoke import scientifik.kmath.structures.Matrix import scientifik.kmath.structures.Structure2D @@ -42,7 +44,7 @@ inline class DMatrixWrapper( val structure: Structure2D ) : DMatrix { override val shape: IntArray get() = structure.shape - override fun get(i: Int, j: Int): T = structure[i, j] + override operator fun get(i: Int, j: Int): T = structure[i, j] } /** @@ -70,9 +72,9 @@ inline class DPointWrapper(val point: Point) : DPoint { override val size: Int get() = point.size - override fun get(index: Int): T = point[index] + override operator fun get(index: Int): T = point[index] - override fun iterator(): Iterator = point.iterator() + override operator fun iterator(): Iterator = point.iterator() } @@ -82,12 +84,14 @@ inline class DPointWrapper(val point: Point) : inline class DMatrixContext>(val context: GenericMatrixContext) { inline fun Matrix.coerce(): DMatrix { - if (rowNum != Dimension.dim().toInt()) { - error("Row number mismatch: expected ${Dimension.dim()} but found $rowNum") - } - if (colNum != Dimension.dim().toInt()) { - error("Column number mismatch: expected ${Dimension.dim()} but found $colNum") - } + check( + rowNum == Dimension.dim().toInt() + ) { "Row number mismatch: expected ${Dimension.dim()} but found $rowNum" } + + check( + colNum == Dimension.dim().toInt() + ) { "Column number mismatch: expected ${Dimension.dim()} but found $colNum" } + return DMatrix.coerceUnsafe(this) } @@ -97,11 +101,12 @@ inline class DMatrixContext>(val context: GenericMatrixCon inline fun produce(noinline initializer: (i: Int, j: Int) -> T): DMatrix { val rows = Dimension.dim() val cols = Dimension.dim() - return context.produce(rows.toInt(), cols.toInt(), initializer).coerce() + return context.produce(rows.toInt(), cols.toInt(), initializer).coerce() } inline fun point(noinline initializer: (Int) -> T): DPoint { val size = Dimension.dim() + return DPoint.coerceUnsafe( context.point( size.toInt(), @@ -112,37 +117,28 @@ inline class DMatrixContext>(val context: GenericMatrixCon inline infix fun DMatrix.dot( other: DMatrix - ): DMatrix { - return context.run { this@dot dot other }.coerce() - } + ): DMatrix = context { this@dot dot other }.coerce() - inline infix fun DMatrix.dot(vector: DPoint): DPoint { - return DPoint.coerceUnsafe(context.run { this@dot dot vector }) - } + inline infix fun DMatrix.dot(vector: DPoint): DPoint = + DPoint.coerceUnsafe(context { this@dot dot vector }) - inline operator fun DMatrix.times(value: T): DMatrix { - return context.run { this@times.times(value) }.coerce() - } + inline operator fun DMatrix.times(value: T): DMatrix = + context { this@times.times(value) }.coerce() inline operator fun T.times(m: DMatrix): DMatrix = m * this + inline operator fun DMatrix.plus(other: DMatrix): DMatrix = + context { this@plus + other }.coerce() - inline operator fun DMatrix.plus(other: DMatrix): DMatrix { - return context.run { this@plus + other }.coerce() - } + inline operator fun DMatrix.minus(other: DMatrix): DMatrix = + context { this@minus + other }.coerce() - inline operator fun DMatrix.minus(other: DMatrix): DMatrix { - return context.run { this@minus + other }.coerce() - } + inline operator fun DMatrix.unaryMinus(): DMatrix = + context { this@unaryMinus.unaryMinus() }.coerce() - inline operator fun DMatrix.unaryMinus(): DMatrix { - return context.run { this@unaryMinus.unaryMinus() }.coerce() - } - - inline fun DMatrix.transpose(): DMatrix { - return context.run { (this@transpose as Matrix).transpose() }.coerce() - } + inline fun DMatrix.transpose(): DMatrix = + context { (this@transpose as Matrix).transpose() }.coerce() /** * A square unit matrix @@ -156,6 +152,6 @@ inline class DMatrixContext>(val context: GenericMatrixCon } companion object { - val real = DMatrixContext(MatrixContext.real) + val real: DMatrixContext = DMatrixContext(MatrixContext.real) } -} \ No newline at end of file +} diff --git a/kmath-dimensions/src/commonTest/kotlin/scientifik/dimensions/DMatrixContextTest.kt b/kmath-dimensions/src/commonTest/kotlin/scientifik/dimensions/DMatrixContextTest.kt index 74d20205c..8dabdeeac 100644 --- a/kmath-dimensions/src/commonTest/kotlin/scientifik/dimensions/DMatrixContextTest.kt +++ b/kmath-dimensions/src/commonTest/kotlin/scientifik/dimensions/DMatrixContextTest.kt @@ -5,11 +5,10 @@ import scientifik.kmath.dimensions.D3 import scientifik.kmath.dimensions.DMatrixContext import kotlin.test.Test - class DMatrixContextTest { @Test fun testDimensionSafeMatrix() { - val res = DMatrixContext.real.run { + val res = with(DMatrixContext.real) { val m = produce { i, j -> (i + j).toDouble() } //The dimension of `one()` is inferred from type @@ -19,7 +18,7 @@ class DMatrixContextTest { @Test fun testTypeCheck() { - val res = DMatrixContext.real.run { + val res = with(DMatrixContext.real) { val m1 = produce { i, j -> (i + j).toDouble() } val m2 = produce { i, j -> (i + j).toDouble() } diff --git a/kmath-for-real/src/commonMain/kotlin/scientifik/kmath/real/RealVector.kt b/kmath-for-real/src/commonMain/kotlin/scientifik/kmath/real/RealVector.kt index 2b89904e3..811b54d7c 100644 --- a/kmath-for-real/src/commonMain/kotlin/scientifik/kmath/real/RealVector.kt +++ b/kmath-for-real/src/commonMain/kotlin/scientifik/kmath/real/RealVector.kt @@ -14,8 +14,8 @@ import kotlin.math.sqrt typealias RealPoint = Point -fun DoubleArray.asVector() = RealVector(this.asBuffer()) -fun List.asVector() = RealVector(this.asBuffer()) +fun DoubleArray.asVector(): RealVector = RealVector(this.asBuffer()) +fun List.asVector(): RealVector = RealVector(this.asBuffer()) object VectorL2Norm : Norm, Double> { override fun norm(arg: Point): Double = sqrt(arg.asIterable().sumByDouble { it.toDouble() }) @@ -32,15 +32,14 @@ inline class RealVector(private val point: Point) : override val size: Int get() = point.size - override fun get(index: Int): Double = point[index] + override operator fun get(index: Int): Double = point[index] - override fun iterator(): Iterator = point.iterator() + override operator fun iterator(): Iterator = point.iterator() companion object { + private val spaceCache: MutableMap> = hashMapOf() - private val spaceCache = HashMap>() - - inline operator fun invoke(dim: Int, initializer: (Int) -> Double) = + inline operator fun invoke(dim: Int, initializer: (Int) -> Double): RealVector = RealVector(RealBuffer(dim, initializer)) operator fun invoke(vararg values: Double): RealVector = values.asVector() @@ -49,4 +48,4 @@ inline class RealVector(private val point: Point) : BufferVectorSpace(dim, RealField) { size, init -> Buffer.real(size, init) } } } -} \ No newline at end of file +} diff --git a/kmath-for-real/src/commonMain/kotlin/scientifik/kmath/real/realMatrix.kt b/kmath-for-real/src/commonMain/kotlin/scientifik/kmath/real/realMatrix.kt index 65f86eec7..5ccba90a9 100644 --- a/kmath-for-real/src/commonMain/kotlin/scientifik/kmath/real/realMatrix.kt +++ b/kmath-for-real/src/commonMain/kotlin/scientifik/kmath/real/realMatrix.kt @@ -3,11 +3,14 @@ package scientifik.kmath.real import scientifik.kmath.linear.MatrixContext import scientifik.kmath.linear.RealMatrixContext.elementContext import scientifik.kmath.linear.VirtualMatrix +import scientifik.kmath.operations.invoke import scientifik.kmath.operations.sum import scientifik.kmath.structures.Buffer import scientifik.kmath.structures.Matrix import scientifik.kmath.structures.RealBuffer import scientifik.kmath.structures.asIterable +import kotlin.contracts.ExperimentalContracts +import kotlin.contracts.contract import kotlin.math.pow /* @@ -27,7 +30,7 @@ typealias RealMatrix = Matrix fun realMatrix(rowNum: Int, colNum: Int, initializer: (i: Int, j: Int) -> Double): RealMatrix = MatrixContext.real.produce(rowNum, colNum, initializer) -fun Array.toMatrix(): RealMatrix{ +fun Array.toMatrix(): RealMatrix { return MatrixContext.real.produce(size, this[0].size) { row, col -> this[row][col] } } @@ -117,13 +120,17 @@ operator fun Matrix.minus(other: Matrix): RealMatrix = * Operations on columns */ -inline fun Matrix.appendColumn(crossinline mapper: (Buffer) -> Double) = - MatrixContext.real.produce(rowNum, colNum + 1) { row, col -> +@OptIn(ExperimentalContracts::class) +inline fun Matrix.appendColumn(crossinline mapper: (Buffer) -> Double): Matrix { + contract { callsInPlace(mapper) } + + return MatrixContext.real.produce(rowNum, colNum + 1) { row, col -> if (col < colNum) this[row, col] else mapper(rows[row]) } +} fun Matrix.extractColumns(columnRange: IntRange): RealMatrix = MatrixContext.real.produce(rowNum, columnRange.count()) { row, col -> @@ -135,17 +142,15 @@ fun Matrix.extractColumn(columnIndex: Int): RealMatrix = fun Matrix.sumByColumn(): RealBuffer = RealBuffer(colNum) { j -> val column = columns[j] - with(elementContext) { - sum(column.asIterable()) - } + elementContext { sum(column.asIterable()) } } fun Matrix.minByColumn(): RealBuffer = RealBuffer(colNum) { j -> - columns[j].asIterable().min() ?: throw Exception("Cannot produce min on empty column") + columns[j].asIterable().min() ?: error("Cannot produce min on empty column") } fun Matrix.maxByColumn(): RealBuffer = RealBuffer(colNum) { j -> - columns[j].asIterable().max() ?: throw Exception("Cannot produce min on empty column") + columns[j].asIterable().max() ?: error("Cannot produce min on empty column") } fun Matrix.averageByColumn(): RealBuffer = RealBuffer(colNum) { j -> @@ -156,10 +161,7 @@ fun Matrix.averageByColumn(): RealBuffer = RealBuffer(colNum) { j -> * Operations processing all elements */ -fun Matrix.sum() = elements().map { (_, value) -> value }.sum() - -fun Matrix.min() = elements().map { (_, value) -> value }.min() - -fun Matrix.max() = elements().map { (_, value) -> value }.max() - -fun Matrix.average() = elements().map { (_, value) -> value }.average() +fun Matrix.sum(): Double = elements().map { (_, value) -> value }.sum() +fun Matrix.min(): Double? = elements().map { (_, value) -> value }.min() +fun Matrix.max(): Double? = elements().map { (_, value) -> value }.max() +fun Matrix.average(): Double = elements().map { (_, value) -> value }.average() diff --git a/kmath-for-real/src/commonTest/kotlin/scientifik/kmath/linear/VectorTest.kt b/kmath-for-real/src/commonTest/kotlin/scientifik/kmath/linear/VectorTest.kt index 28e62b066..ef7f40afe 100644 --- a/kmath-for-real/src/commonTest/kotlin/scientifik/kmath/linear/VectorTest.kt +++ b/kmath-for-real/src/commonTest/kotlin/scientifik/kmath/linear/VectorTest.kt @@ -1,5 +1,6 @@ package scientifik.kmath.linear +import scientifik.kmath.operations.invoke import scientifik.kmath.real.RealVector import kotlin.test.Test import kotlin.test.assertEquals @@ -24,14 +25,10 @@ class VectorTest { fun testDot() { val vector1 = RealVector(5) { it.toDouble() } val vector2 = RealVector(5) { 5 - it.toDouble() } - val matrix1 = vector1.asMatrix() val matrix2 = vector2.asMatrix().transpose() - val product = MatrixContext.real.run { matrix1 dot matrix2 } - - + val product = MatrixContext.real { matrix1 dot matrix2 } assertEquals(5.0, product[1, 0]) assertEquals(6.0, product[2, 2]) } - -} \ No newline at end of file +} diff --git a/kmath-functions/src/commonMain/kotlin/scientifik/kmath/functions/Polynomial.kt b/kmath-functions/src/commonMain/kotlin/scientifik/kmath/functions/Polynomial.kt index b747b521d..46b1d7c90 100644 --- a/kmath-functions/src/commonMain/kotlin/scientifik/kmath/functions/Polynomial.kt +++ b/kmath-functions/src/commonMain/kotlin/scientifik/kmath/functions/Polynomial.kt @@ -2,6 +2,10 @@ package scientifik.kmath.functions import scientifik.kmath.operations.Ring import scientifik.kmath.operations.Space +import scientifik.kmath.operations.invoke +import kotlin.contracts.ExperimentalContracts +import kotlin.contracts.InvocationKind +import kotlin.contracts.contract import kotlin.math.max import kotlin.math.pow @@ -13,20 +17,21 @@ inline class Polynomial(val coefficients: List) { constructor(vararg coefficients: T) : this(coefficients.toList()) } -fun Polynomial.value() = +fun Polynomial.value(): Double = coefficients.reduceIndexed { index: Int, acc: Double, d: Double -> acc + d.pow(index) } - -fun > Polynomial.value(ring: C, arg: T): T = ring.run { - if (coefficients.isEmpty()) return@run zero +fun > Polynomial.value(ring: C, arg: T): T = ring { + if (coefficients.isEmpty()) return@ring zero var res = coefficients.first() var powerArg = arg + for (index in 1 until coefficients.size) { res += coefficients[index] * powerArg //recalculating power on each step to avoid power costs on long polynomials powerArg *= arg } - return@run res + + res } /** @@ -34,7 +39,7 @@ fun > Polynomial.value(ring: C, arg: T): T = ring.run { */ fun > Polynomial.asMathFunction(): MathFunction = object : MathFunction { - override fun C.invoke(arg: T): T = value(this, arg) + override operator fun C.invoke(arg: T): T = value(this, arg) } /** @@ -49,18 +54,16 @@ class PolynomialSpace>(val ring: C) : Space> override fun add(a: Polynomial, b: Polynomial): Polynomial { val dim = max(a.coefficients.size, b.coefficients.size) - ring.run { - return Polynomial(List(dim) { index -> + + return ring { + Polynomial(List(dim) { index -> a.coefficients.getOrElse(index) { zero } + b.coefficients.getOrElse(index) { zero } }) } } - override fun multiply(a: Polynomial, k: Number): Polynomial { - ring.run { - return Polynomial(List(a.coefficients.size) { index -> a.coefficients[index] * k }) - } - } + override fun multiply(a: Polynomial, k: Number): Polynomial = + ring { Polynomial(List(a.coefficients.size) { index -> a.coefficients[index] * k }) } override val zero: Polynomial = Polynomial(emptyList()) @@ -68,6 +71,8 @@ class PolynomialSpace>(val ring: C) : Space> operator fun Polynomial.invoke(arg: T): T = value(ring, arg) } -fun , R> C.polynomial(block: PolynomialSpace.() -> R): R { - return PolynomialSpace(this).run(block) -} \ No newline at end of file +@OptIn(ExperimentalContracts::class) +inline fun , R> C.polynomial(block: PolynomialSpace.() -> R): R { + contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } + return PolynomialSpace(this).block() +} diff --git a/kmath-functions/src/commonMain/kotlin/scientifik/kmath/interpolation/LinearInterpolator.kt b/kmath-functions/src/commonMain/kotlin/scientifik/kmath/interpolation/LinearInterpolator.kt index 98beb4391..a7925180d 100644 --- a/kmath-functions/src/commonMain/kotlin/scientifik/kmath/interpolation/LinearInterpolator.kt +++ b/kmath-functions/src/commonMain/kotlin/scientifik/kmath/interpolation/LinearInterpolator.kt @@ -4,13 +4,13 @@ import scientifik.kmath.functions.OrderedPiecewisePolynomial import scientifik.kmath.functions.PiecewisePolynomial import scientifik.kmath.functions.Polynomial import scientifik.kmath.operations.Field +import scientifik.kmath.operations.invoke /** * Reference JVM implementation: https://github.com/apache/commons-math/blob/master/src/main/java/org/apache/commons/math4/analysis/interpolation/LinearInterpolator.java */ class LinearInterpolator>(override val algebra: Field) : PolynomialInterpolator { - - override fun interpolatePolynomials(points: XYPointSet): PiecewisePolynomial = algebra.run { + override fun interpolatePolynomials(points: XYPointSet): PiecewisePolynomial = algebra { require(points.size > 0) { "Point array should not be empty" } insureSorted(points) @@ -23,4 +23,4 @@ class LinearInterpolator>(override val algebra: Field) : Po } } } -} \ No newline at end of file +} diff --git a/kmath-functions/src/commonMain/kotlin/scientifik/kmath/interpolation/SplineInterpolator.kt b/kmath-functions/src/commonMain/kotlin/scientifik/kmath/interpolation/SplineInterpolator.kt index e1af0c1a2..b709c4e87 100644 --- a/kmath-functions/src/commonMain/kotlin/scientifik/kmath/interpolation/SplineInterpolator.kt +++ b/kmath-functions/src/commonMain/kotlin/scientifik/kmath/interpolation/SplineInterpolator.kt @@ -4,6 +4,7 @@ import scientifik.kmath.functions.OrderedPiecewisePolynomial import scientifik.kmath.functions.PiecewisePolynomial import scientifik.kmath.functions.Polynomial import scientifik.kmath.operations.Field +import scientifik.kmath.operations.invoke import scientifik.kmath.structures.MutableBufferFactory /** @@ -17,7 +18,7 @@ class SplineInterpolator>( //TODO possibly optimize zeroed buffers - override fun interpolatePolynomials(points: XYPointSet): PiecewisePolynomial = algebra.run { + override fun interpolatePolynomials(points: XYPointSet): PiecewisePolynomial = algebra { if (points.size < 3) { error("Can't use spline interpolator with less than 3 points") } diff --git a/kmath-functions/src/commonMain/kotlin/scientifik/kmath/interpolation/XYPointSet.kt b/kmath-functions/src/commonMain/kotlin/scientifik/kmath/interpolation/XYPointSet.kt index d8e10b880..56953f9fc 100644 --- a/kmath-functions/src/commonMain/kotlin/scientifik/kmath/interpolation/XYPointSet.kt +++ b/kmath-functions/src/commonMain/kotlin/scientifik/kmath/interpolation/XYPointSet.kt @@ -14,9 +14,7 @@ interface XYZPointSet : XYPointSet { } internal fun > insureSorted(points: XYPointSet) { - for (i in 0 until points.size - 1) { - if (points.x[i + 1] <= points.x[i]) error("Input data is not sorted at index $i") - } + for (i in 0 until points.size - 1) require(points.x[i + 1] > points.x[i]) { "Input data is not sorted at index $i" } } class NDStructureColumn(val structure: Structure2D, val column: Int) : Buffer { @@ -26,9 +24,9 @@ class NDStructureColumn(val structure: Structure2D, val column: Int) : Buf override val size: Int get() = structure.rowNum - override fun get(index: Int): T = structure[index, column] + override operator fun get(index: Int): T = structure[index, column] - override fun iterator(): Iterator = sequence { + override operator fun iterator(): Iterator = sequence { repeat(size) { yield(get(it)) } diff --git a/kmath-geometry/src/commonMain/kotlin/scientifik/kmath/geometry/Euclidean2DSpace.kt b/kmath-geometry/src/commonMain/kotlin/scientifik/kmath/geometry/Euclidean2DSpace.kt index 2313b2170..f0dc49882 100644 --- a/kmath-geometry/src/commonMain/kotlin/scientifik/kmath/geometry/Euclidean2DSpace.kt +++ b/kmath-geometry/src/commonMain/kotlin/scientifik/kmath/geometry/Euclidean2DSpace.kt @@ -9,25 +9,21 @@ import kotlin.math.sqrt interface Vector2D : Point, Vector, SpaceElement { val x: Double val y: Double - + override val context: Euclidean2DSpace get() = Euclidean2DSpace override val size: Int get() = 2 - override fun get(index: Int): Double = when (index) { + override operator fun get(index: Int): Double = when (index) { 1 -> x 2 -> y else -> error("Accessing outside of point bounds") } - override fun iterator(): Iterator = listOf(x, y).iterator() - - override val context: Euclidean2DSpace get() = Euclidean2DSpace - + override operator fun iterator(): Iterator = listOf(x, y).iterator() override fun unwrap(): Vector2D = this - override fun Vector2D.wrap(): Vector2D = this } -val Vector2D.r: Double get() = Euclidean2DSpace.run { sqrt(norm()) } +val Vector2D.r: Double get() = Euclidean2DSpace { sqrt(norm()) } @Suppress("FunctionName") fun Vector2D(x: Double, y: Double): Vector2D = Vector2DImpl(x, y) diff --git a/kmath-geometry/src/commonMain/kotlin/scientifik/kmath/geometry/Euclidean3DSpace.kt b/kmath-geometry/src/commonMain/kotlin/scientifik/kmath/geometry/Euclidean3DSpace.kt index dd1776342..3748e58c7 100644 --- a/kmath-geometry/src/commonMain/kotlin/scientifik/kmath/geometry/Euclidean3DSpace.kt +++ b/kmath-geometry/src/commonMain/kotlin/scientifik/kmath/geometry/Euclidean3DSpace.kt @@ -2,6 +2,7 @@ package scientifik.kmath.geometry import scientifik.kmath.linear.Point import scientifik.kmath.operations.SpaceElement +import scientifik.kmath.operations.invoke import kotlin.math.sqrt @@ -9,19 +10,17 @@ interface Vector3D : Point, Vector, SpaceElement x 2 -> y 3 -> z else -> error("Accessing outside of point bounds") } - override fun iterator(): Iterator = listOf(x, y, z).iterator() - - override val context: Euclidean3DSpace get() = Euclidean3DSpace + override operator fun iterator(): Iterator = listOf(x, y, z).iterator() override fun unwrap(): Vector3D = this @@ -31,7 +30,7 @@ interface Vector3D : Point, Vector, SpaceElement { override fun Vector3D.dot(other: Vector3D): Double = x * other.x + y * other.y + z * other.z -} \ No newline at end of file +} diff --git a/kmath-histograms/src/commonMain/kotlin/scientifik/kmath/histogram/Histogram.kt b/kmath-histograms/src/commonMain/kotlin/scientifik/kmath/histogram/Histogram.kt index 43d50ad20..9ff2aacf5 100644 --- a/kmath-histograms/src/commonMain/kotlin/scientifik/kmath/histogram/Histogram.kt +++ b/kmath-histograms/src/commonMain/kotlin/scientifik/kmath/histogram/Histogram.kt @@ -4,6 +4,9 @@ import scientifik.kmath.domains.Domain import scientifik.kmath.linear.Point import scientifik.kmath.structures.ArrayBuffer import scientifik.kmath.structures.RealBuffer +import kotlin.contracts.ExperimentalContracts +import kotlin.contracts.InvocationKind +import kotlin.contracts.contract /** * The bin in the histogram. The histogram is by definition always done in the real space @@ -37,20 +40,20 @@ interface MutableHistogram> : Histogram { */ fun putWithWeight(point: Point, weight: Double) - fun put(point: Point) = putWithWeight(point, 1.0) + fun put(point: Point): Unit = putWithWeight(point, 1.0) } -fun MutableHistogram.put(vararg point: T) = put(ArrayBuffer(point)) +fun MutableHistogram.put(vararg point: T): Unit = put(ArrayBuffer(point)) -fun MutableHistogram.put(vararg point: Number) = +fun MutableHistogram.put(vararg point: Number): Unit = put(RealBuffer(point.map { it.toDouble() }.toDoubleArray())) -fun MutableHistogram.put(vararg point: Double) = put(RealBuffer(point)) +fun MutableHistogram.put(vararg point: Double): Unit = put(RealBuffer(point)) -fun MutableHistogram.fill(sequence: Iterable>) = sequence.forEach { put(it) } +fun MutableHistogram.fill(sequence: Iterable>): Unit = sequence.forEach { put(it) } /** * Pass a sequence builder into histogram */ -fun MutableHistogram.fill(buider: suspend SequenceScope>.() -> Unit) = - fill(sequence(buider).asIterable()) \ No newline at end of file +fun MutableHistogram.fill(block: suspend SequenceScope>.() -> Unit): Unit = + fill(sequence(block).asIterable()) diff --git a/kmath-histograms/src/commonMain/kotlin/scientifik/kmath/histogram/RealHistogram.kt b/kmath-histograms/src/commonMain/kotlin/scientifik/kmath/histogram/RealHistogram.kt index 628a68461..f05ae1694 100644 --- a/kmath-histograms/src/commonMain/kotlin/scientifik/kmath/histogram/RealHistogram.kt +++ b/kmath-histograms/src/commonMain/kotlin/scientifik/kmath/histogram/RealHistogram.kt @@ -2,6 +2,7 @@ package scientifik.kmath.histogram import scientifik.kmath.linear.Point import scientifik.kmath.operations.SpaceOperations +import scientifik.kmath.operations.invoke import scientifik.kmath.real.asVector import scientifik.kmath.structures.* import kotlin.math.floor @@ -9,19 +10,16 @@ import kotlin.math.floor data class BinDef>(val space: SpaceOperations>, val center: Point, val sizes: Point) { fun contains(vector: Point): Boolean { - if (vector.size != center.size) error("Dimension mismatch for input vector. Expected ${center.size}, but found ${vector.size}") - val upper = space.run { center + sizes / 2.0 } - val lower = space.run { center - sizes / 2.0 } - return vector.asSequence().mapIndexed { i, value -> - value in lower[i]..upper[i] - }.all { it } + require(vector.size == center.size) { "Dimension mismatch for input vector. Expected ${center.size}, but found ${vector.size}" } + val upper = space { center + sizes / 2.0 } + val lower = space { center - sizes / 2.0 } + return vector.asSequence().mapIndexed { i, value -> value in lower[i]..upper[i] }.all { it } } } class MultivariateBin>(val def: BinDef, override val value: Number) : Bin { - - override fun contains(point: Point): Boolean = def.contains(point) + override operator fun contains(point: Point): Boolean = def.contains(point) override val dimension: Int get() = def.center.size @@ -39,47 +37,34 @@ class RealHistogram( private val upper: Buffer, private val binNums: IntArray = IntArray(lower.size) { 20 } ) : MutableHistogram> { - - private val strides = DefaultStrides(IntArray(binNums.size) { binNums[it] + 2 }) - private val values: NDStructure = NDStructure.auto(strides) { LongCounter() } - private val weights: NDStructure = NDStructure.auto(strides) { DoubleCounter() } - override val dimension: Int get() = lower.size - - private val binSize = RealBuffer(dimension) { (upper[it] - lower[it]) / binNums[it] } init { // argument checks - if (lower.size != upper.size) error("Dimension mismatch in histogram lower and upper limits.") - if (lower.size != binNums.size) error("Dimension mismatch in bin count.") - if ((0 until dimension).any { upper[it] - lower[it] < 0 }) error("Range for one of axis is not strictly positive") + require(lower.size == upper.size) { "Dimension mismatch in histogram lower and upper limits." } + require(lower.size == binNums.size) { "Dimension mismatch in bin count." } + require(!(0 until dimension).any { upper[it] - lower[it] < 0 }) { "Range for one of axis is not strictly positive" } } /** * Get internal [NDStructure] bin index for given axis */ - private fun getIndex(axis: Int, value: Double): Int { - return when { - value >= upper[axis] -> binNums[axis] + 1 // overflow - value < lower[axis] -> 0 // underflow - else -> floor((value - lower[axis]) / binSize[axis]).toInt() + 1 - } + private fun getIndex(axis: Int, value: Double): Int = when { + value >= upper[axis] -> binNums[axis] + 1 // overflow + value < lower[axis] -> 0 // underflow + else -> floor((value - lower[axis]) / binSize[axis]).toInt() + 1 } private fun getIndex(point: Buffer): IntArray = IntArray(dimension) { getIndex(it, point[it]) } - private fun getValue(index: IntArray): Long { - return values[index].sum() - } + private fun getValue(index: IntArray): Long = values[index].sum() - fun getValue(point: Buffer): Long { - return getValue(getIndex(point)) - } + fun getValue(point: Buffer): Long = getValue(getIndex(point)) private fun getDef(index: IntArray): BinDef { val center = index.mapIndexed { axis, i -> @@ -89,14 +74,13 @@ class RealHistogram( else -> lower[axis] + (i.toDouble() - 0.5) * binSize[axis] } }.asBuffer() + return BinDef(RealBufferFieldOperations, center, binSize) } - fun getDef(point: Buffer): BinDef { - return getDef(getIndex(point)) - } + fun getDef(point: Buffer): BinDef = getDef(getIndex(point)) - override fun get(point: Buffer): MultivariateBin? { + override operator fun get(point: Buffer): MultivariateBin? { val index = getIndex(point) return MultivariateBin(getDef(index), getValue(index)) } @@ -112,26 +96,21 @@ class RealHistogram( weights[index].add(weight) } - override fun iterator(): Iterator> = weights.elements().map { (index, value) -> + override operator fun iterator(): Iterator> = weights.elements().map { (index, value) -> MultivariateBin(getDef(index), value.sum()) }.iterator() /** * Convert this histogram into NDStructure containing bin values but not bin descriptions */ - fun values(): NDStructure { - return NDStructure.auto(values.shape) { values[it].sum() } - } + fun values(): NDStructure = NDStructure.auto(values.shape) { values[it].sum() } /** * Sum of weights */ - fun weights():NDStructure{ - return NDStructure.auto(weights.shape) { weights[it].sum() } - } + fun weights(): NDStructure = NDStructure.auto(weights.shape) { weights[it].sum() } companion object { - /** * Use it like * ``` @@ -141,12 +120,10 @@ class RealHistogram( *) *``` */ - fun fromRanges(vararg ranges: ClosedFloatingPointRange): RealHistogram { - return RealHistogram( - ranges.map { it.start }.asVector(), - ranges.map { it.endInclusive }.asVector() - ) - } + fun fromRanges(vararg ranges: ClosedFloatingPointRange): RealHistogram = RealHistogram( + ranges.map { it.start }.asVector(), + ranges.map { it.endInclusive }.asVector() + ) /** * Use it like @@ -157,13 +134,10 @@ class RealHistogram( *) *``` */ - fun fromRanges(vararg ranges: Pair, Int>): RealHistogram { - return RealHistogram( - ListBuffer(ranges.map { it.first.start }), - ListBuffer(ranges.map { it.first.endInclusive }), - ranges.map { it.second }.toIntArray() - ) - } + fun fromRanges(vararg ranges: Pair, Int>): RealHistogram = RealHistogram( + ListBuffer(ranges.map { it.first.start }), + ListBuffer(ranges.map { it.first.endInclusive }), + ranges.map { it.second }.toIntArray() + ) } - -} \ No newline at end of file +} diff --git a/kmath-histograms/src/jvmMain/kotlin/scientifik/kmath/histogram/UnivariateHistogram.kt b/kmath-histograms/src/jvmMain/kotlin/scientifik/kmath/histogram/UnivariateHistogram.kt index af01205bf..e30a45f5a 100644 --- a/kmath-histograms/src/jvmMain/kotlin/scientifik/kmath/histogram/UnivariateHistogram.kt +++ b/kmath-histograms/src/jvmMain/kotlin/scientifik/kmath/histogram/UnivariateHistogram.kt @@ -46,11 +46,11 @@ class UnivariateHistogram private constructor(private val factory: (Double) -> U synchronized(this) { bins.put(it.position, it) } } - override fun get(point: Buffer): UnivariateBin? = get(point[0]) + override operator fun get(point: Buffer): UnivariateBin? = get(point[0]) override val dimension: Int get() = 1 - override fun iterator(): Iterator = bins.values.iterator() + override operator fun iterator(): Iterator = bins.values.iterator() /** * Thread safe put operation @@ -65,15 +65,14 @@ class UnivariateHistogram private constructor(private val factory: (Double) -> U } companion object { - fun uniform(binSize: Double, start: Double = 0.0): UnivariateHistogram { - return UnivariateHistogram { value -> - val center = start + binSize * floor((value - start) / binSize + 0.5) - UnivariateBin(center, binSize) - } + fun uniform(binSize: Double, start: Double = 0.0): UnivariateHistogram = UnivariateHistogram { value -> + val center = start + binSize * floor((value - start) / binSize + 0.5) + UnivariateBin(center, binSize) } fun custom(borders: DoubleArray): UnivariateHistogram { val sorted = borders.sortedArray() + return UnivariateHistogram { value -> when { value < sorted.first() -> UnivariateBin( diff --git a/kmath-koma/src/commonMain/kotlin/scientifik.kmath.linear/KomaMatrix.kt b/kmath-koma/src/commonMain/kotlin/scientifik.kmath.linear/KomaMatrix.kt index 10deabd73..bd8fa782a 100644 --- a/kmath-koma/src/commonMain/kotlin/scientifik.kmath.linear/KomaMatrix.kt +++ b/kmath-koma/src/commonMain/kotlin/scientifik.kmath.linear/KomaMatrix.kt @@ -3,16 +3,16 @@ package scientifik.kmath.linear import koma.extensions.fill import koma.matrix.MatrixFactory import scientifik.kmath.operations.Space +import scientifik.kmath.operations.invoke import scientifik.kmath.structures.Matrix import scientifik.kmath.structures.NDStructure class KomaMatrixContext( private val factory: MatrixFactory>, private val space: Space -) : - MatrixContext { +) : MatrixContext { - override fun produce(rows: Int, columns: Int, initializer: (i: Int, j: Int) -> T) = + override fun produce(rows: Int, columns: Int, initializer: (i: Int, j: Int) -> T): KomaMatrix = KomaMatrix(factory.zeros(rows, columns).fill(initializer)) fun Matrix.toKoma(): KomaMatrix = if (this is KomaMatrix) { @@ -28,31 +28,28 @@ class KomaMatrixContext( } - override fun Matrix.dot(other: Matrix) = - KomaMatrix(this.toKoma().origin * other.toKoma().origin) + override fun Matrix.dot(other: Matrix): KomaMatrix = + KomaMatrix(toKoma().origin * other.toKoma().origin) - override fun Matrix.dot(vector: Point) = - KomaVector(this.toKoma().origin * vector.toKoma().origin) + override fun Matrix.dot(vector: Point): KomaVector = + KomaVector(toKoma().origin * vector.toKoma().origin) - override fun Matrix.unaryMinus() = - KomaMatrix(this.toKoma().origin.unaryMinus()) + override operator fun Matrix.unaryMinus(): KomaMatrix = + KomaMatrix(toKoma().origin.unaryMinus()) - override fun add(a: Matrix, b: Matrix) = + override fun add(a: Matrix, b: Matrix): KomaMatrix = KomaMatrix(a.toKoma().origin + b.toKoma().origin) - override fun Matrix.minus(b: Matrix) = - KomaMatrix(this.toKoma().origin - b.toKoma().origin) + override operator fun Matrix.minus(b: Matrix): KomaMatrix = + KomaMatrix(toKoma().origin - b.toKoma().origin) override fun multiply(a: Matrix, k: Number): Matrix = - produce(a.rowNum, a.colNum) { i, j -> space.run { a[i, j] * k } } + produce(a.rowNum, a.colNum) { i, j -> space { a[i, j] * k } } - override fun Matrix.times(value: T) = - KomaMatrix(this.toKoma().origin * value) - - companion object { - - } + override operator fun Matrix.times(value: T): KomaMatrix = + KomaMatrix(toKoma().origin * value) + companion object } fun KomaMatrixContext.solve(a: Matrix, b: Matrix) = @@ -70,10 +67,11 @@ class KomaMatrix(val origin: koma.matrix.Matrix, features: Set = features ?: setOf( + override val features: Set = features ?: hashSetOf( object : DeterminantFeature { override val determinant: T get() = origin.det() }, + object : LUPDecompositionFeature { private val lup by lazy { origin.LU() } override val l: FeaturedMatrix get() = KomaMatrix(lup.second) @@ -85,7 +83,7 @@ class KomaMatrix(val origin: koma.matrix.Matrix, features: Set = KomaMatrix(this.origin, this.features + features) - override fun get(i: Int, j: Int): T = origin.getGeneric(i, j) + override operator fun get(i: Int, j: Int): T = origin.getGeneric(i, j) override fun equals(other: Any?): Boolean { return NDStructure.equals(this, other as? NDStructure<*> ?: return false) @@ -101,14 +99,12 @@ class KomaMatrix(val origin: koma.matrix.Matrix, features: Set internal constructor(val origin: koma.matrix.Matrix) : Point { - init { - if (origin.numCols() != 1) error("Only single column matrices are allowed") - } - override val size: Int get() = origin.numRows() - override fun get(index: Int): T = origin.getGeneric(index) + init { + require(origin.numCols() == 1) { error("Only single column matrices are allowed") } + } - override fun iterator(): Iterator = origin.toIterable().iterator() + override operator fun get(index: Int): T = origin.getGeneric(index) + override operator fun iterator(): Iterator = origin.toIterable().iterator() } - diff --git a/kmath-memory/src/commonMain/kotlin/scientifik/memory/Memory.kt b/kmath-memory/src/commonMain/kotlin/scientifik/memory/Memory.kt index a749a7074..9e6ca9bde 100644 --- a/kmath-memory/src/commonMain/kotlin/scientifik/memory/Memory.kt +++ b/kmath-memory/src/commonMain/kotlin/scientifik/memory/Memory.kt @@ -1,5 +1,9 @@ package scientifik.memory +import kotlin.contracts.ExperimentalContracts +import kotlin.contracts.InvocationKind +import kotlin.contracts.contract + /** * Represents a display of certain memory structure. */ @@ -80,7 +84,9 @@ interface MemoryReader { /** * Uses the memory for read then releases the reader. */ +@OptIn(ExperimentalContracts::class) inline fun Memory.read(block: MemoryReader.() -> Unit) { + contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } reader().apply(block).release() } @@ -132,7 +138,9 @@ interface MemoryWriter { /** * Uses the memory for write then releases the writer. */ +@OptIn(ExperimentalContracts::class) inline fun Memory.write(block: MemoryWriter.() -> Unit) { + contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } writer().apply(block).release() } diff --git a/kmath-memory/src/commonMain/kotlin/scientifik/memory/MemorySpec.kt b/kmath-memory/src/commonMain/kotlin/scientifik/memory/MemorySpec.kt index 59a93f290..1381afbec 100644 --- a/kmath-memory/src/commonMain/kotlin/scientifik/memory/MemorySpec.kt +++ b/kmath-memory/src/commonMain/kotlin/scientifik/memory/MemorySpec.kt @@ -38,11 +38,7 @@ fun MemoryWriter.write(spec: MemorySpec, offset: Int, value: T): Un * Reads array of [size] objects mapped by [spec] at certain [offset]. */ inline fun MemoryReader.readArray(spec: MemorySpec, offset: Int, size: Int): Array = - Array(size) { i -> - spec.run { - read(offset + i * objectSize) - } - } + Array(size) { i -> with(spec) { read(offset + i * objectSize) } } /** * Writes [array] of objects mapped by [spec] at certain [offset]. diff --git a/kmath-memory/src/jvmMain/kotlin/scientifik/memory/ByteBufferMemory.kt b/kmath-memory/src/jvmMain/kotlin/scientifik/memory/ByteBufferMemory.kt index b5a0dd51b..73ad7deec 100644 --- a/kmath-memory/src/jvmMain/kotlin/scientifik/memory/ByteBufferMemory.kt +++ b/kmath-memory/src/jvmMain/kotlin/scientifik/memory/ByteBufferMemory.kt @@ -1,12 +1,17 @@ package scientifik.memory +import java.io.IOException import java.nio.ByteBuffer import java.nio.channels.FileChannel import java.nio.file.Files import java.nio.file.Path import java.nio.file.StandardOpenOption +import kotlin.contracts.ExperimentalContracts +import kotlin.contracts.InvocationKind +import kotlin.contracts.contract -private class ByteBufferMemory( +@PublishedApi +internal class ByteBufferMemory( val buffer: ByteBuffer, val startOffset: Int = 0, override val size: Int = buffer.limit() @@ -112,7 +117,12 @@ fun ByteBuffer.asMemory(startOffset: Int = 0, size: Int = limit()): Memory = /** * Uses direct memory-mapped buffer from file to read something and close it afterwards. */ -fun Path.readAsMemory(position: Long = 0, size: Long = Files.size(this), block: Memory.() -> R): R = - FileChannel.open(this, StandardOpenOption.READ).use { - ByteBufferMemory(it.map(FileChannel.MapMode.READ_ONLY, position, size)).block() - } +@OptIn(ExperimentalContracts::class) +@Throws(IOException::class) +inline fun Path.readAsMemory(position: Long = 0, size: Long = Files.size(this), block: Memory.() -> R): R { + contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } + + return FileChannel + .open(this, StandardOpenOption.READ) + .use { ByteBufferMemory(it.map(FileChannel.MapMode.READ_ONLY, position, size)).block() } +} diff --git a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/RandomChain.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/RandomChain.kt index 47fc6e4c5..fe5dc8c8c 100644 --- a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/RandomChain.kt +++ b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/RandomChain.kt @@ -1,6 +1,7 @@ package scientifik.kmath.prob import scientifik.kmath.chains.Chain +import kotlin.contracts.ExperimentalContracts /** * A possibly stateful chain producing random values. @@ -11,4 +12,4 @@ class RandomChain(val generator: RandomGenerator, private val gen: suspen override fun fork(): Chain = RandomChain(generator.fork(), gen) } -fun RandomGenerator.chain(gen: suspend RandomGenerator.() -> R): RandomChain = RandomChain(this, gen) \ No newline at end of file +fun RandomGenerator.chain(gen: suspend RandomGenerator.() -> R): RandomChain = RandomChain(this, gen) diff --git a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/SamplerAlgebra.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/SamplerAlgebra.kt index 3a60c0bda..7699df4ee 100644 --- a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/SamplerAlgebra.kt +++ b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/SamplerAlgebra.kt @@ -5,6 +5,7 @@ import scientifik.kmath.chains.ConstantChain import scientifik.kmath.chains.map import scientifik.kmath.chains.zip import scientifik.kmath.operations.Space +import scientifik.kmath.operations.invoke class BasicSampler(val chainBuilder: (RandomGenerator) -> Chain) : Sampler { override fun sample(generator: RandomGenerator): Chain = chainBuilder(generator) @@ -22,10 +23,10 @@ class SamplerSpace(val space: Space) : Space> { override val zero: Sampler = ConstantSampler(space.zero) override fun add(a: Sampler, b: Sampler): Sampler = BasicSampler { generator -> - a.sample(generator).zip(b.sample(generator)) { aValue, bValue -> space.run { aValue + bValue } } + a.sample(generator).zip(b.sample(generator)) { aValue, bValue -> space { aValue + bValue } } } override fun multiply(a: Sampler, k: Number): Sampler = BasicSampler { generator -> - a.sample(generator).map { space.run { it * k.toDouble() } } + a.sample(generator).map { space { it * k.toDouble() } } } } \ No newline at end of file diff --git a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/Statistic.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/Statistic.kt index 804aed089..0bc9c1565 100644 --- a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/Statistic.kt +++ b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/Statistic.kt @@ -29,8 +29,10 @@ interface Statistic { interface ComposableStatistic : Statistic { //compute statistic on a single block suspend fun computeIntermediate(data: Buffer): I + //Compose two blocks suspend fun composeIntermediate(first: I, second: I): I + //Transform block to result suspend fun toResult(intermediate: I): R @@ -58,26 +60,26 @@ private fun ComposableStatistic.flowIntermediate( fun ComposableStatistic.flow( flow: Flow>, dispatcher: CoroutineDispatcher = Dispatchers.Default -): Flow = flowIntermediate(flow,dispatcher).map(::toResult) +): Flow = flowIntermediate(flow, dispatcher).map(::toResult) /** * Arithmetic mean */ class Mean(val space: Space) : ComposableStatistic, T> { override suspend fun computeIntermediate(data: Buffer): Pair = - space.run { sum(data.asIterable()) } to data.size + space { sum(data.asIterable()) } to data.size override suspend fun composeIntermediate(first: Pair, second: Pair): Pair = - space.run { first.first + second.first } to (first.second + second.second) + space { first.first + second.first } to (first.second + second.second) override suspend fun toResult(intermediate: Pair): T = - space.run { intermediate.first / intermediate.second } + space { intermediate.first / intermediate.second } companion object { //TODO replace with optimized version which respects overflow - val real = Mean(RealField) - val int = Mean(IntRing) - val long = Mean(LongRing) + val real: Mean = Mean(RealField) + val int: Mean = Mean(IntRing) + val long: Mean = Mean(LongRing) } } @@ -85,11 +87,10 @@ class Mean(val space: Space) : ComposableStatistic, T> { * Non-composable median */ class Median(private val comparator: Comparator) : Statistic { - override suspend fun invoke(data: Buffer): T { - return data.asSequence().sortedWith(comparator).toList()[data.size / 2] //TODO check if this is correct - } + override suspend fun invoke(data: Buffer): T = + data.asSequence().sortedWith(comparator).toList()[data.size / 2] //TODO check if this is correct companion object { - val real = Median(Comparator { a: Double, b: Double -> a.compareTo(b) }) + val real: Median = Median(Comparator { a: Double, b: Double -> a.compareTo(b) }) } } \ No newline at end of file diff --git a/kmath-viktor/src/main/kotlin/scientifik/kmath/viktor/ViktorBuffer.kt b/kmath-viktor/src/main/kotlin/scientifik/kmath/viktor/ViktorBuffer.kt index 040eee951..e7bf173c3 100644 --- a/kmath-viktor/src/main/kotlin/scientifik/kmath/viktor/ViktorBuffer.kt +++ b/kmath-viktor/src/main/kotlin/scientifik/kmath/viktor/ViktorBuffer.kt @@ -16,5 +16,5 @@ inline class ViktorBuffer(val flatArray: F64FlatArray) : MutableBuffer { return ViktorBuffer(flatArray.copy().flatten()) } - override fun iterator(): Iterator = flatArray.data.iterator() + override operator fun iterator(): Iterator = flatArray.data.iterator() } \ No newline at end of file From f3e2b4a59725d1b76160b0fe21c8298efb61606f Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Sat, 22 Aug 2020 02:51:57 +0700 Subject: [PATCH 49/54] Remove import --- .../src/commonMain/kotlin/scientifik/kmath/prob/RandomChain.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/RandomChain.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/RandomChain.kt index fe5dc8c8c..49163c701 100644 --- a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/RandomChain.kt +++ b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/RandomChain.kt @@ -1,7 +1,6 @@ package scientifik.kmath.prob import scientifik.kmath.chains.Chain -import kotlin.contracts.ExperimentalContracts /** * A possibly stateful chain producing random values. From 1b8ddedfe39f2a26dcacdb0ff297a5b5fc97c6b8 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Sat, 22 Aug 2020 03:01:47 +0700 Subject: [PATCH 50/54] Add newlines --- .../commonMain/kotlin/scientifik/kmath/prob/SamplerAlgebra.kt | 2 +- .../src/commonMain/kotlin/scientifik/kmath/prob/Statistic.kt | 2 +- .../src/main/kotlin/scientifik/kmath/viktor/ViktorBuffer.kt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/SamplerAlgebra.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/SamplerAlgebra.kt index 7699df4ee..02f98439e 100644 --- a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/SamplerAlgebra.kt +++ b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/SamplerAlgebra.kt @@ -29,4 +29,4 @@ class SamplerSpace(val space: Space) : Space> { override fun multiply(a: Sampler, k: Number): Sampler = BasicSampler { generator -> a.sample(generator).map { space { it * k.toDouble() } } } -} \ No newline at end of file +} diff --git a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/Statistic.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/Statistic.kt index 0bc9c1565..c82d262bf 100644 --- a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/Statistic.kt +++ b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/Statistic.kt @@ -93,4 +93,4 @@ class Median(private val comparator: Comparator) : Statistic { companion object { val real: Median = Median(Comparator { a: Double, b: Double -> a.compareTo(b) }) } -} \ No newline at end of file +} diff --git a/kmath-viktor/src/main/kotlin/scientifik/kmath/viktor/ViktorBuffer.kt b/kmath-viktor/src/main/kotlin/scientifik/kmath/viktor/ViktorBuffer.kt index e7bf173c3..551b877a7 100644 --- a/kmath-viktor/src/main/kotlin/scientifik/kmath/viktor/ViktorBuffer.kt +++ b/kmath-viktor/src/main/kotlin/scientifik/kmath/viktor/ViktorBuffer.kt @@ -17,4 +17,4 @@ inline class ViktorBuffer(val flatArray: F64FlatArray) : MutableBuffer { } override operator fun iterator(): Iterator = flatArray.data.iterator() -} \ No newline at end of file +} From 40888f66d60c59c67bb96915a1e427ea325a38d7 Mon Sep 17 00:00:00 2001 From: Iaroslav Postovalov Date: Sat, 22 Aug 2020 16:49:21 +0700 Subject: [PATCH 51/54] Replace opt-in annotations with useExperimentalAnnotation for modules --- examples/build.gradle.kts | 6 ++++++ .../kotlin/scientifik/kmath/utils/utils.kt | 2 -- .../scientifik/kmath/structures/NDField.kt | 1 - .../kmath/structures/typeSafeDimensions.kt | 1 - kmath-ast/build.gradle.kts | 7 ++----- .../scientifik/kmath/ast/MstExpression.kt | 9 --------- .../kmath/asm/internal/codegenUtils.kt | 5 ----- kmath-commons/build.gradle.kts | 9 ++++----- .../kmath/commons/expressions/AutoDiffTest.kt | 2 -- kmath-core/build.gradle.kts | 5 ++--- .../scientifik/kmath/expressions/Builders.kt | 4 ---- .../scientifik/kmath/linear/FeaturedMatrix.kt | 1 - .../kotlin/scientifik/kmath/misc/AutoDiff.kt | 1 - .../scientifik/kmath/misc/cumulative.kt | 1 - .../scientifik/kmath/operations/BigInt.kt | 3 --- .../scientifik/kmath/operations/Complex.kt | 2 -- .../scientifik/kmath/structures/Buffers.kt | 1 - .../kmath/structures/ComplexNDField.kt | 2 -- .../kmath/structures/FlaggedBuffer.kt | 1 - .../kmath/structures/FloatBuffer.kt | 1 - .../scientifik/kmath/structures/IntBuffer.kt | 1 - .../scientifik/kmath/structures/LongBuffer.kt | 1 - .../kmath/structures/NDStructure.kt | 1 - .../scientifik/kmath/structures/RealBuffer.kt | 1 - .../kmath/structures/ShortBuffer.kt | 1 - kmath-coroutines/build.gradle.kts | 19 +++++++++++++------ .../kotlin/scientifik/kmath/chains/Chain.kt | 12 +++--------- .../scientifik/kmath/chains/flowExtra.kt | 1 - .../kmath/coroutines/coroutinesExtra.kt | 17 ++--------------- kmath-for-real/build.gradle.kts | 13 ++++--------- .../scientifik/kmath/real/realMatrix.kt | 1 - kmath-functions/build.gradle.kts | 11 +++-------- .../scientifik/kmath/functions/Polynomial.kt | 1 - kmath-memory/build.gradle.kts | 5 ++--- .../kotlin/scientifik/memory/Memory.kt | 3 --- .../scientifik/memory/ByteBufferMemory.kt | 1 - 36 files changed, 41 insertions(+), 112 deletions(-) diff --git a/examples/build.gradle.kts b/examples/build.gradle.kts index 9f842024d..f5a4d5831 100644 --- a/examples/build.gradle.kts +++ b/examples/build.gradle.kts @@ -56,6 +56,12 @@ benchmark { } } +kotlin.sourceSets.all { + with(languageSettings) { + useExperimentalAnnotation("kotlin.contracts.ExperimentalContracts") + useExperimentalAnnotation("kotlin.ExperimentalUnsignedTypes") + } +} tasks.withType { kotlinOptions { diff --git a/examples/src/benchmarks/kotlin/scientifik/kmath/utils/utils.kt b/examples/src/benchmarks/kotlin/scientifik/kmath/utils/utils.kt index 305f21d4f..3b0d56291 100644 --- a/examples/src/benchmarks/kotlin/scientifik/kmath/utils/utils.kt +++ b/examples/src/benchmarks/kotlin/scientifik/kmath/utils/utils.kt @@ -1,11 +1,9 @@ package scientifik.kmath.utils -import kotlin.contracts.ExperimentalContracts import kotlin.contracts.InvocationKind import kotlin.contracts.contract import kotlin.system.measureTimeMillis -@OptIn(ExperimentalContracts::class) internal inline fun measureAndPrint(title: String, block: () -> Unit) { contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } val time = measureTimeMillis(block) diff --git a/examples/src/main/kotlin/scientifik/kmath/structures/NDField.kt b/examples/src/main/kotlin/scientifik/kmath/structures/NDField.kt index 1ce74b2a3..1bc0ed7c8 100644 --- a/examples/src/main/kotlin/scientifik/kmath/structures/NDField.kt +++ b/examples/src/main/kotlin/scientifik/kmath/structures/NDField.kt @@ -8,7 +8,6 @@ import kotlin.contracts.InvocationKind import kotlin.contracts.contract import kotlin.system.measureTimeMillis -@OptIn(ExperimentalContracts::class) internal inline fun measureAndPrint(title: String, block: () -> Unit) { contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } val time = measureTimeMillis(block) diff --git a/examples/src/main/kotlin/scientifik/kmath/structures/typeSafeDimensions.kt b/examples/src/main/kotlin/scientifik/kmath/structures/typeSafeDimensions.kt index e627cecaa..5d323823a 100644 --- a/examples/src/main/kotlin/scientifik/kmath/structures/typeSafeDimensions.kt +++ b/examples/src/main/kotlin/scientifik/kmath/structures/typeSafeDimensions.kt @@ -16,7 +16,6 @@ fun DMatrixContext.simple() { object D5 : Dimension { - @OptIn(ExperimentalUnsignedTypes::class) override val dim: UInt = 5u } diff --git a/kmath-ast/build.gradle.kts b/kmath-ast/build.gradle.kts index d13a7712d..86b10bdc7 100644 --- a/kmath-ast/build.gradle.kts +++ b/kmath-ast/build.gradle.kts @@ -1,11 +1,8 @@ plugins { id("scientifik.mpp") } kotlin.sourceSets { -// all { -// languageSettings.apply{ -// enableLanguageFeature("NewInference") -// } -// } + all { languageSettings.useExperimentalAnnotation("kotlin.contracts.ExperimentalContracts") } + commonMain { dependencies { api(project(":kmath-core")) diff --git a/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MstExpression.kt b/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MstExpression.kt index 635dc940d..3cee33956 100644 --- a/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MstExpression.kt +++ b/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MstExpression.kt @@ -41,7 +41,6 @@ inline fun , E : Algebra> A.mst( /** * Builds [MstExpression] over [Space]. */ -@OptIn(ExperimentalContracts::class) inline fun Space.mstInSpace(block: MstSpace.() -> MST): MstExpression { contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } return MstExpression(this, MstSpace.block()) @@ -50,7 +49,6 @@ inline fun Space.mstInSpace(block: MstSpace.() -> MST): Mst /** * Builds [MstExpression] over [Ring]. */ -@OptIn(ExperimentalContracts::class) inline fun Ring.mstInRing(block: MstRing.() -> MST): MstExpression { contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } return MstExpression(this, MstRing.block()) @@ -59,7 +57,6 @@ inline fun Ring.mstInRing(block: MstRing.() -> MST): MstExp /** * Builds [MstExpression] over [Field]. */ -@OptIn(ExperimentalContracts::class) inline fun Field.mstInField(block: MstField.() -> MST): MstExpression { contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } return MstExpression(this, MstField.block()) @@ -68,7 +65,6 @@ inline fun Field.mstInField(block: MstField.() -> MST): Mst /** * Builds [MstExpression] over [ExtendedField]. */ -@OptIn(ExperimentalContracts::class) inline fun Field.mstInExtendedField(block: MstExtendedField.() -> MST): MstExpression { contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } return MstExpression(this, MstExtendedField.block()) @@ -77,7 +73,6 @@ inline fun Field.mstInExtendedField(block: MstExtendedField /** * Builds [MstExpression] over [FunctionalExpressionSpace]. */ -@OptIn(ExperimentalContracts::class) inline fun > FunctionalExpressionSpace.mstInSpace(block: MstSpace.() -> MST): MstExpression { contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } return algebra.mstInSpace(block) @@ -86,8 +81,6 @@ inline fun > FunctionalExpressionSpace.mstIn /** * Builds [MstExpression] over [FunctionalExpressionRing]. */ - -@OptIn(ExperimentalContracts::class) inline fun > FunctionalExpressionRing.mstInRing(block: MstRing.() -> MST): MstExpression { contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } return algebra.mstInRing(block) @@ -96,7 +89,6 @@ inline fun > FunctionalExpressionRing.mstInRi /** * Builds [MstExpression] over [FunctionalExpressionField]. */ -@OptIn(ExperimentalContracts::class) inline fun > FunctionalExpressionField.mstInField(block: MstField.() -> MST): MstExpression { contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } return algebra.mstInField(block) @@ -105,7 +97,6 @@ inline fun > FunctionalExpressionField.mstIn /** * Builds [MstExpression] over [FunctionalExpressionExtendedField]. */ -@OptIn(ExperimentalContracts::class) inline fun > FunctionalExpressionExtendedField.mstInExtendedField(block: MstExtendedField.() -> MST): MstExpression { contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } return algebra.mstInExtendedField(block) diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/codegenUtils.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/codegenUtils.kt index 97bfba7f2..6f51fe855 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/codegenUtils.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/codegenUtils.kt @@ -29,7 +29,6 @@ internal val KClass<*>.asm: Type /** * Returns singleton array with this value if the [predicate] is true, returns empty array otherwise. */ -@OptIn(ExperimentalContracts::class) internal inline fun T.wrapToArrayIf(predicate: (T) -> Boolean): Array { contract { callsInPlace(predicate, InvocationKind.EXACTLY_ONCE) } return if (predicate(this)) arrayOf(this) else emptyArray() @@ -43,7 +42,6 @@ private fun MethodVisitor.instructionAdapter(): InstructionAdapter = Instruction /** * Creates an [InstructionAdapter] from this [MethodVisitor] and applies [block] to it. */ -@OptIn(ExperimentalContracts::class) internal inline fun MethodVisitor.instructionAdapter(block: InstructionAdapter.() -> Unit): InstructionAdapter { contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } return instructionAdapter().apply(block) @@ -72,14 +70,12 @@ internal tailrec fun buildName(mst: MST, collision: Int = 0): String { return buildName(mst, collision + 1) } -@OptIn(ExperimentalContracts::class) @Suppress("FunctionName") internal inline fun ClassWriter(flags: Int, block: ClassWriter.() -> Unit): ClassWriter { contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } return ClassWriter(flags).apply(block) } -@OptIn(ExperimentalContracts::class) internal inline fun ClassWriter.visitField( access: Int, name: String, @@ -167,7 +163,6 @@ private fun AsmBuilder.tryInvokeSpecific( /** * Builds specialized algebra call with option to fallback to generic algebra operation accepting String. */ -@OptIn(ExperimentalContracts::class) internal inline fun AsmBuilder.buildAlgebraOperationCall( context: Algebra, name: String, diff --git a/kmath-commons/build.gradle.kts b/kmath-commons/build.gradle.kts index 5ce1b935a..63c832b7c 100644 --- a/kmath-commons/build.gradle.kts +++ b/kmath-commons/build.gradle.kts @@ -1,7 +1,4 @@ -plugins { - id("scientifik.jvm") -} - +plugins { id("scientifik.jvm") } description = "Commons math binding for kmath" dependencies { @@ -10,4 +7,6 @@ dependencies { api(project(":kmath-prob")) api(project(":kmath-functions")) api("org.apache.commons:commons-math3:3.6.1") -} \ No newline at end of file +} + +kotlin.sourceSets.all { languageSettings.useExperimentalAnnotation("kotlin.contracts.ExperimentalContracts") } diff --git a/kmath-commons/src/test/kotlin/scientifik/kmath/commons/expressions/AutoDiffTest.kt b/kmath-commons/src/test/kotlin/scientifik/kmath/commons/expressions/AutoDiffTest.kt index ae4bb755a..bbdcff2fc 100644 --- a/kmath-commons/src/test/kotlin/scientifik/kmath/commons/expressions/AutoDiffTest.kt +++ b/kmath-commons/src/test/kotlin/scientifik/kmath/commons/expressions/AutoDiffTest.kt @@ -1,13 +1,11 @@ package scientifik.kmath.commons.expressions import scientifik.kmath.expressions.invoke -import kotlin.contracts.ExperimentalContracts import kotlin.contracts.InvocationKind import kotlin.contracts.contract import kotlin.test.Test import kotlin.test.assertEquals -@OptIn(ExperimentalContracts::class) inline fun diff(order: Int, vararg parameters: Pair, block: DerivativeStructureField.() -> R): R { contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } return DerivativeStructureField(order, mapOf(*parameters)).run(block) diff --git a/kmath-core/build.gradle.kts b/kmath-core/build.gradle.kts index bea0fbf42..7f9922de4 100644 --- a/kmath-core/build.gradle.kts +++ b/kmath-core/build.gradle.kts @@ -1,7 +1,6 @@ plugins { id("scientifik.mpp") } kotlin.sourceSets { - commonMain { - dependencies { api(project(":kmath-memory")) } - } + all { languageSettings.useExperimentalAnnotation("kotlin.contracts.ExperimentalContracts") } + commonMain { dependencies { api(project(":kmath-memory")) } } } diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/Builders.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/Builders.kt index 3bbc86ce5..8d0b82a89 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/Builders.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/Builders.kt @@ -11,7 +11,6 @@ import kotlin.contracts.contract /** * Creates a functional expression with this [Space]. */ -@OptIn(ExperimentalContracts::class) inline fun Space.spaceExpression(block: FunctionalExpressionSpace>.() -> Expression): Expression { contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } return FunctionalExpressionSpace(this).block() @@ -20,7 +19,6 @@ inline fun Space.spaceExpression(block: FunctionalExpressionSpace Ring.ringExpression(block: FunctionalExpressionRing>.() -> Expression): Expression { contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } return FunctionalExpressionRing(this).block() @@ -29,7 +27,6 @@ inline fun Ring.ringExpression(block: FunctionalExpressionRing /** * Creates a functional expression with this [Field]. */ -@OptIn(ExperimentalContracts::class) inline fun Field.fieldExpression(block: FunctionalExpressionField>.() -> Expression): Expression { contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } return FunctionalExpressionField(this).block() @@ -38,7 +35,6 @@ inline fun Field.fieldExpression(block: FunctionalExpressionField ExtendedField.extendedFieldExpression(block: FunctionalExpressionExtendedField>.() -> Expression): Expression { contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } return FunctionalExpressionExtendedField(this).block() diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/FeaturedMatrix.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/FeaturedMatrix.kt index 39256f4ac..9b60bf719 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/FeaturedMatrix.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/FeaturedMatrix.kt @@ -28,7 +28,6 @@ interface FeaturedMatrix : Matrix { companion object } -@OptIn(ExperimentalContracts::class) inline fun Structure2D.Companion.real(rows: Int, columns: Int, initializer: (Int, Int) -> Double): Matrix { contract { callsInPlace(initializer) } return MatrixContext.real.produce(rows, columns, initializer) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/misc/AutoDiff.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/misc/AutoDiff.kt index 1609f063a..be222783e 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/misc/AutoDiff.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/misc/AutoDiff.kt @@ -55,7 +55,6 @@ class DerivationResult( * assertEquals(9.0, x.d) // dy/dx * ``` */ -@OptIn(ExperimentalContracts::class) inline fun > F.deriv(body: AutoDiffField.() -> Variable): DerivationResult { contract { callsInPlace(body, InvocationKind.EXACTLY_ONCE) } diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/misc/cumulative.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/misc/cumulative.kt index dc7a4bf1f..e11adc135 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/misc/cumulative.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/misc/cumulative.kt @@ -13,7 +13,6 @@ import kotlin.jvm.JvmName * @param R the type of resulting iterable. * @param initial lazy evaluated. */ -@OptIn(ExperimentalContracts::class) inline fun Iterator.cumulative(initial: R, crossinline operation: (R, T) -> R): Iterator { contract { callsInPlace(operation) } diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/BigInt.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/BigInt.kt index d0c4989d8..0eed7132e 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/BigInt.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/BigInt.kt @@ -10,7 +10,6 @@ import kotlin.math.max import kotlin.math.min import kotlin.math.sign - typealias Magnitude = UIntArray typealias TBase = ULong @@ -487,13 +486,11 @@ fun String.parseBigInteger(): BigInt? { return res * sign } -@OptIn(ExperimentalContracts::class) inline fun Buffer.Companion.bigInt(size: Int, initializer: (Int) -> BigInt): Buffer { contract { callsInPlace(initializer) } return boxing(size, initializer) } -@OptIn(ExperimentalContracts::class) inline fun MutableBuffer.Companion.bigInt(size: Int, initializer: (Int) -> BigInt): MutableBuffer { contract { callsInPlace(initializer) } return boxing(size, initializer) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Complex.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Complex.kt index e80e6983a..dcfd97d1a 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Complex.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Complex.kt @@ -198,13 +198,11 @@ data class Complex(val re: Double, val im: Double) : FieldElement Complex): Buffer { contract { callsInPlace(init) } return MemoryBuffer.create(Complex, size, init) } -@OptIn(ExperimentalContracts::class) inline fun MutableBuffer.Companion.complex(size: Int, crossinline init: (Int) -> Complex): Buffer { contract { callsInPlace(init) } return MemoryBuffer.create(Complex, size, init) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/Buffers.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/Buffers.kt index e065f4990..4afaa63ab 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/Buffers.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/Buffers.kt @@ -167,7 +167,6 @@ fun List.asBuffer(): ListBuffer = ListBuffer(this) * The function [init] is called for each array element sequentially starting from the first one. * It should return the value for an array element given its index. */ -@OptIn(ExperimentalContracts::class) inline fun ListBuffer(size: Int, init: (Int) -> T): ListBuffer { contract { callsInPlace(init) } return List(size, init).asBuffer() diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ComplexNDField.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ComplexNDField.kt index a4855ca7c..2c6e3a5c7 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ComplexNDField.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ComplexNDField.kt @@ -112,7 +112,6 @@ inline fun ComplexNDElement.mapIndexed(crossinline transform: ComplexField.(inde /** * Map one [ComplexNDElement] using function without indices. */ -@OptIn(ExperimentalContracts::class) inline fun ComplexNDElement.map(crossinline transform: ComplexField.(Complex) -> Complex): ComplexNDElement { contract { callsInPlace(transform) } val buffer = Buffer.complex(strides.linearSize) { offset -> ComplexField.transform(buffer[offset]) } @@ -153,7 +152,6 @@ fun NDElement.Companion.complex(vararg shape: Int, initializer: ComplexField.(In /** * Produce a context for n-dimensional operations inside this real field */ -@OptIn(ExperimentalContracts::class) inline fun ComplexField.nd(vararg shape: Int, action: ComplexNDField.() -> R): R { contract { callsInPlace(action, InvocationKind.EXACTLY_ONCE) } return NDField.complex(*shape).action() diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/FlaggedBuffer.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/FlaggedBuffer.kt index b78ac0beb..9c32aa31b 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/FlaggedBuffer.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/FlaggedBuffer.kt @@ -66,7 +66,6 @@ class FlaggedRealBuffer(val values: DoubleArray, val flags: ByteArray) : Flagged }.iterator() } -@OptIn(ExperimentalContracts::class) inline fun FlaggedRealBuffer.forEachValid(block: (Double) -> Unit) { contract { callsInPlace(block) } diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/FloatBuffer.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/FloatBuffer.kt index 24822056b..9e974c644 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/FloatBuffer.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/FloatBuffer.kt @@ -30,7 +30,6 @@ inline class FloatBuffer(val array: FloatArray) : MutableBuffer { * The function [init] is called for each array element sequentially starting from the first one. * It should return the value for an buffer element given its index. */ -@OptIn(ExperimentalContracts::class) inline fun FloatBuffer(size: Int, init: (Int) -> Float): FloatBuffer { contract { callsInPlace(init) } return FloatBuffer(FloatArray(size) { init(it) }) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/IntBuffer.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/IntBuffer.kt index 229b62b48..95651c547 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/IntBuffer.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/IntBuffer.kt @@ -31,7 +31,6 @@ inline class IntBuffer(val array: IntArray) : MutableBuffer { * The function [init] is called for each array element sequentially starting from the first one. * It should return the value for an buffer element given its index. */ -@OptIn(ExperimentalContracts::class) inline fun IntBuffer(size: Int, init: (Int) -> Int): IntBuffer { contract { callsInPlace(init) } return IntBuffer(IntArray(size) { init(it) }) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/LongBuffer.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/LongBuffer.kt index cfedb5f35..a44109f8a 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/LongBuffer.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/LongBuffer.kt @@ -31,7 +31,6 @@ inline class LongBuffer(val array: LongArray) : MutableBuffer { * The function [init] is called for each array element sequentially starting from the first one. * It should return the value for an buffer element given its index. */ -@OptIn(ExperimentalContracts::class) inline fun LongBuffer(size: Int, init: (Int) -> Long): LongBuffer { contract { callsInPlace(init) } return LongBuffer(LongArray(size) { init(it) }) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/NDStructure.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/NDStructure.kt index a16caab68..f4eb93b9e 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/NDStructure.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/NDStructure.kt @@ -140,7 +140,6 @@ interface MutableNDStructure : NDStructure { operator fun set(index: IntArray, value: T) } -@OptIn(ExperimentalContracts::class) inline fun MutableNDStructure.mapInPlace(action: (IntArray, T) -> T) { contract { callsInPlace(action) } elements().forEach { (index, oldValue) -> this[index] = action(index, oldValue) } diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/RealBuffer.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/RealBuffer.kt index f897134db..cba8e9689 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/RealBuffer.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/RealBuffer.kt @@ -30,7 +30,6 @@ inline class RealBuffer(val array: DoubleArray) : MutableBuffer { * The function [init] is called for each array element sequentially starting from the first one. * It should return the value for an buffer element given its index. */ -@OptIn(ExperimentalContracts::class) inline fun RealBuffer(size: Int, init: (Int) -> Double): RealBuffer { contract { callsInPlace(init) } return RealBuffer(DoubleArray(size) { init(it) }) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ShortBuffer.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ShortBuffer.kt index 08d4a4376..9aa674177 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ShortBuffer.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ShortBuffer.kt @@ -30,7 +30,6 @@ inline class ShortBuffer(val array: ShortArray) : MutableBuffer { * The function [init] is called for each array element sequentially starting from the first one. * It should return the value for an buffer element given its index. */ -@OptIn(ExperimentalContracts::class) inline fun ShortBuffer(size: Int, init: (Int) -> Short): ShortBuffer { contract { callsInPlace(init) } return ShortBuffer(ShortArray(size) { init(it) }) diff --git a/kmath-coroutines/build.gradle.kts b/kmath-coroutines/build.gradle.kts index 373d9b8ac..4469a9ef6 100644 --- a/kmath-coroutines/build.gradle.kts +++ b/kmath-coroutines/build.gradle.kts @@ -4,20 +4,27 @@ plugins { } kotlin.sourceSets { + all { + with(languageSettings) { + useExperimentalAnnotation("kotlin.contracts.ExperimentalContracts") + useExperimentalAnnotation("kotlinx.coroutines.InternalCoroutinesApi") + useExperimentalAnnotation("kotlinx.coroutines.ExperimentalCoroutinesApi") + useExperimentalAnnotation("kotlinx.coroutines.FlowPreview") + } + } + commonMain { dependencies { api(project(":kmath-core")) api("org.jetbrains.kotlinx:kotlinx-coroutines-core-common:${Scientifik.coroutinesVersion}") } } + jvmMain { - dependencies { - api("org.jetbrains.kotlinx:kotlinx-coroutines-core:${Scientifik.coroutinesVersion}") - } + dependencies { api("org.jetbrains.kotlinx:kotlinx-coroutines-core:${Scientifik.coroutinesVersion}") } } + jsMain { - dependencies { - api("org.jetbrains.kotlinx:kotlinx-coroutines-core-js:${Scientifik.coroutinesVersion}") - } + dependencies { api("org.jetbrains.kotlinx:kotlinx-coroutines-core-js:${Scientifik.coroutinesVersion}") } } } diff --git a/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/Chain.kt b/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/Chain.kt index aac4f2534..f0ffd13cd 100644 --- a/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/Chain.kt +++ b/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/Chain.kt @@ -16,9 +16,9 @@ package scientifik.kmath.chains -import kotlinx.coroutines.InternalCoroutinesApi import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.FlowCollector +import kotlinx.coroutines.flow.flow import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock @@ -37,14 +37,8 @@ interface Chain : Flow { */ fun fork(): Chain - @OptIn(InternalCoroutinesApi::class) - override suspend fun collect(collector: FlowCollector) { - kotlinx.coroutines.flow.flow { - while (true) { - emit(next()) - } - }.collect(collector) - } + override suspend fun collect(collector: FlowCollector): Unit = + flow { while (true) emit(next()) }.collect(collector) companion object } diff --git a/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/flowExtra.kt b/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/flowExtra.kt index 6624722ce..5db660c39 100644 --- a/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/flowExtra.kt +++ b/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/flowExtra.kt @@ -9,7 +9,6 @@ import scientifik.kmath.operations.Space import scientifik.kmath.operations.SpaceOperations import scientifik.kmath.operations.invoke - @ExperimentalCoroutinesApi fun Flow.cumulativeSum(space: SpaceOperations): Flow = space { scanReduce { sum: T, element: T -> sum + element } diff --git a/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/coroutines/coroutinesExtra.kt b/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/coroutines/coroutinesExtra.kt index 8ee6c52ad..692f89589 100644 --- a/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/coroutines/coroutinesExtra.kt +++ b/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/coroutines/coroutinesExtra.kt @@ -3,7 +3,6 @@ package scientifik.kmath.coroutines import kotlinx.coroutines.* import kotlinx.coroutines.channels.produce import kotlinx.coroutines.flow.* -import kotlin.contracts.ExperimentalContracts import kotlin.contracts.contract val Dispatchers.Math: CoroutineDispatcher @@ -25,15 +24,11 @@ internal class LazyDeferred(val dispatcher: CoroutineDispatcher, val block: s } class AsyncFlow internal constructor(internal val deferredFlow: Flow>) : Flow { - @InternalCoroutinesApi override suspend fun collect(collector: FlowCollector) { - deferredFlow.collect { - collector.emit((it.await())) - } + deferredFlow.collect { collector.emit((it.await())) } } } -@FlowPreview fun Flow.async( dispatcher: CoroutineDispatcher = Dispatchers.Default, block: suspend CoroutineScope.(T) -> R @@ -44,7 +39,6 @@ fun Flow.async( return AsyncFlow(flow) } -@FlowPreview fun AsyncFlow.map(action: (T) -> R): AsyncFlow = AsyncFlow(deferredFlow.map { input -> //TODO add function composition @@ -54,10 +48,9 @@ fun AsyncFlow.map(action: (T) -> R): AsyncFlow = } }) -@ExperimentalCoroutinesApi -@FlowPreview suspend fun AsyncFlow.collect(concurrency: Int, collector: FlowCollector) { require(concurrency >= 1) { "Buffer size should be more than 1, but was $concurrency" } + coroutineScope { //Starting up to N deferred coroutines ahead of time val channel = produce(capacity = concurrency - 1) { @@ -83,9 +76,6 @@ suspend fun AsyncFlow.collect(concurrency: Int, collector: FlowCollector< } } -@OptIn(ExperimentalContracts::class) -@ExperimentalCoroutinesApi -@FlowPreview suspend inline fun AsyncFlow.collect(concurrency: Int, crossinline action: suspend (value: T) -> Unit) { contract { callsInPlace(action) } @@ -94,9 +84,6 @@ suspend inline fun AsyncFlow.collect(concurrency: Int, crossinline action }) } -@OptIn(ExperimentalContracts::class) -@ExperimentalCoroutinesApi -@FlowPreview inline fun Flow.mapParallel( dispatcher: CoroutineDispatcher = Dispatchers.Default, crossinline transform: suspend (T) -> R diff --git a/kmath-for-real/build.gradle.kts b/kmath-for-real/build.gradle.kts index a8a8975bc..46d2682f7 100644 --- a/kmath-for-real/build.gradle.kts +++ b/kmath-for-real/build.gradle.kts @@ -1,11 +1,6 @@ -plugins { - id("scientifik.mpp") -} +plugins { id("scientifik.mpp") } kotlin.sourceSets { - commonMain { - dependencies { - api(project(":kmath-core")) - } - } -} \ No newline at end of file + all { languageSettings.useExperimentalAnnotation("kotlin.contracts.ExperimentalContracts") } + commonMain { dependencies { api(project(":kmath-core")) } } +} diff --git a/kmath-for-real/src/commonMain/kotlin/scientifik/kmath/real/realMatrix.kt b/kmath-for-real/src/commonMain/kotlin/scientifik/kmath/real/realMatrix.kt index 5ccba90a9..3752fc3ca 100644 --- a/kmath-for-real/src/commonMain/kotlin/scientifik/kmath/real/realMatrix.kt +++ b/kmath-for-real/src/commonMain/kotlin/scientifik/kmath/real/realMatrix.kt @@ -120,7 +120,6 @@ operator fun Matrix.minus(other: Matrix): RealMatrix = * Operations on columns */ -@OptIn(ExperimentalContracts::class) inline fun Matrix.appendColumn(crossinline mapper: (Buffer) -> Double): Matrix { contract { callsInPlace(mapper) } diff --git a/kmath-functions/build.gradle.kts b/kmath-functions/build.gradle.kts index 4c158a32e..46d2682f7 100644 --- a/kmath-functions/build.gradle.kts +++ b/kmath-functions/build.gradle.kts @@ -1,11 +1,6 @@ -plugins { - id("scientifik.mpp") -} +plugins { id("scientifik.mpp") } kotlin.sourceSets { - commonMain { - dependencies { - api(project(":kmath-core")) - } - } + all { languageSettings.useExperimentalAnnotation("kotlin.contracts.ExperimentalContracts") } + commonMain { dependencies { api(project(":kmath-core")) } } } diff --git a/kmath-functions/src/commonMain/kotlin/scientifik/kmath/functions/Polynomial.kt b/kmath-functions/src/commonMain/kotlin/scientifik/kmath/functions/Polynomial.kt index 46b1d7c90..c4470ad27 100644 --- a/kmath-functions/src/commonMain/kotlin/scientifik/kmath/functions/Polynomial.kt +++ b/kmath-functions/src/commonMain/kotlin/scientifik/kmath/functions/Polynomial.kt @@ -71,7 +71,6 @@ class PolynomialSpace>(val ring: C) : Space> operator fun Polynomial.invoke(arg: T): T = value(ring, arg) } -@OptIn(ExperimentalContracts::class) inline fun , R> C.polynomial(block: PolynomialSpace.() -> R): R { contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } return PolynomialSpace(this).block() diff --git a/kmath-memory/build.gradle.kts b/kmath-memory/build.gradle.kts index 1f34a4f17..44a5ae24d 100644 --- a/kmath-memory/build.gradle.kts +++ b/kmath-memory/build.gradle.kts @@ -1,3 +1,2 @@ -plugins { - id("scientifik.mpp") -} +plugins { id("scientifik.mpp") } +kotlin.sourceSets.all { languageSettings.useExperimentalAnnotation("kotlin.contracts.ExperimentalContracts") } diff --git a/kmath-memory/src/commonMain/kotlin/scientifik/memory/Memory.kt b/kmath-memory/src/commonMain/kotlin/scientifik/memory/Memory.kt index 9e6ca9bde..daae0c3d2 100644 --- a/kmath-memory/src/commonMain/kotlin/scientifik/memory/Memory.kt +++ b/kmath-memory/src/commonMain/kotlin/scientifik/memory/Memory.kt @@ -1,6 +1,5 @@ package scientifik.memory -import kotlin.contracts.ExperimentalContracts import kotlin.contracts.InvocationKind import kotlin.contracts.contract @@ -84,7 +83,6 @@ interface MemoryReader { /** * Uses the memory for read then releases the reader. */ -@OptIn(ExperimentalContracts::class) inline fun Memory.read(block: MemoryReader.() -> Unit) { contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } reader().apply(block).release() @@ -138,7 +136,6 @@ interface MemoryWriter { /** * Uses the memory for write then releases the writer. */ -@OptIn(ExperimentalContracts::class) inline fun Memory.write(block: MemoryWriter.() -> Unit) { contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } writer().apply(block).release() diff --git a/kmath-memory/src/jvmMain/kotlin/scientifik/memory/ByteBufferMemory.kt b/kmath-memory/src/jvmMain/kotlin/scientifik/memory/ByteBufferMemory.kt index 73ad7deec..f4967bf5c 100644 --- a/kmath-memory/src/jvmMain/kotlin/scientifik/memory/ByteBufferMemory.kt +++ b/kmath-memory/src/jvmMain/kotlin/scientifik/memory/ByteBufferMemory.kt @@ -117,7 +117,6 @@ fun ByteBuffer.asMemory(startOffset: Int = 0, size: Int = limit()): Memory = /** * Uses direct memory-mapped buffer from file to read something and close it afterwards. */ -@OptIn(ExperimentalContracts::class) @Throws(IOException::class) inline fun Path.readAsMemory(position: Long = 0, size: Long = Files.size(this), block: Memory.() -> R): R { contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } From 1193349b200e2cd3b8d0c7fad2b5644075d94374 Mon Sep 17 00:00:00 2001 From: Iaroslav Postovalov Date: Thu, 27 Aug 2020 18:21:30 +0700 Subject: [PATCH 52/54] Rework Memory.read method to have more convenient calls with result --- .../src/commonMain/kotlin/scientifik/memory/Memory.kt | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/kmath-memory/src/commonMain/kotlin/scientifik/memory/Memory.kt b/kmath-memory/src/commonMain/kotlin/scientifik/memory/Memory.kt index daae0c3d2..177c6b46b 100644 --- a/kmath-memory/src/commonMain/kotlin/scientifik/memory/Memory.kt +++ b/kmath-memory/src/commonMain/kotlin/scientifik/memory/Memory.kt @@ -83,9 +83,12 @@ interface MemoryReader { /** * Uses the memory for read then releases the reader. */ -inline fun Memory.read(block: MemoryReader.() -> Unit) { +inline fun Memory.read(block: MemoryReader.() -> R): R { contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } - reader().apply(block).release() + val reader = reader() + val result = reader.block() + reader.release() + return result } /** From 924a1d1f382d221b9f3321f26478ef6d79dae448 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sat, 29 Aug 2020 10:39:04 +0300 Subject: [PATCH 53/54] Add publications --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index a081ceedd..24a7d7a4a 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,11 @@ Bintray-dev: [ ![Download](https://api.bintray.com/packages/mipt-npm/dev/kmat Could be pronounced as `key-math`. The Kotlin MATHematics library is intended as a Kotlin-based analog to Python's `numpy` library. In contrast to `numpy` and `scipy` it is modular and has a lightweight core. +## Publications +* [A conceptual article about context-oriented design](https://proandroiddev.com/an-introduction-context-oriented-programming-in-kotlin-2e79d316b0a2) +* [Another article about context-oriented design](https://proandroiddev.com/diving-deeper-into-context-oriented-programming-in-kotlin-3ecb4ec38814) +* [ACAT 2019 conference paper](https://aip.scitation.org/doi/abs/10.1063/1.5130103) + # Goal * Provide a flexible and powerful API to work with mathematics abstractions in Kotlin-multiplatform (JVM and JS for now and Native in future). * Provide basic multiplatform implementations for those abstractions (without significant performance optimization). From 6db921e935a0235eff99ae93576ca42caafc6fe4 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sun, 6 Sep 2020 19:03:24 +0300 Subject: [PATCH 54/54] Update README.md --- kmath-core/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kmath-core/README.md b/kmath-core/README.md index 24e2c57d3..aed33a257 100644 --- a/kmath-core/README.md +++ b/kmath-core/README.md @@ -1,4 +1,4 @@ -# The Core Module (`kmath-ast`) +# The Core Module (`kmath-core`) The core features of KMath: