aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNao Pross <np@0hm.ch>2024-03-06 15:54:50 +0100
committerNao Pross <np@0hm.ch>2024-03-06 15:54:50 +0100
commitf22500c1410b9554d3d93ee93b23e29975c0ef87 (patch)
treee7491f36f3068e286f3e1ff0aaa0c7add93fcd16
parentNuke duck typing and replace with OOP to make type checker happy (diff)
downloadmdpoly-f22500c1410b9554d3d93ee93b23e29975c0ef87.tar.gz
mdpoly-f22500c1410b9554d3d93ee93b23e29975c0ef87.zip
Move to_repr to Expr.to_repr necessary by removing duck typing HasRepr
-rw-r--r--mdpoly/abc.py39
-rw-r--r--mdpoly/algebra.py63
-rw-r--r--mdpoly/leaves.py15
-rw-r--r--mdpoly/representations.py16
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. """