def sdm_nf_buchberger(f, G, O, K, phantom=None): r""" Compute a weak normal form of ``f`` with respect to ``G`` and order ``O``. The ground field is assumed to be ``K``, and monomials ordered according to ``O``. This is the standard Buchberger algorithm for computing weak normal forms with respect to *global* monomial orders [SCA, algorithm 1.6.10]. If ``phantom`` is not ``None``, it should be a pair of "phantom" arguments on which to perform the same computations as on ``f``, ``G``, both results are then returned. """ from itertools import repeat h = f T = list(G) if phantom is not None: # "phantom" variables with suffix p hp = phantom[0] Tp = list(phantom[1]) phantom = True else: Tp = repeat([]) phantom = False while h: try: g, gp = next((g, gp) for g, gp in zip(T, Tp) if sdm_monomial_divides(sdm_LM(g), sdm_LM(h))) except StopIteration: break if phantom: h, hp = sdm_spoly(h, g, O, K, phantom=(hp, gp)) else: h = sdm_spoly(h, g, O, K) if phantom: return h, hp return h
def test_condition_simple(): rl = rewriterule(x, x+1, [x], lambda x: x < 10) assert not list(rl(S(15))) assert rebuild(next(rl(S(5)))) == 6
def test_Exprs_ok(): rl = rewriterule(p+q, q+p, (p, q)) next(rl(x+y)).is_commutative str(next(rl(x+y)))
def sdm_groebner(G, NF, O, K, extended=False): """ Compute a minimal standard basis of ``G`` with respect to order ``O``. The algorithm uses a normal form ``NF``, for example ``sdm_nf_mora``. The ground field is assumed to be ``K``, and monomials ordered according to ``O``. Let `N` denote the submodule generated by elements of `G`. A standard basis for `N` is a subset `S` of `N`, such that `in(S) = in(N)`, where for any subset `X` of `F`, `in(X)` denotes the submodule generated by the initial forms of elements of `X`. [SCA, defn 2.3.2] A standard basis is called minimal if no subset of it is a standard basis. One may show that standard bases are always generating sets. Minimal standard bases are not unique. This algorithm computes a deterministic result, depending on the particular order of `G`. If ``extended=True``, also compute the transition matrix from the initial generators to the groebner basis. That is, return a list of coefficient vectors, expressing the elements of the groebner basis in terms of the elements of ``G``. This functions implements the "sugar" strategy, see Giovini et al: "One sugar cube, please" OR Selection strategies in Buchberger algorithm. """ # The critical pair set. # A critical pair is stored as (i, j, s, t) where (i, j) defines the pair # (by indexing S), s is the sugar of the pair, and t is the lcm of their # leading monomials. P = [] # The eventual standard basis. S = [] Sugars = [] def Ssugar(i, j): """Compute the sugar of the S-poly corresponding to (i, j).""" LMi = sdm_LM(S[i]) LMj = sdm_LM(S[j]) return max(Sugars[i] - sdm_monomial_deg(LMi), Sugars[j] - sdm_monomial_deg(LMj)) \ + sdm_monomial_deg(sdm_monomial_lcm(LMi, LMj)) ourkey = lambda p: (p[2], O(p[3]), p[1]) def update(f, sugar, P): """Add f with sugar ``sugar`` to S, update P.""" if not f: return P k = len(S) S.append(f) Sugars.append(sugar) LMf = sdm_LM(f) def removethis(pair): i, j, s, t = pair if LMf[0] != t[0]: return False tik = sdm_monomial_lcm(LMf, sdm_LM(S[i])) tjk = sdm_monomial_lcm(LMf, sdm_LM(S[j])) return tik != t and tjk != t and sdm_monomial_divides(tik, t) and \ sdm_monomial_divides(tjk, t) # apply the chain criterion P = [p for p in P if not removethis(p)] # new-pair set N = [(i, k, Ssugar(i, k), sdm_monomial_lcm(LMf, sdm_LM(S[i]))) for i in range(k) if LMf[0] == sdm_LM(S[i])[0]] # TODO apply the product criterion? N.sort(key=ourkey) remove = set() for i, p in enumerate(N): for j in range(i + 1, len(N)): if sdm_monomial_divides(p[3], N[j][3]): remove.add(j) # TODO mergesort? P.extend(reversed([p for i, p in enumerate(N) if not i in remove])) P.sort(key=ourkey, reverse=True) # NOTE reverse-sort, because we want to pop from the end return P # Figure out the number of generators in the ground ring. try: # NOTE: we look for the first non-zero vector, take its first monomial # the number of generators in the ring is one less than the length # (since the zeroth entry is for the module generators) numgens = len(next(x[0] for x in G if x)[0]) - 1 except StopIteration: # No non-zero elements in G ... if extended: return [], [] return [] # This list will store expressions of the elements of S in terms of the # initial generators coefficients = [] # First add all the elements of G to S for i, f in enumerate(G): P = update(f, sdm_deg(f), P) if extended and f: coefficients.append(sdm_from_dict({(i,) + (0,)*numgens: K(1)}, O)) # Now carry out the buchberger algorithm. while P: i, j, s, t = P.pop() f, sf, g, sg = S[i], Sugars[i], S[j], Sugars[j] if extended: sp, coeff = sdm_spoly(f, g, O, K, phantom=(coefficients[i], coefficients[j])) h, hcoeff = NF(sp, S, O, K, phantom=(coeff, coefficients)) if h: coefficients.append(hcoeff) else: h = NF(sdm_spoly(f, g, O, K), S, O, K) P = update(h, Ssugar(i, j), P) # Finally interreduce the standard basis. # (TODO again, better data structures) S = set((tuple(f), i) for i, f in enumerate(S)) for (a, ai), (b, bi) in permutations(S, 2): A = sdm_LM(a) B = sdm_LM(b) if sdm_monomial_divides(A, B) and (b, bi) in S and (a, ai) in S: S.remove((b, bi)) L = sorted(((list(f), i) for f, i in S), key=lambda p: O(sdm_LM(p[0])), reverse=True) res = [x[0] for x in L] if extended: return res, [coefficients[i] for _, i in L] return res
def test_defaultdict(): assert next(unify(Variable('x'), 'foo')) == {Variable('x'): 'foo'}
def test_Exprs_ok(): rl = rewriterule(p+q, q+p) next(rl(x+y)).is_commutative str(next(rl(x+y)))
def diop_pell(D, N, t=symbols("t", Integer=True)): """ Solves the generalized Pell equation x**2 - D*y**2 = N. Uses LMM algorithm. Refer [1] for more details on the algorithm. Returns one solution for each class of the solutions. Other solutions can be constructed according to the values of D and N. Returns a list containing the solution tuples (x, y). Usage ===== diop_pell(D, N, t) -> D and N are integers as in x**2 - D*y**2 = N and t is the parameter to be used in the solutions. Details ======= ``D`` corresponds to the D in the equation ``N`` corresponds to the N in the equation ``t`` parameter to be used in the solutions Examples ======== >>> from sympy.solvers.diophantine import diop_pell >>> from sympy.core.compatibility import next >>> diop_pell(13, -4) # Solves equation x**2 - 13*y**2 = -4 [(3, 1), (393, 109), (36, 10)] The output can be interpreted as follows: There are three fundamental solutions to the equation x**2 - 13*y**2 = -4 given by (3, 1), (393, 109) and (36, 10). Each tuple is in the form (x, y), i. e solution (3, 1) means that x = 3 and y = 1. >>> diop_pell(986, 1) # Solves equation x**2 - 986*y**2 = 1 [(49299, 1570)] References ========== ..[1] Solving the generalized Pell equation x**2 - D*y**2 = N, John P. Robertson, July 31, 2004, Pages 16 - 17. http://www.jpr2718.org/pell.pdf """ if D < 0: if N == 0: return [(S.Zero, S.Zero)] elif N < 0: return [] elif N > 0: # TODO: Solution method should be improved sol = [] for y in range(floor(sqrt(-S(N)/D)) + 1): if isinstance(sqrt(N + D*y**2), Integer): sol.append((sqrt(N + D*y**2), y)) return sol elif D == 0: if N < 0 or not isinstance(sqrt(N), Integer): return [] if N == 0: return [(S.Zero, t)] if isinstance(sqrt(N), Integer): return [(sqrt(N), t), (-sqrt(N), t)] else: # D > 0 if isinstance(sqrt(D), Integer): r = sqrt(D) if N == 0: return [(r*t, t), (-r*t, t)] else: sol = [] for y in range(floor(sign(N)*(N - 1)/(2*r)) + 1): if isinstance(sqrt(D*y**2 + N), Integer): sol.append((sqrt(D*y**2 + N), y)) return sol else: if N == 0: return [(S.Zero, S.Zero)] elif abs(N) == 1: pqa = PQa(0, 1, D) a_0 = floor(sqrt(D)) l = 0 G = [] B = [] for i in pqa: a = i[2] G.append(i[5]) B.append(i[4]) if l != 0 and a == 2*a_0: break l = l + 1 if l % 2 == 1: if N == -1: x = G[l-1] y = B[l-1] else: count = l while count < 2*l - 1: i = next(pqa) G.append(i[5]) B.append(i[4]) count = count + 1 x = G[count] y = B[count] else: if N == 1: x = G[l-1] y = B[l-1] else: return [] return [(x, y)] else: fs = [] sol = [] div = divisors(N) for d in div: if divisible(N, d**2): fs.append(d) for f in fs: m = N // f**2 zs = [] for i in range(floor(S(abs(m))/2) + 1): # TODO: efficient algorithm should be used to # solve z^2 = D (mod m) if (i**2 - D) % abs(m) == 0: zs.append(i) if i < S(abs(m))/2 and i != 0: zs.append(-i) for z in zs: pqa = PQa(z, abs(m), D) l = 0 G = [] B = [] for i in pqa: a = i[2] G.append(i[5]) B.append(i[4]) if l != 0 and abs(i[1]) == 1: r = G[l-1] s = B[l-1] if r**2 - D*s**2 == m: sol.append((f*r, f*s)) elif diop_pell(D, -1) != []: a = diop_pell(D, -1) sol.append((f*(r*a[0][0] + a[0][1]*s*D), f*(r*a[0][1] + s*a[0][0]))) break l = l + 1 if l == length(z, abs(m), D): break return sol
def test_commutative_in_commutative(): from sympy.abc import a,b,c,d from sympy import sin, cos eq = sin(3)*sin(4)*sin(5) + 4*cos(3)*cos(4) pat = a*cos(b)*cos(c) + d*sin(b)*sin(c) assert next(unify(eq, pat, variables=(a,b,c,d)))
def test_commutative_in_commutative(): from sympy.abc import a, b, c, d from sympy import sin, cos eq = sin(3) * sin(4) * sin(5) + 4 * cos(3) * cos(4) pat = a * cos(b) * cos(c) + d * sin(b) * sin(c) assert next(unify(eq, pat, variables=(a, b, c, d)))
def test_condition_simple(): rl = rewriterule(x, x + 1, [x], lambda x: x < 10) assert not list(rl(S(15))) assert rebuild(next(rl(S(5)))) == 6