def select(P): # normal selection strategy # select the pair with minimum LCM(LM(f), LM(g)) pr = min( P, key=lambda pair: order(monomial_lcm(f[pair[0]].LM, f[pair[1]].LM))) return pr
def critical_pair(f, g, ring): """ Compute the critical pair corresponding to two labeled polynomials. A critical pair is a tuple (um, f, vm, g), where um and vm are terms such that um * f - vm * g is the S-polynomial of f and g (so, wlog assume um * f > vm * g). For performance sake, a critical pair is represented as a tuple (Sign(um * f), um, f, Sign(vm * g), vm, g), since um * f creates a new, relatively expensive object in memory, whereas Sign(um * f) and um are lightweight and f (in the tuple) is a reference to an already existing object in memory. """ domain = ring.domain ltf = Polyn(f).LT ltg = Polyn(g).LT lt = (monomial_lcm(ltf[0], ltg[0]), domain.one) um = term_div(lt, ltf, domain) vm = term_div(lt, ltg, domain) # The full information is not needed (now), so only the product # with the leading term is considered: fr = lbp_mul_term(lbp(Sign(f), Polyn(f).leading_term(), Num(f)), um) gr = lbp_mul_term(lbp(Sign(g), Polyn(g).leading_term(), Num(g)), vm) # return in proper order, such that the S-polynomial is just # u_first * f_first - u_second * f_second: if lbp_cmp(fr, gr) == -1: return (Sign(gr), vm, g, Sign(fr), um, f) else: return (Sign(fr), um, f, Sign(gr), vm, g)
def groebner_lcm(f, g): """ Computes LCM of two polynomials using Groebner bases. The LCM is computed as the unique generator of the intersection of the two ideals generated by `f` and `g`. The approach is to compute a Groebner basis with respect to lexicographic ordering of `t*f` and `(1 - t)*g`, where `t` is an unrelated variable and then filtering out the solution that doesn't contain `t`. References ========== .. [1] [Cox97]_ """ if f.ring != g.ring: raise ValueError("Values should be equal") ring = f.ring domain = ring.domain if not f or not g: return ring.zero if len(f) <= 1 and len(g) <= 1: monom = monomial_lcm(f.LM, g.LM) coeff = domain.lcm(f.LC, g.LC) return ring.term_new(monom, coeff) fc, f = f.primitive() gc, g = g.primitive() lcm = domain.lcm(fc, gc) f_terms = [((1, ) + monom, coeff) for monom, coeff in f.terms()] g_terms = [((0, ) + monom, coeff) for monom, coeff in g.terms()] + [((1, ) + monom, -coeff) for monom, coeff in g.terms()] t = Dummy("t") t_ring = ring.clone(symbols=(t, ) + ring.symbols, order=lex) F = t_ring.from_terms(f_terms) G = t_ring.from_terms(g_terms) basis = groebner([F, G], t_ring) def is_independent(h, j): return all(not monom[j] for monom in h.monoms()) H = [h for h in basis if is_independent(h, 0)] h_terms = [(monom[1:], coeff * lcm) for monom, coeff in H[0].terms()] h = ring.from_terms(h_terms) return h
def groebner_lcm(f, g): """ Computes LCM of two polynomials using Groebner bases. The LCM is computed as the unique generator of the intersection of the two ideals generated by `f` and `g`. The approach is to compute a Groebner basis with respect to lexicographic ordering of `t*f` and `(1 - t)*g`, where `t` is an unrelated variable and then filtering out the solution that doesn't contain `t`. References ========== 1. [Cox97]_ """ if f.ring != g.ring: raise ValueError("Values should be equal") ring = f.ring domain = ring.domain if not f or not g: return ring.zero if len(f) <= 1 and len(g) <= 1: monom = monomial_lcm(f.LM, g.LM) coeff = domain.lcm(f.LC, g.LC) return ring.term_new(monom, coeff) fc, f = f.primitive() gc, g = g.primitive() lcm = domain.lcm(fc, gc) f_terms = [ ((1,) + monom, coeff) for monom, coeff in f.terms() ] g_terms = [ ((0,) + monom, coeff) for monom, coeff in g.terms() ] \ + [ ((1,) + monom,-coeff) for monom, coeff in g.terms() ] t = Dummy("t") t_ring = ring.clone(symbols=(t,) + ring.symbols, order=lex) F = t_ring.from_terms(f_terms) G = t_ring.from_terms(g_terms) basis = groebner([F, G], t_ring) def is_independent(h, j): return all(not monom[j] for monom in h.monoms()) H = [ h for h in basis if is_independent(h, 0) ] h_terms = [ (monom[1:], coeff*lcm) for monom, coeff in H[0].terms() ] h = ring.from_terms(h_terms) return h
def sdm_spoly(f, g, O, K, phantom=None): """ Compute the generalized s-polynomial of ``f`` and ``g``. The ground field is assumed to be ``K``, and monomials ordered according to ``O``. This is invalid if either of ``f`` or ``g`` is zero. If the leading terms of `f` and `g` involve different basis elements of `F`, their s-poly is defined to be zero. Otherwise it is a certain linear combination of `f` and `g` in which the leading terms cancel. See [SCA, defn 2.3.6] for details. If ``phantom`` is not ``None``, it should be a pair of module elements on which to perform the same operation(s) as on ``f`` and ``g``. The in this case both results are returned. Examples ======== >>> from sympy.polys.distributedmodules import sdm_spoly >>> from sympy.polys import QQ, lex >>> f = [((2, 1, 1), QQ(1)), ((1, 0, 1), QQ(1))] >>> g = [((2, 3, 0), QQ(1))] >>> h = [((1, 2, 3), QQ(1))] >>> sdm_spoly(f, h, lex, QQ) [] >>> sdm_spoly(f, g, lex, QQ) [((1, 2, 1), 1)] """ if not f or not g: return sdm_zero() LM1 = sdm_LM(f) LM2 = sdm_LM(g) if LM1[0] != LM2[0]: return sdm_zero() LM1 = LM1[1:] LM2 = LM2[1:] lcm = monomial_lcm(LM1, LM2) m1 = monomial_div(lcm, LM1) m2 = monomial_div(lcm, LM2) c = K.quo(-sdm_LC(f, K), sdm_LC(g, K)) r1 = sdm_add(sdm_mul_term(f, (m1, K.one), O, K), sdm_mul_term(g, (m2, c), O, K), O, K) if phantom is None: return r1 r2 = sdm_add( sdm_mul_term(phantom[0], (m1, K.one), O, K), sdm_mul_term(phantom[1], (m2, c), O, K), O, K, ) return r1, r2
def sdm_spoly(f, g, O, K, phantom=None): """ Compute the generalized s-polynomial of ``f`` and ``g``. The ground field is assumed to be ``K``, and monomials ordered according to ``O``. This is invalid if either of ``f`` or ``g`` is zero. If the leading terms of `f` and `g` involve different basis elements of `F`, their s-poly is defined to be zero. Otherwise it is a certain linear combination of `f` and `g` in which the leading terms cancel. See [SCA, defn 2.3.6] for details. If ``phantom`` is not ``None``, it should be a pair of module elements on which to perform the same operation(s) as on ``f`` and ``g``. The in this case both results are returned. Examples ======== >>> from sympy.polys.distributedmodules import sdm_spoly >>> from sympy.polys import QQ, lex >>> f = [((2, 1, 1), QQ(1)), ((1, 0, 1), QQ(1))] >>> g = [((2, 3, 0), QQ(1))] >>> h = [((1, 2, 3), QQ(1))] >>> sdm_spoly(f, h, lex, QQ) [] >>> sdm_spoly(f, g, lex, QQ) [((1, 2, 1), 1)] """ if not f or not g: return sdm_zero() LM1 = sdm_LM(f) LM2 = sdm_LM(g) if LM1[0] != LM2[0]: return sdm_zero() LM1 = LM1[1:] LM2 = LM2[1:] lcm = monomial_lcm(LM1, LM2) m1 = monomial_div(lcm, LM1) m2 = monomial_div(lcm, LM2) c = K.quo(-sdm_LC(f, K), sdm_LC(g, K)) r1 = sdm_add(sdm_mul_term(f, (m1, K.one), O, K), sdm_mul_term(g, (m2, c), O, K), O, K) if phantom is None: return r1 r2 = sdm_add(sdm_mul_term(phantom[0], (m1, K.one), O, K), sdm_mul_term(phantom[1], (m2, c), O, K), O, K) return r1, r2
def sdm_monomial_lcm(A, B): """ Return the "least common multiple" of ``A`` and ``B``. IF `A = M e_j` and `B = N e_j`, where `M` and `N` are polynomial monomials, this returns `\lcm(M, N) e_j`. Note that ``A`` and ``B`` involve distinct monomials. Otherwise the result is undefined. >>> from sympy.polys.distributedmodules import sdm_monomial_lcm >>> sdm_monomial_lcm((1, 2, 3), (1, 0, 5)) (1, 2, 5) """ return (A[0], ) + monomial_lcm(A[1:], B[1:])
def sdm_monomial_lcm(A, B): """ Return the "least common multiple" of ``A`` and ``B``. IF `A = M e_j` and `B = N e_j`, where `M` and `N` are polynomial monomials, this returns `\lcm(M, N) e_j`. Note that ``A`` and ``B`` involve distinct monomials. Otherwise the result is undefined. >>> from sympy.polys.distributedmodules import sdm_monomial_lcm >>> sdm_monomial_lcm((1, 2, 3), (1, 0, 5)) (1, 2, 5) """ return (A[0],) + monomial_lcm(A[1:], B[1:])
def test_monomial_lcm(): assert monomial_lcm((3, 4, 1), (1, 2, 0)) == (3, 4, 1)
def lcm_divides(ip): # LCM(LM(h), LM(p)) divides LCM(LM(h), LM(g)) m = monomial_lcm(mh, f[ip].LM) return monomial_div(LCMhg, m)
def update(G, B, ih): # update G using the set of critical pairs B and h # [BW] page 230 h = f[ih] mh = h.LM # 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 = g.LM LCMhg = monomial_lcm(mh, mg) def lcm_divides(ip): # LCM(LM(h), LM(p)) divides LCM(LM(h), LM(g)) m = monomial_lcm(mh, f[ip].LM) 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 = f[ig].LM 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 = f[ig1].LM mg2 = f[ig2].LM 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 = f[ig].LM if not monomial_div(mg, mh): G_new.add(ig) G_new.add(ih) return G_new, B_new
def select(P): # normal selection strategy # select the pair with minimum LCM(LM(f), LM(g)) pr = min(P, key=lambda pair: order(monomial_lcm(f[pair[0]].LM, f[pair[1]].LM))) return pr
def update(G, B, ih): # update G using the set of critical pairs B and h # [BW] page 230 h = f[ih] mh = h.LM # 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 = g.LM LCMhg = monomial_lcm(mh, mg) def lcm_divides(ip): # LCM(LM(h), LM(p)) divides LCM(LM(h), LM(g)) m = monomial_lcm(mh, f[ip].LM) 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 = f[ig].LM 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 = f[ig1].LM mg2 = f[ig2].LM 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 = f[ig].LM if not monomial_div(mg, mh): G_new.add(ig) G_new.add(ih) return G_new, B_new