aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--mdpoly/algebra.py73
1 files changed, 59 insertions, 14 deletions
diff --git a/mdpoly/algebra.py b/mdpoly/algebra.py
index 580c2e2..b98af01 100644
--- a/mdpoly/algebra.py
+++ b/mdpoly/algebra.py
@@ -7,15 +7,17 @@ from .errors import AlgebraicError, InvalidShape
from .state import State
from .index import Shape, MatrixIndex, PolyIndex, Number
-from typing import cast
+from typing import cast, Iterable
from functools import reduce
-from itertools import product
+from itertools import product, chain, combinations_with_replacement
+from math import prod
from abc import abstractmethod
import operator
class BinaryOp(Expr):
+ """ Binary Operator """
def __init__(self, left, right):
self._left = left
self._right = right
@@ -38,6 +40,7 @@ class BinaryOp(Expr):
class UnaryOp(Expr):
+ """ Unary Operator """
def __init__(self, left, right=None):
self._inner = left
@@ -82,10 +85,20 @@ class Reducible(Expr):
""" Reduce the expression to its basic elements """
+# TODO: review this idea before implementing
+# class Simplifiable(Expr):
+# """ Simplifiable Expression. """
+#
+# @abstractmethod
+# def simplify(self) -> Expr:
+# """ Simplify the expression """
+
+
# ┏━┓┏━┓╻ ╻ ╻┏┓╻┏━┓┏┳┓╻┏━┓╻ ┏━┓╻ ┏━╸┏━╸┏┓ ┏━┓┏━┓
# ┣━┛┃ ┃┃ ┗┳┛┃┗┫┃ ┃┃┃┃┃┣━┫┃ ┣━┫┃ ┃╺┓┣╸ ┣┻┓┣┳┛┣━┫
# ╹ ┗━┛┗━╸ ╹ ╹ ╹┗━┛╹ ╹╹╹ ╹┗━╸ ╹ ╹┗━╸┗━┛┗━╸┗━┛╹┗╸╹ ╹
+
class PolyRingExpr(Expr):
r""" Endows with the algebraic structure of a polynomial ring.
@@ -93,6 +106,8 @@ class PolyRingExpr(Expr):
polynomials are scalars.
"""
+ # -- Properties ---
+
@property
def algebra(self) -> Algebra:
return Algebra.poly_ring
@@ -105,24 +120,46 @@ class PolyRingExpr(Expr):
f"shapes {self.left.shape} and {self.right.shape}.")
return self.left.shape
+ # --- Helper methods for construction ---
+
+ @classmethod
+ def from_powers(cls, variable: PolyRingExpr, exponents: Iterable[int]) -> PolyRingExpr:
+ """ Given a variable, say :math:`x`, and a list of exponents ``[0, 3, 5]``
+ returns a polynomial :math:`x^5 + x^3 + 1`.
+ """
+ nonzero_exponents = filter(lambda e: e != 0, exponents)
+ constant_term = PolyConst(1 if 0 in exponents else 0)
+ # FIXME: remove annoying 0
+ return sum((variable ** e for e in nonzero_exponents), constant_term)
+
+
+ @classmethod
+ def make_combinations(cls, variables: Iterable[Var], max_degree: int) -> Iterable[PolyRingExpr]:
+ """ Make a list of """
+ variables = chain(variables, (PolyConst(1),))
+ for comb in combinations_with_replacement(variables, max_degree):
+ yield prod(comb)
+
+ # --- Operator Overloading ---
+
def __add__(self, other):
self._assert_same_algebra(self, other)
- other = self._wrap(Number, Const, other)
+ other = self._wrap(Number, PolyConst, other)
return PolyAdd(self, other)
def __radd__(self, other):
self._assert_same_algebra(self, other)
- other = self._wrap(Number, Const, other)
+ other = self._wrap(Number, PolyConst, other)
return PolyAdd(other, self)
def __sub__(self, other):
self._assert_same_algebra(self, other)
- other = self._wrap(Number, Const, other)
+ other = self._wrap(Number, PolyConst, other)
return PolySub(self, other)
def __rsub__(self, other):
self._assert_same_algebra(self, other)
- other = self._wrap(Number, Const, other)
+ other = self._wrap(Number, PolyConst, other)
return PolyAdd(other, self)
def __neg__(self):
@@ -130,12 +167,12 @@ class PolyRingExpr(Expr):
def __mul__(self, other):
self._assert_same_algebra(self, other)
- other = self._wrap(Number, Const, other)
+ other = self._wrap(Number, PolyConst, other)
return PolyMul(self, other)
def __rmul__(self, other):
self._assert_same_algebra(self, other)
- other = self._wrap(Number, Const, other)
+ other = self._wrap(Number, PolyConst, other)
return PolyMul(other, self)
def __matmul__(self, other):
@@ -145,8 +182,8 @@ class PolyRingExpr(Expr):
self.__rmatmul__(other)
def __truediv__(self, other):
- other = self._wrap(Number, Const, other)
- if not isinstance(other, Const | Param):
+ other = self._wrap(Number, PolyConst, other)
+ if not isinstance(other, PolyConst | PolyParam):
raise AlgebraicError("Cannot divide by variables in polynomial ring.")
return PolyMul(self, other)
@@ -155,13 +192,18 @@ class PolyRingExpr(Expr):
raise AlgebraicError("Cannot perform right division in polynomial ring.")
def __pow__(self, other):
- other = self._wrap(Number, Const, other)
- if not isinstance(other, Const | Param):
+ other = self._wrap(Number, PolyConst, other)
+ if not isinstance(other, PolyConst | PolyParam):
raise AlgebraicError(f"Cannot raise to powers of type {type(other)} in "
"polynomial ring. Only constants and parameters are allowed.")
return PolyExp(self, other)
+class PolyConst(Const, PolyRingExpr): ...
+class PolyVar(Const, PolyRingExpr): ...
+class PolyParam(Param, PolyRingExpr): ...
+
+
class PolyAdd(BinaryOp, PolyRingExpr):
""" Addition operator between scalar polynomials. """
@@ -322,6 +364,9 @@ class MatrixExpr(Expr):
def algebra(self) -> Algebra:
return Algebra.matrix_ring
+ def at(self, entry: MatrixIndex, scalar_type: type) -> Expr:
+ raise NotImplementedError
+
def __add__(self, other):
self._assert_same_algebra(self, other)
other = self._wrap(Number, MatConst, other)
@@ -361,12 +406,12 @@ class MatrixExpr(Expr):
scalar = self._wrap_if_constant(scalar)
raise NotImplementedError
- def transpose(self):
+ def transpose(self) -> MatrixExpr:
""" Matrix transposition. """
return MatTranspose(self)
@property
- def T(self):
+ def T(self) -> MatrixExpr:
""" Shorthand for :py:meth:`mdpoly.algebra.MatrixAlgebra.transpose`. """
return self.transpose()