123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472 |
- """
- This module contains functions for two multivariate resultants. These
- are:
- - Dixon's resultant.
- - Macaulay's resultant.
- Multivariate resultants are used to identify whether a multivariate
- system has common roots. That is when the resultant is equal to zero.
- """
- from sympy.core.mul import (Mul, prod)
- from sympy.matrices.dense import (Matrix, diag)
- from sympy.polys.polytools import (Poly, degree_list, rem)
- from sympy.simplify.simplify import simplify
- from sympy.tensor.indexed import IndexedBase
- from sympy.polys.monomials import itermonomials, monomial_deg
- from sympy.polys.orderings import monomial_key
- from sympy.polys.polytools import poly_from_expr, total_degree
- from sympy.functions.combinatorial.factorials import binomial
- from itertools import combinations_with_replacement
- from sympy.utilities.exceptions import sympy_deprecation_warning
- class DixonResultant():
- """
- A class for retrieving the Dixon's resultant of a multivariate
- system.
- Examples
- ========
- >>> from sympy import symbols
- >>> from sympy.polys.multivariate_resultants import DixonResultant
- >>> x, y = symbols('x, y')
- >>> p = x + y
- >>> q = x ** 2 + y ** 3
- >>> h = x ** 2 + y
- >>> dixon = DixonResultant(variables=[x, y], polynomials=[p, q, h])
- >>> poly = dixon.get_dixon_polynomial()
- >>> matrix = dixon.get_dixon_matrix(polynomial=poly)
- >>> matrix
- Matrix([
- [ 0, 0, -1, 0, -1],
- [ 0, -1, 0, -1, 0],
- [-1, 0, 1, 0, 0],
- [ 0, -1, 0, 0, 1],
- [-1, 0, 0, 1, 0]])
- >>> matrix.det()
- 0
- See Also
- ========
- Notebook in examples: sympy/example/notebooks.
- References
- ==========
- .. [1] [Kapur1994]_
- .. [2] [Palancz08]_
- """
- def __init__(self, polynomials, variables):
- """
- A class that takes two lists, a list of polynomials and list of
- variables. Returns the Dixon matrix of the multivariate system.
- Parameters
- ----------
- polynomials : list of polynomials
- A list of m n-degree polynomials
- variables: list
- A list of all n variables
- """
- self.polynomials = polynomials
- self.variables = variables
- self.n = len(self.variables)
- self.m = len(self.polynomials)
- a = IndexedBase("alpha")
- # A list of n alpha variables (the replacing variables)
- self.dummy_variables = [a[i] for i in range(self.n)]
- # A list of the d_max of each variable.
- self._max_degrees = [max(degree_list(poly)[i] for poly in self.polynomials)
- for i in range(self.n)]
- @property
- def max_degrees(self):
- sympy_deprecation_warning(
- """
- The max_degrees property of DixonResultant is deprecated.
- """,
- deprecated_since_version="1.5",
- active_deprecations_target="deprecated-dixonresultant-properties",
- )
- return self._max_degrees
- def get_dixon_polynomial(self):
- r"""
- Returns
- =======
- dixon_polynomial: polynomial
- Dixon's polynomial is calculated as:
- delta = Delta(A) / ((x_1 - a_1) ... (x_n - a_n)) where,
- A = |p_1(x_1,... x_n), ..., p_n(x_1,... x_n)|
- |p_1(a_1,... x_n), ..., p_n(a_1,... x_n)|
- |... , ..., ...|
- |p_1(a_1,... a_n), ..., p_n(a_1,... a_n)|
- """
- if self.m != (self.n + 1):
- raise ValueError('Method invalid for given combination.')
- # First row
- rows = [self.polynomials]
- temp = list(self.variables)
- for idx in range(self.n):
- temp[idx] = self.dummy_variables[idx]
- substitution = {var: t for var, t in zip(self.variables, temp)}
- rows.append([f.subs(substitution) for f in self.polynomials])
- A = Matrix(rows)
- terms = zip(self.variables, self.dummy_variables)
- product_of_differences = Mul(*[a - b for a, b in terms])
- dixon_polynomial = (A.det() / product_of_differences).factor()
- return poly_from_expr(dixon_polynomial, self.dummy_variables)[0]
- def get_upper_degree(self):
- sympy_deprecation_warning(
- """
- The get_upper_degree() method of DixonResultant is deprecated. Use
- get_max_degrees() instead.
- """,
- deprecated_since_version="1.5",
- active_deprecations_target="deprecated-dixonresultant-properties"
- )
- list_of_products = [self.variables[i] ** self._max_degrees[i]
- for i in range(self.n)]
- product = prod(list_of_products)
- product = Poly(product).monoms()
- return monomial_deg(*product)
- def get_max_degrees(self, polynomial):
- r"""
- Returns a list of the maximum degree of each variable appearing
- in the coefficients of the Dixon polynomial. The coefficients are
- viewed as polys in $x_1, x_2, \dots, x_n$.
- """
- deg_lists = [degree_list(Poly(poly, self.variables))
- for poly in polynomial.coeffs()]
- max_degrees = [max(degs) for degs in zip(*deg_lists)]
- return max_degrees
- def get_dixon_matrix(self, polynomial):
- r"""
- Construct the Dixon matrix from the coefficients of polynomial
- \alpha. Each coefficient is viewed as a polynomial of x_1, ...,
- x_n.
- """
- max_degrees = self.get_max_degrees(polynomial)
- # list of column headers of the Dixon matrix.
- monomials = itermonomials(self.variables, max_degrees)
- monomials = sorted(monomials, reverse=True,
- key=monomial_key('lex', self.variables))
- dixon_matrix = Matrix([[Poly(c, *self.variables).coeff_monomial(m)
- for m in monomials]
- for c in polynomial.coeffs()])
- # remove columns if needed
- if dixon_matrix.shape[0] != dixon_matrix.shape[1]:
- keep = [column for column in range(dixon_matrix.shape[-1])
- if any(element != 0 for element
- in dixon_matrix[:, column])]
- dixon_matrix = dixon_matrix[:, keep]
- return dixon_matrix
- def KSY_precondition(self, matrix):
- """
- Test for the validity of the Kapur-Saxena-Yang precondition.
- The precondition requires that the column corresponding to the
- monomial 1 = x_1 ^ 0 * x_2 ^ 0 * ... * x_n ^ 0 is not a linear
- combination of the remaining ones. In SymPy notation this is
- the last column. For the precondition to hold the last non-zero
- row of the rref matrix should be of the form [0, 0, ..., 1].
- """
- if matrix.is_zero_matrix:
- return False
- m, n = matrix.shape
- # simplify the matrix and keep only its non-zero rows
- matrix = simplify(matrix.rref()[0])
- rows = [i for i in range(m) if any(matrix[i, j] != 0 for j in range(n))]
- matrix = matrix[rows,:]
- condition = Matrix([[0]*(n-1) + [1]])
- if matrix[-1,:] == condition:
- return True
- else:
- return False
- def delete_zero_rows_and_columns(self, matrix):
- """Remove the zero rows and columns of the matrix."""
- rows = [
- i for i in range(matrix.rows) if not matrix.row(i).is_zero_matrix]
- cols = [
- j for j in range(matrix.cols) if not matrix.col(j).is_zero_matrix]
- return matrix[rows, cols]
- def product_leading_entries(self, matrix):
- """Calculate the product of the leading entries of the matrix."""
- res = 1
- for row in range(matrix.rows):
- for el in matrix.row(row):
- if el != 0:
- res = res * el
- break
- return res
- def get_KSY_Dixon_resultant(self, matrix):
- """Calculate the Kapur-Saxena-Yang approach to the Dixon Resultant."""
- matrix = self.delete_zero_rows_and_columns(matrix)
- _, U, _ = matrix.LUdecomposition()
- matrix = self.delete_zero_rows_and_columns(simplify(U))
- return self.product_leading_entries(matrix)
- class MacaulayResultant():
- """
- A class for calculating the Macaulay resultant. Note that the
- polynomials must be homogenized and their coefficients must be
- given as symbols.
- Examples
- ========
- >>> from sympy import symbols
- >>> from sympy.polys.multivariate_resultants import MacaulayResultant
- >>> x, y, z = symbols('x, y, z')
- >>> a_0, a_1, a_2 = symbols('a_0, a_1, a_2')
- >>> b_0, b_1, b_2 = symbols('b_0, b_1, b_2')
- >>> c_0, c_1, c_2,c_3, c_4 = symbols('c_0, c_1, c_2, c_3, c_4')
- >>> f = a_0 * y - a_1 * x + a_2 * z
- >>> g = b_1 * x ** 2 + b_0 * y ** 2 - b_2 * z ** 2
- >>> h = c_0 * y * z ** 2 - c_1 * x ** 3 + c_2 * x ** 2 * z - c_3 * x * z ** 2 + c_4 * z ** 3
- >>> mac = MacaulayResultant(polynomials=[f, g, h], variables=[x, y, z])
- >>> mac.monomial_set
- [x**4, x**3*y, x**3*z, x**2*y**2, x**2*y*z, x**2*z**2, x*y**3,
- x*y**2*z, x*y*z**2, x*z**3, y**4, y**3*z, y**2*z**2, y*z**3, z**4]
- >>> matrix = mac.get_matrix()
- >>> submatrix = mac.get_submatrix(matrix)
- >>> submatrix
- Matrix([
- [-a_1, a_0, a_2, 0],
- [ 0, -a_1, 0, 0],
- [ 0, 0, -a_1, 0],
- [ 0, 0, 0, -a_1]])
- See Also
- ========
- Notebook in examples: sympy/example/notebooks.
- References
- ==========
- .. [1] [Bruce97]_
- .. [2] [Stiller96]_
- """
- def __init__(self, polynomials, variables):
- """
- Parameters
- ==========
- variables: list
- A list of all n variables
- polynomials : list of SymPy polynomials
- A list of m n-degree polynomials
- """
- self.polynomials = polynomials
- self.variables = variables
- self.n = len(variables)
- # A list of the d_max of each polynomial.
- self.degrees = [total_degree(poly, *self.variables) for poly
- in self.polynomials]
- self.degree_m = self._get_degree_m()
- self.monomials_size = self.get_size()
- # The set T of all possible monomials of degree degree_m
- self.monomial_set = self.get_monomials_of_certain_degree(self.degree_m)
- def _get_degree_m(self):
- r"""
- Returns
- =======
- degree_m: int
- The degree_m is calculated as 1 + \sum_1 ^ n (d_i - 1),
- where d_i is the degree of the i polynomial
- """
- return 1 + sum(d - 1 for d in self.degrees)
- def get_size(self):
- r"""
- Returns
- =======
- size: int
- The size of set T. Set T is the set of all possible
- monomials of the n variables for degree equal to the
- degree_m
- """
- return binomial(self.degree_m + self.n - 1, self.n - 1)
- def get_monomials_of_certain_degree(self, degree):
- """
- Returns
- =======
- monomials: list
- A list of monomials of a certain degree.
- """
- monomials = [Mul(*monomial) for monomial
- in combinations_with_replacement(self.variables,
- degree)]
- return sorted(monomials, reverse=True,
- key=monomial_key('lex', self.variables))
- def get_row_coefficients(self):
- """
- Returns
- =======
- row_coefficients: list
- The row coefficients of Macaulay's matrix
- """
- row_coefficients = []
- divisible = []
- for i in range(self.n):
- if i == 0:
- degree = self.degree_m - self.degrees[i]
- monomial = self.get_monomials_of_certain_degree(degree)
- row_coefficients.append(monomial)
- else:
- divisible.append(self.variables[i - 1] **
- self.degrees[i - 1])
- degree = self.degree_m - self.degrees[i]
- poss_rows = self.get_monomials_of_certain_degree(degree)
- for div in divisible:
- for p in poss_rows:
- if rem(p, div) == 0:
- poss_rows = [item for item in poss_rows
- if item != p]
- row_coefficients.append(poss_rows)
- return row_coefficients
- def get_matrix(self):
- """
- Returns
- =======
- macaulay_matrix: Matrix
- The Macaulay numerator matrix
- """
- rows = []
- row_coefficients = self.get_row_coefficients()
- for i in range(self.n):
- for multiplier in row_coefficients[i]:
- coefficients = []
- poly = Poly(self.polynomials[i] * multiplier,
- *self.variables)
- for mono in self.monomial_set:
- coefficients.append(poly.coeff_monomial(mono))
- rows.append(coefficients)
- macaulay_matrix = Matrix(rows)
- return macaulay_matrix
- def get_reduced_nonreduced(self):
- r"""
- Returns
- =======
- reduced: list
- A list of the reduced monomials
- non_reduced: list
- A list of the monomials that are not reduced
- Definition
- ==========
- A polynomial is said to be reduced in x_i, if its degree (the
- maximum degree of its monomials) in x_i is less than d_i. A
- polynomial that is reduced in all variables but one is said
- simply to be reduced.
- """
- divisible = []
- for m in self.monomial_set:
- temp = []
- for i, v in enumerate(self.variables):
- temp.append(bool(total_degree(m, v) >= self.degrees[i]))
- divisible.append(temp)
- reduced = [i for i, r in enumerate(divisible)
- if sum(r) < self.n - 1]
- non_reduced = [i for i, r in enumerate(divisible)
- if sum(r) >= self.n -1]
- return reduced, non_reduced
- def get_submatrix(self, matrix):
- r"""
- Returns
- =======
- macaulay_submatrix: Matrix
- The Macaulay denominator matrix. Columns that are non reduced are kept.
- The row which contains one of the a_{i}s is dropped. a_{i}s
- are the coefficients of x_i ^ {d_i}.
- """
- reduced, non_reduced = self.get_reduced_nonreduced()
- # if reduced == [], then det(matrix) should be 1
- if reduced == []:
- return diag([1])
- # reduced != []
- reduction_set = [v ** self.degrees[i] for i, v
- in enumerate(self.variables)]
- ais = list([self.polynomials[i].coeff(reduction_set[i])
- for i in range(self.n)])
- reduced_matrix = matrix[:, reduced]
- keep = []
- for row in range(reduced_matrix.rows):
- check = [ai in reduced_matrix[row, :] for ai in ais]
- if True not in check:
- keep.append(row)
- return matrix[keep, non_reduced]
|