From 6329722131e10d3922b917ff856c5a0713cdca10 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Mon, 10 Aug 2020 01:30:32 +0700 Subject: [PATCH 1/5] 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 2/5] 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 3/5] 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 4/5] 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 5/5] 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