def test_gcd_terms(): f = 2*(x + 1)*(x + 4)/(5*x**2 + 5) + (2*x + 2)*(x + 5)/(x**2 + 1)/5 + (2*x + 2)*(x + 6)/(5*x**2 + 5) assert _gcd_terms(f) == ((S(6)/5)*((1 + x)/(1 + x**2)), 5 + x, 1) assert _gcd_terms(Add.make_args(f)) == ((S(6)/5)*((1 + x)/(1 + x**2)), 5 + x, 1) assert gcd_terms(f) == (S(6)/5)*((1 + x)*(5 + x)/(1 + x**2)) assert gcd_terms(Add.make_args(f)) == (S(6)/5)*((1 + x)*(5 + x)/(1 + x**2)) assert gcd_terms((2*x + 2)**3 + (2*x + 2)**2) == 4*(x + 1)**2*(2*x + 3) assert gcd_terms(0) == 0 assert gcd_terms(1) == 1 assert gcd_terms(x) == x assert gcd_terms(2 + 2*x) == Mul(2, 1 + x, evaluate=False) arg = x*(2*x + 4*y) garg = 2*x*(x + 2*y) assert gcd_terms(arg) == garg assert gcd_terms(sin(arg)) == sin(garg) # issue 3040-like alpha, alpha1, alpha2, alpha3 = symbols('alpha:4') a = alpha**2 - alpha*x**2 + alpha + x**3 - x*(alpha + 1) rep = (alpha, (1 + sqrt(5))/2 + alpha1*x + alpha2*x**2 + alpha3*x**3) s = (a/(x - alpha)).subs(*rep).series(x, 0, 1) assert simplify(collect(s, x)) == -sqrt(5)/2 - S(3)/2 + O(x) # issue 2818 assert _gcd_terms([S.Zero, S.Zero]) == (0, 0, 1) assert _gcd_terms([2*x + 4]) == (2, x + 2, 1)
def test_factor_terms(): A = Symbol('A', commutative=False) assert factor_terms(9*(x + x*y + 1) + (3*x + 3)**(2 + 2*x)) == \ 9*x*y + 9*x + _keep_coeff(S(3), x + 1)**_keep_coeff(S(2), x + 1) + 9 assert factor_terms(9*(x + x*y + 1) + (3)**(2 + 2*x)) == \ _keep_coeff(S(9), 3**(2*x) + x*y + x + 1) assert factor_terms(3**(2 + 2*x) + a*3**(2 + 2*x)) == \ 9*3**(2*x)*(a + 1) assert factor_terms(x + x*A) == \ x*(1 + A) assert factor_terms(sin(x + x*A)) == \ sin(x*(1 + A)) assert factor_terms((3*x + 3)**((2 + 2*x)/3)) == \ _keep_coeff(S(3), x + 1)**_keep_coeff(S(2)/3, x + 1) assert factor_terms(x + (x*y + x)**(3*x + 3)) == \ x + (x*(y + 1))**_keep_coeff(S(3), x + 1) assert factor_terms(a*(x + x*y) + b*(x*2 + y*x*2)) == \ x*(a + 2*b)*(y + 1) i = Integral(x, (x, 0, oo)) assert factor_terms(i) == i # check radical extraction eq = sqrt(2) + sqrt(10) assert factor_terms(eq) == eq assert factor_terms(eq, radical=True) == sqrt(2)*(1 + sqrt(5)) eq = root(-6, 3) + root(6, 3) assert factor_terms(eq, radical=True) == 6**(S(1)/3)*(1 + (-1)**(S(1)/3)) eq = [x + x*y] ans = [x*(y + 1)] for c in [list, tuple, set]: assert factor_terms(c(eq)) == c(ans) assert factor_terms(Tuple(x + x*y)) == Tuple(x*(y + 1)) assert factor_terms(Interval(0, 1)) == Interval(0, 1) e = 1/sqrt(a/2 + 1) assert factor_terms(e, clear=False) == 1/sqrt(a/2 + 1) assert factor_terms(e, clear=True) == sqrt(2)/sqrt(a + 2) eq = x/(x + 1/x) + 1/(x**2 + 1) assert factor_terms(eq, fraction=False) == eq assert factor_terms(eq, fraction=True) == 1 assert factor_terms((1/(x**3 + x**2) + 2/x**2)*y) == \ y*(2 + 1/(x + 1))/x**2 # if not True, then processesing for this in factor_terms is not necessary assert gcd_terms(-x - y) == -x - y assert factor_terms(-x - y) == Mul(-1, x + y, evaluate=False) # if not True, then "special" processesing in factor_terms is not necessary assert gcd_terms(exp(Mul(-1, x + 1))) == exp(-x - 1) e = exp(-x - 2) + x assert factor_terms(e) == exp(Mul(-1, x + 2, evaluate=False)) + x assert factor_terms(e, sign=False) == e assert factor_terms(exp(-4*x - 2) - x) == -x + exp(Mul(-2, 2*x + 1, evaluate=False))
def test_gcd_terms(): f = 2*(x + 1)*(x + 4)/(5*x**2 + 5) + (2*x + 2)*(x + 5)/(x**2 + 1)/5 + (2*x + 2)*(x + 6)/(5*x**2 + 5) assert _gcd_terms(f) == ((S(6)/5)*((1 + x)/(1 + x**2)), 5 + x, 1) assert _gcd_terms(Add.make_args(f)) == ((S(6)/5)*((1 + x)/(1 + x**2)), 5 + x, 1) assert gcd_terms(f) == (S(6)/5)*((1 + x)*(5 + x)/(1 + x**2)) assert gcd_terms(Add.make_args(f)) == (S(6)/5)*((1 + x)*(5 + x)/(1 + x**2)) assert gcd_terms(0) == 0 assert gcd_terms(1) == 1 assert gcd_terms(x) == x
def test_gcd_terms(): f = 2*(x + 1)*(x + 4)/(5*x**2 + 5) + (2*x + 2)*(x + 5)/(x**2 + 1)/5 + (2*x + 2)*(x + 6)/(5*x**2 + 5) assert _gcd_terms(f) == ((S(6)/5)*((1 + x)/(1 + x**2)), 5 + x, 1) assert _gcd_terms(Add.make_args(f)) == ((S(6)/5)*((1 + x)/(1 + x**2)), 5 + x, 1) assert gcd_terms(f) == (S(6)/5)*((1 + x)*(5 + x)/(1 + x**2)) assert gcd_terms(Add.make_args(f)) == (S(6)/5)*((1 + x)*(5 + x)/(1 + x**2)) assert gcd_terms((2*x + 2)**3 + (2*x + 2)**2) == 4*(x + 1)**2*(2*x + 3) assert gcd_terms(0) == 0 assert gcd_terms(1) == 1 assert gcd_terms(x) == x assert gcd_terms(2 + 2*x) == Mul(2, 1 + x, evaluate=False) arg = x*(2*x + 4*y) garg = 2*x*(x + 2*y) assert gcd_terms(arg) == garg assert gcd_terms(sin(arg)) == sin(garg)
def _together(expr): if isinstance(expr, Basic): if expr.is_Atom or (expr.is_Function and not deep): return expr elif expr.is_Add: return gcd_terms(map(_together, Add.make_args(expr))) elif expr.is_Pow: base = _together(expr.base) if deep: exp = _together(expr.exp) else: exp = expr.exp return expr.__class__(base, exp) else: return expr.__class__(*[ _together(arg) for arg in expr.args ]) elif hasattr(expr, '__iter__'): return expr.__class__([ _together(ex) for ex in expr ]) return expr
def _together(expr): if isinstance(expr, Basic): if expr.is_Atom or (expr.is_Function and not deep): return expr elif expr.is_Add: return gcd_terms(list(map(_together, Add.make_args(expr)))) elif expr.is_Pow: base = _together(expr.base) if deep: exp = _together(expr.exp) else: exp = expr.exp return expr.__class__(base, exp) else: return expr.__class__(*[_together(arg) for arg in expr.args]) elif hasattr(expr, '__iter__'): return expr.__class__([_together(ex) for ex in expr]) return expr
def test_factor_terms(): A = Symbol('A', commutative=False) assert factor_terms(9*(x + x*y + 1) + (3*x + 3)**(2 + 2*x)) == \ 9*x*y + 9*x + _keep_coeff(S(3), x + 1)**_keep_coeff(S(2), x + 1) + 9 assert factor_terms(9*(x + x*y + 1) + (3)**(2 + 2*x)) == \ _keep_coeff(S(9), 3**(2*x) + x*y + x + 1) assert factor_terms(3**(2 + 2*x) + a*3**(2 + 2*x)) == \ 9*3**(2*x)*(a + 1) assert factor_terms(x + x*A) == \ x*(1 + A) assert factor_terms(sin(x + x*A)) == \ sin(x*(1 + A)) assert factor_terms((3*x + 3)**((2 + 2*x)/3)) == \ _keep_coeff(S(3), x + 1)**_keep_coeff(S(2)/3, x + 1) assert factor_terms(x + (x*y + x)**(3*x + 3)) == \ x + (x*(y + 1))**_keep_coeff(S(3), x + 1) assert factor_terms(a*(x + x*y) + b*(x*2 + y*x*2)) == \ x*(a + 2*b)*(y + 1) i = Integral(x, (x, 0, oo)) assert factor_terms(i) == i assert factor_terms(x / 2 + y) == x / 2 + y # fraction doesn't apply to integer denominators assert factor_terms(x / 2 + y, fraction=True) == x / 2 + y # clear *does* apply to the integer denominators assert factor_terms(x / 2 + y, clear=True) == Mul(S.Half, x + 2 * y, evaluate=False) # check radical extraction eq = sqrt(2) + sqrt(10) assert factor_terms(eq) == eq assert factor_terms(eq, radical=True) == sqrt(2) * (1 + sqrt(5)) eq = root(-6, 3) + root(6, 3) assert factor_terms(eq, radical=True) == 6**(S(1) / 3) * (1 + (-1)**(S(1) / 3)) eq = [x + x * y] ans = [x * (y + 1)] for c in [list, tuple, set]: assert factor_terms(c(eq)) == c(ans) assert factor_terms(Tuple(x + x * y)) == Tuple(x * (y + 1)) assert factor_terms(Interval(0, 1)) == Interval(0, 1) e = 1 / sqrt(a / 2 + 1) assert factor_terms(e, clear=False) == 1 / sqrt(a / 2 + 1) assert factor_terms(e, clear=True) == sqrt(2) / sqrt(a + 2) eq = x / (x + 1 / x) + 1 / (x**2 + 1) assert factor_terms(eq, fraction=False) == eq assert factor_terms(eq, fraction=True) == 1 assert factor_terms((1/(x**3 + x**2) + 2/x**2)*y) == \ y*(2 + 1/(x + 1))/x**2 # if not True, then processesing for this in factor_terms is not necessary assert gcd_terms(-x - y) == -x - y assert factor_terms(-x - y) == Mul(-1, x + y, evaluate=False) # if not True, then "special" processesing in factor_terms is not necessary assert gcd_terms(exp(Mul(-1, x + 1))) == exp(-x - 1) e = exp(-x - 2) + x assert factor_terms(e) == exp(Mul(-1, x + 2, evaluate=False)) + x assert factor_terms(e, sign=False) == e assert factor_terms(exp(-4 * x - 2) - x) == -x + exp(Mul(-2, 2 * x + 1, evaluate=False)) # sum/integral tests for F in (Sum, Integral): assert factor_terms(F(x, (y, 1, 10))) == x * F(1, (y, 1, 10)) assert factor_terms(F(x, (y, 1, 10)) + x) == x * (1 + F(1, (y, 1, 10))) assert factor_terms(F(x * y + x * y**2, (y, 1, 10))) == x * F(y * (y + 1), (y, 1, 10))
def test_gcd_terms(): f = 2*(x + 1)*(x + 4)/(5*x**2 + 5) + (2*x + 2)*(x + 5)/(x**2 + 1)/5 + \ (2*x + 2)*(x + 6)/(5*x**2 + 5) assert _gcd_terms(f) == ((S(6) / 5) * ((1 + x) / (1 + x**2)), 5 + x, 1) assert _gcd_terms(Add.make_args(f)) == \ ((S(6)/5)*((1 + x)/(1 + x**2)), 5 + x, 1) newf = (S(6) / 5) * ((1 + x) * (5 + x) / (1 + x**2)) assert gcd_terms(f) == newf args = Add.make_args(f) # non-Basic sequences of terms treated as terms of Add assert gcd_terms(list(args)) == newf assert gcd_terms(tuple(args)) == newf assert gcd_terms(set(args)) == newf # but a Basic sequence is treated as a container assert gcd_terms(Tuple(*args)) != newf assert gcd_terms(Basic(Tuple(1, 3*y + 3*x*y), Tuple(1, 3))) == \ Basic((1, 3*y*(x + 1)), (1, 3)) # but we shouldn't change keys of a dictionary or some may be lost assert gcd_terms(Dict((x*(1 + y), 2), (x + x*y, y + x*y))) == \ Dict({x*(y + 1): 2, x + x*y: y*(1 + x)}) assert gcd_terms((2 * x + 2)**3 + (2 * x + 2)**2) == 4 * (x + 1)**2 * (2 * x + 3) assert gcd_terms(0) == 0 assert gcd_terms(1) == 1 assert gcd_terms(x) == x assert gcd_terms(2 + 2 * x) == Mul(2, 1 + x, evaluate=False) arg = x * (2 * x + 4 * y) garg = 2 * x * (x + 2 * y) assert gcd_terms(arg) == garg assert gcd_terms(sin(arg)) == sin(garg) # issue 6139-like alpha, alpha1, alpha2, alpha3 = symbols('alpha:4') a = alpha**2 - alpha * x**2 + alpha + x**3 - x * (alpha + 1) rep = (alpha, (1 + sqrt(5)) / 2 + alpha1 * x + alpha2 * x**2 + alpha3 * x**3) s = (a / (x - alpha)).subs(*rep).series(x, 0, 1) assert simplify(collect(s, x)) == -sqrt(5) / 2 - S(3) / 2 + O(x) # issue 5917 assert _gcd_terms([S.Zero, S.Zero]) == (0, 0, 1) assert _gcd_terms([2 * x + 4]) == (2, x + 2, 1) eq = x / (x + 1 / x) assert gcd_terms(eq, fraction=False) == eq eq = x / 2 / y + 1 / x / y assert gcd_terms(eq, fraction=True, clear=True) == \ (x**2 + 2)/(2*x*y) assert gcd_terms(eq, fraction=True, clear=False) == \ (x**2/2 + 1)/(x*y) assert gcd_terms(eq, fraction=False, clear=True) == \ (x + 2/x)/(2*y) assert gcd_terms(eq, fraction=False, clear=False) == \ (x/2 + 1/x)/y
def eval(cls, p, q): from sympy.core.add import Add from sympy.core.mul import Mul from sympy.core.singleton import S from sympy.core.exprtools import gcd_terms from sympy.polys.polytools import gcd def doit(p, q): """Try to return p % q if both are numbers or +/-p is known to be less than q. """ if p == q or p == -q or p.is_Pow and p.exp.is_Integer and p.base == q: return S.Zero if p.is_Number and q.is_Number: return (p % q) # by ratio r = p/q try: d = int(r) except TypeError: pass else: if type(d) is int: rv = p - d*q if (rv*q < 0) is True: rv += q return rv # by differencec d = p - q if d.is_negative: if q.is_negative: return d elif q.is_positive: return p rv = doit(p, q) if rv is not None: return rv # denest if p.func is cls: # easy qinner = p.args[1] if qinner == q: return p # XXX other possibilities? # extract gcd; any further simplification should be done by the user G = gcd(p, q) if G is not S.One: p, q = [ gcd_terms(i/G, clear=False, fraction=False) for i in (p, q)] pwas, qwas = p, q # simplify terms # (x + y + 2) % x -> Mod(y + 2, x) if p.is_Add: args = [] for i in p.args: a = cls(i, q) if a.count(cls) > i.count(cls): args.append(i) else: args.append(a) if args != list(p.args): p = Add(*args) else: # handle coefficients if they are not Rational # since those are not handled by factor_terms # e.g. Mod(.6*x, .3*y) -> 0.3*Mod(2*x, y) cp, p = p.as_coeff_Mul() cq, q = q.as_coeff_Mul() ok = False if not cp.is_Rational or not cq.is_Rational: r = cp % cq if r == 0: G *= cq p *= int(cp/cq) ok = True if not ok: p = cp*p q = cq*q # simple -1 extraction if p.could_extract_minus_sign() and q.could_extract_minus_sign(): G, p, q = [-i for i in (G, p, q)] # check again to see if p and q can now be handled as numbers rv = doit(p, q) if rv is not None: return rv*G # put 1.0 from G on inside if G.is_Float and G == 1: p *= G return cls(p, q, evaluate=False) elif G.is_Mul and G.args[0].is_Float and G.args[0] == 1: p = G.args[0]*p G = Mul._from_args(G.args[1:]) return G*cls(p, q, evaluate=(p, q) != (pwas, qwas))
def eval(cls, p, q): from sympy.core.add import Add from sympy.core.mul import Mul from sympy.core.singleton import S from sympy.core.exprtools import gcd_terms from sympy.polys.polytools import gcd def doit(p, q): """Try to return p % q if both are numbers or +/-p is known to be less than or equal q. """ if q.is_zero: raise ZeroDivisionError("Modulo by zero") if p.is_finite is False or q.is_finite is False or p is nan or q is nan: return nan if p is S.Zero or p == q or p == -q or (p.is_integer and q == 1): return S.Zero if q.is_Number: if p.is_Number: return p % q if q == 2: if p.is_even: return S.Zero elif p.is_odd: return S.One if hasattr(p, '_eval_Mod'): rv = getattr(p, '_eval_Mod')(q) if rv is not None: return rv # by ratio r = p / q try: d = int(r) except TypeError: pass else: if isinstance(d, int): rv = p - d * q if (rv * q < 0) == True: rv += q return rv # by difference # -2|q| < p < 2|q| d = abs(p) for _ in range(2): d -= abs(q) if d.is_negative: if q.is_positive: if p.is_positive: return d + q elif p.is_negative: return -d elif q.is_negative: if p.is_positive: return d elif p.is_negative: return -d + q break rv = doit(p, q) if rv is not None: return rv # denest if isinstance(p, cls): qinner = p.args[1] if qinner % q == 0: return cls(p.args[0], q) elif (qinner * (q - qinner)).is_nonnegative: # |qinner| < |q| and have same sign return p elif isinstance(-p, cls): qinner = (-p).args[1] if qinner % q == 0: return cls(-(-p).args[0], q) elif (qinner * (q + qinner)).is_nonpositive: # |qinner| < |q| and have different sign return p elif isinstance(p, Add): # separating into modulus and non modulus both_l = non_mod_l, mod_l = [], [] for arg in p.args: both_l[isinstance(arg, cls)].append(arg) # if q same for all if mod_l and all(inner.args[1] == q for inner in mod_l): net = Add(*non_mod_l) + Add(*[i.args[0] for i in mod_l]) return cls(net, q) elif isinstance(p, Mul): # separating into modulus and non modulus both_l = non_mod_l, mod_l = [], [] for arg in p.args: both_l[isinstance(arg, cls)].append(arg) if mod_l and all(inner.args[1] == q for inner in mod_l): # finding distributive term non_mod_l = [cls(x, q) for x in non_mod_l] mod = [] non_mod = [] for j in non_mod_l: if isinstance(j, cls): mod.append(j.args[0]) else: non_mod.append(j) prod_mod = Mul(*mod) prod_non_mod = Mul(*non_mod) prod_mod1 = Mul(*[i.args[0] for i in mod_l]) net = prod_mod1 * prod_mod return prod_non_mod * cls(net, q) if q.is_Integer and q is not S.One: _ = [] for i in non_mod_l: if i.is_Integer and (i % q is not S.Zero): _.append(i % q) else: _.append(i) non_mod_l = _ p = Mul(*(non_mod_l + mod_l)) # XXX other possibilities? # extract gcd; any further simplification should be done by the user G = gcd(p, q) if G != 1: p, q = [ gcd_terms(i / G, clear=False, fraction=False) for i in (p, q) ] pwas, qwas = p, q # simplify terms # (x + y + 2) % x -> Mod(y + 2, x) if p.is_Add: args = [] for i in p.args: a = cls(i, q) if a.count(cls) > i.count(cls): args.append(i) else: args.append(a) if args != list(p.args): p = Add(*args) else: # handle coefficients if they are not Rational # since those are not handled by factor_terms # e.g. Mod(.6*x, .3*y) -> 0.3*Mod(2*x, y) cp, p = p.as_coeff_Mul() cq, q = q.as_coeff_Mul() ok = False if not cp.is_Rational or not cq.is_Rational: r = cp % cq if r == 0: G *= cq p *= int(cp / cq) ok = True if not ok: p = cp * p q = cq * q # simple -1 extraction if p.could_extract_minus_sign() and q.could_extract_minus_sign(): G, p, q = [-i for i in (G, p, q)] # check again to see if p and q can now be handled as numbers rv = doit(p, q) if rv is not None: return rv * G # put 1.0 from G on inside if G.is_Float and G == 1: p *= G return cls(p, q, evaluate=False) elif G.is_Mul and G.args[0].is_Float and G.args[0] == 1: p = G.args[0] * p G = Mul._from_args(G.args[1:]) return G * cls(p, q, evaluate=(p, q) != (pwas, qwas))
def radsimp(expr, symbolic=True, max_terms=4): r""" Rationalize the denominator by removing square roots. Explanation =========== The expression returned from radsimp must be used with caution since if the denominator contains symbols, it will be possible to make substitutions that violate the assumptions of the simplification process: that for a denominator matching a + b*sqrt(c), a != +/-b*sqrt(c). (If there are no symbols, this assumptions is made valid by collecting terms of sqrt(c) so the match variable ``a`` does not contain ``sqrt(c)``.) If you do not want the simplification to occur for symbolic denominators, set ``symbolic`` to False. If there are more than ``max_terms`` radical terms then the expression is returned unchanged. Examples ======== >>> from sympy import radsimp, sqrt, Symbol, pprint >>> from sympy import factor_terms, fraction, signsimp >>> from sympy.simplify.radsimp import collect_sqrt >>> from sympy.abc import a, b, c >>> radsimp(1/(2 + sqrt(2))) (2 - sqrt(2))/2 >>> x,y = map(Symbol, 'xy') >>> e = ((2 + 2*sqrt(2))*x + (2 + sqrt(8))*y)/(2 + sqrt(2)) >>> radsimp(e) sqrt(2)*(x + y) No simplification beyond removal of the gcd is done. One might want to polish the result a little, however, by collecting square root terms: >>> r2 = sqrt(2) >>> r5 = sqrt(5) >>> ans = radsimp(1/(y*r2 + x*r2 + a*r5 + b*r5)); pprint(ans) ___ ___ ___ ___ \/ 5 *a + \/ 5 *b - \/ 2 *x - \/ 2 *y ------------------------------------------ 2 2 2 2 5*a + 10*a*b + 5*b - 2*x - 4*x*y - 2*y >>> n, d = fraction(ans) >>> pprint(factor_terms(signsimp(collect_sqrt(n))/d, radical=True)) ___ ___ \/ 5 *(a + b) - \/ 2 *(x + y) ------------------------------------------ 2 2 2 2 5*a + 10*a*b + 5*b - 2*x - 4*x*y - 2*y If radicals in the denominator cannot be removed or there is no denominator, the original expression will be returned. >>> radsimp(sqrt(2)*x + sqrt(2)) sqrt(2)*x + sqrt(2) Results with symbols will not always be valid for all substitutions: >>> eq = 1/(a + b*sqrt(c)) >>> eq.subs(a, b*sqrt(c)) 1/(2*b*sqrt(c)) >>> radsimp(eq).subs(a, b*sqrt(c)) nan If ``symbolic=False``, symbolic denominators will not be transformed (but numeric denominators will still be processed): >>> radsimp(eq, symbolic=False) 1/(a + b*sqrt(c)) """ from sympy.simplify.simplify import signsimp syms = symbols("a:d A:D") def _num(rterms): # return the multiplier that will simplify the expression described # by rterms [(sqrt arg, coeff), ... ] a, b, c, d, A, B, C, D = syms if len(rterms) == 2: reps = dict(list(zip([A, a, B, b], [j for i in rterms for j in i]))) return ( sqrt(A)*a - sqrt(B)*b).xreplace(reps) if len(rterms) == 3: reps = dict(list(zip([A, a, B, b, C, c], [j for i in rterms for j in i]))) return ( (sqrt(A)*a + sqrt(B)*b - sqrt(C)*c)*(2*sqrt(A)*sqrt(B)*a*b - A*a**2 - B*b**2 + C*c**2)).xreplace(reps) elif len(rterms) == 4: reps = dict(list(zip([A, a, B, b, C, c, D, d], [j for i in rterms for j in i]))) return ((sqrt(A)*a + sqrt(B)*b - sqrt(C)*c - sqrt(D)*d)*(2*sqrt(A)*sqrt(B)*a*b - A*a**2 - B*b**2 - 2*sqrt(C)*sqrt(D)*c*d + C*c**2 + D*d**2)*(-8*sqrt(A)*sqrt(B)*sqrt(C)*sqrt(D)*a*b*c*d + A**2*a**4 - 2*A*B*a**2*b**2 - 2*A*C*a**2*c**2 - 2*A*D*a**2*d**2 + B**2*b**4 - 2*B*C*b**2*c**2 - 2*B*D*b**2*d**2 + C**2*c**4 - 2*C*D*c**2*d**2 + D**2*d**4)).xreplace(reps) elif len(rterms) == 1: return sqrt(rterms[0][0]) else: raise NotImplementedError def ispow2(d, log2=False): if not d.is_Pow: return False e = d.exp if e.is_Rational and e.q == 2 or symbolic and denom(e) == 2: return True if log2: q = 1 if e.is_Rational: q = e.q elif symbolic: d = denom(e) if d.is_Integer: q = d if q != 1 and log(q, 2).is_Integer: return True return False def handle(expr): # Handle first reduces to the case # expr = 1/d, where d is an add, or d is base**p/2. # We do this by recursively calling handle on each piece. from sympy.simplify.simplify import nsimplify n, d = fraction(expr) if expr.is_Atom or (d.is_Atom and n.is_Atom): return expr elif not n.is_Atom: n = n.func(*[handle(a) for a in n.args]) return _unevaluated_Mul(n, handle(1/d)) elif n is not S.One: return _unevaluated_Mul(n, handle(1/d)) elif d.is_Mul: return _unevaluated_Mul(*[handle(1/d) for d in d.args]) # By this step, expr is 1/d, and d is not a mul. if not symbolic and d.free_symbols: return expr if ispow2(d): d2 = sqrtdenest(sqrt(d.base))**numer(d.exp) if d2 != d: return handle(1/d2) elif d.is_Pow and (d.exp.is_integer or d.base.is_positive): # (1/d**i) = (1/d)**i return handle(1/d.base)**d.exp if not (d.is_Add or ispow2(d)): return 1/d.func(*[handle(a) for a in d.args]) # handle 1/d treating d as an Add (though it may not be) keep = True # keep changes that are made # flatten it and collect radicals after checking for special # conditions d = _mexpand(d) # did it change? if d.is_Atom: return 1/d # is it a number that might be handled easily? if d.is_number: _d = nsimplify(d) if _d.is_Number and _d.equals(d): return 1/_d while True: # collect similar terms collected = defaultdict(list) for m in Add.make_args(d): # d might have become non-Add p2 = [] other = [] for i in Mul.make_args(m): if ispow2(i, log2=True): p2.append(i.base if i.exp is S.Half else i.base**(2*i.exp)) elif i is S.ImaginaryUnit: p2.append(S.NegativeOne) else: other.append(i) collected[tuple(ordered(p2))].append(Mul(*other)) rterms = list(ordered(list(collected.items()))) rterms = [(Mul(*i), Add(*j)) for i, j in rterms] nrad = len(rterms) - (1 if rterms[0][0] is S.One else 0) if nrad < 1: break elif nrad > max_terms: # there may have been invalid operations leading to this point # so don't keep changes, e.g. this expression is troublesome # in collecting terms so as not to raise the issue of 2834: # r = sqrt(sqrt(5) + 5) # eq = 1/(sqrt(5)*r + 2*sqrt(5)*sqrt(-sqrt(5) + 5) + 5*r) keep = False break if len(rterms) > 4: # in general, only 4 terms can be removed with repeated squaring # but other considerations can guide selection of radical terms # so that radicals are removed if all([x.is_Integer and (y**2).is_Rational for x, y in rterms]): nd, d = rad_rationalize(S.One, Add._from_args( [sqrt(x)*y for x, y in rterms])) n *= nd else: # is there anything else that might be attempted? keep = False break from sympy.simplify.powsimp import powsimp, powdenest num = powsimp(_num(rterms)) n *= num d *= num d = powdenest(_mexpand(d), force=symbolic) if d.is_Atom: break if not keep: return expr return _unevaluated_Mul(n, 1/d) coeff, expr = expr.as_coeff_Add() expr = expr.normal() old = fraction(expr) n, d = fraction(handle(expr)) if old != (n, d): if not d.is_Atom: was = (n, d) n = signsimp(n, evaluate=False) d = signsimp(d, evaluate=False) u = Factors(_unevaluated_Mul(n, 1/d)) u = _unevaluated_Mul(*[k**v for k, v in u.factors.items()]) n, d = fraction(u) if old == (n, d): n, d = was n = expand_mul(n) if d.is_Number or d.is_Add: n2, d2 = fraction(gcd_terms(_unevaluated_Mul(n, 1/d))) if d2.is_Number or (d2.count_ops() <= d.count_ops()): n, d = [signsimp(i) for i in (n2, d2)] if n.is_Mul and n.args[0].is_Number: n = n.func(*n.args) return coeff + _unevaluated_Mul(n, 1/d)
def test_gcd_terms(): f = 2*(x + 1)*(x + 4)/(5*x**2 + 5) + (2*x + 2)*(x + 5)/(x**2 + 1)/5 + \ (2*x + 2)*(x + 6)/(5*x**2 + 5) assert _gcd_terms(f) == ((S(6)/5)*((1 + x)/(1 + x**2)), 5 + x, 1) assert _gcd_terms(Add.make_args(f)) == \ ((S(6)/5)*((1 + x)/(1 + x**2)), 5 + x, 1) newf = (S(6)/5)*((1 + x)*(5 + x)/(1 + x**2)) assert gcd_terms(f) == newf args = Add.make_args(f) # non-Basic sequences of terms treated as terms of Add assert gcd_terms(list(args)) == newf assert gcd_terms(tuple(args)) == newf assert gcd_terms(set(args)) == newf # but a Basic sequence is treated as a container assert gcd_terms(Tuple(*args)) != newf assert gcd_terms(Basic(Tuple(1, 3*y + 3*x*y), Tuple(1, 3))) == \ Basic((1, 3*y*(x + 1)), (1, 3)) # but we shouldn't change keys of a dictionary or some may be lost assert gcd_terms(Dict((x*(1 + y), 2), (x + x*y, y + x*y))) == \ Dict({x*(y + 1): 2, x + x*y: y*(1 + x)}) assert gcd_terms((2*x + 2)**3 + (2*x + 2)**2) == 4*(x + 1)**2*(2*x + 3) assert gcd_terms(0) == 0 assert gcd_terms(1) == 1 assert gcd_terms(x) == x assert gcd_terms(2 + 2*x) == Mul(2, 1 + x, evaluate=False) arg = x*(2*x + 4*y) garg = 2*x*(x + 2*y) assert gcd_terms(arg) == garg assert gcd_terms(sin(arg)) == sin(garg) # issue 3040-like alpha, alpha1, alpha2, alpha3 = symbols('alpha:4') a = alpha**2 - alpha*x**2 + alpha + x**3 - x*(alpha + 1) rep = (alpha, (1 + sqrt(5))/2 + alpha1*x + alpha2*x**2 + alpha3*x**3) s = (a/(x - alpha)).subs(*rep).series(x, 0, 1) assert simplify(collect(s, x)) == -sqrt(5)/2 - S(3)/2 + O(x) # issue 2818 assert _gcd_terms([S.Zero, S.Zero]) == (0, 0, 1) assert _gcd_terms([2*x + 4]) == (2, x + 2, 1) eq = x/(x + 1/x) assert gcd_terms(eq, fraction=False) == eq
def eval(cls, p, q): from sympy.core.add import Add from sympy.core.mul import Mul from sympy.core.singleton import S from sympy.core.exprtools import gcd_terms from sympy.polys.polytools import gcd def doit(p, q): """Try to return p % q if both are numbers or +/-p is known to be less than or equal q. """ if q == S.Zero: raise ZeroDivisionError("Modulo by zero") if p.is_infinite or q.is_infinite or p is nan or q is nan: return nan if p == S.Zero or p == q or p == -q or (p.is_integer and q == 1): return S.Zero if q.is_Number: if p.is_Number: return p%q if q == 2: if p.is_even: return S.Zero elif p.is_odd: return S.One if hasattr(p, '_eval_Mod'): rv = getattr(p, '_eval_Mod')(q) if rv is not None: return rv # by ratio r = p/q try: d = int(r) except TypeError: pass else: if isinstance(d, integer_types): rv = p - d*q if (rv*q < 0) == True: rv += q return rv # by difference # -2|q| < p < 2|q| d = abs(p) for _ in range(2): d -= abs(q) if d.is_negative: if q.is_positive: if p.is_positive: return d + q elif p.is_negative: return -d elif q.is_negative: if p.is_positive: return d elif p.is_negative: return -d + q break rv = doit(p, q) if rv is not None: return rv # denest if isinstance(p, cls): qinner = p.args[1] if qinner % q == 0: return cls(p.args[0], q) elif (qinner*(q - qinner)).is_nonnegative: # |qinner| < |q| and have same sign return p elif isinstance(-p, cls): qinner = (-p).args[1] if qinner % q == 0: return cls(-(-p).args[0], q) elif (qinner*(q + qinner)).is_nonpositive: # |qinner| < |q| and have different sign return p elif isinstance(p, Add): # separating into modulus and non modulus both_l = non_mod_l, mod_l = [], [] for arg in p.args: both_l[isinstance(arg, cls)].append(arg) # if q same for all if mod_l and all(inner.args[1] == q for inner in mod_l): net = Add(*non_mod_l) + Add(*[i.args[0] for i in mod_l]) return cls(net, q) elif isinstance(p, Mul): # separating into modulus and non modulus both_l = non_mod_l, mod_l = [], [] for arg in p.args: both_l[isinstance(arg, cls)].append(arg) if mod_l and all(inner.args[1] == q for inner in mod_l): # finding distributive term non_mod_l = [cls(x, q) for x in non_mod_l] mod = [] non_mod = [] for j in non_mod_l: if isinstance(j, cls): mod.append(j.args[0]) else: non_mod.append(j) prod_mod = Mul(*mod) prod_non_mod = Mul(*non_mod) prod_mod1 = Mul(*[i.args[0] for i in mod_l]) net = prod_mod1*prod_mod return prod_non_mod*cls(net, q) if q.is_Integer and q is not S.One: _ = [] for i in non_mod_l: if i.is_Integer and (i % q is not S.Zero): _.append(i%q) else: _.append(i) non_mod_l = _ p = Mul(*(non_mod_l + mod_l)) # XXX other possibilities? # extract gcd; any further simplification should be done by the user G = gcd(p, q) if G != 1: p, q = [ gcd_terms(i/G, clear=False, fraction=False) for i in (p, q)] pwas, qwas = p, q # simplify terms # (x + y + 2) % x -> Mod(y + 2, x) if p.is_Add: args = [] for i in p.args: a = cls(i, q) if a.count(cls) > i.count(cls): args.append(i) else: args.append(a) if args != list(p.args): p = Add(*args) else: # handle coefficients if they are not Rational # since those are not handled by factor_terms # e.g. Mod(.6*x, .3*y) -> 0.3*Mod(2*x, y) cp, p = p.as_coeff_Mul() cq, q = q.as_coeff_Mul() ok = False if not cp.is_Rational or not cq.is_Rational: r = cp % cq if r == 0: G *= cq p *= int(cp/cq) ok = True if not ok: p = cp*p q = cq*q # simple -1 extraction if p.could_extract_minus_sign() and q.could_extract_minus_sign(): G, p, q = [-i for i in (G, p, q)] # check again to see if p and q can now be handled as numbers rv = doit(p, q) if rv is not None: return rv*G # put 1.0 from G on inside if G.is_Float and G == 1: p *= G return cls(p, q, evaluate=False) elif G.is_Mul and G.args[0].is_Float and G.args[0] == 1: p = G.args[0]*p G = Mul._from_args(G.args[1:]) return G*cls(p, q, evaluate=(p, q) != (pwas, qwas))
def test_issue_21623(): from sympy import MatrixSymbol M = MatrixSymbol('X', 2, 2) assert gcd_terms(M[0, 0], 1) == M[0, 0]
def radsimp(expr, symbolic=True, max_terms=4): r""" Rationalize the denominator by removing square roots. Note: the expression returned from radsimp must be used with caution since if the denominator contains symbols, it will be possible to make substitutions that violate the assumptions of the simplification process: that for a denominator matching a + b*sqrt(c), a != +/-b*sqrt(c). (If there are no symbols, this assumptions is made valid by collecting terms of sqrt(c) so the match variable ``a`` does not contain ``sqrt(c)``.) If you do not want the simplification to occur for symbolic denominators, set ``symbolic`` to False. If there are more than ``max_terms`` radical terms then the expression is returned unchanged. Examples ======== >>> from sympy import radsimp, sqrt, Symbol, denom, pprint, I >>> from sympy import factor_terms, fraction, signsimp >>> from sympy.simplify.radsimp import collect_sqrt >>> from sympy.abc import a, b, c >>> radsimp(1/(2 + sqrt(2))) (-sqrt(2) + 2)/2 >>> x,y = map(Symbol, 'xy') >>> e = ((2 + 2*sqrt(2))*x + (2 + sqrt(8))*y)/(2 + sqrt(2)) >>> radsimp(e) sqrt(2)*(x + y) No simplification beyond removal of the gcd is done. One might want to polish the result a little, however, by collecting square root terms: >>> r2 = sqrt(2) >>> r5 = sqrt(5) >>> ans = radsimp(1/(y*r2 + x*r2 + a*r5 + b*r5)); pprint(ans) ___ ___ ___ ___ \/ 5 *a + \/ 5 *b - \/ 2 *x - \/ 2 *y ------------------------------------------ 2 2 2 2 5*a + 10*a*b + 5*b - 2*x - 4*x*y - 2*y >>> n, d = fraction(ans) >>> pprint(factor_terms(signsimp(collect_sqrt(n))/d, radical=True)) ___ ___ \/ 5 *(a + b) - \/ 2 *(x + y) ------------------------------------------ 2 2 2 2 5*a + 10*a*b + 5*b - 2*x - 4*x*y - 2*y If radicals in the denominator cannot be removed or there is no denominator, the original expression will be returned. >>> radsimp(sqrt(2)*x + sqrt(2)) sqrt(2)*x + sqrt(2) Results with symbols will not always be valid for all substitutions: >>> eq = 1/(a + b*sqrt(c)) >>> eq.subs(a, b*sqrt(c)) 1/(2*b*sqrt(c)) >>> radsimp(eq).subs(a, b*sqrt(c)) nan If symbolic=False, symbolic denominators will not be transformed (but numeric denominators will still be processed): >>> radsimp(eq, symbolic=False) 1/(a + b*sqrt(c)) """ from sympy.simplify.simplify import signsimp syms = symbols("a:d A:D") def _num(rterms): # return the multiplier that will simplify the expression described # by rterms [(sqrt arg, coeff), ... ] a, b, c, d, A, B, C, D = syms if len(rterms) == 2: reps = dict(list(zip([A, a, B, b], [j for i in rterms for j in i]))) return ( sqrt(A)*a - sqrt(B)*b).xreplace(reps) if len(rterms) == 3: reps = dict(list(zip([A, a, B, b, C, c], [j for i in rterms for j in i]))) return ( (sqrt(A)*a + sqrt(B)*b - sqrt(C)*c)*(2*sqrt(A)*sqrt(B)*a*b - A*a**2 - B*b**2 + C*c**2)).xreplace(reps) elif len(rterms) == 4: reps = dict(list(zip([A, a, B, b, C, c, D, d], [j for i in rterms for j in i]))) return ((sqrt(A)*a + sqrt(B)*b - sqrt(C)*c - sqrt(D)*d)*(2*sqrt(A)*sqrt(B)*a*b - A*a**2 - B*b**2 - 2*sqrt(C)*sqrt(D)*c*d + C*c**2 + D*d**2)*(-8*sqrt(A)*sqrt(B)*sqrt(C)*sqrt(D)*a*b*c*d + A**2*a**4 - 2*A*B*a**2*b**2 - 2*A*C*a**2*c**2 - 2*A*D*a**2*d**2 + B**2*b**4 - 2*B*C*b**2*c**2 - 2*B*D*b**2*d**2 + C**2*c**4 - 2*C*D*c**2*d**2 + D**2*d**4)).xreplace(reps) elif len(rterms) == 1: return sqrt(rterms[0][0]) else: raise NotImplementedError def ispow2(d, log2=False): if not d.is_Pow: return False e = d.exp if e.is_Rational and e.q == 2 or symbolic and denom(e) == 2: return True if log2: q = 1 if e.is_Rational: q = e.q elif symbolic: d = denom(e) if d.is_Integer: q = d if q != 1 and log(q, 2).is_Integer: return True return False def handle(expr): # Handle first reduces to the case # expr = 1/d, where d is an add, or d is base**p/2. # We do this by recursively calling handle on each piece. from sympy.simplify.simplify import nsimplify n, d = fraction(expr) if expr.is_Atom or (d.is_Atom and n.is_Atom): return expr elif not n.is_Atom: n = n.func(*[handle(a) for a in n.args]) return _unevaluated_Mul(n, handle(1/d)) elif n is not S.One: return _unevaluated_Mul(n, handle(1/d)) elif d.is_Mul: return _unevaluated_Mul(*[handle(1/d) for d in d.args]) # By this step, expr is 1/d, and d is not a mul. if not symbolic and d.free_symbols: return expr if ispow2(d): d2 = sqrtdenest(sqrt(d.base))**numer(d.exp) if d2 != d: return handle(1/d2) elif d.is_Pow and (d.exp.is_integer or d.base.is_positive): # (1/d**i) = (1/d)**i return handle(1/d.base)**d.exp if not (d.is_Add or ispow2(d)): return 1/d.func(*[handle(a) for a in d.args]) # handle 1/d treating d as an Add (though it may not be) keep = True # keep changes that are made # flatten it and collect radicals after checking for special # conditions d = _mexpand(d) # did it change? if d.is_Atom: return 1/d # is it a number that might be handled easily? if d.is_number: _d = nsimplify(d) if _d.is_Number and _d.equals(d): return 1/_d while True: # collect similar terms collected = defaultdict(list) for m in Add.make_args(d): # d might have become non-Add p2 = [] other = [] for i in Mul.make_args(m): if ispow2(i, log2=True): p2.append(i.base if i.exp is S.Half else i.base**(2*i.exp)) elif i is S.ImaginaryUnit: p2.append(S.NegativeOne) else: other.append(i) collected[tuple(ordered(p2))].append(Mul(*other)) rterms = list(ordered(list(collected.items()))) rterms = [(Mul(*i), Add(*j)) for i, j in rterms] nrad = len(rterms) - (1 if rterms[0][0] is S.One else 0) if nrad < 1: break elif nrad > max_terms: # there may have been invalid operations leading to this point # so don't keep changes, e.g. this expression is troublesome # in collecting terms so as not to raise the issue of 2834: # r = sqrt(sqrt(5) + 5) # eq = 1/(sqrt(5)*r + 2*sqrt(5)*sqrt(-sqrt(5) + 5) + 5*r) keep = False break if len(rterms) > 4: # in general, only 4 terms can be removed with repeated squaring # but other considerations can guide selection of radical terms # so that radicals are removed if all([x.is_Integer and (y**2).is_Rational for x, y in rterms]): nd, d = rad_rationalize(S.One, Add._from_args( [sqrt(x)*y for x, y in rterms])) n *= nd else: # is there anything else that might be attempted? keep = False break from sympy.simplify.powsimp import powsimp, powdenest num = powsimp(_num(rterms)) n *= num d *= num d = powdenest(_mexpand(d), force=symbolic) if d.is_Atom: break if not keep: return expr return _unevaluated_Mul(n, 1/d) coeff, expr = expr.as_coeff_Add() expr = expr.normal() old = fraction(expr) n, d = fraction(handle(expr)) if old != (n, d): if not d.is_Atom: was = (n, d) n = signsimp(n, evaluate=False) d = signsimp(d, evaluate=False) u = Factors(_unevaluated_Mul(n, 1/d)) u = _unevaluated_Mul(*[k**v for k, v in u.factors.items()]) n, d = fraction(u) if old == (n, d): n, d = was n = expand_mul(n) if d.is_Number or d.is_Add: n2, d2 = fraction(gcd_terms(_unevaluated_Mul(n, 1/d))) if d2.is_Number or (d2.count_ops() <= d.count_ops()): n, d = [signsimp(i) for i in (n2, d2)] if n.is_Mul and n.args[0].is_Number: n = n.func(*n.args) return coeff + _unevaluated_Mul(n, 1/d)
def test_issue_21623(): from sympy.matrices.expressions.matexpr import MatrixSymbol M = MatrixSymbol('X', 2, 2) assert gcd_terms(M[0, 0], 1) == M[0, 0]
def test_simplify_matrix_expressions(): # Various simplification functions assert type(gcd_terms(C * D + D * C)) == MatAdd a = gcd_terms(2 * C * D + 4 * D * C) assert type(a) == MatAdd assert a.args == (2 * C * D, 4 * D * C)
def eval(cls, p, q): from sympy.core.add import Add from sympy.core.mul import Mul from sympy.core.singleton import S from sympy.core.exprtools import gcd_terms from sympy.polys.polytools import gcd def doit(p, q): """Try to return p % q if both are numbers or +/-p is known to be less than or equal q. """ if q == S.Zero: raise ZeroDivisionError("Modulo by zero") if p.is_infinite or q.is_infinite or p is nan or q is nan: return nan if p == S.Zero or p == q or p == -q or (p.is_integer and q == 1): return S.Zero if q.is_Number: if p.is_Number: return (p % q) if q == 2: if p.is_even: return S.Zero elif p.is_odd: return S.One if hasattr(p, '_eval_Mod'): rv = getattr(p, '_eval_Mod')(q) if rv is not None: return rv # by ratio r = p / q try: d = int(r) except TypeError: pass else: if type(d) is int: rv = p - d * q if (rv * q < 0) == True: rv += q return rv # by difference # -2|q| < p < 2|q| d = abs(p) for _ in range(2): d -= abs(q) if d.is_negative: if q.is_positive: if p.is_positive: return d + q elif p.is_negative: return -d elif q.is_negative: if p.is_positive: return d elif p.is_negative: return -d + q break rv = doit(p, q) if rv is not None: return rv # denest if p.func is cls: qinner = p.args[1] if qinner % q == 0: return cls(p.args[0], q) elif (qinner * (q - qinner)).is_nonnegative: # |qinner| < |q| and have same sign return p elif (-p).func is cls: qinner = (-p).args[1] if qinner % q == 0: return cls(-(-p).args[0], q) elif (qinner * (q + qinner)).is_nonpositive: # |qinner| < |q| and have different sign return p # XXX other possibilities? # extract gcd; any further simplification should be done by the user G = gcd(p, q) if G != 1: p, q = [ gcd_terms(i / G, clear=False, fraction=False) for i in (p, q) ] pwas, qwas = p, q # simplify terms # (x + y + 2) % x -> Mod(y + 2, x) if p.is_Add: args = [] for i in p.args: a = cls(i, q) if a.count(cls) > i.count(cls): args.append(i) else: args.append(a) if args != list(p.args): p = Add(*args) else: # handle coefficients if they are not Rational # since those are not handled by factor_terms # e.g. Mod(.6*x, .3*y) -> 0.3*Mod(2*x, y) cp, p = p.as_coeff_Mul() cq, q = q.as_coeff_Mul() ok = False if not cp.is_Rational or not cq.is_Rational: r = cp % cq if r == 0: G *= cq p *= int(cp / cq) ok = True if not ok: p = cp * p q = cq * q # simple -1 extraction if p.could_extract_minus_sign() and q.could_extract_minus_sign(): G, p, q = [-i for i in (G, p, q)] # check again to see if p and q can now be handled as numbers rv = doit(p, q) if rv is not None: return rv * G # put 1.0 from G on inside if G.is_Float and G == 1: p *= G return cls(p, q, evaluate=False) elif G.is_Mul and G.args[0].is_Float and G.args[0] == 1: p = G.args[0] * p G = Mul._from_args(G.args[1:]) return G * cls(p, q, evaluate=(p, q) != (pwas, qwas))