summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichael Schneeberger <michael.schneeberger@fhnw.ch>2022-07-30 11:34:55 +0200
committerMichael Schneeberger <michael.schneeberger@fhnw.ch>2022-07-30 11:34:55 +0200
commit05d372c6a05e0184fb02ad6fc4b612de7edd91a8 (patch)
treea720ee1e82fc10e6c2fc100343836b3b2d8fdb6e
parentadd eye, sum and symmetric operation (diff)
downloadpolymatrix-05d372c6a05e0184fb02ad6fc4b612de7edd91a8.tar.gz
polymatrix-05d372c6a05e0184fb02ad6fc4b612de7edd91a8.zip
add polynomial operations for sos optimization
-rw-r--r--polymatrix/__init__.py297
-rw-r--r--polymatrix/expression/accumulateexpr.py4
-rw-r--r--polymatrix/expression/divergenceexpr.py4
-rw-r--r--polymatrix/expression/impl/cacheexprimpl.py2
-rw-r--r--polymatrix/expression/impl/combinationsexprimpl.py2
-rw-r--r--polymatrix/expression/impl/divergenceexprimpl.py10
-rw-r--r--polymatrix/expression/impl/divisionexprimpl.py2
-rw-r--r--polymatrix/expression/impl/kktexprimpl.py12
-rw-r--r--polymatrix/expression/impl/linearinexprimpl.py4
-rw-r--r--polymatrix/expression/impl/linearinmonomialsinexprimpl.py9
-rw-r--r--polymatrix/expression/impl/linearmonomialsexprimpl.py9
-rw-r--r--polymatrix/expression/impl/parametrizeexprimpl.py9
-rw-r--r--polymatrix/expression/impl/parametrizetermsexprimpl.py8
-rw-r--r--polymatrix/expression/impl/quadraticinexprimpl.py1
-rw-r--r--polymatrix/expression/impl/setelementatexprimpl.py10
-rw-r--r--polymatrix/expression/impl/sosmonomialsexprimpl.py (renamed from polymatrix/expression/impl/linearin2exprimpl.py)6
-rw-r--r--polymatrix/expression/impl/substituteexprimpl.py10
-rw-r--r--polymatrix/expression/impl/subtractmonomialsexprimpl.py9
-rw-r--r--polymatrix/expression/impl/truncateexprimpl.py10
-rw-r--r--polymatrix/expression/init/initaccumulateexpr.py25
-rw-r--r--polymatrix/expression/init/initcombinationsexpr.py4
-rw-r--r--polymatrix/expression/init/initdivergenceexpr.py13
-rw-r--r--polymatrix/expression/init/initkktexpr.py17
-rw-r--r--polymatrix/expression/init/initlinearin2expr.py12
-rw-r--r--polymatrix/expression/init/initlinearinexpr.py6
-rw-r--r--polymatrix/expression/init/initlinearinmonomialsinexpr.py12
-rw-r--r--polymatrix/expression/init/initlinearmonomialsexpr.py12
-rw-r--r--polymatrix/expression/init/initparametrizeexpr.py15
-rw-r--r--polymatrix/expression/init/initparametrizetermsexpr.py8
-rw-r--r--polymatrix/expression/init/initquadraticinexpr.py2
-rw-r--r--polymatrix/expression/init/initsetelementatexpr.py14
-rw-r--r--polymatrix/expression/init/initsosmonomialsexpr.py12
-rw-r--r--polymatrix/expression/init/initsubstituteexpr.py27
-rw-r--r--polymatrix/expression/init/initsubtractmonomialsexpr.py12
-rw-r--r--polymatrix/expression/init/inittruncateexpr.py14
-rw-r--r--polymatrix/expression/init/initvstackexpr.py13
-rw-r--r--polymatrix/expression/kktexpr.py4
-rw-r--r--polymatrix/expression/linearin2expr.py4
-rw-r--r--polymatrix/expression/linearinexpr.py4
-rw-r--r--polymatrix/expression/linearinmonomialsinexpr.py4
-rw-r--r--polymatrix/expression/linearmatrixinexpr.py4
-rw-r--r--polymatrix/expression/linearmonomialsexpr.py4
-rw-r--r--polymatrix/expression/mixins/addauxequationsexprmixin.py58
-rw-r--r--polymatrix/expression/mixins/assertdegreeexprmixin.py49
-rw-r--r--polymatrix/expression/mixins/blockdiagexprmixin.py8
-rw-r--r--polymatrix/expression/mixins/combinationsexprmixin.py60
-rw-r--r--polymatrix/expression/mixins/derivativeexprmixin.py459
-rw-r--r--polymatrix/expression/mixins/derivativekeymixin.py1
-rw-r--r--polymatrix/expression/mixins/determinantexprmixin.py2
-rw-r--r--polymatrix/expression/mixins/divergenceexprmixin.py65
-rw-r--r--polymatrix/expression/mixins/divisionexprmixin.py6
-rw-r--r--polymatrix/expression/mixins/elemmultexprmixin.py77
-rw-r--r--polymatrix/expression/mixins/evalexprmixin.py17
-rw-r--r--polymatrix/expression/mixins/expressionmixin.py155
-rw-r--r--polymatrix/expression/mixins/fromarrayexprmixin.py15
-rw-r--r--polymatrix/expression/mixins/kktexprmixin.py191
-rw-r--r--polymatrix/expression/mixins/linearinexprmixin.py74
-rw-r--r--polymatrix/expression/mixins/linearinmutipleexprmixin.py144
-rw-r--r--polymatrix/expression/mixins/linearmatrixinexprmixin.py61
-rw-r--r--polymatrix/expression/mixins/linearmonomialsexprmixin.py67
-rw-r--r--polymatrix/expression/mixins/matrixmultexprmixin.py25
-rw-r--r--polymatrix/expression/mixins/oldlinearexprmixin.py131
-rw-r--r--polymatrix/expression/mixins/parametrizeexprmixin.py58
-rw-r--r--polymatrix/expression/mixins/parametrizetermsexprmixin.py54
-rw-r--r--polymatrix/expression/mixins/quadraticinexprmixin.py67
-rw-r--r--polymatrix/expression/mixins/quadraticmonomialsexprmixin.py69
-rw-r--r--polymatrix/expression/mixins/quadraticreprexprmixin.py87
-rw-r--r--polymatrix/expression/mixins/setelementatexprmixin.py69
-rw-r--r--polymatrix/expression/mixins/squeezeexprmixin.py1
-rw-r--r--polymatrix/expression/mixins/substituteexprmixin.py107
-rw-r--r--polymatrix/expression/mixins/subtractmonomialsexprmixin.py60
-rw-r--r--polymatrix/expression/mixins/sumexprmixin.py6
-rw-r--r--polymatrix/expression/mixins/symmetricexprmixin.py15
-rw-r--r--polymatrix/expression/mixins/toquadraticexprmixin.py1
-rw-r--r--polymatrix/expression/mixins/truncateexprmixin.py (renamed from polymatrix/expression/mixins/accumulateexprmixin.py)39
-rw-r--r--polymatrix/expression/mixins/vstackexprmixin.py14
-rw-r--r--polymatrix/expression/parametrizeexpr.py4
-rw-r--r--polymatrix/expression/parametrizetermsexpr.py (renamed from polymatrix/expression/parametrizesymbolsexpr.py)2
-rw-r--r--polymatrix/expression/quadraticinexpr.py4
-rw-r--r--polymatrix/expression/setelementatexpr.py4
-rw-r--r--polymatrix/expression/sosmonomialsexpr.py4
-rw-r--r--polymatrix/expression/substituteexpr.py4
-rw-r--r--polymatrix/expression/subtractmonomialsexpr.py4
-rw-r--r--polymatrix/expression/truncateexpr.py4
-rw-r--r--polymatrix/expression/utils/getderivativemonomials.py114
-rw-r--r--polymatrix/expression/utils/getmonomialindices.py19
-rw-r--r--polymatrix/expression/utils/gettupleremainder.py9
-rw-r--r--polymatrix/expression/utils/getvariableindices.py8
-rw-r--r--polymatrix/expression/utils/mergemonomialindices.py27
-rw-r--r--polymatrix/expression/utils/multiplypolynomial.py24
-rw-r--r--polymatrix/expression/utils/sortmonomialindices.py5
-rw-r--r--polymatrix/expression/utils/sortmonomials.py5
-rw-r--r--polymatrix/expression/utils/splitmonomialindices.py27
-rw-r--r--polymatrix/expression/utils/subtractmonomialindices.py18
94 files changed, 1955 insertions, 1303 deletions
diff --git a/polymatrix/__init__.py b/polymatrix/__init__.py
index 85def18..aa96d21 100644
--- a/polymatrix/__init__.py
+++ b/polymatrix/__init__.py
@@ -8,7 +8,6 @@ import scipy.sparse
from polymatrix.expression.expression import Expression
from polymatrix.expression.expressionstate import ExpressionState
-from polymatrix.expression.init.initaccumulateexpr import init_accumulate_expr
from polymatrix.expression.init.initblockdiagexpr import init_block_diag_expr
from polymatrix.expression.init.initexpression import init_expression
from polymatrix.expression.init.initeyeexpr import init_eye_expr
@@ -38,31 +37,31 @@ def from_polymatrix(
)
-def accumulate(
- expr,
- func,
- initial = None,
-):
- def lifted_func(acc, polymat: PolyMatrix):
+# def accumulate(
+# expr,
+# func,
+# initial = None,
+# ):
+# def lifted_func(acc, polymat: PolyMatrix):
- # # print(f'{terms=}')
- # print(f'{terms=}')
-
- lifeted_val = init_expression(
- underlying=init_from_terms_expr(
- terms=polymat.terms,
- shape=polymat.shape,
- ),
- )
- return func(acc, lifeted_val)
+# # # print(f'{terms=}')
+# # print(f'{terms=}')
+
+# lifeted_val = init_expression(
+# underlying=init_from_terms_expr(
+# terms=polymat.terms,
+# shape=polymat.shape,
+# ),
+# )
+# return func(acc, lifeted_val)
- return init_expression(
- underlying=init_accumulate_expr(
- underlying=expr.underlying,
- acc_func=lifted_func,
- initial=initial,
- ),
- )
+# return init_expression(
+# underlying=init_accumulate_expr(
+# underlying=expr.underlying,
+# acc_func=lifted_func,
+# initial=initial,
+# ),
+# )
def v_stack(
expressions: tuple[Expression],
@@ -96,20 +95,14 @@ def eye(
)
-# def kkt(
-# cost: Expression,
-# variables: Expression,
-# equality: Expression = None,
-# inequality: Expression = None,
+# def sos_matrix(
+# underlying: Expression,
# ):
-# return init_expression(
-# init_kkt_expr(
-# cost=cost,
-# equality=equality,
-# variables=variables,
-# inequality=inequality,
-# )
-# )
+# def func(state: ExpressionState):
+# underlying =
+
+# return init_state_monad(func)
+
def kkt_equality(
variables: Expression,
@@ -293,37 +286,6 @@ def kkt_inequality(
return init_state_monad(func)
-# def to_polymatrix(
-# expr: Expression,
-# ):
-# def func(state: ExpressionState):
-# state, polymatrix = expr.apply(state)
-
-# return state, polymatrix
-
-# return init_state_monad(func)
-
-# def to_linear_matrix(
-# expr: Expression,
-# variables: Expression,
-# ) -> StateMonad[ExpressionState, tuple[Expression, ...]]:
-# def func(state: ExpressionState):
-# state, variable_indices = get_variable_indices(state, variables)
-
-# def gen_matrices():
-# for variable_index in variable_indices:
-# yield init_linear_matrix_in_expr(
-# underlying=expr,
-# variable=variable_index,
-# )
-
-# matrices = tuple(gen_matrices())
-
-# return state, matrices
-
-# return StateMonad.init(func)
-
-
def shape(
expr: Expression,
) -> StateMonadMixin[ExpressionState, tuple[int, ...]]:
@@ -337,8 +299,8 @@ def shape(
@dataclasses.dataclass
class MatrixEquations:
- matrix_equations: tuple[tuple[np.ndarray, ...], ...]
- auxillary_matrix_equations: typing.Optional[tuple[np.ndarray, ...]]
+ matrix_equations: tuple[dict[int, np.ndarray], ...]
+ auxillary_matrix_equations: typing.Optional[dict[int, np.ndarray]]
variable_index: tuple[int, ...]
state: ExpressionState
@@ -351,15 +313,33 @@ class MatrixEquations:
if index < len(self.auxillary_matrix_equations):
yield self.auxillary_matrix_equations[index]
- matrix_1 = np.vstack(tuple(gen_matrices(0)))
- matrix_2 = np.vstack(tuple(gen_matrices(1)))
- matrix_3 = scipy.sparse.vstack(tuple(gen_matrices(2)))
+ indices = set(key for equations in self.matrix_equations + (self.auxillary_matrix_equations,) for key in equations.keys())
- return (matrix_1, matrix_2, matrix_3)
+ def gen_matrices():
+ for index in indices:
+ if index <= 1:
+ yield index, np.vstack(tuple(gen_matrices(index)))
+ else:
+ yield index, scipy.sparse.vstack(tuple(gen_matrices(index)))
+
+ # matrix_1 = np.vstack(tuple(gen_matrices(0)))
+ # matrix_2 = np.vstack(tuple(gen_matrices(1)))
+ # matrix_3 = scipy.sparse.vstack(tuple(gen_matrices(2)))
+
+ return dict(gen_matrices())
def get_value(self, variable, value):
variable_indices = get_variable_indices(self.state, variable)[1]
- value_index = list(self.variable_index.index(variable_index) for variable_index in variable_indices)
+
+ def gen_value_index():
+ for variable_index in variable_indices:
+ try:
+ yield self.variable_index.index(variable_index)
+ except ValueError:
+ raise ValueError(f'{variable_index} not found in {self.variable_index}')
+
+ value_index = list(gen_value_index())
+
return value[value_index]
def set_value(self, variable, value):
@@ -371,112 +351,122 @@ class MatrixEquations:
def to_matrix_equations(
- expr: tuple[Expression],
+ expressions: tuple[Expression],
+ variables: Expression = None,
) -> StateMonadMixin[ExpressionState, tuple[tuple[tuple[np.ndarray, ...], ...], tuple[int, ...]]]:
- if isinstance(expr, Expression):
- expr = (expr,)
+ if isinstance(expressions, Expression):
+ expressions = (expressions,)
def func(state: ExpressionState):
- def acc_underlying(acc, v):
+ def acc_underlying_application(acc, v):
state, underlying_list = acc
state, underlying = v.apply(state)
- assert underlying.shape[1] == 1, f'{underlying.shape[1]} is not 1'
+ assert underlying.shape[1] == 1, f'{underlying.shape[1]=} is not 1'
return state, underlying_list + (underlying,)
*_, (state, underlying_list) = tuple(itertools.accumulate(
- expr,
- acc_underlying,
+ expressions,
+ acc_underlying_application,
initial=(state, tuple()),
))
- # state, underlying = expr.apply(state)
+ if variables is None:
- def gen_used_variables():
- def gen_used_auxillary_variables(considered):
- monomial_terms = state.auxillary_equations[considered[-1]]
- for monomial in monomial_terms.keys():
- for variable in monomial:
- yield variable
+ def gen_used_variables():
+ def gen_used_auxillary_variables(considered):
+ monomial_terms = state.auxillary_equations[considered[-1]]
+ for monomial in monomial_terms.keys():
+ for variable in monomial:
+ yield variable
- if variable not in considered and variable in state.auxillary_equations:
- yield from gen_used_auxillary_variables(considered + (variable,))
+ if variable not in considered and variable in state.auxillary_equations:
+ yield from gen_used_auxillary_variables(considered + (variable,))
- for underlying in underlying_list:
- for row in range(underlying.shape[0]):
- for col in range(underlying.shape[1]):
+ for underlying in underlying_list:
+ for row in range(underlying.shape[0]):
+ for col in range(underlying.shape[1]):
- try:
- underlying_terms = underlying.get_poly(row, col)
- except KeyError:
- continue
+ try:
+ underlying_terms = underlying.get_poly(row, col)
+ except KeyError:
+ continue
- for monomial in underlying_terms.keys():
- for variable in monomial:
- yield variable
+ for monomial in underlying_terms.keys():
+ for variable, _ in monomial:
+ yield variable
- if variable in state.auxillary_equations:
- yield from gen_used_auxillary_variables((variable,))
+ if variable in state.auxillary_equations:
+ yield from gen_used_auxillary_variables((variable,))
+
+ ordered_variable_index = tuple(sorted(set(gen_used_variables())))
+
+ else:
+ state, ordered_variable_index = get_variable_indices(state, variables)
- used_variables = set(gen_used_variables())
- ordered_variable_index = tuple(sorted(used_variables))
variable_index_map = {old: new for new, old in enumerate(ordered_variable_index)}
n_param = len(ordered_variable_index)
+ @dataclasses.dataclass
+ class MatrixBuffer:
+ data: dict
+ n_row: int
+ n_param: int
+
+ def add_buffer(self, index: int):
+ if index <= 1:
+ buffer = np.zeros((self.n_row, self.n_param**index), dtype=np.double)
+
+ else:
+ buffer = scipy.sparse.dok_array((self.n_row, self.n_param**index), dtype=np.double)
+
+ self.data[index] = buffer
+
+ def add(self, row: int, col: int, index: int, value: float):
+ if index not in self.data:
+ self.add_buffer(index)
+
+ self.data[index][row, col] = value
+
+ def __getitem__(self, key):
+ if key not in self.data:
+ self.add_buffer(key)
+
+ return self.data[key]
+
def gen_underlying_matrices():
for underlying in underlying_list:
+
n_row = underlying.shape[0]
- A = np.zeros((n_row, 1), dtype=np.double)
- B = np.zeros((n_row, n_param), dtype=np.double)
- C = scipy.sparse.dok_array((n_row, n_param**2), dtype=np.double)
-
- # def populate_matrices(monomial_terms, row):
- # for monomial, value in monomial_terms.items():
- # new_monomial = tuple(variable_index_map[var] for var in monomial)
- # col = monomial_to_index(n_param, new_monomial)
-
- # match len(new_monomial):
- # case 0:
- # A[row, col] = value
- # case 1:
- # B[row, col] = value
- # case 2:
- # C[row, col] = value
- # case _:
- # raise Exception(f'illegal case {new_monomial=}')
-
- for row in range(underlying.shape[0]):
+ buffer = MatrixBuffer(
+ data={},
+ n_row=n_row,
+ n_param=n_param,
+ )
+
+ for row in range(n_row):
try:
underlying_terms = underlying.get_poly(row, 0)
except KeyError:
continue
-
- # populate_matrices(
- # monomial_terms=underlying_terms,
- # row=row,
- # )
for monomial, value in underlying_terms.items():
- new_monomial = tuple(variable_index_map[var] for var in monomial)
+ try:
+ new_monomial = tuple(variable_index_map[var] for var, count in monomial for _ in range(count))
+ except KeyError:
+ raise KeyError(f'{monomial=} is incompatible with {variable_index_map=}')
col = monomial_to_index(n_param, new_monomial)
- match len(new_monomial):
- case 0:
- A[row, col] = value
- case 1:
- B[row, col] = value
- case 2:
- C[row, col] = value
- case _:
- raise Exception(f'illegal case {new_monomial=}')
+ buffer.add(row, col, sum(count for _, count in monomial), value)
- yield A, B, C
+ # yield A, B, C
+ yield buffer
underlying_matrices = tuple(gen_underlying_matrices())
@@ -495,26 +485,21 @@ def to_matrix_equations(
auxillary_matrix_equations = None
else:
- A = np.zeros((n_row, 1), dtype=np.double)
- B = np.zeros((n_row, n_param), dtype=np.double)
- C = scipy.sparse.dok_array((n_row, n_param**2), dtype=np.double)
+ buffer = MatrixBuffer(
+ data={},
+ n_row=n_row,
+ n_param=n_param,
+ )
for row, (key, monomial_terms) in enumerate(auxillary_equations):
for monomial, value in monomial_terms.items():
- new_monomial = tuple(variable_index_map[var] for var in monomial)
+ # new_monomial = tuple(variable_index_map[var] for var, _ in monomial)
+ new_monomial = tuple(variable_index_map[var] for var, count in monomial for _ in range(count))
col = monomial_to_index(n_param, new_monomial)
- match len(new_monomial):
- case 0:
- A[row, col] = value
- case 1:
- B[row, col] = value
- case 2:
- C[row, col] = value
- case _:
- raise Exception(f'illegal case {new_monomial=}')
-
- auxillary_matrix_equations = (A, B, C)
+ buffer.add(row, col, sum(count for _, count in monomial), value)
+
+ auxillary_matrix_equations = buffer
result = MatrixEquations(
matrix_equations=underlying_matrices,
diff --git a/polymatrix/expression/accumulateexpr.py b/polymatrix/expression/accumulateexpr.py
deleted file mode 100644
index efcbc39..0000000
--- a/polymatrix/expression/accumulateexpr.py
+++ /dev/null
@@ -1,4 +0,0 @@
-from polymatrix.expression.mixins.accumulateexprmixin import AccumulateExprMixin
-
-class AccumulateExpr(AccumulateExprMixin):
- pass
diff --git a/polymatrix/expression/divergenceexpr.py b/polymatrix/expression/divergenceexpr.py
new file mode 100644
index 0000000..9207bd2
--- /dev/null
+++ b/polymatrix/expression/divergenceexpr.py
@@ -0,0 +1,4 @@
+from polymatrix.expression.mixins.divergenceexprmixin import DivergenceExprMixin
+
+class DivergenceExpr(DivergenceExprMixin):
+ pass
diff --git a/polymatrix/expression/impl/cacheexprimpl.py b/polymatrix/expression/impl/cacheexprimpl.py
index a6a74a1..f6e45d3 100644
--- a/polymatrix/expression/impl/cacheexprimpl.py
+++ b/polymatrix/expression/impl/cacheexprimpl.py
@@ -3,6 +3,6 @@ from polymatrix.expression.cacheexpr import CacheExpr
from polymatrix.expression.mixins.expressionbasemixin import ExpressionBaseMixin
-@dataclass_abc.dataclass_abc(frozen=True)
+@dataclass_abc.dataclass_abc(frozen=True, eq=False)
class CacheExprImpl(CacheExpr):
underlying: ExpressionBaseMixin
diff --git a/polymatrix/expression/impl/combinationsexprimpl.py b/polymatrix/expression/impl/combinationsexprimpl.py
index 2e809a2..2438e01 100644
--- a/polymatrix/expression/impl/combinationsexprimpl.py
+++ b/polymatrix/expression/impl/combinationsexprimpl.py
@@ -5,5 +5,5 @@ from polymatrix.expression.mixins.expressionbasemixin import ExpressionBaseMixin
@dataclass_abc.dataclass_abc(frozen=True)
class CombinationsExprImpl(CombinationsExpr):
- underlying: ExpressionBaseMixin
+ monomials: ExpressionBaseMixin
number: int
diff --git a/polymatrix/expression/impl/divergenceexprimpl.py b/polymatrix/expression/impl/divergenceexprimpl.py
new file mode 100644
index 0000000..0ea9cdb
--- /dev/null
+++ b/polymatrix/expression/impl/divergenceexprimpl.py
@@ -0,0 +1,10 @@
+import dataclass_abc
+from polymatrix.expression.divergenceexpr import DivergenceExpr
+
+from polymatrix.expression.mixins.expressionbasemixin import ExpressionBaseMixin
+from typing import Union
+
+@dataclass_abc.dataclass_abc(frozen=True)
+class DivergenceExprImpl(DivergenceExpr):
+ underlying: ExpressionBaseMixin
+ variables: tuple
diff --git a/polymatrix/expression/impl/divisionexprimpl.py b/polymatrix/expression/impl/divisionexprimpl.py
index c2c8966..639210e 100644
--- a/polymatrix/expression/impl/divisionexprimpl.py
+++ b/polymatrix/expression/impl/divisionexprimpl.py
@@ -3,7 +3,7 @@ from polymatrix.expression.divisionexpr import DivisionExpr
from polymatrix.expression.mixins.expressionbasemixin import ExpressionBaseMixin
-@dataclass_abc.dataclass_abc(frozen=True)
+@dataclass_abc.dataclass_abc(frozen=True, eq=False)
class DivisionExprImpl(DivisionExpr):
left: ExpressionBaseMixin
right: ExpressionBaseMixin
diff --git a/polymatrix/expression/impl/kktexprimpl.py b/polymatrix/expression/impl/kktexprimpl.py
deleted file mode 100644
index 415b206..0000000
--- a/polymatrix/expression/impl/kktexprimpl.py
+++ /dev/null
@@ -1,12 +0,0 @@
-import dataclass_abc
-from polymatrix.expression.kktexpr import KKTExpr
-
-from polymatrix.expression.mixins.expressionmixin import ExpressionMixin
-from polymatrix.expression.mixins.expressionbasemixin import ExpressionBaseMixin
-
-@dataclass_abc.dataclass_abc(frozen=True)
-class KKTExprImpl(KKTExpr):
- cost: ExpressionMixin
- equality: ExpressionMixin
- inequality: ExpressionMixin
- variables: ExpressionBaseMixin
diff --git a/polymatrix/expression/impl/linearinexprimpl.py b/polymatrix/expression/impl/linearinexprimpl.py
index d97051f..346b19b 100644
--- a/polymatrix/expression/impl/linearinexprimpl.py
+++ b/polymatrix/expression/impl/linearinexprimpl.py
@@ -6,4 +6,6 @@ from polymatrix.expression.mixins.expressionbasemixin import ExpressionBaseMixin
@dataclass_abc.dataclass_abc(frozen=True)
class LinearInExprImpl(LinearInExpr):
underlying: ExpressionBaseMixin
- variables: tuple
+ monomials: ExpressionBaseMixin
+ variables: ExpressionBaseMixin
+ ignore_unmatched: bool
diff --git a/polymatrix/expression/impl/linearinmonomialsinexprimpl.py b/polymatrix/expression/impl/linearinmonomialsinexprimpl.py
new file mode 100644
index 0000000..537f164
--- /dev/null
+++ b/polymatrix/expression/impl/linearinmonomialsinexprimpl.py
@@ -0,0 +1,9 @@
+import dataclass_abc
+from polymatrix.expression.linearinmonomialsinexpr import LinearInMonomialsInExpr
+
+from polymatrix.expression.mixins.expressionbasemixin import ExpressionBaseMixin
+
+@dataclass_abc.dataclass_abc(frozen=True)
+class LinearInMonomialsInExprImpl(LinearInMonomialsInExpr):
+ underlying: ExpressionBaseMixin
+ variables: tuple
diff --git a/polymatrix/expression/impl/linearmonomialsexprimpl.py b/polymatrix/expression/impl/linearmonomialsexprimpl.py
new file mode 100644
index 0000000..905ec77
--- /dev/null
+++ b/polymatrix/expression/impl/linearmonomialsexprimpl.py
@@ -0,0 +1,9 @@
+import dataclass_abc
+from polymatrix.expression.linearmonomialsexpr import LinearMonomialsExpr
+
+from polymatrix.expression.mixins.expressionbasemixin import ExpressionBaseMixin
+
+@dataclass_abc.dataclass_abc(frozen=True)
+class LinearMonomialsExprImpl(LinearMonomialsExpr):
+ underlying: ExpressionBaseMixin
+ variables: tuple
diff --git a/polymatrix/expression/impl/parametrizeexprimpl.py b/polymatrix/expression/impl/parametrizeexprimpl.py
new file mode 100644
index 0000000..a1fca6e
--- /dev/null
+++ b/polymatrix/expression/impl/parametrizeexprimpl.py
@@ -0,0 +1,9 @@
+import dataclass_abc
+from polymatrix.expression.parametrizeexpr import ParametrizeExpr
+
+from polymatrix.expression.mixins.expressionbasemixin import ExpressionBaseMixin
+
+@dataclass_abc.dataclass_abc(frozen=True, eq=False)
+class ParametrizeExprImpl(ParametrizeExpr):
+ underlying: ExpressionBaseMixin
+ name: str
diff --git a/polymatrix/expression/impl/parametrizetermsexprimpl.py b/polymatrix/expression/impl/parametrizetermsexprimpl.py
index b851306..32beac2 100644
--- a/polymatrix/expression/impl/parametrizetermsexprimpl.py
+++ b/polymatrix/expression/impl/parametrizetermsexprimpl.py
@@ -1,10 +1,10 @@
import dataclass_abc
-from polymatrix.expression.parametrizesymbolsexpr import ParametrizeSymbolsExpr
+from polymatrix.expression.parametrizetermsexpr import ParametrizeTermsExpr
from polymatrix.expression.mixins.expressionbasemixin import ExpressionBaseMixin
@dataclass_abc.dataclass_abc(frozen=True)
-class ParametrizeTermsExprImpl(ParametrizeSymbolsExpr):
- name: str
+class ParametrizeTermsExprImpl(ParametrizeTermsExpr):
+ # name: str
underlying: ExpressionBaseMixin
- variables: tuple
+ # variables: tuple
diff --git a/polymatrix/expression/impl/quadraticinexprimpl.py b/polymatrix/expression/impl/quadraticinexprimpl.py
index 8c7ac17..da8fa21 100644
--- a/polymatrix/expression/impl/quadraticinexprimpl.py
+++ b/polymatrix/expression/impl/quadraticinexprimpl.py
@@ -6,4 +6,5 @@ from polymatrix.expression.mixins.expressionbasemixin import ExpressionBaseMixin
@dataclass_abc.dataclass_abc(frozen=True)
class QuadraticInExprImpl(QuadraticInExpr):
underlying: ExpressionBaseMixin
+ monomials: ExpressionBaseMixin
variables: tuple
diff --git a/polymatrix/expression/impl/setelementatexprimpl.py b/polymatrix/expression/impl/setelementatexprimpl.py
new file mode 100644
index 0000000..6d070ac
--- /dev/null
+++ b/polymatrix/expression/impl/setelementatexprimpl.py
@@ -0,0 +1,10 @@
+import dataclass_abc
+from polymatrix.expression.setelementatexpr import SetElementAtExpr
+
+from polymatrix.expression.mixins.expressionbasemixin import ExpressionBaseMixin
+
+@dataclass_abc.dataclass_abc(frozen=True)
+class SetElementAtExprImpl(SetElementAtExpr):
+ underlying: ExpressionBaseMixin
+ index: tuple
+ value: ExpressionBaseMixin
diff --git a/polymatrix/expression/impl/linearin2exprimpl.py b/polymatrix/expression/impl/sosmonomialsexprimpl.py
index 34b42bf..9e43b23 100644
--- a/polymatrix/expression/impl/linearin2exprimpl.py
+++ b/polymatrix/expression/impl/sosmonomialsexprimpl.py
@@ -1,9 +1,9 @@
import dataclass_abc
-from polymatrix.expression.linearin2expr import LinearIn2Expr
+from polymatrix.expression.sosmonomialsexpr import SOSMonomialsExpr
from polymatrix.expression.mixins.expressionbasemixin import ExpressionBaseMixin
@dataclass_abc.dataclass_abc(frozen=True)
-class LinearIn2ExprImpl(LinearIn2Expr):
+class SOSMonomialsExprImpl(SOSMonomialsExpr):
underlying: ExpressionBaseMixin
- expression: ExpressionBaseMixin
+ variables: tuple
diff --git a/polymatrix/expression/impl/substituteexprimpl.py b/polymatrix/expression/impl/substituteexprimpl.py
new file mode 100644
index 0000000..1eae940
--- /dev/null
+++ b/polymatrix/expression/impl/substituteexprimpl.py
@@ -0,0 +1,10 @@
+import dataclass_abc
+from polymatrix.expression.substituteexpr import SubstituteExpr
+
+from polymatrix.expression.mixins.expressionbasemixin import ExpressionBaseMixin
+
+@dataclass_abc.dataclass_abc(frozen=True)
+class SubstituteExprImpl(SubstituteExpr):
+ underlying: ExpressionBaseMixin
+ variables: tuple
+ substitutions: tuple
diff --git a/polymatrix/expression/impl/subtractmonomialsexprimpl.py b/polymatrix/expression/impl/subtractmonomialsexprimpl.py
new file mode 100644
index 0000000..211f7b1
--- /dev/null
+++ b/polymatrix/expression/impl/subtractmonomialsexprimpl.py
@@ -0,0 +1,9 @@
+import dataclass_abc
+from polymatrix.expression.subtractmonomialsexpr import SubtractMonomialsExpr
+
+from polymatrix.expression.mixins.expressionbasemixin import ExpressionBaseMixin
+
+@dataclass_abc.dataclass_abc(frozen=True)
+class SubtractMonomialsExprImpl(SubtractMonomialsExpr):
+ underlying: ExpressionBaseMixin
+ monomials: ExpressionBaseMixin
diff --git a/polymatrix/expression/impl/truncateexprimpl.py b/polymatrix/expression/impl/truncateexprimpl.py
new file mode 100644
index 0000000..01c47d0
--- /dev/null
+++ b/polymatrix/expression/impl/truncateexprimpl.py
@@ -0,0 +1,10 @@
+import dataclass_abc
+from polymatrix.expression.truncateexpr import TruncateExpr
+
+from polymatrix.expression.mixins.expressionbasemixin import ExpressionBaseMixin
+
+@dataclass_abc.dataclass_abc(frozen=True)
+class TruncateExprImpl(TruncateExpr):
+ underlying: ExpressionBaseMixin
+ variables: ExpressionBaseMixin
+ degree: int
diff --git a/polymatrix/expression/init/initaccumulateexpr.py b/polymatrix/expression/init/initaccumulateexpr.py
deleted file mode 100644
index 30297bf..0000000
--- a/polymatrix/expression/init/initaccumulateexpr.py
+++ /dev/null
@@ -1,25 +0,0 @@
-import dataclass_abc
-import typing
-
-from polymatrix.expression.mixins.expressionbasemixin import ExpressionBaseMixin
-from polymatrix.expression.accumulateexpr import AccumulateExpr
-
-
-def init_accumulate_expr(
- underlying: ExpressionBaseMixin,
- acc_func: typing.Callable,
- initial: ExpressionBaseMixin = None,
-):
-
- @dataclass_abc.dataclass_abc(frozen=True)
- class AccumulateExprImpl(AccumulateExpr):
- underlying: ExpressionBaseMixin
- initial: ExpressionBaseMixin
-
- def acc_func(self, acc, v):
- return acc_func(acc, v)
-
- return AccumulateExprImpl(
- underlying=underlying,
- initial=initial,
- )
diff --git a/polymatrix/expression/init/initcombinationsexpr.py b/polymatrix/expression/init/initcombinationsexpr.py
index 7a1df2f..80bf4d0 100644
--- a/polymatrix/expression/init/initcombinationsexpr.py
+++ b/polymatrix/expression/init/initcombinationsexpr.py
@@ -3,10 +3,10 @@ from polymatrix.expression.impl.combinationsexprimpl import CombinationsExprImpl
def init_combinations_expr(
- underlying: ExpressionBaseMixin,
+ monomials: ExpressionBaseMixin,
number: int,
):
return CombinationsExprImpl(
- underlying=underlying,
+ monomials=monomials,
number=number,
)
diff --git a/polymatrix/expression/init/initdivergenceexpr.py b/polymatrix/expression/init/initdivergenceexpr.py
new file mode 100644
index 0000000..d1aa02c
--- /dev/null
+++ b/polymatrix/expression/init/initdivergenceexpr.py
@@ -0,0 +1,13 @@
+from polymatrix.expression.mixins.expressionbasemixin import ExpressionBaseMixin
+from typing import Union
+from polymatrix.expression.impl.divergenceexprimpl import DivergenceExprImpl
+
+
+def init_divergence_expr(
+ underlying: ExpressionBaseMixin,
+ variables: tuple,
+):
+ return DivergenceExprImpl(
+ underlying=underlying,
+ variables=variables,
+)
diff --git a/polymatrix/expression/init/initkktexpr.py b/polymatrix/expression/init/initkktexpr.py
deleted file mode 100644
index f634d97..0000000
--- a/polymatrix/expression/init/initkktexpr.py
+++ /dev/null
@@ -1,17 +0,0 @@
-from polymatrix.expression.mixins.expressionmixin import ExpressionMixin
-from polymatrix.expression.mixins.expressionbasemixin import ExpressionBaseMixin
-from polymatrix.expression.impl.kktexprimpl import KKTExprImpl
-
-
-def init_kkt_expr(
- cost: ExpressionMixin,
- variables: ExpressionBaseMixin,
- equality: ExpressionMixin = None,
- inequality: ExpressionMixin = None,
-):
- return KKTExprImpl(
- cost=cost,
- equality=equality,
- inequality=inequality,
- variables=variables,
-)
diff --git a/polymatrix/expression/init/initlinearin2expr.py b/polymatrix/expression/init/initlinearin2expr.py
deleted file mode 100644
index 7225510..0000000
--- a/polymatrix/expression/init/initlinearin2expr.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from polymatrix.expression.mixins.expressionbasemixin import ExpressionBaseMixin
-from polymatrix.expression.impl.linearin2exprimpl import LinearIn2ExprImpl
-
-
-def init_linear_in2_expr(
- underlying: ExpressionBaseMixin,
- expression: ExpressionBaseMixin,
-):
- return LinearIn2ExprImpl(
- underlying=underlying,
- expression=expression,
-)
diff --git a/polymatrix/expression/init/initlinearinexpr.py b/polymatrix/expression/init/initlinearinexpr.py
index f7f76e4..5cd172c 100644
--- a/polymatrix/expression/init/initlinearinexpr.py
+++ b/polymatrix/expression/init/initlinearinexpr.py
@@ -4,9 +4,13 @@ from polymatrix.expression.impl.linearinexprimpl import LinearInExprImpl
def init_linear_in_expr(
underlying: ExpressionBaseMixin,
- variables: tuple,
+ monomials: ExpressionBaseMixin,
+ variables: ExpressionBaseMixin,
+ ignore_unmatched: bool = None,
):
return LinearInExprImpl(
underlying=underlying,
+ monomials=monomials,
variables=variables,
+ ignore_unmatched = ignore_unmatched,
)
diff --git a/polymatrix/expression/init/initlinearinmonomialsinexpr.py b/polymatrix/expression/init/initlinearinmonomialsinexpr.py
new file mode 100644
index 0000000..77208a7
--- /dev/null
+++ b/polymatrix/expression/init/initlinearinmonomialsinexpr.py
@@ -0,0 +1,12 @@
+from polymatrix.expression.mixins.expressionbasemixin import ExpressionBaseMixin
+from polymatrix.expression.impl.linearinmonomialsinexprimpl import LinearInMonomialsInExprImpl
+
+
+def init_linear_in_monomials_in(
+ underlying: ExpressionBaseMixin,
+ variables: tuple,
+):
+ return LinearInMonomialsInExprImpl(
+ underlying=underlying,
+ variables=variables,
+)
diff --git a/polymatrix/expression/init/initlinearmonomialsexpr.py b/polymatrix/expression/init/initlinearmonomialsexpr.py
new file mode 100644
index 0000000..f116562
--- /dev/null
+++ b/polymatrix/expression/init/initlinearmonomialsexpr.py
@@ -0,0 +1,12 @@
+from polymatrix.expression.mixins.expressionbasemixin import ExpressionBaseMixin
+from polymatrix.expression.impl.linearmonomialsexprimpl import LinearMonomialsExprImpl
+
+
+def init_linear_monomials_expr(
+ underlying: ExpressionBaseMixin,
+ variables: tuple,
+):
+ return LinearMonomialsExprImpl(
+ underlying=underlying,
+ variables=variables,
+)
diff --git a/polymatrix/expression/init/initparametrizeexpr.py b/polymatrix/expression/init/initparametrizeexpr.py
new file mode 100644
index 0000000..c5b77a3
--- /dev/null
+++ b/polymatrix/expression/init/initparametrizeexpr.py
@@ -0,0 +1,15 @@
+from polymatrix.expression.mixins.expressionbasemixin import ExpressionBaseMixin
+from polymatrix.expression.impl.parametrizeexprimpl import ParametrizeExprImpl
+
+
+def init_parametrize_expr(
+ underlying: ExpressionBaseMixin,
+ name: str = None,
+):
+ if name is None:
+ name = 'undefined'
+
+ return ParametrizeExprImpl(
+ underlying=underlying,
+ name=name,
+)
diff --git a/polymatrix/expression/init/initparametrizetermsexpr.py b/polymatrix/expression/init/initparametrizetermsexpr.py
index b932825..6e2a446 100644
--- a/polymatrix/expression/init/initparametrizetermsexpr.py
+++ b/polymatrix/expression/init/initparametrizetermsexpr.py
@@ -3,12 +3,12 @@ from polymatrix.expression.impl.parametrizetermsexprimpl import ParametrizeTerms
def init_parametrize_terms_expr(
- name: str,
+ # name: str,
underlying: ExpressionBaseMixin,
- variables: tuple,
+ # variables: tuple,
):
return ParametrizeTermsExprImpl(
- name=name,
+ # name=name,
underlying=underlying,
- variables=variables,
+ # variables=variables,
)
diff --git a/polymatrix/expression/init/initquadraticinexpr.py b/polymatrix/expression/init/initquadraticinexpr.py
index 1e73745..5aa40a5 100644
--- a/polymatrix/expression/init/initquadraticinexpr.py
+++ b/polymatrix/expression/init/initquadraticinexpr.py
@@ -4,9 +4,11 @@ from polymatrix.expression.impl.quadraticinexprimpl import QuadraticInExprImpl
def init_quadratic_in_expr(
underlying: ExpressionBaseMixin,
+ monomials: ExpressionBaseMixin,
variables: tuple,
):
return QuadraticInExprImpl(
underlying=underlying,
+ monomials=monomials,
variables=variables,
)
diff --git a/polymatrix/expression/init/initsetelementatexpr.py b/polymatrix/expression/init/initsetelementatexpr.py
new file mode 100644
index 0000000..86df5aa
--- /dev/null
+++ b/polymatrix/expression/init/initsetelementatexpr.py
@@ -0,0 +1,14 @@
+from polymatrix.expression.mixins.expressionbasemixin import ExpressionBaseMixin
+from polymatrix.expression.impl.setelementatexprimpl import SetElementAtExprImpl
+
+
+def init_set_element_at_expr(
+ underlying: ExpressionBaseMixin,
+ index: tuple,
+ value: ExpressionBaseMixin,
+):
+ return SetElementAtExprImpl(
+ underlying=underlying,
+ index=index,
+ value=value,
+)
diff --git a/polymatrix/expression/init/initsosmonomialsexpr.py b/polymatrix/expression/init/initsosmonomialsexpr.py
new file mode 100644
index 0000000..0f5d034
--- /dev/null
+++ b/polymatrix/expression/init/initsosmonomialsexpr.py
@@ -0,0 +1,12 @@
+from polymatrix.expression.mixins.expressionbasemixin import ExpressionBaseMixin
+from polymatrix.expression.impl.sosmonomialsexprimpl import SOSMonomialsExprImpl
+
+
+def init_sos_monomials_expr(
+ underlying: ExpressionBaseMixin,
+ variables: tuple,
+):
+ return SOSMonomialsExprImpl(
+ underlying=underlying,
+ variables=variables,
+)
diff --git a/polymatrix/expression/init/initsubstituteexpr.py b/polymatrix/expression/init/initsubstituteexpr.py
new file mode 100644
index 0000000..63e1123
--- /dev/null
+++ b/polymatrix/expression/init/initsubstituteexpr.py
@@ -0,0 +1,27 @@
+import numpy as np
+
+from polymatrix.expression.mixins.expressionbasemixin import ExpressionBaseMixin
+from polymatrix.expression.impl.substituteexprimpl import SubstituteExprImpl
+
+
+def init_substitute_expr(
+ underlying: ExpressionBaseMixin,
+ variables: tuple,
+ substitutions: tuple = None,
+):
+ if substitutions is None:
+ assert isinstance(variables, tuple)
+
+ variables, substitutions = tuple(zip(*variables))
+
+ elif isinstance(substitutions, np.ndarray):
+ substitutions = tuple(substitutions.reshape(-1))
+
+ elif not isinstance(substitutions, tuple):
+ substitutions = (substitutions,)
+
+ return SubstituteExprImpl(
+ underlying=underlying,
+ variables=variables,
+ substitutions=substitutions,
+)
diff --git a/polymatrix/expression/init/initsubtractmonomialsexpr.py b/polymatrix/expression/init/initsubtractmonomialsexpr.py
new file mode 100644
index 0000000..131401b
--- /dev/null
+++ b/polymatrix/expression/init/initsubtractmonomialsexpr.py
@@ -0,0 +1,12 @@
+from polymatrix.expression.mixins.expressionbasemixin import ExpressionBaseMixin
+from polymatrix.expression.impl.subtractmonomialsexprimpl import SubtractMonomialsExprImpl
+
+
+def init_subtract_monomials_expr(
+ underlying: ExpressionBaseMixin,
+ monomials: ExpressionBaseMixin,
+):
+ return SubtractMonomialsExprImpl(
+ underlying=underlying,
+ monomials=monomials,
+)
diff --git a/polymatrix/expression/init/inittruncateexpr.py b/polymatrix/expression/init/inittruncateexpr.py
new file mode 100644
index 0000000..818981e
--- /dev/null
+++ b/polymatrix/expression/init/inittruncateexpr.py
@@ -0,0 +1,14 @@
+from polymatrix.expression.mixins.expressionbasemixin import ExpressionBaseMixin
+from polymatrix.expression.impl.truncateexprimpl import TruncateExprImpl
+
+
+def init_truncate_expr(
+ underlying: ExpressionBaseMixin,
+ variables: ExpressionBaseMixin,
+ degree: int,
+):
+ return TruncateExprImpl(
+ underlying=underlying,
+ variables=variables,
+ degree=degree,
+)
diff --git a/polymatrix/expression/init/initvstackexpr.py b/polymatrix/expression/init/initvstackexpr.py
index 1d7834e..70764ef 100644
--- a/polymatrix/expression/init/initvstackexpr.py
+++ b/polymatrix/expression/init/initvstackexpr.py
@@ -1,9 +1,20 @@
from polymatrix.expression.impl.vstackexprimpl import VStackExprImpl
+from polymatrix.expression.init.initfromarrayexpr import init_from_array_expr
+from polymatrix.expression.mixins.expressionbasemixin import ExpressionBaseMixin
def init_v_stack_expr(
underlying: tuple,
):
+
+ def gen_underlying():
+
+ for e in underlying:
+ if isinstance(e, ExpressionBaseMixin):
+ yield e
+ else:
+ yield init_from_array_expr(e)
+
return VStackExprImpl(
- underlying=underlying,
+ underlying=tuple(gen_underlying()),
)
diff --git a/polymatrix/expression/kktexpr.py b/polymatrix/expression/kktexpr.py
deleted file mode 100644
index aa5be52..0000000
--- a/polymatrix/expression/kktexpr.py
+++ /dev/null
@@ -1,4 +0,0 @@
-from polymatrix.expression.mixins.kktexprmixin import KKTExprMixin
-
-class KKTExpr(KKTExprMixin):
- pass
diff --git a/polymatrix/expression/linearin2expr.py b/polymatrix/expression/linearin2expr.py
deleted file mode 100644
index 2c40dc8..0000000
--- a/polymatrix/expression/linearin2expr.py
+++ /dev/null
@@ -1,4 +0,0 @@
-from polymatrix.expression.mixins.linearinexprmixin import LinearInExprMixin
-
-class LinearIn2Expr(LinearInExprMixin):
- pass
diff --git a/polymatrix/expression/linearinexpr.py b/polymatrix/expression/linearinexpr.py
index 9054937..4edf8b3 100644
--- a/polymatrix/expression/linearinexpr.py
+++ b/polymatrix/expression/linearinexpr.py
@@ -1,4 +1,4 @@
-from polymatrix.expression.mixins.oldlinearexprmixin import OldLinearExprMixin
+from polymatrix.expression.mixins.linearinexprmixin import LinearInExprMixin
-class LinearInExpr(OldLinearExprMixin):
+class LinearInExpr(LinearInExprMixin):
pass
diff --git a/polymatrix/expression/linearinmonomialsinexpr.py b/polymatrix/expression/linearinmonomialsinexpr.py
new file mode 100644
index 0000000..a78fb40
--- /dev/null
+++ b/polymatrix/expression/linearinmonomialsinexpr.py
@@ -0,0 +1,4 @@
+from polymatrix.expression.mixins.linearinmutipleexprmixin import LinearInMultipleExprMixin
+
+class LinearInMonomialsInExpr(LinearInMultipleExprMixin):
+ pass
diff --git a/polymatrix/expression/linearmatrixinexpr.py b/polymatrix/expression/linearmatrixinexpr.py
index 296e902..2bce2e7 100644
--- a/polymatrix/expression/linearmatrixinexpr.py
+++ b/polymatrix/expression/linearmatrixinexpr.py
@@ -1,4 +1,4 @@
-from polymatrix.expression.mixins.filterlinearpartexprmixin import FilterLinearPartExprMixin
+from polymatrix.expression.mixins.linearmatrixinexprmixin import LinearMatrixInExprMixin
-class LinearMatrixInExpr(FilterLinearPartExprMixin):
+class LinearMatrixInExpr(LinearMatrixInExprMixin):
pass
diff --git a/polymatrix/expression/linearmonomialsexpr.py b/polymatrix/expression/linearmonomialsexpr.py
new file mode 100644
index 0000000..9ff6337
--- /dev/null
+++ b/polymatrix/expression/linearmonomialsexpr.py
@@ -0,0 +1,4 @@
+from polymatrix.expression.mixins.linearmonomialsexprmixin import LinearMonomialsExprMixin
+
+class LinearMonomialsExpr(LinearMonomialsExprMixin):
+ pass
diff --git a/polymatrix/expression/mixins/addauxequationsexprmixin.py b/polymatrix/expression/mixins/addauxequationsexprmixin.py
deleted file mode 100644
index e5353fe..0000000
--- a/polymatrix/expression/mixins/addauxequationsexprmixin.py
+++ /dev/null
@@ -1,58 +0,0 @@
-
-import abc
-import dataclasses
-import itertools
-import dataclass_abc
-from polymatrix.expression.mixins.polymatrixmixin import PolyMatrixMixin
-
-from polymatrix.expression.mixins.expressionbasemixin import ExpressionBaseMixin
-from polymatrix.expression.polymatrix import PolyMatrix
-from polymatrix.expression.expressionstate import ExpressionState
-
-
-# todo: is this really needed?
-class AddAuxEquationsExprMixin(ExpressionBaseMixin):
- @property
- @abc.abstractmethod
- def underlying(self) -> ExpressionBaseMixin:
- ...
-
- # overwrites abstract method of `ExpressionBaseMixin`
- def apply(
- self,
- state: ExpressionState,
- ) -> tuple[ExpressionState, PolyMatrix]:
-
- state, underlying = self.underlying.apply(state=state)
-
- assert underlying.shape[1] == 1
-
- @dataclass_abc.dataclass_abc(frozen=True)
- class AddAuxEquationsPolyMatrix(PolyMatrixMixin):
- underlying: tuple[PolyMatrixMixin]
- shape: tuple[int, int]
- n_row: int
- auxillary_equations: tuple[dict[tuple[int], float]]
-
- def get_poly(self, row: int, col: int) -> dict[tuple[int, ...], float]:
- if row < self.n_row:
- return self.underlying.get_poly(row, col)
-
- elif row < self.shape[0]:
- return self.auxillary_equations[row - self.n_row]
-
- else:
- raise Exception(f'row {row} is out of bounds')
-
- auxillary_equations = tuple(state.auxillary_equations.values())
-
- polymat = AddAuxEquationsPolyMatrix(
- underlying=underlying,
- shape=(underlying.shape[0] + len(auxillary_equations), 1),
- n_row=underlying.shape[0],
- auxillary_equations=auxillary_equations,
- )
-
- state = dataclasses.replace(state, auxillary_equations={})
-
- return state, polymat
diff --git a/polymatrix/expression/mixins/assertdegreeexprmixin.py b/polymatrix/expression/mixins/assertdegreeexprmixin.py
new file mode 100644
index 0000000..af0a3e6
--- /dev/null
+++ b/polymatrix/expression/mixins/assertdegreeexprmixin.py
@@ -0,0 +1,49 @@
+
+import abc
+import itertools
+import dataclass_abc
+from polymatrix.expression.mixins.polymatrixmixin import PolyMatrixMixin
+
+from polymatrix.expression.mixins.expressionbasemixin import ExpressionBaseMixin
+from polymatrix.expression.polymatrix import PolyMatrix
+from polymatrix.expression.expressionstate import ExpressionState
+
+
+class AssertDegreeExprMixin(ExpressionBaseMixin):
+ @property
+ @abc.abstractmethod
+ def underlying(self) -> tuple[ExpressionBaseMixin, ...]:
+ ...
+
+ @property
+ @abc.abstractmethod
+ def degrees(self) -> tuple[int, ...]:
+ ...
+
+ # overwrites abstract method of `ExpressionBaseMixin`
+ def apply(
+ self,
+ state: ExpressionState,
+ ) -> tuple[ExpressionState, PolyMatrix]:
+
+ state, underlying = self.underlying.apply(state)
+
+ @dataclass_abc.dataclass_abc(frozen=True)
+ class AssertDegreePolyMatrix(PolyMatrixMixin):
+ underlying: PolyMatrixMixin
+ degrees: tuple[int, ...]
+
+ def get_poly(self, row: int, col: int) -> dict[tuple[int, ...], float]:
+ terms = self.underlying.get_poly(row, col)
+
+ for monomial in terms.keys():
+ pass
+
+ return terms
+
+ polymatrix = AssertDegreePolyMatrix(
+ underlying=underlying,
+ degrees=self.degrees,
+ )
+
+ return state, polymatrix
diff --git a/polymatrix/expression/mixins/blockdiagexprmixin.py b/polymatrix/expression/mixins/blockdiagexprmixin.py
index df4bd80..b0006e3 100644
--- a/polymatrix/expression/mixins/blockdiagexprmixin.py
+++ b/polymatrix/expression/mixins/blockdiagexprmixin.py
@@ -35,11 +35,11 @@ class BlockDiagExprMixin(ExpressionBaseMixin):
shape: tuple[int, int]
def get_poly(self, row: int, col: int) -> dict[tuple[int, ...], float]:
- for polymat, ((row_start, col_start), (row_end, col_end)) in zip(self.all_underlying, self.underlying_row_col_range):
+ for polymatrix, ((row_start, col_start), (row_end, col_end)) in zip(self.all_underlying, self.underlying_row_col_range):
if row_start <= row < row_end:
if col_start <= col < col_end:
- return polymat.get_poly(
+ return polymatrix.get_poly(
row=row-row_start,
col=col-col_start,
)
@@ -58,10 +58,10 @@ class BlockDiagExprMixin(ExpressionBaseMixin):
shape = underlying_row_col_range[-1][1]
- polymat = BlockDiagPolyMatrix(
+ polymatrix = BlockDiagPolyMatrix(
all_underlying=all_underlying,
shape=shape,
underlying_row_col_range=underlying_row_col_range,
)
- return state, polymat
+ return state, polymatrix
diff --git a/polymatrix/expression/mixins/combinationsexprmixin.py b/polymatrix/expression/mixins/combinationsexprmixin.py
index 6d0f6eb..824ebf1 100644
--- a/polymatrix/expression/mixins/combinationsexprmixin.py
+++ b/polymatrix/expression/mixins/combinationsexprmixin.py
@@ -7,12 +7,15 @@ from polymatrix.expression.init.initpolymatrix import init_poly_matrix
from polymatrix.expression.mixins.expressionbasemixin import ExpressionBaseMixin
from polymatrix.expression.polymatrix import PolyMatrix
from polymatrix.expression.expressionstate import ExpressionState
+from polymatrix.expression.utils.getmonomialindices import get_monomial_indices
+from polymatrix.expression.utils.mergemonomialindices import merge_monomial_indices
+from polymatrix.expression.utils.sortmonomials import sort_monomials
class CombinationsExprMixin(ExpressionBaseMixin):
@property
@abc.abstractmethod
- def underlying(self) -> ExpressionBaseMixin:
+ def monomials(self) -> ExpressionBaseMixin:
...
@property
@@ -25,51 +28,34 @@ class CombinationsExprMixin(ExpressionBaseMixin):
self,
state: ExpressionState,
) -> tuple[ExpressionState, PolyMatrix]:
- state, underlying = self.underlying.apply(state=state)
+ if self.number == 0:
+ terms = {(0, 0): {tuple(): 1.0}}
- assert underlying.shape[1] == 1
-
- def gen_monomials():
- for _, monomial_terms in underlying.get_terms():
- assert len(monomial_terms) == 1, f'{monomial_terms} has more than 1 element'
-
- for monomial, values in monomial_terms.items():
- yield monomial, values
-
- monomial_terms = tuple(gen_monomials())
+ poly_matrix = init_poly_matrix(
+ terms=terms,
+ shape=(1, 1),
+ )
- assert underlying.shape[0] == len(monomial_terms)
+ elif self.number == 1:
+ state, monomials = self.monomials.apply(state=state)
+ poly_matrix = monomials
- combinations = tuple(itertools.combinations_with_replacement(range(underlying.shape[0]), self.number))
+ else:
- # print(combinations)
- # print(math.comb(underlying.shape[0]+self.number-1, self.number))
+ state, monomials = get_monomial_indices(state, self.monomials)
- terms = {}
+ combinations = tuple(itertools.combinations_with_replacement(monomials, self.number))
- for row, combination in enumerate(combinations):
- def gen_combination_monomials():
- for index in combination:
- monomial, _ = monomial_terms[index]
- yield from monomial
+ terms = {}
- combination_monomial = tuple(gen_combination_monomials())
+ for row, combination in enumerate(combinations):
+ combination_monomial = merge_monomial_indices(combination)
- def acc_combination_value(acc, index):
- _, value = monomial_terms[index]
- return acc * value
+ terms[row, 0] = {combination_monomial: 1.0}
- *_, combination_value = itertools.accumulate(
- combination,
- acc_combination_value,
- initial=1.0,
+ poly_matrix = init_poly_matrix(
+ terms=terms,
+ shape=(math.comb(len(monomials) + self.number - 1, self.number), 1),
)
- terms[row, 0] = {combination_monomial: combination_value}
-
- poly_matrix = init_poly_matrix(
- terms=terms,
- shape=(math.comb(underlying.shape[0]+self.number-1, self.number), 1),
- )
-
return state, poly_matrix
diff --git a/polymatrix/expression/mixins/derivativeexprmixin.py b/polymatrix/expression/mixins/derivativeexprmixin.py
index b403873..4fc48a5 100644
--- a/polymatrix/expression/mixins/derivativeexprmixin.py
+++ b/polymatrix/expression/mixins/derivativeexprmixin.py
@@ -10,6 +10,7 @@ from polymatrix.expression.init.initpolymatrix import init_poly_matrix
from polymatrix.expression.mixins.expressionbasemixin import ExpressionBaseMixin
from polymatrix.expression.polymatrix import PolyMatrix
from polymatrix.expression.expressionstate import ExpressionState
+from polymatrix.expression.utils.getderivativemonomials import get_derivative_monomials
from polymatrix.expression.utils.getvariableindices import get_variable_indices
@@ -36,112 +37,119 @@ class DerivativeExprMixin(ExpressionBaseMixin):
) -> tuple[ExpressionState, PolyMatrix]:
state, underlying = self.underlying.apply(state=state)
+
+ assert underlying.shape[1] == 1, f'{underlying.shape=}'
state, diff_wrt_variables = get_variable_indices(state, self.variables)
- def get_derivative_terms(
- monomial_terms,
- diff_wrt_variable: int,
- state: ExpressionState,
- considered_variables: set,
- ):
+ # def get_derivative_terms(
+ # monomial_terms,
+ # diff_wrt_variable: int,
+ # state: ExpressionState,
+ # considered_variables: set,
+ # ):
+
+ # if self.introduce_derivatives:
- if self.introduce_derivatives:
+ # raise Exception('not implemented')
- def gen_new_variables():
- for monomial in monomial_terms.keys():
- for var in monomial:
- if var not in diff_wrt_variables and var not in considered_variables:
- yield var
+ # def gen_new_variables():
+ # for monomial in monomial_terms.keys():
+ # for var in monomial:
+ # if var not in diff_wrt_variables and var not in considered_variables:
+ # yield var
- new_variables = set(gen_new_variables())
+ # new_variables = set(gen_new_variables())
- new_considered_variables = considered_variables | new_variables
+ # new_considered_variables = considered_variables | new_variables
- def acc_state_candidates(acc, new_variable):
- state, candidates = acc
+ # def acc_state_candidates(acc, new_variable):
+ # state, candidates = acc
- key = init_derivative_key(
- variable=new_variable,
- with_respect_to=diff_wrt_variable,
- )
- state = state.register(key=key, n_param=1)
+ # key = init_derivative_key(
+ # variable=new_variable,
+ # with_respect_to=diff_wrt_variable,
+ # )
+ # state = state.register(key=key, n_param=1)
- state, auxillary_derivation_terms = get_derivative_terms(
- monomial_terms=state.auxillary_equations[new_variable],
- diff_wrt_variable=diff_wrt_variable,
- state=state,
- considered_variables=new_considered_variables,
- )
+ # state, auxillary_derivation_terms = get_derivative_terms(
+ # monomial_terms=state.auxillary_equations[new_variable],
+ # diff_wrt_variable=diff_wrt_variable,
+ # state=state,
+ # considered_variables=new_considered_variables,
+ # )
- if 1 < len(auxillary_derivation_terms):
- derivation_variable = state.offset_dict[key][0]
+ # if 1 < len(auxillary_derivation_terms):
+ # derivation_variable = state.offset_dict[key][0]
- state = dataclasses.replace(
- state,
- auxillary_equations=state.auxillary_equations | {derivation_variable: auxillary_derivation_terms},
- )
+ # state = dataclasses.replace(
+ # state,
+ # auxillary_equations=state.auxillary_equations | {derivation_variable: auxillary_derivation_terms},
+ # )
- return state, candidates + (new_variable,)
+ # return state, candidates + (new_variable,)
- else:
- return state, candidates
+ # else:
+ # return state, candidates
- *_, (state, confirmed_variables) = itertools.accumulate(
- new_variables,
- acc_state_candidates,
- initial=(state, tuple()),
- )
+ # *_, (state, confirmed_variables) = itertools.accumulate(
+ # new_variables,
+ # acc_state_candidates,
+ # initial=(state, tuple()),
+ # )
- else:
- confirmed_variables = tuple()
+ # else:
+ # confirmed_variables = tuple()
- derivation_terms = collections.defaultdict(float)
+ # derivation_terms = collections.defaultdict(float)
- for monomial, value in monomial_terms.items():
+ # for monomial, value in monomial_terms.items():
- # count powers for each variable
- monomial_cnt = dict(collections.Counter(monomial))
+ # # # count powers for each variable
+ # # monomial_cnt = dict(collections.Counter(monomial))
+ # monomial_cnt = dict(monomial)
- def differentiate_monomial(dependent_variable, derivation_variable=None):
- def gen_diff_monomial():
- for current_variable, current_count in monomial_cnt.items():
+ # def differentiate_monomial(dependent_variable, derivation_variable=None):
+ # def gen_diff_monomial():
+ # for current_variable, current_count in monomial:
- if current_variable is dependent_variable:
- sel_counter = current_count - 1
+ # if current_variable is dependent_variable:
+ # sel_counter = current_count - 1
- else:
- sel_counter = current_count
+ # else:
+ # sel_counter = current_count
- for _ in range(sel_counter):
- yield current_variable
+ # # for _ in range(sel_counter):
+ # # yield current_variable
+ # yield current_variable, sel_counter
- if derivation_variable is not None:
- yield derivation_variable
+ # if derivation_variable is not None:
+ # yield derivation_variable
- diff_monomial = tuple(sorted(gen_diff_monomial()))
+ # diff_monomial = tuple(sorted(gen_diff_monomial()))
- return diff_monomial, value * monomial_cnt[dependent_variable]
+ # return diff_monomial, value * monomial_cnt[dependent_variable]
- if diff_wrt_variable in monomial_cnt:
- diff_monomial, value = differentiate_monomial(diff_wrt_variable)
- derivation_terms[diff_monomial] += value
+ # if diff_wrt_variable in monomial_cnt:
+ # diff_monomial, value = differentiate_monomial(diff_wrt_variable)
+ # derivation_terms[diff_monomial] += value
- for candidate_variable in monomial_cnt.keys():
- if candidate_variable in considered_variables or candidate_variable in confirmed_variables:
- key = init_derivative_key(
- variable=candidate_variable,
- with_respect_to=diff_wrt_variable,
- )
- derivation_variable = state.offset_dict[key][0]
+ # # only used if introduce_derivatives == True
+ # for candidate_variable in monomial_cnt.keys():
+ # if candidate_variable in considered_variables or candidate_variable in confirmed_variables:
+ # key = init_derivative_key(
+ # variable=candidate_variable,
+ # with_respect_to=diff_wrt_variable,
+ # )
+ # derivation_variable = state.offset_dict[key][0]
- diff_monomial, value = differentiate_monomial(
- dependent_variable=candidate_variable,
- derivation_variable=derivation_variable,
- )
- derivation_terms[diff_monomial] += value
+ # diff_monomial, value = differentiate_monomial(
+ # dependent_variable=candidate_variable,
+ # derivation_variable=derivation_variable,
+ # )
+ # derivation_terms[diff_monomial] += value
- return state, dict(derivation_terms)
+ # return state, dict(derivation_terms)
terms = {}
@@ -155,11 +163,12 @@ class DerivativeExprMixin(ExpressionBaseMixin):
# derivate each variable and map result to the corresponding column
for col, diff_wrt_variable in enumerate(diff_wrt_variables):
- state, derivation_terms = get_derivative_terms(
+ state, derivation_terms = get_derivative_monomials(
monomial_terms=underlying_terms,
diff_wrt_variable=diff_wrt_variable,
state=state,
considered_variables=set(),
+ introduce_derivatives=self.introduce_derivatives,
)
if 0 < len(derivation_terms):
@@ -171,299 +180,3 @@ class DerivativeExprMixin(ExpressionBaseMixin):
)
return state, poly_matrix
-
- # def get_derivative_terms(
- # monomial_terms,
- # diff_wrt_variable: int,
- # state: PolyMatrixExprState,
- # considered_variables: set,
- # # implement_derivation: bool,
- # ):
- # derivation_terms = collections.defaultdict(float)
-
- # other_independent_variables = tuple(var for var in diff_wrt_variables if var is not diff_wrt_variable)
-
- # # print(other_independent_variables)
- # # print(tuple(variable for monomial in monomial_terms.keys() for variable in monomial))
-
- # if sum(variable not in other_independent_variables for monomial in monomial_terms.keys() for variable in monomial) < 2:
- # return {}, state
-
- # # if not implement_derivation:
- # # implement_derivation = any(diff_wrt_variable in monomial for monomial in monomial_terms.keys())
-
- # for monomial, value in monomial_terms.items():
-
- # # count powers for each variable
- # monomial_cnt = dict(collections.Counter(monomial))
-
- # def differentiate_monomial(dependent_variable, derivation_variable=None):
- # def gen_diff_monomial():
- # for current_variable, current_count in monomial_cnt.items():
-
- # if current_variable is dependent_variable:
- # sel_counter = current_count - 1
-
- # else:
- # sel_counter = current_count
-
- # for _ in range(sel_counter):
- # yield current_variable
-
- # if derivation_variable is not None:
- # yield derivation_variable
-
- # diff_monomial = tuple(gen_diff_monomial())
-
- # return diff_monomial, value * monomial_cnt[dependent_variable]
-
- # if diff_wrt_variable in monomial_cnt:
- # diff_monomial, value = differentiate_monomial(diff_wrt_variable)
- # derivation_terms[diff_monomial] += value
-
- # if self.introduce_derivatives:
-
- # def gen_derivation_keys():
- # for variable in monomial_cnt.keys():
- # if variable not in diff_wrt_variables:
- # yield variable
-
- # candidate_variables = tuple(gen_derivation_keys())
-
- # new_considered_derivations = considered_variables | set(candidate_variables)
-
- # for candidate_variable in candidate_variables:
-
- # # introduce new auxillary equation
- # if candidate_variable not in considered_variables:
- # auxillary_derivation_terms, state = get_derivative_terms(
- # monomial_terms=state.auxillary_equations[candidate_variable],
- # diff_wrt_variable=diff_wrt_variable,
- # state=state,
- # considered_variables=new_considered_derivations,
- # # implement_derivation=implement_derivation,
- # )
-
- # if 0 < len(auxillary_derivation_terms):
- # key = init_derivative_key(
- # variable=candidate_variable,
- # with_respect_to=diff_wrt_variable,
- # )
- # state = state.register(key=key, n_param=1)
- # derivation_variable = state.offset_dict[key][0]
-
- # state = dataclasses.replace(
- # state,
- # auxillary_equations=state.auxillary_equations | {derivation_variable: auxillary_derivation_terms},
- # )
-
- # else:
-
- # key = init_derivative_key(
- # variable=candidate_variable,
- # with_respect_to=diff_wrt_variable,
- # )
- # state = state.register(key=key, n_param=1)
- # derivation_variable = state.offset_dict[key][0]
-
- # diff_monomial, value = differentiate_monomial(
- # dependent_variable=candidate_variable,
- # derivation_variable=derivation_variable,
- # )
- # derivation_terms[diff_monomial] += value
-
- # return dict(derivation_terms), state
-
- # terms = {}
-
- # for row in range(self.shape[0]):
-
- # try:
- # underlying_terms = underlying.get_poly(row, 0)
- # except KeyError:
- # continue
-
- # # derivate each variable and map result to the corresponding column
- # for col, diff_wrt_variable in enumerate(diff_wrt_variables):
-
- # derivation_terms, state = get_derivative_terms(
- # monomial_terms=underlying_terms,
- # diff_wrt_variable=diff_wrt_variable,
- # state=state,
- # considered_variables=set(),
- # # implement_derivation=False,
- # )
-
- # if 0 < len(derivation_terms):
- # terms[row, col] = derivation_terms
-
- # poly_matrix = init_poly_matrix(
- # terms=terms,
- # shape=self.shape,
- # )
-
- # return state, poly_matrix
-
-
-
- # state = [state]
-
- # state, underlying = self.underlying.apply(state=state)
-
- # match self.variables:
- # case ExpressionBaseMixin():
- # assert self.variables.shape[1] == 1
-
- # state, dependent_variables = self.variables.apply(state)
-
- # def gen_indices():
- # for row in range(dependent_variables.shape[0]):
- # for monomial in dependent_variables.get_poly(row, 0).keys():
- # yield monomial[0]
-
- # variable_indices = tuple(sorted(gen_indices()))
-
- # case _:
- # def gen_indices():
- # for variable in self.variables:
- # if variable in state.offset_dict:
- # yield state.offset_dict[variable][0]
-
- # variable_indices = tuple(sorted(gen_indices()))
-
- # terms = {}
- # derivations_keys = set()
-
- # # derivate each variable and map result to the corresponding column
- # for col, derivation_variable in enumerate(variable_indices):
-
- # def get_derivative_terms(monomial_terms):
-
- # terms_row_col = collections.defaultdict(float)
-
- # for monomial, value in monomial_terms.items():
-
- # # count powers for each variable
- # monomial_cnt = dict(collections.Counter(monomial))
-
- # variable_candidates = tuple()
-
- # if derivation_variable in monomial_cnt:
- # variable_candidates += ((derivation_variable, None),)
-
- # if self.introduce_derivatives:
- # def gen_dependent_variables():
- # for dependent_variable in monomial_cnt.keys():
- # if dependent_variable not in variable_indices:
- # derivation_key = init_derivative_key(
- # variable=dependent_variable,
- # with_respect_to=derivation_variable,
- # )
- # derivations_keys.add(derivation_key)
- # state = state.register(key=derivation_key, n_param=1)
- # yield dependent_variable, derivation_key
-
- # variable_candidates += tuple(gen_dependent_variables())
-
- # for variable_candidate, derivation_key in variable_candidates:
-
- # def generate_monomial():
- # for current_variable, current_count in monomial_cnt.items():
-
- # if current_variable is variable_candidate:
- # sel_counter = current_count - 1
-
- # else:
- # sel_counter = current_count
-
- # for _ in range(sel_counter):
- # yield current_variable
-
- # if derivation_key is not None:
- # yield state.offset_dict[derivation_key][0]
-
- # col_monomial = tuple(generate_monomial())
-
- # terms_row_col[col_monomial] += value * monomial_cnt[variable_candidate]
-
- # return dict(terms_row_col)
-
- # for row in range(self.shape[0]):
-
- # try:
- # underlying_terms = underlying.get_poly(row, 0)
- # except KeyError:
- # continue
-
- # derivative_terms = get_derivative_terms(underlying_terms)
-
- # if 0 < len(derivative_terms):
- # terms[row, col] = derivative_terms
-
- # derivation_variables = collections.defaultdict(list)
- # for derivation_key in derivations_keys:
- # derivation_variables[derivation_key.with_respect_to].append(derivation_key)
-
- # aux_der_terms = []
-
- # for derivation_variable, derivation_keys in derivation_variables.items():
-
- # dependent_variables = tuple(derivation_key.variable for derivation_key in derivation_keys)
-
-
- # for aux_terms in state.auxillary_equations:
-
- # # only intoduce a new auxillary equation if there is a monomial containing at least one dependent variable
- # if any(variable in dependent_variables for monomial in aux_terms.keys() for variable in monomial):
-
- # terms_row_col = collections.defaultdict(float)
-
- # # for each monomial
- # for aux_monomial, value in aux_terms.items():
-
- # # count powers for each variable
- # monomial_cnt = dict(collections.Counter(aux_monomial))
-
- # variable_candidates = tuple()
-
- # if derivation_variable in monomial_cnt:
- # variable_candidates += ((derivation_variable, None),)
-
- # # add dependent variables
- # variable_candidates += tuple((derivation_key.variable, derivation_key) for derivation_key in derivation_keys if derivation_key.variable in monomial_cnt)
-
- # for variable_candidate, derivative_key in variable_candidates:
-
- # def generate_monomial():
- # for current_variable, current_count in monomial_cnt.items():
-
- # if current_variable is variable_candidate:
- # sel_counter = current_count - 1
-
- # else:
- # sel_counter = current_count
-
- # for _ in range(sel_counter):
- # yield current_variable
-
- # if derivative_key is not None:
- # yield state.offset_dict[derivative_key][0]
-
- # col_monomial = tuple(generate_monomial())
-
- # terms_row_col[col_monomial] += value * monomial_cnt[variable_candidate]
-
- # if 0 < len(terms_row_col):
- # aux_der_terms.append(dict(terms_row_col))
-
- # state = dataclasses.replace(
- # state,
- # auxillary_equations=state.auxillary_equations + tuple(aux_der_terms),
- # )
-
- # poly_matrix = init_poly_matrix(
- # terms=terms,
- # shape=self.shape,
- # )
-
- # return state, poly_matrix
diff --git a/polymatrix/expression/mixins/derivativekeymixin.py b/polymatrix/expression/mixins/derivativekeymixin.py
index e0adddc..db4951a 100644
--- a/polymatrix/expression/mixins/derivativekeymixin.py
+++ b/polymatrix/expression/mixins/derivativekeymixin.py
@@ -1,5 +1,6 @@
import abc
+# move this class
class DerivativeKeyMixin(abc.ABC):
@property
@abc.abstractmethod
diff --git a/polymatrix/expression/mixins/determinantexprmixin.py b/polymatrix/expression/mixins/determinantexprmixin.py
index f002f0b..8d41e3d 100644
--- a/polymatrix/expression/mixins/determinantexprmixin.py
+++ b/polymatrix/expression/mixins/determinantexprmixin.py
@@ -26,6 +26,8 @@ class DeterminantExprMixin(ExpressionBaseMixin):
self,
state: ExpressionState,
) -> tuple[ExpressionState, PolyMatrix]:
+ raise Exception('not implemented')
+
if self in state.cache:
return state, state.cache[self]
diff --git a/polymatrix/expression/mixins/divergenceexprmixin.py b/polymatrix/expression/mixins/divergenceexprmixin.py
new file mode 100644
index 0000000..b523b2d
--- /dev/null
+++ b/polymatrix/expression/mixins/divergenceexprmixin.py
@@ -0,0 +1,65 @@
+
+import abc
+import collections
+import dataclasses
+import itertools
+import typing
+from polymatrix.expression.init.initderivativekey import init_derivative_key
+
+from polymatrix.expression.init.initpolymatrix import init_poly_matrix
+from polymatrix.expression.mixins.expressionbasemixin import ExpressionBaseMixin
+from polymatrix.expression.polymatrix import PolyMatrix
+from polymatrix.expression.expressionstate import ExpressionState
+from polymatrix.expression.utils.getderivativemonomials import get_derivative_monomials
+from polymatrix.expression.utils.getvariableindices import get_variable_indices
+
+
+class DivergenceExprMixin(ExpressionBaseMixin):
+ @property
+ @abc.abstractmethod
+ def underlying(self) -> ExpressionBaseMixin:
+ ...
+
+ @property
+ @abc.abstractmethod
+ def variables(self) -> typing.Union[tuple, ExpressionBaseMixin]:
+ ...
+
+ # overwrites abstract method of `ExpressionBaseMixin`
+ def apply(
+ self,
+ state: ExpressionState,
+ ) -> tuple[ExpressionState, PolyMatrix]:
+
+ state, underlying = self.underlying.apply(state=state)
+ state, variables = get_variable_indices(state, self.variables)
+
+ assert underlying.shape[1] == 1, f'{underlying.shape=}'
+ assert len(variables) == underlying.shape[0], f'{variables=}, {underlying.shape=}'
+
+ monomial_terms = collections.defaultdict(float)
+
+ for row, variable in enumerate(variables):
+
+ try:
+ underlying_terms = underlying.get_poly(row, 0)
+ except KeyError:
+ continue
+
+ state, derivation_terms = get_derivative_monomials(
+ monomial_terms=underlying_terms,
+ diff_wrt_variable=variable,
+ state=state,
+ considered_variables=set(),
+ introduce_derivatives=False,
+ )
+
+ for monomial, value in derivation_terms.items():
+ monomial_terms[monomial] += value
+
+ poly_matrix = init_poly_matrix(
+ terms={(0, 0): dict(monomial_terms)},
+ shape=(1, 1),
+ )
+
+ return state, poly_matrix
diff --git a/polymatrix/expression/mixins/divisionexprmixin.py b/polymatrix/expression/mixins/divisionexprmixin.py
index dc5f616..7551cda 100644
--- a/polymatrix/expression/mixins/divisionexprmixin.py
+++ b/polymatrix/expression/mixins/divisionexprmixin.py
@@ -19,17 +19,13 @@ class DivisionExprMixin(ExpressionBaseMixin):
def right(self) -> ExpressionBaseMixin:
...
- # # overwrites abstract method of `ExpressionBaseMixin`
- # @property
- # def shape(self) -> tuple[int, int]:
- # return self.left.shape
-
# overwrites abstract method of `ExpressionBaseMixin`
def apply(
self,
state: ExpressionState,
) -> tuple[ExpressionState, PolyMatrix]:
+ # add an auxillary equation and, therefore, needs to be cached
if self in state.cache:
return state, state.cache[self]
diff --git a/polymatrix/expression/mixins/elemmultexprmixin.py b/polymatrix/expression/mixins/elemmultexprmixin.py
index 931e022..7e0bc8c 100644
--- a/polymatrix/expression/mixins/elemmultexprmixin.py
+++ b/polymatrix/expression/mixins/elemmultexprmixin.py
@@ -1,11 +1,15 @@
import abc
import itertools
+import typing
+import dataclass_abc
from polymatrix.expression.init.initpolymatrix import init_poly_matrix
from polymatrix.expression.mixins.expressionbasemixin import ExpressionBaseMixin
+from polymatrix.expression.mixins.polymatrixmixin import PolyMatrixMixin
from polymatrix.expression.polymatrix import PolyMatrix
from polymatrix.expression.expressionstate import ExpressionState
+from polymatrix.expression.utils.mergemonomialindices import merge_monomial_indices
class ElemMultExprMixin(ExpressionBaseMixin):
@@ -32,9 +36,59 @@ class ElemMultExprMixin(ExpressionBaseMixin):
state, left = self.left.apply(state=state)
state, right = self.right.apply(state=state)
- assert right.shape == (1, 1)
+ if right.shape == (1, 1):
+ right_poly = right.get_poly(0, 0)
- right_terms = right.get_poly(0, 0)
+ @dataclass_abc.dataclass_abc(frozen=True)
+ class BroadCastedPolyMatrix(PolyMatrixMixin):
+ underlying: tuple[tuple[int], float]
+ shape: tuple[int, int]
+
+ def get_poly(self, row: int, col: int) -> typing.Optional[dict[tuple[int, ...], float]]:
+ return self.underlying
+
+ right = BroadCastedPolyMatrix(
+ underlying=right_poly,
+ shape=left.shape,
+ )
+
+ # if right.shape == (1, 1):
+
+ # right_terms = right.get_poly(0, 0)
+
+ # terms = {}
+
+ # for poly_row in range(left.shape[0]):
+ # for poly_col in range(left.shape[1]):
+
+ # terms_row_col = {}
+
+ # try:
+ # left_terms = left.get_poly(poly_row, poly_col)
+ # except KeyError:
+ # continue
+
+ # for (left_monomial, left_value), (right_monomial, right_value) \
+ # in itertools.product(left_terms.items(), right_terms.items()):
+
+ # value = left_value * right_value
+
+ # # if value == 0:
+ # # continue
+
+ # # monomial = tuple(sorted(left_monomial + right_monomial))
+
+ # new_monomial = merge_monomial_indices((left_monomial, right_monomial))
+
+ # if new_monomial not in terms_row_col:
+ # terms_row_col[new_monomial] = 0
+
+ # terms_row_col[new_monomial] += value
+
+ # if 0 < len(terms_row_col):
+ # terms[poly_row, poly_col] = terms_row_col
+
+ # elif left.shape == right.shape:
terms = {}
@@ -48,20 +102,27 @@ class ElemMultExprMixin(ExpressionBaseMixin):
except KeyError:
continue
+ try:
+ right_terms = right.get_poly(poly_row, poly_col)
+ except KeyError:
+ continue
+
for (left_monomial, left_value), (right_monomial, right_value) \
in itertools.product(left_terms.items(), right_terms.items()):
value = left_value * right_value
- if value == 0:
- continue
+ # if value == 0:
+ # continue
+
+ # monomial = tuple(sorted(left_monomial + right_monomial))
- monomial = tuple(sorted(left_monomial + right_monomial))
+ new_monomial = merge_monomial_indices((left_monomial, right_monomial))
- if monomial not in terms_row_col:
- terms_row_col[monomial] = 0
+ if new_monomial not in terms_row_col:
+ terms_row_col[new_monomial] = 0
- terms_row_col[monomial] += value
+ terms_row_col[new_monomial] += value
if 0 < len(terms_row_col):
terms[poly_row, poly_col] = terms_row_col
diff --git a/polymatrix/expression/mixins/evalexprmixin.py b/polymatrix/expression/mixins/evalexprmixin.py
index 3089bbb..7fa149e 100644
--- a/polymatrix/expression/mixins/evalexprmixin.py
+++ b/polymatrix/expression/mixins/evalexprmixin.py
@@ -1,6 +1,7 @@
import abc
import itertools
+import math
from polymatrix.expression.init.initpolymatrix import init_poly_matrix
from polymatrix.expression.mixins.expressionbasemixin import ExpressionBaseMixin
@@ -56,16 +57,17 @@ class EvalExprMixin(ExpressionBaseMixin):
for monomial, value in underlying_terms.items():
- def acc_monomial(acc, variable):
- monomial, value = acc
+ def acc_monomial(acc, next):
+ new_monomial, value = acc
+ variable, count = next
if variable in variable_indices:
index = variable_indices.index(variable)
- new_value = value * values[index]
- return monomial, new_value
+ new_value = value * values[index] * count
+ return new_monomial, new_value
else:
- return monomial + (variable,), value
+ return new_monomial + (next,), value
*_, (new_monomial, new_value) = tuple(itertools.accumulate(
monomial,
@@ -79,10 +81,11 @@ class EvalExprMixin(ExpressionBaseMixin):
terms_row_col[new_monomial] += new_value
# delete zero entries
- if terms_row_col[new_monomial] == 0:
+ if math.isclose(terms_row_col[new_monomial], 0, abs_tol=1e-12):
del terms_row_col[new_monomial]
- terms[row, col] = terms_row_col
+ if 0 < len(terms_row_col):
+ terms[row, col] = terms_row_col
poly_matrix = init_poly_matrix(
terms=terms,
diff --git a/polymatrix/expression/mixins/expressionmixin.py b/polymatrix/expression/mixins/expressionmixin.py
index f8b5ef7..e75973e 100644
--- a/polymatrix/expression/mixins/expressionmixin.py
+++ b/polymatrix/expression/mixins/expressionmixin.py
@@ -3,31 +3,36 @@ import dataclasses
import typing
import numpy as np
from sympy import re
-from polymatrix.expression.init.initaccumulateexpr import init_accumulate_expr
+
from polymatrix.expression.init.initadditionexpr import init_addition_expr
from polymatrix.expression.init.initcacheexpr import init_cache_expr
from polymatrix.expression.init.initcombinationsexpr import init_combinations_expr
from polymatrix.expression.init.initderivativeexpr import init_derivative_expr
from polymatrix.expression.init.initdeterminantexpr import init_determinant_expr
+from polymatrix.expression.init.initdivergenceexpr import init_divergence_expr
from polymatrix.expression.init.initdivisionexpr import init_division_expr
from polymatrix.expression.init.initelemmultexpr import init_elem_mult_expr
from polymatrix.expression.init.initevalexpr import init_eval_expr
-from polymatrix.expression.init.initlinearin2expr import init_linear_in2_expr
from polymatrix.expression.init.initlinearinexpr import init_linear_in_expr
+from polymatrix.expression.init.initlinearinmonomialsinexpr import init_linear_in_monomials_in
from polymatrix.expression.init.initfromarrayexpr import init_from_array_expr
from polymatrix.expression.init.initgetitemexpr import init_get_item_expr
from polymatrix.expression.init.initlinearmatrixinexpr import init_linear_matrix_in_expr
+from polymatrix.expression.init.initlinearmonomialsexpr import init_linear_monomials_expr
from polymatrix.expression.init.initmatrixmultexpr import init_matrix_mult_expr
-from polymatrix.expression.init.initparametrizetermsexpr import init_parametrize_terms_expr
+from polymatrix.expression.init.initparametrizeexpr import init_parametrize_expr
from polymatrix.expression.init.initquadraticinexpr import init_quadratic_in_expr
from polymatrix.expression.init.initrepmatexpr import init_rep_mat_expr
from polymatrix.expression.init.initreshapeexpr import init_reshape_expr
-from polymatrix.expression.init.initsqueezeexpr import init_squeeze_expr
+from polymatrix.expression.init.initsetelementatexpr import init_set_element_at_expr
+from polymatrix.expression.init.initsosmonomialsexpr import init_sos_monomials_expr
+from polymatrix.expression.init.initsubtractmonomialsexpr import init_subtract_monomials_expr
from polymatrix.expression.init.initsumexpr import init_sum_expr
from polymatrix.expression.init.initsymmetricexpr import init_symmetric_expr
from polymatrix.expression.init.inittoconstantexpr import init_to_constant_expr
from polymatrix.expression.init.inittoquadraticexpr import init_to_quadratic_expr
from polymatrix.expression.init.inittransposeexpr import init_transpose_expr
+from polymatrix.expression.init.inittruncateexpr import init_truncate_expr
from polymatrix.expression.mixins.expressionbasemixin import ExpressionBaseMixin
from polymatrix.expression.polymatrix import PolyMatrix
@@ -48,15 +53,6 @@ class ExpressionMixin(
def apply(self, state: ExpressionState) -> tuple[ExpressionState, PolyMatrix]:
return self.underlying.apply(state)
- # # overwrites abstract method of `PolyMatrixExprBaseMixin`
- # @property
- # def degrees(self) -> set[int]:
- # return self.underlying.degrees
-
- # def __iter__(self):
- # for row in range(self.shape[0]):
- # yield self[row, 0]
-
def __add__(self, other: ExpressionBaseMixin) -> 'ExpressionMixin':
if other is None:
return self
@@ -181,7 +177,7 @@ class ExpressionMixin(
return dataclasses.replace(
self,
underlying=init_combinations_expr(
- underlying=self.underlying,
+ monomials=self.underlying,
number=number,
),
)
@@ -208,6 +204,18 @@ class ExpressionMixin(
),
)
+ def divergence(
+ self,
+ variables: tuple,
+ ) -> 'ExpressionMixin':
+ return dataclasses.replace(
+ self,
+ underlying=init_divergence_expr(
+ underlying=self.underlying,
+ variables=variables,
+ ),
+ )
+
def eval(
self,
variable: tuple,
@@ -222,7 +230,7 @@ class ExpressionMixin(
),
)
- def filter_linear_part(self, variable) -> 'ExpressionMixin':
+ def linear_matrix_in(self, variable: 'ExpressionMixin') -> 'ExpressionMixin':
return dataclasses.replace(
self,
underlying=init_linear_matrix_in_expr(
@@ -231,30 +239,73 @@ class ExpressionMixin(
),
)
- def linear_in(self, variables: tuple) -> 'ExpressionMixin':
+ def linear_monomials(self, variables: 'ExpressionMixin') -> 'ExpressionMixin':
return dataclasses.replace(
self,
- underlying=init_linear_in2_expr(
+ underlying=init_linear_monomials_expr(
underlying=self.underlying,
- expression=variables,
+ variables=variables,
),
)
- def parametrize(self, name: str, variables: tuple) -> 'ExpressionMixin':
+ def linear_in(
+ self,
+ variables: 'ExpressionMixin',
+ monomials: 'ExpressionMixin' = None,
+ ignore_unmatched: bool = None,
+ ) -> 'ExpressionMixin':
+ if monomials is None:
+ monomials = self.linear_monomials(variables)
+
return dataclasses.replace(
self,
- underlying=init_parametrize_terms_expr(
- name=name,
+ underlying=init_linear_in_expr(
underlying=self.underlying,
+ monomials=monomials,
variables=variables,
+ ignore_unmatched=ignore_unmatched,
),
)
- def quadratic_in(self, variables: tuple) -> 'ExpressionMixin':
+ def expressions_in(self, variables: tuple):
+ return dataclasses.replace(
+ self,
+ underlying=init_linear_in_monomials_in(
+ underlying=self.underlying,
+ variables=variables,
+ ),
+ )
+
+ def parametrize(self, name: str = None) -> 'ExpressionMixin':
+ return dataclasses.replace(
+ self,
+ underlying=init_parametrize_expr(
+ underlying=self.underlying,
+ name=name,
+ ),
+ )
+
+ def quadratic_in(self, variables: 'ExpressionMixin', monomials: 'ExpressionMixin' = None) -> 'ExpressionMixin':
+ if monomials is None:
+ monomials = self.quadratic_monomials(variables)
+
return dataclasses.replace(
self,
underlying=init_quadratic_in_expr(
underlying=self.underlying,
+ monomials=monomials,
+ variables=variables,
+ ),
+ )
+
+ def quadratic_monomials(
+ self,
+ variables: tuple,
+ ) -> 'ExpressionMixin':
+ return dataclasses.replace(
+ self,
+ underlying=init_sos_monomials_expr(
+ underlying=self.underlying,
variables=variables,
),
)
@@ -277,11 +328,57 @@ class ExpressionMixin(
),
)
- def squeeze(self):
+ # def squeeze(self):
+ # return dataclasses.replace(
+ # self,
+ # underlying=init_squeeze_expr(
+ # underlying=self.underlying,
+ # ),
+ # )
+
+ def set_element_at(
+ self,
+ row: int,
+ col: int,
+ value: 'ExpressionMixin',
+ ) -> 'ExpressionMixin':
+ if isinstance(value, ExpressionMixin):
+ value = value.underlying
+ else:
+ value = init_from_array_expr(value)
+
+ return dataclasses.replace(
+ self,
+ underlying=init_set_element_at_expr(
+ underlying=self.underlying,
+ index=(row, col),
+ value=value,
+ ),
+ )
+
+ def subtract_monomials(
+ self,
+ monomials: 'ExpressionMixin',
+ ) -> 'ExpressionMixin':
return dataclasses.replace(
self,
- underlying=init_squeeze_expr(
+ underlying=init_subtract_monomials_expr(
underlying=self.underlying,
+ monomials=monomials,
+ ),
+ )
+
+ def substitute(
+ self,
+ variable: tuple,
+ substitutions: tuple['ExpressionMixin', ...] = None,
+ ) -> 'ExpressionMixin':
+ return dataclasses.replace(
+ self,
+ underlying=init_eval_expr(
+ underlying=self.underlying,
+ variables=variable,
+ substitutions=substitutions,
),
)
@@ -316,3 +413,13 @@ class ExpressionMixin(
underlying=self.underlying,
),
)
+
+ def truncate(self, variables: tuple, degree: int):
+ return dataclasses.replace(
+ self,
+ underlying=init_truncate_expr(
+ underlying=self.underlying,
+ variables=variables,
+ degree=degree,
+ ),
+ )
diff --git a/polymatrix/expression/mixins/fromarrayexprmixin.py b/polymatrix/expression/mixins/fromarrayexprmixin.py
index 179cec4..2b066d4 100644
--- a/polymatrix/expression/mixins/fromarrayexprmixin.py
+++ b/polymatrix/expression/mixins/fromarrayexprmixin.py
@@ -1,5 +1,6 @@
import abc
+import math
from numpy import poly
import sympy
@@ -29,7 +30,9 @@ class FromArrayExprMixin(ExpressionBaseMixin):
try:
poly = sympy.poly(poly_data)
except sympy.polys.polyerrors.GeneratorsNeeded:
- terms[poly_row, poly_col] = {tuple(): poly_data}
+
+ if not math.isclose(poly_data, 0):
+ terms[poly_row, poly_col] = {tuple(): poly_data}
continue
for symbol in poly.gens:
@@ -41,17 +44,15 @@ class FromArrayExprMixin(ExpressionBaseMixin):
# a5 x1 x3**2 -> c=a5, m_cnt=(1, 0, 2)
for value, monomial_count in zip(poly.coeffs(), poly.monoms()):
- if value == 0.0:
+ if math.isclose(value, 0):
continue
# m_cnt=(1, 0, 2) -> m=(0, 2, 2)
def gen_monomial():
for rel_idx, p in enumerate(monomial_count):
-
- idx, _ = state.offset_dict[poly.gens[rel_idx]]
-
- for _ in range(p):
- yield idx
+ if 0 < p:
+ idx, _ = state.offset_dict[poly.gens[rel_idx]]
+ yield idx, p
monomial = tuple(gen_monomial())
diff --git a/polymatrix/expression/mixins/kktexprmixin.py b/polymatrix/expression/mixins/kktexprmixin.py
deleted file mode 100644
index d4a3d97..0000000
--- a/polymatrix/expression/mixins/kktexprmixin.py
+++ /dev/null
@@ -1,191 +0,0 @@
-
-import abc
-import itertools
-import typing
-import dataclass_abc
-from polymatrix.expression.derivativekey import DerivativeKey
-
-from polymatrix.expression.init.initpolymatrix import init_poly_matrix
-from polymatrix.expression.mixins.expressionbasemixin import ExpressionBaseMixin
-from polymatrix.expression.mixins.expressionmixin import ExpressionMixin
-from polymatrix.expression.polymatrix import PolyMatrix
-from polymatrix.expression.expressionstate import ExpressionState
-
-
-# todo: remove this?
-class KKTExprMixin(ExpressionBaseMixin):
- @property
- @abc.abstractmethod
- def cost(self) -> ExpressionMixin:
- ...
-
- @property
- @abc.abstractmethod
- def equality(self) -> ExpressionMixin:
- ...
-
- @property
- @abc.abstractmethod
- def equality(self) -> typing.Optional[ExpressionMixin]:
- ...
-
- @property
- @abc.abstractmethod
- def inequality(self) -> typing.Optional[ExpressionMixin]:
- ...
-
- @property
- @abc.abstractmethod
- def variables(self) -> ExpressionBaseMixin:
- ...
-
- # overwrites abstract method of `ExpressionBaseMixin`
- def apply(
- self,
- state: ExpressionState,
- ) -> tuple[ExpressionState, PolyMatrix]:
-
- state, cost = self.cost.apply(state=state)
-
- assert cost.shape[1] == 1
-
- if self.equality is not None:
-
- state, equality = self.equality.apply(state=state)
-
- state, equality_der = self.equality.diff(
- self.variables,
- introduce_derivatives=True,
- ).apply(state)
-
- assert cost.shape[0] == equality_der.shape[1]
-
- def acc_nu_variables(acc, v):
- state, nu_variables = acc
-
- nu_variable = state.n_param
- state = state.register(n_param=1)
-
- return state, nu_variables + [nu_variable]
-
- *_, (state, nu_variables) = tuple(itertools.accumulate(
- range(equality.shape[0]),
- acc_nu_variables,
- initial=(state, []),
- ))
-
- else:
- nu_variables = tuple()
-
- if self.inequality is not None:
-
- state, inequality = self.inequality.apply(state=state)
-
- state, inequality_der = self.inequality.diff(
- self.variables,
- introduce_derivatives=True,
- ).apply(state)
-
- assert cost.shape[0] == inequality_der.shape[1]
-
- def acc_lambda_variables(acc, v):
- state, lambda_variables = acc
-
- lambda_variable = state.n_param
- state = state.register(n_param=3)
-
- return state, lambda_variables + [lambda_variable]
-
- *_, (state, lambda_variables) = tuple(itertools.accumulate(
- range(inequality.shape[0]),
- acc_lambda_variables,
- initial=(state, []),
- ))
-
- else:
- lambda_variables = tuple()
-
- idx_start = 0
-
- terms = {}
-
- for row in range(cost.shape[0]):
- try:
- monomial_terms = cost.get_poly(row, 0)
- except KeyError:
- monomial_terms = {}
-
- for eq_idx, nu_variable in enumerate(nu_variables):
-
- try:
- underlying_terms = equality_der.get_poly(eq_idx, row)
- except KeyError:
- continue
-
- for monomial, value in underlying_terms.items():
- new_monomial = monomial + (nu_variable,)
-
- if new_monomial not in monomial_terms:
- monomial_terms[new_monomial] = 0
-
- monomial_terms[new_monomial] += value
-
- for inequality_idx, lambda_variable in enumerate(lambda_variables):
- try:
- underlying_terms = inequality_der.get_poly(inequality_idx, row)
- except KeyError:
- continue
-
- for monomial, value in underlying_terms.items():
- new_monomial = monomial + (lambda_variable,)
-
- if new_monomial not in monomial_terms:
- monomial_terms[new_monomial] = 0
-
- monomial_terms[new_monomial] += value
-
- terms[idx_start, 0] = monomial_terms
- idx_start += 1
-
- # equality constraints
- # --------------------
-
- for eq_idx, nu_variable in enumerate(nu_variables):
- try:
- underlying_terms = equality.get_poly(eq_idx, 0)
- except KeyError:
- continue
-
- terms[idx_start, 0] = underlying_terms
- idx_start += 1
-
- # inequality constraints
- # --------------------
-
- for inequality_idx, lambda_variable in enumerate(lambda_variables):
- r_lambda = lambda_variable + 1
- r_inequality = lambda_variable + 2
-
- try:
- underlying_terms = inequality.get_poly(inequality_idx, 0)
- except KeyError:
- continue
-
- # f(x) <= -0.01
- terms[idx_start, 0] = underlying_terms | {(r_inequality, r_inequality): 1}
- idx_start += 1
-
- # dual feasibility, lambda >= 0
- terms[idx_start, 0] = {(lambda_variable,): 1, (r_lambda, r_lambda): -1}
- idx_start += 1
-
- # complementary slackness
- terms[idx_start, 0] = {(r_lambda, r_inequality): 1}
- idx_start += 1
-
- poly_matrix = init_poly_matrix(
- terms=terms,
- shape=(idx_start, 1),
- )
-
- return state, poly_matrix
diff --git a/polymatrix/expression/mixins/linearinexprmixin.py b/polymatrix/expression/mixins/linearinexprmixin.py
index b690891..369467a 100644
--- a/polymatrix/expression/mixins/linearinexprmixin.py
+++ b/polymatrix/expression/mixins/linearinexprmixin.py
@@ -7,6 +7,7 @@ from polymatrix.expression.init.initpolymatrix import init_poly_matrix
from polymatrix.expression.mixins.expressionbasemixin import ExpressionBaseMixin
from polymatrix.expression.polymatrix import PolyMatrix
from polymatrix.expression.expressionstate import ExpressionState
+from polymatrix.expression.utils.getmonomialindices import get_monomial_indices
from polymatrix.expression.utils.getvariableindices import get_variable_indices
@@ -18,7 +19,17 @@ class LinearInExprMixin(ExpressionBaseMixin):
@property
@abc.abstractmethod
- def expression(self) -> ExpressionBaseMixin:
+ def monomials(self) -> ExpressionBaseMixin:
+ ...
+
+ @property
+ @abc.abstractmethod
+ def variables(self) -> tuple:
+ ...
+
+ @property
+ @abc.abstractmethod
+ def ignore_unmatched(self) -> bool:
...
# overwrites abstract method of `ExpressionBaseMixin`
@@ -27,59 +38,38 @@ class LinearInExprMixin(ExpressionBaseMixin):
state: ExpressionState,
) -> tuple[ExpressionState, PolyMatrix]:
state, underlying = self.underlying.apply(state=state)
+ state, monomials = get_monomial_indices(state, self.monomials)
+ state, variable_indices = get_variable_indices(state, self.variables)
- state, variables = self.expression.apply(state=state)
-
- def gen_variable_monomials():
- for _, term in variables.get_terms():
- assert len(term) == 1, f'{term} should have only a single monomial'
-
- for monomial in term.keys():
- yield monomial
-
- variable_monomials = tuple(gen_variable_monomials())
- all_variables = set(variable for monomial in variable_monomials for variable in monomial)
-
- # print(variables)
- # print(variable_monomials)
+ assert underlying.shape[1] == 1
terms = collections.defaultdict(dict)
for row in range(underlying.shape[0]):
- for col in range(underlying.shape[1]):
-
- try:
- underlying_terms = underlying.get_poly(row, col)
- except KeyError:
- continue
-
- for monomial, value in underlying_terms.items():
- for variable_index, variable_monomial in enumerate(variable_monomials):
+ try:
+ underlying_terms = underlying.get_poly(row, 0)
+ except KeyError:
+ continue
- remainder = list(monomial)
+ for monomial, value in underlying_terms.items():
- try:
- for variable in variable_monomial:
- remainder.remove(variable)
+ x_monomial = tuple((var_idx, count) for var_idx, count in monomial if var_idx in variable_indices)
+ p_monomial = tuple((var_idx, count) for var_idx, count in monomial if var_idx not in variable_indices)
- except ValueError:
- continue
-
- # print(remainder)
- # print(variable_monomial)
- # print(all(variable not in remainder for variable in all_variables))
-
- # take the first that matches
- if all(variable not in remainder for variable in all_variables):
-
- terms[row, variable_index][tuple(remainder)] = value
-
- break
+ try:
+ col = monomials.index(x_monomial)
+ except ValueError:
+ if self.ignore_unmatched:
+ continue
+ else:
+ raise Exception(f'{x_monomial} not in {monomials}')
+
+ terms[row, col][p_monomial] = value
poly_matrix = init_poly_matrix(
terms=dict(terms),
- shape=(underlying.shape[0], len(variable_monomials)),
+ shape=(underlying.shape[0], len(monomials)),
)
return state, poly_matrix
diff --git a/polymatrix/expression/mixins/linearinmutipleexprmixin.py b/polymatrix/expression/mixins/linearinmutipleexprmixin.py
new file mode 100644
index 0000000..7d4f00e
--- /dev/null
+++ b/polymatrix/expression/mixins/linearinmutipleexprmixin.py
@@ -0,0 +1,144 @@
+
+import abc
+import collections
+
+from polymatrix.expression.init.initpolymatrix import init_poly_matrix
+from polymatrix.expression.mixins.expressionbasemixin import ExpressionBaseMixin
+from polymatrix.expression.polymatrix import PolyMatrix
+from polymatrix.expression.expressionstate import ExpressionState
+from polymatrix.expression.utils.getvariableindices import get_variable_indices
+
+
+class LinearInMultipleExprMixin(ExpressionBaseMixin):
+ @property
+ @abc.abstractmethod
+ def underlying(self) -> ExpressionBaseMixin:
+ ...
+
+ @property
+ @abc.abstractmethod
+ def variables(self) -> ExpressionBaseMixin:
+ ...
+
+ # overwrites abstract method of `ExpressionBaseMixin`
+ def apply(
+ self,
+ state: ExpressionState,
+ ) -> tuple[ExpressionState, PolyMatrix]:
+ state, underlying = self.underlying.apply(state=state)
+
+ state, variables = self.variables.apply(state=state)
+
+ def gen_variable_monomials():
+ for _, term in variables.get_terms():
+ assert len(term) == 1, f'{term} should have only a single monomial'
+
+ for monomial in term.keys():
+ yield monomial
+
+ variable_monomials = tuple(gen_variable_monomials())
+ all_variables = set(variable for monomial in variable_monomials for variable in monomial)
+
+ terms = {}
+ idx_row = 0
+
+ for row in range(underlying.shape[0]):
+ for col in range(underlying.shape[1]):
+
+ try:
+ underlying_terms = underlying.get_poly(row, col)
+ except KeyError:
+ continue
+
+ x_monomial_terms = collections.defaultdict(lambda: collections.defaultdict(float))
+
+ for monomial, value in underlying_terms.items():
+
+ assert tuple(sorted(monomial)) == monomial, f'{monomial} is not sorted'
+
+ x_monomial = tuple((var_idx for var_idx in monomial if var_idx in all_variables))
+ p_monomial = tuple(var_idx for var_idx in monomial if var_idx not in all_variables)
+
+ x_monomial_terms[x_monomial][p_monomial] += value
+
+ for data in x_monomial_terms.values():
+ terms[idx_row, 0] = dict(data)
+ idx_row += 1
+
+ poly_matrix = init_poly_matrix(
+ terms=terms,
+ shape=(idx_row, 1),
+ )
+
+ return state, poly_matrix
+
+ # state, variable_indices = get_variable_indices(state, self.variables)
+
+ # underlying_terms = underlying.get_terms()
+
+ # def gen_variable_terms():
+ # for _, monomial_term in underlying_terms:
+ # for monomial in monomial_term.keys():
+
+ # x_monomial = tuple(var_idx for var_idx in monomial if var_idx in variable_indices)
+ # yield x_monomial
+
+ # variable_terms = tuple(set(gen_variable_terms()))
+
+ # terms = {}
+
+ # for (row, _), monomial_term in underlying.get_terms():
+
+ # x_monomial_terms = collections.defaultdict(lambda: collections.defaultdict(float))
+
+ # for monomial, value in monomial_term.items():
+
+ # x_monomial = tuple(var_idx for var_idx in monomial if var_idx in variable_indices)
+ # p_monomial = tuple(var_idx for var_idx in monomial if var_idx not in variable_indices)
+
+ # assert tuple(sorted(x_monomial)) == x_monomial, f'{x_monomial} is not sorted'
+
+ # x_monomial_terms[x_monomial][p_monomial] += value
+
+ # for x_monomial, data in x_monomial_terms.items():
+ # terms[row, variable_terms.index(x_monomial)] = dict(data)
+
+ # poly_matrix = init_poly_matrix(
+ # terms=terms,
+ # shape=(underlying.shape[0], len(variable_terms)),
+ # )
+
+ # return state, poly_matrix
+
+ # terms = {}
+ # idx_row = 0
+
+ # for row in range(underlying.shape[0]):
+ # for col in range(underlying.shape[1]):
+
+ # try:
+ # underlying_terms = underlying.get_poly(row, col)
+ # except KeyError:
+ # continue
+
+ # x_monomial_terms = collections.defaultdict(lambda: collections.defaultdict(float))
+
+ # for monomial, value in underlying_terms.items():
+
+ # x_monomial = tuple(var_idx for var_idx in monomial if var_idx in variable_indices)
+ # p_monomial = tuple(var_idx for var_idx in monomial if var_idx not in variable_indices)
+
+ # assert tuple(sorted(x_monomial)) == x_monomial, f'{x_monomial} is not sorted'
+
+ # x_monomial_terms[x_monomial][p_monomial] += value
+
+ # for data in x_monomial_terms.values():
+ # terms[idx_row, 0] = dict(data)
+ # idx_row += 1
+
+ # poly_matrix = init_poly_matrix(
+ # terms=terms,
+ # shape=(idx_row, 1),
+ # )
+
+ # return state, poly_matrix
diff --git a/polymatrix/expression/mixins/linearmatrixinexprmixin.py b/polymatrix/expression/mixins/linearmatrixinexprmixin.py
new file mode 100644
index 0000000..956b01a
--- /dev/null
+++ b/polymatrix/expression/mixins/linearmatrixinexprmixin.py
@@ -0,0 +1,61 @@
+
+import abc
+import collections
+from numpy import var
+
+from polymatrix.expression.init.initpolymatrix import init_poly_matrix
+from polymatrix.expression.mixins.expressionbasemixin import ExpressionBaseMixin
+from polymatrix.expression.polymatrix import PolyMatrix
+from polymatrix.expression.expressionstate import ExpressionState
+from polymatrix.expression.utils.getvariableindices import get_variable_indices
+
+
+class LinearMatrixInExprMixin(ExpressionBaseMixin):
+ @property
+ @abc.abstractmethod
+ def underlying(self) -> ExpressionBaseMixin:
+ ...
+
+ @property
+ @abc.abstractmethod
+ def variables(self) -> tuple:
+ ...
+
+ # overwrites abstract method of `ExpressionBaseMixin`
+ def apply(
+ self,
+ state: ExpressionState,
+ ) -> tuple[ExpressionState, PolyMatrix]:
+ state, underlying = self.underlying.apply(state=state)
+ state, variable_index = get_variable_indices(state, variables=self.variable)
+
+ assert len(variable_index) == 1
+
+ terms = collections.defaultdict(dict)
+
+ for row in range(underlying.shape[0]):
+ for col in range(underlying.shape[1]):
+
+ try:
+ underlying_terms = underlying.get_poly(row, col)
+ except KeyError:
+ continue
+
+ for monomial, value in underlying_terms.items():
+
+ if len(monomial) == 1:
+
+ variable, _ = monomial[0]
+
+ if variable == variable_index:
+
+ terms[row, col][tuple()] = value
+
+ break
+
+ poly_matrix = init_poly_matrix(
+ terms=dict(terms),
+ shape=underlying.shape,
+ )
+
+ return state, poly_matrix
diff --git a/polymatrix/expression/mixins/linearmonomialsexprmixin.py b/polymatrix/expression/mixins/linearmonomialsexprmixin.py
new file mode 100644
index 0000000..42a5b95
--- /dev/null
+++ b/polymatrix/expression/mixins/linearmonomialsexprmixin.py
@@ -0,0 +1,67 @@
+
+import abc
+import dataclasses
+
+from polymatrix.expression.init.initpolymatrix import init_poly_matrix
+from polymatrix.expression.mixins.expressionbasemixin import ExpressionBaseMixin
+from polymatrix.expression.mixins.expressionstatemixin import ExpressionStateMixin
+from polymatrix.expression.mixins.polymatrixmixin import PolyMatrixMixin
+from polymatrix.expression.mixins.quadraticinexprmixin import QuadraticInExprMixin
+from polymatrix.expression.utils.getvariableindices import get_variable_indices
+from polymatrix.expression.utils.sortmonomials import sort_monomials
+from polymatrix.expression.utils.splitmonomialindices import split_monomial_indices
+
+
+class LinearMonomialsExprMixin(ExpressionBaseMixin):
+ @property
+ @abc.abstractclassmethod
+ def underlying(self) -> ExpressionBaseMixin:
+ ...
+
+ @property
+ @abc.abstractmethod
+ def variables(self) -> tuple:
+ ...
+
+ # overwrites abstract method of `ExpressionBaseMixin`
+ def apply(
+ self,
+ state: ExpressionStateMixin,
+ ) -> tuple[ExpressionStateMixin, PolyMatrixMixin]:
+
+ state, underlying = self.underlying.apply(state=state)
+ state, variable_indices = get_variable_indices(state, self.variables)
+
+ def gen_linear_monomials():
+ for row in range(underlying.shape[0]):
+ for col in range(underlying.shape[1]):
+
+ try:
+ polynomial = underlying.get_poly(row, col)
+ except KeyError:
+ continue
+
+ for monomial in polynomial.keys():
+ x_monomial = tuple((var_idx, count) for var_idx, count in monomial if var_idx in variable_indices)
+
+ yield x_monomial
+
+ linear_monomials = sort_monomials(set(gen_linear_monomials()))
+
+ def gen_terms():
+ for index, monomial in enumerate(linear_monomials):
+ yield (index, 0), {monomial: 1.0}
+
+ terms = dict(gen_terms())
+
+ poly_matrix = init_poly_matrix(
+ terms=terms,
+ shape=(len(linear_monomials), 1),
+ )
+
+ state = dataclasses.replace(
+ state,
+ cache=state.cache | {self: poly_matrix},
+ )
+
+ return state, poly_matrix
diff --git a/polymatrix/expression/mixins/matrixmultexprmixin.py b/polymatrix/expression/mixins/matrixmultexprmixin.py
index 1f09fdc..029f700 100644
--- a/polymatrix/expression/mixins/matrixmultexprmixin.py
+++ b/polymatrix/expression/mixins/matrixmultexprmixin.py
@@ -1,11 +1,14 @@
import abc
import itertools
+import math
from polymatrix.expression.init.initpolymatrix import init_poly_matrix
from polymatrix.expression.mixins.expressionbasemixin import ExpressionBaseMixin
from polymatrix.expression.polymatrix import PolyMatrix
from polymatrix.expression.expressionstate import ExpressionState
+from polymatrix.expression.utils.mergemonomialindices import merge_monomial_indices
+from polymatrix.expression.utils.multiplypolynomial import multiply_polynomial
class MatrixMultExprMixin(ExpressionBaseMixin):
@@ -44,23 +47,27 @@ class MatrixMultExprMixin(ExpressionBaseMixin):
except KeyError:
continue
- for (left_monomial, left_value), (right_monomial, right_value) \
- in itertools.product(left_terms.items(), right_terms.items()):
+ # for (left_monomial, left_value), (right_monomial, right_value) \
+ # in itertools.product(left_terms.items(), right_terms.items()):
- value = left_value * right_value
+ # value = left_value * right_value
- if value == 0:
- continue
+ # if value == 0:
+ # continue
- monomial = tuple(sorted(left_monomial + right_monomial))
+ # # monomial = tuple(sorted(left_monomial + right_monomial))
+ # monomial = merge_monomial_indices((left_monomial, right_monomial))
- if monomial not in terms_row_col:
- terms_row_col[monomial] = 0
+ # if monomial not in terms_row_col:
+ # terms_row_col[monomial] = 0
- terms_row_col[monomial] += value
+ # terms_row_col[monomial] += value
+
+ multiply_polynomial(left_terms, right_terms, terms_row_col)
if 0 < len(terms_row_col):
terms[poly_row, poly_col] = terms_row_col
+ # terms[poly_row, poly_col] = {key: val for key, val in terms_row_col.items() if not math.isclose(val, 0, abs_tol=1e-12)}
poly_matrix = init_poly_matrix(
terms=terms,
diff --git a/polymatrix/expression/mixins/oldlinearexprmixin.py b/polymatrix/expression/mixins/oldlinearexprmixin.py
deleted file mode 100644
index 1bdebd9..0000000
--- a/polymatrix/expression/mixins/oldlinearexprmixin.py
+++ /dev/null
@@ -1,131 +0,0 @@
-
-import abc
-import collections
-from numpy import var
-
-from polymatrix.expression.init.initpolymatrix import init_poly_matrix
-from polymatrix.expression.mixins.expressionbasemixin import ExpressionBaseMixin
-from polymatrix.expression.polymatrix import PolyMatrix
-from polymatrix.expression.expressionstate import ExpressionState
-from polymatrix.expression.utils.getvariableindices import get_variable_indices
-
-
-class OldLinearExprMixin(ExpressionBaseMixin):
- @property
- @abc.abstractmethod
- def underlying(self) -> ExpressionBaseMixin:
- ...
-
- @property
- @abc.abstractmethod
- def variables(self) -> ExpressionBaseMixin:
- ...
-
- # overwrites abstract method of `ExpressionBaseMixin`
- def apply(
- self,
- state: ExpressionState,
- ) -> tuple[ExpressionState, PolyMatrix]:
- state, underlying = self.underlying.apply(state=state)
-
- state, variable_indices = get_variable_indices(state, self.variables)
-
- underlying_terms = underlying.get_terms()
-
- def gen_variable_terms():
- for _, monomial_term in underlying_terms:
- for monomial in monomial_term.keys():
-
- x_monomial = tuple(var_idx for var_idx in monomial if var_idx in variable_indices)
- yield x_monomial
-
- variable_terms = tuple(set(gen_variable_terms()))
-
- terms = {}
-
- for (row, _), monomial_term in underlying_terms:
-
- x_monomial_terms = collections.defaultdict(lambda: collections.defaultdict(float))
-
- for monomial, value in monomial_term.items():
-
- x_monomial = tuple(var_idx for var_idx in monomial if var_idx in variable_indices)
- p_monomial = tuple(var_idx for var_idx in monomial if var_idx not in variable_indices)
-
- assert tuple(sorted(x_monomial)) == x_monomial, f'{x_monomial} is not sorted'
-
- x_monomial_terms[x_monomial][p_monomial] += value
-
- for x_monomial, data in x_monomial_terms.items():
- terms[row, variable_terms.index(x_monomial)] = dict(data)
-
- poly_matrix = init_poly_matrix(
- terms=terms,
- shape=(underlying.shape[0], len(variable_terms)),
- )
-
- return state, poly_matrix
-
-
- # for row in range(underlying.shape[0]):
- # for col in range(underlying.shape[1]):
-
- # try:
- # underlying_terms = underlying.get_poly(row, col)
- # except KeyError:
- # continue
-
- # x_monomial_terms = collections.defaultdict(lambda: collections.defaultdict(float))
-
- # for monomial, value in underlying_terms.items():
-
- # x_monomial = tuple(var_idx for var_idx in monomial if var_idx in variable_indices)
- # p_monomial = tuple(var_idx for var_idx in monomial if var_idx not in variable_indices)
-
- # assert tuple(sorted(x_monomial)) == x_monomial, f'{x_monomial} is not sorted'
-
- # x_monomial_terms[x_monomial][p_monomial] += value
-
- # for data in x_monomial_terms.values():
- # terms[idx_row, 0] = dict(data)
- # idx_row += 1
-
- # poly_matrix = init_poly_matrix(
- # terms=terms,
- # shape=(idx_row, 1),
- # )
-
- # return state, poly_matrix
-
- # terms = {}
- # idx_row = 0
-
- # for row in range(underlying.shape[0]):
- # for col in range(underlying.shape[1]):
-
- # try:
- # underlying_terms = underlying.get_poly(row, col)
- # except KeyError:
- # continue
-
- # x_monomial_terms = collections.defaultdict(lambda: collections.defaultdict(float))
-
- # for monomial, value in underlying_terms.items():
-
- # x_monomial = tuple(var_idx for var_idx in monomial if var_idx in variable_indices)
- # p_monomial = tuple(var_idx for var_idx in monomial if var_idx not in variable_indices)
-
- # assert tuple(sorted(x_monomial)) == x_monomial, f'{x_monomial} is not sorted'
-
- # x_monomial_terms[x_monomial][p_monomial] += value
-
- # for data in x_monomial_terms.values():
- # terms[idx_row, 0] = dict(data)
- # idx_row += 1
-
- # poly_matrix = init_poly_matrix(
- # terms=terms,
- # shape=(idx_row, 1),
- # )
-
- # return state, poly_matrix
diff --git a/polymatrix/expression/mixins/parametrizeexprmixin.py b/polymatrix/expression/mixins/parametrizeexprmixin.py
new file mode 100644
index 0000000..11a767f
--- /dev/null
+++ b/polymatrix/expression/mixins/parametrizeexprmixin.py
@@ -0,0 +1,58 @@
+
+import abc
+import dataclasses
+
+from polymatrix.expression.init.initpolymatrix import init_poly_matrix
+from polymatrix.expression.mixins.expressionbasemixin import ExpressionBaseMixin
+from polymatrix.expression.mixins.expressionstatemixin import ExpressionStateMixin
+from polymatrix.expression.mixins.polymatrixmixin import PolyMatrixMixin
+
+
+class ParametrizeExprMixin(ExpressionBaseMixin):
+ @property
+ @abc.abstractclassmethod
+ def underlying(self) -> ExpressionBaseMixin:
+ ...
+
+ @property
+ @abc.abstractclassmethod
+ def name(self) -> str:
+ ...
+
+ # overwrites abstract method of `ExpressionBaseMixin`
+ def apply(
+ self,
+ state: ExpressionStateMixin,
+ ) -> tuple[ExpressionStateMixin, PolyMatrixMixin]:
+
+ # cache polymatrix to not re-parametrize at every apply call
+ if self in state.cache:
+ return state, state.cache[self]
+
+ state, underlying = self.underlying.apply(state)
+
+ assert underlying.shape[1] == 1
+
+ start_index = state.n_param
+ terms = {}
+
+ for row in range(underlying.shape[0]):
+ var_index = start_index + row
+ terms[row, 0] = {((var_index, 1),): 1.0}
+
+ state = state.register(
+ key=self,
+ n_param=underlying.shape[0],
+ )
+
+ poly_matrix = init_poly_matrix(
+ terms=terms,
+ shape=underlying.shape,
+ )
+
+ state = dataclasses.replace(
+ state,
+ cache=state.cache | {self: poly_matrix},
+ )
+
+ return state, poly_matrix
diff --git a/polymatrix/expression/mixins/parametrizetermsexprmixin.py b/polymatrix/expression/mixins/parametrizetermsexprmixin.py
index d678db5..51244d4 100644
--- a/polymatrix/expression/mixins/parametrizetermsexprmixin.py
+++ b/polymatrix/expression/mixins/parametrizetermsexprmixin.py
@@ -7,23 +7,24 @@ from polymatrix.expression.mixins.expressionbasemixin import ExpressionBaseMixin
from polymatrix.expression.mixins.expressionstatemixin import ExpressionStateMixin
from polymatrix.expression.mixins.polymatrixmixin import PolyMatrixMixin
from polymatrix.expression.utils.getvariableindices import get_variable_indices
+from polymatrix.expression.utils.mergemonomialindices import merge_monomial_indices
class ParametrizeTermsExprMixin(ExpressionBaseMixin):
- @property
- @abc.abstractclassmethod
- def name(self) -> str:
- ...
+ # @property
+ # @abc.abstractclassmethod
+ # def name(self) -> str:
+ # ...
@property
@abc.abstractclassmethod
def underlying(self) -> ExpressionBaseMixin:
...
- @property
- @abc.abstractclassmethod
- def variables(self) -> tuple:
- ...
+ # @property
+ # @abc.abstractclassmethod
+ # def variables(self) -> tuple:
+ # ...
# overwrites abstract method of `ExpressionBaseMixin`
def apply(
@@ -31,17 +32,13 @@ class ParametrizeTermsExprMixin(ExpressionBaseMixin):
state: ExpressionStateMixin,
) -> tuple[ExpressionStateMixin, PolyMatrixMixin]:
+ # cache polymatrix to not re-parametrize at every apply call
if self in state.cache:
return state, state.cache[self]
state, underlying = self.underlying.apply(state)
- # for variable in self.variables:
- # state = state.register(key=variable, n_param=1)
-
- # variable_indices = tuple(state.offset_dict[variable][0] for variable in self.variables if variable in state.offset_dict)
-
- state, variable_indices = get_variable_indices(state, self.variables)
+ # state, variable_indices = get_variable_indices(state, self.variables)
start_index = state.n_param
terms = {}
@@ -54,22 +51,33 @@ class ParametrizeTermsExprMixin(ExpressionBaseMixin):
except KeyError:
continue
- def gen_x_monomial_terms():
- for monomial, value in underlying_terms.items():
- x_monomial = tuple(var_idx for var_idx in monomial if var_idx in variable_indices)
- yield monomial, x_monomial, value
+ # def gen_x_monomial_terms():
+ # for monomial, value in underlying_terms.items():
+ # x_monomial = tuple((var_idx, count) for var_idx, count in monomial if var_idx in variable_indices)
+ # yield monomial, x_monomial, value
- x_monomial_terms = tuple(gen_x_monomial_terms())
+ # x_monomial_terms = tuple(gen_x_monomial_terms())
- collected_terms = tuple(sorted(set((x_monomial for _, x_monomial, _ in x_monomial_terms))))
+ # # use sort to make the parametrization predictable
+ # # x_monomials = (0, 1), then the parametrization is (2, 3)
+ # collected_terms = tuple(sorted(
+ # set((x_monomial for _, x_monomial, _ in x_monomial_terms)),
+ # key=lambda m: (sum(count for _, count in m), m)),
+ # )
+ collected_terms = tuple(underlying_terms.keys())
+
+ # print(tuple(x_monomial for _, x_monomial, _ in x_monomial_terms))
+ # print(x_monomial_terms)
terms_row_col = {}
- for monomial, x_monomial, value in x_monomial_terms:
+ for monomial, value in underlying_terms.items():
+
+ param_index = start_index + collected_terms.index(monomial)
- new_monomial = monomial + (start_index + collected_terms.index(x_monomial),)
+ monomial_with_param = merge_monomial_indices(monomial, ((param_index, 1),))
- terms_row_col[new_monomial] = value
+ terms_row_col[monomial_with_param] = value
terms[row, col] = terms_row_col
diff --git a/polymatrix/expression/mixins/quadraticinexprmixin.py b/polymatrix/expression/mixins/quadraticinexprmixin.py
new file mode 100644
index 0000000..dbfb76d
--- /dev/null
+++ b/polymatrix/expression/mixins/quadraticinexprmixin.py
@@ -0,0 +1,67 @@
+
+import abc
+import collections
+
+from polymatrix.expression.init.initpolymatrix import init_poly_matrix
+from polymatrix.expression.mixins.expressionbasemixin import ExpressionBaseMixin
+from polymatrix.expression.polymatrix import PolyMatrix
+from polymatrix.expression.expressionstate import ExpressionState
+from polymatrix.expression.utils.getmonomialindices import get_monomial_indices
+from polymatrix.expression.utils.getvariableindices import get_variable_indices
+from polymatrix.expression.utils.splitmonomialindices import split_monomial_indices
+
+
+class QuadraticInExprMixin(ExpressionBaseMixin):
+ @property
+ @abc.abstractmethod
+ def underlying(self) -> ExpressionBaseMixin:
+ ...
+
+ @property
+ @abc.abstractmethod
+ def monomials(self) -> ExpressionBaseMixin:
+ ...
+
+ @property
+ @abc.abstractmethod
+ def variables(self) -> tuple:
+ ...
+
+ # overwrites abstract method of `ExpressionBaseMixin`
+ def apply(
+ self,
+ state: ExpressionState,
+ ) -> tuple[ExpressionState, PolyMatrix]:
+ state, underlying = self.underlying.apply(state=state)
+ state, sos_monomials = get_monomial_indices(state, self.monomials)
+ state, variable_indices = get_variable_indices(state, self.variables)
+
+ assert underlying.shape == (1, 1), f'underlying shape is {underlying.shape}'
+
+ underlying_terms = underlying.get_poly(0, 0)
+
+ terms = collections.defaultdict(dict)
+
+ for monomial, value in underlying_terms.items():
+
+ x_monomial = tuple((var_idx, count) for var_idx, count in monomial if var_idx in variable_indices)
+ p_monomial = tuple((var_idx, count) for var_idx, count in monomial if var_idx not in variable_indices)
+
+ left, right = split_monomial_indices(x_monomial)
+
+ row = sos_monomials.index(left)
+ col = sos_monomials.index(right)
+
+ monomial_terms = terms[row, col]
+
+ if p_monomial not in monomial_terms:
+ monomial_terms[p_monomial] = 0
+
+ monomial_terms[p_monomial] += value
+
+ poly_matrix = init_poly_matrix(
+ terms=dict(terms),
+ shape=2*(len(sos_monomials),),
+ )
+
+ return state, poly_matrix
diff --git a/polymatrix/expression/mixins/quadraticmonomialsexprmixin.py b/polymatrix/expression/mixins/quadraticmonomialsexprmixin.py
new file mode 100644
index 0000000..b953d17
--- /dev/null
+++ b/polymatrix/expression/mixins/quadraticmonomialsexprmixin.py
@@ -0,0 +1,69 @@
+
+import abc
+import dataclasses
+
+from polymatrix.expression.init.initpolymatrix import init_poly_matrix
+from polymatrix.expression.mixins.expressionbasemixin import ExpressionBaseMixin
+from polymatrix.expression.mixins.expressionstatemixin import ExpressionStateMixin
+from polymatrix.expression.mixins.polymatrixmixin import PolyMatrixMixin
+from polymatrix.expression.mixins.quadraticinexprmixin import QuadraticInExprMixin
+from polymatrix.expression.utils.getvariableindices import get_variable_indices
+from polymatrix.expression.utils.splitmonomialindices import split_monomial_indices
+
+
+class QuadraticMonomialsExprMixin(ExpressionBaseMixin):
+ @property
+ @abc.abstractclassmethod
+ def underlying(self) -> ExpressionBaseMixin:
+ ...
+
+ @property
+ @abc.abstractmethod
+ def variables(self) -> tuple:
+ ...
+
+ # overwrites abstract method of `ExpressionBaseMixin`
+ def apply(
+ self,
+ state: ExpressionStateMixin,
+ ) -> tuple[ExpressionStateMixin, PolyMatrixMixin]:
+
+ state, underlying = self.underlying.apply(state=state)
+ state, variable_indices = get_variable_indices(state, self.variables)
+
+ def gen_sos_monomials():
+ for row in range(underlying.shape[0]):
+ for col in range(underlying.shape[1]):
+
+ try:
+ polynomial = underlying.get_poly(row, col)
+ except KeyError:
+ continue
+
+ for monomial in polynomial.keys():
+ x_monomial = tuple((var_idx, count) for var_idx, count in monomial if var_idx in variable_indices)
+
+ left, right = split_monomial_indices(x_monomial)
+
+ yield left
+ yield right
+
+ sos_monomials = tuple(sorted(set(gen_sos_monomials()), key=lambda m: (len(m), m)))
+
+ def gen_terms():
+ for index, monomial in enumerate(sos_monomials):
+ yield (index, 0), {monomial: 1.0}
+
+ terms = dict(gen_terms())
+
+ poly_matrix = init_poly_matrix(
+ terms=terms,
+ shape=(len(sos_monomials), 1),
+ )
+
+ state = dataclasses.replace(
+ state,
+ cache=state.cache | {self: poly_matrix},
+ )
+
+ return state, poly_matrix
diff --git a/polymatrix/expression/mixins/quadraticreprexprmixin.py b/polymatrix/expression/mixins/quadraticreprexprmixin.py
deleted file mode 100644
index 29ef950..0000000
--- a/polymatrix/expression/mixins/quadraticreprexprmixin.py
+++ /dev/null
@@ -1,87 +0,0 @@
-
-import abc
-import collections
-
-from polymatrix.expression.init.initpolymatrix import init_poly_matrix
-from polymatrix.expression.mixins.expressionbasemixin import ExpressionBaseMixin
-from polymatrix.expression.polymatrix import PolyMatrix
-from polymatrix.expression.expressionstate import ExpressionState
-from polymatrix.expression.utils.getvariableindices import get_variable_indices
-
-
-class QuadraticReprExprMixin(ExpressionBaseMixin):
- @property
- @abc.abstractmethod
- def underlying(self) -> ExpressionBaseMixin:
- ...
-
- @property
- @abc.abstractmethod
- def variables(self) -> tuple:
- ...
-
- # overwrites abstract method of `ExpressionBaseMixin`
- def apply(
- self,
- state: ExpressionState,
- ) -> tuple[ExpressionState, PolyMatrix]:
- state, underlying = self.underlying.apply(state=state)
-
- assert underlying.shape == (1, 1)
-
- state, variable_indices = get_variable_indices(state, self.variables)
-
- # state, variables = self.expression.apply(state=state)
-
- def gen_variable_monomials():
- _, monomials = underlying.get_terms()[0]
-
- for monomial, value in monomials.items():
- x_monomial = tuple(variable for variable in monomial if variable in variable_indices)
- p_monomial = tuple(variable for variable in monomial if variable not in variable_indices)
-
- assert len(x_monomial) % 2 == 0, f'length of {x_monomial} should be a multiple of 2'
-
- x_monomial_sorted = tuple(sorted(x_monomial))
-
- idx_half = int(len(x_monomial)/2)
-
- yield x_monomial_sorted[:idx_half], x_monomial_sorted[idx_half:], p_monomial, value
-
- underlying_monomials = tuple(gen_variable_monomials())
-
- def gen_sos_monomials():
- for left_monomial, right_monomial, _, _ in underlying_monomials:
- yield left_monomial
- yield right_monomial
-
- sos_monomials = tuple(sorted(set(gen_sos_monomials()), key=lambda m: (len(m), m)))
-
- # print(f'{underlying_monomials=}')
- # print(f'{sos_monomials=}')
-
- terms = collections.defaultdict(dict)
-
- for left_monomial, right_monomial, p_monomial, value in underlying_monomials:
-
- col = sos_monomials.index(left_monomial)
- row = sos_monomials.index(right_monomial)
- key = (row, col)
-
- # print(key)
-
- monomial_terms = terms[key]
-
- if p_monomial not in monomial_terms:
- monomial_terms[p_monomial] = 0
-
- monomial_terms[p_monomial] += value
-
- # print(terms)
-
- poly_matrix = init_poly_matrix(
- terms=dict(terms),
- shape=2*(len(sos_monomials),),
- )
-
- return state, poly_matrix
diff --git a/polymatrix/expression/mixins/setelementatexprmixin.py b/polymatrix/expression/mixins/setelementatexprmixin.py
new file mode 100644
index 0000000..5714631
--- /dev/null
+++ b/polymatrix/expression/mixins/setelementatexprmixin.py
@@ -0,0 +1,69 @@
+
+import abc
+import dataclasses
+import typing
+import dataclass_abc
+from polymatrix.expression.mixins.polymatrixmixin import PolyMatrixMixin
+
+from polymatrix.expression.init.initpolymatrix import init_poly_matrix
+from polymatrix.expression.mixins.expressionbasemixin import ExpressionBaseMixin
+from polymatrix.expression.polymatrix import PolyMatrix
+from polymatrix.expression.expressionstate import ExpressionState
+
+
+class SetElementAtExprMixin(ExpressionBaseMixin):
+ @dataclasses.dataclass(frozen=True)
+ class Slice:
+ start: int
+ step: int
+ stop: int
+
+ @property
+ @abc.abstractmethod
+ def underlying(self) -> ExpressionBaseMixin:
+ ...
+
+ @property
+ @abc.abstractmethod
+ def index(self) -> tuple[tuple[int, ...], tuple[int, ...]]:
+ ...
+
+ @property
+ @abc.abstractmethod
+ def value(self) -> ExpressionBaseMixin:
+ ...
+
+ # overwrites abstract method of `ExpressionBaseMixin`
+ def apply(
+ self,
+ state: ExpressionState,
+ ) -> tuple[ExpressionState, PolyMatrix]:
+ state, underlying = self.underlying.apply(state=state)
+ state, value = self.value.apply(state=state)
+
+ assert value.shape == (1, 1)
+
+ @dataclass_abc.dataclass_abc(frozen=True)
+ class SetElementAtPolyMatrix(PolyMatrixMixin):
+ underlying: PolyMatrixMixin
+ shape: tuple[int, int]
+ index: tuple[int, int]
+ value: dict
+
+ # @property
+ # def shape(self) -> tuple[int, int]:
+ # return self.shape
+
+ def get_poly(self, row: int, col: int) -> dict[tuple[int, ...], float]:
+ if (row, col) == self.index:
+ return self.value
+ else:
+ return self.underlying.get_poly(row, col)
+
+ return state, SetElementAtPolyMatrix(
+ underlying=underlying,
+ index=self.index,
+ shape=underlying.shape,
+ value=value.get_poly(0, 0)
+ )
+ \ No newline at end of file
diff --git a/polymatrix/expression/mixins/squeezeexprmixin.py b/polymatrix/expression/mixins/squeezeexprmixin.py
index 47f2fcf..62fe7de 100644
--- a/polymatrix/expression/mixins/squeezeexprmixin.py
+++ b/polymatrix/expression/mixins/squeezeexprmixin.py
@@ -10,6 +10,7 @@ from polymatrix.expression.polymatrix import PolyMatrix
from polymatrix.expression.expressionstate import ExpressionState
+# remove?
class SqueezeExprMixin(ExpressionBaseMixin):
@property
@abc.abstractmethod
diff --git a/polymatrix/expression/mixins/substituteexprmixin.py b/polymatrix/expression/mixins/substituteexprmixin.py
new file mode 100644
index 0000000..fc71f73
--- /dev/null
+++ b/polymatrix/expression/mixins/substituteexprmixin.py
@@ -0,0 +1,107 @@
+
+import abc
+import collections
+import itertools
+import math
+
+from polymatrix.expression.init.initpolymatrix import init_poly_matrix
+from polymatrix.expression.mixins.expressionbasemixin import ExpressionBaseMixin
+from polymatrix.expression.polymatrix import PolyMatrix
+from polymatrix.expression.expressionstate import ExpressionState
+from polymatrix.expression.utils.getvariableindices import get_variable_indices
+from polymatrix.expression.utils.multiplypolynomial import multiply_polynomial
+
+
+class SubstituteExprMixin(ExpressionBaseMixin):
+ @property
+ @abc.abstractmethod
+ def underlying(self) -> ExpressionBaseMixin:
+ ...
+
+ @property
+ @abc.abstractmethod
+ def variables(self) -> tuple:
+ ...
+
+ @property
+ @abc.abstractmethod
+ def substitutions(self) -> tuple[ExpressionBaseMixin, ...]:
+ ...
+
+ # overwrites abstract method of `ExpressionBaseMixin`
+ def apply(
+ self,
+ state: ExpressionState,
+ ) -> tuple[ExpressionState, PolyMatrix]:
+ state, underlying = self.underlying.apply(state=state)
+ state, variable_indices = get_variable_indices(state, self.variables)
+
+ def acc_substitutions(acc, substitution_expr):
+ state, result = acc
+
+ # for expr in self.expressions:
+ if isinstance(substitution_expr, ExpressionBaseMixin):
+ state, substitution = substitution_expr.apply(state)
+
+ assert substitution.shape == (1, 1), f'{substitution=}'
+
+ polynomial = substitution.get_poly(0, 0)
+
+ elif isinstance(substitution_expr, int) or isinstance(substitution_expr, float):
+ polynomial = {tuple(): substitution_expr}
+
+ return state, result + (polynomial,)
+
+ *_, (state, substitutions) = tuple(itertools.accumulate(
+ self.substitutions,
+ acc_substitutions,
+ initial=(state, tuple()),
+ ))
+
+ terms = {}
+
+ for row in range(underlying.shape[0]):
+ for col in range(underlying.shape[1]):
+
+ try:
+ underlying_terms = underlying.get_poly(row, col)
+ except KeyError:
+ continue
+
+ terms_row_col = collections.defaultdict(float)
+
+ for monomial, value in underlying_terms.items():
+
+ terms_monomial = {tuple(): value}
+
+ for variable, count in monomial:
+ if variable in variable_indices:
+ index = variable_indices.index(variable)
+ substitution = substitutions[index]
+
+
+ for _ in range(count):
+
+ next = {}
+ multiply_polynomial(terms_monomial, substitution, next)
+ terms_monomial = next
+
+ else:
+ next = {}
+ multiply_polynomial(terms_monomial, {((variable, count),): 1.0}, next)
+ terms_monomial = next
+
+ for monomial, value in terms_monomial.items():
+ terms_row_col[monomial] += value
+
+ terms_row_col = {key: val for key, val in terms_row_col.items() if not math.isclose(val, 0, abs_tol=1e-12)}
+
+ if 0 < len(terms_row_col):
+ terms[row, col] = terms_row_col
+
+ poly_matrix = init_poly_matrix(
+ terms=terms,
+ shape=underlying.shape,
+ )
+
+ return state, poly_matrix
diff --git a/polymatrix/expression/mixins/subtractmonomialsexprmixin.py b/polymatrix/expression/mixins/subtractmonomialsexprmixin.py
new file mode 100644
index 0000000..45ef444
--- /dev/null
+++ b/polymatrix/expression/mixins/subtractmonomialsexprmixin.py
@@ -0,0 +1,60 @@
+
+import abc
+import dataclasses
+
+from polymatrix.expression.init.initpolymatrix import init_poly_matrix
+from polymatrix.expression.mixins.expressionbasemixin import ExpressionBaseMixin
+from polymatrix.expression.mixins.expressionstatemixin import ExpressionStateMixin
+from polymatrix.expression.mixins.polymatrixmixin import PolyMatrixMixin
+from polymatrix.expression.mixins.quadraticinexprmixin import QuadraticInExprMixin
+from polymatrix.expression.utils.getmonomialindices import get_monomial_indices
+from polymatrix.expression.utils.getvariableindices import get_variable_indices
+from polymatrix.expression.utils.sortmonomials import sort_monomials
+from polymatrix.expression.utils.subtractmonomialindices import subtract_monomial_indices
+
+
+class SubtractMonomialsExprMixin(ExpressionBaseMixin):
+ @property
+ @abc.abstractclassmethod
+ def underlying(self) -> ExpressionBaseMixin:
+ ...
+
+ @property
+ @abc.abstractmethod
+ def monomials(self) -> ExpressionBaseMixin:
+ ...
+
+ # overwrites abstract method of `ExpressionBaseMixin`
+ def apply(
+ self,
+ state: ExpressionStateMixin,
+ ) -> tuple[ExpressionStateMixin, PolyMatrixMixin]:
+
+ state, underlying = get_monomial_indices(state, self.underlying)
+ state, sub_monomials = get_monomial_indices(state, self.monomials)
+
+ def gen_remainders():
+ for m1 in underlying:
+ for m2 in sub_monomials:
+ try:
+ remainder = subtract_monomial_indices(m1, m2)
+ except KeyError:
+ continue
+
+ yield remainder
+
+ remainders = sort_monomials(set(gen_remainders()))
+
+ terms = {(row, 0): {remainder: 1.0} for row, remainder in enumerate(remainders)}
+
+ poly_matrix = init_poly_matrix(
+ terms=terms,
+ shape=(len(remainders), 1),
+ )
+
+ state = dataclasses.replace(
+ state,
+ cache=state.cache | {self: poly_matrix},
+ )
+
+ return state, poly_matrix
diff --git a/polymatrix/expression/mixins/sumexprmixin.py b/polymatrix/expression/mixins/sumexprmixin.py
index 96bf3da..b3afd51 100644
--- a/polymatrix/expression/mixins/sumexprmixin.py
+++ b/polymatrix/expression/mixins/sumexprmixin.py
@@ -35,11 +35,11 @@ class SumExprMixin(ExpressionBaseMixin):
term_monomials = terms[row, 0]
- for monomial, coeff in underlying_terms.items():
+ for monomial, value in underlying_terms.items():
if monomial in term_monomials:
- term_monomials[monomial] += coeff
+ term_monomials[monomial] += value
else:
- term_monomials[monomial] = coeff
+ term_monomials[monomial] = value
poly_matrix = init_poly_matrix(
terms=terms,
diff --git a/polymatrix/expression/mixins/symmetricexprmixin.py b/polymatrix/expression/mixins/symmetricexprmixin.py
index e914895..da07715 100644
--- a/polymatrix/expression/mixins/symmetricexprmixin.py
+++ b/polymatrix/expression/mixins/symmetricexprmixin.py
@@ -1,5 +1,6 @@
import abc
+import collections
import itertools
import dataclass_abc
from polymatrix.expression.mixins.polymatrixmixin import PolyMatrixMixin
@@ -50,19 +51,17 @@ class SymmetricExprMixin(ExpressionBaseMixin):
raise KeyError()
else:
- terms = {}
+ terms = collections.defaultdict(float)
# merge monomials
for monomials in all_monomials:
for monomial, value in monomials.items():
- if monomial in terms:
- terms[monomial] = (terms[monomial] + value) / 2
- else:
- terms[monomial] = value
- return terms
+ terms[monomial] += value / 2
- polymat = SymmetricPolyMatrix(
+ return dict(terms)
+
+ polymatrix = SymmetricPolyMatrix(
underlying=underlying,
)
- return state, polymat
+ return state, polymatrix
diff --git a/polymatrix/expression/mixins/toquadraticexprmixin.py b/polymatrix/expression/mixins/toquadraticexprmixin.py
index 2b62a89..02e366d 100644
--- a/polymatrix/expression/mixins/toquadraticexprmixin.py
+++ b/polymatrix/expression/mixins/toquadraticexprmixin.py
@@ -9,6 +9,7 @@ from polymatrix.expression.polymatrix import PolyMatrix
from polymatrix.expression.expressionstate import ExpressionState
+# to be deleted?
class ToQuadraticExprMixin(ExpressionBaseMixin):
@property
@abc.abstractmethod
diff --git a/polymatrix/expression/mixins/accumulateexprmixin.py b/polymatrix/expression/mixins/truncateexprmixin.py
index 47df335..7d0927a 100644
--- a/polymatrix/expression/mixins/accumulateexprmixin.py
+++ b/polymatrix/expression/mixins/truncateexprmixin.py
@@ -1,27 +1,29 @@
import abc
-import typing
+import collections
+from numpy import var
from polymatrix.expression.init.initpolymatrix import init_poly_matrix
from polymatrix.expression.mixins.expressionbasemixin import ExpressionBaseMixin
from polymatrix.expression.polymatrix import PolyMatrix
from polymatrix.expression.expressionstate import ExpressionState
+from polymatrix.expression.utils.getvariableindices import get_variable_indices
-# todo: is this needed?
-class AccumulateExprMixin(ExpressionBaseMixin):
+class TruncateExprMixin(ExpressionBaseMixin):
@property
@abc.abstractmethod
def underlying(self) -> ExpressionBaseMixin:
...
+ @property
@abc.abstractmethod
- def acc_func(self) -> typing.Callable:
+ def variables(self) -> ExpressionBaseMixin:
...
@property
@abc.abstractmethod
- def initial(self) -> ExpressionBaseMixin:
+ def degree(self) -> int:
...
# overwrites abstract method of `ExpressionBaseMixin`
@@ -31,27 +33,32 @@ class AccumulateExprMixin(ExpressionBaseMixin):
) -> tuple[ExpressionState, PolyMatrix]:
state, underlying = self.underlying.apply(state=state)
- acc = self.initial
+ state, variable_indices = get_variable_indices(state, self.variables)
- for row in range(underlying.shape[0]):
+ terms = {}
- col_terms = {}
-
+ for row in range(underlying.shape[0]):
for col in range(underlying.shape[1]):
+
try:
underlying_terms = underlying.get_poly(row, col)
except KeyError:
continue
- col_terms[0, col] = underlying_terms
+ terms_row_col = {}
- poly_matrix = init_poly_matrix(
- terms=col_terms,
- shape=(1, underlying.shape[1]),
- )
+ for monomial, value in underlying_terms.items():
- acc = self.acc_func(acc, poly_matrix)
+ degree = sum((count for var_idx, count in monomial if var_idx in variable_indices))
- state, poly_matrix = acc.apply(state=state)
+ if degree <= self.degree:
+ terms_row_col[monomial] = value
+
+ terms[row, col] = terms_row_col
+
+ poly_matrix = init_poly_matrix(
+ terms=dict(terms),
+ shape=underlying.shape,
+ )
return state, poly_matrix
diff --git a/polymatrix/expression/mixins/vstackexprmixin.py b/polymatrix/expression/mixins/vstackexprmixin.py
index b27828e..2929f7a 100644
--- a/polymatrix/expression/mixins/vstackexprmixin.py
+++ b/polymatrix/expression/mixins/vstackexprmixin.py
@@ -23,8 +23,8 @@ class VStackExprMixin(ExpressionBaseMixin):
all_underlying = []
for expr in self.underlying:
- state, polymat = expr.apply(state=state)
- all_underlying.append(polymat)
+ state, polymatrix = expr.apply(state=state)
+ all_underlying.append(polymatrix)
for underlying in all_underlying:
assert underlying.shape[1] == all_underlying[0].shape[1], f'{underlying.shape[1]} not equal {all_underlying[0].shape[1]}'
@@ -36,9 +36,9 @@ class VStackExprMixin(ExpressionBaseMixin):
shape: tuple[int, int]
def get_poly(self, row: int, col: int) -> dict[tuple[int, ...], float]:
- for polymat, (row_start, row_end) in zip(self.all_underlying, self.underlying_row_range):
+ for polymatrix, (row_start, row_end) in zip(self.all_underlying, self.underlying_row_range):
if row_start <= row < row_end:
- return polymat.get_poly(
+ return polymatrix.get_poly(
row=row - row_start,
col=col,
)
@@ -51,12 +51,12 @@ class VStackExprMixin(ExpressionBaseMixin):
initial=0)
))
- n_row = sum(polymat.shape[0] for polymat in all_underlying)
+ n_row = sum(polymatrix.shape[0] for polymatrix in all_underlying)
- polymat = VStackPolyMatrix(
+ polymatrix = VStackPolyMatrix(
all_underlying=all_underlying,
shape=(n_row, all_underlying[0].shape[1]),
underlying_row_range=underlying_row_range,
)
- return state, polymat
+ return state, polymatrix
diff --git a/polymatrix/expression/parametrizeexpr.py b/polymatrix/expression/parametrizeexpr.py
new file mode 100644
index 0000000..f98fdcd
--- /dev/null
+++ b/polymatrix/expression/parametrizeexpr.py
@@ -0,0 +1,4 @@
+from polymatrix.expression.mixins.parametrizeexprmixin import ParametrizeExprMixin
+
+class ParametrizeExpr(ParametrizeExprMixin):
+ pass
diff --git a/polymatrix/expression/parametrizesymbolsexpr.py b/polymatrix/expression/parametrizetermsexpr.py
index a81ec23..18b4de2 100644
--- a/polymatrix/expression/parametrizesymbolsexpr.py
+++ b/polymatrix/expression/parametrizetermsexpr.py
@@ -1,4 +1,4 @@
from polymatrix.expression.mixins.parametrizetermsexprmixin import ParametrizeTermsExprMixin
-class ParametrizeSymbolsExpr(ParametrizeTermsExprMixin):
+class ParametrizeTermsExpr(ParametrizeTermsExprMixin):
pass
diff --git a/polymatrix/expression/quadraticinexpr.py b/polymatrix/expression/quadraticinexpr.py
index 59f452b..bab76f6 100644
--- a/polymatrix/expression/quadraticinexpr.py
+++ b/polymatrix/expression/quadraticinexpr.py
@@ -1,4 +1,4 @@
-from polymatrix.expression.mixins.quadraticreprexprmixin import QuadraticReprExprMixin
+from polymatrix.expression.mixins.quadraticinexprmixin import QuadraticInExprMixin
-class QuadraticInExpr(QuadraticReprExprMixin):
+class QuadraticInExpr(QuadraticInExprMixin):
pass
diff --git a/polymatrix/expression/setelementatexpr.py b/polymatrix/expression/setelementatexpr.py
new file mode 100644
index 0000000..147d1f8
--- /dev/null
+++ b/polymatrix/expression/setelementatexpr.py
@@ -0,0 +1,4 @@
+from polymatrix.expression.mixins.setelementatexprmixin import SetElementAtExprMixin
+
+class SetElementAtExpr(SetElementAtExprMixin):
+ pass
diff --git a/polymatrix/expression/sosmonomialsexpr.py b/polymatrix/expression/sosmonomialsexpr.py
new file mode 100644
index 0000000..63dfcef
--- /dev/null
+++ b/polymatrix/expression/sosmonomialsexpr.py
@@ -0,0 +1,4 @@
+from polymatrix.expression.mixins.quadraticmonomialsexprmixin import QuadraticMonomialsExprMixin
+
+class SOSMonomialsExpr(QuadraticMonomialsExprMixin):
+ pass
diff --git a/polymatrix/expression/substituteexpr.py b/polymatrix/expression/substituteexpr.py
new file mode 100644
index 0000000..3fd4916
--- /dev/null
+++ b/polymatrix/expression/substituteexpr.py
@@ -0,0 +1,4 @@
+from polymatrix.expression.mixins.substituteexprmixin import SubstituteExprMixin
+
+class SubstituteExpr(SubstituteExprMixin):
+ pass
diff --git a/polymatrix/expression/subtractmonomialsexpr.py b/polymatrix/expression/subtractmonomialsexpr.py
new file mode 100644
index 0000000..9bc92ea
--- /dev/null
+++ b/polymatrix/expression/subtractmonomialsexpr.py
@@ -0,0 +1,4 @@
+from polymatrix.expression.mixins.subtractmonomialsexprmixin import SubtractMonomialsExprMixin
+
+class SubtractMonomialsExpr(SubtractMonomialsExprMixin):
+ pass
diff --git a/polymatrix/expression/truncateexpr.py b/polymatrix/expression/truncateexpr.py
new file mode 100644
index 0000000..b555480
--- /dev/null
+++ b/polymatrix/expression/truncateexpr.py
@@ -0,0 +1,4 @@
+from polymatrix.expression.mixins.truncateexprmixin import TruncateExprMixin
+
+class TruncateExpr(TruncateExprMixin):
+ pass
diff --git a/polymatrix/expression/utils/getderivativemonomials.py b/polymatrix/expression/utils/getderivativemonomials.py
new file mode 100644
index 0000000..9c5ac98
--- /dev/null
+++ b/polymatrix/expression/utils/getderivativemonomials.py
@@ -0,0 +1,114 @@
+import collections
+from polymatrix.expression.expressionstate import ExpressionState
+
+
+def get_derivative_monomials(
+ monomial_terms,
+ diff_wrt_variable: int,
+ state: ExpressionState,
+ considered_variables: set,
+ introduce_derivatives: bool,
+):
+
+ # if self.introduce_derivatives:
+
+ # raise Exception('not implemented')
+
+ # def gen_new_variables():
+ # for monomial in monomial_terms.keys():
+ # for var in monomial:
+ # if var not in diff_wrt_variables and var not in considered_variables:
+ # yield var
+
+ # new_variables = set(gen_new_variables())
+
+ # new_considered_variables = considered_variables | new_variables
+
+ # def acc_state_candidates(acc, new_variable):
+ # state, candidates = acc
+
+ # key = init_derivative_key(
+ # variable=new_variable,
+ # with_respect_to=diff_wrt_variable,
+ # )
+ # state = state.register(key=key, n_param=1)
+
+ # state, auxillary_derivation_terms = get_derivative_terms(
+ # monomial_terms=state.auxillary_equations[new_variable],
+ # diff_wrt_variable=diff_wrt_variable,
+ # state=state,
+ # considered_variables=new_considered_variables,
+ # )
+
+ # if 1 < len(auxillary_derivation_terms):
+ # derivation_variable = state.offset_dict[key][0]
+
+ # state = dataclasses.replace(
+ # state,
+ # auxillary_equations=state.auxillary_equations | {derivation_variable: auxillary_derivation_terms},
+ # )
+
+ # return state, candidates + (new_variable,)
+
+ # else:
+ # return state, candidates
+
+ # *_, (state, confirmed_variables) = itertools.accumulate(
+ # new_variables,
+ # acc_state_candidates,
+ # initial=(state, tuple()),
+ # )
+
+ # else:
+ # confirmed_variables = tuple()
+
+ derivation_terms = collections.defaultdict(float)
+
+ for monomial, value in monomial_terms.items():
+
+ # # count powers for each variable
+ # monomial_cnt = dict(collections.Counter(monomial))
+ monomial_cnt = dict(monomial)
+
+ def differentiate_monomial(dependent_variable, derivation_variable=None):
+ def gen_diff_monomial():
+ for current_variable, current_count in monomial:
+
+ if current_variable is dependent_variable:
+ sel_counter = current_count - 1
+
+ else:
+ sel_counter = current_count
+
+ # for _ in range(sel_counter):
+ # yield current_variable
+ if 0 < sel_counter:
+ yield current_variable, sel_counter
+
+ if derivation_variable is not None:
+ yield derivation_variable
+
+ diff_monomial = tuple(sorted(gen_diff_monomial()))
+
+ return diff_monomial, value * monomial_cnt[dependent_variable]
+
+ if diff_wrt_variable in monomial_cnt:
+ diff_monomial, value = differentiate_monomial(diff_wrt_variable)
+ derivation_terms[diff_monomial] += value
+
+ # # only used if introduce_derivatives == True
+ # for candidate_variable in monomial_cnt.keys():
+ # if candidate_variable in considered_variables or candidate_variable in confirmed_variables:
+ # key = init_derivative_key(
+ # variable=candidate_variable,
+ # with_respect_to=diff_wrt_variable,
+ # )
+ # derivation_variable = state.offset_dict[key][0]
+
+ # diff_monomial, value = differentiate_monomial(
+ # dependent_variable=candidate_variable,
+ # derivation_variable=derivation_variable,
+ # )
+ # derivation_terms[diff_monomial] += value
+
+ return state, dict(derivation_terms) \ No newline at end of file
diff --git a/polymatrix/expression/utils/getmonomialindices.py b/polymatrix/expression/utils/getmonomialindices.py
new file mode 100644
index 0000000..6d6c599
--- /dev/null
+++ b/polymatrix/expression/utils/getmonomialindices.py
@@ -0,0 +1,19 @@
+from polymatrix.expression.mixins.expressionbasemixin import ExpressionBaseMixin
+
+
+def get_monomial_indices(state, monomials) -> tuple:
+
+ state, monomials_obj = monomials.apply(state)
+
+ assert monomials_obj.shape[1] == 1, f'{monomials_obj.shape=}'
+
+ def gen_indices():
+ for row in range(monomials_obj.shape[0]):
+ row_terms = monomials_obj.get_poly(row, 0)
+
+ assert len(row_terms) == 1, f'{row_terms} contains more than one term'
+
+ yield from row_terms.keys()
+
+ return state, tuple(gen_indices())
+
diff --git a/polymatrix/expression/utils/gettupleremainder.py b/polymatrix/expression/utils/gettupleremainder.py
new file mode 100644
index 0000000..2a22ead
--- /dev/null
+++ b/polymatrix/expression/utils/gettupleremainder.py
@@ -0,0 +1,9 @@
+def get_tuple_remainder(l1, l2):
+ # create a copy of l1
+ l1_copy = list(l1)
+
+ # remove from the copy all elements of l2
+ for e in l2:
+ l1_copy.remove(e)
+
+ return tuple(l1_copy) \ No newline at end of file
diff --git a/polymatrix/expression/utils/getvariableindices.py b/polymatrix/expression/utils/getvariableindices.py
index 71a91f6..9aa3619 100644
--- a/polymatrix/expression/utils/getvariableindices.py
+++ b/polymatrix/expression/utils/getvariableindices.py
@@ -17,14 +17,16 @@ def get_variable_indices(state, variables):
assert len(row_terms) == 1, f'{row_terms} contains more than one term'
for monomial in row_terms.keys():
- assert len(monomial) == 1, f'{monomial} contains more than one variable'
- yield monomial[0]
+ assert len(monomial) == 1, f'{monomial=} contains more than one variable'
+ assert monomial[0][1] == 1, f'{monomial[0]=}'
+
+ yield monomial[0][0]
return state, tuple(gen_indices())
else:
- raise Exception('not supported anymore')
+ # raise Exception('not supported anymore')
if not isinstance(variables, tuple):
variables = (variables,)
diff --git a/polymatrix/expression/utils/mergemonomialindices.py b/polymatrix/expression/utils/mergemonomialindices.py
new file mode 100644
index 0000000..7f8ba15
--- /dev/null
+++ b/polymatrix/expression/utils/mergemonomialindices.py
@@ -0,0 +1,27 @@
+from polymatrix.expression.utils.sortmonomialindices import sort_monomial_indices
+
+
+def merge_monomial_indices(monomials):
+ if len(monomials) == 0:
+ return tuple()
+
+ elif len(monomials) == 1:
+ return monomials[0]
+
+ else:
+
+ m1_dict = dict(monomials[0])
+
+ for other in monomials[1:]:
+ for index, count in other:
+ if index in m1_dict:
+ m1_dict[index] += count
+ else:
+ m1_dict[index] = count
+
+ # return tuple(sorted(
+ # m1_dict.items(),
+ # key=lambda m: m[0],
+ # ))
+
+ return sort_monomial_indices(m1_dict.items())
diff --git a/polymatrix/expression/utils/multiplypolynomial.py b/polymatrix/expression/utils/multiplypolynomial.py
new file mode 100644
index 0000000..0b6f5b6
--- /dev/null
+++ b/polymatrix/expression/utils/multiplypolynomial.py
@@ -0,0 +1,24 @@
+import itertools
+import math
+
+from polymatrix.expression.utils.mergemonomialindices import merge_monomial_indices
+
+
+def multiply_polynomial(left, right, terms):
+ for (left_monomial, left_value), (right_monomial, right_value) \
+ in itertools.product(left.items(), right.items()):
+
+ value = left_value * right_value
+
+ if math.isclose(value, 0, abs_tol=1e-12):
+ continue
+
+ monomial = merge_monomial_indices((left_monomial, right_monomial))
+
+ if monomial not in terms:
+ terms[monomial] = 0
+
+ terms[monomial] += value
+
+ if math.isclose(terms[monomial], 0, abs_tol=1e-12):
+ del terms[monomial]
diff --git a/polymatrix/expression/utils/sortmonomialindices.py b/polymatrix/expression/utils/sortmonomialindices.py
new file mode 100644
index 0000000..3d4734e
--- /dev/null
+++ b/polymatrix/expression/utils/sortmonomialindices.py
@@ -0,0 +1,5 @@
+def sort_monomial_indices(monomial):
+ return tuple(sorted(
+ monomial,
+ key=lambda m: m[0],
+ )) \ No newline at end of file
diff --git a/polymatrix/expression/utils/sortmonomials.py b/polymatrix/expression/utils/sortmonomials.py
new file mode 100644
index 0000000..46e7118
--- /dev/null
+++ b/polymatrix/expression/utils/sortmonomials.py
@@ -0,0 +1,5 @@
+def sort_monomials(monomials):
+ return tuple(sorted(
+ monomials,
+ key=lambda m: (sum(count for _, count in m), len(m), m),
+ )) \ No newline at end of file
diff --git a/polymatrix/expression/utils/splitmonomialindices.py b/polymatrix/expression/utils/splitmonomialindices.py
new file mode 100644
index 0000000..82e5454
--- /dev/null
+++ b/polymatrix/expression/utils/splitmonomialindices.py
@@ -0,0 +1,27 @@
+def split_monomial_indices(monomial):
+ left = []
+ right = []
+
+ is_left = True
+
+ for idx, count in monomial:
+ count_left = count // 2
+
+ is_uneven = count % 2
+ if is_uneven:
+ if is_left:
+ count_left = count_left + 1
+
+ is_left = not is_left
+
+ count_right = count - count_left
+
+ if 0 < count_left:
+ left.append((idx, count_left))
+
+ if 0 < count_right:
+ right.append((idx, count - count_left))
+
+ # print((monomial, tuple(left), tuple(right)))
+
+ return tuple(left), tuple(right) \ No newline at end of file
diff --git a/polymatrix/expression/utils/subtractmonomialindices.py b/polymatrix/expression/utils/subtractmonomialindices.py
new file mode 100644
index 0000000..a8aef1c
--- /dev/null
+++ b/polymatrix/expression/utils/subtractmonomialindices.py
@@ -0,0 +1,18 @@
+from polymatrix.expression.utils.sortmonomialindices import sort_monomial_indices
+
+
+def subtract_monomial_indices(m1, m2):
+ m1_dict = dict(m1)
+
+ for index, count in m2:
+ m1_dict[index] -= count
+
+ if m1_dict[index] == 0:
+ del m1_dict[index]
+
+ # return tuple(sorted(
+ # m1_dict.items(),
+ # key=lambda m: m[0],
+ # ))
+
+ return sort_monomial_indices(m1_dict.items())