All Downloads are FREE. Search and download functionalities are using the official Maven repository.

commonMain.space.kscience.kmath.operations.Algebra.kt Maven / Gradle / Ivy

package space.kscience.kmath.operations

import space.kscience.kmath.expressions.Symbol

/**
 * Stub for DSL the [Algebra] is.
 */
@DslMarker
public annotation class KMathContext

/**
 * Represents an algebraic structure.
 *
 * @param T the type of element of this structure.
 */
public interface Algebra {
    /**
     * Wraps a raw string to [T] object. This method is designed for three purposes:
     *
     * 1. Mathematical constants (`e`, `pi`).
     * 2. Variables for expression-like contexts (`a`, `b`, `c`...).
     * 3. Literals (`{1, 2}`, (`(3; 4)`)).
     *
     * In case if algebra can't parse the string, this method must throw [kotlin.IllegalStateException].
     *
     * @param value the raw string.
     * @return an object.
     */
    public fun bindSymbol(value: String): T = error("Wrapping of '$value' is not supported in $this")

    /**
     * Dynamically dispatches an unary operation with the certain name.
     *
     * This function must follow two properties:
     *
     * 1. In case if operation is not defined in the structure, the function throws [kotlin.IllegalStateException].
     * 2. This function is symmetric with second `unaryOperation` overload:
     * i.e. `unaryOperationFunction(a)(b) == unaryOperation(a, b)`.
     *
     * @param operation the name of operation.
     * @return an operation.
     */
    public fun unaryOperationFunction(operation: String): (arg: T) -> T =
        error("Unary operation $operation not defined in $this")

    /**
     * Dynamically invokes an unary operation with the certain name.
     *
     * This function must follow two properties:
     *
     * 1. In case if operation is not defined in the structure, the function throws [kotlin.IllegalStateException].
     * 2. This function is symmetric with second [unaryOperationFunction] overload:
     * i.e. `unaryOperationFunction(a)(b) == unaryOperation(a, b)`.
     *
     * @param operation the name of operation.
     * @param arg the argument of operation.
     * @return a result of operation.
     */
    public fun unaryOperation(operation: String, arg: T): T = unaryOperationFunction(operation)(arg)

    /**
     * Dynamically dispatches a binary operation with the certain name.
     *
     * This function must follow two properties:
     *
     * 1. In case if operation is not defined in the structure, the function throws [kotlin.IllegalStateException].
     * 2. This function is symmetric with second [binaryOperationFunction] overload:
     * i.e. `binaryOperationFunction(a)(b, c) == binaryOperation(a, b, c)`.
     *
     * @param operation the name of operation.
     * @return an operation.
     */
    public fun binaryOperationFunction(operation: String): (left: T, right: T) -> T =
        error("Binary operation $operation not defined in $this")

    /**
     * Dynamically invokes a binary operation with the certain name.
     *
     * This function must follow two properties:
     *
     * 1. In case if operation is not defined in the structure, the function throws [kotlin.IllegalStateException].
     * 2. This function is symmetric with second [binaryOperationFunction] overload:
     * i.e. `binaryOperationFunction(a)(b, c) == binaryOperation(a, b, c)`.
     *
     * @param operation the name of operation.
     * @param left the first argument of operation.
     * @param right the second argument of operation.
     * @return a result of operation.
     */
    public fun binaryOperation(operation: String, left: T, right: T): T = binaryOperationFunction(operation)(left, right)
}

public fun  Algebra.bindSymbol(symbol: Symbol): T = bindSymbol(symbol.identity)

/**
 * Call a block with an [Algebra] as receiver.
 */
// TODO add contract when KT-32313 is fixed
public inline operator fun , R> A.invoke(block: A.() -> R): R = run(block)

/**
 * Represents linear space without neutral element, i.e. algebraic structure with associative, binary operation [add]
 * and scalar multiplication [multiply].
 *
 * @param T the type of element of this semispace.
 */
public interface SpaceOperations : Algebra {
    /**
     * Addition of two elements.
     *
     * @param a the addend.
     * @param b the augend.
     * @return the sum.
     */
    public fun add(a: T, b: T): T

    /**
     * Multiplication of element by scalar.
     *
     * @param a the multiplier.
     * @param k the multiplicand.
     * @return the produce.
     */
    public fun multiply(a: T, k: Number): T

    // 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.
     */
    public operator fun T.unaryMinus(): T = multiply(this, -1.0)

