summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichael Schneeberger <michael.schneeberger@fhnw.ch>2022-02-21 16:21:50 +0100
committerMichael Schneeberger <michael.schneeberger@fhnw.ch>2022-02-21 16:21:50 +0100
commit4abcab8da3e553390cd58b42cae52f210d011ada (patch)
treeaea7a9e4f54d6f7f9588bea5724700048240e0dd
parentadd skew-symmetry and gradient function (diff)
downloadpolymatrix-4abcab8da3e553390cd58b42cae52f210d011ada.tar.gz
polymatrix-4abcab8da3e553390cd58b42cae52f210d011ada.zip
bugfixes
-rw-r--r--polymatrix/polystruct.py492
-rw-r--r--polymatrix/sympyutils.py55
2 files changed, 393 insertions, 154 deletions
diff --git a/polymatrix/polystruct.py b/polymatrix/polystruct.py
index 64c0f42..202f6cc 100644
--- a/polymatrix/polystruct.py
+++ b/polymatrix/polystruct.py
@@ -1,11 +1,13 @@
import abc
import collections
+from pickletools import int4
import typing
import numpy as np
import dataclasses
import dataclass_abc
-import scipy.special
+from scipy.special import binom
import itertools
+import functools
from polymatrix.utils import variable_to_index
@@ -30,76 +32,257 @@ class PolyMatrixMixin(abc.ABC):
@property
@abc.abstractmethod
- def subs_func(self) -> typing.Callable[[DegreeType, int, int], tuple[float]]:
+ def re_index(self) -> typing.Callable[[int, int, int, tuple[int, ...]], tuple[int, int, int, tuple[int, ...], float]]:
...
@property
@abc.abstractmethod
- def re_index(self) -> dict[DegreeType, dict[int, int, tuple[int, int, float]]]:
+ def is_constant(self) -> int:
...
@property
@abc.abstractmethod
- def re_index_func(self) -> typing.Callable[[int, int], tuple[int, int, float]]:
+ def is_vector(self) -> bool:
...
+
+class EqualityConstraintMixin(abc.ABC):
@property
@abc.abstractmethod
- def re_index_func_2(self) -> typing.Callable[[int, int], tuple[int, int, float]]:
+ def terms(self) -> dict[int, dict[tuple[int, tuple, int], float]]:
...
+ # @property
+ # @abc.abstractmethod
+ # def offset_dict(self) -> dict[tuple[PolyMatrixMixin, int], int]:
+ # ...
+
+ # @property
+ # @abc.abstractmethod
+ # def param_dict(self) -> dict[tuple[PolyMatrixMixin, int], int]:
+ # ...
+
@property
@abc.abstractmethod
- def is_vector(self) -> bool:
+ def n_param(self) -> int:
...
+ @functools.cached_property
+ def eq_to_row_index(self):
+ rows_to_eq = list(set((eq_idx, perm) for eq_tuple_degree in self.terms.values() for (eq_idx, perm, var) in eq_tuple_degree.keys()))
+ eq_to_rows = {eq: idx for idx, eq in enumerate(rows_to_eq)}
+ return eq_to_rows
-class EquationMixin(abc.ABC):
+ @property
+ def n_eq(self):
+ return len(self.eq_to_row_index)
+
+ # @property
+ # @abc.abstractmethod
+ # def n_var(self) -> int:
+ # ...
+
+ def get_constraint_func(self):
+ def func(x):
+ mat = np.zeros((self.n_eq,))
+
+ for degree, degree_tuples in self.terms.items():
+ if 0 == degree:
+ for (idx_eq, perm, variables), value in degree_tuples.items():
+ row_idx = self.eq_to_row_index[(idx_eq, perm)]
+ mat[row_idx] += value
+
+ elif 0 < degree:
+ def gen_vector():
+ for indices in itertools.combinations_with_replacement(range(self.n_param), degree):
+ yield np.prod(list(x[idx] for idx in indices))
+ vector = list(gen_vector())
+
+ for (idx_eq, perm, variables), value in degree_tuples.items():
+ row_idx = self.eq_to_row_index[(idx_eq, perm)]
+ vector_val = vector[variable_to_index(self.n_param, variables)]
+ mat[row_idx] += value * vector_val
+
+ return mat
+ return func
+
+ def get_constraint_jacobian(self):
+ def func(x):
+ jac_mat = np.zeros((self.n_eq, self.n_param))
+
+ for degree, degree_tuples in self.terms.items():
+ if 1 == degree:
+ for (idx_eq, perm, variables), value in degree_tuples.items():
+ row_idx = self.eq_to_row_index[(idx_eq, perm)]
+ jac_mat[row_idx, variables[0]] += value
+
+ # for var in variables:
+ # col_idx = variable_to_index(self.n_param, (var,))
+ # jac_mat[row_idx, col_idx] += value
+
+ elif 1 < degree:
+ def gen_vector():
+ for indices in itertools.combinations_with_replacement(range(self.n_param), degree-1):
+ yield np.prod(list(x[idx] for idx in indices))
+ vector = list(gen_vector())
+
+ for (idx_eq, perm, variables), value in degree_tuples.items():
+ row_idx = self.eq_to_row_index[(idx_eq, perm)]
+
+ for var_idx, var in enumerate(variables):
+ other_variables = variables[:var_idx] + variables[var_idx+1:]
+ vector_val = vector[variable_to_index(self.n_param, other_variables)]
+
+ # col_idx = variable_to_index(self.n_param, (var,))
+ jac_mat[row_idx, var] += value*vector_val
+
+ return jac_mat
+ return func
+
+ def get_constraint_hessian(self):
+ def func(x, v):
+ hess_mat = np.zeros((self.n_param, self.n_param))
+
+ for degree, degree_tuples in self.terms.items():
+ if 2 == degree:
+ for (idx_eq, perm, variables), value in degree_tuples.items():
+ eq_idx = self.eq_to_row_index[(idx_eq, perm)]
+
+ for var_idx_x, var_x in enumerate(variables):
+ other_variables = variables[:var_idx_x] + variables[var_idx_x+1:]
+
+ for var_idx_y, var_y in enumerate(other_variables):
+ hess_mat[var_x, var_y] = v[eq_idx]*value
+
+ elif 2 < degree:
+ def gen_vector():
+ for indices in itertools.combinations_with_replacement(range(self.n_param), degree-2):
+ yield np.prod(list(x[idx] for idx in indices))
+ vector = list(gen_vector())
+
+ for (idx_eq, perm, variables), value in degree_tuples.items():
+ eq_idx = self.eq_to_row_index[(idx_eq, perm)]
+
+ for var_idx_x, var_x in enumerate(variables):
+ other_variables = variables[:var_idx_x] + variables[var_idx_x+1:]
+
+ for var_idx_y, var_y in enumerate(other_variables):
+ other_variables_2 = variables[:var_idx_y] + variables[var_idx_y+1:]
+ vector_val = vector[variable_to_index(self.n_param, other_variables_2)]
+ hess_mat[var_x, var_y] = v[eq_idx]*value*vector_val
+
+ return hess_mat
+ return func
+
+
+class PolyEquationMixin(abc.ABC):
@property
@abc.abstractmethod
def terms(self) -> list[tuple[PolyMatrixMixin, PolyMatrixMixin]]:
+ """
+ the terms the polynomial matrix equation consists of
+ """
+
...
@property
@abc.abstractmethod
def n_var(self) -> int:
- ...
+ """
+ number of variables defining the polynomials
- def create(
- self,
- subs: dict[PolyMatrixMixin, dict[DegreeType, dict[int, int, float]]] = None,
- ):
- if subs is None:
- added_subs = {}
- else:
- added_subs = subs
+ for example n_var=3: x1, x2 and x3
+ """
- binom = scipy.special.binom
+ ...
+
+ @functools.cached_property
+ def _param_list(self) -> list[tuple[PolyMatrixMixin, int], int]:
+ """
+ used to determine the offset of the coefficients of each polynomial matrix
+ """
# create parameter offset
all_structs = set(indexed_poly_mat for term in self.terms for indexed_poly_mat in term)
def gen_n_param_per_struct():
- acc = 0
+ # acc = 0
for struct in all_structs:
- for degree in struct.degrees:
+ if struct.is_constant:
+ continue
- yield (struct, degree), acc
+ for degree in struct.degrees:
if struct.is_vector:
number_of_terms = int(self.n_var * binom(self.n_var+degree-1, degree))
else:
number_of_terms = int(self.n_var**2 * binom(self.n_var+degree-1, degree))
- acc += number_of_terms
-
- yield None, acc #, None
+ yield (struct, degree), number_of_terms
param_list = list(gen_n_param_per_struct())
- n_param = param_list[-1][1]
- offset_dict = dict(((e[0], e[1]) for e in param_list[:-1]))
+
+ return param_list
+
+ @functools.cached_property
+ def offset_dict(self) -> dict[tuple[PolyMatrixMixin, int], int]:
+ """
+ determine the offset of the coefficients of each polynomial matrix ordered by degree
+
+ The polynomial equation
+
+ A * B = 0
+
+ is represented by a vector of coefficients `coeff`. Each coefficients is associated to polynomial matrix.
+
+ For example
+
+ offset_dict[(A,0)] = 12
+
+ means that the first coefficient a011 (meaning 011=degree+row+col) associated to A and degree 0 is located at index 12 of `coeff`.
+ """
+
+ param_key_value = list(zip(*self._param_list))
+
+ if 0 < len(param_key_value):
+ param_key, param_value = param_key_value
+ cum_sum = list(itertools.accumulate(param_value))
+ offset_dict = dict(zip(param_key, [0] + cum_sum[:-1]))
+ else:
+ offset_dict = {}
+
+ return offset_dict
+
+ @functools.cached_property
+ def n_param(self) -> dict[tuple[PolyMatrixMixin, int], int]:
+ """
+ number of coefficients of polynomial matrix equation, e.g. `len(coeff)`
+ """
+
+ if 0 < len(self._param_list):
+ *_, n_param = itertools.accumulate(e[1] for e in self._param_list)
+ else:
+ n_param = 0
+
+ return n_param
+
+ def create(
+ self,
+ subs: dict[PolyMatrixMixin, dict[DegreeType, dict[int, int, float]]] = None,
+ ) -> EqualityConstraintMixin:
+ n_var_2 = self.n_var**2
+
+ if subs is None:
+ added_subs = {}
+ else:
+ added_subs = subs
+
+ # binom = scipy.special.binom
+
+ # create parameter offset
+ all_structs = set(indexed_poly_mat for term in self.terms for indexed_poly_mat in term)
def gen_substitutions():
for struct in all_structs:
@@ -123,47 +306,12 @@ class EquationMixin(abc.ABC):
else:
all_subs = None
-
- def subs_func(degree, idx_eq, idx_col, all_subs=all_subs, struct=struct):
- if struct.re_index_func is not None:
- re_index = struct.re_index_func(degree, idx_eq, idx_col)
- else:
- re_index = None
-
- if re_index is None:
- n_coord = (idx_eq, idx_col)
- factor = 1
- else:
- n_coord = (re_index[0], re_index[1])
- factor = re_index[2]
-
- if all_subs is not None and degree in all_subs:
- all_degree_subs = all_subs[degree]
-
- if n_coord in all_degree_subs:
- subs_val = all_degree_subs[n_coord] * factor
- else:
- subs_val = None
-
- elif struct.subs_func is not None:
- sub_value = struct.subs_func(degree, n_coord[0], n_coord[1])
-
- if sub_value is not None:
- subs_val = sub_value * factor
- else:
- subs_val = None
-
- else:
- subs_val = None
-
- return n_coord, factor, subs_val
-
- yield struct, subs_func
+ yield struct, all_subs
subs_dict = dict(gen_substitutions())
- terms = collections.defaultdict(lambda: collections.defaultdict(lambda: collections.defaultdict(int)))
+ terms = collections.defaultdict(lambda: collections.defaultdict(float))
for left, right in self.terms:
@@ -172,13 +320,21 @@ class EquationMixin(abc.ABC):
for d1 in left.degrees:
- n_param_1 = binom(self.n_var+d1-1, d1)
- offset_1 = offset_dict[(left, d1)]
+ if subs_1 is not None and d1 in subs_1:
+ subs_1_d = subs_1[d1]
+ else:
+ subs_1_d = None
+
+ offset_1 = self.offset_dict.get((left, d1), 0)
for d2 in right.degrees:
- n_param_2 = binom(self.n_var+d2-1, d2)
- offset_2 = offset_dict[(right, d2)]
+ if subs_2 is not None and d2 in subs_2:
+ subs_2_d = subs_2[d2]
+ else:
+ subs_2_d = None
+
+ offset_2 = self.offset_dict.get((right, d2), 0)
total_degree = d1 + d2
@@ -199,74 +355,150 @@ class EquationMixin(abc.ABC):
# (1,0) -> x2*x1 instead of (0,1)->x1*x2
if non_increasing(grp1) and non_increasing(grp2):
- left_idx = variable_to_index(self.n_var, grp1)
- d_right_idx = variable_to_index(self.n_var, grp2)
+ left_col_default = variable_to_index(self.n_var, grp1)
+ right_col_default = variable_to_index(self.n_var, grp2)
# for each column of the poly matrix, and row of the poly vector
- for idx_col in range(self.n_var):
+ for left_poly_col in range(self.n_var):
- if right.re_index_func_2 is None:
- re_index_2 = None
+ right_poly_row = left_poly_col
+
+ if right.re_index is not None:
+ re_index_2 = right.re_index(d2, right_poly_row, 0, grp2)
else:
- re_index_2 = right.re_index_func_2(d2, idx_col, grp2)
+ re_index_2 = None
if re_index_2 is None:
- v_idx_row = idx_col
- factor_22 = 1
- right_idx = d_right_idx
+ n_right_poly_row, factor_2, right_col = right_poly_row, 1, right_col_default
else:
- v_idx_row, n_grp2, factor_22 = re_index_2
- right_idx = variable_to_index(self.n_var, n_grp2)
-
- n_coord_2, factor_21, subs_val_2 = subs_2(d2, v_idx_row, 0)
-
- factor_2 = factor_21 * factor_22
+ n_right_poly_row, _, n_grp2, factor_2 = re_index_2
+ right_col = variable_to_index(self.n_var, n_grp2)
+
+ key_2 = (n_right_poly_row, 0, right_col)
+ if subs_2_d is not None:
+ try:
+ subs_val_2 = subs_2_d[key_2]
+ except KeyError:
+ subs_val_2 = None
+ else:
+ subs_val_2 = None
if factor_2 == 0 or subs_val_2 == 0:
continue
- # for each polynomial equation
- for idx_eq in range(self.n_var):
+ right_row = n_right_poly_row
+ right_param_idx = int(offset_2 + right_row + right_col * self.n_var)
- n_coord_1, factor_1, subs_val_1 = subs_1(d1, idx_eq, idx_col)
+ # for each polynomial equation
+ for left_poly_row in range(self.n_var):
+
+ if left.re_index is not None:
+ re_index_1 = left.re_index(d1, left_poly_row, left_poly_col, grp1)
+ else:
+ re_index_1 = None
+
+ if re_index_1 is None:
+ n_left_poly_row, n_left_poly_col, factor_1 = left_poly_row, left_poly_col, 1
+ left_col = left_col_default
+ else:
+ n_left_poly_row, n_left_poly_col, n_grp1, factor_1 = re_index_1
+ left_col = variable_to_index(self.n_var, n_grp1)
+
+ key_1 = (n_left_poly_row, n_left_poly_col, left_col)
+ if subs_1_d is not None:
+ try:
+ subs_val_1 = subs_1_d[key_1]
+ except KeyError:
+ subs_val_1 = None
+ else:
+ subs_val_1 = None
if factor_1 == 0 or subs_val_1 == 0:
continue
-
- left_param_idx = int(offset_1 + left_idx + (self.n_var * n_coord_1[0] + n_coord_1[1]) * n_param_1)
- right_param_idx = int(offset_2 + right_idx + n_coord_2[0] * n_param_2)
+
+ left_row = n_left_poly_row + n_left_poly_col * self.n_var
+ left_param_idx = int(offset_1 + left_row + left_col * n_var_2)
+
+ # print(left_poly_row)
+ # print(f'{left_row=}')
+ # print(f'{left_param_idx=}')
+
+ total_factor = factor_1 * factor_2
match (subs_val_1, subs_val_2):
case (None, None):
- col_idx = variable_to_index(n_param, (left_param_idx, right_param_idx))
- # col_idx = (left_param_idx, right_param_idx)
+ col_idx = (left_param_idx, right_param_idx)
degree = 2
- value = factor_1*factor_2
+ value = total_factor
- terms[1][(idx_eq, perm)][left_param_idx] += 2*value
- terms[1][(idx_eq, perm)][right_param_idx] += 2*value
- terms[0][(idx_eq, perm)][0] += value
+ # terms[1][(idx_eq, perm)][left_param_idx] += 2*value
+ # terms[1][(idx_eq, perm)][right_param_idx] += 2*value
+ # terms[0][(idx_eq, perm)][0] += value
case (subs_val, None):
- col_idx = right_param_idx
- # col_idx = (right_param_idx,)
+ col_idx = (right_param_idx,)
degree = 1
- value = subs_val_1*factor_2
+ value = subs_val_1*total_factor
- terms[degree][(idx_eq, perm)][0] += value
+ # terms[degree][(idx_eq, perm)][0] += value
case (None, subs_val):
- col_idx = left_param_idx
- # col_idx = (left_param_idx,)
+ col_idx = (left_param_idx,)
degree = 1
- value = subs_val_2*factor_1
+ value = subs_val_2*total_factor
+
+ # terms[degree][(idx_eq, perm)][0] += value
case _:
- degree, col_idx, value = 0, tuple(), subs_val_1*subs_val_2*factor_1*factor_2
+ degree, col_idx, value = 0, tuple(), subs_val_1*subs_val_2*total_factor
+
+ terms[degree][left_poly_row, perm, col_idx] += value
+
+ return EqualityConstraintImpl(
+ terms=terms,
+ n_param=self.n_param,
+ )
- terms[degree][(idx_eq, perm)][col_idx] += value
+ def matrix_to_poly(self, struct, x, param, tol=None):
+ assert len(x) == self.n_var, f'variable {x} needs to be of length {self.n_var}'
- return terms, offset_dict, n_param
+ n_var_2 = self.n_var**2
+
+ if struct.is_vector:
+ n_col = 1
+
+ else:
+ n_col = self.n_var
+
+ sym_expr = [[0 for _ in range(n_col)] for _ in range(self.n_var)]
+
+ for degree in struct.degrees:
+ offset = self.offset_dict[(struct, degree)]
+ # number_of_terms = int(binom(self.n_var+degree-1, degree))
+
+ def write_to_expr(row, col, val, term=1):
+ if tol is None or val <= -tol or tol <= val:
+ sym_expr[row][col] += val*term
+
+ if 0 == degree:
+ for row in range(self.n_var):
+ for col in range(n_col):
+ write_to_expr(row, col, param[offset + row + col * self.n_var])
+
+ else:
+ def gen_vector():
+ for comb in itertools.combinations_with_replacement(range(self.n_var), degree):
+ *_, last = itertools.accumulate(comb, lambda acc, idx: acc*x[idx], initial=1)
+ yield last
+ vector = list(gen_vector())
+
+ for row in range(self.n_var):
+ for col in range(n_col):
+ for idx, term in enumerate(vector):
+ # print(f'{offset + (row + col * self.n_var) * number_of_terms + idx=}, {param[offset + (row + col * self.n_var) * number_of_terms + idx]=}')
+ write_to_expr(row, col, param[offset + row + col * self.n_var + idx * n_var_2], term)
+
+ return sym_expr
########################################
# Classes
@@ -276,9 +508,14 @@ class PolyMatrix(PolyMatrixMixin):
pass
-class Equation(EquationMixin):
+class EqualityConstraint(EqualityConstraintMixin):
+ pass
+
+
+class PolyEquation(PolyEquationMixin):
pass
+
########################################
# Implementations
########################################
@@ -287,15 +524,22 @@ class Equation(EquationMixin):
class PolyMatrixImpl(PolyMatrix):
degrees: list[int]
subs: dict[int, dict[tuple[int, int], float]]
- subs_func: typing.Callable[[int, int, int], tuple[float]]
- re_index: dict[int, dict[tuple[int, int], tuple[int, int, float]]]
- re_index_func: typing.Callable[[int, int], tuple[int, int, float]]
- re_index_func_2: typing.Callable[[int, int, tuple[int, ...]], tuple[int, tuple[int, ...], float]]
+ re_index: typing.Callable[[int, int, int, tuple[int, ...]], tuple[int, int, int, tuple[int, ...], float]]
+ is_constant: bool
is_vector: bool
@dataclass_abc.dataclass_abc(frozen=True)
-class EquationImpl(Equation):
+class EqualityConstraintImpl(EqualityConstraintMixin):
+ terms: dict[int, dict[tuple[int, tuple, int], float]]
+ # offset_dict: dict[tuple[PolyMatrixMixin, int], int]
+ # param_dict: dict[tuple[PolyMatrixMixin, int], int]
+ n_param: int
+ # n_var: int
+
+
+@dataclass_abc.dataclass_abc(frozen=True)
+class EquationImpl(PolyEquation):
terms: list[tuple[PolyMatrix, PolyMatrix]]
n_var: int
@@ -306,40 +550,34 @@ class EquationImpl(Equation):
def init_poly_vector(
degrees: list[int] = None,
subs: dict[int, dict[tuple[int, int], float]] = None,
- subs_func: typing.Callable[[int, int, int], tuple[float]] = None,
- re_index: dict[int, dict[tuple[int, int], tuple[int, int, float]]] = None,
- re_index_func: typing.Callable[[int, int], tuple[int, int, float]] = None,
- re_index_func_2: typing.Callable[[int, int], tuple[int, int, float]] = None,
+ re_index: typing.Callable[[int, int, int, tuple[int, ...]], tuple[int, int, int, tuple[int, ...], float]] = None,
+ is_constant: bool = None,
):
return init_poly_matrix(
degrees=degrees,
subs=subs,
- subs_func=subs_func,
re_index=re_index,
- re_index_func=re_index_func,
is_vector=True,
- re_index_func_2=re_index_func_2,
+ is_constant=is_constant,
)
def init_poly_matrix(
degrees: list[int] = None,
subs: dict[int, dict[tuple[int, int], float]] = None,
- subs_func: typing.Callable[[int, int, int], tuple[float]] = None,
- re_index: dict[int, dict[tuple[int, int], tuple[int, int, float]]] = None,
- re_index_func: typing.Callable[[int, int], tuple[int, int, float]] = None,
- re_index_func_2: typing.Callable[[int, int], tuple[int, int, float]] = None,
+ re_index: typing.Callable[[int, int, int, tuple[int, ...]], tuple[int, int, int, tuple[int, ...], float]] = None,
is_vector: bool = None,
+ is_constant: bool = None,
):
if degrees is None:
assert isinstance(subs, dict)
degrees = list(subs.keys())
- if subs is None and subs_func is None:
- subs = {}
-
- if re_index is None and re_index_func is None:
- re_index = {}
+ if is_constant is None:
+ if subs is None:
+ is_constant = False
+ else:
+ is_constant = True
if is_vector is None:
is_vector = False
@@ -347,10 +585,8 @@ def init_poly_matrix(
return PolyMatrixImpl(
degrees=degrees,
subs=subs,
- subs_func=subs_func,
re_index = re_index,
- re_index_func = re_index_func,
- re_index_func_2 = re_index_func_2,
+ is_constant = is_constant,
is_vector = is_vector,
)
diff --git a/polymatrix/sympyutils.py b/polymatrix/sympyutils.py
index e2c37d8..6fe7938 100644
--- a/polymatrix/sympyutils.py
+++ b/polymatrix/sympyutils.py
@@ -1,11 +1,13 @@
+import collections
import itertools
import numpy as np
import scipy.sparse
+import sympy
from polymatrix.utils import variable_powers_to_index
-def poly_to_data_coord(poly_list, power = None):
+def poly_to_data_coord(poly_list, x, degree = None):
"""
poly_list = [
poly(x1*x3**2, x)
@@ -13,70 +15,71 @@ def poly_to_data_coord(poly_list, power = None):
power: up to which power
"""
- if power is None:
- power = max(degree for poly in poly_list for degree in poly.degree_list())
+ sympy_poly_list = tuple(tuple(sympy.poly(p, x) for p in inner_poly_list) for inner_poly_list in poly_list)
- def all_equal(iterable):
- g = itertools.groupby(iterable)
- return next(g, True) and not next(g, False)
+ if degree is None:
+ degree = max(degree for inner_poly_list in sympy_poly_list for poly in inner_poly_list for degree in poly.degree_list())
- assert all_equal((p.gens for p in poly_list)), 'all polynomials need to have identical generators'
+ # def all_equal(iterable):
+ # g = itertools.groupby(iterable)
+ # return next(g, True) and not next(g, False)
+
+ # assert all_equal((p.gens for p in poly_list)), 'all polynomials need to have identical generators'
def gen_power_mat():
# for all powers generate a matrix
- for current_power in range(power + 1):
+ for current_degree in range(degree + 1):
def gen_value_index():
# each polynomial defines a row in the matrix
- for row, p in enumerate(poly_list):
+ for poly_row, inner_poly_list in enumerate(sympy_poly_list):
+
+ for poly_col, p in enumerate(inner_poly_list):
- # a5 x1 x3**2 -> c=a5, m=(1, 0, 2)
- for c, m in zip(p.coeffs(), p.monoms()):
+ # a5 x1 x3**2 -> c=a5, m=(1, 0, 2)
+ for c, m in zip(p.coeffs(), p.monoms()):
- if sum(m) == current_power:
+ if sum(m) == current_degree:
- index = variable_powers_to_index(m)
- yield (row, index), c
+ index = variable_powers_to_index(m)
+ yield (poly_row, poly_col, index), c
- # yield list(zip(*gen_value_index()))
- data = dict(gen_value_index())
+ data = dict(gen_value_index()) | collections.defaultdict(float)
if len(data) > 0:
- yield current_power, data
+ yield current_degree, data
return dict(gen_power_mat())
-def poly_to_matrix(poly_list, power = None):
+def poly_to_matrix(poly_list, x, power = None):
"""
"""
- data_coord_dict = poly_to_data_coord(poly_list, power)
+ data_coord_dict = poly_to_data_coord(poly_list, x, power)
- n_free_symbols = len(poly_list[0].gens)
+ # n_free_symbols = len(poly_list[0][0].gens)
+ n_free_symbols = len(x)
def gen_power_mat():
# for all powers generate a matrix
- for current_power, data_coord in data_coord_dict:
+ for current_degree, data_coord in data_coord_dict:
# empty matrix
- shape = (len(poly_list), n_free_symbols**current_power)
+ shape = (len(poly_list), n_free_symbols**current_degree)
# m = np.zeros((len(poly_list), n_free_symbols**current_power))
# fill matrix
if len(data_coord) == 0:
- yield np.zeros((len(poly_list), n_free_symbols**current_power))
+ yield np.zeros((len(poly_list), n_free_symbols**current_degree))
else:
rows, cols, data = list(zip(*data_coord))
yield scipy.sparse.coo_matrix((data, (rows, cols)), dtype=np.double, shape=shape)
-
- # m[rows, cols] = data
- # yield m
return list(gen_power_mat())