def test_sqrtdenest4(): # see Denest_en.pdf in https://github.com/sympy/sympy/issues/3192 z = sqrt(8 - r2*sqrt(5 - r5) - sqrt(3)*(1 + r5)) z1 = sqrtdenest(z) c = sqrt(-r5 + 5) z1 = ((-r15*c - r3*c + c + r5*c - r6 - r2 + r10 + sqrt(30))/4).expand() assert sqrtdenest(z) == z1 z = sqrt(2*r2*sqrt(r2 + 2) + 5*r2 + 4*sqrt(r2 + 2) + 8) assert sqrtdenest(z) == r2 + sqrt(r2 + 2) + 2 w = 2 + r2 + r3 + (1 + r3)*sqrt(2 + r2 + 5*r3) z = sqrt((w**2).expand()) assert sqrtdenest(z) == w.expand()
def test_issue_19914(): a = Integer(-8) b = Integer(-1) r = Integer(63) d2 = a*a - b*b*r assert _sqrt_numeric_denest(a, b, r, d2) == \ sqrt(14)*I/2 + 3*sqrt(2)*I/2 assert sqrtdenest(sqrt(-8-sqrt(63))) == sqrt(14)*I/2 + 3*sqrt(2)*I/2
def test_sqrtdenest(): d = {sqrt(5 + 2 * r6): r2 + r3, sqrt(5. + 2 * r6): sqrt(5. + 2 * r6), sqrt(5. + 4*sqrt(5 + 2 * r6)): sqrt(5.0 + 4*r2 + 4*r3), sqrt(r2): sqrt(r2), sqrt(5 + r7): sqrt(5 + r7), sqrt(3 + sqrt(5 + 2*r7)): 3*r2*(5 + 2*r7)**Rational(1, 4)/(2*sqrt(6 + 3*r7)) + r2*sqrt(6 + 3*r7)/(2*(5 + 2*r7)**Rational(1, 4)), sqrt(3 + 2*r3): 3**Rational(3, 4)*(r6/2 + 3*r2/2)/3} for i in d: assert sqrtdenest(i) == d[i], i
def test_issue_5857(): from sympy.abc import x, y z = sqrt(1/(4*r3 + 7) + 1) ans = (r2 + r6)/(r3 + 2) assert sqrtdenest(z) == ans assert sqrtdenest(1 + z) == 1 + ans assert sqrtdenest(Integral(z + 1, (x, 1, 2))) == \ Integral(1 + ans, (x, 1, 2)) assert sqrtdenest(x + sqrt(y)) == x + sqrt(y) ans = (r2 + r6)/(r3 + 2) assert sqrtdenest(z) == ans assert sqrtdenest(1 + z) == 1 + ans assert sqrtdenest(Integral(z + 1, (x, 1, 2))) == \ Integral(1 + ans, (x, 1, 2)) assert sqrtdenest(x + sqrt(y)) == x + sqrt(y)
def _nthroot_solve(p, n, prec): """ helper function for ``nthroot`` It denests ``p**Rational(1, n)`` using its minimal polynomial """ from sympy.polys.numberfields import _minimal_polynomial_sq from sympy.solvers import solve while n % 2 == 0: p = sqrtdenest(sqrt(p)) n = n // 2 if n == 1: return p pn = p**Rational(1, n) x = Symbol('x') f = _minimal_polynomial_sq(p, n, x) if f is None: return None sols = solve(f, x) for sol in sols: if abs(sol - pn).n() < 1./10**prec: sol = sqrtdenest(sol) if _mexpand(sol**n) == p: return sol
def test_sqrtdenest3(): z = sqrt(13 - 2*r10 + 2*r2*sqrt(-2*r10 + 11)) assert sqrtdenest(z) == -1 + r2 + r10 assert sqrtdenest(z, max_iter=1) == -1 + sqrt(2) + sqrt(10) z = sqrt(sqrt(r2 + 2) + 2) assert sqrtdenest(z) == z assert sqrtdenest(sqrt(-2*r10 + 4*r2*sqrt(-2*r10 + 11) + 20)) == \ sqrt(-2*r10 - 4*r2 + 8*r5 + 20) assert sqrtdenest(sqrt((112 + 70*r2) + (46 + 34*r2)*r5)) == \ r10 + 5 + 4*r2 + 3*r5 z = sqrt(5 + sqrt(2*r6 + 5)*sqrt(-2*r29 + 2*sqrt(-10*r29 + 55) + 16)) r = sqrt(-2*r29 + 11) assert sqrtdenest(z) == sqrt(r2*r + r3*r + r10 + r15 + 5) n = sqrt(2*r6/7 + 2*r7/7 + 2*sqrt(42)/7 + 2) d = sqrt(16 - 2*r29 + 2*sqrt(55 - 10*r29)) assert sqrtdenest(n/d) == r7*(1 + r6 + r7)/(Mul(7, (sqrt(-2*r29 + 11) + r5), evaluate=False))
def test_sqrt_symbolic_denest(): x = Symbol('x') z = sqrt(((1 + sqrt(sqrt(2 + x) + 3))**2).expand()) assert sqrtdenest(z) == sqrt((1 + sqrt(sqrt(2 + x) + 3))**2) z = sqrt(((1 + sqrt(sqrt(2 + cos(1)) + 3))**2).expand()) assert sqrtdenest(z) == 1 + sqrt(sqrt(2 + cos(1)) + 3) z = ((1 + cos(2))**4 + 1).expand() assert sqrtdenest(z) == z z = sqrt(((1 + sqrt(sqrt(2 + cos(3*x)) + 3))**2 + 1).expand()) assert sqrtdenest(z) == z c = cos(3) c2 = c**2 assert sqrtdenest(sqrt(2*sqrt(1 + r3)*c + c2 + 1 + r3*c2)) == \ -1 - sqrt(1 + r3)*c ra = sqrt(1 + r3) z = sqrt(20*ra*sqrt(3 + 3*r3) + 12*r3*ra*sqrt(3 + 3*r3) + 64*r3 + 112) assert sqrtdenest(z) == z
def test_issue_6241(): z = sqrt( -320 + 32*sqrt(5) + 64*r15) assert sqrtdenest(z) == z
def test_sqrtdenest_rec(): assert sqrtdenest(sqrt(-4*sqrt(14) - 2*r6 + 4*sqrt(21) + 33)) == \ -r2 + r3 + 2*r7 assert sqrtdenest(sqrt(-28*r7 - 14*r5 + 4*sqrt(35) + 82)) == \ -7 + r5 + 2*r7 assert sqrtdenest(sqrt(6*r2/11 + 2*sqrt(22)/11 + 6*sqrt(11)/11 + 2)) == \ sqrt(11)*(r2 + 3 + sqrt(11))/11 assert sqrtdenest(sqrt(468*r3 + 3024*r2 + 2912*r6 + 19735)) == \ 9*r3 + 26 + 56*r6 z = sqrt(-490*r3 - 98*sqrt(115) - 98*sqrt(345) - 2107) assert sqrtdenest(z) == sqrt(-1)*(7*r5 + 7*r15 + 7*sqrt(23)) z = sqrt(-4*sqrt(14) - 2*r6 + 4*sqrt(21) + 34) assert sqrtdenest(z) == z assert sqrtdenest(sqrt(-8*r2 - 2*r5 + 18)) == -r10 + 1 + r2 + r5 assert sqrtdenest(sqrt(8*r2 + 2*r5 - 18)) == \ sqrt(-1)*(-r10 + 1 + r2 + r5) assert sqrtdenest(sqrt(8*r2/3 + 14*r5/3 + Rational(154, 9))) == \ -r10/3 + r2 + r5 + 3 assert sqrtdenest(sqrt(sqrt(2*r6 + 5) + sqrt(2*r7 + 8))) == \ sqrt(1 + r2 + r3 + r7) assert sqrtdenest(sqrt(4*r15 + 8*r5 + 12*r3 + 24)) == 1 + r3 + r5 + r15 w = 1 + r2 + r3 + r5 + r7 assert sqrtdenest(sqrt((w**2).expand())) == w z = sqrt((w**2).expand() + 1) assert sqrtdenest(z) == z z = sqrt(2*r10 + 6*r2 + 4*r5 + 12 + 10*r15 + 30*r3) assert sqrtdenest(z) == z
def test_sqrtdenest2(): assert sqrtdenest(sqrt(16 - 2*r29 + 2*sqrt(55 - 10*r29))) == \ r5 + sqrt(11 - 2*r29) e = sqrt(-r5 + sqrt(-2*r29 + 2*sqrt(-10*r29 + 55) + 16)) assert sqrtdenest(e) == root(-2*r29 + 11, 4) r = sqrt(1 + r7) assert sqrtdenest(sqrt(1 + r)) == sqrt(1 + r) e = sqrt(((1 + sqrt(1 + 2*sqrt(3 + r2 + r5)))**2).expand()) assert sqrtdenest(e) == 1 + sqrt(1 + 2*sqrt(r2 + r5 + 3)) assert sqrtdenest(sqrt(5*r3 + 6*r2)) == \ sqrt(2)*root(3, 4) + root(3, 4)**3 assert sqrtdenest(sqrt(((1 + r5 + sqrt(1 + r3))**2).expand())) == \ 1 + r5 + sqrt(1 + r3) assert sqrtdenest(sqrt(((1 + r5 + r7 + sqrt(1 + r3))**2).expand())) == \ 1 + sqrt(1 + r3) + r5 + r7 e = sqrt(((1 + cos(2) + cos(3) + sqrt(1 + r3))**2).expand()) assert sqrtdenest(e) == cos(3) + cos(2) + 1 + sqrt(1 + r3) e = sqrt(-2*r10 + 2*r2*sqrt(-2*r10 + 11) + 14) assert sqrtdenest(e) == sqrt(-2*r10 - 2*r2 + 4*r5 + 14) # check that the result is not more complicated than the input z = sqrt(-2*r29 + cos(2) + 2*sqrt(-10*r29 + 55) + 16) assert sqrtdenest(z) == z assert sqrtdenest(sqrt(r6 + sqrt(15))) == sqrt(r6 + sqrt(15)) z = sqrt(15 - 2*sqrt(31) + 2*sqrt(55 - 10*r29)) assert sqrtdenest(z) == z
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)
def test_sqrt_ratcomb(): assert sqrtdenest(sqrt(1 + r3) + sqrt(3 + 3*r3) - sqrt(10 + 6*r3)) == 0
def test_issue_12420(): assert sqrtdenest((3 - sqrt(2)*sqrt(4 + 3*I) + 3*I)/2) == I e = 3 - sqrt(2)*sqrt(4 + I) + 3*I assert sqrtdenest(e) == e
def test_issue_5653(): assert sqrtdenest( sqrt(2 + sqrt(2 + sqrt(2)))) == sqrt(2 + sqrt(2 + sqrt(2)))
def test_issue_18041(): e = -sqrt(-2 + 2*sqrt(3)*I) assert sqrtdenest(e) == -1 - sqrt(3)*I
def checksysodesol(eqs, sols, func=None): r""" Substitutes corresponding ``sols`` for each functions into each ``eqs`` and checks that the result of substitutions for each equation is ``0``. The equations and solutions passed can be any iterable. This only works when each ``sols`` have one function only, like `x(t)` or `y(t)`. For each function, ``sols`` can have a single solution or a list of solutions. In most cases it will not be necessary to explicitly identify the function, but if the function cannot be inferred from the original equation it can be supplied through the ``func`` argument. When a sequence of equations is passed, the same sequence is used to return the result for each equation with each function substituted with corresponding solutions. It tries the following method to find zero equivalence for each equation: Substitute the solutions for functions, like `x(t)` and `y(t)` into the original equations containing those functions. This function returns a tuple. The first item in the tuple is ``True`` if the substitution results for each equation is ``0``, and ``False`` otherwise. The second item in the tuple is what the substitution results in. Each element of the ``list`` should always be ``0`` corresponding to each equation if the first item is ``True``. Note that sometimes this function may return ``False``, but with an expression that is identically equal to ``0``, instead of returning ``True``. This is because :py:meth:`~sympy.simplify.simplify.simplify` cannot reduce the expression to ``0``. If an expression returned by each function vanishes identically, then ``sols`` really is a solution to ``eqs``. If this function seems to hang, it is probably because of a difficult simplification. Examples ======== >>> from sympy import Eq, diff, symbols, sin, cos, exp, sqrt, S, Function >>> from sympy.solvers.ode.subscheck import checksysodesol >>> C1, C2 = symbols('C1:3') >>> t = symbols('t') >>> x, y = symbols('x, y', cls=Function) >>> eq = (Eq(diff(x(t),t), x(t) + y(t) + 17), Eq(diff(y(t),t), -2*x(t) + y(t) + 12)) >>> sol = [Eq(x(t), (C1*sin(sqrt(2)*t) + C2*cos(sqrt(2)*t))*exp(t) - S(5)/3), ... Eq(y(t), (sqrt(2)*C1*cos(sqrt(2)*t) - sqrt(2)*C2*sin(sqrt(2)*t))*exp(t) - S(46)/3)] >>> checksysodesol(eq, sol) (True, [0, 0]) >>> eq = (Eq(diff(x(t),t),x(t)*y(t)**4), Eq(diff(y(t),t),y(t)**3)) >>> sol = [Eq(x(t), C1*exp(-1/(4*(C2 + t)))), Eq(y(t), -sqrt(2)*sqrt(-1/(C2 + t))/2), ... Eq(x(t), C1*exp(-1/(4*(C2 + t)))), Eq(y(t), sqrt(2)*sqrt(-1/(C2 + t))/2)] >>> checksysodesol(eq, sol) (True, [0, 0]) """ def _sympify(eq): return list(map(sympify, eq if iterable(eq) else [eq])) eqs = _sympify(eqs) for i in range(len(eqs)): if isinstance(eqs[i], Equality): eqs[i] = eqs[i].lhs - eqs[i].rhs if func is None: funcs = [] for eq in eqs: derivs = eq.atoms(Derivative) func = set().union(*[d.atoms(AppliedUndef) for d in derivs]) for func_ in func: funcs.append(func_) funcs = list(set(funcs)) if not all(isinstance(func, AppliedUndef) and len(func.args) == 1 for func in funcs)\ and len({func.args for func in funcs})!=1: raise ValueError("func must be a function of one variable, not %s" % func) for sol in sols: if len(sol.atoms(AppliedUndef)) != 1: raise ValueError("solutions should have one function only") if len(funcs) != len({sol.lhs for sol in sols}): raise ValueError("number of solutions provided does not match the number of equations") dictsol = dict() for sol in sols: func = list(sol.atoms(AppliedUndef))[0] if sol.rhs == func: sol = sol.reversed solved = sol.lhs == func and not sol.rhs.has(func) if not solved: rhs = solve(sol, func) if not rhs: raise NotImplementedError else: rhs = sol.rhs dictsol[func] = rhs checkeq = [] for eq in eqs: for func in funcs: eq = sub_func_doit(eq, func, dictsol[func]) ss = simplify(eq) if ss != 0: eq = ss.expand(force=True) if eq != 0: eq = sqrtdenest(eq).simplify() else: eq = 0 checkeq.append(eq) if len(set(checkeq)) == 1 and list(set(checkeq))[0] == 0: return (True, checkeq) else: return (False, checkeq)