def get_ring(self): """Returns a ring associated with `self`. """ raise DomainError('there is no ring associated with %s' % self)
def get_field(self): """Returns a field associated with ``self``. """ raise DomainError('there is no field associated with %s' % self)
def algebraic_field(self, *extension): r"""Returns an algebraic field, i.e. `K(\alpha, \ldots)`. """ raise DomainError("can't create algebraic field over %s" % self)
def sdp_groebner(f, u, O, K, gens='', verbose=False): """ Computes Groebner basis for a set of polynomials in `K[X]`. Given a set of multivariate polynomials `F`, finds another set `G`, such that Ideal `F = Ideal G` and `G` is a reduced Groebner basis. The resulting basis is unique and has monic generators if the ground domains is a field. Otherwise the result is non-unique but Groebner bases over e.g. integers can be computed (if the input polynomials are monic). Groebner bases can be used to choose specific generators for a polynomial ideal. Because these bases are unique you can check for ideal equality by comparing the Groebner bases. To see if one polynomial lies in an ideal, divide by the elements in the base and see if the remainder vanishes. They can also be used to solve systems of polynomial equations as, by choosing lexicographic ordering, you can eliminate one variable at a time, provided that the ideal is zero-dimensional (finite number of solutions). **References** 1. [Bose03]_ 2. [Giovini91]_ 3. [Ajwa95]_ 4. [Cox97]_ Algorithm used: an improved version of Buchberger's algorithm as presented in T. Becker, V. Weispfenning, Groebner Bases: A Computational Approach to Commutative Algebra, Springer, 1993, page 232. Added optional ``gens`` argument to apply :func:`sdp_str` for the purpose of debugging the algorithm. """ if not K.has_Field: raise DomainError("can't compute a Groebner basis over %s" % K) def select(P): # normal selection strategy # select the pair with minimum LCM(LM(f), LM(g)) pr = minkey(P, key=lambda pair: O(monomial_lcm(sdp_LM(f[pair[0]], u), sdp_LM(f[pair[1]], u)))) return pr def normal(g, J): h = sdp_rem(g, [ f[j] for j in J ], u, O, K) if not h: return None else: h = sdp_monic(h, K) h = tuple(h) if not h in I: I[h] = len(f) f.append(h) return sdp_LM(h, u), I[h] def update(G, B, ih): # update G using the set of critical pairs B and h # [BW] page 230 h = f[ih] mh = sdp_LM(h, u) # filter new pairs (h, g), g in G C = G.copy() D = set() while C: # select a pair (h, g) by popping an element from C ig = C.pop() g = f[ig] mg = sdp_LM(g, u) LCMhg = monomial_lcm(mh, mg) def lcm_divides(ip): # LCM(LM(h), LM(p)) divides LCM(LM(h), LM(g)) m = monomial_lcm(mh, sdp_LM(f[ip], u)) return monomial_div(LCMhg, m) # HT(h) and HT(g) disjoint: mh*mg == LCMhg if monomial_mul(mh, mg) == LCMhg or ( not any(lcm_divides(ipx) for ipx in C) and not any(lcm_divides(pr[1]) for pr in D)): D.add((ih, ig)) E = set() while D: # select h, g from D (h the same as above) ih, ig = D.pop() mg = sdp_LM(f[ig], u) LCMhg = monomial_lcm(mh, mg) if not monomial_mul(mh, mg) == LCMhg: E.add((ih, ig)) # filter old pairs B_new = set() while B: # select g1, g2 from B (-> CP) ig1, ig2 = B.pop() mg1 = sdp_LM(f[ig1], u) mg2 = sdp_LM(f[ig2], u) LCM12 = monomial_lcm(mg1, mg2) # if HT(h) does not divide lcm(HT(g1), HT(g2)) if not monomial_div(LCM12, mh) or \ monomial_lcm(mg1, mh) == LCM12 or \ monomial_lcm(mg2, mh) == LCM12: B_new.add((ig1, ig2)) B_new |= E # filter polynomials G_new = set() while G: ig = G.pop() mg = sdp_LM(f[ig], u) if not monomial_div(mg, mh): G_new.add(ig) G_new.add(ih) return G_new, B_new # end of update ################################ if not f: return [] # replace f with a reduced list of initial polynomials; see [BW] page 203 f1 = f[:] while True: f = f1[:] f1 = [] for i in range(len(f)): p = f[i] r = sdp_rem(p, f[:i], u, O, K) if r: f1.append(sdp_monic(r, K)) if f == f1: break f = [tuple(p) for p in f] I = {} # ip = I[p]; p = f[ip] F = set() # set of indices of polynomials G = set() # set of indices of intermediate would-be Groebner basis CP = set() # set of pairs of indices of critical pairs for i, h in enumerate(f): I[h] = i F.add(i) ##################################### # algorithm GROEBNERNEWS2 in [BW] page 232 while F: # select p with minimum monomial according to the monomial ordering O h = minkey([f[x] for x in F], key=lambda f: O(sdp_LM(f, u))) ih = I[h] F.remove(ih) G, CP = update(G, CP, ih) # count the number of critical pairs which reduce to zero reductions_to_zero = 0 while CP: ig1, ig2 = select(CP) CP.remove((ig1, ig2)) h = sdp_spoly(f[ig1], f[ig2], u, O, K) # ordering divisors is on average more efficient [Cox] page 111 G1 = sorted(G, key=lambda g: O(sdp_LM(f[g], u))) ht = normal(h, G1) if ht: G, CP = update(G, CP, ht[1]) else: reductions_to_zero += 1 ###################################### # now G is a Groebner basis; reduce it Gr = set() for ig in G: ht = normal(f[ig], G - set([ig])) if ht: Gr.add(ht[1]) Gr = [list(f[ig]) for ig in Gr] # order according to the monomial ordering Gr = sorted(Gr, key=lambda f: O(sdp_LM(f, u)), reverse=True) if verbose: print 'reductions_to_zero = %d' % reductions_to_zero return Gr
def test_pickling_polys_errors(): from sympy.polys.polyerrors import ( ExactQuotientFailed, OperationNotSupported, HeuristicGCDFailed, HomomorphismFailed, IsomorphismFailed, ExtraneousFactors, EvaluationFailed, RefinementFailed, CoercionFailed, NotInvertible, NotReversible, NotAlgebraic, DomainError, PolynomialError, UnificationFailed, GeneratorsError, GeneratorsNeeded, ComputationFailed, UnivariatePolynomialError, MultivariatePolynomialError, PolificationFailed, OptionError, FlagError) x = Symbol('x') # TODO: TypeError: __init__() takes at least 3 arguments (1 given) # for c in (ExactQuotientFailed, ExactQuotientFailed(x, 3*x, ZZ)): # check(c) # TODO: TypeError: can't pickle instancemethod objects # for c in (OperationNotSupported, OperationNotSupported(Poly(x), Poly.gcd)): # check(c) for c in (HeuristicGCDFailed, HeuristicGCDFailed()): check(c) for c in (HomomorphismFailed, HomomorphismFailed()): check(c) for c in (IsomorphismFailed, IsomorphismFailed()): check(c) for c in (ExtraneousFactors, ExtraneousFactors()): check(c) for c in (EvaluationFailed, EvaluationFailed()): check(c) for c in (RefinementFailed, RefinementFailed()): check(c) for c in (CoercionFailed, CoercionFailed()): check(c) for c in (NotInvertible, NotInvertible()): check(c) for c in (NotReversible, NotReversible()): check(c) for c in (NotAlgebraic, NotAlgebraic()): check(c) for c in (DomainError, DomainError()): check(c) for c in (PolynomialError, PolynomialError()): check(c) for c in (UnificationFailed, UnificationFailed()): check(c) for c in (GeneratorsError, GeneratorsError()): check(c) for c in (GeneratorsNeeded, GeneratorsNeeded()): check(c) # TODO: PicklingError: Can't pickle <function <lambda> at 0x38578c0>: it's not found as __main__.<lambda> # for c in (ComputationFailed, ComputationFailed(lambda t: t, 3, None)): # check(c) for c in (UnivariatePolynomialError, UnivariatePolynomialError()): check(c) for c in (MultivariatePolynomialError, MultivariatePolynomialError()): check(c) # TODO: TypeError: __init__() takes at least 3 arguments (1 given) # for c in (PolificationFailed, PolificationFailed({}, x, x, False)): # check(c) for c in (OptionError, OptionError()): check(c) for c in (FlagError, FlagError()): check(c)
def dmp_gf_factor(f, u, K): """Factor multivariate polynomials over finite fields. """ raise DomainError('multivariate polynomials over %s' % K)
def dmp_factor_list(f, u, K0): """Factor polynomials into irreducibles in `K[X]`. """ if not u: return dup_factor_list(f, K0) J, f = dmp_terms_gcd(f, u, K0) if not K0.has_CharacteristicZero: # pragma: no cover coeff, factors = dmp_gf_factor(f, u, K0) elif K0.is_Algebraic: coeff, factors = dmp_ext_factor(f, u, K0) else: if not K0.is_Exact: K0_inexact, K0 = K0, K0.get_exact() f = dmp_convert(f, u, K0_inexact, K0) else: K0_inexact = None if K0.has_Field: K = K0.get_ring() denom, f = dmp_clear_denoms(f, u, K0, K) f = dmp_convert(f, u, K0, K) else: K = K0 if K.is_ZZ: levels, f, v = dmp_exclude(f, u, K) coeff, factors = dmp_zz_factor(f, v, K) for i, (f, k) in enumerate(factors): factors[i] = (dmp_include(f, levels, v, K), k) elif K.is_Poly: f, v = dmp_inject(f, u, K) coeff, factors = dmp_factor_list(f, v, K.dom) for i, (f, k) in enumerate(factors): factors[i] = (dmp_eject(f, v, K), k) coeff = K.convert(coeff, K.dom) else: # pragma: no cover raise DomainError('factorization not supported over %s' % K0) if K0.has_Field: for i, (f, k) in enumerate(factors): factors[i] = (dmp_convert(f, u, K, K0), k) coeff = K0.convert(coeff, K) denom = K0.convert(denom, K) coeff = K0.quo(coeff, denom) if K0_inexact is not None: for i, (f, k) in enumerate(factors): factors[i] = (dmp_convert(f, u, K0, K0_inexact), k) coeff = K0_inexact.convert(coeff, K0) for i, j in enumerate(reversed(J)): if not j: continue term = {(0,)*(u-i) + (1,) + (0,)*i: K0.one} factors.insert(0, (dmp_from_dict(term, u, K0), j)) return coeff, _sort_factors(factors)
def rs_cos(p, x, prec): """ Cosine of a series Returns the series expansion of the cos of p, about 0. Examples ======== >>> from sympy.polys.domains import QQ >>> from sympy.polys.rings import ring >>> from sympy.polys.ring_series import rs_cos >>> R, x, y = ring('x, y', QQ) >>> rs_cos(x + x*y, x, 4) -1/2*x**2*y**2 - x**2*y - 1/2*x**2 + 1 >>> rs_cos(x + x*y, x, 4)/x**QQ(7, 5) -1/2*x**(3/5)*y**2 - x**(3/5)*y - 1/2*x**(3/5) + x**(-7/5) See Also ======== cos """ if rs_is_puiseux(p, x): return rs_puiseux(rs_cos, p, x, prec) R = p.ring if _has_constant_term(p, x): zm = R.zero_monom c = p[zm] if R.domain is EX: c_expr = c.as_expr() t1, t2 = sin(c_expr), cos(c_expr) elif isinstance(c, PolyElement): try: c_expr = c.as_expr() t1, t2 = R(sin(c_expr)), R(cos(c_expr)) except ValueError: raise DomainError("The given series can't be expanded in " "this domain.") else: try: t1, t2 = R(sin(c)), R(cos(c)) except ValueError: raise DomainError("The given series can't be expanded in " "this domain.") p1 = p - c # Makes use of sympy cos, sin fuctions to evaluate the values of the # cos/sin of the constant term. return rs_cos(p1, x, prec) * t2 - rs_sin(p1, x, prec) * t1 # Series is calculated in terms of tan as its evaluation is fast. if len(p) > 20 and R.ngens == 1: t = rs_tan(p / 2, x, prec) t2 = rs_square(t, x, prec) p1 = rs_series_inversion(1 + t2, x, prec) return rs_mul(p1, 1 - t2, x, prec) one = R(1) n = 1 c = [] for k in range(2, prec + 2, 2): c.append(one / n) c.append(0) n *= -k * (k - 1) return rs_series_from_list(p, c, x, prec)
def rs_nth_root(p, n, x, prec): """ Multivariate series expansion of the nth root of p n(integer): compute p**(1/n) x: variable name prec: precision of the series Notes ===== The result of this function is dependent on the ring over which the polynomial has been defined. If the answer involves a root of a constant, make sure that the polynomial is over a real field. It can not yet handle roots of symbols. Examples ======== >>> from sympy.polys.domains import QQ, RR >>> from sympy.polys.rings import ring >>> from sympy.polys.ring_series import rs_nth_root >>> R, x, y = ring('x, y', QQ) >>> rs_nth_root(1 + x + x*y, -3, x, 3) 2/9*x**2*y**2 + 4/9*x**2*y + 2/9*x**2 - 1/3*x*y - 1/3*x + 1 >>> R, x, y = ring('x, y', RR) >>> rs_nth_root(3 + x + x*y, 3, x, 2) 0.160249952256379*x*y + 0.160249952256379*x + 1.44224957030741 """ p0 = p n0 = n if n == 0: if p == 0: raise ValueError('0**0 expression') else: return p.ring(1) if n == 1: return rs_trunc(p, x, prec) R = p.ring zm = R.zero_monom index = R.gens.index(x) m = min(p, key=lambda k: k[index])[index] p = mul_xin(p, index, -m) prec -= m if _has_constant_term(p - 1, x): zm = R.zero_monom c = p[zm] if R.domain is EX: c_expr = c.as_expr() const = c_expr**QQ(1, n) elif isinstance(c, PolyElement): try: c_expr = c.as_expr() const = R(c_expr**(QQ(1, n))) except ValueError: raise DomainError("The given series can't be expanded in " "this domain.") else: try: # RealElement doesn't support const = R(c**Rational(1, n)) # exponentiation with mpq object except ValueError: # as exponent raise DomainError("The given series can't be expanded in " "this domain.") res = rs_nth_root(p / c, n, x, prec) * const else: res = _nth_root1(p, n, x, prec) if m: m = QQ(m, n) res = mul_xin(res, index, m) return res
def dup_factor_list(f, K0): """Factor univariate polynomials into irreducibles in `K[x]`. """ j, f = dup_terms_gcd(f, K0) cont, f = dup_primitive(f, K0) if K0.is_FiniteField: coeff, factors = dup_gf_factor(f, K0) elif K0.is_Algebraic: coeff, factors = dup_ext_factor(f, K0) elif K0.is_GaussianRing: coeff, factors = dup_zz_i_factor(f, K0) elif K0.is_GaussianField: coeff, factors = dup_qq_i_factor(f, K0) else: if not K0.is_Exact: K0_inexact, K0 = K0, K0.get_exact() f = dup_convert(f, K0_inexact, K0) else: K0_inexact = None if K0.is_Field: K = K0.get_ring() denom, f = dup_clear_denoms(f, K0, K) f = dup_convert(f, K0, K) else: K = K0 if K.is_ZZ: coeff, factors = dup_zz_factor(f, K) elif K.is_Poly: f, u = dmp_inject(f, 0, K) coeff, factors = dmp_factor_list(f, u, K.dom) for i, (f, k) in enumerate(factors): factors[i] = (dmp_eject(f, u, K), k) coeff = K.convert(coeff, K.dom) else: # pragma: no cover raise DomainError('factorization not supported over %s' % K0) if K0.is_Field: for i, (f, k) in enumerate(factors): factors[i] = (dup_convert(f, K, K0), k) coeff = K0.convert(coeff, K) coeff = K0.quo(coeff, denom) if K0_inexact: for i, (f, k) in enumerate(factors): max_norm = dup_max_norm(f, K0) f = dup_quo_ground(f, max_norm, K0) f = dup_convert(f, K0, K0_inexact) factors[i] = (f, k) coeff = K0.mul(coeff, K0.pow(max_norm, k)) coeff = K0_inexact.convert(coeff, K0) K0 = K0_inexact if j: factors.insert(0, ([K0.one, K0.zero], j)) return coeff * cont, _sort_factors(factors)
def dmp_factor_list(f, u, K0): """Factor multivariate polynomials into irreducibles in `K[X]`. """ if not u: return dup_factor_list(f, K0) J, f = dmp_terms_gcd(f, u, K0) cont, f = dmp_ground_primitive(f, u, K0) if K0.is_FiniteField: # pragma: no cover coeff, factors = dmp_gf_factor(f, u, K0) elif K0.is_Algebraic: coeff, factors = dmp_ext_factor(f, u, K0) elif K0.is_GaussianRing: coeff, factors = dmp_zz_i_factor(f, u, K0) elif K0.is_GaussianField: coeff, factors = dmp_qq_i_factor(f, u, K0) else: if not K0.is_Exact: K0_inexact, K0 = K0, K0.get_exact() f = dmp_convert(f, u, K0_inexact, K0) else: K0_inexact = None if K0.is_Field: K = K0.get_ring() denom, f = dmp_clear_denoms(f, u, K0, K) f = dmp_convert(f, u, K0, K) else: K = K0 if K.is_ZZ: levels, f, v = dmp_exclude(f, u, K) coeff, factors = dmp_zz_factor(f, v, K) for i, (f, k) in enumerate(factors): factors[i] = (dmp_include(f, levels, v, K), k) elif K.is_Poly: f, v = dmp_inject(f, u, K) coeff, factors = dmp_factor_list(f, v, K.dom) for i, (f, k) in enumerate(factors): factors[i] = (dmp_eject(f, v, K), k) coeff = K.convert(coeff, K.dom) else: # pragma: no cover raise DomainError('factorization not supported over %s' % K0) if K0.is_Field: for i, (f, k) in enumerate(factors): factors[i] = (dmp_convert(f, u, K, K0), k) coeff = K0.convert(coeff, K) coeff = K0.quo(coeff, denom) if K0_inexact: for i, (f, k) in enumerate(factors): max_norm = dmp_max_norm(f, u, K0) f = dmp_quo_ground(f, max_norm, u, K0) f = dmp_convert(f, u, K0, K0_inexact) factors[i] = (f, k) coeff = K0.mul(coeff, K0.pow(max_norm, k)) coeff = K0_inexact.convert(coeff, K0) K0 = K0_inexact for i, j in enumerate(reversed(J)): if not j: continue term = {(0, ) * (u - i) + (1, ) + (0, ) * i: K0.one} factors.insert(0, (dmp_from_dict(term, u, K0), j)) return coeff * cont, _sort_factors(factors)
def ratsimpmodprime(expr, G, *gens, quick=True, polynomial=False, **args): """ Simplifies a rational expression ``expr`` modulo the prime ideal generated by ``G``. ``G`` should be a Groebner basis of the ideal. >>> from sympy.simplify.ratsimp import ratsimpmodprime >>> from sympy.abc import x, y >>> eq = (x + y**5 + y)/(x - y) >>> ratsimpmodprime(eq, [x*y**5 - x - y], x, y, order='lex') (-x**2 - x*y - x - y)/(-x**2 + x*y) If ``polynomial`` is False, the algorithm computes a rational simplification which minimizes the sum of the total degrees of the numerator and the denominator. If ``polynomial`` is True, this function just brings numerator and denominator into a canonical form. This is much faster, but has potentially worse results. References ========== .. [1] M. Monagan, R. Pearce, Rational Simplification Modulo a Polynomial Ideal, http://citeseer.ist.psu.edu/viewdoc/summary?doi=10.1.1.163.6984 (specifically, the second algorithm) """ from sympy import solve debug('ratsimpmodprime', expr) # usual preparation of polynomials: num, denom = cancel(expr).as_numer_denom() try: polys, opt = parallel_poly_from_expr([num, denom] + G, *gens, **args) except PolificationFailed: return expr domain = opt.domain if domain.has_assoc_Field: opt.domain = domain.get_field() else: raise DomainError("can't compute rational simplification over %s" % domain) # compute only once leading_monomials = [g.LM(opt.order) for g in polys[2:]] tested = set() def staircase(n): """ Compute all monomials with degree less than ``n`` that are not divisible by any element of ``leading_monomials``. """ if n == 0: return [1] S = [] for mi in combinations_with_replacement(range(len(opt.gens)), n): m = [0] * len(opt.gens) for i in mi: m[i] += 1 if all([monomial_div(m, lmg) is None for lmg in leading_monomials]): S.append(m) return [Monomial(s).as_expr(*opt.gens) for s in S] + staircase(n - 1) def _ratsimpmodprime(a, b, allsol, N=0, D=0): r""" Computes a rational simplification of ``a/b`` which minimizes the sum of the total degrees of the numerator and the denominator. The algorithm proceeds by looking at ``a * d - b * c`` modulo the ideal generated by ``G`` for some ``c`` and ``d`` with degree less than ``a`` and ``b`` respectively. The coefficients of ``c`` and ``d`` are indeterminates and thus the coefficients of the normalform of ``a * d - b * c`` are linear polynomials in these indeterminates. If these linear polynomials, considered as system of equations, have a nontrivial solution, then `\frac{a}{b} \equiv \frac{c}{d}` modulo the ideal generated by ``G``. So, by construction, the degree of ``c`` and ``d`` is less than the degree of ``a`` and ``b``, so a simpler representation has been found. After a simpler representation has been found, the algorithm tries to reduce the degree of the numerator and denominator and returns the result afterwards. As an extension, if quick=False, we look at all possible degrees such that the total degree is less than *or equal to* the best current solution. We retain a list of all solutions of minimal degree, and try to find the best one at the end. """ c, d = a, b steps = 0 maxdeg = a.total_degree() + b.total_degree() if quick: bound = maxdeg - 1 else: bound = maxdeg while N + D <= bound: if (N, D) in tested: break tested.add((N, D)) M1 = staircase(N) M2 = staircase(D) debug('%s / %s: %s, %s' % (N, D, M1, M2)) Cs = symbols("c:%d" % len(M1), cls=Dummy) Ds = symbols("d:%d" % len(M2), cls=Dummy) ng = Cs + Ds c_hat = Poly(sum([Cs[i] * M1[i] for i in range(len(M1))]), opt.gens + ng) d_hat = Poly(sum([Ds[i] * M2[i] for i in range(len(M2))]), opt.gens + ng) r = reduced(a * d_hat - b * c_hat, G, opt.gens + ng, order=opt.order, polys=True)[1] S = Poly(r, gens=opt.gens).coeffs() sol = solve(S, Cs + Ds, particular=True, quick=True) if sol and not all([s == 0 for s in sol.values()]): c = c_hat.subs(sol) d = d_hat.subs(sol) # The "free" variables occurring before as parameters # might still be in the substituted c, d, so set them # to the value chosen before: c = c.subs(dict(list(zip(Cs + Ds, [1] * (len(Cs) + len(Ds)))))) d = d.subs(dict(list(zip(Cs + Ds, [1] * (len(Cs) + len(Ds)))))) c = Poly(c, opt.gens) d = Poly(d, opt.gens) if d == 0: raise ValueError('Ideal not prime?') allsol.append((c_hat, d_hat, S, Cs + Ds)) if N + D != maxdeg: allsol = [allsol[-1]] break steps += 1 N += 1 D += 1 if steps > 0: c, d, allsol = _ratsimpmodprime(c, d, allsol, N, D - steps) c, d, allsol = _ratsimpmodprime(c, d, allsol, N - steps, D) return c, d, allsol # preprocessing. this improves performance a bit when deg(num) # and deg(denom) are large: num = reduced(num, G, opt.gens, order=opt.order)[1] denom = reduced(denom, G, opt.gens, order=opt.order)[1] if polynomial: return (num / denom).cancel() c, d, allsol = _ratsimpmodprime(Poly(num, opt.gens, domain=opt.domain), Poly(denom, opt.gens, domain=opt.domain), []) if not quick and allsol: debug('Looking for best minimal solution. Got: %s' % len(allsol)) newsol = [] for c_hat, d_hat, S, ng in allsol: sol = solve(S, ng, particular=True, quick=False) newsol.append((c_hat.subs(sol), d_hat.subs(sol))) c, d = min(newsol, key=lambda x: len(x[0].terms()) + len(x[1].terms())) if not domain.is_Field: cn, c = c.clear_denoms(convert=True) dn, d = d.clear_denoms(convert=True) r = Rational(cn, dn) else: r = Rational(1) return (c * r.q) / (d * r.p)
def get_exact(self): """Returns an exact domain associated with ``self``. """ raise DomainError("there is no exact domain associated with %s" % self)
def f5b(F, u, O, K, gens='', verbose=False): """ Computes a reduced Groebner basis for the ideal generated by F. f5b is an implementation of the F5B algorithm by Yao Sun and Dingkang Wang. Similarly to Buchberger's algorithm, the algorithm proceeds by computing critical pairs, computing the S-polynomial, reducing it and adjoining the reduced S-polynomial if it is not 0. Unlike Buchberger's algorithm, each polynomial contains additional information, namely a signature and a number. The signature specifies the path of computation (i.e. from which polynomial in the original basis was it derived and how), the number says when the polynomial was added to the basis. With this information it is (often) possible to decide if an S-polynomial will reduce to 0 and can be discarded. Optimizations include: Reducing the generators before computing a Groebner basis, removing redundant critical pairs when a new polynomial enters the basis and sorting the critical pairs and the current basis. Once a Groebner basis has been found, it gets reduced. ** References ** Yao Sun, Dingkang Wang: "A New Proof for the Correctness of F5 (F5-Like) Algorithm", http://arxiv.org/abs/1004.0084 (specifically v4) Thomas Becker, Volker Weispfenning, Groebner bases: A computational approach to commutative algebra, 1993, p. 203, 216 """ if not K.has_Field: raise DomainError("can't compute a Groebner basis over %s" % K) # reduce polynomials (like in Mario Pernici's implementation) (Becker, Weispfenning, p. 203) B = F while True: F = B B = [] for i in xrange(len(F)): p = F[i] r = sdp_rem(p, F[:i], u, O, K) if r != []: B.append(r) if F == B: break # basis B = [lbp(sig((0, ) * (u + 1), i + 1), F[i], i + 1) for i in xrange(len(F))] B.sort(key=lambda f: O(sdp_LM(Polyn(f), u)), reverse=True) # critical pairs CP = [ critical_pair(B[i], B[j], u, O, K) for i in xrange(len(B)) for j in xrange(i + 1, len(B)) ] CP.sort(key=lambda cp: cp_key(cp, O), reverse=True) k = len(B) reductions_to_zero = 0 while len(CP): cp = CP.pop() # discard redundant critical pairs: if is_rewritable_or_comparable(cp[0], Num(cp[2]), B, u, K): continue if is_rewritable_or_comparable(cp[3], Num(cp[5]), B, u, K): continue s = s_poly(cp, u, O, K) p = f5_reduce(s, B, u, O, K) p = lbp(Sign(p), sdp_monic(Polyn(p), K), k + 1) if Polyn(p) != []: # remove old critical pairs, that become redundant when adding p: indices = [] for i, cp in enumerate(CP): if is_rewritable_or_comparable(cp[0], Num(cp[2]), [p], u, K): indices.append(i) elif is_rewritable_or_comparable(cp[3], Num(cp[5]), [p], u, K): indices.append(i) for i in reversed(indices): del CP[i] # only add new critical pairs that are not made redundant by p: for g in B: if Polyn(g) != []: cp = critical_pair(p, g, u, O, K) if is_rewritable_or_comparable(cp[0], Num(cp[2]), [p], u, K): continue elif is_rewritable_or_comparable(cp[3], Num(cp[5]), [p], u, K): continue CP.append(cp) # sort (other sorting methods/selection strategies were not as successful) CP.sort(key=lambda cp: cp_key(cp, O), reverse=True) # insert p into B: m = sdp_LM(Polyn(p), u) if O(m) <= O(sdp_LM(Polyn(B[-1]), u)): B.append(p) else: for i, q in enumerate(B): if O(m) > O(sdp_LM(Polyn(q), u)): B.insert(i, p) break k += 1 #print(len(B), len(CP), "%d critical pairs removed" % len(indices)) else: reductions_to_zero += 1 if verbose: print("%d reductions to zero" % reductions_to_zero) # reduce Groebner basis: H = [sdp_monic(Polyn(g), K) for g in B] H = red_groebner(H, u, O, K) return sorted(H, key=lambda f: O(sdp_LM(f, u)), reverse=True)
def dmp_gf_sqf_list(f, u, K, all=False): """Compute square-free decomposition of ``f`` in ``GF(p)[X]``. """ raise DomainError('multivariate polynomials over %s' % K)
def dmp_gf_sqf_part(f, K): """Compute square-free part of ``f`` in ``GF(p)[X]``. """ raise DomainError('multivariate polynomials over %s' % K)