def test_random_poly(): poly = random_poly(x, 10, -100, 100, polys=False) assert Poly(poly).degree() == 10 assert all(-100 <= coeff <= 100 for coeff in Poly(poly).coeffs()) is True poly = random_poly(x, 10, -100, 100, polys=True) assert poly.degree() == 10 assert all(-100 <= coeff <= 100 for coeff in poly.coeffs()) is True
def dmp_deflate(f, u, K): """Map `x_i**m_i` to `y_i` in a polynomial in `K[X]`. """ if dmp_zero_p(f, u): return (1, ) * (u + 1), f F = dmp_to_dict(f, u) B = [0] * (u + 1) for M in F.iterkeys(): for i, m in enumerate(M): B[i] = igcd(B[i], m) for i, b in enumerate(B): if not b: B[i] = 1 B = tuple(B) if all([b == 1 for b in B]): return B, f H = {} for A, coeff in F.iteritems(): N = [a // b for a, b in zip(A, B)] H[tuple(N)] = coeff return B, dmp_from_dict(H, u, K)
def dmp_ext_factor(f, u, K): """Factor multivariate polynomials over algebraic number fields. """ if not u: return dup_ext_factor(f, K) lc = dmp_ground_LC(f, u, K) f = dmp_ground_monic(f, u, K) if all([d <= 0 for d in dmp_degree_list(f, u)]): return lc, [] f, F = dmp_sqf_part(f, u, K), f s, g, r = dmp_sqf_norm(f, u, K) factors = dmp_factor_list_include(r, u, K.dom) if len(factors) == 1: coeff, factors = lc, [f] else: H = dmp_raise([K.one, s * K.unit], u, 0, K) for i, (factor, _) in enumerate(factors): h = dmp_convert(factor, u, K.dom, K) h, _, g = dmp_inner_gcd(h, g, u, K) h = dmp_compose(h, H, u, K) factors[i] = h return lc, dmp_trial_division(F, factors, u, K)
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(*F.keys()) if all([ g == 0 for g in G ]): return G, f f = {} for monom, coeff in F.iteritems(): f[monomial_div(monom, G)] = coeff return G, dmp_from_dict(f, u, K)
def test_roots_quartic(): assert roots_quartic(Poly(x**4, x)) == [0, 0, 0, 0] assert roots_quartic(Poly(x**4 + x**3, x)) in [ [-1,0,0,0], [0,-1,0,0], [0,0,-1,0], [0,0,0,-1] ] assert roots_quartic(Poly(x**4 - x**3, x)) in [ [1,0,0,0], [0,1,0,0], [0,0,1,0], [0,0,0,1] ] lhs = roots_quartic(Poly(x**4 + x, x)) rhs = [S.Half + I*sqrt(3)/2, S.Half - I*sqrt(3)/2, S.Zero, -S.One] assert sorted(lhs, key=hash) == sorted(rhs, key=hash) # test of all branches of roots quartic for i, (a, b, c, d) in enumerate([(1, 2, 3, 0), (3, -7, -9, 9), (1, 2, 3, 4), (1, 2, 3, 4), (-7, -3, 3, -6), (-3, 5, -6, -4)]): if i == 2: c = -a*(a**2/S(8) - b/S(2)) elif i == 3: d = a*(a*(3*a**2/S(256) - b/S(16)) + c/S(4)) eq = x**4 + a*x**3 + b*x**2 + c*x + d ans = roots_quartic(Poly(eq, x)) assert all([eq.subs(x, ai).n(chop=True) == 0 for ai in ans])
def __new__(cls, function, *symbols, **assumptions): # Any embedded piecewise functions need to be brought out to the # top level so that integration can go into piecewise mode at the # earliest possible moment. function = piecewise_fold(sympify(function)) if function is S.NaN: return S.NaN if symbols: limits, sign = _process_limits(*symbols) else: # no symbols provided -- let's compute full anti-derivative limits, sign = [Tuple(s) for s in function.free_symbols], 1 if not limits: raise ValueError('An integration variable is required.') while isinstance(function, Integral): # denest the integrand limits = list(function.limits) + limits function = function.function obj = Expr.__new__(cls, **assumptions) arglist = [sign*function] arglist.extend(limits) obj._args = tuple(arglist) obj.is_commutative = all(s.is_commutative for s in obj.free_symbols) return obj
def roots_binomial(f): """Returns a list of roots of a binomial polynomial.""" n = f.degree() a, b = f.nth(n), f.nth(0) alpha = (-cancel(b / a))**Rational(1, n) if alpha.is_number: alpha = alpha.expand(complex=True) roots, I = [], S.ImaginaryUnit for k in xrange(n): zeta = exp(2 * k * S.Pi * I / n).expand(complex=True) roots.append((alpha * zeta).expand(power_base=False)) if all([r.is_number for r in roots]): reals, complexes = [], [] for root in roots: if root.is_real: reals.append(root) else: complexes.append(root) roots = sorted(reals) + sorted(complexes, key=lambda r: (re(r), -im(r))) return roots
def dmp_deflate(f, u, K): """Map `x_i**m_i` to `y_i` in a polynomial in `K[X]`. """ if dmp_zero_p(f, u): return (1,)*(u+1), f F = dmp_to_dict(f, u) B = [0]*(u+1) for M in F.iterkeys(): for i, m in enumerate(M): B[i] = igcd(B[i], m) for i, b in enumerate(B): if not b: B[i] = 1 B = tuple(B) if all([ b == 1 for b in B ]): return B, f H = {} for A, coeff in F.iteritems(): N = [ a // b for a, b in zip(A, B) ] H[tuple(N)] = coeff return B, dmp_from_dict(H, u, K)
def __new__(cls, function, *symbols, **assumptions): # Any embedded piecewise functions need to be brought out to the # top level so that integration can go into piecewise mode at the # earliest possible moment. function = piecewise_fold(sympify(function)) if function is S.NaN: return S.NaN if symbols: limits, sign = _process_limits(*symbols) else: # no symbols provided -- let's compute full anti-derivative limits, sign = [Tuple(s) for s in function.free_symbols], 1 if len(limits) != 1: raise ValueError("specify integration variables to integrate %s" % function) while isinstance(function, Integral): # denest the integrand limits = list(function.limits) + limits function = function.function obj = Expr.__new__(cls, **assumptions) arglist = [sign*function] arglist.extend(limits) obj._args = tuple(arglist) obj.is_commutative = all(s.is_commutative for s in obj.free_symbols) return obj
def dmp_ext_factor(f, u, K): """Factor multivariate polynomials over algebraic number fields. """ if not u: return dup_ext_factor(f, K) lc = dmp_ground_LC(f, u, K) f = dmp_ground_monic(f, u, K) if all([ d <= 0 for d in dmp_degree_list(f, u) ]): return lc, [] f, F = dmp_sqf_part(f, u, K), f s, g, r = dmp_sqf_norm(f, u, K) factors = dmp_factor_list_include(r, u, K.dom) if len(factors) == 1: coeff, factors = lc, [f] else: H = dmp_raise([K.one, s*K.unit], u, 0, K) for i, (factor, _) in enumerate(factors): h = dmp_convert(factor, u, K.dom, K) h, _, g = dmp_inner_gcd(h, g, u, K) h = dmp_compose(h, H, u, K) factors[i] = h return lc, dmp_trial_division(F, factors, u, K)
def monomial_div(A, B): """ Division of tuples representing monomials. Lets divide `x**3*y**4*z` by `x*y**2`:: >>> from sympy.polys.monomialtools import monomial_div >>> monomial_div((3, 4, 1), (1, 2, 0)) (2, 2, 1) which gives `x**2*y**2*z`. However:: >>> monomial_div((3, 4, 1), (1, 2, 2)) is None True `x*y**2*z**2` does not divide `x**3*y**4*z`. """ C = [ a - b for a, b in zip(A, B) ] if all([ c >= 0 for c in C ]): return tuple(C) else: return None
def monomial_div(A, B): """ Division of tuples representing monomials. Lets divide `x**3*y**4*z` by `x*y**2`:: >>> from sympy.polys.monomialtools import monomial_div >>> monomial_div((3, 4, 1), (1, 2, 0)) (2, 2, 1) which gives `x**2*y**2*z`. However:: >>> monomial_div((3, 4, 1), (1, 2, 2)) is None True `x*y**2*z**2` does not divide `x**3*y**4*z`. """ C = [a - b for a, b in zip(A, B)] if all([c >= 0 for c in C]): return tuple(C) else: return None
def _separatevars_dict(expr, *symbols): if symbols: assert all((t.is_Atom for t in symbols)), "symbols must be Atoms." ret = dict(((i,sympify(1)) for i in symbols)) ret['coeff'] = sympify(1) if expr.is_Mul: for i in expr.args: expsym = i.atoms(Symbol) if len(set(symbols).intersection(expsym)) > 1: return None if len(set(symbols).intersection(expsym)) == 0: # There are no symbols, so it is part of the coefficient ret['coeff'] *= i else: ret[expsym.pop()] *= i else: expsym = expr.atoms(Symbol) if len(set(symbols).intersection(expsym)) > 1: return None if len(set(symbols).intersection(expsym)) == 0: # There are no symbols, so it is part of the coefficient ret['coeff'] *= expr else: ret[expsym.pop()] *= expr return ret
def roots_binomial(f): """Returns a list of roots of a binomial polynomial.""" n = f.degree() a, b = f.nth(n), f.nth(0) alpha = (-cancel(b/a))**Rational(1, n) if alpha.is_number: alpha = alpha.expand(complex=True) roots, I = [], S.ImaginaryUnit for k in xrange(n): zeta = exp(2*k*S.Pi*I/n).expand(complex=True) roots.append((alpha*zeta).expand(power_base=False)) if all([ r.is_number for r in roots ]): reals, complexes = [], [] for root in roots: if root.is_real: reals.append(root) else: complexes.append(root) roots = sorted(reals) + sorted(complexes, key=lambda r: (re(r), -im(r))) return roots
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(*F.keys()) if all([g == 0 for g in G]): return G, f f = {} for monom, coeff in F.iteritems(): f[monomial_div(monom, G)] = coeff return G, dmp_from_dict(f, u, K)
def test_simplify(): x, y, z, k, n, m, w, f, s, A = symbols('x,y,z,k,n,m,w,f,s,A') assert all(simplify(tmp) == tmp for tmp in [I, E, oo, x, -x, -oo, -E, -I]) e = 1/x + 1/y assert e != (x+y)/(x*y) assert simplify(e) == (x+y)/(x*y) e = A**2*s**4/(4*pi*k*m**3) assert simplify(e) == e e = (4+4*x-2*(2+2*x))/(2+2*x) assert simplify(e) == 0 e = (-4*x*y**2-2*y**3-2*x**2*y)/(x+y)**2 assert simplify(e) == -2*y e = -x-y-(x+y)**(-1)*y**2+(x+y)**(-1)*x**2 assert simplify(e) == -2*y e = (x+x*y)/x assert simplify(e) == 1 + y e = (f(x)+y*f(x))/f(x) assert simplify(e) == 1 + y e = (2 * (1/n - cos(n * pi)/n))/pi assert simplify(e) == 2*((1 - 1*cos(pi*n))/(pi*n)) e = integrate(1/(x**3+1), x).diff(x) assert simplify(e) == 1/(x**3+1) e = integrate(x/(x**2+3*x+1), x).diff(x) assert simplify(e) == x/(x**2+3*x+1) A = Matrix([[2*k-m*w**2, -k], [-k, k-m*w**2]]).inv() assert simplify((A*Matrix([0,f]))[1]) == \ (f*(2*k - m*w**2))/(k**2 - 3*k*m*w**2 + m**2*w**4) a, b, c, d, e, f, g, h, i = symbols('a,b,c,d,e,f,g,h,i') f_1 = x*a + y*b + z*c - 1 f_2 = x*d + y*e + z*f - 1 f_3 = x*g + y*h + z*i - 1 solutions = solve([f_1, f_2, f_3], x, y, z, simplified=False) assert simplify(solutions[y]) == \ (a*i+c*d+f*g-a*f-c*g-d*i)/(a*e*i+b*f*g+c*d*h-a*f*h-b*d*i-c*e*g) f = -x + y/(z + t) + z*x/(z + t) + z*a/(z + t) + t*x/(z + t) assert simplify(f) == (y + a*z)/(z + t) A, B = symbols('A,B', commutative=False) assert simplify(A*B - B*A) == A*B - B*A
def test_simplify(): x, y, z, k, n, m, w, f, s, A = symbols('x,y,z,k,n,m,w,f,s,A') assert all(simplify(tmp) == tmp for tmp in [I, E, oo, x, -x, -oo, -E, -I]) e = 1 / x + 1 / y assert e != (x + y) / (x * y) assert simplify(e) == (x + y) / (x * y) e = A**2 * s**4 / (4 * pi * k * m**3) assert simplify(e) == e e = (4 + 4 * x - 2 * (2 + 2 * x)) / (2 + 2 * x) assert simplify(e) == 0 e = (-4 * x * y**2 - 2 * y**3 - 2 * x**2 * y) / (x + y)**2 assert simplify(e) == -2 * y e = -x - y - (x + y)**(-1) * y**2 + (x + y)**(-1) * x**2 assert simplify(e) == -2 * y e = (x + x * y) / x assert simplify(e) == 1 + y e = (f(x) + y * f(x)) / f(x) assert simplify(e) == 1 + y e = (2 * (1 / n - cos(n * pi) / n)) / pi assert simplify(e) == 2 * ((1 - 1 * cos(pi * n)) / (pi * n)) e = integrate(1 / (x**3 + 1), x).diff(x) assert simplify(e) == 1 / (x**3 + 1) e = integrate(x / (x**2 + 3 * x + 1), x).diff(x) assert simplify(e) == x / (x**2 + 3 * x + 1) A = Matrix([[2 * k - m * w**2, -k], [-k, k - m * w**2]]).inv() assert simplify((A*Matrix([0,f]))[1]) == \ (f*(2*k - m*w**2))/(k**2 - 3*k*m*w**2 + m**2*w**4) a, b, c, d, e, f, g, h, i = symbols('a,b,c,d,e,f,g,h,i') f_1 = x * a + y * b + z * c - 1 f_2 = x * d + y * e + z * f - 1 f_3 = x * g + y * h + z * i - 1 solutions = solve([f_1, f_2, f_3], x, y, z, simplified=False) assert simplify(solutions[y]) == \ (a*i+c*d+f*g-a*f-c*g-d*i)/(a*e*i+b*f*g+c*d*h-a*f*h-b*d*i-c*e*g) f = -x + y / (z + t) + z * x / (z + t) + z * a / (z + t) + t * x / (z + t) assert simplify(f) == (y + a * z) / (z + t) A, B = symbols('A,B', commutative=False) assert simplify(A * B - B * A) == A * B - B * A
def dmp_inflate(f, M, u, K): """Map `y_i` to `x_i**k_i` in a polynomial in `K[X]`. """ if not u: return dup_inflate(f, M[0], K) if all([m == 1 for m in M]): return f else: return _rec_inflate(f, M, u, 0, K)
def denester(nested): """ Denests a list of expressions that contain nested square roots. This method should not be called directly - use 'denest' instead. This algorithm is based on <http://www.almaden.ibm.com/cs/people/fagin/symb85.pdf>. It is assumed that all of the elements of 'nested' share the same bottom-level radicand. (This is stated in the paper, on page 177, in the paragraph immediately preceding the algorithm.) When evaluating all of the arguments in parallel, the bottom-level radicand only needs to be denested once. This means that calling denester with x arguments results in a recursive invocation with x+1 arguments; hence denester has polynomial complexity. However, if the arguments were evaluated separately, each call would result in two recursive invocations, and the algorithm would have exponential complexity. This is discussed in the paper in the middle paragraph of page 179. """ if all((n ** 2).is_Number for n in nested): # If none of the arguments are nested for f in subsets(len(nested)): # Test subset 'f' of nested p = prod(nested[i] ** 2 for i in range(len(f)) if f[i]).expand() if 1 in f and f.count(1) > 1 and f[-1]: p = -p if sqrt(p).is_Number: return sqrt(p), f # If we got a perfect square, return its square root. return nested[-1], [0] * len(nested) # Otherwise, return the radicand from the previous invocation. else: a, b, r, R = Wild("a"), Wild("b"), Wild("r"), None values = [expr.match(sqrt(a + b * sqrt(r))) for expr in nested] for v in values: if r in v: # Since if b=0, r is not defined if R is not None: assert R == v[r] # All the 'r's should be the same. else: R = v[r] d, f = denester([sqrt((v[a] ** 2).expand() - (R * v[b] ** 2).expand()) for v in values] + [sqrt(R)]) if not any([f[i] for i in range(len(nested))]): # If f[i]=0 for all i < len(nested) v = values[-1] return sqrt(v[a] + v[b] * d), f else: v = prod(nested[i] ** 2 for i in range(len(nested)) if f[i]).expand().match(a + b * sqrt(r)) if 1 in f and f.index(1) < len(nested) - 1 and f[len(nested) - 1]: v[a] = -1 * v[a] v[b] = -1 * v[b] if not f[len(nested)]: # Solution denests with square roots return ( ( sqrt((v[a] + d).expand() / 2) + sign(v[b]) * sqrt((v[b] ** 2 * R / (2 * (v[a] + d))).expand()) ).expand(), f, ) else: # Solution requires a fourth root FR, s = (R.expand() ** Rational(1, 4)), sqrt((v[b] * R).expand() + d) return (s / (sqrt(2) * FR) + v[a] * FR / (sqrt(2) * s)).expand(), f
def dmp_inflate(f, M, u, K): """Map `y_i` to `x_i**k_i` in a polynomial in `K[X]`. """ if not u: return dup_inflate(f, M[0], K) if all([ m == 1 for m in M ]): return f else: return _rec_inflate(f, M, u, 0, K)
def __new__(cls, function, *symbols, **assumptions): # Any embedded piecewise functions need to be brought out to the # top level so that integration can go into piecewise mode at the # earliest possible moment. function = piecewise_fold(sympify(function)) if function is S.NaN: return S.NaN symbols = list(symbols) if not symbols: # no symbols provided -- let's compute full anti-derivative symbols = sorted(function.free_symbols, Basic.compare) if not symbols: raise ValueError('An integration variable is required.') while isinstance(function, Integral): # denest the integrand symbols = list(function.limits) + symbols function = function.function limits = [] for V in symbols: if isinstance(V, Symbol): limits.append(Tuple(V)) continue elif isinstance(V, (tuple, list, Tuple)): V = sympify(flatten(V)) if V[0].is_Symbol: newsymbol = V[0] if len(V) == 3: if V[1] is None and V[2] is not None: nlim = [V[2]] elif V[1] is not None and V[2] is None: function = -function nlim = [V[1]] elif V[1] is None and V[2] is None: nlim = [] else: nlim = V[1:] limits.append(Tuple(newsymbol, *nlim )) continue elif len(V) == 1 or (len(V) == 2 and V[1] is None): limits.append(Tuple(newsymbol)) continue elif len(V) == 2: limits.append(Tuple(newsymbol, V[1])) continue raise ValueError("Invalid integration variable or limits: %s" % str(symbols)) obj = Expr.__new__(cls, **assumptions) obj._args = tuple([function] + limits) obj.is_commutative = all(s.is_commutative for s in obj.free_symbols) return obj
def test_dup_random(): f = dup_random(0, -10, 10, ZZ) assert dup_degree(f) == 0 assert all([-10 <= c <= 10 for c in f]) f = dup_random(1, -20, 20, ZZ) assert dup_degree(f) == 1 assert all([-20 <= c <= 20 for c in f]) f = dup_random(2, -30, 30, ZZ) assert dup_degree(f) == 2 assert all([-30 <= c <= 30 for c in f]) f = dup_random(3, -40, 40, ZZ) assert dup_degree(f) == 3 assert all([-40 <= c <= 40 for c in f])
def test_dup_random(): f = dup_random(0, -10, 10, ZZ) assert dup_degree(f) == 0 assert all([ -10 <= c <= 10 for c in f ]) f = dup_random(1, -20, 20, ZZ) assert dup_degree(f) == 1 assert all([ -20 <= c <= 20 for c in f ]) f = dup_random(2, -30, 30, ZZ) assert dup_degree(f) == 2 assert all([ -30 <= c <= 30 for c in f ]) f = dup_random(3, -40, 40, ZZ) assert dup_degree(f) == 3 assert all([ -40 <= c <= 40 for c in f ])
def dmp_multi_deflate(polys, u, K): """ Map ``x_i**m_i`` to ``y_i`` in a set of polynomials in ``K[X]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densebasic import dmp_multi_deflate >>> f = ZZ.map([[1, 0, 0, 2], [], [3, 0, 0, 4]]) >>> g = ZZ.map([[1, 0, 2], [], [3, 0, 4]]) >>> dmp_multi_deflate((f, g), 1, ZZ) ((2, 1), ([[1, 0, 0, 2], [3, 0, 0, 4]], [[1, 0, 2], [3, 0, 4]])) """ if not u: M, H = dup_multi_deflate(polys, K) return (M, ), H F, B = [], [0] * (u + 1) for p in polys: f = dmp_to_dict(p, u) if not dmp_zero_p(p, u): for M in f.iterkeys(): for i, m in enumerate(M): B[i] = igcd(B[i], m) F.append(f) for i, b in enumerate(B): if not b: B[i] = 1 B = tuple(B) if all([b == 1 for b in B]): return B, polys H = [] for f in F: h = {} for A, coeff in f.iteritems(): N = [a // b for a, b in zip(A, B)] h[tuple(N)] = coeff H.append(dmp_from_dict(h, u, K)) return B, tuple(H)
def dmp_multi_deflate(polys, u, K): """ Map ``x_i**m_i`` to ``y_i`` in a set of polynomials in ``K[X]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densebasic import dmp_multi_deflate >>> f = ZZ.map([[1, 0, 0, 2], [], [3, 0, 0, 4]]) >>> g = ZZ.map([[1, 0, 2], [], [3, 0, 4]]) >>> dmp_multi_deflate((f, g), 1, ZZ) ((2, 1), ([[1, 0, 0, 2], [3, 0, 0, 4]], [[1, 0, 2], [3, 0, 4]])) """ if not u: M, H = dup_multi_deflate(polys, K) return (M,), H F, B = [], [0]*(u+1) for p in polys: f = dmp_to_dict(p, u) if not dmp_zero_p(p, u): for M in f.iterkeys(): for i, m in enumerate(M): B[i] = igcd(B[i], m) F.append(f) for i, b in enumerate(B): if not b: B[i] = 1 B = tuple(B) if all([ b == 1 for b in B ]): return B, polys H = [] for f in F: h = {} for A, coeff in f.iteritems(): N = [ a // b for a, b in zip(A, B) ] h[tuple(N)] = coeff H.append(dmp_from_dict(h, u, K)) return B, tuple(H)
def dmp_terms_gcd(f, u, K): """Remove GCD of terms from `f` in `K[X]`. """ 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(*F.keys()) if all([ g == 0 for g in G ]): return G, f f = {} for monom, coeff in F.iteritems(): f[monomial_div(monom, G)] = coeff return G, dmp_from_dict(f, u, K)
def dmp_terms_gcd(f, u, K): """Remove GCD of terms from `f` in `K[X]`. """ 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(*F.keys()) if all([g == 0 for g in G]): return G, f f = {} for monom, coeff in F.iteritems(): f[monomial_div(monom, G)] = coeff return G, dmp_from_dict(f, u, K)
def _get_indices_Add(expr): """Determine outer indices of an Add object. In a sum, each term must have the same set of outer indices. A valid expression could be x(i)*y(j) - x(j)*y(i) But we do not allow expressions like: x(i)*y(j) - z(j)*z(j) FIXME: Add support for Numpy broadcasting >>> from sympy.tensor.index_methods import _get_indices_Add >>> from sympy.tensor.indexed import IndexedBase, Idx >>> i, j, k = map(Idx, ['i', 'j', 'k']) >>> x = IndexedBase('x') >>> y = IndexedBase('y') >>> _get_indices_Add(x[i] + x[k]*y[i, k]) (set([i]), {}) """ inds = map(get_indices, expr.args) inds, syms = zip(*inds) # allow broadcast of scalars non_scalars = filter(lambda x: x != set(), inds) if not non_scalars: return set(), {} if not all(map(lambda x: x == non_scalars[0], non_scalars[1:])): raise IndexConformanceException("Indices are not consistent: %s" % expr) if not reduce(lambda x, y: x != y or y, syms): symmetries = syms[0] else: # FIXME: search for symmetries symmetries = {} return non_scalars[0], symmetries
def dmp_deflate(f, u, K): """ Map ``x_i**m_i`` to ``y_i`` in a polynomial in ``K[X]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densebasic import dmp_deflate >>> f = ZZ.map([[1, 0, 0, 2], [], [3, 0, 0, 4]]) >>> dmp_deflate(f, 1, ZZ) ((2, 3), [[1, 2], [3, 4]]) """ if dmp_zero_p(f, u): return (1, ) * (u + 1), f F = dmp_to_dict(f, u) B = [0] * (u + 1) for M in F.iterkeys(): for i, m in enumerate(M): B[i] = igcd(B[i], m) for i, b in enumerate(B): if not b: B[i] = 1 B = tuple(B) if all([b == 1 for b in B]): return B, f H = {} for A, coeff in F.iteritems(): N = [a // b for a, b in zip(A, B)] H[tuple(N)] = coeff return B, dmp_from_dict(H, u, K)
def _get_indices_Add(expr): """Determine outer indices of an Add object. In a sum, each term must have the same set of outer indices. A valid expression could be x(i)*y(j) - x(j)*y(i) But we do not allow expressions like: x(i)*y(j) - z(j)*z(j) FIXME: Add support for Numpy broadcasting >>> from sympy.tensor.index_methods import _get_indices_Add >>> from sympy.tensor.indexed import IndexedBase, Idx >>> i, j, k = map(Idx, ['i', 'j', 'k']) >>> x = IndexedBase('x') >>> y = IndexedBase('y') >>> _get_indices_Add(x[i] + x[k]*y[i, k]) (set([i]), {}) """ inds = map(get_indices, expr.args) inds, syms = zip(*inds) # allow broadcast of scalars non_scalars = filter(lambda x: x != set(), inds) if not non_scalars: return set(), {} if not all(map(lambda x: x == non_scalars[0], non_scalars[1:])): raise IndexConformanceException("Indices are not consistent: %s"%expr) if not reduce(lambda x, y: x!=y or y, syms): symmetries = syms[0] else: # FIXME: search for symmetries symmetries = {} return non_scalars[0], symmetries
def dmp_deflate(f, u, K): """ Map ``x_i**m_i`` to ``y_i`` in a polynomial in ``K[X]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densebasic import dmp_deflate >>> f = ZZ.map([[1, 0, 0, 2], [], [3, 0, 0, 4]]) >>> dmp_deflate(f, 1, ZZ) ((2, 3), [[1, 2], [3, 4]]) """ if dmp_zero_p(f, u): return (1,)*(u+1), f F = dmp_to_dict(f, u) B = [0]*(u+1) for M in F.iterkeys(): for i, m in enumerate(M): B[i] = igcd(B[i], m) for i, b in enumerate(B): if not b: B[i] = 1 B = tuple(B) if all([ b == 1 for b in B ]): return B, f H = {} for A, coeff in F.iteritems(): N = [ a // b for a, b in zip(A, B) ] H[tuple(N)] = coeff return B, dmp_from_dict(H, u, K)
def dmp_inflate(f, M, u, K): """ Map ``y_i`` to ``x_i**k_i`` in a polynomial in ``K[X]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densebasic import dmp_inflate >>> f = ZZ.map([[1, 2], [3, 4]]) >>> dmp_inflate(f, (2, 3), 1, ZZ) [[1, 0, 0, 2], [], [3, 0, 0, 4]] """ if not u: return dup_inflate(f, M[0], K) if all([m == 1 for m in M]): return f else: return _rec_inflate(f, M, u, 0, K)
def dmp_multi_deflate(polys, u, K): """Map `x_i**m_i` to `y_i` in a set of polynomials in `K[X]`. """ if not u: M, H = dup_multi_deflate(polys, K) return (M, ), H F, B = [], [0] * (u + 1) for p in polys: f = dmp_to_dict(p, u) if not dmp_zero_p(p, u): for M in f.iterkeys(): for i, m in enumerate(M): B[i] = igcd(B[i], m) F.append(f) for i, b in enumerate(B): if not b: B[i] = 1 B = tuple(B) if all([b == 1 for b in B]): return B, polys H = [] for f in F: h = {} for A, coeff in f.iteritems(): N = [a // b for a, b in zip(A, B)] h[tuple(N)] = coeff H.append(dmp_from_dict(h, u, K)) return B, tuple(H)
def dmp_multi_deflate(polys, u, K): """Map `x_i**m_i` to `y_i` in a set of polynomials in `K[X]`. """ if not u: M, H = dup_multi_deflate(polys, K) return (M,), H F, B = [], [0]*(u+1) for p in polys: f = dmp_to_dict(p, u) if not dmp_zero_p(p, u): for M in f.iterkeys(): for i, m in enumerate(M): B[i] = igcd(B[i], m) F.append(f) for i, b in enumerate(B): if not b: B[i] = 1 B = tuple(B) if all([ b == 1 for b in B ]): return B, polys H = [] for f in F: h = {} for A, coeff in f.iteritems(): N = [ a // b for a, b in zip(A, B) ] h[tuple(N)] = coeff H.append(dmp_from_dict(h, u, K)) return B, tuple(H)
def dmp_inflate(f, M, u, K): """ Map ``y_i`` to ``x_i**k_i`` in a polynomial in ``K[X]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densebasic import dmp_inflate >>> f = ZZ.map([[1, 2], [3, 4]]) >>> dmp_inflate(f, (2, 3), 1, ZZ) [[1, 0, 0, 2], [], [3, 0, 0, 4]] """ if not u: return dup_inflate(f, M[0], K) if all([ m == 1 for m in M ]): return f else: return _rec_inflate(f, M, u, 0, K)
def monomial_div(a, b): """Division of tuples representing monomials. Lets divide x**3*y**4*z by x*y**2: >>> monomial_div((3, 4, 1), (1, 2, 0)) (2, 2, 1) which gives x**2*y**2*z. However >>> monomial_div((3, 4, 1), (1, 2, 2)) None x*y**2*z**2 does not divide x**3*y**4*z. """ result = [x - y for x, y in zip(a, b)] if all(e >= 0 for e in result): return tuple(result) else: return None
def monomial_div(a, b): """Division of tuples representing monomials. Lets divide x**3*y**4*z by x*y**2: >>> from sympy.polys.monomial import monomial_div >>> monomial_div((3, 4, 1), (1, 2, 0)) (2, 2, 1) which gives x**2*y**2*z. However, nothing is obtained for the following, >>> monomial_div((3, 4, 1), (1, 2, 2)) <BLANKLINE> since x*y**2*z**2 does not divide x**3*y**4*z. """ result = [ x - y for x, y in zip(a, b) ] if all(e >= 0 for e in result): return tuple(result) else: return None
def preprocess_roots(poly): """Try to get rid of symbolic coefficients from ``poly``. """ coeff = S.One try: _, poly = poly.clear_denoms(convert=True) except DomainError: return coeff, poly poly = poly.primitive()[1] poly = poly.retract() if poly.get_domain().is_Poly and all(c.is_monomial for c in poly.rep.coeffs()): poly = poly.inject() strips = zip(*poly.monoms()) gens = list(poly.gens[1:]) base, strips = strips[0], strips[1:] for gen, strip in zip(gens, strips): reverse = False if strip[0] < strip[-1]: strip = reversed(strip) reverse = True ratio = None for a, b in zip(base, strip): if not a and not b: continue elif not a or not b: break elif b % a != 0: break else: _ratio = b // a if ratio is None: ratio = _ratio elif ratio != _ratio: break else: if reverse: ratio = -ratio poly = poly.eval(gen, 1) coeff *= gen**(-ratio) gens.remove(gen) if gens: poly = poly.eject(*gens) if poly.is_univariate and poly.get_domain().is_ZZ: basis = _integer_basis(poly) if basis is not None: n = poly.degree() def func((k,), coeff): return coeff//basis**(n-k) poly = poly.termwise(func) coeff *= basis
def solve(f, *symbols, **flags): """Solves equations and systems of equations. Currently supported are univariate polynomial and transcendental equations and systems of linear and polynomial equations. Input is formed as a single expression or an equation, or an iterable container in case of an equation system. The type of output may vary and depends heavily on the input. For more details refer to more problem specific functions. By default all solutions are simplified to make the output more readable. If this is not the expected behavior, eg. because of speed issues, set simplified=False in function arguments. To solve equations and systems of equations of other kind, eg. recurrence relations of differential equations use rsolve() or dsolve() functions respectively. >>> from sympy import * >>> x,y = symbols('xy') Solve a polynomial equation: >>> solve(x**4-1, x) [1, -1, -I, I] Solve a linear system: >>> solve((x+5*y-2, -3*x+6*y-15), x, y) {x: -3, y: 1} """ if not symbols: raise ValueError('no symbols were given') if len(symbols) == 1: if isinstance(symbols[0], (list, tuple, set)): symbols = symbols[0] symbols = map(sympify, symbols) if any(not s.is_Symbol for s in symbols): raise TypeError('not a Symbol') if not isinstance(f, (tuple, list, set)): f = sympify(f) if isinstance(f, Equality): f = f.lhs - f.rhs if len(symbols) != 1: raise NotImplementedError('multivariate equation') symbol = symbols[0] strategy = guess_solve_strategy(f, symbol) if strategy == GS_POLY: poly = f.as_poly(symbol) assert poly is not None result = roots(poly, cubics=True, quartics=True).keys() elif strategy == GS_RATIONAL: P, Q = f.as_numer_denom() #TODO: check for Q != 0 return solve(P, symbol, **flags) elif strategy == GS_POLY_CV_1: # we must search for a suitable change of variable # collect exponents exponents_denom = list() args = list(f.args) if isinstance(f, Add): for arg in args: if isinstance(arg, Pow): exponents_denom.append(arg.exp.q) elif isinstance(arg, Mul): for mul_arg in arg.args: if isinstance(mul_arg, Pow): exponents_denom.append(mul_arg.exp.q) elif isinstance(f, Mul): for mul_arg in args: if isinstance(mul_arg, Pow): exponents_denom.append(mul_arg.exp.q) assert len(exponents_denom) > 0 if len(exponents_denom) == 1: m = exponents_denom[0] else: # get the GCD of the denominators m = ilcm(*exponents_denom) # x -> y**m. # we assume positive for simplification purposes t = Symbol('t', positive=True, dummy=True) f_ = f.subs(symbol, t**m) if guess_solve_strategy(f_, t) != GS_POLY: raise TypeError( "Could not convert to a polynomial equation: %s" % f_) cv_sols = solve(f_, t) result = list() for sol in cv_sols: result.append(sol**(S.One / m)) elif strategy == GS_POLY_CV_2: m = 0 args = list(f.args) if isinstance(f, Add): for arg in args: if isinstance(arg, Pow): m = min(m, arg.exp) elif isinstance(arg, Mul): for mul_arg in arg.args: if isinstance(mul_arg, Pow): m = min(m, mul_arg.exp) elif isinstance(f, Mul): for mul_arg in args: if isinstance(mul_arg, Pow): m = min(m, mul_arg.exp) f1 = simplify(f * symbol**(-m)) result = solve(f1, symbol) # TODO: we might have introduced unwanted solutions # when multiplied by x**-m elif strategy == GS_TRASCENDENTAL: #a, b = f.as_numer_denom() # Let's throw away the denominator for now. When we have robust # assumptions, it should be checked, that for the solution, # b!=0. result = tsolve(f, *symbols) elif strategy == -1: raise Exception('Could not parse expression %s' % f) else: raise NotImplementedError( "No algorithms where implemented to solve equation %s" % f) if flags.get('simplified', True): return map(simplify, result) else: return result else: if not f: return {} else: polys = [] for g in f: g = sympify(g) if isinstance(g, Equality): g = g.lhs - g.rhs poly = g.as_poly(*symbols) if poly is not None: polys.append(poly) else: raise NotImplementedError if all(p.is_linear for p in polys): n, m = len(f), len(symbols) matrix = zeros((n, m + 1)) for i, poly in enumerate(polys): for coeff, monom in poly.iter_terms(): try: j = list(monom).index(1) matrix[i, j] = coeff except ValueError: matrix[i, m] = -coeff return solve_linear_system(matrix, *symbols, **flags) else: return solve_poly_system(polys)
def _solve(f, *symbols, **flags): """ Return a checked solution for f in terms of one or more of the symbols.""" if not iterable(f): if len(symbols) != 1: soln = None free = f.free_symbols ex = free - set(symbols) if len(ex) == 1: ex = ex.pop() try: # may come back as dict or list (if non-linear) soln = solve_undetermined_coeffs(f, symbols, ex) except NotImplementedError: pass if not soln is None: return soln # find first successful solution failed = [] for s in symbols: n, d = solve_linear(f, x=[s]) if n.is_Symbol: soln = {n: cancel(d)} return soln failed.append(s) for s in failed: try: soln = _solve(f, s, **flags) return soln except NotImplementedError: pass else: msg = "No algorithms are implemented to solve equation %s" raise NotImplementedError(msg % f) symbol = symbols[0] # first see if it really depends on symbol and whether there # is a linear solution f_num, sol = solve_linear(f, x=symbols) if not symbol in f_num.free_symbols: return [] elif f_num.is_Symbol: return [cancel(sol)] strategy = guess_solve_strategy(f, symbol) result = False # no solution was obtained if strategy == GS_POLY: poly = f.as_poly(symbol) if poly is None: msg = "Cannot solve equation %s for %s" % (f, symbol) else: # for cubics and quartics, if the flag wasn't set, DON'T do it # by default since the results are quite long. Perhaps one could # base this decision on a certain critical length of the roots. if poly.degree() > 2: flags['simplified'] = flags.get('simplified', False) result = roots(poly, cubics=True, quartics=True).keys() elif strategy == GS_RATIONAL: P, _ = f.as_numer_denom() dens = denoms(f, x=symbols) try: soln = _solve(P, symbol, **flags) except NotImplementedError: msg = "Cannot solve equation %s for %s" % (P, symbol) result = [] else: if dens: # reject any result that makes any denom. affirmatively 0; # if in doubt, keep it result = [ s for s in soln if all(not checksol(den, {symbol: s}) for den in dens) ] else: result = soln elif strategy == GS_POLY_CV_1: args = list(f.args) if isinstance(f, Pow): result = _solve(args[0], symbol, **flags) elif isinstance(f, Add): # we must search for a suitable change of variables # collect exponents exponents_denom = list() for arg in args: if isinstance(arg, Pow): exponents_denom.append(arg.exp.q) elif isinstance(arg, Mul): for mul_arg in arg.args: if isinstance(mul_arg, Pow): exponents_denom.append(mul_arg.exp.q) assert len(exponents_denom) > 0 if len(exponents_denom) == 1: m = exponents_denom[0] else: # get the LCM of the denominators m = reduce(ilcm, exponents_denom) # x -> y**m. # we assume positive for simplification purposes t = Dummy('t', positive=True) f_ = f.subs(symbol, t**m) if guess_solve_strategy(f_, t) != GS_POLY: msg = "Could not convert to a polynomial equation: %s" % f_ result = [] else: soln = [s**m for s in _solve(f_, t)] # we might have introduced solutions from another branch # when changing variables; check and keep solutions # unless they definitely aren't a solution result = [ s for s in soln if checksol(f, {symbol: s}) is not False ] elif isinstance(f, Mul): result = [] for m in f.args: result.extend(_solve(m, symbol, **flags) or []) elif strategy == GS_POLY_CV_2: m = 0 args = list(f.args) if isinstance(f, Add): for arg in args: if isinstance(arg, Pow): m = min(m, arg.exp) elif isinstance(arg, Mul): for mul_arg in arg.args: if isinstance(mul_arg, Pow): m = min(m, mul_arg.exp) elif isinstance(f, Mul): for mul_arg in args: if isinstance(mul_arg, Pow): m = min(m, mul_arg.exp) if m and m != 1: f_ = simplify(f * symbol**(-m)) try: sols = _solve(f_, symbol) except NotImplementedError: msg = 'Could not solve %s for %s' % (f_, symbol) else: # we might have introduced unwanted solutions # when multiplying by x**-m; check and keep solutions # unless they definitely aren't a solution if sols: result = [ s for s in sols if checksol(f, {symbol: s}) is not False ] else: msg = 'CV_2 calculated %d but it should have been other than 0 or 1' % m elif strategy == GS_PIECEWISE: result = set() for expr, cond in f.args: candidates = _solve(expr, *symbols) if isinstance(cond, bool) or cond.is_Number: if not cond: continue # Only include solutions that do not match the condition # of any of the other pieces. for candidate in candidates: matches_other_piece = False for other_expr, other_cond in f.args: if isinstance(other_cond, bool) \ or other_cond.is_Number: continue if bool(other_cond.subs(symbol, candidate)): matches_other_piece = True break if not matches_other_piece: result.add(candidate) else: for candidate in candidates: if bool(cond.subs(symbol, candidate)): result.add(candidate) result = list(result) elif strategy == -1: raise ValueError('Could not parse expression %s' % f) # this is the fallback for not getting any other solution if result is False or strategy == GS_TRANSCENDENTAL: soln = tsolve(f_num, symbol) dens = denoms(f, x=symbols) if not dens: result = soln else: # reject any result that makes any denom. affirmatively 0; # if in doubt, keep it result = [ s for s in soln if all(not checksol(den, {symbol: s}) for den in dens) ] if result is False: raise NotImplementedError( msg + "\nNo algorithms are implemented to solve equation %s" % f) if flags.get('simplified', True) and strategy != GS_RATIONAL: result = map(simplify, result) return result else: if not f: return [] else: polys = [] for g in f: poly = g.as_poly(*symbols, **{'extension': True}) if poly is not None: polys.append(poly) else: raise NotImplementedError() if all(p.is_linear for p in polys): n, m = len(f), len(symbols) matrix = zeros((n, m + 1)) for i, poly in enumerate(polys): for monom, coeff in poly.terms(): try: j = list(monom).index(1) matrix[i, j] = coeff except ValueError: matrix[i, m] = -coeff # a dictionary of symbols: values or None result = solve_linear_system(matrix, *symbols, **flags) return result else: # a list of tuples, T, where T[i] [j] corresponds to the ith solution for symbols[j] result = solve_poly_system(polys) return result
def is_quadratic(f): """Returns `True` if `f` is quadratic in all its variables. """ return all([ sum(monom) <= 2 for monom in dmp_to_dict(f.rep, f.lev, f.dom).keys() ])
def is_linear(f): """Returns `True` if `f` is linear in all its variables. """ return all([ sum(monom) <= 1 for monom in dmp_to_dict(f.rep, f.lev, f.dom).keys() ])
def _together(expr): from sympy.core.function import Function if expr.is_Add: items, coeffs, basis = [], [], {} for elem in expr.args: numer, q = fraction(_together(elem)) denom = {} for term in make_list(q.expand(), Mul): expo = S.One coeff = S.One if term.is_Pow: if term.exp.is_Rational: term, expo = term.base, term.exp elif term.exp.is_Mul: coeff, tail = term.exp.as_coeff_terms() if coeff.is_Rational: tail = C.Mul(*tail) term, expo = Pow(term.base, tail), coeff coeff = S.One elif term.func is C.exp: if term.args[0].is_Rational: term, expo = S.Exp1, term.args[0] elif term.args[0].is_Mul: coeff, tail = term.args[0].as_coeff_terms() if coeff.is_Rational: tail = C.Mul(*tail) term, expo = C.exp(tail), coeff coeff = S.One elif term.is_Rational: coeff = Integer(term.q) term = Integer(term.p) if term in denom: denom[term] += expo else: denom[term] = expo if term in basis: total, maxi = basis[term] n_total = total + expo n_maxi = max(maxi, expo) basis[term] = (n_total, n_maxi) else: basis[term] = (expo, expo) coeffs.append(coeff) items.append((numer, denom)) numerator, denominator = [], [] for (term, (total, maxi)) in basis.iteritems(): basis[term] = (total, total - maxi) if term.func is C.exp: denominator.append(C.exp(maxi * term.args[0])) else: if maxi is S.One: denominator.append(term) else: denominator.append(Pow(term, maxi)) if all([c.is_integer for c in coeffs]): gcds = lambda x, y: igcd(int(x), int(y)) common = Rational(reduce(gcds, coeffs)) else: common = S.One product = Mul(*coeffs) / common for ((numer, denom), coeff) in zip(items, coeffs): expr, coeff = [], product / (coeff * common) for term in basis.iterkeys(): total, sub = basis[term] if term in denom: expo = total - denom[term] - sub else: expo = total - sub if term.func is C.exp: expr.append(C.exp(expo * term.args[0])) else: if expo is S.One: expr.append(term) else: expr.append(Pow(term, expo)) numerator.append(coeff * Mul(*([numer] + expr))) return Add(*numerator) / (product * Mul(*denominator)) elif expr.is_Mul or expr.is_Pow: return type(expr)(*[_together(t) for t in expr.args]) elif expr.is_Function and deep: return expr.func(*[_together(t) for t in expr.args]) else: return expr
def is_linear(f): """Returns `True` if `f` is linear in all its variables. """ return all( [sum(monom) <= 1 for monom in dmp_to_dict(f.rep, f.lev).keys()])
def is_commutative(self): if all(getattr(t, 'is_commutative') for t in self.args): return True else: return False
def is_quadratic(f): """Returns `True` if `f` is quadratic in all its variables. """ return all( [sum(monom) <= 2 for monom in dmp_to_dict(f.rep, f.lev).keys()])
def is_ground(f): """Returns `True` if `f` is an element of the ground domain. """ return all(d <= 0 for d in dmp_degree_list(f.rep, f.lev))
def solve(f, *symbols, **flags): """Solves equations and systems of equations. Currently supported are univariate polynomial and transcendental equations and systems of linear and polynomial equations. Input is formed as a single expression or an equation, or an iterable container in case of an equation system. The type of output may vary and depends heavily on the input. For more details refer to more problem specific functions. By default all solutions are simplified to make the output more readable. If this is not the expected behavior, eg. because of speed issues, set simplified=False in function arguments. To solve equations and systems of equations of other kind, eg. recurrence relations of differential equations use rsolve() or dsolve() functions respectively. >>> from sympy import * >>> x,y = symbols('xy') Solve a polynomial equation: >>> solve(x**4-1, x) [1, -1, -I, I] Solve a linear system: >>> solve((x+5*y-2, -3*x+6*y-15), x, y) {x: -3, y: 1} """ if not symbols: raise ValueError('no symbols were given') if len(symbols) == 1: if isinstance(symbols[0], (list, tuple, set)): symbols = symbols[0] symbols = map(sympify, symbols) result = list() # Begin code handling for Function and Derivative instances # Basic idea: store all the passed symbols in symbols_passed, check to see # if any of them are Function or Derivative types, if so, use a dummy # symbol in their place, and set symbol_swapped = True so that other parts # of the code can be aware of the swap. Once all swapping is done, the # continue on with regular solving as usual, and swap back at the end of # the routine, so that whatever was passed in symbols is what is returned. symbols_new = [] symbol_swapped = False if isinstance(symbols, (list, tuple)): symbols_passed = symbols[:] elif isinstance(symbols, set): symbols_passed = list(symbols) i = 0 for s in symbols: if s.is_Symbol: s_new = s elif s.is_Function: symbol_swapped = True s_new = Symbol('F%d' % i, dummy=True) elif s.is_Derivative: symbol_swapped = True s_new = Symbol('D%d' % i, dummy=True) else: raise TypeError('not a Symbol or a Function') symbols_new.append(s_new) i += 1 if symbol_swapped: swap_back_dict = dict(zip(symbols_new, symbols)) # End code for handling of Function and Derivative instances if not isinstance(f, (tuple, list, set)): f = sympify(f) # Create a swap dictionary for storing the passed symbols to be solved # for, so that they may be swapped back. if symbol_swapped: swap_dict = zip(symbols, symbols_new) f = f.subs(swap_dict) symbols = symbols_new if isinstance(f, Equality): f = f.lhs - f.rhs if len(symbols) != 1: raise NotImplementedError('multivariate equation') symbol = symbols[0] strategy = guess_solve_strategy(f, symbol) if strategy == GS_POLY: poly = f.as_poly(symbol) assert poly is not None result = roots(poly, cubics=True, quartics=True).keys() elif strategy == GS_RATIONAL: P, Q = f.as_numer_denom() #TODO: check for Q != 0 return solve(P, symbol, **flags) elif strategy == GS_POLY_CV_1: args = list(f.args) if isinstance(f, Add): # we must search for a suitable change of variable # collect exponents exponents_denom = list() for arg in args: if isinstance(arg, Pow): exponents_denom.append(arg.exp.q) elif isinstance(arg, Mul): for mul_arg in arg.args: if isinstance(mul_arg, Pow): exponents_denom.append(mul_arg.exp.q) assert len(exponents_denom) > 0 if len(exponents_denom) == 1: m = exponents_denom[0] else: # get the GCD of the denominators m = ilcm(*exponents_denom) # x -> y**m. # we assume positive for simplification purposes t = Symbol('t', positive=True, dummy=True) f_ = f.subs(symbol, t**m) if guess_solve_strategy(f_, t) != GS_POLY: raise TypeError( "Could not convert to a polynomial equation: %s" % f_) cv_sols = solve(f_, t) for sol in cv_sols: result.append(sol**m) elif isinstance(f, Mul): for mul_arg in args: result.extend(solve(mul_arg, symbol)) elif strategy == GS_POLY_CV_2: m = 0 args = list(f.args) if isinstance(f, Add): for arg in args: if isinstance(arg, Pow): m = min(m, arg.exp) elif isinstance(arg, Mul): for mul_arg in arg.args: if isinstance(mul_arg, Pow): m = min(m, mul_arg.exp) elif isinstance(f, Mul): for mul_arg in args: if isinstance(mul_arg, Pow): m = min(m, mul_arg.exp) f1 = simplify(f * symbol**(-m)) result = solve(f1, symbol) # TODO: we might have introduced unwanted solutions # when multiplied by x**-m elif strategy == GS_TRANSCENDENTAL: #a, b = f.as_numer_denom() # Let's throw away the denominator for now. When we have robust # assumptions, it should be checked, that for the solution, # b!=0. result = tsolve(f, *symbols) elif strategy == -1: raise ValueError('Could not parse expression %s' % f) else: raise NotImplementedError( "No algorithms are implemented to solve equation %s" % f) if flags.get('simplified', True): return map(simplify, result) else: return result else: if not f: return {} else: # Create a swap dictionary for storing the passed symbols to be # solved for, so that they may be swapped back. if symbol_swapped: swap_dict = zip(symbols, symbols_new) f = [fi.subs(swap_dict) for fi in f] symbols = symbols_new polys = [] for g in f: g = sympify(g) if isinstance(g, Equality): g = g.lhs - g.rhs poly = g.as_poly(*symbols) if poly is not None: polys.append(poly) else: raise NotImplementedError() if all(p.is_linear for p in polys): n, m = len(f), len(symbols) matrix = zeros((n, m + 1)) for i, poly in enumerate(polys): for coeff, monom in poly.iter_terms(): try: j = list(monom).index(1) matrix[i, j] = coeff except ValueError: matrix[i, m] = -coeff soln = solve_linear_system(matrix, *symbols, **flags) else: soln = solve_poly_system(polys) # Use swap_dict to ensure we return the same type as what was # passed if symbol_swapped: if isinstance(soln, dict): res = {} for k in soln.keys(): res.update({swap_back_dict[k]: soln[k]}) return res else: return soln else: return soln
def reduce_inequalities(inequalities, assume=True): """Reduce a system of inequalities with rational coefficients. """ if not hasattr(inequalities, '__iter__'): inequalities = [inequalities] poly_part, abs_part, extra_assume = {}, {}, [] for inequality in inequalities: if isinstance(inequality, bool): if inequality is False: return False else: continue if isinstance(inequality, AppliedPredicate): extra_assume.append(inequality) continue if inequality.is_Relational: expr, rel = inequality.lhs - inequality.rhs, inequality.rel_op else: expr, rel = inequality, '==' gens = expr.atoms(Symbol) if not gens: return False elif len(gens) == 1: gen = gens.pop() else: raise NotImplementedError("only univariate inequalities are supported") components = expr.find(lambda u: u.is_Function) if not components: if gen in poly_part: poly_part[gen].append((expr, rel)) else: poly_part[gen] = [(expr, rel)] else: if all(isinstance(comp, Abs) for comp in components): if gen in abs_part: abs_part[gen].append((expr, rel)) else: abs_part[gen] = [(expr, rel)] else: raise NotImplementedError("can't reduce %s" % inequalities) extra_assume = And(*extra_assume) if assume is not None: assume = And(assume, extra_assume) else: assume = extra_assume poly_reduced = [] abs_reduced = [] for gen, exprs in poly_part.iteritems(): poly_reduced.append(reduce_poly_inequalities([exprs], gen, assume)) for gen, exprs in abs_part.iteritems(): abs_reduced.append(reduce_abs_inequalities(exprs, gen, assume)) return And(*(poly_reduced + abs_reduced))
def solve(f, *symbols, **flags): """Solves equations and systems of equations. Currently supported are univariate polynomial, transcendental equations, piecewise combinations thereof and systems of linear and polynomial equations. Input is formed as a single expression or an equation, or an iterable container in case of an equation system. The type of output may vary and depends heavily on the input. For more details refer to more problem specific functions. By default all solutions are simplified to make the output more readable. If this is not the expected behavior (e.g., because of speed issues) set simplified=False in function arguments. To solve equations and systems of equations like recurrence relations or differential equations, use rsolve() or dsolve(), respectively. >>> from sympy import I, solve >>> from sympy.abc import x, y Solve a polynomial equation: >>> solve(x**4-1, x) [1, -1, -I, I] Solve a linear system: >>> solve((x+5*y-2, -3*x+6*y-15), x, y) {x: -3, y: 1} """ def sympit(w): return map(sympify, iff(isinstance(w, (list, tuple, set)), w, [w])) # make f and symbols into lists of sympified quantities # keeping track of how f was passed since if it is a list # a dictionary of results will be returned. bare_f = not isinstance(f, (list, tuple, set)) f, symbols = (sympit(w) for w in [f, symbols]) for i, fi in enumerate(f): if isinstance(fi, Equality): f[i] = fi.lhs - fi.rhs if not symbols: #get symbols from equations or supply dummy symbols since #solve(3,x) returns []...though it seems that it should raise some sort of error TODO symbols = set([]) for fi in f: symbols |= fi.atoms(Symbol) or set([Dummy('x')]) symbols = list(symbols) if bare_f: f = f[0] if len(symbols) == 1: if isinstance(symbols[0], (list, tuple, set)): symbols = symbols[0] result = list() # Begin code handling for Function and Derivative instances # Basic idea: store all the passed symbols in symbols_passed, check to see # if any of them are Function or Derivative types, if so, use a dummy # symbol in their place, and set symbol_swapped = True so that other parts # of the code can be aware of the swap. Once all swapping is done, the # continue on with regular solving as usual, and swap back at the end of # the routine, so that whatever was passed in symbols is what is returned. symbols_new = [] symbol_swapped = False symbols_passed = list(symbols) for i, s in enumerate(symbols): if s.is_Symbol: s_new = s elif s.is_Function: symbol_swapped = True s_new = Dummy('F%d' % i) elif s.is_Derivative: symbol_swapped = True s_new = Dummy('D%d' % i) else: raise TypeError('not a Symbol or a Function') symbols_new.append(s_new) if symbol_swapped: swap_back_dict = dict(zip(symbols_new, symbols)) # End code for handling of Function and Derivative instances if not isinstance(f, (tuple, list, set)): # Create a swap dictionary for storing the passed symbols to be solved # for, so that they may be swapped back. if symbol_swapped: swap_dict = zip(symbols, symbols_new) f = f.subs(swap_dict) symbols = symbols_new # Any embedded piecewise functions need to be brought out to the # top level so that the appropriate strategy gets selected. f = piecewise_fold(f) if len(symbols) != 1: result = {} for s in symbols: result[s] = solve(f, s, **flags) if flags.get('simplified', True): for s, r in result.items(): result[s] = map(simplify, r) return result symbol = symbols[0] strategy = guess_solve_strategy(f, symbol) if strategy == GS_POLY: poly = f.as_poly(symbol) if poly is None: raise NotImplementedError("Cannot solve equation " + str(f) + " for " + str(symbol)) # for cubics and quartics, if the flag wasn't set, DON'T do it # by default since the results are quite long. Perhaps one could # base this decision on a certain crtical length of the roots. if poly.degree > 2: flags['simplified'] = flags.get('simplified', False) result = roots(poly, cubics=True, quartics=True).keys() elif strategy == GS_RATIONAL: P, Q = f.as_numer_denom() #TODO: check for Q != 0 result = solve(P, symbol, **flags) elif strategy == GS_POLY_CV_1: args = list(f.args) if isinstance(f, Add): # we must search for a suitable change of variable # collect exponents exponents_denom = list() for arg in args: if isinstance(arg, Pow): exponents_denom.append(arg.exp.q) elif isinstance(arg, Mul): for mul_arg in arg.args: if isinstance(mul_arg, Pow): exponents_denom.append(mul_arg.exp.q) assert len(exponents_denom) > 0 if len(exponents_denom) == 1: m = exponents_denom[0] else: # get the LCM of the denominators m = reduce(ilcm, exponents_denom) # x -> y**m. # we assume positive for simplification purposes t = Dummy('t', positive=True) f_ = f.subs(symbol, t**m) if guess_solve_strategy(f_, t) != GS_POLY: raise NotImplementedError( "Could not convert to a polynomial equation: %s" % f_) cv_sols = solve(f_, t) for sol in cv_sols: result.append(sol**m) elif isinstance(f, Mul): for mul_arg in args: result.extend(solve(mul_arg, symbol)) elif strategy == GS_POLY_CV_2: m = 0 args = list(f.args) if isinstance(f, Add): for arg in args: if isinstance(arg, Pow): m = min(m, arg.exp) elif isinstance(arg, Mul): for mul_arg in arg.args: if isinstance(mul_arg, Pow): m = min(m, mul_arg.exp) elif isinstance(f, Mul): for mul_arg in args: if isinstance(mul_arg, Pow): m = min(m, mul_arg.exp) f1 = simplify(f * symbol**(-m)) result = solve(f1, symbol) # TODO: we might have introduced unwanted solutions # when multiplied by x**-m elif strategy == GS_PIECEWISE: result = set() for expr, cond in f.args: candidates = solve(expr, *symbols) if isinstance(cond, bool) or cond.is_Number: if not cond: continue # Only include solutions that do not match the condition # of any of the other pieces. for candidate in candidates: matches_other_piece = False for other_expr, other_cond in f.args: if isinstance(other_cond, bool) \ or other_cond.is_Number: continue if bool(other_cond.subs(symbol, candidate)): matches_other_piece = True break if not matches_other_piece: result.add(candidate) else: for candidate in candidates: if bool(cond.subs(symbol, candidate)): result.add(candidate) result = list(result) elif strategy == GS_TRANSCENDENTAL: #a, b = f.as_numer_denom() # Let's throw away the denominator for now. When we have robust # assumptions, it should be checked, that for the solution, # b!=0. result = tsolve(f, *symbols) elif strategy == -1: raise ValueError('Could not parse expression %s' % f) else: raise NotImplementedError( "No algorithms are implemented to solve equation %s" % f) # This symbol swap should not be necessary for the single symbol case: if you've # solved for the symbol the it will not appear in the solution. Right now, however # ode's are getting solutions for solve (even though they shouldn't be -- see the # swap_back test in test_solvers). if symbol_swapped: result = [ri.subs(swap_back_dict) for ri in result] if flags.get('simplified', True) and strategy != GS_RATIONAL: return map(simplify, result) else: return result else: if not f: return {} else: # Create a swap dictionary for storing the passed symbols to be # solved for, so that they may be swapped back. if symbol_swapped: swap_dict = zip(symbols, symbols_new) f = [fi.subs(swap_dict) for fi in f] symbols = symbols_new polys = [] for g in f: poly = g.as_poly(*symbols) if poly is not None: polys.append(poly) else: raise NotImplementedError() if all(p.is_linear for p in polys): n, m = len(f), len(symbols) matrix = zeros((n, m + 1)) for i, poly in enumerate(polys): for monom, coeff in poly.terms(): try: j = list(monom).index(1) matrix[i, j] = coeff except ValueError: matrix[i, m] = -coeff soln = solve_linear_system(matrix, *symbols, **flags) else: soln = solve_poly_system(polys) # Use swap_dict to ensure we return the same type as what was # passed if symbol_swapped: if isinstance(soln, dict): res = {} for k in soln.keys(): res.update({swap_back_dict[k]: soln[k]}) return res else: return soln else: return soln
def solve(f, *symbols, **flags): """Solves equations and systems of equations. Currently supported are univariate polynomial and transcendental equations and systems of linear and polynomial equations. Input is formed as a single expression or an equation, or an iterable container in case of an equation system. The type of output may vary and depends heavily on the input. For more details refer to more problem specific functions. By default all solutions are simplified to make the output more readable. If this is not the expected behavior, eg. because of speed issues, set simplified=False in function arguments. To solve equations and systems of equations of other kind, eg. recurrence relations of differential equations use rsolve() or dsolve() functions respectively. >>> from sympy import * >>> x,y = symbols('xy') Solve a polynomial equation: >>> solve(x**4-1, x) [1, -1, -I, I] Solve a linear system: >>> solve((x+5*y-2, -3*x+6*y-15), x, y) {x: -3, y: 1} """ if not symbols: raise ValueError('no symbols were given') if len(symbols) == 1: if isinstance(symbols[0], (list, tuple, set)): symbols = symbols[0] symbols = map(sympify, symbols) result = list() # Begin code handling for Function and Derivative instances # Basic idea: store all the passed symbols in symbols_passed, check to see # if any of them are Function or Derivative types, if so, use a dummy # symbol in their place, and set symbol_swapped = True so that other parts # of the code can be aware of the swap. Once all swapping is done, the # continue on with regular solving as usual, and swap back at the end of # the routine, so that whatever was passed in symbols is what is returned. symbols_new = [] symbol_swapped = False if isinstance(symbols, (list, tuple)): symbols_passed = symbols[:] elif isinstance(symbols, set): symbols_passed = list(symbols) i = 0 for s in symbols: if s.is_Symbol: s_new = s elif s.is_Function: symbol_swapped = True s_new = Symbol('F%d' % i, dummy=True) elif s.is_Derivative: symbol_swapped = True s_new = Symbol('D%d' % i, dummy=True) else: raise TypeError('not a Symbol or a Function') symbols_new.append(s_new) i += 1 if symbol_swapped: swap_back_dict = dict(zip(symbols_new, symbols)) # End code for handling of Function and Derivative instances if not isinstance(f, (tuple, list, set)): f = sympify(f) # Create a swap dictionary for storing the passed symbols to be solved # for, so that they may be swapped back. if symbol_swapped: swap_dict = zip(symbols, symbols_new) f = f.subs(swap_dict) symbols = symbols_new if isinstance(f, Equality): f = f.lhs - f.rhs if len(symbols) != 1: raise NotImplementedError('multivariate equation') symbol = symbols[0] strategy = guess_solve_strategy(f, symbol) if strategy == GS_POLY: poly = f.as_poly( symbol ) assert poly is not None result = roots(poly, cubics=True, quartics=True).keys() elif strategy == GS_RATIONAL: P, Q = f.as_numer_denom() #TODO: check for Q != 0 return solve(P, symbol, **flags) elif strategy == GS_POLY_CV_1: args = list(f.args) if isinstance(f, Add): # we must search for a suitable change of variable # collect exponents exponents_denom = list() for arg in args: if isinstance(arg, Pow): exponents_denom.append(arg.exp.q) elif isinstance(arg, Mul): for mul_arg in arg.args: if isinstance(mul_arg, Pow): exponents_denom.append(mul_arg.exp.q) assert len(exponents_denom) > 0 if len(exponents_denom) == 1: m = exponents_denom[0] else: # get the GCD of the denominators m = ilcm(*exponents_denom) # x -> y**m. # we assume positive for simplification purposes t = Symbol('t', positive=True, dummy=True) f_ = f.subs(symbol, t**m) if guess_solve_strategy(f_, t) != GS_POLY: raise TypeError("Could not convert to a polynomial equation: %s" % f_) cv_sols = solve(f_, t) for sol in cv_sols: result.append(sol**m) elif isinstance(f, Mul): for mul_arg in args: result.extend(solve(mul_arg, symbol)) elif strategy == GS_POLY_CV_2: m = 0 args = list(f.args) if isinstance(f, Add): for arg in args: if isinstance(arg, Pow): m = min(m, arg.exp) elif isinstance(arg, Mul): for mul_arg in arg.args: if isinstance(mul_arg, Pow): m = min(m, mul_arg.exp) elif isinstance(f, Mul): for mul_arg in args: if isinstance(mul_arg, Pow): m = min(m, mul_arg.exp) f1 = simplify(f*symbol**(-m)) result = solve(f1, symbol) # TODO: we might have introduced unwanted solutions # when multiplied by x**-m elif strategy == GS_TRANSCENDENTAL: #a, b = f.as_numer_denom() # Let's throw away the denominator for now. When we have robust # assumptions, it should be checked, that for the solution, # b!=0. result = tsolve(f, *symbols) elif strategy == -1: raise ValueError('Could not parse expression %s' % f) else: raise NotImplementedError("No algorithms are implemented to solve equation %s" % f) if flags.get('simplified', True): return map(simplify, result) else: return result else: if not f: return {} else: # Create a swap dictionary for storing the passed symbols to be # solved for, so that they may be swapped back. if symbol_swapped: swap_dict = zip(symbols, symbols_new) f = [fi.subs(swap_dict) for fi in f] symbols = symbols_new polys = [] for g in f: g = sympify(g) if isinstance(g, Equality): g = g.lhs - g.rhs poly = g.as_poly(*symbols) if poly is not None: polys.append(poly) else: raise NotImplementedError() if all(p.is_linear for p in polys): n, m = len(f), len(symbols) matrix = zeros((n, m + 1)) for i, poly in enumerate(polys): for coeff, monom in poly.iter_terms(): try: j = list(monom).index(1) matrix[i, j] = coeff except ValueError: matrix[i, m] = -coeff soln = solve_linear_system(matrix, *symbols, **flags) else: soln = solve_poly_system(polys) # Use swap_dict to ensure we return the same type as what was # passed if symbol_swapped: if isinstance(soln, dict): res = {} for k in soln.keys(): res.update({swap_back_dict[k]: soln[k]}) return res else: return soln else: return soln
def nth(f, *N): """Returns the `n`-th coefficient of `f`. """ if all(isinstance(n, int) for n in N): return dmp_ground_nth(f.rep, N, f.lev, f.dom) else: raise TypeError("a sequence of integers expected")
def solve(f, *symbols, **flags): """Solves equations and systems of equations. Currently supported are univariate polynomial, transcendental equations, piecewise combinations thereof and systems of linear and polynomial equations. Input is formed as a single expression or an equation, or an iterable container in case of an equation system. The type of output may vary and depends heavily on the input. For more details refer to more problem specific functions. By default all solutions are simplified to make the output more readable. If this is not the expected behavior (e.g., because of speed issues) set simplified=False in function arguments. To solve equations and systems of equations like recurrence relations or differential equations, use rsolve() or dsolve(), respectively. >>> from sympy import I, solve >>> from sympy.abc import x, y Solve a polynomial equation: >>> solve(x**4-1, x) [1, -1, -I, I] Solve a linear system: >>> solve((x+5*y-2, -3*x+6*y-15), x, y) {x: -3, y: 1} """ def sympit(w): return map(sympify, iff(isinstance(w, (list, tuple, set)), w, [w])) # make f and symbols into lists of sympified quantities # keeping track of how f was passed since if it is a list # a dictionary of results will be returned. bare_f = not isinstance(f, (list, tuple, set)) f, symbols = (sympit(w) for w in [f, symbols]) if any(isinstance(fi, bool) or (fi.is_Relational and not fi.is_Equality) for fi in f): return reduce_inequalities(f, assume=flags.get("assume")) for i, fi in enumerate(f): if fi.is_Equality: f[i] = fi.lhs - fi.rhs if not symbols: # get symbols from equations or supply dummy symbols since # solve(3,x) returns []...though it seems that it should raise some sort of error TODO symbols = set([]) for fi in f: symbols |= fi.atoms(Symbol) or set([Dummy("x")]) symbols = list(symbols) if bare_f: f = f[0] if len(symbols) == 1: if isinstance(symbols[0], (list, tuple, set)): symbols = symbols[0] result = list() # Begin code handling for Function and Derivative instances # Basic idea: store all the passed symbols in symbols_passed, check to see # if any of them are Function or Derivative types, if so, use a dummy # symbol in their place, and set symbol_swapped = True so that other parts # of the code can be aware of the swap. Once all swapping is done, the # continue on with regular solving as usual, and swap back at the end of # the routine, so that whatever was passed in symbols is what is returned. symbols_new = [] symbol_swapped = False symbols_passed = list(symbols) for i, s in enumerate(symbols): if s.is_Symbol: s_new = s elif s.is_Function: symbol_swapped = True s_new = Dummy("F%d" % i) elif s.is_Derivative: symbol_swapped = True s_new = Dummy("D%d" % i) else: raise TypeError("not a Symbol or a Function") symbols_new.append(s_new) if symbol_swapped: swap_back_dict = dict(zip(symbols_new, symbols)) # End code for handling of Function and Derivative instances if not isinstance(f, (tuple, list, set)): # Create a swap dictionary for storing the passed symbols to be solved # for, so that they may be swapped back. if symbol_swapped: swap_dict = zip(symbols, symbols_new) f = f.subs(swap_dict) symbols = symbols_new # Any embedded piecewise functions need to be brought out to the # top level so that the appropriate strategy gets selected. f = piecewise_fold(f) if len(symbols) != 1: result = {} for s in symbols: result[s] = solve(f, s, **flags) if flags.get("simplified", True): for s, r in result.items(): result[s] = map(simplify, r) return result symbol = symbols[0] strategy = guess_solve_strategy(f, symbol) if strategy == GS_POLY: poly = f.as_poly(symbol) if poly is None: raise NotImplementedError("Cannot solve equation " + str(f) + " for " + str(symbol)) # for cubics and quartics, if the flag wasn't set, DON'T do it # by default since the results are quite long. Perhaps one could # base this decision on a certain crtical length of the roots. if poly.degree > 2: flags["simplified"] = flags.get("simplified", False) result = roots(poly, cubics=True, quartics=True).keys() elif strategy == GS_RATIONAL: P, Q = f.as_numer_denom() # TODO: check for Q != 0 result = solve(P, symbol, **flags) elif strategy == GS_POLY_CV_1: args = list(f.args) if isinstance(f, Add): # we must search for a suitable change of variable # collect exponents exponents_denom = list() for arg in args: if isinstance(arg, Pow): exponents_denom.append(arg.exp.q) elif isinstance(arg, Mul): for mul_arg in arg.args: if isinstance(mul_arg, Pow): exponents_denom.append(mul_arg.exp.q) assert len(exponents_denom) > 0 if len(exponents_denom) == 1: m = exponents_denom[0] else: # get the LCM of the denominators m = reduce(ilcm, exponents_denom) # x -> y**m. # we assume positive for simplification purposes t = Dummy("t", positive=True) f_ = f.subs(symbol, t ** m) if guess_solve_strategy(f_, t) != GS_POLY: raise NotImplementedError("Could not convert to a polynomial equation: %s" % f_) cv_sols = solve(f_, t) for sol in cv_sols: result.append(sol ** m) elif isinstance(f, Mul): for mul_arg in args: result.extend(solve(mul_arg, symbol)) elif strategy == GS_POLY_CV_2: m = 0 args = list(f.args) if isinstance(f, Add): for arg in args: if isinstance(arg, Pow): m = min(m, arg.exp) elif isinstance(arg, Mul): for mul_arg in arg.args: if isinstance(mul_arg, Pow): m = min(m, mul_arg.exp) elif isinstance(f, Mul): for mul_arg in args: if isinstance(mul_arg, Pow): m = min(m, mul_arg.exp) f1 = simplify(f * symbol ** (-m)) result = solve(f1, symbol) # TODO: we might have introduced unwanted solutions # when multiplied by x**-m elif strategy == GS_PIECEWISE: result = set() for expr, cond in f.args: candidates = solve(expr, *symbols) if isinstance(cond, bool) or cond.is_Number: if not cond: continue # Only include solutions that do not match the condition # of any of the other pieces. for candidate in candidates: matches_other_piece = False for other_expr, other_cond in f.args: if isinstance(other_cond, bool) or other_cond.is_Number: continue if bool(other_cond.subs(symbol, candidate)): matches_other_piece = True break if not matches_other_piece: result.add(candidate) else: for candidate in candidates: if bool(cond.subs(symbol, candidate)): result.add(candidate) result = list(result) elif strategy == GS_TRANSCENDENTAL: # a, b = f.as_numer_denom() # Let's throw away the denominator for now. When we have robust # assumptions, it should be checked, that for the solution, # b!=0. result = tsolve(f, *symbols) elif strategy == -1: raise ValueError("Could not parse expression %s" % f) else: raise NotImplementedError("No algorithms are implemented to solve equation %s" % f) # This symbol swap should not be necessary for the single symbol case: if you've # solved for the symbol the it will not appear in the solution. Right now, however # ode's are getting solutions for solve (even though they shouldn't be -- see the # swap_back test in test_solvers). if symbol_swapped: result = [ri.subs(swap_back_dict) for ri in result] if flags.get("simplified", True) and strategy != GS_RATIONAL: return map(simplify, result) else: return result else: if not f: return {} else: # Create a swap dictionary for storing the passed symbols to be # solved for, so that they may be swapped back. if symbol_swapped: swap_dict = zip(symbols, symbols_new) f = [fi.subs(swap_dict) for fi in f] symbols = symbols_new polys = [] for g in f: poly = g.as_poly(*symbols) if poly is not None: polys.append(poly) else: raise NotImplementedError() if all(p.is_linear for p in polys): n, m = len(f), len(symbols) matrix = zeros((n, m + 1)) for i, poly in enumerate(polys): for monom, coeff in poly.terms(): try: j = list(monom).index(1) matrix[i, j] = coeff except ValueError: matrix[i, m] = -coeff soln = solve_linear_system(matrix, *symbols, **flags) else: soln = solve_poly_system(polys) # Use swap_dict to ensure we return the same type as what was # passed if symbol_swapped: if isinstance(soln, dict): res = {} for k in soln.keys(): res.update({swap_back_dict[k]: soln[k]}) return res else: return soln else: return soln
def dmp_zz_factor(f, u, K): """ Factor (non square-free) polynomials in `Z[X]`. Given a multivariate polynomial `f` in `Z[x]` computes its complete factorization `f_1, ..., f_n` into irreducibles over integers:: f = content(f) f_1**k_1 ... f_n**k_n The factorization is computed by reducing the input polynomial into a primitive square-free polynomial and factoring it using Enhanced Extended Zassenhaus (EEZ) algorithm. Trial division is used to recover the multiplicities of factors. The result is returned as a tuple consisting of:: (content(f), [(f_1, k_1), ..., (f_n, k_n)) Consider polynomial `f = 2*(x**2 - y**2)`:: >>> from sympy.polys.factortools import dmp_zz_factor >>> from sympy.polys.domains import ZZ >>> dmp_zz_factor([[2], [], [-2, 0, 0]], 1, ZZ) (2, [([[1], [-1, 0]], 1), ([[1], [1, 0]], 1)]) In result we got the following factorization:: f = 2 (x - y) (x + y) **References** 1. [Gathen99]_ """ if not u: return dup_zz_factor(f, K) if dmp_zero_p(f, u): return K.zero, [] cont, g = dmp_ground_primitive(f, u, K) if dmp_ground_LC(g, u, K) < 0: cont, g = -cont, dmp_neg(g, u, K) if all([d <= 0 for d in dmp_degree_list(g, u)]): return cont, [] G, g = dmp_primitive(g, u, K) factors = [] if dmp_degree(g, u) > 0: g = dmp_sqf_part(g, u, K) H = dmp_zz_wang(g, u, K) for h in H: k = 0 while True: q, r = dmp_div(f, h, u, K) if dmp_zero_p(r, u): f, k = q, k + 1 else: break factors.append((h, k)) for g, k in dmp_zz_factor(G, u - 1, K)[1]: factors.insert(0, ([g], k)) return cont, _sort_factors(factors)