    /**
     * Returns this value.
     *
     * @receiver this value.
     * @return this value.
     */
    public operator fun T.unaryPlus(): T = this

    /**
     * Addition of two elements.
     *
     * @receiver the addend.
     * @param b the augend.
     * @return the sum.
     */
    public operator fun T.plus(b: T): T = add(this, b)

    /**
     * Subtraction of two elements.
     *
     * @receiver the minuend.
     * @param b the subtrahend.
     * @return the difference.
     */
    public operator fun T.minus(b: T): T = add(this, -b)

    /**
     * Multiplication of this element by a scalar.
     *
     * @receiver the multiplier.
     * @param k the multiplicand.
     * @return the product.
     */
    public operator fun T.times(k: Number): T = multiply(this, k)

    /**
     * Division of this element by scalar.
     *
     * @receiver the dividend.
     * @param k the divisor.
     * @return the quotient.
     */
    public 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.
     */
    public operator fun Number.times(b: T): T = b * this

    public override fun unaryOperationFunction(operation: String): (arg: T) -> T = when (operation) {
        PLUS_OPERATION -> { arg -> arg }
        MINUS_OPERATION -> { arg -> -arg }
        else -> super.unaryOperationFunction(operation)
    }

    public override fun binaryOperationFunction(operation: String): (left: T, right: T) -> T = when (operation) {
        PLUS_OPERATION -> ::add
        MINUS_OPERATION -> { left, right -> left - right }
        else -> super.binaryOperationFunction(operation)
    }

    public companion object {
        /**
         * The identifier of addition and unary positive operator.
         */
        public const val PLUS_OPERATION: String = "+"

        /**
         * The identifier of subtraction and unary negative operator.
         */
        public const val MINUS_OPERATION: String = "-"
    }
}

/**
 * Represents linear space with neutral element, i.e. algebraic structure with associative, binary operation [add] and
 * scalar multiplication [multiply].
 *
 * @param T the type of element of this semispace.
 */
public interface Space : SpaceOperations {
    /**
     * The neutral element of addition.
     */
    public val zero: T
}

/**
 * Represents rng, i.e. algebraic structure with associative, binary, commutative operation [add] and associative,
 * operation [multiply] distributive over [add].
 *
 * @param T the type of element of this semiring.
 */
public interface RingOperations : SpaceOperations {
    /**
     * Multiplies two elements.
     *
     * @param a the multiplier.
     * @param b the multiplicand.
     */
    public fun multiply(a: T, b: T): T

    /**
     * Multiplies this element by scalar.
     *
     * @receiver the multiplier.
     * @param b the multiplicand.
     */
    public operator fun T.times(b: T): T = multiply(this, b)

    public override fun binaryOperationFunction(operation: String): (left: T, right: T) -> T = when (operation) {
        TIMES_OPERATION -> ::multiply
        else -> super.binaryOperationFunction(operation)
    }

    public companion object {
        /**
         * The identifier of multiplication.
         */
        public const val TIMES_OPERATION: String = "*"
    }
}

/**
 * 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.
 */
public interface Ring : Space, RingOperations {
    /**
     * neutral operation for multiplication
     */
    public val one: T
}

/**
 * Represents field without identity elements, i.e. algebraic structure with associative, binary, commutative operations
 * [add] and [multiply]; binary operation [divide] as multiplication of left operand by reciprocal of right one.
 *
 * @param T the type of element of this semifield.
 */
public interface FieldOperations : RingOperations {
    /**
     * Division of two elements.
     *
     * @param a the dividend.
     * @param b the divisor.
     * @return the quotient.
     */
    public fun divide(a: T, b: T): T

    /**
     * Division of two elements.
     *
     * @receiver the dividend.
     * @param b the divisor.
     * @return the quotient.
     */
    public operator fun T.div(b: T): T = divide(this, b)

    public override fun binaryOperationFunction(operation: String): (left: T, right: T) -> T = when (operation) {
        DIV_OPERATION -> ::divide
        else -> super.binaryOperationFunction(operation)
    }

    public companion object {
        /**
         * The identifier of division.
         */
        public const val DIV_OPERATION: String = "/"
    }
}

/**
 * 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.
 */
public interface Field : Ring, FieldOperations {
    /**
     * Division of element by scalar.
     *
     * @receiver the dividend.
     * @param b the divisor.
     * @return the quotient.
     */
    public operator fun Number.div(b: T): T = this * divide(one, b)
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy