Buckets:
MisterAI/LocalAI_Demo_backends / cpu-diffusers.upgrade-tmp /venv /lib /python3.10 /site-packages /sympy /polys /groebnertools.py
| """Groebner bases algorithms. """ | |
| from sympy.core.symbol import Dummy | |
| from sympy.polys.monomials import monomial_mul, monomial_lcm, monomial_divides, term_div | |
| from sympy.polys.orderings import lex | |
| from sympy.polys.polyerrors import DomainError | |
| from sympy.polys.polyconfig import query | |
| def groebner(seq, ring, method=None): | |
| """ | |
| Computes Groebner basis for a set of polynomials in `K[X]`. | |
| Wrapper around the (default) improved Buchberger and the other algorithms | |
| for computing Groebner bases. The choice of algorithm can be changed via | |
| ``method`` argument or :func:`sympy.polys.polyconfig.setup`, where | |
| ``method`` can be either ``buchberger`` or ``f5b``. | |
| """ | |
| if method is None: | |
| method = query('groebner') | |
| _groebner_methods = { | |
| 'buchberger': _buchberger, | |
| 'f5b': _f5b, | |
| } | |
| try: | |
| _groebner = _groebner_methods[method] | |
| except KeyError: | |
| raise ValueError("'%s' is not a valid Groebner bases algorithm (valid are 'buchberger' and 'f5b')" % method) | |
| domain, orig = ring.domain, None | |
| if not domain.is_Field or not domain.has_assoc_Field: | |
| try: | |
| orig, ring = ring, ring.clone(domain=domain.get_field()) | |
| except DomainError: | |
| raise DomainError("Cannot compute a Groebner basis over %s" % domain) | |
| else: | |
| seq = [ s.set_ring(ring) for s in seq ] | |
| G = _groebner(seq, ring) | |
| if orig is not None: | |
| G = [ g.clear_denoms()[1].set_ring(orig) for g in G ] | |
| return G | |
| def _buchberger(f, ring): | |
| """ | |
| 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). | |
| Notes | |
| ===== | |
| 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. | |
| References | |
| ========== | |
| .. [1] [Bose03]_ | |
| .. [2] [Giovini91]_ | |
| .. [3] [Ajwa95]_ | |
| .. [4] [Cox97]_ | |
| """ | |
| order = ring.order | |
| monomial_mul = ring.monomial_mul | |
| monomial_div = ring.monomial_div | |
| monomial_lcm = ring.monomial_lcm | |
| 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 normal(g, J): | |
| h = g.rem([ f[j] for j in J ]) | |
| if not h: | |
| return None | |
| else: | |
| h = h.monic() | |
| if h not in I: | |
| I[h] = len(f) | |
| f.append(h) | |
| return h.LM, 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 = 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 | |
| # 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 = p.rem(f[:i]) | |
| if r: | |
| f1.append(r.monic()) | |
| if f == f1: | |
| break | |
| 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 | |
| h = min([f[x] for x in F], key=lambda f: order(f.LM)) | |
| 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 = spoly(f[ig1], f[ig2], ring) | |
| # ordering divisors is on average more efficient [Cox] page 111 | |
| G1 = sorted(G, key=lambda g: order(f[g].LM)) | |
| 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 - {ig}) | |
| if ht: | |
| Gr.add(ht[1]) | |
| Gr = [f[ig] for ig in Gr] | |
| # order according to the monomial ordering | |
| Gr = sorted(Gr, key=lambda f: order(f.LM), reverse=True) | |
| return Gr | |
| def spoly(p1, p2, ring): | |
| """ | |
| Compute LCM(LM(p1), LM(p2))/LM(p1)*p1 - LCM(LM(p1), LM(p2))/LM(p2)*p2 | |
| This is the S-poly provided p1 and p2 are monic | |
| """ | |
| LM1 = p1.LM | |
| LM2 = p2.LM | |
| LCM12 = ring.monomial_lcm(LM1, LM2) | |
| m1 = ring.monomial_div(LCM12, LM1) | |
| m2 = ring.monomial_div(LCM12, LM2) | |
| s1 = p1.mul_monom(m1) | |
| s2 = p2.mul_monom(m2) | |
| s = s1 - s2 | |
| return s | |
| # F5B | |
| # convenience functions | |
| def Sign(f): | |
| return f[0] | |
| def Polyn(f): | |
| return f[1] | |
| def Num(f): | |
| return f[2] | |
| def sig(monomial, index): | |
| return (monomial, index) | |
| def lbp(signature, polynomial, number): | |
| return (signature, polynomial, number) | |
| # signature functions | |
| def sig_cmp(u, v, order): | |
| """ | |
| Compare two signatures by extending the term order to K[X]^n. | |
| u < v iff | |
| - the index of v is greater than the index of u | |
| or | |
| - the index of v is equal to the index of u and u[0] < v[0] w.r.t. order | |
| u > v otherwise | |
| """ | |
| if u[1] > v[1]: | |
| return -1 | |
| if u[1] == v[1]: | |
| #if u[0] == v[0]: | |
| # return 0 | |
| if order(u[0]) < order(v[0]): | |
| return -1 | |
| return 1 | |
| def sig_key(s, order): | |
| """ | |
| Key for comparing two signatures. | |
| s = (m, k), t = (n, l) | |
| s < t iff [k > l] or [k == l and m < n] | |
| s > t otherwise | |
| """ | |
| return (-s[1], order(s[0])) | |
| def sig_mult(s, m): | |
| """ | |
| Multiply a signature by a monomial. | |
| The product of a signature (m, i) and a monomial n is defined as | |
| (m * t, i). | |
| """ | |
| return sig(monomial_mul(s[0], m), s[1]) | |
| # labeled polynomial functions | |
| def lbp_sub(f, g): | |
| """ | |
| Subtract labeled polynomial g from f. | |
| The signature and number of the difference of f and g are signature | |
| and number of the maximum of f and g, w.r.t. lbp_cmp. | |
| """ | |
| if sig_cmp(Sign(f), Sign(g), Polyn(f).ring.order) < 0: | |
| max_poly = g | |
| else: | |
| max_poly = f | |
| ret = Polyn(f) - Polyn(g) | |
| return lbp(Sign(max_poly), ret, Num(max_poly)) | |
| def lbp_mul_term(f, cx): | |
| """ | |
| Multiply a labeled polynomial with a term. | |
| The product of a labeled polynomial (s, p, k) by a monomial is | |
| defined as (m * s, m * p, k). | |
| """ | |
| return lbp(sig_mult(Sign(f), cx[0]), Polyn(f).mul_term(cx), Num(f)) | |
| def lbp_cmp(f, g): | |
| """ | |
| Compare two labeled polynomials. | |
| f < g iff | |
| - Sign(f) < Sign(g) | |
| or | |
| - Sign(f) == Sign(g) and Num(f) > Num(g) | |
| f > g otherwise | |
| """ | |
| if sig_cmp(Sign(f), Sign(g), Polyn(f).ring.order) == -1: | |
| return -1 | |
| if Sign(f) == Sign(g): | |
| if Num(f) > Num(g): | |
| return -1 | |
| #if Num(f) == Num(g): | |
| # return 0 | |
| return 1 | |
| def lbp_key(f): | |
| """ | |
| Key for comparing two labeled polynomials. | |
| """ | |
| return (sig_key(Sign(f), Polyn(f).ring.order), -Num(f)) | |
| # algorithm and helper functions | |
| 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 cp_cmp(c, d): | |
| """ | |
| Compare two critical pairs c and d. | |
| c < d iff | |
| - lbp(c[0], _, Num(c[2]) < lbp(d[0], _, Num(d[2])) (this | |
| corresponds to um_c * f_c and um_d * f_d) | |
| or | |
| - lbp(c[0], _, Num(c[2]) >< lbp(d[0], _, Num(d[2])) and | |
| lbp(c[3], _, Num(c[5])) < lbp(d[3], _, Num(d[5])) (this | |
| corresponds to vm_c * g_c and vm_d * g_d) | |
| c > d otherwise | |
| """ | |
| zero = Polyn(c[2]).ring.zero | |
| c0 = lbp(c[0], zero, Num(c[2])) | |
| d0 = lbp(d[0], zero, Num(d[2])) | |
| r = lbp_cmp(c0, d0) | |
| if r == -1: | |
| return -1 | |
| if r == 0: | |
| c1 = lbp(c[3], zero, Num(c[5])) | |
| d1 = lbp(d[3], zero, Num(d[5])) | |
| r = lbp_cmp(c1, d1) | |
| if r == -1: | |
| return -1 | |
| #if r == 0: | |
| # return 0 | |
| return 1 | |
| def cp_key(c, ring): | |
| """ | |
| Key for comparing critical pairs. | |
| """ | |
| return (lbp_key(lbp(c[0], ring.zero, Num(c[2]))), lbp_key(lbp(c[3], ring.zero, Num(c[5])))) | |
| def s_poly(cp): | |
| """ | |
| Compute the S-polynomial of a critical pair. | |
| The S-polynomial of a critical pair cp is cp[1] * cp[2] - cp[4] * cp[5]. | |
| """ | |
| return lbp_sub(lbp_mul_term(cp[2], cp[1]), lbp_mul_term(cp[5], cp[4])) | |
| def is_rewritable_or_comparable(sign, num, B): | |
| """ | |
| Check if a labeled polynomial is redundant by checking if its | |
| signature and number imply rewritability or comparability. | |
| (sign, num) is comparable if there exists a labeled polynomial | |
| h in B, such that sign[1] (the index) is less than Sign(h)[1] | |
| and sign[0] is divisible by the leading monomial of h. | |
| (sign, num) is rewritable if there exists a labeled polynomial | |
| h in B, such thatsign[1] is equal to Sign(h)[1], num < Num(h) | |
| and sign[0] is divisible by Sign(h)[0]. | |
| """ | |
| for h in B: | |
| # comparable | |
| if sign[1] < Sign(h)[1]: | |
| if monomial_divides(Polyn(h).LM, sign[0]): | |
| return True | |
| # rewritable | |
| if sign[1] == Sign(h)[1]: | |
| if num < Num(h): | |
| if monomial_divides(Sign(h)[0], sign[0]): | |
| return True | |
| return False | |
| def f5_reduce(f, B): | |
| """ | |
| F5-reduce a labeled polynomial f by B. | |
| Continuously searches for non-zero labeled polynomial h in B, such | |
| that the leading term lt_h of h divides the leading term lt_f of | |
| f and Sign(lt_h * h) < Sign(f). If such a labeled polynomial h is | |
| found, f gets replaced by f - lt_f / lt_h * h. If no such h can be | |
| found or f is 0, f is no further F5-reducible and f gets returned. | |
| A polynomial that is reducible in the usual sense need not be | |
| F5-reducible, e.g.: | |
| >>> from sympy.polys.groebnertools import lbp, sig, f5_reduce, Polyn | |
| >>> from sympy.polys import ring, QQ, lex | |
| >>> R, x,y,z = ring("x,y,z", QQ, lex) | |
| >>> f = lbp(sig((1, 1, 1), 4), x, 3) | |
| >>> g = lbp(sig((0, 0, 0), 2), x, 2) | |
| >>> Polyn(f).rem([Polyn(g)]) | |
| 0 | |
| >>> f5_reduce(f, [g]) | |
| (((1, 1, 1), 4), x, 3) | |
| """ | |
| order = Polyn(f).ring.order | |
| domain = Polyn(f).ring.domain | |
| if not Polyn(f): | |
| return f | |
| while True: | |
| g = f | |
| for h in B: | |
| if Polyn(h): | |
| if monomial_divides(Polyn(h).LM, Polyn(f).LM): | |
| t = term_div(Polyn(f).LT, Polyn(h).LT, domain) | |
| if sig_cmp(sig_mult(Sign(h), t[0]), Sign(f), order) < 0: | |
| # The following check need not be done and is in general slower than without. | |
| #if not is_rewritable_or_comparable(Sign(gp), Num(gp), B): | |
| hp = lbp_mul_term(h, t) | |
| f = lbp_sub(f, hp) | |
| break | |
| if g == f or not Polyn(f): | |
| return f | |
| def _f5b(F, ring): | |
| """ | |
| 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 | |
| ========== | |
| .. [1] Yao Sun, Dingkang Wang: "A New Proof for the Correctness of F5 | |
| (F5-Like) Algorithm", https://arxiv.org/abs/1004.0084 (specifically | |
| v4) | |
| .. [2] Thomas Becker, Volker Weispfenning, Groebner bases: A computational | |
| approach to commutative algebra, 1993, p. 203, 216 | |
| """ | |
| order = ring.order | |
| # reduce polynomials (like in Mario Pernici's implementation) (Becker, Weispfenning, p. 203) | |
| B = F | |
| while True: | |
| F = B | |
| B = [] | |
| for i in range(len(F)): | |
| p = F[i] | |
| r = p.rem(F[:i]) | |
| if r: | |
| B.append(r) | |
| if F == B: | |
| break | |
| # basis | |
| B = [lbp(sig(ring.zero_monom, i + 1), F[i], i + 1) for i in range(len(F))] | |
| B.sort(key=lambda f: order(Polyn(f).LM), reverse=True) | |
| # critical pairs | |
| CP = [critical_pair(B[i], B[j], ring) for i in range(len(B)) for j in range(i + 1, len(B))] | |
| CP.sort(key=lambda cp: cp_key(cp, ring), 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): | |
| continue | |
| if is_rewritable_or_comparable(cp[3], Num(cp[5]), B): | |
| continue | |
| s = s_poly(cp) | |
| p = f5_reduce(s, B) | |
| p = lbp(Sign(p), Polyn(p).monic(), 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]): | |
| indices.append(i) | |
| elif is_rewritable_or_comparable(cp[3], Num(cp[5]), [p]): | |
| 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, ring) | |
| if is_rewritable_or_comparable(cp[0], Num(cp[2]), [p]): | |
| continue | |
| elif is_rewritable_or_comparable(cp[3], Num(cp[5]), [p]): | |
| continue | |
| CP.append(cp) | |
| # sort (other sorting methods/selection strategies were not as successful) | |
| CP.sort(key=lambda cp: cp_key(cp, ring), reverse=True) | |
| # insert p into B: | |
| m = Polyn(p).LM | |
| if order(m) <= order(Polyn(B[-1]).LM): | |
| B.append(p) | |
| else: | |
| for i, q in enumerate(B): | |
| if order(m) > order(Polyn(q).LM): | |
| B.insert(i, p) | |
| break | |
| k += 1 | |
| #print(len(B), len(CP), "%d critical pairs removed" % len(indices)) | |
| else: | |
| reductions_to_zero += 1 | |
| # reduce Groebner basis: | |
| H = [Polyn(g).monic() for g in B] | |
| H = red_groebner(H, ring) | |
| return sorted(H, key=lambda f: order(f.LM), reverse=True) | |
| def red_groebner(G, ring): | |
| """ | |
| Compute reduced Groebner basis, from BeckerWeispfenning93, p. 216 | |
| Selects a subset of generators, that already generate the ideal | |
| and computes a reduced Groebner basis for them. | |
| """ | |
| def reduction(P): | |
| """ | |
| The actual reduction algorithm. | |
| """ | |
| Q = [] | |
| for i, p in enumerate(P): | |
| h = p.rem(P[:i] + P[i + 1:]) | |
| if h: | |
| Q.append(h) | |
| return [p.monic() for p in Q] | |
| F = G | |
| H = [] | |
| while F: | |
| f0 = F.pop() | |
| if not any(monomial_divides(f.LM, f0.LM) for f in F + H): | |
| H.append(f0) | |
| # Becker, Weispfenning, p. 217: H is Groebner basis of the ideal generated by G. | |
| return reduction(H) | |
| def is_groebner(G, ring): | |
| """ | |
| Check if G is a Groebner basis. | |
| """ | |
| for i in range(len(G)): | |
| for j in range(i + 1, len(G)): | |
| s = spoly(G[i], G[j], ring) | |
| s = s.rem(G) | |
| if s: | |
| return False | |
| return True | |
| def is_minimal(G, ring): | |
| """ | |
| Checks if G is a minimal Groebner basis. | |
| """ | |
| order = ring.order | |
| domain = ring.domain | |
| G.sort(key=lambda g: order(g.LM)) | |
| for i, g in enumerate(G): | |
| if g.LC != domain.one: | |
| return False | |
| for h in G[:i] + G[i + 1:]: | |
| if monomial_divides(h.LM, g.LM): | |
| return False | |
| return True | |
| def is_reduced(G, ring): | |
| """ | |
| Checks if G is a reduced Groebner basis. | |
| """ | |
| order = ring.order | |
| domain = ring.domain | |
| G.sort(key=lambda g: order(g.LM)) | |
| for i, g in enumerate(G): | |
| if g.LC != domain.one: | |
| return False | |
| for term in g.terms(): | |
| for h in G[:i] + G[i + 1:]: | |
| if monomial_divides(h.LM, term[0]): | |
| return False | |
| return True | |
| 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 does not 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 not any(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_gcd(f, g): | |
| """Computes GCD of two polynomials using Groebner bases. """ | |
| if f.ring != g.ring: | |
| raise ValueError("Values should be equal") | |
| domain = f.ring.domain | |
| if not domain.is_Field: | |
| fc, f = f.primitive() | |
| gc, g = g.primitive() | |
| gcd = domain.gcd(fc, gc) | |
| H = (f*g).quo([groebner_lcm(f, g)]) | |
| if len(H) != 1: | |
| raise ValueError("Length should be 1") | |
| h = H[0] | |
| if not domain.is_Field: | |
| return gcd*h | |
| else: | |
| return h.monic() | |
Xet Storage Details
- Size:
- 23.3 kB
- Xet hash:
- 12aac60aeac2860e72d37ecdd5db5b4ef4c94210a81d8a371ce4f7a5aaef3b8e
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.