Source code for cake.core.expressions.add

## Nodes are used for generating syntax trees for simplifying expressions
## For example:
##
## Variable + Variable -> Add(Variable, Variable)
##
## Expression('x + y') / 5 -> Divide(Add(Variable, Variable), 5)
##
## Variable + Variable - Variable -> Add(Add(Variable, Variable), -Variable)
##
## (U1 + U2)(U3 - U4) -> Add(U1 * U3, U1 * U4, U2 * U3, U2 * U4)
##                    -> Add(U1 * U3, Add(U1 * U4 + U2 * U3), U2 * U4)
##                    -> ...
##
from __future__ import annotations
from abc import ABC, abstractmethod
from cake.basic import BasicNode
import cake


class ExpressionNode(ABC, object):
    ''' Base class for identifying nodes in an expression '''

    def __init__(self, x: BasicNode, y: BasicNode, /, *nodes: BasicNode) -> None:
        self.nodes = list((x, y) + nodes)
        for index, node in enumerate(self.nodes):
            if not isinstance(node, BasicNode):
                if isinstance(node, str):
                    node = cake.Variable(node)
                else:
                    node = cake.Number.convert(node)
                self.nodes[index] = node

        self.__post_init__()

    def __repr__(self) -> str:
        return f'{self.__class__.__name__}({", ".join(map(str, self.nodes))})'

    @abstractmethod
    def __post_init__(self) -> None:
        ...


[docs]class Operation(ExpressionNode): ''' Base class for defining operations within the cake library, operations cannot be individually manipulated. Use :class:`Expression` to assist in this. ''' def __post_init__(self) -> None: self.flatten()
[docs] @abstractmethod def flatten(self) -> None: ''' Simplifies the expression into its simplest form if possible, can also be used as a check method when new operations are executed. ''' raise NotImplemented
class Add(Operation): def __str__(self) -> str: return ' + '.join(map(str, self.nodes)) def flatten(self) -> None: cleaned_nodes = [] nodes = [] for node in self.nodes: ## Expression has been passed if hasattr(node, 'exp'): node = node.exp if isinstance(node, Add): nodes.extend(node.nodes) else: nodes.append(node) for node in nodes: if not node: continue for index, cleaned_node in enumerate(cleaned_nodes): if isinstance(node, cake.Number) and isinstance(cleaned_node, cake.Number): cleaned_nodes[index] = node + cleaned_nodes[index] break if isinstance(node, cake.Variable) and isinstance(cleaned_node, cake.Variable): similar = cake.Variable.is_similar(node, cleaned_node) if similar: cleaned_nodes[index] = cleaned_nodes[index] + node break if isinstance(node, cake.VariableGroup) and isinstance(cleaned_node, cake.VariableGroup): similar = cake.VariableGroup.is_similar(node, cleaned_node) if similar: cleaned_nodes[index] = cleaned_nodes[index] + node break else: cleaned_nodes.append(node) self.nodes = cleaned_nodes