From 774b1123f729123e94461aee1ddc9517ba8d10df Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Mon, 8 Jun 2020 12:07:27 +0300 Subject: [PATCH 1/8] Minor change in grid builders --- .../kmath/linear/LUPDecomposition.kt | 6 +-- .../kotlin/scientifik/kmath/misc/Grids.kt | 44 ++++++++++++------- 2 files changed, 30 insertions(+), 20 deletions(-) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/LUPDecomposition.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/LUPDecomposition.kt index 87e0ef027..75d7da169 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/LUPDecomposition.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/LUPDecomposition.kt @@ -128,14 +128,14 @@ fun , F : Field> GenericMatrixContext.lup( luRow[col] = sum // maintain best permutation choice - if (abs(sum) > largest) { - largest = abs(sum) + if (this@lup.abs(sum) > largest) { + largest = this@lup.abs(sum) max = row } } // Singularity check - if (checkSingular(abs(lu[max, col]))) { + if (checkSingular(this@lup.abs(lu[max, col]))) { error("The matrix is singular") } diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/misc/Grids.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/misc/Grids.kt index 90ce5da68..f040fb8d4 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/misc/Grids.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/misc/Grids.kt @@ -1,5 +1,7 @@ package scientifik.kmath.misc +import kotlin.math.abs + /** * Convert double range to sequence. * @@ -8,28 +10,36 @@ package scientifik.kmath.misc * * If step is negative, the same goes from upper boundary downwards */ -fun ClosedFloatingPointRange.toSequence(step: Double): Sequence = - when { - step == 0.0 -> error("Zero step in double progression") - step > 0 -> sequence { - var current = start - while (current <= endInclusive) { - yield(current) - current += step - } - } - else -> sequence { - var current = endInclusive - while (current >= start) { - yield(current) - current += step - } - } +fun ClosedFloatingPointRange.toSequenceWithStep(step: Double): Sequence = when { + step == 0.0 -> error("Zero step in double progression") + step > 0 -> sequence { + var current = start + while (current <= endInclusive) { + yield(current) + current += step } + } + else -> sequence { + var current = endInclusive + while (current >= start) { + yield(current) + current += step + } + } +} + +/** + * Convert double range to sequence with the fixed number of points + */ +fun ClosedFloatingPointRange.toSequenceWithPoints(numPoints: Int): Sequence { + require(numPoints > 1) { "The number of points should be more than 2" } + return toSequenceWithStep(abs(endInclusive - start) / (numPoints - 1)) +} /** * Convert double range to array of evenly spaced doubles, where the size of array equals [numPoints] */ +@Deprecated("Replace by 'toSequenceWithPoints'") fun ClosedFloatingPointRange.toGrid(numPoints: Int): DoubleArray { if (numPoints < 2) error("Can't create generic grid with less than two points") return DoubleArray(numPoints) { i -> start + (endInclusive - start) / (numPoints - 1) * i } From f46615d3bc3e036bd33d59912ae8e3a82f6e1b9b Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Fri, 12 Jun 2020 08:43:47 +0300 Subject: [PATCH 2/8] Left and right-side operations in Algebra --- .../kmath/expressions/MathSyntaxTree.kt | 61 +++++++++++++++++++ .../kmath/expressions/SyntaxTreeNode.kt | 52 ---------------- .../scientifik/kmath/operations/Algebra.kt | 16 ++++- .../kmath/operations/NumberAlgebra.kt | 28 ++++++++- .../kmath/operations/OptionalOperations.kt | 26 +++++--- 5 files changed, 120 insertions(+), 63 deletions(-) create mode 100644 kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/MathSyntaxTree.kt delete mode 100644 kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/SyntaxTreeNode.kt diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/MathSyntaxTree.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/MathSyntaxTree.kt new file mode 100644 index 000000000..fbc055f80 --- /dev/null +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/MathSyntaxTree.kt @@ -0,0 +1,61 @@ +package scientifik.kmath.expressions + +import scientifik.kmath.operations.NumericAlgebra +import scientifik.kmath.operations.RealField + +/** + * A syntax tree node for mathematical expressions + */ +sealed class MathSyntaxTree + +/** + * A node containing unparsed string + */ +data class SingularNode(val value: String) : MathSyntaxTree() + +/** + * A node containing a number + */ +data class NumberNode(val value: Number) : MathSyntaxTree() + +/** + * A node containing an unary operation + */ +data class UnaryNode(val operation: String, val value: MathSyntaxTree) : MathSyntaxTree() { + companion object { + const val ABS_OPERATION = "abs" + //TODO add operations + } +} + +/** + * A node containing binary operation + */ +data class BinaryNode(val operation: String, val left: MathSyntaxTree, val right: MathSyntaxTree) : MathSyntaxTree() { + companion object +} + +//TODO add a function with positional arguments + +//TODO add a function with named arguments + +fun NumericAlgebra.compile(node: MathSyntaxTree): T { + return when (node) { + is NumberNode -> number(node.value) + is SingularNode -> raw(node.value) + is UnaryNode -> unaryOperation(node.operation, compile(node.value)) + is BinaryNode -> when { + node.left is NumberNode && node.right is NumberNode -> { + val number = RealField.binaryOperation( + node.operation, + node.left.value.toDouble(), + node.right.value.toDouble() + ) + number(number) + } + node.left is NumberNode -> leftSideNumberOperation(node.operation, node.left.value, compile(node.right)) + node.right is NumberNode -> rightSideNumberOperation(node.operation, compile(node.left), node.right.value) + else -> binaryOperation(node.operation, compile(node.left), compile(node.right)) + } + } +} \ No newline at end of file diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/SyntaxTreeNode.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/SyntaxTreeNode.kt deleted file mode 100644 index e56165aad..000000000 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/SyntaxTreeNode.kt +++ /dev/null @@ -1,52 +0,0 @@ -package scientifik.kmath.expressions - -import scientifik.kmath.operations.NumericAlgebra - -/** - * A syntax tree node for mathematical expressions - */ -sealed class SyntaxTreeNode - -/** - * A node containing unparsed string - */ -data class SingularNode(val value: String) : SyntaxTreeNode() - -/** - * A node containing a number - */ -data class NumberNode(val value: Number) : SyntaxTreeNode() - -/** - * A node containing an unary operation - */ -data class UnaryNode(val operation: String, val value: SyntaxTreeNode) : SyntaxTreeNode() { - companion object { - const val ABS_OPERATION = "abs" - const val SIN_OPERATION = "sin" - const val COS_OPERATION = "cos" - const val EXP_OPERATION = "exp" - const val LN_OPERATION = "ln" - //TODO add operations - } -} - -/** - * A node containing binary operation - */ -data class BinaryNode(val operation: String, val left: SyntaxTreeNode, val right: SyntaxTreeNode) : SyntaxTreeNode() { - companion object -} - -//TODO add a function with positional arguments - -//TODO add a function with named arguments - -fun NumericAlgebra.compile(node: SyntaxTreeNode): T{ - return when (node) { - is NumberNode -> number(node.value) - is SingularNode -> raw(node.value) - is UnaryNode -> unaryOperation(node.operation, compile(node.value)) - is BinaryNode -> binaryOperation(node.operation, compile(node.left), compile(node.right)) - } -} \ No newline at end of file diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Algebra.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Algebra.kt index c9fbc1c8b..166287ec7 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Algebra.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Algebra.kt @@ -31,6 +31,12 @@ interface NumericAlgebra : Algebra { * Wrap a number */ fun number(value: Number): T + + fun leftSideNumberOperation(operation: String, left: Number, right: T): T = + binaryOperation(operation, number(left), right) + + fun rightSideNumberOperation(operation: String, left: T, right: Number): T = + leftSideNumberOperation(operation, right, left) } /** @@ -128,8 +134,14 @@ interface Ring : Space, RingOperations, NumericAlgebra { override fun number(value: Number): T = one * value.toDouble() - // those operators are blocked by type conflict in RealField - // operator fun T.plus(b: Number) = this.plus(b * one) + override fun leftSideNumberOperation(operation: String, left: Number, right: T): T = when (operation) { + RingOperations.TIMES_OPERATION -> left * right + else -> super.leftSideNumberOperation(operation, left, right) + } + + //TODO those operators are blocked by type conflict in RealField + +// operator fun T.plus(b: Number) = this.plus(b * one) // operator fun Number.plus(b: T) = b + this // // operator fun T.minus(b: Number) = this.minus(b * one) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/NumberAlgebra.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/NumberAlgebra.kt index 9639e4c28..e844d404e 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/NumberAlgebra.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/NumberAlgebra.kt @@ -10,9 +10,30 @@ interface ExtendedFieldOperations : FieldOperations, TrigonometricOperations, PowerOperations, - ExponentialOperations + ExponentialOperations { -interface ExtendedField : ExtendedFieldOperations, Field + override fun tan(arg: T): T = sin(arg) / cos(arg) + + override fun unaryOperation(operation: String, arg: T): T = when (operation) { + TrigonometricOperations.COS_OPERATION -> cos(arg) + TrigonometricOperations.SIN_OPERATION -> sin(arg) + PowerOperations.SQRT_OPERATION -> sqrt(arg) + ExponentialOperations.EXP_OPERATION -> exp(arg) + ExponentialOperations.LN_OPERATION -> ln(arg) + else -> super.unaryOperation(operation, arg) + } + +} + +interface ExtendedField : ExtendedFieldOperations, Field { + override fun rightSideNumberOperation(operation: String, left: T, right: Number): T { + return when (operation) { + PowerOperations.POW_OPERATION -> power(left, right) + else -> super.rightSideNumberOperation(operation, left, right) + } + + } +} /** * Real field element wrapping double. @@ -44,6 +65,7 @@ object RealField : ExtendedField, Norm { override inline fun sin(arg: Double) = kotlin.math.sin(arg) override inline fun cos(arg: Double) = kotlin.math.cos(arg) + override inline fun tan(arg: Double) = kotlin.math.tan(arg) override inline fun power(arg: Double, pow: Number) = arg.kpow(pow.toDouble()) @@ -76,6 +98,8 @@ object FloatField : ExtendedField, Norm { override inline fun sin(arg: Float) = kotlin.math.sin(arg) override inline fun cos(arg: Float) = kotlin.math.cos(arg) + override inline fun tan(arg: Float): Float = kotlin.math.tan(arg) + override inline fun power(arg: Float, pow: Number) = arg.pow(pow.toFloat()) override inline fun exp(arg: Float) = kotlin.math.exp(arg) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/OptionalOperations.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/OptionalOperations.kt index bd83932e7..1b58b7254 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/OptionalOperations.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/OptionalOperations.kt @@ -10,30 +10,37 @@ package scientifik.kmath.operations * It also allows to override behavior for optional operations * */ -interface TrigonometricOperations : FieldOperations { +interface TrigonometricOperations { fun sin(arg: T): T fun cos(arg: T): T - fun tg(arg: T): T = sin(arg) / cos(arg) + fun tan(arg: T): T - fun ctg(arg: T): T = cos(arg) / sin(arg) + companion object { + const val SIN_OPERATION = "sin" + const val COS_OPERATION = "cos" + } } fun >> sin(arg: T): T = arg.context.sin(arg) fun >> cos(arg: T): T = arg.context.cos(arg) -fun >> tg(arg: T): T = arg.context.tg(arg) -fun >> ctg(arg: T): T = arg.context.ctg(arg) +fun >> tan(arg: T): T = arg.context.tan(arg) /* Power and roots */ /** * A context extension to include power operations like square roots, etc */ -interface PowerOperations : Algebra { +interface PowerOperations { fun power(arg: T, pow: Number): T fun sqrt(arg: T) = power(arg, 0.5) infix fun T.pow(pow: Number) = power(this, pow) + + companion object { + const val POW_OPERATION = "pow" + const val SQRT_OPERATION = "sqrt" + } } infix fun >> T.pow(power: Double): T = context.power(this, power) @@ -42,9 +49,14 @@ fun >> sqr(arg: T): T = arg pow 2.0 /* Exponential */ -interface ExponentialOperations: Algebra { +interface ExponentialOperations { fun exp(arg: T): T fun ln(arg: T): T + + companion object { + const val EXP_OPERATION = "exp" + const val LN_OPERATION = "ln" + } } fun >> exp(arg: T): T = arg.context.exp(arg) From 5e92d85c4676c27c45fbba43d58a12569a80a0a7 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Fri, 12 Jun 2020 10:40:59 +0300 Subject: [PATCH 3/8] Separate ast module --- kmath-ast/build.gradle.kts | 16 +++++++++++++ .../scientifik/kmath/ast}/MathSyntaxTree.kt | 2 +- .../kotlin/scientifik/kmath/ast/asm.kt | 23 +++++++++++++++++++ .../commons/expressions/DiffExpression.kt | 4 ++-- .../kmath/expressions/Expression.kt | 2 +- .../expressions/functionalExpressions.kt | 13 +++++++---- .../scientifik/kmath/operations/bigNumbers.kt | 7 +++++- settings.gradle.kts | 1 + 8 files changed, 58 insertions(+), 10 deletions(-) create mode 100644 kmath-ast/build.gradle.kts rename {kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions => kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast}/MathSyntaxTree.kt (98%) create mode 100644 kmath-ast/src/jvmMain/kotlin/scientifik/kmath/ast/asm.kt diff --git a/kmath-ast/build.gradle.kts b/kmath-ast/build.gradle.kts new file mode 100644 index 000000000..f3d37bf65 --- /dev/null +++ b/kmath-ast/build.gradle.kts @@ -0,0 +1,16 @@ +plugins { + id("scientifik.mpp") +} + +repositories{ + maven("https://dl.bintray.com/hotkeytlt/maven") +} + +kotlin.sourceSets { + commonMain { + dependencies { + api(project(":kmath-core")) + implementation("com.github.h0tk3y.betterParse:better-parse-multiplatform:0.4.0-alpha3") + } + } +} \ No newline at end of file diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/MathSyntaxTree.kt b/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MathSyntaxTree.kt similarity index 98% rename from kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/MathSyntaxTree.kt rename to kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MathSyntaxTree.kt index fbc055f80..b43ef705d 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/MathSyntaxTree.kt +++ b/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MathSyntaxTree.kt @@ -1,4 +1,4 @@ -package scientifik.kmath.expressions +package scientifik.kmath.ast import scientifik.kmath.operations.NumericAlgebra import scientifik.kmath.operations.RealField diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/ast/asm.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/ast/asm.kt new file mode 100644 index 000000000..6c4325859 --- /dev/null +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/ast/asm.kt @@ -0,0 +1,23 @@ +package scientifik.kmath.ast + +import scientifik.kmath.expressions.Expression +import scientifik.kmath.operations.Algebra +import scientifik.kmath.operations.NumericAlgebra + +//TODO stubs for asm generation + +interface AsmExpression + +interface AsmExpressionAlgebra> : NumericAlgebra> { + val algebra: A +} + +fun AsmExpression.compile(): Expression = TODO() + +//TODO add converter for functional expressions + +inline fun > A.asm( + block: AsmExpressionAlgebra.() -> AsmExpression +): Expression = TODO() + +inline fun > A.asm(ast: MathSyntaxTree): Expression = asm { compile(ast) } \ No newline at end of file diff --git a/kmath-commons/src/main/kotlin/scientifik/kmath/commons/expressions/DiffExpression.kt b/kmath-commons/src/main/kotlin/scientifik/kmath/commons/expressions/DiffExpression.kt index 8c19395d3..f6f61e08a 100644 --- a/kmath-commons/src/main/kotlin/scientifik/kmath/commons/expressions/DiffExpression.kt +++ b/kmath-commons/src/main/kotlin/scientifik/kmath/commons/expressions/DiffExpression.kt @@ -2,7 +2,7 @@ package scientifik.kmath.commons.expressions import org.apache.commons.math3.analysis.differentiation.DerivativeStructure import scientifik.kmath.expressions.Expression -import scientifik.kmath.expressions.ExpressionContext +import scientifik.kmath.expressions.ExpressionAlgebra import scientifik.kmath.operations.ExtendedField import scientifik.kmath.operations.Field import kotlin.properties.ReadOnlyProperty @@ -113,7 +113,7 @@ fun DiffExpression.derivative(name: String) = derivative(name to 1) /** * A context for [DiffExpression] (not to be confused with [DerivativeStructure]) */ -object DiffExpressionContext : ExpressionContext, Field { +object DiffExpressionAlgebra : ExpressionAlgebra, Field { override fun variable(name: String, default: Double?) = DiffExpression { variable(name, default?.const()) } diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/Expression.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/Expression.kt index aba7357e8..df5c981f5 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/Expression.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/Expression.kt @@ -14,7 +14,7 @@ operator fun Expression.invoke(vararg pairs: Pair): T = invoke /** * A context for expression construction */ -interface ExpressionContext : Algebra { +interface ExpressionAlgebra : Algebra { /** * Introduce a variable into expression context */ diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/functionalExpressions.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/functionalExpressions.kt index 4f38e3a71..c3861d256 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/functionalExpressions.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/functionalExpressions.kt @@ -21,8 +21,11 @@ internal class SumExpression( override fun invoke(arguments: Map): T = context.add(first.invoke(arguments), second.invoke(arguments)) } -internal class ProductExpression(val context: Ring, val first: Expression, val second: Expression) : - Expression { +internal class ProductExpression( + val context: Ring, + val first: Expression, + val second: Expression +) : Expression { override fun invoke(arguments: Map): T = context.multiply(first.invoke(arguments), second.invoke(arguments)) } @@ -39,7 +42,7 @@ internal class DivExpession(val context: Field, val expr: Expression, v open class FunctionalExpressionSpace( val space: Space -) : Space>, ExpressionContext> { +) : Space>, ExpressionAlgebra> { override val zero: Expression = ConstantExpression(space.zero) @@ -61,12 +64,12 @@ open class FunctionalExpressionSpace( open class FunctionalExpressionField( val field: Field -) : Field>, ExpressionContext>, FunctionalExpressionSpace(field) { +) : Field>, ExpressionAlgebra>, FunctionalExpressionSpace(field) { override val one: Expression get() = const(this.field.one) - fun const(value: Double): Expression = const(field.run { one*value}) + fun const(value: Double): Expression = const(field.run { one * value }) override fun multiply(a: Expression, b: Expression): Expression = ProductExpression(field, a, b) diff --git a/kmath-core/src/jvmMain/kotlin/scientifik/kmath/operations/bigNumbers.kt b/kmath-core/src/jvmMain/kotlin/scientifik/kmath/operations/bigNumbers.kt index 76ca199c5..e6f09c040 100644 --- a/kmath-core/src/jvmMain/kotlin/scientifik/kmath/operations/bigNumbers.kt +++ b/kmath-core/src/jvmMain/kotlin/scientifik/kmath/operations/bigNumbers.kt @@ -1,10 +1,12 @@ package scientifik.kmath.operations -import scientifik.kmath.structures.* import java.math.BigDecimal import java.math.BigInteger import java.math.MathContext +/** + * A field wrapper for Java [BigInteger] + */ object JBigIntegerField : Field { override val zero: BigInteger = BigInteger.ZERO override val one: BigInteger = BigInteger.ONE @@ -18,6 +20,9 @@ object JBigIntegerField : Field { override fun divide(a: BigInteger, b: BigInteger): BigInteger = a.div(b) } +/** + * A Field wrapper for Java [BigDecimal] + */ class JBigDecimalField(val mathContext: MathContext = MathContext.DECIMAL64) : Field { override val zero: BigDecimal = BigDecimal.ZERO override val one: BigDecimal = BigDecimal.ONE diff --git a/settings.gradle.kts b/settings.gradle.kts index 57173250b..465ecfca8 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -44,5 +44,6 @@ include( ":kmath-dimensions", ":kmath-for-real", ":kmath-geometry", + ":kmath-ast", ":examples" ) From 047af8c172278cdf077e3e9db5f86aff3387f515 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Fri, 12 Jun 2020 11:11:13 +0300 Subject: [PATCH 4/8] Fix ND extendend fields --- kmath-ast/build.gradle.kts | 2 +- .../kotlin/scientifik/kmath/operations/Complex.kt | 2 +- .../scientifik/kmath/structures/ExtendedNDField.kt | 9 ++------- 3 files changed, 4 insertions(+), 9 deletions(-) diff --git a/kmath-ast/build.gradle.kts b/kmath-ast/build.gradle.kts index f3d37bf65..f17809381 100644 --- a/kmath-ast/build.gradle.kts +++ b/kmath-ast/build.gradle.kts @@ -10,7 +10,7 @@ kotlin.sourceSets { commonMain { dependencies { api(project(":kmath-core")) - implementation("com.github.h0tk3y.betterParse:better-parse-multiplatform:0.4.0-alpha3") + implementation("com.github.h0tk3y.betterParse:better-parse-multiplatform:0.4.0-alpha-3") } } } \ No newline at end of file diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Complex.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Complex.kt index 6c529f55e..e33e4078e 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Complex.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Complex.kt @@ -11,7 +11,7 @@ import kotlin.math.* /** * A field for complex numbers */ -object ComplexField : ExtendedFieldOperations, Field { +object ComplexField : ExtendedField { override val zero: Complex = Complex(0.0, 0.0) override val one: Complex = Complex(1.0, 0.0) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ExtendedNDField.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ExtendedNDField.kt index 3437644ff..776cff880 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ExtendedNDField.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ExtendedNDField.kt @@ -1,13 +1,8 @@ package scientifik.kmath.structures -import scientifik.kmath.operations.* +import scientifik.kmath.operations.ExtendedField -interface ExtendedNDField> : - NDField, - TrigonometricOperations, - PowerOperations, - ExponentialOperations - where F : ExtendedFieldOperations, F : Field +interface ExtendedNDField, N : NDStructure> : NDField, ExtendedField ///** From 2751cee9267b95a7370cba9e0565f56c3b29d7cf Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Fri, 12 Jun 2020 16:56:58 +0300 Subject: [PATCH 5/8] MST expression --- kmath-ast/build.gradle.kts | 5 ++ .../kotlin/scientifik/kmath/ast/MST.kt | 62 +++++++++++++++++++ .../scientifik/kmath/ast/MSTExpression.kt | 19 ++++++ .../scientifik/kmath/ast/MathSyntaxTree.kt | 61 ------------------ .../kotlin/scientifik/kmath/ast/asm.kt | 2 +- .../kotlin/scientifik/kmath/ast/parser.kt | 56 +++++++++++++++++ .../kotlin/scietifik/kmath/ast/ParserTest.kt | 17 +++++ .../kmath/expressions/Expression.kt | 6 ++ .../expressions/functionalExpressions.kt | 17 ++++- .../scientifik/kmath/operations/Complex.kt | 6 ++ 10 files changed, 186 insertions(+), 65 deletions(-) create mode 100644 kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MST.kt create mode 100644 kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MSTExpression.kt delete mode 100644 kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MathSyntaxTree.kt create mode 100644 kmath-ast/src/jvmMain/kotlin/scientifik/kmath/ast/parser.kt create mode 100644 kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/ParserTest.kt diff --git a/kmath-ast/build.gradle.kts b/kmath-ast/build.gradle.kts index f17809381..88540e7b8 100644 --- a/kmath-ast/build.gradle.kts +++ b/kmath-ast/build.gradle.kts @@ -13,4 +13,9 @@ kotlin.sourceSets { implementation("com.github.h0tk3y.betterParse:better-parse-multiplatform:0.4.0-alpha-3") } } + jvmMain{ + dependencies{ + implementation("com.github.h0tk3y.betterParse:better-parse-jvm:0.4.0-alpha-3") + } + } } \ No newline at end of file diff --git a/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MST.kt b/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MST.kt new file mode 100644 index 000000000..eba2c3343 --- /dev/null +++ b/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MST.kt @@ -0,0 +1,62 @@ +package scientifik.kmath.ast + +import scientifik.kmath.operations.NumericAlgebra +import scientifik.kmath.operations.RealField + +/** + * A Mathematical Syntax Tree node for mathematical expressions + */ +sealed class MST { + + /** + * A node containing unparsed string + */ + data class Singular(val value: String) : MST() + + /** + * A node containing a number + */ + data class Numeric(val value: Number) : MST() + + /** + * A node containing an unary operation + */ + data class Unary(val operation: String, val value: MST) : MST() { + companion object { + const val ABS_OPERATION = "abs" + //TODO add operations + } + } + + /** + * A node containing binary operation + */ + data class Binary(val operation: String, val left: MST, val right: MST) : MST() { + companion object + } +} + +//TODO add a function with positional arguments + +//TODO add a function with named arguments + +fun NumericAlgebra.evaluate(node: MST): T { + return when (node) { + is MST.Numeric -> number(node.value) + is MST.Singular -> raw(node.value) + is MST.Unary -> unaryOperation(node.operation, evaluate(node.value)) + is MST.Binary -> when { + node.left is MST.Numeric && node.right is MST.Numeric -> { + val number = RealField.binaryOperation( + node.operation, + node.left.value.toDouble(), + node.right.value.toDouble() + ) + number(number) + } + node.left is MST.Numeric -> leftSideNumberOperation(node.operation, node.left.value, evaluate(node.right)) + node.right is MST.Numeric -> rightSideNumberOperation(node.operation, evaluate(node.left), node.right.value) + else -> binaryOperation(node.operation, evaluate(node.left), evaluate(node.right)) + } + } +} \ No newline at end of file diff --git a/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MSTExpression.kt b/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MSTExpression.kt new file mode 100644 index 000000000..c0f124a35 --- /dev/null +++ b/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MSTExpression.kt @@ -0,0 +1,19 @@ +package scientifik.kmath.ast + +import scientifik.kmath.expressions.Expression +import scientifik.kmath.operations.NumericAlgebra + +/** + * The expression evaluates MST on-flight + */ +class MSTExpression(val algebra: NumericAlgebra, val mst: MST) : Expression { + + /** + * Substitute algebra raw value + */ + private inner class InnerAlgebra(val arguments: Map) : NumericAlgebra by algebra { + override fun raw(value: String): T = arguments[value] ?: super.raw(value) + } + + override fun invoke(arguments: Map): T = InnerAlgebra(arguments).evaluate(mst) +} \ No newline at end of file diff --git a/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MathSyntaxTree.kt b/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MathSyntaxTree.kt deleted file mode 100644 index b43ef705d..000000000 --- a/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MathSyntaxTree.kt +++ /dev/null @@ -1,61 +0,0 @@ -package scientifik.kmath.ast - -import scientifik.kmath.operations.NumericAlgebra -import scientifik.kmath.operations.RealField - -/** - * A syntax tree node for mathematical expressions - */ -sealed class MathSyntaxTree - -/** - * A node containing unparsed string - */ -data class SingularNode(val value: String) : MathSyntaxTree() - -/** - * A node containing a number - */ -data class NumberNode(val value: Number) : MathSyntaxTree() - -/** - * A node containing an unary operation - */ -data class UnaryNode(val operation: String, val value: MathSyntaxTree) : MathSyntaxTree() { - companion object { - const val ABS_OPERATION = "abs" - //TODO add operations - } -} - -/** - * A node containing binary operation - */ -data class BinaryNode(val operation: String, val left: MathSyntaxTree, val right: MathSyntaxTree) : MathSyntaxTree() { - companion object -} - -//TODO add a function with positional arguments - -//TODO add a function with named arguments - -fun NumericAlgebra.compile(node: MathSyntaxTree): T { - return when (node) { - is NumberNode -> number(node.value) - is SingularNode -> raw(node.value) - is UnaryNode -> unaryOperation(node.operation, compile(node.value)) - is BinaryNode -> when { - node.left is NumberNode && node.right is NumberNode -> { - val number = RealField.binaryOperation( - node.operation, - node.left.value.toDouble(), - node.right.value.toDouble() - ) - number(number) - } - node.left is NumberNode -> leftSideNumberOperation(node.operation, node.left.value, compile(node.right)) - node.right is NumberNode -> rightSideNumberOperation(node.operation, compile(node.left), node.right.value) - else -> binaryOperation(node.operation, compile(node.left), compile(node.right)) - } - } -} \ No newline at end of file diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/ast/asm.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/ast/asm.kt index 6c4325859..c01648fb0 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/ast/asm.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/ast/asm.kt @@ -20,4 +20,4 @@ inline fun > A.asm( block: AsmExpressionAlgebra.() -> AsmExpression ): Expression = TODO() -inline fun > A.asm(ast: MathSyntaxTree): Expression = asm { compile(ast) } \ No newline at end of file +inline fun > A.asm(ast: MST): Expression = asm { evaluate(ast) } \ No newline at end of file diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/ast/parser.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/ast/parser.kt new file mode 100644 index 000000000..dcab1c972 --- /dev/null +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/ast/parser.kt @@ -0,0 +1,56 @@ +package scientifik.kmath.ast + +import com.github.h0tk3y.betterParse.combinators.* +import com.github.h0tk3y.betterParse.grammar.Grammar +import com.github.h0tk3y.betterParse.grammar.parseToEnd +import com.github.h0tk3y.betterParse.grammar.parser +import com.github.h0tk3y.betterParse.grammar.tryParseToEnd +import com.github.h0tk3y.betterParse.parser.ParseResult +import com.github.h0tk3y.betterParse.parser.Parser +import scientifik.kmath.operations.FieldOperations +import scientifik.kmath.operations.PowerOperations +import scientifik.kmath.operations.RingOperations +import scientifik.kmath.operations.SpaceOperations + +private object ArithmeticsEvaluator : Grammar() { + val num by token("-?[\\d.]+(?:[eE]-?\\d+)?") + val lpar by token("\\(") + val rpar by token("\\)") + val mul by token("\\*") + val pow by token("\\^") + val div by token("/") + val minus by token("-") + val plus by token("\\+") + val ws by token("\\s+", ignore = true) + + val number: Parser by num use { MST.Numeric(text.toDouble()) } + + val term: Parser by number or + (skip(minus) and parser(this::term) map { MST.Unary(SpaceOperations.MINUS_OPERATION, it) }) or + (skip(lpar) and parser(this::rootParser) and skip(rpar)) + + val powChain by leftAssociative(term, pow) { a, _, b -> + MST.Binary(PowerOperations.POW_OPERATION, a, b) + } + + val divMulChain: Parser by leftAssociative(powChain, div or mul use { type }) { a, op, b -> + if (op == div) { + MST.Binary(FieldOperations.DIV_OPERATION, a, b) + } else { + MST.Binary(RingOperations.TIMES_OPERATION, a, b) + } + } + + val subSumChain: Parser by leftAssociative(divMulChain, plus or minus use { type }) { a, op, b -> + if (op == plus) { + MST.Binary(SpaceOperations.PLUS_OPERATION, a, b) + } else { + MST.Binary(SpaceOperations.MINUS_OPERATION, a, b) + } + } + + override val rootParser: Parser by subSumChain +} + +fun String.tryParseMath(): ParseResult = ArithmeticsEvaluator.tryParseToEnd(this) +fun String.parseMath(): MST = ArithmeticsEvaluator.parseToEnd(this) \ No newline at end of file diff --git a/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/ParserTest.kt b/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/ParserTest.kt new file mode 100644 index 000000000..6849d24b8 --- /dev/null +++ b/kmath-ast/src/jvmTest/kotlin/scietifik/kmath/ast/ParserTest.kt @@ -0,0 +1,17 @@ +package scietifik.kmath.ast + +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Test +import scientifik.kmath.ast.evaluate +import scientifik.kmath.ast.parseMath +import scientifik.kmath.operations.Complex +import scientifik.kmath.operations.ComplexField + +internal class ParserTest{ + @Test + fun parsedExpression(){ + val mst = "2+2*(2+2)".parseMath() + val res = ComplexField.evaluate(mst) + assertEquals(Complex(10.0,0.0), res) + } +} \ No newline at end of file diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/Expression.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/Expression.kt index df5c981f5..eaf3cd1d7 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/Expression.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/Expression.kt @@ -7,6 +7,12 @@ import scientifik.kmath.operations.Algebra */ interface Expression { operator fun invoke(arguments: Map): T + + companion object { + operator fun invoke(block: (Map) -> T): Expression = object : Expression { + override fun invoke(arguments: Map): T = block(arguments) + } + } } operator fun Expression.invoke(vararg pairs: Pair): T = invoke(mapOf(*pairs)) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/functionalExpressions.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/functionalExpressions.kt index c3861d256..6d7676c25 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/functionalExpressions.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/functionalExpressions.kt @@ -66,8 +66,7 @@ open class FunctionalExpressionField( val field: Field ) : Field>, ExpressionAlgebra>, FunctionalExpressionSpace(field) { - override val one: Expression - get() = const(this.field.one) + override val one: Expression = ConstantExpression(this.field.one) fun const(value: Double): Expression = const(field.run { one * value }) @@ -80,4 +79,16 @@ open class FunctionalExpressionField( operator fun T.times(arg: Expression) = arg * this operator fun T.div(arg: Expression) = arg / this -} \ No newline at end of file +} + +/** + * Create a functional expression on this [Space] + */ +fun Space.buildExpression(block: FunctionalExpressionSpace.() -> Expression): Expression = + FunctionalExpressionSpace(this).run(block) + +/** + * Create a functional expression on this [Field] + */ +fun Field.buildExpression(block: FunctionalExpressionField.() -> Expression): Expression = + FunctionalExpressionField(this).run(block) \ No newline at end of file diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Complex.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Complex.kt index e33e4078e..01aef824d 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Complex.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Complex.kt @@ -50,6 +50,12 @@ object ComplexField : ExtendedField { operator fun Complex.minus(d: Double) = add(this, -d.toComplex()) operator fun Double.times(c: Complex) = Complex(c.re * this, c.im * this) + + override fun raw(value: String): Complex = if (value == "i") { + i + } else { + super.raw(value) + } } /** From 09641a5c9c8e4a6af739d984e4a0c634c702f934 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Fri, 12 Jun 2020 16:59:36 +0300 Subject: [PATCH 6/8] Documentation --- .../commonMain/kotlin/scientifik/kmath/ast/MSTExpression.kt | 2 +- kmath-ast/src/jvmMain/kotlin/scientifik/kmath/ast/parser.kt | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MSTExpression.kt b/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MSTExpression.kt index c0f124a35..3ee454b2a 100644 --- a/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MSTExpression.kt +++ b/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MSTExpression.kt @@ -4,7 +4,7 @@ import scientifik.kmath.expressions.Expression import scientifik.kmath.operations.NumericAlgebra /** - * The expression evaluates MST on-flight + * The expression evaluates MST on-flight. Should be much faster than functional expression, but slower than ASM-generated expressions. */ class MSTExpression(val algebra: NumericAlgebra, val mst: MST) : Expression { diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/ast/parser.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/ast/parser.kt index dcab1c972..cec61a8ff 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/ast/parser.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/ast/parser.kt @@ -12,6 +12,9 @@ import scientifik.kmath.operations.PowerOperations import scientifik.kmath.operations.RingOperations import scientifik.kmath.operations.SpaceOperations +/** + * TODO move to common + */ private object ArithmeticsEvaluator : Grammar() { val num by token("-?[\\d.]+(?:[eE]-?\\d+)?") val lpar by token("\\(") From 866ae47239f93eea7625e65a94799ce7245f03c4 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sat, 13 Jun 2020 11:51:33 +0300 Subject: [PATCH 7/8] replace `raw` by `symbol` in algebra --- kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MST.kt | 2 +- .../commonMain/kotlin/scientifik/kmath/ast/MSTExpression.kt | 2 +- .../commonMain/kotlin/scientifik/kmath/operations/Algebra.kt | 5 ++++- .../commonMain/kotlin/scientifik/kmath/operations/Complex.kt | 4 ++-- 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MST.kt b/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MST.kt index eba2c3343..3375ecdf3 100644 --- a/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MST.kt +++ b/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MST.kt @@ -43,7 +43,7 @@ sealed class MST { fun NumericAlgebra.evaluate(node: MST): T { return when (node) { is MST.Numeric -> number(node.value) - is MST.Singular -> raw(node.value) + is MST.Singular -> symbol(node.value) is MST.Unary -> unaryOperation(node.operation, evaluate(node.value)) is MST.Binary -> when { node.left is MST.Numeric && node.right is MST.Numeric -> { diff --git a/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MSTExpression.kt b/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MSTExpression.kt index 3ee454b2a..dbd5238e3 100644 --- a/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MSTExpression.kt +++ b/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/MSTExpression.kt @@ -12,7 +12,7 @@ class MSTExpression(val algebra: NumericAlgebra, val mst: MST) : Expressio * Substitute algebra raw value */ private inner class InnerAlgebra(val arguments: Map) : NumericAlgebra by algebra { - override fun raw(value: String): T = arguments[value] ?: super.raw(value) + override fun symbol(value: String): T = arguments[value] ?: super.symbol(value) } override fun invoke(arguments: Map): T = InnerAlgebra(arguments).evaluate(mst) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Algebra.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Algebra.kt index 166287ec7..8ed3f329e 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Algebra.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Algebra.kt @@ -10,7 +10,10 @@ interface Algebra { /** * Wrap raw string or variable */ - fun raw(value: String): T = error("Wrapping of '$value' is not supported in $this") + fun symbol(value: String): T = error("Wrapping of '$value' is not supported in $this") + + @Deprecated("Symbol is more concise",replaceWith = ReplaceWith("symbol")) + fun raw(value: String): T = symbol(value) /** * Dynamic call of unary operation with name [operation] on [arg] diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Complex.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Complex.kt index 01aef824d..6ce1d929b 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Complex.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Complex.kt @@ -51,10 +51,10 @@ object ComplexField : ExtendedField { operator fun Double.times(c: Complex) = Complex(c.re * this, c.im * this) - override fun raw(value: String): Complex = if (value == "i") { + override fun symbol(value: String): Complex = if (value == "i") { i } else { - super.raw(value) + super.symbol(value) } } From 0950580b85ca02a72c13863efc72aae2367e3ab3 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sat, 13 Jun 2020 18:26:18 +0300 Subject: [PATCH 8/8] Moe better-parse to common. Watch for https://github.com/h0tk3y/better-parse/issues/27 --- kmath-ast/build.gradle.kts | 6 ++++++ .../kotlin/scientifik/kmath/ast/parser.kt | 0 2 files changed, 6 insertions(+) rename kmath-ast/src/{jvmMain => commonMain}/kotlin/scientifik/kmath/ast/parser.kt (100%) diff --git a/kmath-ast/build.gradle.kts b/kmath-ast/build.gradle.kts index 88540e7b8..65a1ac12c 100644 --- a/kmath-ast/build.gradle.kts +++ b/kmath-ast/build.gradle.kts @@ -11,6 +11,7 @@ kotlin.sourceSets { dependencies { api(project(":kmath-core")) implementation("com.github.h0tk3y.betterParse:better-parse-multiplatform:0.4.0-alpha-3") + implementation("com.github.h0tk3y.betterParse:better-parse-multiplatform-metadata:0.4.0-alpha-3") } } jvmMain{ @@ -18,4 +19,9 @@ kotlin.sourceSets { implementation("com.github.h0tk3y.betterParse:better-parse-jvm:0.4.0-alpha-3") } } + jsMain{ + dependencies{ + implementation("com.github.h0tk3y.betterParse:better-parse-js:0.4.0-alpha-3") + } + } } \ No newline at end of file diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/ast/parser.kt b/kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/parser.kt similarity index 100% rename from kmath-ast/src/jvmMain/kotlin/scientifik/kmath/ast/parser.kt rename to kmath-ast/src/commonMain/kotlin/scientifik/kmath/ast/parser.kt