Refactor code in directories + add doc strings
This commit is contained in:
parent
1e5113b208
commit
0d9a636da6
8 changed files with 175 additions and 81 deletions
111
python_symb/Expressions/expr.py
Normal file
111
python_symb/Expressions/expr.py
Normal file
|
@ -0,0 +1,111 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import python_symb.MathTypes.symbols
|
||||||
|
from python_symb.Expressions.tree import Tree
|
||||||
|
from python_symb.MathTypes.operator_file import Add, Mul, Min
|
||||||
|
|
||||||
|
from python_symb.MathTypes.operator_file import UnaryOperator, BinOperator, Add, Mul, Min
|
||||||
|
|
||||||
|
from typing import List
|
||||||
|
from python_symb.MathTypes.symbols import Var
|
||||||
|
|
||||||
|
from python_symb.Parsing.parse import infix_str_to_postfix
|
||||||
|
|
||||||
|
|
||||||
|
class Expr(Tree):
|
||||||
|
"""
|
||||||
|
A class to represent an expression tree
|
||||||
|
|
||||||
|
value: the value of the node
|
||||||
|
children: the subtrees of the root (Default : None)
|
||||||
|
|
||||||
|
exemple :
|
||||||
|
5*(2+3) represented as
|
||||||
|
Expr(Mul, [Expr(5), Expr(Expr(Add, [Expr(2), Expr(3)]))])
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, value, children=None):
|
||||||
|
super().__init__(value, children if children else [])
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def from_postfix_list(postfix: List):
|
||||||
|
"""
|
||||||
|
Create an expression tree from a postfix list of tokens
|
||||||
|
|
||||||
|
tokens are : int, float, Var, UnaryOperator, BinOperator
|
||||||
|
|
||||||
|
exemple :
|
||||||
|
x = Var('x')
|
||||||
|
[5, 2, Add] -> Expr(Add, [Expr(5), Expr(2)])
|
||||||
|
"""
|
||||||
|
|
||||||
|
def aux():
|
||||||
|
first = postfix.pop()
|
||||||
|
match first:
|
||||||
|
case int() | Var():
|
||||||
|
return Expr(first)
|
||||||
|
case UnaryOperator():
|
||||||
|
return Expr(first, [aux()])
|
||||||
|
case BinOperator():
|
||||||
|
return Expr(first, [aux(), aux()])
|
||||||
|
|
||||||
|
return aux()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def from_infix_str(expr_str) -> Expr:
|
||||||
|
"""
|
||||||
|
Create an expression tree from an infix string
|
||||||
|
"""
|
||||||
|
expr_rev_polish = infix_str_to_postfix(expr_str)
|
||||||
|
return Expr.from_postfix_list(expr_rev_polish)
|
||||||
|
|
||||||
|
def bin_op_constructor(self, other, op):
|
||||||
|
"""
|
||||||
|
Construct a binary operation
|
||||||
|
"""
|
||||||
|
match other:
|
||||||
|
case Expr():
|
||||||
|
return Expr(op, [self, other])
|
||||||
|
case Var() | int() | float():
|
||||||
|
return Expr(op, [self, Expr(other)])
|
||||||
|
case _:
|
||||||
|
return ValueError(f'Invalid type for operation: {other} : {type(other)}')
|
||||||
|
|
||||||
|
def __add__(self, other):
|
||||||
|
return self.bin_op_constructor(other, Add)
|
||||||
|
|
||||||
|
def __mul__(self, other):
|
||||||
|
return self.bin_op_constructor(other, Mul)
|
||||||
|
|
||||||
|
def __sub__(self, other):
|
||||||
|
return self.bin_op_constructor(other, Min)
|
||||||
|
|
||||||
|
|
||||||
|
def test1():
|
||||||
|
x, y = Var('x'), Var('y')
|
||||||
|
expr1 = x + y
|
||||||
|
expr2 = 5+x
|
||||||
|
print(expr1 + expr2)
|
||||||
|
|
||||||
|
|
||||||
|
def test2():
|
||||||
|
from python_symb.MathTypes.operator_file import Sin
|
||||||
|
a, b = Var('a'), Var('b')
|
||||||
|
expr = Sin(a+b)
|
||||||
|
print(expr)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
test1()
|
||||||
|
test2()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
from typing import Iterable, Generator
|
from typing import Iterable, Generator
|
||||||
from collections import deque
|
from collections import deque
|
||||||
from parse import update_symbols_dict
|
from abc import ABC, abstractmethod
|
||||||
|
|
||||||
|
|
||||||
class Tree:
|
class Tree(ABC):
|
||||||
"""
|
"""
|
||||||
Ultra generic Test class. Can be used to represent any Test structure.
|
Ultra generic Test class. Can be used to represent any Test structure.
|
||||||
|
|
||||||
value : value of the node. Can be a binary operator like "+", a ternary operator like "if", a number etc...
|
:param value : value of the node. Can be a binary operator like "+", a ternary operator like "if", a number etc...
|
||||||
|
|
||||||
depth_first_order : the default order of the node in the depth first traversal. Used to implement the depth_first method.
|
depth_first_order : the default order of the node in the depth first traversal. Used to implement the depth_first method.
|
||||||
0 is pre-order, 1 is in-order (for binary Test), -1 is post-order.
|
0 is pre-order, 1 is in-order (for binary Test), -1 is post-order.
|
||||||
|
@ -25,7 +25,7 @@ class Tree:
|
||||||
self.children = children if children else []
|
self.children = children if children else []
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
return f'Tree({self.value}, {self.children})' if self.children else f'Leaf({self.value})'
|
return f'Tree({self.value}, {self.children})' if self.children else f'Tree({self.value})'
|
||||||
|
|
||||||
def height(self) -> int:
|
def height(self) -> int:
|
||||||
return 1 + max((child.height() for child in self.children), default=0)
|
return 1 + max((child.height() for child in self.children), default=0)
|
|
@ -1,11 +1,11 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
from typing import Iterable, Generator
|
from typing import Iterable, Generator
|
||||||
from tools import gcd
|
from python_symb.IndependantTools.tools import gcd
|
||||||
|
|
||||||
|
|
||||||
class Fraction:
|
class Fraction:
|
||||||
"""
|
"""
|
||||||
Should represent a fraction not a division
|
Represent a fraction a/b with a and b elements of a ring (support +, -, *, /)
|
||||||
"""
|
"""
|
||||||
__slots__ = ['num', 'den']
|
__slots__ = ['num', 'den']
|
||||||
__match_args__ = ("num", "den")
|
__match_args__ = ("num", "den")
|
||||||
|
@ -36,7 +36,7 @@ class Fraction:
|
||||||
# can be completed with others objects that support gcd like polynomials etc...
|
# can be completed with others objects that support gcd like polynomials etc...
|
||||||
|
|
||||||
def simplify_to_num(self):
|
def simplify_to_num(self):
|
||||||
"""from frac(a, 1) return a."""
|
"""from frac(a, 1) return a """
|
||||||
if self.den == 1:
|
if self.den == 1:
|
||||||
return self.num
|
return self.num
|
||||||
def simplify_nested(self, rec=True):
|
def simplify_nested(self, rec=True):
|
|
@ -1,11 +1,20 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
from typing import Dict, Callable
|
from typing import Dict, Callable
|
||||||
from symbols import Symbols
|
from python_symb.MathTypes.symbols import Symbols
|
||||||
|
|
||||||
|
|
||||||
class Operator(Symbols):
|
class Operator(Symbols):
|
||||||
|
"""
|
||||||
|
Represent an operator, like +, *, sin, anything that can be applied to an expression
|
||||||
|
"""
|
||||||
instances = []
|
instances = []
|
||||||
def __init__(self, name : str, precedence: int, call: Callable):
|
|
||||||
|
def __init__(self, name: str, precedence: int, call: Callable):
|
||||||
|
"""
|
||||||
|
:param name of the operator
|
||||||
|
:param precedence: precedence of the operator, higher is better
|
||||||
|
:param call: function to apply the operator
|
||||||
|
"""
|
||||||
super().__init__(name)
|
super().__init__(name)
|
||||||
self.precedence = precedence
|
self.precedence = precedence
|
||||||
self.call = call
|
self.call = call
|
||||||
|
@ -16,20 +25,44 @@ class Operator(Symbols):
|
||||||
|
|
||||||
|
|
||||||
class UnaryOperator(Operator):
|
class UnaryOperator(Operator):
|
||||||
|
"""
|
||||||
|
Represent a unary operator, like sin, cos, - etc...
|
||||||
|
all operators that take only one argument
|
||||||
|
"""
|
||||||
instances = []
|
instances = []
|
||||||
|
|
||||||
def __init__(self, name: str, precedence: int, call: Callable):
|
def __init__(self, name: str, precedence: int, call: Callable):
|
||||||
UnaryOperator.instances.append(self)
|
UnaryOperator.instances.append(self)
|
||||||
super().__init__(name, precedence, call)
|
super().__init__(name, precedence, call)
|
||||||
|
|
||||||
def __call__(self, expr):
|
def apply(self, expr):
|
||||||
return self.call(expr)
|
return self.call(expr)
|
||||||
|
|
||||||
|
def __call__(self, e):
|
||||||
|
from python_symb.Expressions.expr import Expr
|
||||||
|
return Expr(self, [e])
|
||||||
|
|
||||||
|
|
||||||
class BinProperties:
|
class BinProperties:
|
||||||
|
"""
|
||||||
|
Represent the properties of a binary operator
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self, associativity: bool, commutativity: True,
|
def __init__(self, associativity: bool, commutativity: True,
|
||||||
left_distributivity: Dict[str, bool], right_distributivity: Dict[str, bool]):
|
left_distributivity: Dict[str, bool], right_distributivity: Dict[str, bool]):
|
||||||
|
"""
|
||||||
|
:param associativity: True if the operator is associative
|
||||||
|
:param commutativity: True if the operator is commutative
|
||||||
|
:param left_distributivity: a dictionary of the operators that the current operator distribute over
|
||||||
|
:param right_distributivity: a dictionary of the operators that distribute over the current operator
|
||||||
|
|
||||||
|
exemple:
|
||||||
|
for the operator *:
|
||||||
|
associativity = True
|
||||||
|
commutativity = True
|
||||||
|
left_distributivity = {'+': True}
|
||||||
|
right_distributivity = {'+': True}
|
||||||
|
"""
|
||||||
|
|
||||||
self.associativity = associativity
|
self.associativity = associativity
|
||||||
self.commutativity = commutativity
|
self.commutativity = commutativity
|
||||||
|
@ -38,6 +71,12 @@ class BinProperties:
|
||||||
|
|
||||||
|
|
||||||
class BinOperator(Operator):
|
class BinOperator(Operator):
|
||||||
|
"""
|
||||||
|
Represent a binary operator, like +, *, etc...
|
||||||
|
all operators that take two arguments
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Used to store all the instances of BinOperator, used in the parser
|
||||||
instances = []
|
instances = []
|
||||||
|
|
||||||
def __init__(self, name: str, precedence: int, properties: BinProperties, call: Callable):
|
def __init__(self, name: str, precedence: int, properties: BinProperties, call: Callable):
|
||||||
|
@ -45,10 +84,14 @@ class BinOperator(Operator):
|
||||||
super().__init__(name, precedence, call)
|
super().__init__(name, precedence, call)
|
||||||
self.properties = properties
|
self.properties = properties
|
||||||
|
|
||||||
def __call__(self, left, right):
|
def apply(self, left, right):
|
||||||
return self.call(left, right)
|
return self.call(left, right)
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
Generic operators
|
||||||
|
"""
|
||||||
|
|
||||||
AddProperties = BinProperties(True, True, {'*': True}, {'*': True})
|
AddProperties = BinProperties(True, True, {'*': True}, {'*': True})
|
||||||
Add = BinOperator('+', 2, AddProperties, lambda x, y: x + y)
|
Add = BinOperator('+', 2, AddProperties, lambda x, y: x + y)
|
||||||
|
|
||||||
|
@ -58,5 +101,5 @@ Mul = BinOperator('*', 3, MulProperties, lambda x, y: x * y)
|
||||||
Sin = UnaryOperator('sin', 10, lambda x: x)
|
Sin = UnaryOperator('sin', 10, lambda x: x)
|
||||||
|
|
||||||
Min = BinOperator('-', 2, AddProperties, lambda x, y: x - y)
|
Min = BinOperator('-', 2, AddProperties, lambda x, y: x - y)
|
||||||
# Pns = UnaryOperator('()', 0, lambda x: x)
|
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
from typing import List, Union
|
from typing import List, Union
|
||||||
from operator_file import Add, Mul, Min, BinOperator, UnaryOperator
|
from python_symb.MathTypes.operator_file import BinOperator, UnaryOperator
|
||||||
from symbols import Symbols, Var
|
from python_symb.MathTypes.symbols import Symbols, Var
|
||||||
from fraction import Fraction
|
from python_symb.MathTypes.fraction import Fraction
|
||||||
|
|
||||||
x, y = Var('x'), Var('y')
|
x, y = Var('x'), Var('y')
|
||||||
|
|
||||||
|
@ -37,6 +37,7 @@ def preprocess(expr: str) -> List:
|
||||||
:param expr: string expression
|
:param expr: string expression
|
||||||
:return: list of symbols and numbers
|
:return: list of symbols and numbers
|
||||||
"""
|
"""
|
||||||
|
update_symbols_dict()
|
||||||
return_list = []
|
return_list = []
|
||||||
expr = expr.strip()
|
expr = expr.strip()
|
||||||
expr = expr.replace(' ', '')
|
expr = expr.replace(' ', '')
|
|
@ -1,63 +0,0 @@
|
||||||
from __future__ import annotations
|
|
||||||
from tree import Tree
|
|
||||||
from operator_file import Add, Mul
|
|
||||||
from operator_file import UnaryOperator, BinOperator, Add, Mul, Min
|
|
||||||
from typing import List
|
|
||||||
from symbols import Var
|
|
||||||
from parse import infix_str_to_postfix
|
|
||||||
|
|
||||||
|
|
||||||
class Expr(Tree):
|
|
||||||
def __init__(self, value, children=None):
|
|
||||||
super().__init__(value, children if children else [])
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def from_postfix_list(postfix: List):
|
|
||||||
|
|
||||||
def aux():
|
|
||||||
first = postfix.pop()
|
|
||||||
match first:
|
|
||||||
case int() | Var():
|
|
||||||
return Tree(first)
|
|
||||||
case UnaryOperator():
|
|
||||||
return Tree(first, [aux()])
|
|
||||||
case BinOperator():
|
|
||||||
return Tree(first, [aux(), aux()])
|
|
||||||
|
|
||||||
return aux()
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def from_infix_str(expr_str):
|
|
||||||
expr_rev_polish = infix_str_to_postfix(expr_str)
|
|
||||||
return Expr.from_postfix_list(expr_rev_polish)
|
|
||||||
|
|
||||||
def bin_op_constructor(self, other, op):
|
|
||||||
match other:
|
|
||||||
case Expr():
|
|
||||||
return Expr(op, [self, other])
|
|
||||||
case Var() | int() | float():
|
|
||||||
return Expr(op, [self, Expr(other)])
|
|
||||||
case _:
|
|
||||||
return ValueError(f'Invalid type for operation: {other} : {type(other)}')
|
|
||||||
|
|
||||||
def __add__(self, other):
|
|
||||||
self.bin_op_constructor(other, Add)
|
|
||||||
|
|
||||||
def __mul__(self, other):
|
|
||||||
self.bin_op_constructor(other, Mul)
|
|
||||||
|
|
||||||
def __sub__(self, other):
|
|
||||||
self.bin_op_constructor(other, Min)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
x, y = Var('x'), Var('y')
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
import tkinter as tk
|
import tkinter as tk
|
||||||
from expr import Expr, Var
|
from python_symb.Expressions.expr import Expr
|
||||||
from parse import update_symbols_dict
|
from python_symb.MathTypes.symbols import Var
|
||||||
|
from python_symb.Parsing.parse import update_symbols_dict
|
||||||
|
|
||||||
|
|
||||||
class Visual:
|
class Visual:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
@ -93,7 +95,7 @@ class Visual:
|
||||||
return self.tree_canvas.create_oval(x0, y0, x1, y1, fill=color)
|
return self.tree_canvas.create_oval(x0, y0, x1, y1, fill=color)
|
||||||
|
|
||||||
def draw_tree(self, tree, first_x=400, first_y=50, x_offset=250, y_offset=200, radius=30, fact_mult=0.75):
|
def draw_tree(self, tree, first_x=400, first_y=50, x_offset=250, y_offset=200, radius=30, fact_mult=0.75):
|
||||||
|
print(tree)
|
||||||
children = tree.children
|
children = tree.children
|
||||||
n = len(children)
|
n = len(children)
|
||||||
for i in range(n):
|
for i in range(n):
|
Loading…
Add table
Add a link
Reference in a new issue