def simplify_sqrt_real(expr): r""" Simplify ``sqrt`` in symbolic expressions in the real domain. EXAMPLES: Simplifications of basic expressions:: sage: from sage.manifolds.utilities import simplify_sqrt_real sage: simplify_sqrt_real( sqrt(x^2) ) abs(x) sage: assume(x<0) sage: simplify_sqrt_real( sqrt(x^2) ) -x sage: simplify_sqrt_real( sqrt(x^2-2*x+1) ) -x + 1 sage: simplify_sqrt_real( sqrt(x^2) + sqrt(x^2-2*x+1) ) -2*x + 1 This improves over Sage's :meth:`~sage.symbolic.expression.Expression.canonicalize_radical`, which yields incorrect results when ``x < 0``:: sage: forget() # removes the assumption x<0 sage: sqrt(x^2).canonicalize_radical() x sage: assume(x<0) sage: sqrt(x^2).canonicalize_radical() # wrong output x sage: sqrt(x^2-2*x+1).canonicalize_radical() # wrong output x - 1 sage: ( sqrt(x^2) + sqrt(x^2-2*x+1) ).canonicalize_radical() # wrong output 2*x - 1 Simplification of nested ``sqrt``'s:: sage: forget() # removes the assumption x<0 sage: simplify_sqrt_real( sqrt(1 + sqrt(x^2)) ) sqrt(abs(x) + 1) sage: assume(x<0) sage: simplify_sqrt_real( sqrt(1 + sqrt(x^2)) ) sqrt(-x + 1) sage: simplify_sqrt_real( sqrt(x^2 + sqrt(4*x^2) + 1) ) -x + 1 Again, :meth:`~sage.symbolic.expression.Expression.canonicalize_radical` fails on the last one:: sage: (sqrt(x^2 + sqrt(4*x^2) + 1)).canonicalize_radical() # wrong output x + 1 """ from sage.symbolic.ring import SR from sage.functions.other import sqrt # 1/ Search for the sqrt's in expr sexpr = str(expr) if 'sqrt(' not in sexpr: # no sqrt to simplify return expr if ('D[' in sexpr) or ('diff(' in sexpr): return expr #!# the code below is not capable of simplifying # expressions with symbolic derivatives denoted # by Pynac symbols of the type D[0] or diff(...) # Lists to store the positions of all the top-level sqrt's in sexpr: pos_sqrts = [] # position of first character, i.e. 's' of 'sqrt(...)' pos_after = [] # position of character immediately after 'sqrt(...)' the_sqrts = [] # the sqrt sub-expressions in sexpr, i.e. 'sqrt(...)' pos_max = len(sexpr) - 6 pos = 0 while pos < pos_max: if sexpr[pos:pos + 5] == 'sqrt(': pos_sqrts.append(pos) parenth = 1 scan = pos + 5 while parenth != 0: if sexpr[scan] == '(': parenth += 1 if sexpr[scan] == ')': parenth -= 1 scan += 1 the_sqrts.append(sexpr[pos:scan]) pos_after.append(scan) pos = scan else: pos += 1 # 2/ Search for sub-sqrt's: for i in range(len(the_sqrts)): argum = the_sqrts[i][5:-1] # the sqrt argument if 'sqrt(' in argum: simpl = simplify_sqrt_real(SR(argum)) the_sqrts[i] = 'sqrt(' + str(simpl) + ')' # 3/ Simplifications of the sqrt's new_expr = "" # will contain the result pos0 = 0 for i, pos in enumerate(pos_sqrts): # radcan is called on each sqrt: x = SR(the_sqrts[i]) argum = x.operands()[0] # the argument of sqrt den = argum.denominator() if not (den == 1): # the argument of sqrt is a fraction # NB: after #19312 (integrated in Sage 6.10.beta7), the above # cannot be written as # if den != 1!: num = argum.numerator() if num < 0 or den < 0: x = sqrt(-num) / sqrt(-den) # new equivalent expression for x simpl = SR(x._maxima_().radcan()) if str(simpl)[:5] == 'sqrt(' or str(simpl)[:7] == '1/sqrt(': # no further simplification seems possible: ssimpl = str(simpl) else: # the absolute value of radcan's output is taken, the call to # simplify() taking into account possible assumptions regarding the # sign of simpl: ssimpl = str(abs(simpl).simplify()) # search for abs(1/sqrt(...)) term to simplify it into 1/sqrt(...): pstart = ssimpl.find('abs(1/sqrt(') if pstart != -1: ssimpl = ssimpl[:pstart] + ssimpl[pstart + 3:] # getting rid of 'abs' new_expr += sexpr[pos0:pos] + '(' + ssimpl + ')' pos0 = pos_after[i] new_expr += sexpr[pos0:] return SR(new_expr)
def simplify_sqrt_real(expr): r""" Simplify ``sqrt`` in symbolic expressions in the real domain. EXAMPLES: Simplifications of basic expressions:: sage: from sage.manifolds.utilities import simplify_sqrt_real sage: simplify_sqrt_real( sqrt(x^2) ) abs(x) sage: assume(x<0) sage: simplify_sqrt_real( sqrt(x^2) ) -x sage: simplify_sqrt_real( sqrt(x^2-2*x+1) ) -x + 1 sage: simplify_sqrt_real( sqrt(x^2) + sqrt(x^2-2*x+1) ) -2*x + 1 This improves over Sage's :meth:`~sage.symbolic.expression.Expression.canonicalize_radical`, which yields incorrect results when ``x < 0``:: sage: forget() # removes the assumption x<0 sage: sqrt(x^2).canonicalize_radical() x sage: assume(x<0) sage: sqrt(x^2).canonicalize_radical() # wrong output x sage: sqrt(x^2-2*x+1).canonicalize_radical() # wrong output x - 1 sage: ( sqrt(x^2) + sqrt(x^2-2*x+1) ).canonicalize_radical() # wrong output 2*x - 1 Simplification of nested ``sqrt``'s:: sage: forget() # removes the assumption x<0 sage: simplify_sqrt_real( sqrt(1 + sqrt(x^2)) ) sqrt(abs(x) + 1) sage: assume(x<0) sage: simplify_sqrt_real( sqrt(1 + sqrt(x^2)) ) sqrt(-x + 1) sage: simplify_sqrt_real( sqrt(x^2 + sqrt(4*x^2) + 1) ) -x + 1 Again, :meth:`~sage.symbolic.expression.Expression.canonicalize_radical` fails on the last one:: sage: (sqrt(x^2 + sqrt(4*x^2) + 1)).canonicalize_radical() # wrong output x + 1 """ from sage.symbolic.ring import SR from sage.functions.other import sqrt # 1/ Search for the sqrt's in expr sexpr = str(expr) if 'sqrt(' not in sexpr: # no sqrt to simplify return expr if 'D[' in sexpr: return expr #!# the code below is not capable of simplifying # expressions with symbolic derivatives denoted by Pynac # symbols of the type D[0] # Lists to store the positions of all the top-level sqrt's in sexpr: pos_sqrts = [] # position of first character, i.e. 's' of 'sqrt(...)' pos_after = [] # position of character immediatelty after 'sqrt(...)' the_sqrts = [] # the sqrt sub-expressions in sexpr, i.e. 'sqrt(...)' pos_max = len(sexpr) - 6 pos = 0 while pos < pos_max: if sexpr[pos:pos+5] == 'sqrt(': pos_sqrts.append(pos) parenth = 1 scan = pos+5 while parenth != 0: if sexpr[scan] == '(': parenth += 1 if sexpr[scan] == ')': parenth -= 1 scan += 1 the_sqrts.append( sexpr[pos:scan] ) pos_after.append(scan) pos = scan else: pos += 1 # 2/ Search for sub-sqrt's: for i in range(len(the_sqrts)): argum = the_sqrts[i][5:-1] # the sqrt argument if 'sqrt(' in argum: simpl = simplify_sqrt_real(SR(argum)) the_sqrts[i] = 'sqrt(' + str(simpl) + ')' # 3/ Simplifications of the sqrt's new_expr = "" # will contain the result pos0 = 0 for i, pos in enumerate(pos_sqrts): # radcan is called on each sqrt: x = SR(the_sqrts[i]) argum = x.operands()[0] # the argument of sqrt den = argum.denominator() if not (den == 1): # the argument of sqrt is a fraction # NB: after #19312 (integrated in Sage 6.10.beta7), the above # cannot be written as # if den != 1!: num = argum.numerator() if num < 0 or den < 0: x = sqrt(-num) / sqrt(-den) # new equivalent expression for x simpl = SR(x._maxima_().radcan()) if str(simpl)[:5] == 'sqrt(' or str(simpl)[:7] == '1/sqrt(': # no further simplification seems possible: ssimpl = str(simpl) else: # the absolute value of radcan's output is taken, the call to # simplify() taking into account possible assumptions regarding the # sign of simpl: ssimpl = str(abs(simpl).simplify()) # search for abs(1/sqrt(...)) term to simplify it into 1/sqrt(...): pstart = ssimpl.find('abs(1/sqrt(') if pstart != -1: ssimpl = ssimpl[:pstart] + ssimpl[pstart+3:] # getting rid of 'abs' new_expr += sexpr[pos0:pos] + '(' + ssimpl + ')' pos0 = pos_after[i] new_expr += sexpr[pos0:] return SR(new_expr)
def simplify_sqrt_real(expr): r""" Simplify sqrt in symbolic expressions in the real domain. EXAMPLES: Simplifications of basic expressions:: sage: from sage.geometry.manifolds.utilities import simplify_sqrt_real sage: assume(x<0) sage: simplify_sqrt_real( sqrt(x^2) ) -x sage: simplify_sqrt_real( sqrt(x^2-2*x+1) ) -x + 1 sage: simplify_sqrt_real( sqrt(x^2) + sqrt(x^2-2*x+1) ) -2*x + 1 This improves over Sage's :meth:`~sage.symbolic.expression.Expression.canonicalize_radical` which yields incorrect results when x<0:: sage: sqrt(x^2).canonicalize_radical() # wrong output x sage: sqrt(x^2-2*x+1).canonicalize_radical() # wrong output x - 1 sage: ( sqrt(x^2) + sqrt(x^2-2*x+1) ).canonicalize_radical() # wrong output 2*x - 1 """ from sage.symbolic.ring import SR from sage.calculus.calculus import maxima from sage.functions.other import sqrt # 1/ Search for the sqrt's in expr sexpr = str(expr) if 'sqrt(' not in sexpr: # no sqrt to simplify return expr if 'D[' in sexpr: return expr #!# the code below is not capable of simplifying # expressions with symbolic derivatives denoted by Pynac # symbols of the type D[0] pos_sqrts = [] # positions of the sqrt's in sexpr the_sqrts = [] # the sqrt sub-expressions in sexpr for pos in range(len(sexpr)): if sexpr[pos:pos+5] == 'sqrt(': pos_sqrts.append(pos) parenth = 1 scan = pos+5 while parenth != 0: if sexpr[scan] == '(': parenth += 1 if sexpr[scan] == ')': parenth -= 1 scan += 1 the_sqrts.append( sexpr[pos:scan] ) # 2/ Simplifications of the sqrt's new_expr = "" # will contain the result pos0 = 0 for i, pos in enumerate(pos_sqrts): # radcan is called on each sqrt: x = SR(the_sqrts[i]) argum = x.operands()[0] # the argument of sqrt den = argum.denominator() if den != 1: # the argument of sqrt is a fraction num = argum.numerator() if num < 0 or den < 0: x = sqrt(-num) / sqrt(-den) # new equivalent expression for x simpl = SR(x._maxima_().radcan()) if str(simpl)[:5] != 'sqrt(': # the absolute value of radcan's output is taken, the call to simplify() # taking into account possible assumptions regarding the sign of simpl: ssimpl = str(abs(simpl).simplify()) else: ssimpl = str(simpl) # search for abs(1/sqrt(...)) term to simplify it into 1/sqrt(...): pstart = ssimpl.find('abs(1/sqrt(') if pstart != -1: ssimpl = ssimpl[:pstart] + ssimpl[pstart+3:] # getting rid of 'abs' new_expr += sexpr[pos0:pos] + '(' + ssimpl + ')' pos0 = pos + len(the_sqrts[i]) new_expr += sexpr[pos0:] return SR(new_expr)