diff options
author | Nao Pross <np@0hm.ch> | 2024-03-06 15:54:50 +0100 |
---|---|---|
committer | Nao Pross <np@0hm.ch> | 2024-03-06 15:54:50 +0100 |
commit | f22500c1410b9554d3d93ee93b23e29975c0ef87 (patch) | |
tree | e7491f36f3068e286f3e1ff0aaa0c7add93fcd16 | |
parent | Nuke duck typing and replace with OOP to make type checker happy (diff) | |
download | mdpoly-f22500c1410b9554d3d93ee93b23e29975c0ef87.tar.gz mdpoly-f22500c1410b9554d3d93ee93b23e29975c0ef87.zip |
Move to_repr to Expr.to_repr necessary by removing duck typing HasRepr
-rw-r--r-- | mdpoly/abc.py | 39 | ||||
-rw-r--r-- | mdpoly/algebra.py | 63 | ||||
-rw-r--r-- | mdpoly/leaves.py | 15 | ||||
-rw-r--r-- | mdpoly/representations.py | 16 |
4 files changed, 83 insertions, 50 deletions
diff --git a/mdpoly/abc.py b/mdpoly/abc.py index 5d43055..35346b8 100644 --- a/mdpoly/abc.py +++ b/mdpoly/abc.py @@ -3,10 +3,12 @@ from __future__ import annotations from .index import Number, Shape, MatrixIndex, PolyIndex from .constants import NUMERICS_EPS +from .state import State from .util import iszero from typing import Self, Any, Iterable, Sequence from enum import Enum, auto +from copy import copy from abc import ABC, abstractmethod @@ -28,10 +30,18 @@ class Expr(ABC): @abstractmethod def left(self) -> Self: ... + @left.setter + @abstractmethod + def left(self, left: Self) -> None: ... + @property @abstractmethod def right(self) -> Self: ... + @right.setter + @abstractmethod + def right(self, right: Self) -> None: ... + @property @abstractmethod def algebra(self) -> Algebra: ... @@ -48,6 +58,15 @@ class Expr(ABC): name = self.__class__.__qualname__ return f"{name}(left={self.left}, right={self.right})" + @abstractmethod + def to_repr(self, repr_type: type, state: State) -> tuple[Repr, State]: + """ Convert to a concrete representation + + To convert an abstract object from the expression tree, we ... TODO: finish. + + See also :py:class:`mdpoly.state.State`. + """ + def children(self) -> Sequence[Expr]: """ Iterate over the two nodes """ return self.left, self.right @@ -102,9 +121,10 @@ class Expr(ABC): if isinstance(node, Leaf): return node - left = replace_all(node.left) - right = replace_all(node.right) - return node.__class__(left, right) + node_copy = copy(node) + node_copy.left = replace_all(node.left) + node_copy.right = replace_all(node.right) + return node_copy return replace_all(self) @@ -171,12 +191,17 @@ class Leaf(Expr): return True @property - def left(self): - ... + def left(self): ... + + @left.setter + def left(self, left: Self) -> None: ... @property - def right(self): - ... + def right(self): ... + + @right.setter + def right(self, right: Self) -> None: ... + class Rel(ABC): """ Relation between two expressions. """ diff --git a/mdpoly/algebra.py b/mdpoly/algebra.py index aa12ae3..117ad51 100644 --- a/mdpoly/algebra.py +++ b/mdpoly/algebra.py @@ -6,7 +6,6 @@ from .leaves import Nothing, Const, Param, Var from .errors import AlgebraicError, InvalidShape from .state import State from .index import Shape, MatrixIndex, PolyIndex, Number -from .representations import HasRepr from typing import cast, Protocol, Self, Sequence from functools import reduce @@ -16,45 +15,56 @@ from abc import abstractmethod import operator -# To make the type checker happy -class ExprWithRepr(Expr, HasRepr): - - def children(self) -> Sequence[Self]: # type: ignore[override] - return cast(Sequence[Self], Expr.children(self)) - - -class BinaryOp(ExprWithRepr): +class BinaryOp(Expr): def __init__(self, left, right): self._left = left self._right = right @property - def left(self) -> ExprWithRepr: # type: ignore[override] + def left(self) -> Expr: return self._left + @left.setter + def left(self, left: Expr) -> None: + self._left = left + @property - def right(self) -> ExprWithRepr: # type: ignore[override] + def right(self) -> Expr: return self._right + @right.setter + def right(self, right: Expr) -> None: + self._right = right + -class UnaryOp(ExprWithRepr): - def __init__(self, inner): - self._inner = inner +class UnaryOp(Expr): + def __init__(self, left, right=None): + self._inner = left @property def inner(self) -> Expr: return self._inner @property - def left(self) -> ExprWithRepr: # type: ignore[override] + def left(self) -> Expr: return self._inner + @left.setter + def left(self, left: Expr) -> None: + self._inner = left + @property - def right(self) -> ExprWithRepr: # type: ignore[override] - return Nothing() # type: ignore + def right(self) -> Expr: + return Nothing() + + @right.setter + def right(self, right) -> None: + if not isinstance(right, Nothing): + raise ValueError("Cannot set right of left-acting unary opertator " + "to somethig that is not of type Nothing.") -class Reducible(HasRepr, Protocol): +class Reducible(Expr): """ Reducible Expression Algebraic expression that can be written in terms of other (existing) @@ -68,7 +78,7 @@ class Reducible(HasRepr, Protocol): return self.reduce().to_repr(repr_type, state) @abstractmethod - def reduce(self) -> HasRepr: + def reduce(self) -> Expr: """ Reduce the expression to its basic elements """ @@ -150,7 +160,7 @@ class PolyAdd(BinaryOp, PolyRingExpr): """ Addition operator between scalar polynomials. """ def to_repr(self, repr_type: type, state: State) -> tuple[Repr, State]: - """ See :py:meth:`mdpoly.representations.HasRepr.to_repr`. """ + """ See :py:meth:`mdpoly.HasRepr.to_repr`. """ # Make a new empty representation r = repr_type() @@ -173,7 +183,7 @@ class PolyAdd(BinaryOp, PolyRingExpr): class PolySub(BinaryOp, PolyRingExpr, Reducible): """ Subtraction operator between scalar polynomials. """ - def reduce(self) -> HasRepr: + def reduce(self) -> Expr: """ See :py:meth:`mdpoly.algebra.ReducibleExpr.reduce`. """ return self.left + (-1 * self.right) @@ -221,7 +231,13 @@ class PolyExp(BinaryOp, PolyRingExpr, Reducible): return cast(Const, super().right) - def reduce(self) -> HasRepr: + @right.setter + def right(self, right: Expr) -> None: + # Workaround necessary because of a bug + # https://bugs.python.org/issue14965 + BinaryOp.right.fset(self, right) # type: ignore + + def reduce(self) -> Expr: """ See :py:meth:`mdpoly.algebra.ReducibleExpr.reduce`. """ var = self.left if not isinstance(self.right.value, int): @@ -236,6 +252,7 @@ class PolyExp(BinaryOp, PolyRingExpr, Reducible): class PolyPartialDiff(UnaryOp, PolyRingExpr): """ Partial differentiation of scalar polynomials """ + def __init__(self, inner: Expr, with_respect_to: Var): UnaryOp.__init__(self, inner) self.wrt = with_respect_to @@ -372,7 +389,7 @@ class MatAdd(BinaryOp, PolyRingExpr): class MatSub(BinaryOp, PolyRingExpr, Reducible): """ Subtraction operator between matrices. """ - def reduce(self) -> HasRepr: + def reduce(self) -> Expr: """ See :py:meth:`mdpoly.algebra.ReducibleExpr.reduce`. """ return self.left + (-1 * self.right) diff --git a/mdpoly/leaves.py b/mdpoly/leaves.py index 83324a3..90c303e 100644 --- a/mdpoly/leaves.py +++ b/mdpoly/leaves.py @@ -2,7 +2,6 @@ from .abc import Algebra, Leaf, Repr from .index import Number, Shape, MatrixIndex, PolyVarIndex, PolyIndex from .state import State from .errors import MissingParameters -from .representations import HasRepr from dataclasses import dataclass @@ -12,13 +11,19 @@ class Nothing(Leaf): """ A leaf that is nothing. This is a placeholder (eg. for unary operators). """ - name: str = "<nothing>" + name: str = "" shape: Shape = Shape(0, 0) algebra: Algebra = Algebra.none + def to_repr(self, repr_type: type, state: State) -> tuple[Repr, State]: + raise ValueError("Nothing cannot be represented.") + + def __repr__(self) -> str: + return "Nothing" + @dataclass(frozen=True) -class Const(Leaf, HasRepr): +class Const(Leaf): """ A scalar constant """ @@ -40,7 +45,7 @@ class Const(Leaf, HasRepr): @dataclass(frozen=True) -class Var(Leaf, HasRepr): +class Var(Leaf): """ Polynomial variable """ @@ -59,7 +64,7 @@ class Var(Leaf, HasRepr): @dataclass(frozen=True) -class Param(Leaf, HasRepr): +class Param(Leaf): """ A parameter """ diff --git a/mdpoly/representations.py b/mdpoly/representations.py index b541c3e..7b7028c 100644 --- a/mdpoly/representations.py +++ b/mdpoly/representations.py @@ -1,26 +1,12 @@ from .abc import Repr from .index import Number, Shape, MatrixIndex, PolyIndex -from .state import State -from typing import Protocol, Iterable -from abc import abstractmethod +from typing import Iterable import numpy as np import numpy.typing as npt -class HasRepr(Protocol): - """ Has a representation. """ - @abstractmethod - def to_repr(self, repr_type: type, state: State) -> tuple[Repr, State]: - """ Convert to a concrete representation - - To convert an abstract object from the expression tree, we ... TODO: finish. - - See also :py:class:`mdpoly.state.State`. - """ - - class SparseRepr(Repr): """ Sparse representation of polynomial. """ |