# Ticket #12417: trac_12417-pfd.patch

File trac_12417-pfd.patch, 29.0 KB (added by malb, 10 years ago)
• ## sage/categories/quotient_fields.py

# HG changeset patch
# User Alex Raichev <tortoise.said@gmail.com>
# Date 1328736876 -46800
# Node ID f8971d29e69f37b2d56e728a3ece3dea0af53ff0
# Parent  e9fd586e05a45dbf67a1eefe8a1094e8be6fb8a1
initial version; fails

diff --git a/sage/categories/quotient_fields.py b/sage/categories/quotient_fields.py
 a return (self.numerator().factor(*args, **kwds) / self.denominator().factor(*args, **kwds)) def partial_fraction_decomposition(self): def partial_fraction_decomposition_univariate(self): """ Decomposes fraction field element into a whole part and a list of fraction field elements over prime power denominators. parts.append(n/d) return whole, parts def partial_fraction_decomposition(self, factored=False): r""" Returns a partial fraction decomposition of self. See the note section below for more details. INPUT: - factored - (Optional; default=False) If True, then the denominators of the partial fractions will be factored. Note: factoring happens anyway as part of the algorithm. EXAMPLES:: Extend Sage's old partial fraction decomposition function:: sage: f = 26/15 sage: pfd = f.partial_fraction_decomposition() sage: print pfd (1, [1/3, 2/5]) sage: print pfd[0] + sum(pfd[1]) == f True Extend Sage's old partial fraction decomposition function:: sage: R. = PolynomialRing(QQ) sage: f = x + 1/(x^4 - 4) sage: pfd = f.partial_fraction_decomposition() sage: print pfd (x, [1/4/(x^2 - 2), -1/4/(x^2 + 2)]) sage: print pfd[0] + sum(pfd[1]) == f True New stuff:: sage: R.= PolynomialRing(QQ) sage: f = x + 1/(x*y*(x*y - 1)) sage: pfd1 = f.partial_fraction_decomposition() sage: pfd2 = f.partial_fraction_decomposition(factored=True) sage: print pfd1 (x, [1/(x*y - 1), (-1)/(x*y)]) sage: print pfd2 (x, [[1, [(x*y - 1, 1)]], [-1, [(y, 1), (x, 1)]]]) sage: print pfd1[0] + sum(pfd1[1]) == f True :: sage: R.= PolynomialRing(QQ) sage: f = x + (x^2 + y)/(x*y^2*(x*z + 1)^2*(y*z - 1)) sage: pfd1 = f.partial_fraction_decomposition() sage: pfd2 = f.partial_fraction_decomposition(factored=True) sage: print pfd2 (x, [[x^2*z^2 + y*z^2, [(x, 1), (y*z - 1, 1)]], [-x^2*y*z - y^2*z - x^2 - y, [(x, 1), (y, 2)]], [-x^3*z^4 - x*y*z^4 - 2*x^2*z^3 - 2*y*z^3, [(y*z - 1, 1), (x*z + 1, 2)]], [x^3*y*z^3 + x*y^2*z^3 + x^3*z^2 + 2*x^2*y*z^2 + x*y*z^2 + 2*y^2*z^2 + 2*x^2*z + 2*y*z, [(y, 2), (x*z + 1, 2)]]]) sage: print pfd1[0] + sum(pfd1[1]) == f True TESTS: Fails over inexact fields, since the call to quo_rem() fails over inexact fields:: sage: R.= PolynomialRing(RealField(20)) sage: f = x + 1/(x*y*(x*y - 1)) sage: print f (x^3*y^2 - x^2*y + 1.0000)/(x^2*y^2 - x*y) sage: pfd = f.partial_fraction_decomposition() Traceback (most recent call last): ... KeyError: '1.000e+00' Fails over rings whose coefficient rings are not fields:: sage: R.= PolynomialRing(ZZ) sage: f = x + 1/(x*y*(x*y - 1)) sage: pfd1 = f.partial_fraction_decomposition() sage: print pfd1 sage: print pfd1[0] + sum(pfd1[1]) == f Traceback (most recent call last): ... NotImplementedError: Factorization of multivariate polynomials over non-fields is not implemented. Works over finite fields:: sage: R.= PolynomialRing(GF(2)) sage: f = x + (x^2 + y)/(x*y^2*(x*z + 1)^2*(y*z - 1)) sage: pfd1 = f.partial_fraction_decomposition() sage: pfd2 = f.partial_fraction_decomposition(factored=True) sage: print pfd2 (x, [[x^2*z^2 + y*z^2, [(x, 1), (y*z + 1, 1)]], [x^2*y*z + y^2*z + x^2 + y, [(x, 1), (y, 2)]], [x^3*z^4 + x*y*z^4, [(y*z + 1, 1), (x*z + 1, 2)]], [x^3*y*z^3 + x*y^2*z^3 + x^3*z^2 + x*y*z^2, [(y, 2), (x*z + 1, 2)]]]) sage: print pfd1[0] + sum(pfd1[1]) == f True NOTE:: This function extends partial_fraction_decomposition_univariate() to work over any element of a fraction field of a multivariate polynomial ring over a field. What is a partial fraction decomposition in this case? Let f = P/Q be a rational expression where P and Q lie in a multivariate polynomial ring R over a field. Let Q_1^{e_1} \cdots Q_m^{e_m} be the unique factorization of Q in R into irreducible factors, and let d be the number of indeterminates of R. Then by Theorem 1 of [Lein1978]_, f can be written as a sum of rational expressions in the form \sum P_A/\prod_{j\in A} Q_j^{b_j}, where the b_j \le e_j are positive integers, the P_A are in R, and the sum is taken over all subsets A \subseteq \{ 1, \ldots, d \} such that S:= \{ Q_j : j\in A \} is algebraically independent and the ideal generated by S is not all of R (that is, by the Nullstellensatz, the polynomials of S have a common zero in K^d, where K is the algebraic closure of the coefficient field of R). Following [Lein1978]_, i call any such decomposition of f a \emph{partial fraction decomposition}. Such a decomposition is not unique. The algorithm herein comes from [Lein1978]_. By the way, [Lein1978]_ has a typo in equation (c) on the second page and equation (b) on the third page. The right sides of (c) and (b) should be multiplied by P. REFERENCES: .. [Lein1978]  E. K. Leinartas, "On expansion of rational functions of several variables into partial fractions", Soviet Math. (Iz. VUZ) 22 (1978), no. 10, 35--38. AUTHORS: - Alex Raichev (2012-02-02) """ # Use old Sage partial fraction decomposition in univariate case. R = self.denominator().parent() if R.ngens() < 2: return self.partial_fraction_decomposition_univariate() # Use this new Sage partial fraction decomposition in multivariate case. Q = self.denominator() whole, P = self.numerator().quo_rem(Q) fractions = (P/Q)._to_refd().partial_fraction_decomposition() if factored: return whole, [t.list() for t in fractions] else: return whole, [t.quotient() for t in fractions] def _to_refd(self): r""" Converts self into a REFD object. Assumes that the total degree of the numerator of f is less than the total degree of the denominator of f. Used to format f in preparation for a partial fraction decomposition. EXAMPLES:: sage: R.= PolynomialRing(QQ) sage: f = (x + 1)/(x*y*(x*y+1)^2) sage: print f._to_refd() [x + 1, [(y, 1), (x, 1), (x*y + 1, 2)]] AUTHORS: - Alex Raichev (2012-02-02) """ P = self.numerator() Q = self.denominator() R = P.parent() if Q in R.base_ring(): return REFD(P/Q, []) Qs= Q.factor(proof=False)   # Singular needs proof=False for finite fields. if Qs.unit() != 1: P = P/Qs.unit() Qs= [(q,e) for q,e in Qs] return REFD(P, Qs) def derivative(self, *args): r""" The derivative of this rational function, with respect to variables return self.__class__(R, num, den, coerce=False, reduce=False) class REFD(object): r""" Represents a rational expression with factored denominator (REFD) P/(Q_1^e_1 \cdots Q_m^e_m) by storing the parts P and [(Q_1,e_1), \ldots, (Q_m,e_m)], where P, Q_1, \ldots, Q_m are elements of a common polynomial ring R, Q_1, \ldots, Q_m are irreducible elements of R, and e_1, \ldots, e_m are positive integers. A polynomial P is represented as [P, (,)]. Used in partial_fraction_decomposition() AUTHORS: - Alex Raichev (2012-02-02) """ def __init__(self, numerator, denominators, reduce=True): r""" EXAMPLES:: sage: R.= PolynomialRing(QQ) sage: Qs = [x,1], [y,1], [x*y+1,1] sage: f = REFD(x, Qs) sage: print f [1, [(y, 1), (x*y + 1, 1)]] sage: ff = REFD(x, Qs, reduce=False) sage: print ff [x, [(y, 1), (x, 1), (x*y + 1, 1)]] :: sage: R.= PolynomialRing(QQ) sage: f = REFD(x + y, [(x + y,1)]) sage: print f [1, []] """ if denominators: R = denominators[0][0].parent() else: R = numerator.parent() self._numerator = numerator self._denominators = sorted([tuple(t) for t in denominators]) self._base_ring = R if not reduce: return # Reduce rational expression P = numerator Qs = denominators newP = P newQs = [] for Q,e in Qs: a = 0 while Q.divides(newP) and a < e: a += 1 newP = R(newP/Q) if e - a > 0: newQs.append((Q,e-a)) self._numerator = newP self._denominators = newQs self._base_ring = R def __str__(self): r""" EXAMPLES:: sage: R.= PolynomialRing(QQ) sage: Qs = [x,1], [y,1], [x*y+1,1] sage: f = REFD(x, Qs) sage: print f [1, [(y, 1), (x*y + 1, 1)]] """ return str(self.list()) def __eq__(self, other): r""" EXAMPLES:: sage: R.= PolynomialRing(QQ) sage: Qs = [x,1], [y,1], [x*y+1,1] sage: f = REFD(x, Qs) sage: print f == ff True sage: g = REFD(y, Qs) sage: print g == f False """ if isinstance(other, REFD) \ and self.quotient() == other.quotient(): return True else: return False def __ne__(self, other): r""" EXAMPLES:: sage: R.= PolynomialRing(QQ) sage: Qs = [x,1], [y,1], [x*y+1,1] sage: f = REFD(x, Qs) sage: print f != ff False sage: g = REFD(y, Qs) sage: print g != f True """ if not self == other: return True else: return False def __lt__(self, other): r""" EXAMPLES:: sage: R.= PolynomialRing(QQ) sage: Qs = [x,1], [y,1], [x*y+1,1] sage: f = REFD(x, Qs) sage: ff = REFD(x, Qs, reduce=False) sage: g = REFD(x + 1, Qs) sage: h = REFD(x + 2, Qs) sage: print f < ff False sage: print f < g True sage: print g < f False sage: print g < h True """ snum = self.numerator() onum = other.numerator() sdenoms = self.denominators() odenoms = other.denominators() sdenom = self.denominator() odenom = other.denominator() if self == other: return False elif len(sdenoms) < len(odenoms) or \ (len(sdenoms) == len(odenoms) and sdenom < odenom) or \ (len(sdenoms) == len(odenoms) and sdenom == odenom and snum < onum): return True else: return False def __gt__(self, other): r""" EXAMPLES:: sage: R.= PolynomialRing(QQ) sage: Qs = [x,1], [y,1], [x*y+1,1] sage: f = REFD(x, Qs) sage: ff = REFD(x, Qs, reduce=False) sage: g = REFD(x + 1, Qs) sage: h = REFD(x + 2, Qs) sage: print f > ff False sage: print f > g False sage: print g > f True sage: print g > h False """ if (not self == other) and (not self < other): return True else: return False def __add__(self, other): r""" EXAMPLES:: sage: R.= PolynomialRing(QQ) sage: Qs = [x,1], [y,1] sage: f = REFD(x, Qs) sage: ff = REFD(x, Qs, reduce=False) sage: print f + ff [2, [(y, 1), (x, 1)]] sage: print f + 2 [2*y + 1, [(y, 1)]] """ R = self.base_ring() P = self.numerator() Q = self.denominator() Qs = self.denominators() if not isinstance(other, REFD): try: t = R(other) except: print "Fail! %s is not an element of %s" % (other, R) else: return REFD(P + R(other)*Q, Qs) # Compute the sum's numerator num = (self.quotient() + other.quotient()).numerator() # Compute the sum's denominators. denoms = self.denominators() + other.denominators() if not denoms: # Then just added two polynomials. Done. return REFD(num, denoms, reduce=False) denoms.sort() common_denoms = [] current = list(denoms[0]) for i in range(1,len(denoms)): if current[0] == denoms[i][0]: current[1] = max(current[1],denoms[i][1]) else: # Append current to common_denoms and update current common_denoms.append(tuple(current)) current = list(denoms[i]) common_denoms.append(tuple(current)) return REFD(num, common_denoms, reduce=False) def __radd__(self, x): r""" EXAMPLES:: sage: R.= PolynomialRing(QQ) sage: Qs = [x,1], [y,1] sage: f = REFD(x, Qs) sage: print 2 + f [2*y + 1, [(y, 1)]] """ return self + x def list(self): return [self.numerator(), self.denominators()] def numerator(self): return self._numerator def denominators(self): return self._denominators def denominator(self): from sage.misc.misc import prod return prod([q**e for q,e in self._denominators]) def base_ring(self): return self._base_ring def quotient(self): return self.numerator()/self.denominator() def partial_fraction_decomposition(self): r""" Return a partial fraction decomposition of self as a REFDSum object. EXAMPLES:: sage: R.= PolynomialRing(QQ) sage: f = (x^2 + y)/(x*y*(x*y + 1)^2) sage: pfd = f._to_refd().partial_fraction_decomposition() sage: print pfd [[-x^3*y - x*y^2 - 2*x^2 - 2*y, [(x*y + 1, 2)]], [x^2 + y, [(y, 1), (x, 1)]]] sage: print sum([t.quotient() for t in pfd]) == f True :: sage: R.= PolynomialRing(QQ) sage: f = (x^2 + y)/(x*y^2*(x*z + 1)^2*(y*z - 1)) sage: pfd = f._to_refd().partial_fraction_decomposition() sage: print pfd [[x^2*z^2 + y*z^2, [(x, 1), (y*z - 1, 1)]], [-x^2*y*z - y^2*z - x^2 - y, [(x, 1), (y, 2)]], [-x^3*z^4 - x*y*z^4 - 2*x^2*z^3 - 2*y*z^3, [(y*z - 1, 1), (x*z + 1, 2)]], [x^3*y*z^3 + x*y^2*z^3 + x^3*z^2 + 2*x^2*y*z^2 + x*y*z^2 + 2*y^2*z^2 + 2*x^2*z + 2*y*z, [(y, 2), (x*z + 1, 2)]]] sage: print sum([t.quotient() for t in pfd]) == f True NOTE:: See the note section of the __main__ function partial_fraction_decomposition(). """ return REFDSum([self]).decompose_via_nullstellensatz()\ .decompose_via_algebraic_dependence() class REFDSum(list): r""" A list representing the sum of REFD objects with distinct denominator lists. Used in partial_fraction_decomposition() AUTHORS: - Alex Raichev (2012-02-02) """ def simplify(self): r""" Adds terms in self with the same denominator. EXAMPLES:: sage: R.= PolynomialRing(QQ) sage: f = REFD(1, [(x,1), (y,1), (x*y + 1,1)]) sage: g = REFD(x, [(x,1), (y,1), (x*y + 1,1)]) sage: s = REFDSum([f, g, f]) sage: t = s.simplify() sage: print s [[1, [(x, 1), (y, 1), (x*y + 1, 1)]], [1, [(y, 1), (x*y + 1, 1)]], [1, [(x, 1), (y, 1), (x*y + 1, 1)]]] sage: print t [[1, [(y, 1), (x*y + 1, 1)]], [2, [(y, 1), (x, 1), (x*y + 1, 1)]]] sage: print sum(t) == sum(s) True """ # Combine like terms. terms = sorted(self) new_terms = [] total = terms[0] for i in range(len(terms) - 1): if  terms[i].denominators() == terms[i + 1].denominators(): total += terms[i + 1] else: new_terms.append(total) total = terms[i + 1] new_terms.append(total) return REFDSum(new_terms) def __str__(self): return str(self.list()) def list(self): return [t.list() for t in self] def base_ring(self): return self[0].base_ring() def decompose_via_algebraic_dependence(self): r""" Returns a REFDSum decomposition of self according to Lemma 2 of [Lein1978]_. Recursive. EXAMPLES:: sage: R.= PolynomialRing(QQ) sage: f = (x^2 + y)/(x*y*(x*y + 1)^2) sage: pfd = REFDSum([f._to_refd()]).decompose_via_algebraic_dependence() sage: print pfd [[-x^3*y - x*y^2 - 2*x^2 - 2*y, [(x*y + 1, 2)]], [x^2 + y, [(y, 1), (x, 1)]]] sage: print sum([t.quotient() for t in pfd]) == f True :: sage: R.= PolynomialRing(QQ) sage: f = (x^2 + y)/(x*y^2*(x*z + 1)^2*(y*z - 1)) sage: pfd = REFDSum([f._to_refd()]).decompose_via_algebraic_dependence() sage: print pfd [[2*x^3*y*z + 2*x*y^2*z + 2*x^3 + 2*x*y, [(y, 4)]], [2*x^3 + 2*x*y, [(y, 4), (y*z - 1, 1)]], [2*x^3*y*z + 2*x*y^2*z + 2*x^3 + 2*x*y, [(x*z + 1, 2), (y, 4)]], [-x^5*y^3*z^3 - x^3*y^4*z^3 - x^5*y^2*z^2 - x^3*y^3*z^2 - x^5*y*z - x^3*y^2*z - x^5 - x^3*y, [(x*z + 1, 2), (y, 6)]], [-x^4*z^2 - x^2*y*z^2 - 2*x^3*z - 2*x*y*z + x^2 + y, [(y*z - 1, 1), (y, 2), (x, 1)]], [2*x^3 + 2*x*y, [(y, 4), (y*z - 1, 1), (x*z + 1, 2)]], [-x^5 - x^3*y, [(y, 6), (y*z - 1, 1), (x*z + 1, 2)]]] sage: print sum([t.quotient() for t in pfd]) == f True NOTE:: Let f = P/Q be a rational expression where P and Q lie in a (possibly multivariate) polynomial ring R. Let Q_1^{e_1} \cdots Q_m^{e_m} be the unique factorization of Q in R into irreducible factors, and let d be the number of indeterminates of R. If Q_1, \ldots, Q_m are algebraically dependent, then by Lemma 2 of [Lein1978]_, f can be written as a sum of rational expressions in the form \sum P_A/\prod_{j\in A} Q_j^{b_j}, where the b_j are positive integers, the P_A are in R, and the sum is taken over all size < m subsets A \subseteq \{ 1, \ldots, d \} such that S:= \{ Q_j : j\in A \} is algebraically independent. In other words, f can be decomposed into a sum of rational expressions, each of which has a denominator whose set of irreducible factors is an algebraically independent subset of \{Q_1, \ldots, Q_m\}. This function decomposes every rational expression f in self as described above. By the way, [Lein1978]_ has a typo in equation (c) on the second page and equation (b) on the third page. The right sides of (c) and (b) should be multiplied by P. REFERENCES: .. [Lein1978]  E. K. Leinartas, "On expansion of rational functions of several variables into partial fractions", Soviet Math. (Iz. VUZ) 22 (1978), no. 10, 35--38. AUTHORS: - Alex Raichev (2011-01-10) """ from sage.structure.sequence import Sequence from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing parts = REFDSum([]) R = self.base_ring() done_decomposing = True for t in self: decomp = REFDSum([t]) P = t.numerator() Qs = t.denominators() m = len(Qs) # The polynomials [q**e for q,e in Qs] are algebraically dependent # iff the polynomials [q for q,e in Qs] are algebraically dependent. G = Sequence([q for q,e in Qs], R).algebraic_dependence().gens()[0] if G: # Then [q**e for q,e in Qs] are algebraically dependent and # hence we can decompose t. done_decomposing = False # Todo: speed up step below by using # G to calculate F.  See [Lein1978]_ Lemma 1. F = Sequence([q**e for q,e in Qs], R).algebraic_dependence().gens()[0] new_vars = F.parent().gens() # Note that each new_vars[j] corresponds to Qs[j] such that # F([q**e for q,e in Qs]) = 0. # Assume that F.parent() has negdeglex term order # so that F.lt() is indeed the monomial we want below. # Use F to rewrite P/Qs into a sum of REFDs, # each with < m denominator factors. FF = (F.lt() - F)/(F.lc()) numers = map(mul,zip(FF.coefficients(),FF.monomials())) e = list(F.lt().exponents())[0:m] denoms = [(new_vars[j], e[0][j] + 1) for j in range(m)] decomp_temp = REFDSum([REFD(a, denoms) for a in numers]).simplify() decomp = REFDSum() # Substitute in Qs. Qpowsub = dict([(new_vars[j],Qs[j][0]**Qs[j][1]) for j in range(m)]) for f in decomp_temp: gnum = P*F.parent()(f.numerator()).subs(Qpowsub) gdenoms = [] for q,e in f.denominators(): j = new_vars.index(q) gdenoms.append((Qs[j][0], Qs[j][1]*e)) # if q in new_vars: #     j = new_vars.index(q) #     gdenoms.append((Qs[j][0], Qs[j][1]*e)) # else: #     # Occasionally q is an integer. #     gdenoms.append((q,e)) decomp.append(REFD(gnum,gdenoms)) parts.extend(decomp) parts = parts.simplify() if done_decomposing: return parts else: return parts.decompose_via_algebraic_dependence() def decompose_via_nullstellensatz(self): r""" Returns a REFDSum decomposition of self according to Lemma 3 of [Lein1978]_. Recursive. EXAMPLES:: sage: R.= PolynomialRing(QQ) sage: f = (x^2 + y)/(x*y*(x*y + 1)^2) sage: pfd = REFDSum([f._to_refd()]).decompose_via_nullstellensatz() sage: print pfd [[-x^3*y - x*y^2 - 2*x^2 - 2*y, [(x*y + 1, 2)]], [x^2 + y, [(y, 1), (x, 1)]]] sage: print sum([t.quotient() for t in pfd]) == f True :: sage: R.= PolynomialRing(QQ) sage: f = (x^2 + y)/(x*y^2*(x*z + 1)^2*(y*z - 1)) sage: pfd = REFDSum([f._to_refd()]).decompose_via_nullstellensatz() sage: print pfd [[x^2*z^2 + y*z^2, [(x, 1), (y*z - 1, 1)]], [-x^2*y*z - y^2*z - x^2 - y, [(x, 1), (y, 2)]], [-x^3*z^4 - x*y*z^4 - 2*x^2*z^3 - 2*y*z^3, [(y*z - 1, 1), (x*z + 1, 2)]], [x^3*y*z^3 + x*y^2*z^3 + x^3*z^2 + 2*x^2*y*z^2 + x*y*z^2 + 2*y^2*z^2 + 2*x^2*z + 2*y*z, [(y, 2), (x*z + 1, 2)]]] sage: print sum([t.quotient() for t in pfd]) == f True NOTE:: Let f = P/Q be a rational expression where P and Q lie in a (possibly multivariate) polynomial ring R. Let Q_1^{e_1} \cdots Q_m^{e_m} be the unique factorization of Q in R into irreducible factors, and let d be the number of indeterminates of R. If the ideal generated by Q_1, \ldots, Q_m is not all of R (that is, by the Nullstellensatz, the polynomials have a common zerozero in K^d, where K is the algebraic closure of the coefficient field of R), then by Lemma 3 of [Lein1978]_, f can be written as a sum of rational expressions in the form \sum P_A/\prod_{j\in A} Q_j^{b_j}, where the b_j \le e_j are positive integers, the P_A are in R, and the sum is taken over all size < m subsets A \subseteq \{ 1, \ldots, d \}. In other words, f can be decomposed into a sum of rational expressions, each of which has a denominator whose set of irreducible factors is a size < m subset of \{Q_1, \ldots, Q_m\}. This function decomposes every rational expression f in self as described above. By the way, [Lein1978]_ has a typo in equation (c) on the second page and equation (b) on the third page. The right sides of (c) and (b) should be multiplied by P. REFERENCES: .. [Lein1978]  E. K. Leinartas, "On expansion of rational functions of several variables into partial fractions", Soviet Math. (Iz. VUZ) 22 (1978), no. 10, 35--38. AUTHORS: - Alex Raichev (2011-01-10) """ parts = REFDSum([]) R= self.base_ring() done_decomposing = True for t in self: decomp = REFDSum([t]) P= t.numerator() Qs= t.denominators() m= len(Qs) if R(1) in R.ideal([q for q,e in Qs]): # Then we can decompose t. done_decomposing = False L = R(1).lift(R.ideal([q**e for q,e in Qs])) decomp = REFDSum([ REFD(P*L[i],[(Qs[j][0], Qs[j][1]) for j in range(m) if j != i])\ for i in range(m) if L[i] != 0]) # The procedure above yields no like terms to combine. parts.extend(decomp) parts = parts.simplify() if done_decomposing: return parts else: return parts.decompose_via_nullstellensatz()
• ## sage/rings/polynomial/multi_polynomial_sequence.py

diff --git a/sage/rings/polynomial/multi_polynomial_sequence.py b/sage/rings/polynomial/multi_polynomial_sequence.py
 a from sage.rings.quotient_ring_element import QuotientRingElement from sage.rings.polynomial.multi_polynomial_ideal import MPolynomialIdeal from sage.rings.polynomial.multi_polynomial import is_MPolynomial from sage.structure.sequence import Sequence_generic from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing from sage.interfaces.singular import singular