Left and right-side operations in Algebra

This commit is contained in:
Alexander Nozik 2020-06-12 08:43:47 +03:00
parent e8a6ecd5c3
commit f46615d3bc
5 changed files with 120 additions and 63 deletions

View File

@ -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 <T> NumericAlgebra<T>.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))
}
}
}

View File

@ -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 <T> NumericAlgebra<T>.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))
}
}

View File

@ -31,6 +31,12 @@ interface NumericAlgebra<T> : Algebra<T> {
* 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,7 +134,13 @@ interface Ring<T> : Space<T>, RingOperations<T>, NumericAlgebra<T> {
override fun number(value: Number): T = one * value.toDouble()
// those operators are blocked by type conflict in RealField
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
//

View File

@ -10,9 +10,30 @@ interface ExtendedFieldOperations<T> :
FieldOperations<T>,
TrigonometricOperations<T>,
PowerOperations<T>,
ExponentialOperations<T>
ExponentialOperations<T> {
interface ExtendedField<T> : ExtendedFieldOperations<T>, Field<T>
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<T> : ExtendedFieldOperations<T>, Field<T> {
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<Double>, Norm<Double, Double> {
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<Float>, Norm<Float, Float> {
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)

View File

@ -10,30 +10,37 @@ package scientifik.kmath.operations
* It also allows to override behavior for optional operations
*
*/
interface TrigonometricOperations<T> : FieldOperations<T> {
interface TrigonometricOperations<T> {
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 <T : MathElement<out TrigonometricOperations<T>>> sin(arg: T): T = arg.context.sin(arg)
fun <T : MathElement<out TrigonometricOperations<T>>> cos(arg: T): T = arg.context.cos(arg)
fun <T : MathElement<out TrigonometricOperations<T>>> tg(arg: T): T = arg.context.tg(arg)
fun <T : MathElement<out TrigonometricOperations<T>>> ctg(arg: T): T = arg.context.ctg(arg)
fun <T : MathElement<out TrigonometricOperations<T>>> tan(arg: T): T = arg.context.tan(arg)
/* Power and roots */
/**
* A context extension to include power operations like square roots, etc
*/
interface PowerOperations<T> : Algebra<T> {
interface PowerOperations<T> {
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 : MathElement<out PowerOperations<T>>> T.pow(power: Double): T = context.power(this, power)
@ -42,9 +49,14 @@ fun <T : MathElement<out PowerOperations<T>>> sqr(arg: T): T = arg pow 2.0
/* Exponential */
interface ExponentialOperations<T>: Algebra<T> {
interface ExponentialOperations<T> {
fun exp(arg: T): T
fun ln(arg: T): T
companion object {
const val EXP_OPERATION = "exp"
const val LN_OPERATION = "ln"
}
}
fun <T : MathElement<out ExponentialOperations<T>>> exp(arg: T): T = arg.context.exp(arg)