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 dmp_terms_gcd(f, u, K): """ Remove GCD of terms from ``f`` in ``K[X]``. Examples ======== >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densebasic import dmp_terms_gcd >>> f = ZZ.map([[1, 0], [1, 0, 0], [], []]) >>> dmp_terms_gcd(f, 1, ZZ) ((2, 1), [[1], [1, 0]]) """ if dmp_ground_TC(f, u, K) or dmp_zero_p(f, u): return (0,)*(u + 1), f F = dmp_to_dict(f, u) G = monomial_min(*list(F.keys())) if all(g == 0 for g in G): return G, f f = {} for monom, coeff in F.items(): f[monomial_div(monom, G)] = coeff return G, dmp_from_dict(f, u, K)
def _basis(G, ring): """ Computes a list of monomials which are not divisible by the leading monomials wrt to ``O`` of ``G``. These monomials are a basis of `K[X_1, \ldots, X_n]/(G)`. """ order = ring.order leading_monomials = [g.LM for g in G] candidates = [ring.zero_monom] basis = [] while candidates: t = candidates.pop() basis.append(t) new_candidates = [ _incr_k(t, k) for k in xrange(ring.ngens) if all( monomial_div(_incr_k(t, k), lmg) is None for lmg in leading_monomials) ] candidates.extend(new_candidates) candidates.sort(key=lambda m: order(m), reverse=True) basis = list(set(basis)) return sorted(basis, key=lambda m: order(m))
def _basis(G, ring): r""" Computes a list of monomials which are not divisible by the leading monomials wrt to ``O`` of ``G``. These monomials are a basis of `K[X_1, \ldots, X_n]/(G)`. """ order = ring.order leading_monomials = [g.LM for g in G] candidates = [ring.zero_monom] basis = [] while candidates: t = candidates.pop() basis.append(t) new_candidates = [_incr_k(t, k) for k in range(ring.ngens) if all(monomial_div(_incr_k(t, k), lmg) is None for lmg in leading_monomials)] candidates.extend(new_candidates) candidates.sort(key=lambda m: order(m), reverse=True) basis = list(set(basis)) return sorted(basis, key=lambda m: order(m))
def rs_diff(p, x): """ Computes partial derivative of p with respect to x `x`: variable with respect to which p is differentiated, Examples ======== >>> from sympy.polys.domains import QQ >>> from sympy.polys.rings import ring >>> from sympy.polys.ring_series import rs_diff >>> R, x, y = ring('x, y', QQ) >>> p = x + x**2*y**3 >>> rs_diff(p, x) 2*x*y**3 + 1 """ ring = p.ring n = ring.gens.index(x) p1 = ring.zero mn = [0]*ring.ngens mn[n] = 1 mn = tuple(mn) for expv in p: if expv[n]: e = monomial_div(expv, mn) p1[e] = p[expv]*expv[n] return p1
def dmp_terms_gcd(f, u, K): """ Remove GCD of terms from ``f`` in ``K[X]``. Examples ======== >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densebasic import dmp_terms_gcd >>> f = ZZ.map([[1, 0], [1, 0, 0], [], []]) >>> dmp_terms_gcd(f, 1, ZZ) ((2, 1), [[1], [1, 0]]) """ if dmp_ground_TC(f, u, K) or dmp_zero_p(f, u): return (0, ) * (u + 1), f F = dmp_to_dict(f, u) G = monomial_min(*list(F.keys())) if all(g == 0 for g in G): return G, f f = {} for monom, coeff in F.items(): f[monomial_div(monom, G)] = coeff return G, dmp_from_dict(f, u, K)
def rs_diff(p, x): """ Computes partial derivative of p with respect to x `x`: variable with respect to which p is differentiated, Examples ======== >>> from sympy.polys.domains import QQ >>> from sympy.polys.rings import ring >>> from sympy.polys.ring_series import rs_diff >>> R, x, y = ring('x, y', QQ) >>> p = x + x**2*y**3 >>> rs_diff(p, x) 2*x*y**3 + 1 """ R = p.ring n = R.gens.index(x) p1 = R.zero mn = [0] * R.ngens mn[n] = 1 mn = tuple(mn) for expv in p: if expv[n]: e = monomial_div(expv, mn) p1[e] = p[expv] * expv[n] return p1
def _coefficient_t(p, t): """Coefficient of `x_i**j` in p, where t = (i, j)""" i, j = t R = p.ring expv1 = [0] * R.ngens expv1[i] = j expv1 = tuple(expv1) p1 = R(0) for expv in p: if expv[i] == j: p1[monomial_div(expv, expv1)] = p[expv] return p1
def _coefficient_t(p, t): """Coefficient of `x_i**j` in p, where t = (i, j)""" i, j = t R = p.ring expv1 = [0]*R.ngens expv1[i] = j expv1 = tuple(expv1) p1 = R(0) for expv in p: if expv[i] == j: p1[monomial_div(expv, expv1)] = p[expv] return p1
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 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 matrix_fglm(F, ring, O_to): """ Converts the reduced Groebner basis ``F`` of a zero-dimensional ideal w.r.t. ``O_from`` to a reduced Groebner basis w.r.t. ``O_to``. References ========== J.C. Faugere, P. Gianni, D. Lazard, T. Mora (1994). Efficient Computation of Zero-dimensional Groebner Bases by Change of Ordering """ domain = ring.domain ngens = ring.ngens ring_to = ring.clone(order=O_to) old_basis = _basis(F, ring) M = _representing_matrices(old_basis, F, ring) # V contains the normalforms (wrt O_from) of S S = [ring.zero_monom] V = [[domain.one] + [domain.zero] * (len(old_basis) - 1)] G = [] L = [(i, 0) for i in xrange(ngens)] # (i, j) corresponds to x_i * S[j] L.sort(key=lambda k_l: O_to(_incr_k(S[k_l[1]], k_l[0])), reverse=True) t = L.pop() P = _identity_matrix(len(old_basis), domain) while True: s = len(S) v = _matrix_mul(M[t[0]], V[t[1]]) _lambda = _matrix_mul(P, v) if all(_lambda[i] == domain.zero for i in xrange(s, len(old_basis))): # there is a linear combination of v by V lt = ring.term_new(_incr_k(S[t[1]], t[0]), domain.one) rest = ring.from_dict(dict([(S[i], _lambda[i]) for i in xrange(s)])) g = (lt - rest).set_ring(ring_to) if g: G.append(g) else: # v is linearly independant from V P = _update(s, _lambda, P) S.append(_incr_k(S[t[1]], t[0])) V.append(v) L.extend([(i, s) for i in xrange(ngens)]) L = list(set(L)) L.sort(key=lambda k_l: O_to(_incr_k(S[k_l[1]], k_l[0])), reverse=True) L = [(k, l) for (k, l) in L if all( monomial_div(_incr_k(S[l], k), g.LM) is None for g in G)] if not L: G = [g.monic() for g in G] return sorted(G, key=lambda g: O_to(g.LM), reverse=True) t = L.pop()
def matrix_fglm(F, ring, O_to): """ Converts the reduced Groebner basis ``F`` of a zero-dimensional ideal w.r.t. ``O_from`` to a reduced Groebner basis w.r.t. ``O_to``. References ========== .. [1] J.C. Faugere, P. Gianni, D. Lazard, T. Mora (1994). Efficient Computation of Zero-dimensional Groebner Bases by Change of Ordering """ domain = ring.domain ngens = ring.ngens ring_to = ring.clone(order=O_to) old_basis = _basis(F, ring) M = _representing_matrices(old_basis, F, ring) # V contains the normalforms (wrt O_from) of S S = [ring.zero_monom] V = [[domain.one] + [domain.zero] * (len(old_basis) - 1)] G = [] L = [(i, 0) for i in range(ngens)] # (i, j) corresponds to x_i * S[j] L.sort(key=lambda k_l: O_to(_incr_k(S[k_l[1]], k_l[0])), reverse=True) t = L.pop() P = _identity_matrix(len(old_basis), domain) while True: s = len(S) v = _matrix_mul(M[t[0]], V[t[1]]) _lambda = _matrix_mul(P, v) if all(_lambda[i] == domain.zero for i in range(s, len(old_basis))): # there is a linear combination of v by V lt = ring.term_new(_incr_k(S[t[1]], t[0]), domain.one) rest = ring.from_dict({S[i]: _lambda[i] for i in range(s)}) g = (lt - rest).set_ring(ring_to) if g: G.append(g) else: # v is linearly independent from V P = _update(s, _lambda, P) S.append(_incr_k(S[t[1]], t[0])) V.append(v) L.extend([(i, s) for i in range(ngens)]) L = list(set(L)) L.sort(key=lambda k_l: O_to(_incr_k(S[k_l[1]], k_l[0])), reverse=True) L = [(k, l) for (k, l) in L if all(monomial_div(_incr_k(S[l], k), g.LM) is None for g in G)] if not L: G = [ g.monic() for g in G ] return sorted(G, key=lambda g: O_to(g.LM), reverse=True) t = L.pop()
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 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 test_monomial_div(): assert monomial_div((3, 4, 1), (1, 2, 0)) == (2, 2, 1)