def test_nthroot(): assert real_root(-8, 3) == -2 assert real_root(-16, 4) == root(-16, 4) r = root(-7, 4) assert real_root(r) == r r1 = root(-1, 3) r2 = r1**2 r3 = root(-1, 4) assert real_root(r1 + r2 + r3) == -1 + r2 + r3
def test_issue_11461(): if not matplotlib: skip("Matplotlib not the default backend") x = Symbol('x') p = plot(real_root((log(x/(x-2))), 3), show=False) # Random number of segments, probably more than 100, but we want to see # that there are segments generated, as opposed to when the bug was present # and that there are no exceptions. assert len(p[0].get_data()[0]) >= 30
def test_issue_14000(): assert isinstance(sqrt(4, evaluate=False), Pow) == True assert isinstance(cbrt(3.5, evaluate=False), Pow) == True assert isinstance(root(16, 4, evaluate=False), Pow) == True assert sqrt(4, evaluate=False) == Pow(4, S.Half, evaluate=False) assert cbrt(3.5, evaluate=False) == Pow(3.5, Rational(1, 3), evaluate=False) assert root(4, 2, evaluate=False) == Pow(4, S.Half, evaluate=False) assert root(16, 4, 2, evaluate=False).has(Pow) == True assert real_root(-8, 3, evaluate=False).has(Pow) == True
def test_issue_11463(): numpy = import_module('numpy') if not numpy: skip("numpy not installed.") x = Symbol('x') f = lambdify(x, real_root((log(x/(x-2))), 3), 'numpy') # numpy.select evaluates all options before considering conditions, # so it raises a warning about root of negative number which does # not affect the outcome. This warning is suppressed here with ignore_warnings(RuntimeWarning): assert f(numpy.array(-1)) < -1
def test_issue_14000(): assert isinstance(sqrt(4, evaluate=False), Pow) == True assert isinstance(cbrt(3.5, evaluate=False), Pow) == True assert isinstance(root(16, 4, evaluate=False), Pow) == True assert sqrt(4, evaluate=False) == Pow(4, S.Half, evaluate=False) assert cbrt(3.5, evaluate=False) == Pow(3.5, Rational(1, 3), evaluate=False) assert root(4, 2, evaluate=False) == Pow(4, Rational(1, 2), evaluate=False) assert root(16, 4, 2, evaluate=False).has(Pow) == True assert real_root(-8, 3, evaluate=False).has(Pow) == True
def test_real_root(): assert real_root(-8, 3) == -2 assert real_root(-16, 4) == root(-16, 4) r = root(-7, 4) assert real_root(r) == r r1 = root(-1, 3) r2 = r1**2 r3 = root(-1, 4) assert real_root(r1 + r2 + r3) == -1 + r2 + r3 assert real_root(root(-2, 3)) == -root(2, 3) assert real_root(-8., 3) == -2
def test_real_root(): assert real_root(-8, 3) == -2 assert real_root(-16, 4) == root(-16, 4) r = root(-7, 4) assert real_root(r) == r r1 = root(-1, 3) r2 = r1**2 r3 = root(-1, 4) assert real_root(r1 + r2 + r3) == -1 + r2 + r3 assert real_root(root(-2, 3)) == -root(2, 3) assert real_root(-8., 3) == -2 x = Symbol('x') n = Symbol('n') g = real_root(x, n) assert g.subs(dict(x=-8, n=3)) == -2 assert g.subs(dict(x=8, n=3)) == 2 # give principle root if there is no real root -- if this is not desired # then maybe a Root class is needed to raise an error instead assert g.subs(dict(x=I, n=3)) == cbrt(I) assert g.subs(dict(x=-8, n=2)) == sqrt(-8) assert g.subs(dict(x=I, n=2)) == sqrt(I)
def __pow__(self, other): from sympy.functions.elementary.miscellaneous import real_root if isinstance(other, Expr): if other is S.Infinity: if self.min.is_nonnegative: if self.max < 1: return S.Zero if self.min > 1: return S.Infinity return AccumBounds(0, oo) elif self.max.is_negative: if self.min > -1: return S.Zero if self.max < -1: return FiniteSet(-oo, oo) return AccumBounds(-oo, oo) else: if self.min > -1: if self.max < 1: return S.Zero return AccumBounds(0, oo) return AccumBounds(-oo, oo) if other is S.NegativeInfinity: return (1 / self)**oo if other.is_real and other.is_number: if other.is_zero: return S.One if other.is_Integer: if self.min.is_positive: return AccumBounds( Min(self.min ** other, self.max ** other), Max(self.min ** other, self.max ** other)) elif self.max.is_negative: return AccumBounds( Min(self.max ** other, self.min ** other), Max(self.max ** other, self.min ** other)) if other % 2 == 0: if other.is_negative: if self.min.is_zero: return AccumBounds(self.max**other, oo) if self.max.is_zero: return AccumBounds(self.min**other, oo) return AccumBounds(0, oo) return AccumBounds( S.Zero, Max(self.min**other, self.max**other)) else: if other.is_negative: if self.min.is_zero: return AccumBounds(self.max**other, oo) if self.max.is_zero: return AccumBounds(-oo, self.min**other) return AccumBounds(-oo, oo) return AccumBounds(self.min**other, self.max**other) num, den = other.as_numer_denom() if num == S(1): if den % 2 == 0: if S.Zero in self: if self.min.is_negative: return AccumBounds(0, real_root(self.max, den)) return AccumBounds(real_root(self.min, den), real_root(self.max, den)) num_pow = self**num return num_pow**(1 / den) return Pow(self, other, evaluate=False) return NotImplemented
def __pow__(self, other): from sympy.functions.elementary.miscellaneous import real_root if isinstance(other, Expr): if other is S.Infinity: if self.min.is_nonnegative: if self.max < 1: return S.Zero if self.min > 1: return S.Infinity return AccumBounds(0, oo) elif self.max.is_negative: if self.min > -1: return S.Zero if self.max < -1: return FiniteSet(-oo, oo) return AccumBounds(-oo, oo) else: if self.min > -1: if self.max < 1: return S.Zero return AccumBounds(0, oo) return AccumBounds(-oo, oo) if other is S.NegativeInfinity: return (1/self)**oo if other.is_real and other.is_number: if other.is_zero: return S.One if other.is_Integer: if self.min.is_positive: return AccumBounds(Min(self.min**other, self.max**other), Max(self.min**other, self.max**other)) elif self.max.is_negative: return AccumBounds(Min(self.max**other, self.min**other), Max(self.max**other, self.min**other)) if other % 2 == 0: if other.is_negative: if self.min.is_zero: return AccumBounds(self.max**other, oo) if self.max.is_zero: return AccumBounds(self.min**other, oo) return AccumBounds(0, oo) return AccumBounds(S.Zero, Max(self.min**other, self.max**other)) else: if other.is_negative: if self.min.is_zero: return AccumBounds(self.max**other, oo) if self.max.is_zero: return AccumBounds(-oo, self.min**other) return AccumBounds(-oo, oo) return AccumBounds(self.min**other, self.max**other) num, den = other.as_numer_denom() if num == S(1): if den % 2 == 0: if S.Zero in self: if self.min.is_negative: return AccumBounds(0, real_root(self.max, den)) return AccumBounds(real_root(self.min, den), real_root(self.max, den)) num_pow = self**num return num_pow**(1/den) return Pow(self, other, evaluate=False) return NotImplemented
def _invert_real(f, g_ys, symbol): """Helper function for _invert.""" if f == symbol: return (f, g_ys) n = Dummy('n', real=True) if hasattr(f, 'inverse') and not isinstance(f, ( TrigonometricFunction, HyperbolicFunction, )): if len(f.args) > 1: raise ValueError("Only functions with one argument are supported.") return _invert_real(f.args[0], imageset(Lambda(n, f.inverse()(n)), g_ys), symbol) if isinstance(f, Abs): pos = Interval(0, S.Infinity) neg = Interval(S.NegativeInfinity, 0) return _invert_real(f.args[0], Union(imageset(Lambda(n, n), g_ys).intersect(pos), imageset(Lambda(n, -n), g_ys).intersect(neg)), symbol) if f.is_Add: # f = g + h g, h = f.as_independent(symbol) if g is not S.Zero: return _invert_real(h, imageset(Lambda(n, n - g), g_ys), symbol) if f.is_Mul: # f = g*h g, h = f.as_independent(symbol) if g is not S.One: return _invert_real(h, imageset(Lambda(n, n/g), g_ys), symbol) if f.is_Pow: base, expo = f.args base_has_sym = base.has(symbol) expo_has_sym = expo.has(symbol) if not expo_has_sym: res = imageset(Lambda(n, real_root(n, expo)), g_ys) if expo.is_rational: numer, denom = expo.as_numer_denom() if numer == S.One or numer == - S.One: return _invert_real(base, res, symbol) else: if numer % 2 == 0: n = Dummy('n') neg_res = imageset(Lambda(n, -n), res) return _invert_real(base, res + neg_res, symbol) else: return _invert_real(base, res, symbol) else: if not base.is_positive: raise ValueError("x**w where w is irrational is not " "defined for negative x") return _invert_real(base, res, symbol) if not base_has_sym: return _invert_real(expo, imageset(Lambda(n, log(n)/log(base)), g_ys), symbol) if isinstance(f, TrigonometricFunction): if isinstance(g_ys, FiniteSet): def inv(trig): if isinstance(f, (sin, csc)): F = asin if isinstance(f, sin) else acsc return (lambda a: n*pi + (-1)**n*F(a),) if isinstance(f, (cos, sec)): F = acos if isinstance(f, cos) else asec return ( lambda a: 2*n*pi + F(a), lambda a: 2*n*pi - F(a),) if isinstance(f, (tan, cot)): return (lambda a: n*pi + f.inverse()(a),) n = Dummy('n', integer=True) invs = S.EmptySet for L in inv(f): invs += Union(*[imageset(Lambda(n, L(g)), S.Integers) for g in g_ys]) return _invert_real(f.args[0], invs, symbol) return (f, g_ys)
def _invert_real(f, g_ys, symbol): """ Helper function for invert_real """ if not f.has(symbol): raise ValueError("Inverse of constant function doesn't exist") if f is symbol: return (f, g_ys) n = Dummy('n') if hasattr(f, 'inverse') and not isinstance(f, TrigonometricFunction) and \ not isinstance(f, HyperbolicFunction): if len(f.args) > 1: raise ValueError("Only functions with one argument are supported.") return _invert_real(f.args[0], imageset(Lambda(n, f.inverse()(n)), g_ys), symbol) if isinstance(f, Abs): return _invert_real( f.args[0], Union( imageset(Lambda(n, n), g_ys).intersect(Interval(0, oo)), imageset(Lambda(n, -n), g_ys).intersect(Interval(-oo, 0))), symbol) if f.is_Add: # f = g + h g, h = f.as_independent(symbol) if g != S.Zero: return _invert_real(h, imageset(Lambda(n, n - g), g_ys), symbol) if f.is_Mul: # f = g*h g, h = f.as_independent(symbol) if g != S.One: return _invert_real(h, imageset(Lambda(n, n / g), g_ys), symbol) if f.is_Pow: base, expo = f.args base_has_sym = base.has(symbol) expo_has_sym = expo.has(symbol) if not expo_has_sym: res = imageset(Lambda(n, real_root(n, expo)), g_ys) if expo.is_rational: numer, denom = expo.as_numer_denom() if numer == S.One or numer == -S.One: return _invert_real(base, res, symbol) else: if numer % 2 == 0: n = Dummy('n') neg_res = imageset(Lambda(n, -n), res) return _invert_real(base, res + neg_res, symbol) else: return _invert_real(base, res, symbol) else: if not base.is_positive: raise ValueError("x**w where w is irrational is not " "defined for negative x") return _invert_real(base, res, symbol) if not base_has_sym: return _invert_real(expo, imageset(Lambda(n, log(n) / log(base)), g_ys), symbol) if isinstance(f, sin): n = Dummy('n') if isinstance(g_ys, FiniteSet): sin_invs = Union(*[imageset(Lambda(n, n*pi + (-1)**n*asin(g_y)), \ S.Integers) for g_y in g_ys]) return _invert_real(f.args[0], sin_invs, symbol) if isinstance(f, csc): n = Dummy('n') if isinstance(g_ys, FiniteSet): csc_invs = Union(*[imageset(Lambda(n, n*pi + (-1)**n*acsc(g_y)), \ S.Integers) for g_y in g_ys]) return _invert_real(f.args[0], csc_invs, symbol) if isinstance(f, cos): n = Dummy('n') if isinstance(g_ys, FiniteSet): cos_invs_f1 = Union(*[imageset(Lambda(n, 2*n*pi + acos(g_y)), \ S.Integers) for g_y in g_ys]) cos_invs_f2 = Union(*[imageset(Lambda(n, 2*n*pi - acos(g_y)), \ S.Integers) for g_y in g_ys]) cos_invs = Union(cos_invs_f1, cos_invs_f2) return _invert_real(f.args[0], cos_invs, symbol) if isinstance(f, sec): n = Dummy('n') if isinstance(g_ys, FiniteSet): sec_invs_f1 = Union(*[imageset(Lambda(n, 2*n*pi + asec(g_y)), \ S.Integers) for g_y in g_ys]) sec_invs_f2 = Union(*[imageset(Lambda(n, 2*n*pi - asec(g_y)), \ S.Integers) for g_y in g_ys]) sec_invs = Union(sec_invs_f1, sec_invs_f2) return _invert_real(f.args[0], sec_invs, symbol) if isinstance(f, tan) or isinstance(f, cot): n = Dummy('n') if isinstance(g_ys, FiniteSet): tan_cot_invs = Union(*[imageset(Lambda(n, n*pi + f.inverse()(g_y)), \ S.Integers) for g_y in g_ys]) return _invert_real(f.args[0], tan_cot_invs, symbol) return (f, g_ys)
def _invert_real(f, g_ys, symbol): """ Helper function for invert_real """ if not f.has(symbol): raise ValueError("Inverse of constant function doesn't exist") if f is symbol: return (f, g_ys) n = Dummy('n') if hasattr(f, 'inverse') and not isinstance(f, TrigonometricFunction) and \ not isinstance(f, HyperbolicFunction): if len(f.args) > 1: raise ValueError("Only functions with one argument are supported.") return _invert_real(f.args[0], imageset(Lambda(n, f.inverse()(n)), g_ys), symbol) if isinstance(f, Abs): return _invert_real(f.args[0], Union(imageset(Lambda(n, n), g_ys).intersect(Interval(0, oo)), imageset(Lambda(n, -n), g_ys).intersect(Interval(-oo, 0))), symbol) if f.is_Add: # f = g + h g, h = f.as_independent(symbol) if g != S.Zero: return _invert_real(h, imageset(Lambda(n, n - g), g_ys), symbol) if f.is_Mul: # f = g*h g, h = f.as_independent(symbol) if g != S.One: return _invert_real(h, imageset(Lambda(n, n/g), g_ys), symbol) if f.is_Pow: base, expo = f.args base_has_sym = base.has(symbol) expo_has_sym = expo.has(symbol) if not expo_has_sym: res = imageset(Lambda(n, real_root(n, expo)), g_ys) if expo.is_rational: numer, denom = expo.as_numer_denom() if numer == S.One or numer == - S.One: return _invert_real(base, res, symbol) else: if numer % 2 == 0: n = Dummy('n') neg_res = imageset(Lambda(n, -n), res) return _invert_real(base, res + neg_res, symbol) else: return _invert_real(base, res, symbol) else: if not base.is_positive: raise ValueError("x**w where w is irrational is not " "defined for negative x") return _invert_real(base, res, symbol) if not base_has_sym: return _invert_real(expo, imageset(Lambda(n, log(n)/log(base)), g_ys), symbol) if isinstance(f, sin): n = Dummy('n') if isinstance(g_ys, FiniteSet): sin_invs = Union(*[imageset(Lambda(n, n*pi + (-1)**n*asin(g_y)), \ S.Integers) for g_y in g_ys]) return _invert_real(f.args[0], sin_invs, symbol) if isinstance(f, csc): n = Dummy('n') if isinstance(g_ys, FiniteSet): csc_invs = Union(*[imageset(Lambda(n, n*pi + (-1)**n*acsc(g_y)), \ S.Integers) for g_y in g_ys]) return _invert_real(f.args[0], csc_invs, symbol) if isinstance(f, cos): n = Dummy('n') if isinstance(g_ys, FiniteSet): cos_invs_f1 = Union(*[imageset(Lambda(n, 2*n*pi + acos(g_y)), \ S.Integers) for g_y in g_ys]) cos_invs_f2 = Union(*[imageset(Lambda(n, 2*n*pi - acos(g_y)), \ S.Integers) for g_y in g_ys]) cos_invs = Union(cos_invs_f1, cos_invs_f2) return _invert_real(f.args[0], cos_invs, symbol) if isinstance(f, sec): n = Dummy('n') if isinstance(g_ys, FiniteSet): sec_invs_f1 = Union(*[imageset(Lambda(n, 2*n*pi + asec(g_y)), \ S.Integers) for g_y in g_ys]) sec_invs_f2 = Union(*[imageset(Lambda(n, 2*n*pi - asec(g_y)), \ S.Integers) for g_y in g_ys]) sec_invs = Union(sec_invs_f1, sec_invs_f2) return _invert_real(f.args[0], sec_invs, symbol) if isinstance(f, tan) or isinstance(f, cot): n = Dummy('n') if isinstance(g_ys, FiniteSet): tan_cot_invs = Union(*[imageset(Lambda(n, n*pi + f.inverse()(g_y)), \ S.Integers) for g_y in g_ys]) return _invert_real(f.args[0], tan_cot_invs, symbol) return (f, g_ys)
def test_piecewise(): # https://github.com/sympy/sympy/issues/18363 assert limit((real_root(x - 6, 3) + 2)/(x + 2), x, -2, '+') == Rational(1, 12)