From 4a26c1080e2b16ab3a6d88675e7604ae2141f775 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Thu, 14 May 2020 20:30:43 +0300 Subject: [PATCH] A prototype for advanced expressoins --- .../kmath/expressions/Expression.kt | 67 +--------- .../expressions/functionalExpressions.kt | 125 ++++++++++++++++++ .../kmath/expressions/syntaxTree.kt | 31 +++++ .../kmath/expressions/ExpressionFieldTest.kt | 12 +- 4 files changed, 163 insertions(+), 72 deletions(-) create mode 100644 kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/functionalExpressions.kt create mode 100644 kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/syntaxTree.kt 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 aa7407c0a..bc3d70b4f 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/Expression.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/Expression.kt @@ -1,9 +1,5 @@ package scientifik.kmath.expressions -import scientifik.kmath.operations.Field -import scientifik.kmath.operations.Ring -import scientifik.kmath.operations.Space - /** * An elementary function that could be invoked on a map of arguments */ @@ -26,67 +22,6 @@ interface ExpressionContext { * A constant expression which does not depend on arguments */ fun const(value: T): Expression -} -internal class VariableExpression(val name: String, val default: T? = null) : Expression { - override fun invoke(arguments: Map): T = - arguments[name] ?: default ?: error("Parameter not found: $name") -} - -internal class ConstantExpression(val value: T) : Expression { - override fun invoke(arguments: Map): T = value -} - -internal class SumExpression(val context: Space, val first: Expression, val second: Expression) : - Expression { - 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 { - override fun invoke(arguments: Map): T = - context.multiply(first.invoke(arguments), second.invoke(arguments)) -} - -internal class ConstProductExpession(val context: Space, val expr: Expression, val const: Number) : - Expression { - override fun invoke(arguments: Map): T = context.multiply(expr.invoke(arguments), const) -} - -internal class DivExpession(val context: Field, val expr: Expression, val second: Expression) : - Expression { - override fun invoke(arguments: Map): T = context.divide(expr.invoke(arguments), second.invoke(arguments)) -} - -open class ExpressionSpace(val space: Space) : Space>, ExpressionContext { - override val zero: Expression = ConstantExpression(space.zero) - - override fun const(value: T): Expression = ConstantExpression(value) - - override fun variable(name: String, default: T?): Expression = VariableExpression(name, default) - - override fun add(a: Expression, b: Expression): Expression = SumExpression(space, a, b) - - override fun multiply(a: Expression, k: Number): Expression = ConstProductExpession(space, a, k) - - - operator fun Expression.plus(arg: T) = this + const(arg) - operator fun Expression.minus(arg: T) = this - const(arg) - - operator fun T.plus(arg: Expression) = arg + this - operator fun T.minus(arg: Expression) = arg - this -} - - -class ExpressionField(val field: Field) : Field>, ExpressionSpace(field) { - override val one: Expression = ConstantExpression(field.one) - override fun multiply(a: Expression, b: Expression): Expression = ProductExpression(field, a, b) - - override fun divide(a: Expression, b: Expression): Expression = DivExpession(field, a, b) - - operator fun Expression.times(arg: T) = this * const(arg) - operator fun Expression.div(arg: T) = this / const(arg) - - operator fun T.times(arg: Expression) = arg * this - operator fun T.div(arg: Expression) = arg / this + fun produce(node: SyntaxTreeNode): Expression } \ No newline at end of file diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/functionalExpressions.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/functionalExpressions.kt new file mode 100644 index 000000000..64b3ad72c --- /dev/null +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/functionalExpressions.kt @@ -0,0 +1,125 @@ +package scientifik.kmath.expressions + +import scientifik.kmath.operations.Field +import scientifik.kmath.operations.Ring +import scientifik.kmath.operations.Space + +internal class VariableExpression(val name: String, val default: T? = null) : Expression { + override fun invoke(arguments: Map): T = + arguments[name] ?: default ?: error("Parameter not found: $name") +} + +internal class ConstantExpression(val value: T) : Expression { + override fun invoke(arguments: Map): T = value +} + +internal class SumExpression( + val context: Space, + val first: Expression, + val second: Expression +) : Expression { + 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 { + override fun invoke(arguments: Map): T = + context.multiply(first.invoke(arguments), second.invoke(arguments)) +} + +internal class ConstProductExpession(val context: Space, val expr: Expression, val const: Number) : + Expression { + override fun invoke(arguments: Map): T = context.multiply(expr.invoke(arguments), const) +} + +internal class DivExpession(val context: Field, val expr: Expression, val second: Expression) : + Expression { + override fun invoke(arguments: Map): T = context.divide(expr.invoke(arguments), second.invoke(arguments)) +} + +open class FunctionalExpressionSpace( + val space: Space, + one: T +) : Space>, ExpressionContext { + + override val zero: Expression = ConstantExpression(space.zero) + + val one: Expression = ConstantExpression(one) + + override fun const(value: T): Expression = ConstantExpression(value) + + override fun variable(name: String, default: T?): Expression = VariableExpression(name, default) + + override fun add(a: Expression, b: Expression): Expression = SumExpression(space, a, b) + + override fun multiply(a: Expression, k: Number): Expression = ConstProductExpession(space, a, k) + + + operator fun Expression.plus(arg: T) = this + const(arg) + operator fun Expression.minus(arg: T) = this - const(arg) + + operator fun T.plus(arg: Expression) = arg + this + operator fun T.minus(arg: Expression) = arg - this + + fun const(value: Double): Expression = one.times(value) + + open fun produceSingular(value: String): Expression { + val numberValue = value.toDoubleOrNull() + return if (numberValue == null) { + variable(value) + } else { + const(numberValue) + } + } + + open fun produceUnary(operation: String, value: Expression): Expression { + return when (operation) { + UnaryNode.PLUS_OPERATION -> value + UnaryNode.MINUS_OPERATION -> -value + else -> error("Unary operation $operation is not supported by $this") + } + } + + open fun produceBinary(operation: String, left: Expression, right: Expression): Expression { + return when (operation) { + BinaryNode.PLUS_OPERATION -> left + right + BinaryNode.MINUS_OPERATION -> left - right + else -> error("Binary operation $operation is not supported by $this") + } + } + + override fun produce(node: SyntaxTreeNode): Expression { + return when (node) { + is SingularNode -> produceSingular(node.value) + is UnaryNode -> produceUnary(node.operation, produce(node.value)) + is BinaryNode -> produceBinary(node.operation, produce(node.left), produce(node.right)) + } + } +} + +open class FunctionalExpressionField( + val field: Field +) : Field>, FunctionalExpressionSpace(field, field.one) { + override fun multiply(a: Expression, b: Expression): Expression = ProductExpression(field, a, b) + + override fun divide(a: Expression, b: Expression): Expression = DivExpession(field, a, b) + + operator fun Expression.times(arg: T) = this * const(arg) + operator fun Expression.div(arg: T) = this / const(arg) + + operator fun T.times(arg: Expression) = arg * this + operator fun T.div(arg: Expression) = arg / this + + override fun produce(node: SyntaxTreeNode): Expression { + //TODO bring together numeric and typed expressions + return super.produce(node) + } + + override fun produceBinary(operation: String, left: Expression, right: Expression): Expression { + return when (operation) { + BinaryNode.TIMES_OPERATION -> left * right + BinaryNode.DIV_OPERATION -> left / right + else -> super.produceBinary(operation, left, right) + } + } +} \ No newline at end of file diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/syntaxTree.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/syntaxTree.kt new file mode 100644 index 000000000..daa17977d --- /dev/null +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/syntaxTree.kt @@ -0,0 +1,31 @@ +package scientifik.kmath.expressions + +sealed class SyntaxTreeNode + +data class SingularNode(val value: String) : SyntaxTreeNode() + +data class UnaryNode(val operation: String, val value: SyntaxTreeNode): SyntaxTreeNode(){ + companion object{ + const val PLUS_OPERATION = "+" + const val MINUS_OPERATION = "-" + const val NOT_OPERATION = "!" + const val ABS_OPERATION = "abs" + const val SIN_OPERATION = "sin" + const val cos_OPERATION = "cos" + //TODO add operations + } +} + +data class BinaryNode(val operation: String, val left: SyntaxTreeNode, val right: SyntaxTreeNode): SyntaxTreeNode(){ + companion object{ + const val PLUS_OPERATION = "+" + const val MINUS_OPERATION = "-" + const val TIMES_OPERATION = "*" + const val DIV_OPERATION = "/" + //TODO add operations + } +} + +//TODO add a function with positional arguments + +//TODO add a function with named arguments \ No newline at end of file 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 033b2792f..b933f6a17 100644 --- a/kmath-core/src/commonTest/kotlin/scientifik/kmath/expressions/ExpressionFieldTest.kt +++ b/kmath-core/src/commonTest/kotlin/scientifik/kmath/expressions/ExpressionFieldTest.kt @@ -9,7 +9,7 @@ import kotlin.test.assertEquals class ExpressionFieldTest { @Test fun testExpression() { - val context = ExpressionField(RealField) + val context = FunctionalExpressionField(RealField) val expression = with(context) { val x = variable("x", 2.0) x * x + 2 * x + one @@ -20,7 +20,7 @@ class ExpressionFieldTest { @Test fun testComplex() { - val context = ExpressionField(ComplexField) + val context = FunctionalExpressionField(ComplexField) val expression = with(context) { val x = variable("x", Complex(2.0, 0.0)) x * x + 2 * x + one @@ -31,23 +31,23 @@ class ExpressionFieldTest { @Test fun separateContext() { - fun ExpressionField.expression(): Expression { + fun FunctionalExpressionField.expression(): Expression { val x = variable("x") return x * x + 2 * x + one } - val expression = ExpressionField(RealField).expression() + val expression = FunctionalExpressionField(RealField).expression() assertEquals(expression("x" to 1.0), 4.0) } @Test fun valueExpression() { - val expressionBuilder: ExpressionField.() -> Expression = { + val expressionBuilder: FunctionalExpressionField.() -> Expression = { val x = variable("x") x * x + 2 * x + one } - val expression = ExpressionField(RealField).expressionBuilder() + val expression = FunctionalExpressionField(RealField).expressionBuilder() assertEquals(expression("x" to 1.0), 4.0) } } \ No newline at end of file