def _sqrtdenest34(expr): """Helper that denests the square root of three or four surds. Examples ======== >>> from sympy import sqrt >>> from sympy.simplify.sqrtdenest import _sqrtdenest34 >>> _sqrtdenest34(sqrt(-72*sqrt(2) + 158*sqrt(5) + 498)) -sqrt(10) + sqrt(2) + 9 + 9*sqrt(5) >>> _sqrtdenest34(sqrt(12 + 2*sqrt(6) + 2*sqrt(14) + 2*sqrt(21))) sqrt(2) + sqrt(3) + sqrt(7) References ========== - D. J. Jeffrey and A. D. Rich, 'Symplifying Square Roots of Square Roots by Denesting' """ from sympy.simplify.simplify import radsimp if expr.base < 0: return sqrt(-1) * _sqrtdenest34(sqrt(-expr.base)) # a should be > b so we sort; this also makes the process canonical args = sorted(expr.base.args, reverse=True) a = Add._from_args(args[:2]) b = Add._from_args(args[2:]) c = _sqrtdenest1(sqrt(_mexpand(a**2 - b**2))) if sqrt_depth(c) > 1: return expr d = _sqrtdenest1(sqrt(a + c)) if sqrt_depth(d) > 1: return expr return _mexpand(radsimp((d / sqrt(2) + b / (sqrt(2) * d))))
def _sqrtdenest34(expr): """Helper that denests the square root of three or four surds. Examples ======== >>> from sympy import sqrt >>> from sympy.simplify.sqrtdenest import _sqrtdenest34 >>> _sqrtdenest34(sqrt(-72*sqrt(2) + 158*sqrt(5) + 498)) -sqrt(10) + sqrt(2) + 9 + 9*sqrt(5) >>> _sqrtdenest34(sqrt(12 + 2*sqrt(6) + 2*sqrt(14) + 2*sqrt(21))) sqrt(2) + sqrt(3) + sqrt(7) References ========== - D. J. Jeffrey and A. D. Rich, 'Symplifying Square Roots of Square Roots by Denesting' """ from sympy.simplify.simplify import radsimp if expr.base < 0: return sqrt(-1)*_sqrtdenest34(sqrt(-expr.base)) # a should be > b so we sort; this also makes the process canonical args = sorted(expr.base.args, reverse=True) a = Add._from_args(args[:2]) b = Add._from_args(args[2:]) c = _sqrtdenest1(sqrt(_mexpand(a**2 - b**2))) if sqrt_depth(c) > 1: return expr d = _sqrtdenest1(sqrt(a + c)) if sqrt_depth(d) > 1: return expr return _mexpand(radsimp((d/sqrt(2) + b/(sqrt(2)*d))))
def _convert_to_Add(eq, evaluate=True): # Mimic Equality._eval_rewrite_as_Add. args = eq.lhs, eq.rhs L, R = args if evaluate: # allow cancellation of args return L - R args = Add.make_args(L) + Add.make_args(-R) if evaluate is None: # no cancellation, but canonical return _unevaluated_Add(*args) # no cancellation, not canonical return Add._from_args(args)
def _sqrtdenest34(expr): """Helper that denests the square root of three or four surds. Examples ======== >>> from sympy import sqrt >>> from sympy.simplify.sqrtdenest import _sqrtdenest34 >>> _sqrtdenest34(sqrt(-72*sqrt(2) + 158*sqrt(5) + 498)) -sqrt(10) + sqrt(2) + 9 + 9*sqrt(5) >>> _sqrtdenest34(sqrt(12 + 2*sqrt(6) + 2*sqrt(14) + 2*sqrt(21))) sqrt(2) + sqrt(3) + sqrt(7) References ========== - D. J. Jeffrey and A. D. Rich, 'Symplifying Square Roots of Square Roots by Denesting' """ from sympy.simplify.simplify import radsimp if not is_sqrt(expr): return expr a_plus_b = expr.base if not (a_plus_b.is_Add and len(a_plus_b.args) > 2): return expr if a_plus_b < 0: return sqrt(-1)*_sqrtdenest34(sqrt(-a_plus_b)) args = a_plus_b.args a = Add._from_args(args[:2]) b = Add._from_args(args[2:]) if a < b: # we want a > b; since a + b > 0 then a**2 > b**2, too a, b = b, a c = _sqrtdenest1(sqrt(_mexpand(a**2 - b**2))) if sqrt_depth(c) > 1: return expr d = _sqrtdenest1(sqrt(a + c)) if sqrt_depth(d) > 1: return expr return _mexpand(radsimp((d/sqrt(2) + b/(sqrt(2)*d))))
def _sqrt_match(p): """Return [a, b, r] for p.match(a + b*sqrt(r)) where, in addition to matching, sqrt(r) also has then maximal sqrt_depth among addends of p. Examples ======== >>> from sympy.functions.elementary.miscellaneous import sqrt >>> from sympy.simplify.sqrtdenest import _sqrt_match >>> _sqrt_match(1 + sqrt(2) + sqrt(2)*sqrt(3) + 2*sqrt(1+sqrt(5))) [1 + sqrt(2) + sqrt(6), 2, 1 + sqrt(5)] """ p = _mexpand(p) if p.is_Number: res = (p, S.Zero, S.Zero) elif p.is_Add: pargs = list(p.args) # to make the process canonical, the argument is included in the tuple # so when the max is selected, it will be the largest arg having a # given depth v = [(sqrt_depth(x), x, i) for i, x in enumerate(pargs)] nmax = max(v) if nmax[0] == 0: res = [] else: depth, _, i = nmax r = pargs.pop(i) a = Add._from_args(pargs) b = S.One if r.is_Mul: bv = [] rv = [] for x in r.args: if sqrt_depth(x) < depth: bv.append(x) else: rv.append(x) b = Mul._from_args(bv) r = Mul._from_args(rv) res = (a, b, r**2) else: b, r = p.as_coeff_Mul() if is_sqrt(r): res = (S.Zero, b, r**2) else: res = [] return list(res)
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)