Spaces:
Running
Running
from __future__ import annotations | |
import numbers | |
import decimal | |
import fractions | |
import math | |
from .containers import Tuple | |
from .sympify import (SympifyError, _sympy_converter, sympify, _convert_numpy_types, | |
_sympify, _is_numpy_instance) | |
from .singleton import S, Singleton | |
from .basic import Basic | |
from .expr import Expr, AtomicExpr | |
from .evalf import pure_complex | |
from .cache import cacheit, clear_cache | |
from .decorators import _sympifyit | |
from .intfunc import num_digits, igcd, ilcm, mod_inverse, integer_nthroot | |
from .logic import fuzzy_not | |
from .kind import NumberKind | |
from sympy.external.gmpy import SYMPY_INTS, gmpy, flint | |
from sympy.multipledispatch import dispatch | |
import mpmath | |
import mpmath.libmp as mlib | |
from mpmath.libmp import bitcount, round_nearest as rnd | |
from mpmath.libmp.backend import MPZ | |
from mpmath.libmp import mpf_pow, mpf_pi, mpf_e, phi_fixed | |
from mpmath.ctx_mp_python import mpnumeric | |
from mpmath.libmp.libmpf import ( | |
finf as _mpf_inf, fninf as _mpf_ninf, | |
fnan as _mpf_nan, fzero, _normalize as mpf_normalize, | |
prec_to_dps, dps_to_prec) | |
from sympy.utilities.misc import debug | |
from .parameters import global_parameters | |
_LOG2 = math.log(2) | |
def comp(z1, z2, tol=None): | |
r"""Return a bool indicating whether the error between z1 and z2 | |
is $\le$ ``tol``. | |
Examples | |
======== | |
If ``tol`` is ``None`` then ``True`` will be returned if | |
:math:`|z1 - z2|\times 10^p \le 5` where $p$ is minimum value of the | |
decimal precision of each value. | |
>>> from sympy import comp, pi | |
>>> pi4 = pi.n(4); pi4 | |
3.142 | |
>>> comp(_, 3.142) | |
True | |
>>> comp(pi4, 3.141) | |
False | |
>>> comp(pi4, 3.143) | |
False | |
A comparison of strings will be made | |
if ``z1`` is a Number and ``z2`` is a string or ``tol`` is ''. | |
>>> comp(pi4, 3.1415) | |
True | |
>>> comp(pi4, 3.1415, '') | |
False | |
When ``tol`` is provided and $z2$ is non-zero and | |
:math:`|z1| > 1` the error is normalized by :math:`|z1|`: | |
>>> abs(pi4 - 3.14)/pi4 | |
0.000509791731426756 | |
>>> comp(pi4, 3.14, .001) # difference less than 0.1% | |
True | |
>>> comp(pi4, 3.14, .0005) # difference less than 0.1% | |
False | |
When :math:`|z1| \le 1` the absolute error is used: | |
>>> 1/pi4 | |
0.3183 | |
>>> abs(1/pi4 - 0.3183)/(1/pi4) | |
3.07371499106316e-5 | |
>>> abs(1/pi4 - 0.3183) | |
9.78393554684764e-6 | |
>>> comp(1/pi4, 0.3183, 1e-5) | |
True | |
To see if the absolute error between ``z1`` and ``z2`` is less | |
than or equal to ``tol``, call this as ``comp(z1 - z2, 0, tol)`` | |
or ``comp(z1 - z2, tol=tol)``: | |
>>> abs(pi4 - 3.14) | |
0.00160156249999988 | |
>>> comp(pi4 - 3.14, 0, .002) | |
True | |
>>> comp(pi4 - 3.14, 0, .001) | |
False | |
""" | |
if isinstance(z2, str): | |
if not pure_complex(z1, or_real=True): | |
raise ValueError('when z2 is a str z1 must be a Number') | |
return str(z1) == z2 | |
if not z1: | |
z1, z2 = z2, z1 | |
if not z1: | |
return True | |
if not tol: | |
a, b = z1, z2 | |
if tol == '': | |
return str(a) == str(b) | |
if tol is None: | |
a, b = sympify(a), sympify(b) | |
if not all(i.is_number for i in (a, b)): | |
raise ValueError('expecting 2 numbers') | |
fa = a.atoms(Float) | |
fb = b.atoms(Float) | |
if not fa and not fb: | |
# no floats -- compare exactly | |
return a == b | |
# get a to be pure_complex | |
for _ in range(2): | |
ca = pure_complex(a, or_real=True) | |
if not ca: | |
if fa: | |
a = a.n(prec_to_dps(min(i._prec for i in fa))) | |
ca = pure_complex(a, or_real=True) | |
break | |
else: | |
fa, fb = fb, fa | |
a, b = b, a | |
cb = pure_complex(b) | |
if not cb and fb: | |
b = b.n(prec_to_dps(min(i._prec for i in fb))) | |
cb = pure_complex(b, or_real=True) | |
if ca and cb and (ca[1] or cb[1]): | |
return all(comp(i, j) for i, j in zip(ca, cb)) | |
tol = 10**prec_to_dps(min(a._prec, getattr(b, '_prec', a._prec))) | |
return int(abs(a - b)*tol) <= 5 | |
diff = abs(z1 - z2) | |
az1 = abs(z1) | |
if z2 and az1 > 1: | |
return diff/az1 <= tol | |
else: | |
return diff <= tol | |
def mpf_norm(mpf, prec): | |
"""Return the mpf tuple normalized appropriately for the indicated | |
precision after doing a check to see if zero should be returned or | |
not when the mantissa is 0. ``mpf_normlize`` always assumes that this | |
is zero, but it may not be since the mantissa for mpf's values "+inf", | |
"-inf" and "nan" have a mantissa of zero, too. | |
Note: this is not intended to validate a given mpf tuple, so sending | |
mpf tuples that were not created by mpmath may produce bad results. This | |
is only a wrapper to ``mpf_normalize`` which provides the check for non- | |
zero mpfs that have a 0 for the mantissa. | |
""" | |
sign, man, expt, bc = mpf | |
if not man: | |
# hack for mpf_normalize which does not do this; | |
# it assumes that if man is zero the result is 0 | |
# (see issue 6639) | |
if not bc: | |
return fzero | |
else: | |
# don't change anything; this should already | |
# be a well formed mpf tuple | |
return mpf | |
# Necessary if mpmath is using the gmpy backend | |
from mpmath.libmp.backend import MPZ | |
rv = mpf_normalize(sign, MPZ(man), expt, bc, prec, rnd) | |
return rv | |
# TODO: we should use the warnings module | |
_errdict = {"divide": False} | |
def seterr(divide=False): | |
""" | |
Should SymPy raise an exception on 0/0 or return a nan? | |
divide == True .... raise an exception | |
divide == False ... return nan | |
""" | |
if _errdict["divide"] != divide: | |
clear_cache() | |
_errdict["divide"] = divide | |
def _as_integer_ratio(p): | |
neg_pow, man, expt, _ = getattr(p, '_mpf_', mpmath.mpf(p)._mpf_) | |
p = [1, -1][neg_pow % 2]*man | |
if expt < 0: | |
q = 2**-expt | |
else: | |
q = 1 | |
p *= 2**expt | |
return int(p), int(q) | |
def _decimal_to_Rational_prec(dec): | |
"""Convert an ordinary decimal instance to a Rational.""" | |
if not dec.is_finite(): | |
raise TypeError("dec must be finite, got %s." % dec) | |
s, d, e = dec.as_tuple() | |
prec = len(d) | |
if e >= 0: # it's an integer | |
rv = Integer(int(dec)) | |
else: | |
s = (-1)**s | |
d = sum(di*10**i for i, di in enumerate(reversed(d))) | |
rv = Rational(s*d, 10**-e) | |
return rv, prec | |
_dig = str.maketrans(dict.fromkeys('1234567890')) | |
def _literal_float(s): | |
"""return True if s is space-trimmed number literal else False | |
Python allows underscore as digit separators: there must be a | |
digit on each side. So neither a leading underscore nor a | |
double underscore are valid as part of a number. A number does | |
not have to precede the decimal point, but there must be a | |
digit before the optional "e" or "E" that begins the signs | |
exponent of the number which must be an integer, perhaps with | |
underscore separators. | |
SymPy allows space as a separator; if the calling routine replaces | |
them with underscores then the same semantics will be enforced | |
for them as for underscores: there can only be 1 *between* digits. | |
We don't check for error from float(s) because we don't know | |
whether s is malicious or not. A regex for this could maybe | |
be written but will it be understood by most who read it? | |
""" | |
# mantissa and exponent | |
parts = s.split('e') | |
if len(parts) > 2: | |
return False | |
if len(parts) == 2: | |
m, e = parts | |
if e.startswith(tuple('+-')): | |
e = e[1:] | |
if not e: | |
return False | |
else: | |
m, e = s, '1' | |
# integer and fraction of mantissa | |
parts = m.split('.') | |
if len(parts) > 2: | |
return False | |
elif len(parts) == 2: | |
i, f = parts | |
else: | |
i, f = m, '1' | |
if not i and not f: | |
return False | |
if i and i[0] in '+-': | |
i = i[1:] | |
if not i: # -.3e4 -> -0.3e4 | |
i = '1' | |
f = f or '1' | |
# check that all groups contain only digits and are not null | |
for n in (i, f, e): | |
for g in n.split('_'): | |
if not g or g.translate(_dig): | |
return False | |
return True | |
# (a,b) -> gcd(a,b) | |
# TODO caching with decorator, but not to degrade performance | |
class Number(AtomicExpr): | |
"""Represents atomic numbers in SymPy. | |
Explanation | |
=========== | |
Floating point numbers are represented by the Float class. | |
Rational numbers (of any size) are represented by the Rational class. | |
Integer numbers (of any size) are represented by the Integer class. | |
Float and Rational are subclasses of Number; Integer is a subclass | |
of Rational. | |
For example, ``2/3`` is represented as ``Rational(2, 3)`` which is | |
a different object from the floating point number obtained with | |
Python division ``2/3``. Even for numbers that are exactly | |
represented in binary, there is a difference between how two forms, | |
such as ``Rational(1, 2)`` and ``Float(0.5)``, are used in SymPy. | |
The rational form is to be preferred in symbolic computations. | |
Other kinds of numbers, such as algebraic numbers ``sqrt(2)`` or | |
complex numbers ``3 + 4*I``, are not instances of Number class as | |
they are not atomic. | |
See Also | |
======== | |
Float, Integer, Rational | |
""" | |
is_commutative = True | |
is_number = True | |
is_Number = True | |
__slots__ = () | |
# Used to make max(x._prec, y._prec) return x._prec when only x is a float | |
_prec = -1 | |
kind = NumberKind | |
def __new__(cls, *obj): | |
if len(obj) == 1: | |
obj = obj[0] | |
if isinstance(obj, Number): | |
return obj | |
if isinstance(obj, SYMPY_INTS): | |
return Integer(obj) | |
if isinstance(obj, tuple) and len(obj) == 2: | |
return Rational(*obj) | |
if isinstance(obj, (float, mpmath.mpf, decimal.Decimal)): | |
return Float(obj) | |
if isinstance(obj, str): | |
_obj = obj.lower() # float('INF') == float('inf') | |
if _obj == 'nan': | |
return S.NaN | |
elif _obj == 'inf': | |
return S.Infinity | |
elif _obj == '+inf': | |
return S.Infinity | |
elif _obj == '-inf': | |
return S.NegativeInfinity | |
val = sympify(obj) | |
if isinstance(val, Number): | |
return val | |
else: | |
raise ValueError('String "%s" does not denote a Number' % obj) | |
msg = "expected str|int|long|float|Decimal|Number object but got %r" | |
raise TypeError(msg % type(obj).__name__) | |
def could_extract_minus_sign(self): | |
return bool(self.is_extended_negative) | |
def invert(self, other, *gens, **args): | |
from sympy.polys.polytools import invert | |
if getattr(other, 'is_number', True): | |
return mod_inverse(self, other) | |
return invert(self, other, *gens, **args) | |
def __divmod__(self, other): | |
from sympy.functions.elementary.complexes import sign | |
try: | |
other = Number(other) | |
if self.is_infinite or S.NaN in (self, other): | |
return (S.NaN, S.NaN) | |
except TypeError: | |
return NotImplemented | |
if not other: | |
raise ZeroDivisionError('modulo by zero') | |
if self.is_Integer and other.is_Integer: | |
return Tuple(*divmod(self.p, other.p)) | |
elif isinstance(other, Float): | |
rat = self/Rational(other) | |
else: | |
rat = self/other | |
if other.is_finite: | |
w = int(rat) if rat >= 0 else int(rat) - 1 | |
r = self - other*w | |
if r == Float(other): | |
w += 1 | |
r = 0 | |
if isinstance(self, Float) or isinstance(other, Float): | |
r = Float(r) # in case w or r is 0 | |
else: | |
w = 0 if not self or (sign(self) == sign(other)) else -1 | |
r = other if w else self | |
return Tuple(w, r) | |
def __rdivmod__(self, other): | |
try: | |
other = Number(other) | |
except TypeError: | |
return NotImplemented | |
return divmod(other, self) | |
def _as_mpf_val(self, prec): | |
"""Evaluation of mpf tuple accurate to at least prec bits.""" | |
raise NotImplementedError('%s needs ._as_mpf_val() method' % | |
(self.__class__.__name__)) | |
def _eval_evalf(self, prec): | |
return Float._new(self._as_mpf_val(prec), prec) | |
def _as_mpf_op(self, prec): | |
prec = max(prec, self._prec) | |
return self._as_mpf_val(prec), prec | |
def __float__(self): | |
return mlib.to_float(self._as_mpf_val(53)) | |
def floor(self): | |
raise NotImplementedError('%s needs .floor() method' % | |
(self.__class__.__name__)) | |
def ceiling(self): | |
raise NotImplementedError('%s needs .ceiling() method' % | |
(self.__class__.__name__)) | |
def __floor__(self): | |
return self.floor() | |
def __ceil__(self): | |
return self.ceiling() | |
def _eval_conjugate(self): | |
return self | |
def _eval_order(self, *symbols): | |
from sympy.series.order import Order | |
# Order(5, x, y) -> Order(1,x,y) | |
return Order(S.One, *symbols) | |
def _eval_subs(self, old, new): | |
if old == -self: | |
return -new | |
return self # there is no other possibility | |
def class_key(cls): | |
return 1, 0, 'Number' | |
def sort_key(self, order=None): | |
return self.class_key(), (0, ()), (), self | |
def __add__(self, other): | |
if isinstance(other, Number) and global_parameters.evaluate: | |
if other is S.NaN: | |
return S.NaN | |
elif other is S.Infinity: | |
return S.Infinity | |
elif other is S.NegativeInfinity: | |
return S.NegativeInfinity | |
return AtomicExpr.__add__(self, other) | |
def __sub__(self, other): | |
if isinstance(other, Number) and global_parameters.evaluate: | |
if other is S.NaN: | |
return S.NaN | |
elif other is S.Infinity: | |
return S.NegativeInfinity | |
elif other is S.NegativeInfinity: | |
return S.Infinity | |
return AtomicExpr.__sub__(self, other) | |
def __mul__(self, other): | |
if isinstance(other, Number) and global_parameters.evaluate: | |
if other is S.NaN: | |
return S.NaN | |
elif other is S.Infinity: | |
if self.is_zero: | |
return S.NaN | |
elif self.is_positive: | |
return S.Infinity | |
else: | |
return S.NegativeInfinity | |
elif other is S.NegativeInfinity: | |
if self.is_zero: | |
return S.NaN | |
elif self.is_positive: | |
return S.NegativeInfinity | |
else: | |
return S.Infinity | |
elif isinstance(other, Tuple): | |
return NotImplemented | |
return AtomicExpr.__mul__(self, other) | |
def __truediv__(self, other): | |
if isinstance(other, Number) and global_parameters.evaluate: | |
if other is S.NaN: | |
return S.NaN | |
elif other in (S.Infinity, S.NegativeInfinity): | |
return S.Zero | |
return AtomicExpr.__truediv__(self, other) | |
def __eq__(self, other): | |
raise NotImplementedError('%s needs .__eq__() method' % | |
(self.__class__.__name__)) | |
def __ne__(self, other): | |
raise NotImplementedError('%s needs .__ne__() method' % | |
(self.__class__.__name__)) | |
def __lt__(self, other): | |
try: | |
other = _sympify(other) | |
except SympifyError: | |
raise TypeError("Invalid comparison %s < %s" % (self, other)) | |
raise NotImplementedError('%s needs .__lt__() method' % | |
(self.__class__.__name__)) | |
def __le__(self, other): | |
try: | |
other = _sympify(other) | |
except SympifyError: | |
raise TypeError("Invalid comparison %s <= %s" % (self, other)) | |
raise NotImplementedError('%s needs .__le__() method' % | |
(self.__class__.__name__)) | |
def __gt__(self, other): | |
try: | |
other = _sympify(other) | |
except SympifyError: | |
raise TypeError("Invalid comparison %s > %s" % (self, other)) | |
return _sympify(other).__lt__(self) | |
def __ge__(self, other): | |
try: | |
other = _sympify(other) | |
except SympifyError: | |
raise TypeError("Invalid comparison %s >= %s" % (self, other)) | |
return _sympify(other).__le__(self) | |
def __hash__(self): | |
return super().__hash__() | |
def is_constant(self, *wrt, **flags): | |
return True | |
def as_coeff_mul(self, *deps, rational=True, **kwargs): | |
# a -> c*t | |
if self.is_Rational or not rational: | |
return self, () | |
elif self.is_negative: | |
return S.NegativeOne, (-self,) | |
return S.One, (self,) | |
def as_coeff_add(self, *deps): | |
# a -> c + t | |
if self.is_Rational: | |
return self, () | |
return S.Zero, (self,) | |
def as_coeff_Mul(self, rational=False): | |
"""Efficiently extract the coefficient of a product.""" | |
if not rational: | |
return self, S.One | |
return S.One, self | |
def as_coeff_Add(self, rational=False): | |
"""Efficiently extract the coefficient of a summation.""" | |
if not rational: | |
return self, S.Zero | |
return S.Zero, self | |
def gcd(self, other): | |
"""Compute GCD of `self` and `other`. """ | |
from sympy.polys.polytools import gcd | |
return gcd(self, other) | |
def lcm(self, other): | |
"""Compute LCM of `self` and `other`. """ | |
from sympy.polys.polytools import lcm | |
return lcm(self, other) | |
def cofactors(self, other): | |
"""Compute GCD and cofactors of `self` and `other`. """ | |
from sympy.polys.polytools import cofactors | |
return cofactors(self, other) | |
class Float(Number): | |
"""Represent a floating-point number of arbitrary precision. | |
Examples | |
======== | |
>>> from sympy import Float | |
>>> Float(3.5) | |
3.50000000000000 | |
>>> Float(3) | |
3.00000000000000 | |
Creating Floats from strings (and Python ``int`` and ``long`` | |
types) will give a minimum precision of 15 digits, but the | |
precision will automatically increase to capture all digits | |
entered. | |
>>> Float(1) | |
1.00000000000000 | |
>>> Float(10**20) | |
100000000000000000000. | |
>>> Float('1e20') | |
100000000000000000000. | |
However, *floating-point* numbers (Python ``float`` types) retain | |
only 15 digits of precision: | |
>>> Float(1e20) | |
1.00000000000000e+20 | |
>>> Float(1.23456789123456789) | |
1.23456789123457 | |
It may be preferable to enter high-precision decimal numbers | |
as strings: | |
>>> Float('1.23456789123456789') | |
1.23456789123456789 | |
The desired number of digits can also be specified: | |
>>> Float('1e-3', 3) | |
0.00100 | |
>>> Float(100, 4) | |
100.0 | |
Float can automatically count significant figures if a null string | |
is sent for the precision; spaces or underscores are also allowed. (Auto- | |
counting is only allowed for strings, ints and longs). | |
>>> Float('123 456 789.123_456', '') | |
123456789.123456 | |
>>> Float('12e-3', '') | |
0.012 | |
>>> Float(3, '') | |
3. | |
If a number is written in scientific notation, only the digits before the | |
exponent are considered significant if a decimal appears, otherwise the | |
"e" signifies only how to move the decimal: | |
>>> Float('60.e2', '') # 2 digits significant | |
6.0e+3 | |
>>> Float('60e2', '') # 4 digits significant | |
6000. | |
>>> Float('600e-2', '') # 3 digits significant | |
6.00 | |
Notes | |
===== | |
Floats are inexact by their nature unless their value is a binary-exact | |
value. | |
>>> approx, exact = Float(.1, 1), Float(.125, 1) | |
For calculation purposes, evalf needs to be able to change the precision | |
but this will not increase the accuracy of the inexact value. The | |
following is the most accurate 5-digit approximation of a value of 0.1 | |
that had only 1 digit of precision: | |
>>> approx.evalf(5) | |
0.099609 | |
By contrast, 0.125 is exact in binary (as it is in base 10) and so it | |
can be passed to Float or evalf to obtain an arbitrary precision with | |
matching accuracy: | |
>>> Float(exact, 5) | |
0.12500 | |
>>> exact.evalf(20) | |
0.12500000000000000000 | |
Trying to make a high-precision Float from a float is not disallowed, | |
but one must keep in mind that the *underlying float* (not the apparent | |
decimal value) is being obtained with high precision. For example, 0.3 | |
does not have a finite binary representation. The closest rational is | |
the fraction 5404319552844595/2**54. So if you try to obtain a Float of | |
0.3 to 20 digits of precision you will not see the same thing as 0.3 | |
followed by 19 zeros: | |
>>> Float(0.3, 20) | |
0.29999999999999998890 | |
If you want a 20-digit value of the decimal 0.3 (not the floating point | |
approximation of 0.3) you should send the 0.3 as a string. The underlying | |
representation is still binary but a higher precision than Python's float | |
is used: | |
>>> Float('0.3', 20) | |
0.30000000000000000000 | |
Although you can increase the precision of an existing Float using Float | |
it will not increase the accuracy -- the underlying value is not changed: | |
>>> def show(f): # binary rep of Float | |
... from sympy import Mul, Pow | |
... s, m, e, b = f._mpf_ | |
... v = Mul(int(m), Pow(2, int(e), evaluate=False), evaluate=False) | |
... print('%s at prec=%s' % (v, f._prec)) | |
... | |
>>> t = Float('0.3', 3) | |
>>> show(t) | |
4915/2**14 at prec=13 | |
>>> show(Float(t, 20)) # higher prec, not higher accuracy | |
4915/2**14 at prec=70 | |
>>> show(Float(t, 2)) # lower prec | |
307/2**10 at prec=10 | |
The same thing happens when evalf is used on a Float: | |
>>> show(t.evalf(20)) | |
4915/2**14 at prec=70 | |
>>> show(t.evalf(2)) | |
307/2**10 at prec=10 | |
Finally, Floats can be instantiated with an mpf tuple (n, c, p) to | |
produce the number (-1)**n*c*2**p: | |
>>> n, c, p = 1, 5, 0 | |
>>> (-1)**n*c*2**p | |
-5 | |
>>> Float((1, 5, 0)) | |
-5.00000000000000 | |
An actual mpf tuple also contains the number of bits in c as the last | |
element of the tuple: | |
>>> _._mpf_ | |
(1, 5, 0, 3) | |
This is not needed for instantiation and is not the same thing as the | |
precision. The mpf tuple and the precision are two separate quantities | |
that Float tracks. | |
In SymPy, a Float is a number that can be computed with arbitrary | |
precision. Although floating point 'inf' and 'nan' are not such | |
numbers, Float can create these numbers: | |
>>> Float('-inf') | |
-oo | |
>>> _.is_Float | |
False | |
Zero in Float only has a single value. Values are not separate for | |
positive and negative zeroes. | |
""" | |
__slots__ = ('_mpf_', '_prec') | |
_mpf_: tuple[int, int, int, int] | |
# A Float, though rational in form, does not behave like | |
# a rational in all Python expressions so we deal with | |
# exceptions (where we want to deal with the rational | |
# form of the Float as a rational) at the source rather | |
# than assigning a mathematically loaded category of 'rational' | |
is_rational = None | |
is_irrational = None | |
is_number = True | |
is_real = True | |
is_extended_real = True | |
is_Float = True | |
_remove_non_digits = str.maketrans(dict.fromkeys("-+_.")) | |
def __new__(cls, num, dps=None, precision=None): | |
if dps is not None and precision is not None: | |
raise ValueError('Both decimal and binary precision supplied. ' | |
'Supply only one. ') | |
if isinstance(num, str): | |
_num = num = num.strip() # Python ignores leading and trailing space | |
num = num.replace(' ', '_').lower() # Float treats spaces as digit sep; E -> e | |
if num.startswith('.') and len(num) > 1: | |
num = '0' + num | |
elif num.startswith('-.') and len(num) > 2: | |
num = '-0.' + num[2:] | |
elif num in ('inf', '+inf'): | |
return S.Infinity | |
elif num == '-inf': | |
return S.NegativeInfinity | |
elif num == 'nan': | |
return S.NaN | |
elif not _literal_float(num): | |
raise ValueError('string-float not recognized: %s' % _num) | |
elif isinstance(num, float) and num == 0: | |
num = '0' | |
elif isinstance(num, float) and num == float('inf'): | |
return S.Infinity | |
elif isinstance(num, float) and num == float('-inf'): | |
return S.NegativeInfinity | |
elif isinstance(num, float) and math.isnan(num): | |
return S.NaN | |
elif isinstance(num, (SYMPY_INTS, Integer)): | |
num = str(num) | |
elif num is S.Infinity: | |
return num | |
elif num is S.NegativeInfinity: | |
return num | |
elif num is S.NaN: | |
return num | |
elif _is_numpy_instance(num): # support for numpy datatypes | |
num = _convert_numpy_types(num) | |
elif isinstance(num, mpmath.mpf): | |
if precision is None: | |
if dps is None: | |
precision = num.context.prec | |
num = num._mpf_ | |
if dps is None and precision is None: | |
dps = 15 | |
if isinstance(num, Float): | |
return num | |
if isinstance(num, str): | |
try: | |
Num = decimal.Decimal(num) | |
except decimal.InvalidOperation: | |
pass | |
else: | |
isint = '.' not in num | |
num, dps = _decimal_to_Rational_prec(Num) | |
if num.is_Integer and isint: | |
# 12e3 is shorthand for int, not float; | |
# 12.e3 would be the float version | |
dps = max(dps, num_digits(num)) | |
dps = max(15, dps) | |
precision = dps_to_prec(dps) | |
elif precision == '' and dps is None or precision is None and dps == '': | |
if not isinstance(num, str): | |
raise ValueError('The null string can only be used when ' | |
'the number to Float is passed as a string or an integer.') | |
try: | |
Num = decimal.Decimal(num) | |
except decimal.InvalidOperation: | |
raise ValueError('string-float not recognized by Decimal: %s' % num) | |
else: | |
isint = '.' not in num | |
num, dps = _decimal_to_Rational_prec(Num) | |
if num.is_Integer and isint: | |
# without dec, e-notation is short for int | |
dps = max(dps, num_digits(num)) | |
precision = dps_to_prec(dps) | |
# decimal precision(dps) is set and maybe binary precision(precision) | |
# as well.From here on binary precision is used to compute the Float. | |
# Hence, if supplied use binary precision else translate from decimal | |
# precision. | |
if precision is None or precision == '': | |
precision = dps_to_prec(dps) | |
precision = int(precision) | |
if isinstance(num, float): | |
_mpf_ = mlib.from_float(num, precision, rnd) | |
elif isinstance(num, str): | |
_mpf_ = mlib.from_str(num, precision, rnd) | |
elif isinstance(num, decimal.Decimal): | |
if num.is_finite(): | |
_mpf_ = mlib.from_str(str(num), precision, rnd) | |
elif num.is_nan(): | |
return S.NaN | |
elif num.is_infinite(): | |
if num > 0: | |
return S.Infinity | |
return S.NegativeInfinity | |
else: | |
raise ValueError("unexpected decimal value %s" % str(num)) | |
elif isinstance(num, tuple) and len(num) in (3, 4): | |
if isinstance(num[1], str): | |
# it's a hexadecimal (coming from a pickled object) | |
num = list(num) | |
# If we're loading an object pickled in Python 2 into | |
# Python 3, we may need to strip a tailing 'L' because | |
# of a shim for int on Python 3, see issue #13470. | |
if num[1].endswith('L'): | |
num[1] = num[1][:-1] | |
# Strip leading '0x' - gmpy2 only documents such inputs | |
# with base prefix as valid when the 2nd argument (base) is 0. | |
# When mpmath uses Sage as the backend, however, it | |
# ends up including '0x' when preparing the picklable tuple. | |
# See issue #19690. | |
if num[1].startswith('0x'): | |
num[1] = num[1][2:] | |
# Now we can assume that it is in standard form | |
num[1] = MPZ(num[1], 16) | |
_mpf_ = tuple(num) | |
else: | |
if len(num) == 4: | |
# handle normalization hack | |
return Float._new(num, precision) | |
else: | |
if not all(( | |
num[0] in (0, 1), | |
num[1] >= 0, | |
all(type(i) in (int, int) for i in num) | |
)): | |
raise ValueError('malformed mpf: %s' % (num,)) | |
# don't compute number or else it may | |
# over/underflow | |
return Float._new( | |
(num[0], num[1], num[2], bitcount(num[1])), | |
precision) | |
elif isinstance(num, (Number, NumberSymbol)): | |
_mpf_ = num._as_mpf_val(precision) | |
else: | |
_mpf_ = mpmath.mpf(num, prec=precision)._mpf_ | |
return cls._new(_mpf_, precision, zero=False) | |
def _new(cls, _mpf_, _prec, zero=True): | |
# special cases | |
if zero and _mpf_ == fzero: | |
return S.Zero # Float(0) -> 0.0; Float._new((0,0,0,0)) -> 0 | |
elif _mpf_ == _mpf_nan: | |
return S.NaN | |
elif _mpf_ == _mpf_inf: | |
return S.Infinity | |
elif _mpf_ == _mpf_ninf: | |
return S.NegativeInfinity | |
obj = Expr.__new__(cls) | |
obj._mpf_ = mpf_norm(_mpf_, _prec) | |
obj._prec = _prec | |
return obj | |
def __getnewargs_ex__(self): | |
sign, man, exp, bc = self._mpf_ | |
arg = (sign, hex(man)[2:], exp, bc) | |
kwargs = {'precision': self._prec} | |
return ((arg,), kwargs) | |
def _hashable_content(self): | |
return (self._mpf_, self._prec) | |
def floor(self): | |
return Integer(int(mlib.to_int( | |
mlib.mpf_floor(self._mpf_, self._prec)))) | |
def ceiling(self): | |
return Integer(int(mlib.to_int( | |
mlib.mpf_ceil(self._mpf_, self._prec)))) | |
def __floor__(self): | |
return self.floor() | |
def __ceil__(self): | |
return self.ceiling() | |
def num(self): | |
return mpmath.mpf(self._mpf_) | |
def _as_mpf_val(self, prec): | |
rv = mpf_norm(self._mpf_, prec) | |
if rv != self._mpf_ and self._prec == prec: | |
debug(self._mpf_, rv) | |
return rv | |
def _as_mpf_op(self, prec): | |
return self._mpf_, max(prec, self._prec) | |
def _eval_is_finite(self): | |
if self._mpf_ in (_mpf_inf, _mpf_ninf): | |
return False | |
return True | |
def _eval_is_infinite(self): | |
if self._mpf_ in (_mpf_inf, _mpf_ninf): | |
return True | |
return False | |
def _eval_is_integer(self): | |
if self._mpf_ == fzero: | |
return True | |
if not int_valued(self): | |
return False | |
def _eval_is_negative(self): | |
if self._mpf_ in (_mpf_ninf, _mpf_inf): | |
return False | |
return self.num < 0 | |
def _eval_is_positive(self): | |
if self._mpf_ in (_mpf_ninf, _mpf_inf): | |
return False | |
return self.num > 0 | |
def _eval_is_extended_negative(self): | |
if self._mpf_ == _mpf_ninf: | |
return True | |
if self._mpf_ == _mpf_inf: | |
return False | |
return self.num < 0 | |
def _eval_is_extended_positive(self): | |
if self._mpf_ == _mpf_inf: | |
return True | |
if self._mpf_ == _mpf_ninf: | |
return False | |
return self.num > 0 | |
def _eval_is_zero(self): | |
return self._mpf_ == fzero | |
def __bool__(self): | |
return self._mpf_ != fzero | |
def __neg__(self): | |
if not self: | |
return self | |
return Float._new(mlib.mpf_neg(self._mpf_), self._prec) | |
def __add__(self, other): | |
if isinstance(other, Number) and global_parameters.evaluate: | |
rhs, prec = other._as_mpf_op(self._prec) | |
return Float._new(mlib.mpf_add(self._mpf_, rhs, prec, rnd), prec) | |
return Number.__add__(self, other) | |
def __sub__(self, other): | |
if isinstance(other, Number) and global_parameters.evaluate: | |
rhs, prec = other._as_mpf_op(self._prec) | |
return Float._new(mlib.mpf_sub(self._mpf_, rhs, prec, rnd), prec) | |
return Number.__sub__(self, other) | |
def __mul__(self, other): | |
if isinstance(other, Number) and global_parameters.evaluate: | |
rhs, prec = other._as_mpf_op(self._prec) | |
return Float._new(mlib.mpf_mul(self._mpf_, rhs, prec, rnd), prec) | |
return Number.__mul__(self, other) | |
def __truediv__(self, other): | |
if isinstance(other, Number) and other != 0 and global_parameters.evaluate: | |
rhs, prec = other._as_mpf_op(self._prec) | |
return Float._new(mlib.mpf_div(self._mpf_, rhs, prec, rnd), prec) | |
return Number.__truediv__(self, other) | |
def __mod__(self, other): | |
if isinstance(other, Rational) and other.q != 1 and global_parameters.evaluate: | |
# calculate mod with Rationals, *then* round the result | |
return Float(Rational.__mod__(Rational(self), other), | |
precision=self._prec) | |
if isinstance(other, Float) and global_parameters.evaluate: | |
r = self/other | |
if int_valued(r): | |
return Float(0, precision=max(self._prec, other._prec)) | |
if isinstance(other, Number) and global_parameters.evaluate: | |
rhs, prec = other._as_mpf_op(self._prec) | |
return Float._new(mlib.mpf_mod(self._mpf_, rhs, prec, rnd), prec) | |
return Number.__mod__(self, other) | |
def __rmod__(self, other): | |
if isinstance(other, Float) and global_parameters.evaluate: | |
return other.__mod__(self) | |
if isinstance(other, Number) and global_parameters.evaluate: | |
rhs, prec = other._as_mpf_op(self._prec) | |
return Float._new(mlib.mpf_mod(rhs, self._mpf_, prec, rnd), prec) | |
return Number.__rmod__(self, other) | |
def _eval_power(self, expt): | |
""" | |
expt is symbolic object but not equal to 0, 1 | |
(-p)**r -> exp(r*log(-p)) -> exp(r*(log(p) + I*Pi)) -> | |
-> p**r*(sin(Pi*r) + cos(Pi*r)*I) | |
""" | |
if equal_valued(self, 0): | |
if expt.is_extended_positive: | |
return self | |
if expt.is_extended_negative: | |
return S.ComplexInfinity | |
if isinstance(expt, Number): | |
if isinstance(expt, Integer): | |
prec = self._prec | |
return Float._new( | |
mlib.mpf_pow_int(self._mpf_, expt.p, prec, rnd), prec) | |
elif isinstance(expt, Rational) and \ | |
expt.p == 1 and expt.q % 2 and self.is_negative: | |
return Pow(S.NegativeOne, expt, evaluate=False)*( | |
-self)._eval_power(expt) | |
expt, prec = expt._as_mpf_op(self._prec) | |
mpfself = self._mpf_ | |
try: | |
y = mpf_pow(mpfself, expt, prec, rnd) | |
return Float._new(y, prec) | |
except mlib.ComplexResult: | |
re, im = mlib.mpc_pow( | |
(mpfself, fzero), (expt, fzero), prec, rnd) | |
return Float._new(re, prec) + \ | |
Float._new(im, prec)*S.ImaginaryUnit | |
def __abs__(self): | |
return Float._new(mlib.mpf_abs(self._mpf_), self._prec) | |
def __int__(self): | |
if self._mpf_ == fzero: | |
return 0 | |
return int(mlib.to_int(self._mpf_)) # uses round_fast = round_down | |
def __eq__(self, other): | |
if isinstance(other, float): | |
other = Float(other) | |
return Basic.__eq__(self, other) | |
def __ne__(self, other): | |
eq = self.__eq__(other) | |
if eq is NotImplemented: | |
return eq | |
else: | |
return not eq | |
def __hash__(self): | |
float_val = float(self) | |
if not math.isinf(float_val): | |
return hash(float_val) | |
return Basic.__hash__(self) | |
def _Frel(self, other, op): | |
try: | |
other = _sympify(other) | |
except SympifyError: | |
return NotImplemented | |
if other.is_Rational: | |
# test self*other.q <?> other.p without losing precision | |
''' | |
>>> f = Float(.1,2) | |
>>> i = 1234567890 | |
>>> (f*i)._mpf_ | |
(0, 471, 18, 9) | |
>>> mlib.mpf_mul(f._mpf_, mlib.from_int(i)) | |
(0, 505555550955, -12, 39) | |
''' | |
smpf = mlib.mpf_mul(self._mpf_, mlib.from_int(other.q)) | |
ompf = mlib.from_int(other.p) | |
return _sympify(bool(op(smpf, ompf))) | |
elif other.is_Float: | |
return _sympify(bool( | |
op(self._mpf_, other._mpf_))) | |
elif other.is_comparable and other not in ( | |
S.Infinity, S.NegativeInfinity): | |
other = other.evalf(prec_to_dps(self._prec)) | |
if other._prec > 1: | |
if other.is_Number: | |
return _sympify(bool( | |
op(self._mpf_, other._as_mpf_val(self._prec)))) | |
def __gt__(self, other): | |
if isinstance(other, NumberSymbol): | |
return other.__lt__(self) | |
rv = self._Frel(other, mlib.mpf_gt) | |
if rv is None: | |
return Expr.__gt__(self, other) | |
return rv | |
def __ge__(self, other): | |
if isinstance(other, NumberSymbol): | |
return other.__le__(self) | |
rv = self._Frel(other, mlib.mpf_ge) | |
if rv is None: | |
return Expr.__ge__(self, other) | |
return rv | |
def __lt__(self, other): | |
if isinstance(other, NumberSymbol): | |
return other.__gt__(self) | |
rv = self._Frel(other, mlib.mpf_lt) | |
if rv is None: | |
return Expr.__lt__(self, other) | |
return rv | |
def __le__(self, other): | |
if isinstance(other, NumberSymbol): | |
return other.__ge__(self) | |
rv = self._Frel(other, mlib.mpf_le) | |
if rv is None: | |
return Expr.__le__(self, other) | |
return rv | |
def epsilon_eq(self, other, epsilon="1e-15"): | |
return abs(self - other) < Float(epsilon) | |
def __format__(self, format_spec): | |
return format(decimal.Decimal(str(self)), format_spec) | |
# Add sympify converters | |
_sympy_converter[float] = _sympy_converter[decimal.Decimal] = Float | |
# this is here to work nicely in Sage | |
RealNumber = Float | |
class Rational(Number): | |
"""Represents rational numbers (p/q) of any size. | |
Examples | |
======== | |
>>> from sympy import Rational, nsimplify, S, pi | |
>>> Rational(1, 2) | |
1/2 | |
Rational is unprejudiced in accepting input. If a float is passed, the | |
underlying value of the binary representation will be returned: | |
>>> Rational(.5) | |
1/2 | |
>>> Rational(.2) | |
3602879701896397/18014398509481984 | |
If the simpler representation of the float is desired then consider | |
limiting the denominator to the desired value or convert the float to | |
a string (which is roughly equivalent to limiting the denominator to | |
10**12): | |
>>> Rational(str(.2)) | |
1/5 | |
>>> Rational(.2).limit_denominator(10**12) | |
1/5 | |
An arbitrarily precise Rational is obtained when a string literal is | |
passed: | |
>>> Rational("1.23") | |
123/100 | |
>>> Rational('1e-2') | |
1/100 | |
>>> Rational(".1") | |
1/10 | |
>>> Rational('1e-2/3.2') | |
1/320 | |
The conversion of other types of strings can be handled by | |
the sympify() function, and conversion of floats to expressions | |
or simple fractions can be handled with nsimplify: | |
>>> S('.[3]') # repeating digits in brackets | |
1/3 | |
>>> S('3**2/10') # general expressions | |
9/10 | |
>>> nsimplify(.3) # numbers that have a simple form | |
3/10 | |
But if the input does not reduce to a literal Rational, an error will | |
be raised: | |
>>> Rational(pi) | |
Traceback (most recent call last): | |
... | |
TypeError: invalid input: pi | |
Low-level | |
--------- | |
Access numerator and denominator as .p and .q: | |
>>> r = Rational(3, 4) | |
>>> r | |
3/4 | |
>>> r.p | |
3 | |
>>> r.q | |
4 | |
Note that p and q return integers (not SymPy Integers) so some care | |
is needed when using them in expressions: | |
>>> r.p/r.q | |
0.75 | |
If an unevaluated Rational is desired, ``gcd=1`` can be passed and | |
this will keep common divisors of the numerator and denominator | |
from being eliminated. It is not possible, however, to leave a | |
negative value in the denominator. | |
>>> Rational(2, 4, gcd=1) | |
2/4 | |
>>> Rational(2, -4, gcd=1).q | |
4 | |
See Also | |
======== | |
sympy.core.sympify.sympify, sympy.simplify.simplify.nsimplify | |
""" | |
is_real = True | |
is_integer = False | |
is_rational = True | |
is_number = True | |
__slots__ = ('p', 'q') | |
p: int | |
q: int | |
is_Rational = True | |
def __new__(cls, p, q=None, gcd=None): | |
if q is None: | |
if isinstance(p, Rational): | |
return p | |
if isinstance(p, SYMPY_INTS): | |
pass | |
else: | |
if isinstance(p, (float, Float)): | |
return Rational(*_as_integer_ratio(p)) | |
if not isinstance(p, str): | |
try: | |
p = sympify(p) | |
except (SympifyError, SyntaxError): | |
pass # error will raise below | |
else: | |
if p.count('/') > 1: | |
raise TypeError('invalid input: %s' % p) | |
p = p.replace(' ', '') | |
pq = p.rsplit('/', 1) | |
if len(pq) == 2: | |
p, q = pq | |
fp = fractions.Fraction(p) | |
fq = fractions.Fraction(q) | |
p = fp/fq | |
try: | |
p = fractions.Fraction(p) | |
except ValueError: | |
pass # error will raise below | |
else: | |
return Rational(p.numerator, p.denominator, 1) | |
if not isinstance(p, Rational): | |
raise TypeError('invalid input: %s' % p) | |
q = 1 | |
gcd = 1 | |
Q = 1 | |
if not isinstance(p, SYMPY_INTS): | |
p = Rational(p) | |
Q *= p.q | |
p = p.p | |
else: | |
p = int(p) | |
if not isinstance(q, SYMPY_INTS): | |
q = Rational(q) | |
p *= q.q | |
Q *= q.p | |
else: | |
Q *= int(q) | |
q = Q | |
# p and q are now ints | |
if q == 0: | |
if p == 0: | |
if _errdict["divide"]: | |
raise ValueError("Indeterminate 0/0") | |
else: | |
return S.NaN | |
return S.ComplexInfinity | |
if q < 0: | |
q = -q | |
p = -p | |
if not gcd: | |
gcd = igcd(abs(p), q) | |
if gcd > 1: | |
p //= gcd | |
q //= gcd | |
if q == 1: | |
return Integer(p) | |
if p == 1 and q == 2: | |
return S.Half | |
obj = Expr.__new__(cls) | |
obj.p = p | |
obj.q = q | |
return obj | |
def limit_denominator(self, max_denominator=1000000): | |
"""Closest Rational to self with denominator at most max_denominator. | |
Examples | |
======== | |
>>> from sympy import Rational | |
>>> Rational('3.141592653589793').limit_denominator(10) | |
22/7 | |
>>> Rational('3.141592653589793').limit_denominator(100) | |
311/99 | |
""" | |
f = fractions.Fraction(self.p, self.q) | |
return Rational(f.limit_denominator(fractions.Fraction(int(max_denominator)))) | |
def __getnewargs__(self): | |
return (self.p, self.q) | |
def _hashable_content(self): | |
return (self.p, self.q) | |
def _eval_is_positive(self): | |
return self.p > 0 | |
def _eval_is_zero(self): | |
return self.p == 0 | |
def __neg__(self): | |
return Rational(-self.p, self.q) | |
def __add__(self, other): | |
if global_parameters.evaluate: | |
if isinstance(other, Integer): | |
return Rational(self.p + self.q*other.p, self.q, 1) | |
elif isinstance(other, Rational): | |
#TODO: this can probably be optimized more | |
return Rational(self.p*other.q + self.q*other.p, self.q*other.q) | |
elif isinstance(other, Float): | |
return other + self | |
else: | |
return Number.__add__(self, other) | |
return Number.__add__(self, other) | |
__radd__ = __add__ | |
def __sub__(self, other): | |
if global_parameters.evaluate: | |
if isinstance(other, Integer): | |
return Rational(self.p - self.q*other.p, self.q, 1) | |
elif isinstance(other, Rational): | |
return Rational(self.p*other.q - self.q*other.p, self.q*other.q) | |
elif isinstance(other, Float): | |
return -other + self | |
else: | |
return Number.__sub__(self, other) | |
return Number.__sub__(self, other) | |
def __rsub__(self, other): | |
if global_parameters.evaluate: | |
if isinstance(other, Integer): | |
return Rational(self.q*other.p - self.p, self.q, 1) | |
elif isinstance(other, Rational): | |
return Rational(self.q*other.p - self.p*other.q, self.q*other.q) | |
elif isinstance(other, Float): | |
return -self + other | |
else: | |
return Number.__rsub__(self, other) | |
return Number.__rsub__(self, other) | |
def __mul__(self, other): | |
if global_parameters.evaluate: | |
if isinstance(other, Integer): | |
return Rational(self.p*other.p, self.q, igcd(other.p, self.q)) | |
elif isinstance(other, Rational): | |
return Rational(self.p*other.p, self.q*other.q, igcd(self.p, other.q)*igcd(self.q, other.p)) | |
elif isinstance(other, Float): | |
return other*self | |
else: | |
return Number.__mul__(self, other) | |
return Number.__mul__(self, other) | |
__rmul__ = __mul__ | |
def __truediv__(self, other): | |
if global_parameters.evaluate: | |
if isinstance(other, Integer): | |
if self.p and other.p == S.Zero: | |
return S.ComplexInfinity | |
else: | |
return Rational(self.p, self.q*other.p, igcd(self.p, other.p)) | |
elif isinstance(other, Rational): | |
return Rational(self.p*other.q, self.q*other.p, igcd(self.p, other.p)*igcd(self.q, other.q)) | |
elif isinstance(other, Float): | |
return self*(1/other) | |
else: | |
return Number.__truediv__(self, other) | |
return Number.__truediv__(self, other) | |
def __rtruediv__(self, other): | |
if global_parameters.evaluate: | |
if isinstance(other, Integer): | |
return Rational(other.p*self.q, self.p, igcd(self.p, other.p)) | |
elif isinstance(other, Rational): | |
return Rational(other.p*self.q, other.q*self.p, igcd(self.p, other.p)*igcd(self.q, other.q)) | |
elif isinstance(other, Float): | |
return other*(1/self) | |
else: | |
return Number.__rtruediv__(self, other) | |
return Number.__rtruediv__(self, other) | |
def __mod__(self, other): | |
if global_parameters.evaluate: | |
if isinstance(other, Rational): | |
n = (self.p*other.q) // (other.p*self.q) | |
return Rational(self.p*other.q - n*other.p*self.q, self.q*other.q) | |
if isinstance(other, Float): | |
# calculate mod with Rationals, *then* round the answer | |
return Float(self.__mod__(Rational(other)), | |
precision=other._prec) | |
return Number.__mod__(self, other) | |
return Number.__mod__(self, other) | |
def __rmod__(self, other): | |
if isinstance(other, Rational): | |
return Rational.__mod__(other, self) | |
return Number.__rmod__(self, other) | |
def _eval_power(self, expt): | |
if isinstance(expt, Number): | |
if isinstance(expt, Float): | |
return self._eval_evalf(expt._prec)**expt | |
if expt.is_extended_negative: | |
# (3/4)**-2 -> (4/3)**2 | |
ne = -expt | |
if (ne is S.One): | |
return Rational(self.q, self.p) | |
if self.is_negative: | |
return S.NegativeOne**expt*Rational(self.q, -self.p)**ne | |
else: | |
return Rational(self.q, self.p)**ne | |
if expt is S.Infinity: # -oo already caught by test for negative | |
if self.p > self.q: | |
# (3/2)**oo -> oo | |
return S.Infinity | |
if self.p < -self.q: | |
# (-3/2)**oo -> oo + I*oo | |
return S.Infinity + S.Infinity*S.ImaginaryUnit | |
return S.Zero | |
if isinstance(expt, Integer): | |
# (4/3)**2 -> 4**2 / 3**2 | |
return Rational(self.p**expt.p, self.q**expt.p, 1) | |
if isinstance(expt, Rational): | |
intpart = expt.p // expt.q | |
if intpart: | |
intpart += 1 | |
remfracpart = intpart*expt.q - expt.p | |
ratfracpart = Rational(remfracpart, expt.q) | |
if self.p != 1: | |
return Integer(self.p)**expt*Integer(self.q)**ratfracpart*Rational(1, self.q**intpart, 1) | |
return Integer(self.q)**ratfracpart*Rational(1, self.q**intpart, 1) | |
else: | |
remfracpart = expt.q - expt.p | |
ratfracpart = Rational(remfracpart, expt.q) | |
if self.p != 1: | |
return Integer(self.p)**expt*Integer(self.q)**ratfracpart*Rational(1, self.q, 1) | |
return Integer(self.q)**ratfracpart*Rational(1, self.q, 1) | |
if self.is_extended_negative and expt.is_even: | |
return (-self)**expt | |
return | |
def _as_mpf_val(self, prec): | |
return mlib.from_rational(self.p, self.q, prec, rnd) | |
def _mpmath_(self, prec, rnd): | |
return mpmath.make_mpf(mlib.from_rational(self.p, self.q, prec, rnd)) | |
def __abs__(self): | |
return Rational(abs(self.p), self.q) | |
def __int__(self): | |
p, q = self.p, self.q | |
if p < 0: | |
return -int(-p//q) | |
return int(p//q) | |
def floor(self): | |
return Integer(self.p // self.q) | |
def ceiling(self): | |
return -Integer(-self.p // self.q) | |
def __floor__(self): | |
return self.floor() | |
def __ceil__(self): | |
return self.ceiling() | |
def __eq__(self, other): | |
try: | |
other = _sympify(other) | |
except SympifyError: | |
return NotImplemented | |
if not isinstance(other, Number): | |
# S(0) == S.false is False | |
# S(0) == False is True | |
return False | |
if not self: | |
return not other | |
if other.is_NumberSymbol: | |
if other.is_irrational: | |
return False | |
return other.__eq__(self) | |
if other.is_Rational: | |
# a Rational is always in reduced form so will never be 2/4 | |
# so we can just check equivalence of args | |
return self.p == other.p and self.q == other.q | |
return False | |
def __ne__(self, other): | |
return not self == other | |
def _Rrel(self, other, attr): | |
# if you want self < other, pass self, other, __gt__ | |
try: | |
other = _sympify(other) | |
except SympifyError: | |
return NotImplemented | |
if other.is_Number: | |
op = None | |
s, o = self, other | |
if other.is_NumberSymbol: | |
op = getattr(o, attr) | |
elif other.is_Float: | |
op = getattr(o, attr) | |
elif other.is_Rational: | |
s, o = Integer(s.p*o.q), Integer(s.q*o.p) | |
op = getattr(o, attr) | |
if op: | |
return op(s) | |
if o.is_number and o.is_extended_real: | |
return Integer(s.p), s.q*o | |
def __gt__(self, other): | |
rv = self._Rrel(other, '__lt__') | |
if rv is None: | |
rv = self, other | |
elif not isinstance(rv, tuple): | |
return rv | |
return Expr.__gt__(*rv) | |
def __ge__(self, other): | |
rv = self._Rrel(other, '__le__') | |
if rv is None: | |
rv = self, other | |
elif not isinstance(rv, tuple): | |
return rv | |
return Expr.__ge__(*rv) | |
def __lt__(self, other): | |
rv = self._Rrel(other, '__gt__') | |
if rv is None: | |
rv = self, other | |
elif not isinstance(rv, tuple): | |
return rv | |
return Expr.__lt__(*rv) | |
def __le__(self, other): | |
rv = self._Rrel(other, '__ge__') | |
if rv is None: | |
rv = self, other | |
elif not isinstance(rv, tuple): | |
return rv | |
return Expr.__le__(*rv) | |
def __hash__(self): | |
return super().__hash__() | |
def factors(self, limit=None, use_trial=True, use_rho=False, | |
use_pm1=False, verbose=False, visual=False): | |
"""A wrapper to factorint which return factors of self that are | |
smaller than limit (or cheap to compute). Special methods of | |
factoring are disabled by default so that only trial division is used. | |
""" | |
from sympy.ntheory.factor_ import factorrat | |
return factorrat(self, limit=limit, use_trial=use_trial, | |
use_rho=use_rho, use_pm1=use_pm1, | |
verbose=verbose).copy() | |
def numerator(self): | |
return self.p | |
def denominator(self): | |
return self.q | |
def gcd(self, other): | |
if isinstance(other, Rational): | |
if other == S.Zero: | |
return other | |
return Rational( | |
igcd(self.p, other.p), | |
ilcm(self.q, other.q)) | |
return Number.gcd(self, other) | |
def lcm(self, other): | |
if isinstance(other, Rational): | |
return Rational( | |
self.p // igcd(self.p, other.p) * other.p, | |
igcd(self.q, other.q)) | |
return Number.lcm(self, other) | |
def as_numer_denom(self): | |
return Integer(self.p), Integer(self.q) | |
def as_content_primitive(self, radical=False, clear=True): | |
"""Return the tuple (R, self/R) where R is the positive Rational | |
extracted from self. | |
Examples | |
======== | |
>>> from sympy import S | |
>>> (S(-3)/2).as_content_primitive() | |
(3/2, -1) | |
See docstring of Expr.as_content_primitive for more examples. | |
""" | |
if self: | |
if self.is_positive: | |
return self, S.One | |
return -self, S.NegativeOne | |
return S.One, self | |
def as_coeff_Mul(self, rational=False): | |
"""Efficiently extract the coefficient of a product.""" | |
return self, S.One | |
def as_coeff_Add(self, rational=False): | |
"""Efficiently extract the coefficient of a summation.""" | |
return self, S.Zero | |
class Integer(Rational): | |
"""Represents integer numbers of any size. | |
Examples | |
======== | |
>>> from sympy import Integer | |
>>> Integer(3) | |
3 | |
If a float or a rational is passed to Integer, the fractional part | |
will be discarded; the effect is of rounding toward zero. | |
>>> Integer(3.8) | |
3 | |
>>> Integer(-3.8) | |
-3 | |
A string is acceptable input if it can be parsed as an integer: | |
>>> Integer("9" * 20) | |
99999999999999999999 | |
It is rarely needed to explicitly instantiate an Integer, because | |
Python integers are automatically converted to Integer when they | |
are used in SymPy expressions. | |
""" | |
q = 1 | |
is_integer = True | |
is_number = True | |
is_Integer = True | |
__slots__ = () | |
def _as_mpf_val(self, prec): | |
return mlib.from_int(self.p, prec, rnd) | |
def _mpmath_(self, prec, rnd): | |
return mpmath.make_mpf(self._as_mpf_val(prec)) | |
def __new__(cls, i): | |
if isinstance(i, str): | |
i = i.replace(' ', '') | |
# whereas we cannot, in general, make a Rational from an | |
# arbitrary expression, we can make an Integer unambiguously | |
# (except when a non-integer expression happens to round to | |
# an integer). So we proceed by taking int() of the input and | |
# let the int routines determine whether the expression can | |
# be made into an int or whether an error should be raised. | |
try: | |
ival = int(i) | |
except TypeError: | |
raise TypeError( | |
"Argument of Integer should be of numeric type, got %s." % i) | |
# We only work with well-behaved integer types. This converts, for | |
# example, numpy.int32 instances. | |
if ival == 1: | |
return S.One | |
if ival == -1: | |
return S.NegativeOne | |
if ival == 0: | |
return S.Zero | |
obj = Expr.__new__(cls) | |
obj.p = ival | |
return obj | |
def __getnewargs__(self): | |
return (self.p,) | |
# Arithmetic operations are here for efficiency | |
def __int__(self): | |
return self.p | |
def floor(self): | |
return Integer(self.p) | |
def ceiling(self): | |
return Integer(self.p) | |
def __floor__(self): | |
return self.floor() | |
def __ceil__(self): | |
return self.ceiling() | |
def __neg__(self): | |
return Integer(-self.p) | |
def __abs__(self): | |
if self.p >= 0: | |
return self | |
else: | |
return Integer(-self.p) | |
def __divmod__(self, other): | |
if isinstance(other, Integer) and global_parameters.evaluate: | |
return Tuple(*(divmod(self.p, other.p))) | |
else: | |
return Number.__divmod__(self, other) | |
def __rdivmod__(self, other): | |
if isinstance(other, int) and global_parameters.evaluate: | |
return Tuple(*(divmod(other, self.p))) | |
else: | |
try: | |
other = Number(other) | |
except TypeError: | |
msg = "unsupported operand type(s) for divmod(): '%s' and '%s'" | |
oname = type(other).__name__ | |
sname = type(self).__name__ | |
raise TypeError(msg % (oname, sname)) | |
return Number.__divmod__(other, self) | |
# TODO make it decorator + bytecodehacks? | |
def __add__(self, other): | |
if global_parameters.evaluate: | |
if isinstance(other, int): | |
return Integer(self.p + other) | |
elif isinstance(other, Integer): | |
return Integer(self.p + other.p) | |
elif isinstance(other, Rational): | |
return Rational(self.p*other.q + other.p, other.q, 1) | |
return Rational.__add__(self, other) | |
else: | |
return Add(self, other) | |
def __radd__(self, other): | |
if global_parameters.evaluate: | |
if isinstance(other, int): | |
return Integer(other + self.p) | |
elif isinstance(other, Rational): | |
return Rational(other.p + self.p*other.q, other.q, 1) | |
return Rational.__radd__(self, other) | |
return Rational.__radd__(self, other) | |
def __sub__(self, other): | |
if global_parameters.evaluate: | |
if isinstance(other, int): | |
return Integer(self.p - other) | |
elif isinstance(other, Integer): | |
return Integer(self.p - other.p) | |
elif isinstance(other, Rational): | |
return Rational(self.p*other.q - other.p, other.q, 1) | |
return Rational.__sub__(self, other) | |
return Rational.__sub__(self, other) | |
def __rsub__(self, other): | |
if global_parameters.evaluate: | |
if isinstance(other, int): | |
return Integer(other - self.p) | |
elif isinstance(other, Rational): | |
return Rational(other.p - self.p*other.q, other.q, 1) | |
return Rational.__rsub__(self, other) | |
return Rational.__rsub__(self, other) | |
def __mul__(self, other): | |
if global_parameters.evaluate: | |
if isinstance(other, int): | |
return Integer(self.p*other) | |
elif isinstance(other, Integer): | |
return Integer(self.p*other.p) | |
elif isinstance(other, Rational): | |
return Rational(self.p*other.p, other.q, igcd(self.p, other.q)) | |
return Rational.__mul__(self, other) | |
return Rational.__mul__(self, other) | |
def __rmul__(self, other): | |
if global_parameters.evaluate: | |
if isinstance(other, int): | |
return Integer(other*self.p) | |
elif isinstance(other, Rational): | |
return Rational(other.p*self.p, other.q, igcd(self.p, other.q)) | |
return Rational.__rmul__(self, other) | |
return Rational.__rmul__(self, other) | |
def __mod__(self, other): | |
if global_parameters.evaluate: | |
if isinstance(other, int): | |
return Integer(self.p % other) | |
elif isinstance(other, Integer): | |
return Integer(self.p % other.p) | |
return Rational.__mod__(self, other) | |
return Rational.__mod__(self, other) | |
def __rmod__(self, other): | |
if global_parameters.evaluate: | |
if isinstance(other, int): | |
return Integer(other % self.p) | |
elif isinstance(other, Integer): | |
return Integer(other.p % self.p) | |
return Rational.__rmod__(self, other) | |
return Rational.__rmod__(self, other) | |
def __eq__(self, other): | |
if isinstance(other, int): | |
return (self.p == other) | |
elif isinstance(other, Integer): | |
return (self.p == other.p) | |
return Rational.__eq__(self, other) | |
def __ne__(self, other): | |
return not self == other | |
def __gt__(self, other): | |
try: | |
other = _sympify(other) | |
except SympifyError: | |
return NotImplemented | |
if other.is_Integer: | |
return _sympify(self.p > other.p) | |
return Rational.__gt__(self, other) | |
def __lt__(self, other): | |
try: | |
other = _sympify(other) | |
except SympifyError: | |
return NotImplemented | |
if other.is_Integer: | |
return _sympify(self.p < other.p) | |
return Rational.__lt__(self, other) | |
def __ge__(self, other): | |
try: | |
other = _sympify(other) | |
except SympifyError: | |
return NotImplemented | |
if other.is_Integer: | |
return _sympify(self.p >= other.p) | |
return Rational.__ge__(self, other) | |
def __le__(self, other): | |
try: | |
other = _sympify(other) | |
except SympifyError: | |
return NotImplemented | |
if other.is_Integer: | |
return _sympify(self.p <= other.p) | |
return Rational.__le__(self, other) | |
def __hash__(self): | |
return hash(self.p) | |
def __index__(self): | |
return self.p | |
######################################## | |
def _eval_is_odd(self): | |
return bool(self.p % 2) | |
def _eval_power(self, expt): | |
""" | |
Tries to do some simplifications on self**expt | |
Returns None if no further simplifications can be done. | |
Explanation | |
=========== | |
When exponent is a fraction (so we have for example a square root), | |
we try to find a simpler representation by factoring the argument | |
up to factors of 2**15, e.g. | |
- sqrt(4) becomes 2 | |
- sqrt(-4) becomes 2*I | |
- (2**(3+7)*3**(6+7))**Rational(1,7) becomes 6*18**(3/7) | |
Further simplification would require a special call to factorint on | |
the argument which is not done here for sake of speed. | |
""" | |
from sympy.ntheory.factor_ import perfect_power | |
if expt is S.Infinity: | |
if self.p > S.One: | |
return S.Infinity | |
# cases -1, 0, 1 are done in their respective classes | |
return S.Infinity + S.ImaginaryUnit*S.Infinity | |
if expt is S.NegativeInfinity: | |
return Rational(1, self, 1)**S.Infinity | |
if not isinstance(expt, Number): | |
# simplify when expt is even | |
# (-2)**k --> 2**k | |
if self.is_negative and expt.is_even: | |
return (-self)**expt | |
if isinstance(expt, Float): | |
# Rational knows how to exponentiate by a Float | |
return super()._eval_power(expt) | |
if not isinstance(expt, Rational): | |
return | |
if expt is S.Half and self.is_negative: | |
# we extract I for this special case since everyone is doing so | |
return S.ImaginaryUnit*Pow(-self, expt) | |
if expt.is_negative: | |
# invert base and change sign on exponent | |
ne = -expt | |
if self.is_negative: | |
return S.NegativeOne**expt*Rational(1, -self, 1)**ne | |
else: | |
return Rational(1, self.p, 1)**ne | |
# see if base is a perfect root, sqrt(4) --> 2 | |
x, xexact = integer_nthroot(abs(self.p), expt.q) | |
if xexact: | |
# if it's a perfect root we've finished | |
result = Integer(x**abs(expt.p)) | |
if self.is_negative: | |
result *= S.NegativeOne**expt | |
return result | |
# The following is an algorithm where we collect perfect roots | |
# from the factors of base. | |
# if it's not an nth root, it still might be a perfect power | |
b_pos = int(abs(self.p)) | |
p = perfect_power(b_pos) | |
if p is not False: | |
dict = {p[0]: p[1]} | |
else: | |
dict = Integer(b_pos).factors(limit=2**15) | |
# now process the dict of factors | |
out_int = 1 # integer part | |
out_rad = 1 # extracted radicals | |
sqr_int = 1 | |
sqr_gcd = 0 | |
sqr_dict = {} | |
for prime, exponent in dict.items(): | |
exponent *= expt.p | |
# remove multiples of expt.q: (2**12)**(1/10) -> 2*(2**2)**(1/10) | |
div_e, div_m = divmod(exponent, expt.q) | |
if div_e > 0: | |
out_int *= prime**div_e | |
if div_m > 0: | |
# see if the reduced exponent shares a gcd with e.q | |
# (2**2)**(1/10) -> 2**(1/5) | |
g = igcd(div_m, expt.q) | |
if g != 1: | |
out_rad *= Pow(prime, Rational(div_m//g, expt.q//g, 1)) | |
else: | |
sqr_dict[prime] = div_m | |
# identify gcd of remaining powers | |
for p, ex in sqr_dict.items(): | |
if sqr_gcd == 0: | |
sqr_gcd = ex | |
else: | |
sqr_gcd = igcd(sqr_gcd, ex) | |
if sqr_gcd == 1: | |
break | |
for k, v in sqr_dict.items(): | |
sqr_int *= k**(v//sqr_gcd) | |
if sqr_int == b_pos and out_int == 1 and out_rad == 1: | |
result = None | |
else: | |
result = out_int*out_rad*Pow(sqr_int, Rational(sqr_gcd, expt.q)) | |
if self.is_negative: | |
result *= Pow(S.NegativeOne, expt) | |
return result | |
def _eval_is_prime(self): | |
from sympy.ntheory.primetest import isprime | |
return isprime(self) | |
def _eval_is_composite(self): | |
if self > 1: | |
return fuzzy_not(self.is_prime) | |
else: | |
return False | |
def as_numer_denom(self): | |
return self, S.One | |
def __floordiv__(self, other): | |
if not isinstance(other, Expr): | |
return NotImplemented | |
if isinstance(other, Integer): | |
return Integer(self.p // other) | |
return divmod(self, other)[0] | |
def __rfloordiv__(self, other): | |
return Integer(Integer(other).p // self.p) | |
# These bitwise operations (__lshift__, __rlshift__, ..., __invert__) are defined | |
# for Integer only and not for general SymPy expressions. This is to achieve | |
# compatibility with the numbers.Integral ABC which only defines these operations | |
# among instances of numbers.Integral. Therefore, these methods check explicitly for | |
# integer types rather than using sympify because they should not accept arbitrary | |
# symbolic expressions and there is no symbolic analogue of numbers.Integral's | |
# bitwise operations. | |
def __lshift__(self, other): | |
if isinstance(other, (int, Integer, numbers.Integral)): | |
return Integer(self.p << int(other)) | |
else: | |
return NotImplemented | |
def __rlshift__(self, other): | |
if isinstance(other, (int, numbers.Integral)): | |
return Integer(int(other) << self.p) | |
else: | |
return NotImplemented | |
def __rshift__(self, other): | |
if isinstance(other, (int, Integer, numbers.Integral)): | |
return Integer(self.p >> int(other)) | |
else: | |
return NotImplemented | |
def __rrshift__(self, other): | |
if isinstance(other, (int, numbers.Integral)): | |
return Integer(int(other) >> self.p) | |
else: | |
return NotImplemented | |
def __and__(self, other): | |
if isinstance(other, (int, Integer, numbers.Integral)): | |
return Integer(self.p & int(other)) | |
else: | |
return NotImplemented | |
def __rand__(self, other): | |
if isinstance(other, (int, numbers.Integral)): | |
return Integer(int(other) & self.p) | |
else: | |
return NotImplemented | |
def __xor__(self, other): | |
if isinstance(other, (int, Integer, numbers.Integral)): | |
return Integer(self.p ^ int(other)) | |
else: | |
return NotImplemented | |
def __rxor__(self, other): | |
if isinstance(other, (int, numbers.Integral)): | |
return Integer(int(other) ^ self.p) | |
else: | |
return NotImplemented | |
def __or__(self, other): | |
if isinstance(other, (int, Integer, numbers.Integral)): | |
return Integer(self.p | int(other)) | |
else: | |
return NotImplemented | |
def __ror__(self, other): | |
if isinstance(other, (int, numbers.Integral)): | |
return Integer(int(other) | self.p) | |
else: | |
return NotImplemented | |
def __invert__(self): | |
return Integer(~self.p) | |
# Add sympify converters | |
_sympy_converter[int] = Integer | |
class AlgebraicNumber(Expr): | |
r""" | |
Class for representing algebraic numbers in SymPy. | |
Symbolically, an instance of this class represents an element | |
$\alpha \in \mathbb{Q}(\theta) \hookrightarrow \mathbb{C}$. That is, the | |
algebraic number $\alpha$ is represented as an element of a particular | |
number field $\mathbb{Q}(\theta)$, with a particular embedding of this | |
field into the complex numbers. | |
Formally, the primitive element $\theta$ is given by two data points: (1) | |
its minimal polynomial (which defines $\mathbb{Q}(\theta)$), and (2) a | |
particular complex number that is a root of this polynomial (which defines | |
the embedding $\mathbb{Q}(\theta) \hookrightarrow \mathbb{C}$). Finally, | |
the algebraic number $\alpha$ which we represent is then given by the | |
coefficients of a polynomial in $\theta$. | |
""" | |
__slots__ = ('rep', 'root', 'alias', 'minpoly', '_own_minpoly') | |
is_AlgebraicNumber = True | |
is_algebraic = True | |
is_number = True | |
kind = NumberKind | |
# Optional alias symbol is not free. | |
# Actually, alias should be a Str, but some methods | |
# expect that it be an instance of Expr. | |
free_symbols: set[Basic] = set() | |
def __new__(cls, expr, coeffs=None, alias=None, **args): | |
r""" | |
Construct a new algebraic number $\alpha$ belonging to a number field | |
$k = \mathbb{Q}(\theta)$. | |
There are four instance attributes to be determined: | |
=========== ============================================================================ | |
Attribute Type/Meaning | |
=========== ============================================================================ | |
``root`` :py:class:`~.Expr` for $\theta$ as a complex number | |
``minpoly`` :py:class:`~.Poly`, the minimal polynomial of $\theta$ | |
``rep`` :py:class:`~sympy.polys.polyclasses.DMP` giving $\alpha$ as poly in $\theta$ | |
``alias`` :py:class:`~.Symbol` for $\theta$, or ``None`` | |
=========== ============================================================================ | |
See Parameters section for how they are determined. | |
Parameters | |
========== | |
expr : :py:class:`~.Expr`, or pair $(m, r)$ | |
There are three distinct modes of construction, depending on what | |
is passed as *expr*. | |
**(1)** *expr* is an :py:class:`~.AlgebraicNumber`: | |
In this case we begin by copying all four instance attributes from | |
*expr*. If *coeffs* were also given, we compose the two coeff | |
polynomials (see below). If an *alias* was given, it overrides. | |
**(2)** *expr* is any other type of :py:class:`~.Expr`: | |
Then ``root`` will equal *expr*. Therefore it | |
must express an algebraic quantity, and we will compute its | |
``minpoly``. | |
**(3)** *expr* is an ordered pair $(m, r)$ giving the | |
``minpoly`` $m$, and a ``root`` $r$ thereof, which together | |
define $\theta$. In this case $m$ may be either a univariate | |
:py:class:`~.Poly` or any :py:class:`~.Expr` which represents the | |
same, while $r$ must be some :py:class:`~.Expr` representing a | |
complex number that is a root of $m$, including both explicit | |
expressions in radicals, and instances of | |
:py:class:`~.ComplexRootOf` or :py:class:`~.AlgebraicNumber`. | |
coeffs : list, :py:class:`~.ANP`, None, optional (default=None) | |
This defines ``rep``, giving the algebraic number $\alpha$ as a | |
polynomial in $\theta$. | |
If a list, the elements should be integers or rational numbers. | |
If an :py:class:`~.ANP`, we take its coefficients (using its | |
:py:meth:`~.ANP.to_list()` method). If ``None``, then the list of | |
coefficients defaults to ``[1, 0]``, meaning that $\alpha = \theta$ | |
is the primitive element of the field. | |
If *expr* was an :py:class:`~.AlgebraicNumber`, let $g(x)$ be its | |
``rep`` polynomial, and let $f(x)$ be the polynomial defined by | |
*coeffs*. Then ``self.rep`` will represent the composition | |
$(f \circ g)(x)$. | |
alias : str, :py:class:`~.Symbol`, None, optional (default=None) | |
This is a way to provide a name for the primitive element. We | |
described several ways in which the *expr* argument can define the | |
value of the primitive element, but none of these methods gave it | |
a name. Here, for example, *alias* could be set as | |
``Symbol('theta')``, in order to make this symbol appear when | |
$\alpha$ is printed, or rendered as a polynomial, using the | |
:py:meth:`~.as_poly()` method. | |
Examples | |
======== | |
Recall that we are constructing an algebraic number as a field element | |
$\alpha \in \mathbb{Q}(\theta)$. | |
>>> from sympy import AlgebraicNumber, sqrt, CRootOf, S | |
>>> from sympy.abc import x | |
Example (1): $\alpha = \theta = \sqrt{2}$ | |
>>> a1 = AlgebraicNumber(sqrt(2)) | |
>>> a1.minpoly_of_element().as_expr(x) | |
x**2 - 2 | |
>>> a1.evalf(10) | |
1.414213562 | |
Example (2): $\alpha = 3 \sqrt{2} - 5$, $\theta = \sqrt{2}$. We can | |
either build on the last example: | |
>>> a2 = AlgebraicNumber(a1, [3, -5]) | |
>>> a2.as_expr() | |
-5 + 3*sqrt(2) | |
or start from scratch: | |
>>> a2 = AlgebraicNumber(sqrt(2), [3, -5]) | |
>>> a2.as_expr() | |
-5 + 3*sqrt(2) | |
Example (3): $\alpha = 6 \sqrt{2} - 11$, $\theta = \sqrt{2}$. Again we | |
can build on the previous example, and we see that the coeff polys are | |
composed: | |
>>> a3 = AlgebraicNumber(a2, [2, -1]) | |
>>> a3.as_expr() | |
-11 + 6*sqrt(2) | |
reflecting the fact that $(2x - 1) \circ (3x - 5) = 6x - 11$. | |
Example (4): $\alpha = \sqrt{2}$, $\theta = \sqrt{2} + \sqrt{3}$. The | |
easiest way is to use the :py:func:`~.to_number_field()` function: | |
>>> from sympy import to_number_field | |
>>> a4 = to_number_field(sqrt(2), sqrt(2) + sqrt(3)) | |
>>> a4.minpoly_of_element().as_expr(x) | |
x**2 - 2 | |
>>> a4.to_root() | |
sqrt(2) | |
>>> a4.primitive_element() | |
sqrt(2) + sqrt(3) | |
>>> a4.coeffs() | |
[1/2, 0, -9/2, 0] | |
but if you already knew the right coefficients, you could construct it | |
directly: | |
>>> a4 = AlgebraicNumber(sqrt(2) + sqrt(3), [S(1)/2, 0, S(-9)/2, 0]) | |
>>> a4.to_root() | |
sqrt(2) | |
>>> a4.primitive_element() | |
sqrt(2) + sqrt(3) | |
Example (5): Construct the Golden Ratio as an element of the 5th | |
cyclotomic field, supposing we already know its coefficients. This time | |
we introduce the alias $\zeta$ for the primitive element of the field: | |
>>> from sympy import cyclotomic_poly | |
>>> from sympy.abc import zeta | |
>>> a5 = AlgebraicNumber(CRootOf(cyclotomic_poly(5), -1), | |
... [-1, -1, 0, 0], alias=zeta) | |
>>> a5.as_poly().as_expr() | |
-zeta**3 - zeta**2 | |
>>> a5.evalf() | |
1.61803398874989 | |
(The index ``-1`` to ``CRootOf`` selects the complex root with the | |
largest real and imaginary parts, which in this case is | |
$\mathrm{e}^{2i\pi/5}$. See :py:class:`~.ComplexRootOf`.) | |
Example (6): Building on the last example, construct the number | |
$2 \phi \in \mathbb{Q}(\phi)$, where $\phi$ is the Golden Ratio: | |
>>> from sympy.abc import phi | |
>>> a6 = AlgebraicNumber(a5.to_root(), coeffs=[2, 0], alias=phi) | |
>>> a6.as_poly().as_expr() | |
2*phi | |
>>> a6.primitive_element().evalf() | |
1.61803398874989 | |
Note that we needed to use ``a5.to_root()``, since passing ``a5`` as | |
the first argument would have constructed the number $2 \phi$ as an | |
element of the field $\mathbb{Q}(\zeta)$: | |
>>> a6_wrong = AlgebraicNumber(a5, coeffs=[2, 0]) | |
>>> a6_wrong.as_poly().as_expr() | |
-2*zeta**3 - 2*zeta**2 | |
>>> a6_wrong.primitive_element().evalf() | |
0.309016994374947 + 0.951056516295154*I | |
""" | |
from sympy.polys.polyclasses import ANP, DMP | |
from sympy.polys.numberfields import minimal_polynomial | |
expr = sympify(expr) | |
rep0 = None | |
alias0 = None | |
if isinstance(expr, (tuple, Tuple)): | |
minpoly, root = expr | |
if not minpoly.is_Poly: | |
from sympy.polys.polytools import Poly | |
minpoly = Poly(minpoly) | |
elif expr.is_AlgebraicNumber: | |
minpoly, root, rep0, alias0 = (expr.minpoly, expr.root, | |
expr.rep, expr.alias) | |
else: | |
minpoly, root = minimal_polynomial( | |
expr, args.get('gen'), polys=True), expr | |
dom = minpoly.get_domain() | |
if coeffs is not None: | |
if not isinstance(coeffs, ANP): | |
rep = DMP.from_sympy_list(sympify(coeffs), 0, dom) | |
scoeffs = Tuple(*coeffs) | |
else: | |
rep = DMP.from_list(coeffs.to_list(), 0, dom) | |
scoeffs = Tuple(*coeffs.to_list()) | |
else: | |
rep = DMP.from_list([1, 0], 0, dom) | |
scoeffs = Tuple(1, 0) | |
if rep0 is not None: | |
from sympy.polys.densetools import dup_compose | |
c = dup_compose(rep.to_list(), rep0.to_list(), dom) | |
rep = DMP.from_list(c, 0, dom) | |
scoeffs = Tuple(*c) | |
if rep.degree() >= minpoly.degree(): | |
rep = rep.rem(minpoly.rep) | |
sargs = (root, scoeffs) | |
alias = alias or alias0 | |
if alias is not None: | |
from .symbol import Symbol | |
if not isinstance(alias, Symbol): | |
alias = Symbol(alias) | |
sargs = sargs + (alias,) | |
obj = Expr.__new__(cls, *sargs) | |
obj.rep = rep | |
obj.root = root | |
obj.alias = alias | |
obj.minpoly = minpoly | |
obj._own_minpoly = None | |
return obj | |
def __hash__(self): | |
return super().__hash__() | |
def _eval_evalf(self, prec): | |
return self.as_expr()._evalf(prec) | |
def is_aliased(self): | |
"""Returns ``True`` if ``alias`` was set. """ | |
return self.alias is not None | |
def as_poly(self, x=None): | |
"""Create a Poly instance from ``self``. """ | |
from sympy.polys.polytools import Poly, PurePoly | |
if x is not None: | |
return Poly.new(self.rep, x) | |
else: | |
if self.alias is not None: | |
return Poly.new(self.rep, self.alias) | |
else: | |
from .symbol import Dummy | |
return PurePoly.new(self.rep, Dummy('x')) | |
def as_expr(self, x=None): | |
"""Create a Basic expression from ``self``. """ | |
return self.as_poly(x or self.root).as_expr().expand() | |
def coeffs(self): | |
"""Returns all SymPy coefficients of an algebraic number. """ | |
return [ self.rep.dom.to_sympy(c) for c in self.rep.all_coeffs() ] | |
def native_coeffs(self): | |
"""Returns all native coefficients of an algebraic number. """ | |
return self.rep.all_coeffs() | |
def to_algebraic_integer(self): | |
"""Convert ``self`` to an algebraic integer. """ | |
from sympy.polys.polytools import Poly | |
f = self.minpoly | |
if f.LC() == 1: | |
return self | |
coeff = f.LC()**(f.degree() - 1) | |
poly = f.compose(Poly(f.gen/f.LC())) | |
minpoly = poly*coeff | |
root = f.LC()*self.root | |
return AlgebraicNumber((minpoly, root), self.coeffs()) | |
def _eval_simplify(self, **kwargs): | |
from sympy.polys.rootoftools import CRootOf | |
from sympy.polys import minpoly | |
measure, ratio = kwargs['measure'], kwargs['ratio'] | |
for r in [r for r in self.minpoly.all_roots() if r.func != CRootOf]: | |
if minpoly(self.root - r).is_Symbol: | |
# use the matching root if it's simpler | |
if measure(r) < ratio*measure(self.root): | |
return AlgebraicNumber(r) | |
return self | |
def field_element(self, coeffs): | |
r""" | |
Form another element of the same number field. | |
Explanation | |
=========== | |
If we represent $\alpha \in \mathbb{Q}(\theta)$, form another element | |
$\beta \in \mathbb{Q}(\theta)$ of the same number field. | |
Parameters | |
========== | |
coeffs : list, :py:class:`~.ANP` | |
Like the *coeffs* arg to the class | |
:py:meth:`constructor<.AlgebraicNumber.__new__>`, defines the | |
new element as a polynomial in the primitive element. | |
If a list, the elements should be integers or rational numbers. | |
If an :py:class:`~.ANP`, we take its coefficients (using its | |
:py:meth:`~.ANP.to_list()` method). | |
Examples | |
======== | |
>>> from sympy import AlgebraicNumber, sqrt | |
>>> a = AlgebraicNumber(sqrt(5), [-1, 1]) | |
>>> b = a.field_element([3, 2]) | |
>>> print(a) | |
1 - sqrt(5) | |
>>> print(b) | |
2 + 3*sqrt(5) | |
>>> print(b.primitive_element() == a.primitive_element()) | |
True | |
See Also | |
======== | |
AlgebraicNumber | |
""" | |
return AlgebraicNumber( | |
(self.minpoly, self.root), coeffs=coeffs, alias=self.alias) | |
def is_primitive_element(self): | |
r""" | |
Say whether this algebraic number $\alpha \in \mathbb{Q}(\theta)$ is | |
equal to the primitive element $\theta$ for its field. | |
""" | |
c = self.coeffs() | |
# Second case occurs if self.minpoly is linear: | |
return c == [1, 0] or c == [self.root] | |
def primitive_element(self): | |
r""" | |
Get the primitive element $\theta$ for the number field | |
$\mathbb{Q}(\theta)$ to which this algebraic number $\alpha$ belongs. | |
Returns | |
======= | |
AlgebraicNumber | |
""" | |
if self.is_primitive_element: | |
return self | |
return self.field_element([1, 0]) | |
def to_primitive_element(self, radicals=True): | |
r""" | |
Convert ``self`` to an :py:class:`~.AlgebraicNumber` instance that is | |
equal to its own primitive element. | |
Explanation | |
=========== | |
If we represent $\alpha \in \mathbb{Q}(\theta)$, $\alpha \neq \theta$, | |
construct a new :py:class:`~.AlgebraicNumber` that represents | |
$\alpha \in \mathbb{Q}(\alpha)$. | |
Examples | |
======== | |
>>> from sympy import sqrt, to_number_field | |
>>> from sympy.abc import x | |
>>> a = to_number_field(sqrt(2), sqrt(2) + sqrt(3)) | |
The :py:class:`~.AlgebraicNumber` ``a`` represents the number | |
$\sqrt{2}$ in the field $\mathbb{Q}(\sqrt{2} + \sqrt{3})$. Rendering | |
``a`` as a polynomial, | |
>>> a.as_poly().as_expr(x) | |
x**3/2 - 9*x/2 | |
reflects the fact that $\sqrt{2} = \theta^3/2 - 9 \theta/2$, where | |
$\theta = \sqrt{2} + \sqrt{3}$. | |
``a`` is not equal to its own primitive element. Its minpoly | |
>>> a.minpoly.as_poly().as_expr(x) | |
x**4 - 10*x**2 + 1 | |
is that of $\theta$. | |
Converting to a primitive element, | |
>>> a_prim = a.to_primitive_element() | |
>>> a_prim.minpoly.as_poly().as_expr(x) | |
x**2 - 2 | |
we obtain an :py:class:`~.AlgebraicNumber` whose ``minpoly`` is that of | |
the number itself. | |
Parameters | |
========== | |
radicals : boolean, optional (default=True) | |
If ``True``, then we will try to return an | |
:py:class:`~.AlgebraicNumber` whose ``root`` is an expression | |
in radicals. If that is not possible (or if *radicals* is | |
``False``), ``root`` will be a :py:class:`~.ComplexRootOf`. | |
Returns | |
======= | |
AlgebraicNumber | |
See Also | |
======== | |
is_primitive_element | |
""" | |
if self.is_primitive_element: | |
return self | |
m = self.minpoly_of_element() | |
r = self.to_root(radicals=radicals) | |
return AlgebraicNumber((m, r)) | |
def minpoly_of_element(self): | |
r""" | |
Compute the minimal polynomial for this algebraic number. | |
Explanation | |
=========== | |
Recall that we represent an element $\alpha \in \mathbb{Q}(\theta)$. | |
Our instance attribute ``self.minpoly`` is the minimal polynomial for | |
our primitive element $\theta$. This method computes the minimal | |
polynomial for $\alpha$. | |
""" | |
if self._own_minpoly is None: | |
if self.is_primitive_element: | |
self._own_minpoly = self.minpoly | |
else: | |
from sympy.polys.numberfields.minpoly import minpoly | |
theta = self.primitive_element() | |
self._own_minpoly = minpoly(self.as_expr(theta), polys=True) | |
return self._own_minpoly | |
def to_root(self, radicals=True, minpoly=None): | |
""" | |
Convert to an :py:class:`~.Expr` that is not an | |
:py:class:`~.AlgebraicNumber`, specifically, either a | |
:py:class:`~.ComplexRootOf`, or, optionally and where possible, an | |
expression in radicals. | |
Parameters | |
========== | |
radicals : boolean, optional (default=True) | |
If ``True``, then we will try to return the root as an expression | |
in radicals. If that is not possible, we will return a | |
:py:class:`~.ComplexRootOf`. | |
minpoly : :py:class:`~.Poly` | |
If the minimal polynomial for `self` has been pre-computed, it can | |
be passed in order to save time. | |
""" | |
if self.is_primitive_element and not isinstance(self.root, AlgebraicNumber): | |
return self.root | |
m = minpoly or self.minpoly_of_element() | |
roots = m.all_roots(radicals=radicals) | |
if len(roots) == 1: | |
return roots[0] | |
ex = self.as_expr() | |
for b in roots: | |
if m.same_root(b, ex): | |
return b | |
class RationalConstant(Rational): | |
""" | |
Abstract base class for rationals with specific behaviors | |
Derived classes must define class attributes p and q and should probably all | |
be singletons. | |
""" | |
__slots__ = () | |
def __new__(cls): | |
return AtomicExpr.__new__(cls) | |
class IntegerConstant(Integer): | |
__slots__ = () | |
def __new__(cls): | |
return AtomicExpr.__new__(cls) | |
class Zero(IntegerConstant, metaclass=Singleton): | |
"""The number zero. | |
Zero is a singleton, and can be accessed by ``S.Zero`` | |
Examples | |
======== | |
>>> from sympy import S, Integer | |
>>> Integer(0) is S.Zero | |
True | |
>>> 1/S.Zero | |
zoo | |
References | |
========== | |
.. [1] https://en.wikipedia.org/wiki/Zero | |
""" | |
p = 0 | |
q = 1 | |
is_positive = False | |
is_negative = False | |
is_zero = True | |
is_number = True | |
is_comparable = True | |
__slots__ = () | |
def __getnewargs__(self): | |
return () | |
def __abs__(): | |
return S.Zero | |
def __neg__(): | |
return S.Zero | |
def _eval_power(self, expt): | |
if expt.is_extended_positive: | |
return self | |
if expt.is_extended_negative: | |
return S.ComplexInfinity | |
if expt.is_extended_real is False: | |
return S.NaN | |
if expt.is_zero: | |
return S.One | |
# infinities are already handled with pos and neg | |
# tests above; now throw away leading numbers on Mul | |
# exponent since 0**-x = zoo**x even when x == 0 | |
coeff, terms = expt.as_coeff_Mul() | |
if coeff.is_negative: | |
return S.ComplexInfinity**terms | |
if coeff is not S.One: # there is a Number to discard | |
return self**terms | |
def _eval_order(self, *symbols): | |
# Order(0,x) -> 0 | |
return self | |
def __bool__(self): | |
return False | |
class One(IntegerConstant, metaclass=Singleton): | |
"""The number one. | |
One is a singleton, and can be accessed by ``S.One``. | |
Examples | |
======== | |
>>> from sympy import S, Integer | |
>>> Integer(1) is S.One | |
True | |
References | |
========== | |
.. [1] https://en.wikipedia.org/wiki/1_%28number%29 | |
""" | |
is_number = True | |
is_positive = True | |
p = 1 | |
q = 1 | |
__slots__ = () | |
def __getnewargs__(self): | |
return () | |
def __abs__(): | |
return S.One | |
def __neg__(): | |
return S.NegativeOne | |
def _eval_power(self, expt): | |
return self | |
def _eval_order(self, *symbols): | |
return | |
def factors(limit=None, use_trial=True, use_rho=False, use_pm1=False, | |
verbose=False, visual=False): | |
if visual: | |
return S.One | |
else: | |
return {} | |
class NegativeOne(IntegerConstant, metaclass=Singleton): | |
"""The number negative one. | |
NegativeOne is a singleton, and can be accessed by ``S.NegativeOne``. | |
Examples | |
======== | |
>>> from sympy import S, Integer | |
>>> Integer(-1) is S.NegativeOne | |
True | |
See Also | |
======== | |
One | |
References | |
========== | |
.. [1] https://en.wikipedia.org/wiki/%E2%88%921_%28number%29 | |
""" | |
is_number = True | |
p = -1 | |
q = 1 | |
__slots__ = () | |
def __getnewargs__(self): | |
return () | |
def __abs__(): | |
return S.One | |
def __neg__(): | |
return S.One | |
def _eval_power(self, expt): | |
if expt.is_odd: | |
return S.NegativeOne | |
if expt.is_even: | |
return S.One | |
if isinstance(expt, Number): | |
if isinstance(expt, Float): | |
return Float(-1.0)**expt | |
if expt is S.NaN: | |
return S.NaN | |
if expt in (S.Infinity, S.NegativeInfinity): | |
return S.NaN | |
if expt is S.Half: | |
return S.ImaginaryUnit | |
if isinstance(expt, Rational): | |
if expt.q == 2: | |
return S.ImaginaryUnit**Integer(expt.p) | |
i, r = divmod(expt.p, expt.q) | |
if i: | |
return self**i*self**Rational(r, expt.q) | |
return | |
class Half(RationalConstant, metaclass=Singleton): | |
"""The rational number 1/2. | |
Half is a singleton, and can be accessed by ``S.Half``. | |
Examples | |
======== | |
>>> from sympy import S, Rational | |
>>> Rational(1, 2) is S.Half | |
True | |
References | |
========== | |
.. [1] https://en.wikipedia.org/wiki/One_half | |
""" | |
is_number = True | |
p = 1 | |
q = 2 | |
__slots__ = () | |
def __getnewargs__(self): | |
return () | |
def __abs__(): | |
return S.Half | |
class Infinity(Number, metaclass=Singleton): | |
r"""Positive infinite quantity. | |
Explanation | |
=========== | |
In real analysis the symbol `\infty` denotes an unbounded | |
limit: `x\to\infty` means that `x` grows without bound. | |
Infinity is often used not only to define a limit but as a value | |
in the affinely extended real number system. Points labeled `+\infty` | |
and `-\infty` can be added to the topological space of the real numbers, | |
producing the two-point compactification of the real numbers. Adding | |
algebraic properties to this gives us the extended real numbers. | |
Infinity is a singleton, and can be accessed by ``S.Infinity``, | |
or can be imported as ``oo``. | |
Examples | |
======== | |
>>> from sympy import oo, exp, limit, Symbol | |
>>> 1 + oo | |
oo | |
>>> 42/oo | |
0 | |
>>> x = Symbol('x') | |
>>> limit(exp(x), x, oo) | |
oo | |
See Also | |
======== | |
NegativeInfinity, NaN | |
References | |
========== | |
.. [1] https://en.wikipedia.org/wiki/Infinity | |
""" | |
is_commutative = True | |
is_number = True | |
is_complex = False | |
is_extended_real = True | |
is_infinite = True | |
is_comparable = True | |
is_extended_positive = True | |
is_prime = False | |
__slots__ = () | |
def __new__(cls): | |
return AtomicExpr.__new__(cls) | |
def _latex(self, printer): | |
return r"\infty" | |
def _eval_subs(self, old, new): | |
if self == old: | |
return new | |
def _eval_evalf(self, prec=None): | |
return Float('inf') | |
def evalf(self, prec=None, **options): | |
return self._eval_evalf(prec) | |
def __add__(self, other): | |
if isinstance(other, Number) and global_parameters.evaluate: | |
if other in (S.NegativeInfinity, S.NaN): | |
return S.NaN | |
return self | |
return Number.__add__(self, other) | |
__radd__ = __add__ | |
def __sub__(self, other): | |
if isinstance(other, Number) and global_parameters.evaluate: | |
if other in (S.Infinity, S.NaN): | |
return S.NaN | |
return self | |
return Number.__sub__(self, other) | |
def __rsub__(self, other): | |
return (-self).__add__(other) | |
def __mul__(self, other): | |
if isinstance(other, Number) and global_parameters.evaluate: | |
if other.is_zero or other is S.NaN: | |
return S.NaN | |
if other.is_extended_positive: | |
return self | |
return S.NegativeInfinity | |
return Number.__mul__(self, other) | |
__rmul__ = __mul__ | |
def __truediv__(self, other): | |
if isinstance(other, Number) and global_parameters.evaluate: | |
if other is S.Infinity or \ | |
other is S.NegativeInfinity or \ | |
other is S.NaN: | |
return S.NaN | |
if other.is_extended_nonnegative: | |
return self | |
return S.NegativeInfinity | |
return Number.__truediv__(self, other) | |
def __abs__(self): | |
return S.Infinity | |
def __neg__(self): | |
return S.NegativeInfinity | |
def _eval_power(self, expt): | |
""" | |
``expt`` is symbolic object but not equal to 0 or 1. | |
================ ======= ============================== | |
Expression Result Notes | |
================ ======= ============================== | |
``oo ** nan`` ``nan`` | |
``oo ** -p`` ``0`` ``p`` is number, ``oo`` | |
================ ======= ============================== | |
See Also | |
======== | |
Pow | |
NaN | |
NegativeInfinity | |
""" | |
if expt.is_extended_positive: | |
return S.Infinity | |
if expt.is_extended_negative: | |
return S.Zero | |
if expt is S.NaN: | |
return S.NaN | |
if expt is S.ComplexInfinity: | |
return S.NaN | |
if expt.is_extended_real is False and expt.is_number: | |
from sympy.functions.elementary.complexes import re | |
expt_real = re(expt) | |
if expt_real.is_positive: | |
return S.ComplexInfinity | |
if expt_real.is_negative: | |
return S.Zero | |
if expt_real.is_zero: | |
return S.NaN | |
return self**expt.evalf() | |
def _as_mpf_val(self, prec): | |
return mlib.finf | |
def __hash__(self): | |
return super().__hash__() | |
def __eq__(self, other): | |
return other is S.Infinity or other == float('inf') | |
def __ne__(self, other): | |
return other is not S.Infinity and other != float('inf') | |
__gt__ = Expr.__gt__ | |
__ge__ = Expr.__ge__ | |
__lt__ = Expr.__lt__ | |
__le__ = Expr.__le__ | |
def __mod__(self, other): | |
if not isinstance(other, Expr): | |
return NotImplemented | |
return S.NaN | |
__rmod__ = __mod__ | |
def floor(self): | |
return self | |
def ceiling(self): | |
return self | |
oo = S.Infinity | |
class NegativeInfinity(Number, metaclass=Singleton): | |
"""Negative infinite quantity. | |
NegativeInfinity is a singleton, and can be accessed | |
by ``S.NegativeInfinity``. | |
See Also | |
======== | |
Infinity | |
""" | |
is_extended_real = True | |
is_complex = False | |
is_commutative = True | |
is_infinite = True | |
is_comparable = True | |
is_extended_negative = True | |
is_number = True | |
is_prime = False | |
__slots__ = () | |
def __new__(cls): | |
return AtomicExpr.__new__(cls) | |
def _latex(self, printer): | |
return r"-\infty" | |
def _eval_subs(self, old, new): | |
if self == old: | |
return new | |
def _eval_evalf(self, prec=None): | |
return Float('-inf') | |
def evalf(self, prec=None, **options): | |
return self._eval_evalf(prec) | |
def __add__(self, other): | |
if isinstance(other, Number) and global_parameters.evaluate: | |
if other in (S.Infinity, S.NaN): | |
return S.NaN | |
return self | |
return Number.__add__(self, other) | |
__radd__ = __add__ | |
def __sub__(self, other): | |
if isinstance(other, Number) and global_parameters.evaluate: | |
if other in (S.NegativeInfinity, S.NaN): | |
return S.NaN | |
return self | |
return Number.__sub__(self, other) | |
def __rsub__(self, other): | |
return (-self).__add__(other) | |
def __mul__(self, other): | |
if isinstance(other, Number) and global_parameters.evaluate: | |
if other.is_zero or other is S.NaN: | |
return S.NaN | |
if other.is_extended_positive: | |
return self | |
return S.Infinity | |
return Number.__mul__(self, other) | |
__rmul__ = __mul__ | |
def __truediv__(self, other): | |
if isinstance(other, Number) and global_parameters.evaluate: | |
if other is S.Infinity or \ | |
other is S.NegativeInfinity or \ | |
other is S.NaN: | |
return S.NaN | |
if other.is_extended_nonnegative: | |
return self | |
return S.Infinity | |
return Number.__truediv__(self, other) | |
def __abs__(self): | |
return S.Infinity | |
def __neg__(self): | |
return S.Infinity | |
def _eval_power(self, expt): | |
""" | |
``expt`` is symbolic object but not equal to 0 or 1. | |
================ ======= ============================== | |
Expression Result Notes | |
================ ======= ============================== | |
``(-oo) ** nan`` ``nan`` | |
``(-oo) ** oo`` ``nan`` | |
``(-oo) ** -oo`` ``nan`` | |
``(-oo) ** e`` ``oo`` ``e`` is positive even integer | |
``(-oo) ** o`` ``-oo`` ``o`` is positive odd integer | |
================ ======= ============================== | |
See Also | |
======== | |
Infinity | |
Pow | |
NaN | |
""" | |
if expt.is_number: | |
if expt is S.NaN or \ | |
expt is S.Infinity or \ | |
expt is S.NegativeInfinity: | |
return S.NaN | |
if isinstance(expt, Integer) and expt.is_extended_positive: | |
if expt.is_odd: | |
return S.NegativeInfinity | |
else: | |
return S.Infinity | |
inf_part = S.Infinity**expt | |
s_part = S.NegativeOne**expt | |
if inf_part == 0 and s_part.is_finite: | |
return inf_part | |
if (inf_part is S.ComplexInfinity and | |
s_part.is_finite and not s_part.is_zero): | |
return S.ComplexInfinity | |
return s_part*inf_part | |
def _as_mpf_val(self, prec): | |
return mlib.fninf | |
def __hash__(self): | |
return super().__hash__() | |
def __eq__(self, other): | |
return other is S.NegativeInfinity or other == float('-inf') | |
def __ne__(self, other): | |
return other is not S.NegativeInfinity and other != float('-inf') | |
__gt__ = Expr.__gt__ | |
__ge__ = Expr.__ge__ | |
__lt__ = Expr.__lt__ | |
__le__ = Expr.__le__ | |
def __mod__(self, other): | |
if not isinstance(other, Expr): | |
return NotImplemented | |
return S.NaN | |
__rmod__ = __mod__ | |
def floor(self): | |
return self | |
def ceiling(self): | |
return self | |
def as_powers_dict(self): | |
return {S.NegativeOne: 1, S.Infinity: 1} | |
class NaN(Number, metaclass=Singleton): | |
""" | |
Not a Number. | |
Explanation | |
=========== | |
This serves as a place holder for numeric values that are indeterminate. | |
Most operations on NaN, produce another NaN. Most indeterminate forms, | |
such as ``0/0`` or ``oo - oo` produce NaN. Two exceptions are ``0**0`` | |
and ``oo**0``, which all produce ``1`` (this is consistent with Python's | |
float). | |
NaN is loosely related to floating point nan, which is defined in the | |
IEEE 754 floating point standard, and corresponds to the Python | |
``float('nan')``. Differences are noted below. | |
NaN is mathematically not equal to anything else, even NaN itself. This | |
explains the initially counter-intuitive results with ``Eq`` and ``==`` in | |
the examples below. | |
NaN is not comparable so inequalities raise a TypeError. This is in | |
contrast with floating point nan where all inequalities are false. | |
NaN is a singleton, and can be accessed by ``S.NaN``, or can be imported | |
as ``nan``. | |
Examples | |
======== | |
>>> from sympy import nan, S, oo, Eq | |
>>> nan is S.NaN | |
True | |
>>> oo - oo | |
nan | |
>>> nan + 1 | |
nan | |
>>> Eq(nan, nan) # mathematical equality | |
False | |
>>> nan == nan # structural equality | |
True | |
References | |
========== | |
.. [1] https://en.wikipedia.org/wiki/NaN | |
""" | |
is_commutative = True | |
is_extended_real = None | |
is_real = None | |
is_rational = None | |
is_algebraic = None | |
is_transcendental = None | |
is_integer = None | |
is_comparable = False | |
is_finite = None | |
is_zero = None | |
is_prime = None | |
is_positive = None | |
is_negative = None | |
is_number = True | |
__slots__ = () | |
def __new__(cls): | |
return AtomicExpr.__new__(cls) | |
def _latex(self, printer): | |
return r"\text{NaN}" | |
def __neg__(self): | |
return self | |
def __add__(self, other): | |
return self | |
def __sub__(self, other): | |
return self | |
def __mul__(self, other): | |
return self | |
def __truediv__(self, other): | |
return self | |
def floor(self): | |
return self | |
def ceiling(self): | |
return self | |
def _as_mpf_val(self, prec): | |
return _mpf_nan | |
def __hash__(self): | |
return super().__hash__() | |
def __eq__(self, other): | |
# NaN is structurally equal to another NaN | |
return other is S.NaN | |
def __ne__(self, other): | |
return other is not S.NaN | |
# Expr will _sympify and raise TypeError | |
__gt__ = Expr.__gt__ | |
__ge__ = Expr.__ge__ | |
__lt__ = Expr.__lt__ | |
__le__ = Expr.__le__ | |
nan = S.NaN | |
# type:ignore | |
def _eval_is_eq(a, b): # noqa:F811 | |
return False | |
class ComplexInfinity(AtomicExpr, metaclass=Singleton): | |
r"""Complex infinity. | |
Explanation | |
=========== | |
In complex analysis the symbol `\tilde\infty`, called "complex | |
infinity", represents a quantity with infinite magnitude, but | |
undetermined complex phase. | |
ComplexInfinity is a singleton, and can be accessed by | |
``S.ComplexInfinity``, or can be imported as ``zoo``. | |
Examples | |
======== | |
>>> from sympy import zoo | |
>>> zoo + 42 | |
zoo | |
>>> 42/zoo | |
0 | |
>>> zoo + zoo | |
nan | |
>>> zoo*zoo | |
zoo | |
See Also | |
======== | |
Infinity | |
""" | |
is_commutative = True | |
is_infinite = True | |
is_number = True | |
is_prime = False | |
is_complex = False | |
is_extended_real = False | |
kind = NumberKind | |
__slots__ = () | |
def __new__(cls): | |
return AtomicExpr.__new__(cls) | |
def _latex(self, printer): | |
return r"\tilde{\infty}" | |
def __abs__(): | |
return S.Infinity | |
def floor(self): | |
return self | |
def ceiling(self): | |
return self | |
def __neg__(): | |
return S.ComplexInfinity | |
def _eval_power(self, expt): | |
if expt is S.ComplexInfinity: | |
return S.NaN | |
if isinstance(expt, Number): | |
if expt.is_zero: | |
return S.NaN | |
else: | |
if expt.is_positive: | |
return S.ComplexInfinity | |
else: | |
return S.Zero | |
zoo = S.ComplexInfinity | |
class NumberSymbol(AtomicExpr): | |
is_commutative = True | |
is_finite = True | |
is_number = True | |
__slots__ = () | |
is_NumberSymbol = True | |
kind = NumberKind | |
def __new__(cls): | |
return AtomicExpr.__new__(cls) | |
def approximation(self, number_cls): | |
""" Return an interval with number_cls endpoints | |
that contains the value of NumberSymbol. | |
If not implemented, then return None. | |
""" | |
def _eval_evalf(self, prec): | |
return Float._new(self._as_mpf_val(prec), prec) | |
def __eq__(self, other): | |
try: | |
other = _sympify(other) | |
except SympifyError: | |
return NotImplemented | |
if self is other: | |
return True | |
if other.is_Number and self.is_irrational: | |
return False | |
return False # NumberSymbol != non-(Number|self) | |
def __ne__(self, other): | |
return not self == other | |
def __le__(self, other): | |
if self is other: | |
return S.true | |
return Expr.__le__(self, other) | |
def __ge__(self, other): | |
if self is other: | |
return S.true | |
return Expr.__ge__(self, other) | |
def __int__(self): | |
# subclass with appropriate return value | |
raise NotImplementedError | |
def __hash__(self): | |
return super().__hash__() | |
class Exp1(NumberSymbol, metaclass=Singleton): | |
r"""The `e` constant. | |
Explanation | |
=========== | |
The transcendental number `e = 2.718281828\ldots` is the base of the | |
natural logarithm and of the exponential function, `e = \exp(1)`. | |
Sometimes called Euler's number or Napier's constant. | |
Exp1 is a singleton, and can be accessed by ``S.Exp1``, | |
or can be imported as ``E``. | |
Examples | |
======== | |
>>> from sympy import exp, log, E | |
>>> E is exp(1) | |
True | |
>>> log(E) | |
1 | |
References | |
========== | |
.. [1] https://en.wikipedia.org/wiki/E_%28mathematical_constant%29 | |
""" | |
is_real = True | |
is_positive = True | |
is_negative = False # XXX Forces is_negative/is_nonnegative | |
is_irrational = True | |
is_number = True | |
is_algebraic = False | |
is_transcendental = True | |
__slots__ = () | |
def _latex(self, printer): | |
return r"e" | |
def __abs__(): | |
return S.Exp1 | |
def __int__(self): | |
return 2 | |
def _as_mpf_val(self, prec): | |
return mpf_e(prec) | |
def approximation_interval(self, number_cls): | |
if issubclass(number_cls, Integer): | |
return (Integer(2), Integer(3)) | |
elif issubclass(number_cls, Rational): | |
pass | |
def _eval_power(self, expt): | |
if global_parameters.exp_is_pow: | |
return self._eval_power_exp_is_pow(expt) | |
else: | |
from sympy.functions.elementary.exponential import exp | |
return exp(expt) | |
def _eval_power_exp_is_pow(self, arg): | |
if arg.is_Number: | |
if arg is oo: | |
return oo | |
elif arg == -oo: | |
return S.Zero | |
from sympy.functions.elementary.exponential import log | |
if isinstance(arg, log): | |
return arg.args[0] | |
# don't autoexpand Pow or Mul (see the issue 3351): | |
elif not arg.is_Add: | |
Ioo = I*oo | |
if arg in [Ioo, -Ioo]: | |
return nan | |
coeff = arg.coeff(pi*I) | |
if coeff: | |
if (2*coeff).is_integer: | |
if coeff.is_even: | |
return S.One | |
elif coeff.is_odd: | |
return S.NegativeOne | |
elif (coeff + S.Half).is_even: | |
return -I | |
elif (coeff + S.Half).is_odd: | |
return I | |
elif coeff.is_Rational: | |
ncoeff = coeff % 2 # restrict to [0, 2pi) | |
if ncoeff > 1: # restrict to (-pi, pi] | |
ncoeff -= 2 | |
if ncoeff != coeff: | |
return S.Exp1**(ncoeff*S.Pi*S.ImaginaryUnit) | |
# Warning: code in risch.py will be very sensitive to changes | |
# in this (see DifferentialExtension). | |
# look for a single log factor | |
coeff, terms = arg.as_coeff_Mul() | |
# but it can't be multiplied by oo | |
if coeff in (oo, -oo): | |
return | |
coeffs, log_term = [coeff], None | |
for term in Mul.make_args(terms): | |
if isinstance(term, log): | |
if log_term is None: | |
log_term = term.args[0] | |
else: | |
return | |
elif term.is_comparable: | |
coeffs.append(term) | |
else: | |
return | |
return log_term**Mul(*coeffs) if log_term else None | |
elif arg.is_Add: | |
out = [] | |
add = [] | |
argchanged = False | |
for a in arg.args: | |
if a is S.One: | |
add.append(a) | |
continue | |
newa = self**a | |
if isinstance(newa, Pow) and newa.base is self: | |
if newa.exp != a: | |
add.append(newa.exp) | |
argchanged = True | |
else: | |
add.append(a) | |
else: | |
out.append(newa) | |
if out or argchanged: | |
return Mul(*out)*Pow(self, Add(*add), evaluate=False) | |
elif arg.is_Matrix: | |
return arg.exp() | |
def _eval_rewrite_as_sin(self, **kwargs): | |
from sympy.functions.elementary.trigonometric import sin | |
return sin(I + S.Pi/2) - I*sin(I) | |
def _eval_rewrite_as_cos(self, **kwargs): | |
from sympy.functions.elementary.trigonometric import cos | |
return cos(I) + I*cos(I + S.Pi/2) | |
E = S.Exp1 | |
class Pi(NumberSymbol, metaclass=Singleton): | |
r"""The `\pi` constant. | |
Explanation | |
=========== | |
The transcendental number `\pi = 3.141592654\ldots` represents the ratio | |
of a circle's circumference to its diameter, the area of the unit circle, | |
the half-period of trigonometric functions, and many other things | |
in mathematics. | |
Pi is a singleton, and can be accessed by ``S.Pi``, or can | |
be imported as ``pi``. | |
Examples | |
======== | |
>>> from sympy import S, pi, oo, sin, exp, integrate, Symbol | |
>>> S.Pi | |
pi | |
>>> pi > 3 | |
True | |
>>> pi.is_irrational | |
True | |
>>> x = Symbol('x') | |
>>> sin(x + 2*pi) | |
sin(x) | |
>>> integrate(exp(-x**2), (x, -oo, oo)) | |
sqrt(pi) | |
References | |
========== | |
.. [1] https://en.wikipedia.org/wiki/Pi | |
""" | |
is_real = True | |
is_positive = True | |
is_negative = False | |
is_irrational = True | |
is_number = True | |
is_algebraic = False | |
is_transcendental = True | |
__slots__ = () | |
def _latex(self, printer): | |
return r"\pi" | |
def __abs__(): | |
return S.Pi | |
def __int__(self): | |
return 3 | |
def _as_mpf_val(self, prec): | |
return mpf_pi(prec) | |
def approximation_interval(self, number_cls): | |
if issubclass(number_cls, Integer): | |
return (Integer(3), Integer(4)) | |
elif issubclass(number_cls, Rational): | |
return (Rational(223, 71, 1), Rational(22, 7, 1)) | |
pi = S.Pi | |
class GoldenRatio(NumberSymbol, metaclass=Singleton): | |
r"""The golden ratio, `\phi`. | |
Explanation | |
=========== | |
`\phi = \frac{1 + \sqrt{5}}{2}` is an algebraic number. Two quantities | |
are in the golden ratio if their ratio is the same as the ratio of | |
their sum to the larger of the two quantities, i.e. their maximum. | |
GoldenRatio is a singleton, and can be accessed by ``S.GoldenRatio``. | |
Examples | |
======== | |
>>> from sympy import S | |
>>> S.GoldenRatio > 1 | |
True | |
>>> S.GoldenRatio.expand(func=True) | |
1/2 + sqrt(5)/2 | |
>>> S.GoldenRatio.is_irrational | |
True | |
References | |
========== | |
.. [1] https://en.wikipedia.org/wiki/Golden_ratio | |
""" | |
is_real = True | |
is_positive = True | |
is_negative = False | |
is_irrational = True | |
is_number = True | |
is_algebraic = True | |
is_transcendental = False | |
__slots__ = () | |
def _latex(self, printer): | |
return r"\phi" | |
def __int__(self): | |
return 1 | |
def _as_mpf_val(self, prec): | |
# XXX track down why this has to be increased | |
rv = mlib.from_man_exp(phi_fixed(prec + 10), -prec - 10) | |
return mpf_norm(rv, prec) | |
def _eval_expand_func(self, **hints): | |
from sympy.functions.elementary.miscellaneous import sqrt | |
return S.Half + S.Half*sqrt(5) | |
def approximation_interval(self, number_cls): | |
if issubclass(number_cls, Integer): | |
return (S.One, Rational(2)) | |
elif issubclass(number_cls, Rational): | |
pass | |
_eval_rewrite_as_sqrt = _eval_expand_func | |
class TribonacciConstant(NumberSymbol, metaclass=Singleton): | |
r"""The tribonacci constant. | |
Explanation | |
=========== | |
The tribonacci numbers are like the Fibonacci numbers, but instead | |
of starting with two predetermined terms, the sequence starts with | |
three predetermined terms and each term afterwards is the sum of the | |
preceding three terms. | |
The tribonacci constant is the ratio toward which adjacent tribonacci | |
numbers tend. It is a root of the polynomial `x^3 - x^2 - x - 1 = 0`, | |
and also satisfies the equation `x + x^{-3} = 2`. | |
TribonacciConstant is a singleton, and can be accessed | |
by ``S.TribonacciConstant``. | |
Examples | |
======== | |
>>> from sympy import S | |
>>> S.TribonacciConstant > 1 | |
True | |
>>> S.TribonacciConstant.expand(func=True) | |
1/3 + (19 - 3*sqrt(33))**(1/3)/3 + (3*sqrt(33) + 19)**(1/3)/3 | |
>>> S.TribonacciConstant.is_irrational | |
True | |
>>> S.TribonacciConstant.n(20) | |
1.8392867552141611326 | |
References | |
========== | |
.. [1] https://en.wikipedia.org/wiki/Generalizations_of_Fibonacci_numbers#Tribonacci_numbers | |
""" | |
is_real = True | |
is_positive = True | |
is_negative = False | |
is_irrational = True | |
is_number = True | |
is_algebraic = True | |
is_transcendental = False | |
__slots__ = () | |
def _latex(self, printer): | |
return r"\text{TribonacciConstant}" | |
def __int__(self): | |
return 1 | |
def _as_mpf_val(self, prec): | |
return self._eval_evalf(prec)._mpf_ | |
def _eval_evalf(self, prec): | |
rv = self._eval_expand_func(function=True)._eval_evalf(prec + 4) | |
return Float(rv, precision=prec) | |
def _eval_expand_func(self, **hints): | |
from sympy.functions.elementary.miscellaneous import cbrt, sqrt | |
return (1 + cbrt(19 - 3*sqrt(33)) + cbrt(19 + 3*sqrt(33))) / 3 | |
def approximation_interval(self, number_cls): | |
if issubclass(number_cls, Integer): | |
return (S.One, Rational(2)) | |
elif issubclass(number_cls, Rational): | |
pass | |
_eval_rewrite_as_sqrt = _eval_expand_func | |
class EulerGamma(NumberSymbol, metaclass=Singleton): | |
r"""The Euler-Mascheroni constant. | |
Explanation | |
=========== | |
`\gamma = 0.5772157\ldots` (also called Euler's constant) is a mathematical | |
constant recurring in analysis and number theory. It is defined as the | |
limiting difference between the harmonic series and the | |
natural logarithm: | |
.. math:: \gamma = \lim\limits_{n\to\infty} | |
\left(\sum\limits_{k=1}^n\frac{1}{k} - \ln n\right) | |
EulerGamma is a singleton, and can be accessed by ``S.EulerGamma``. | |
Examples | |
======== | |
>>> from sympy import S | |
>>> S.EulerGamma.is_irrational | |
>>> S.EulerGamma > 0 | |
True | |
>>> S.EulerGamma > 1 | |
False | |
References | |
========== | |
.. [1] https://en.wikipedia.org/wiki/Euler%E2%80%93Mascheroni_constant | |
""" | |
is_real = True | |
is_positive = True | |
is_negative = False | |
is_irrational = None | |
is_number = True | |
__slots__ = () | |
def _latex(self, printer): | |
return r"\gamma" | |
def __int__(self): | |
return 0 | |
def _as_mpf_val(self, prec): | |
# XXX track down why this has to be increased | |
v = mlib.libhyper.euler_fixed(prec + 10) | |
rv = mlib.from_man_exp(v, -prec - 10) | |
return mpf_norm(rv, prec) | |
def approximation_interval(self, number_cls): | |
if issubclass(number_cls, Integer): | |
return (S.Zero, S.One) | |
elif issubclass(number_cls, Rational): | |
return (S.Half, Rational(3, 5, 1)) | |
class Catalan(NumberSymbol, metaclass=Singleton): | |
r"""Catalan's constant. | |
Explanation | |
=========== | |
$G = 0.91596559\ldots$ is given by the infinite series | |
.. math:: G = \sum_{k=0}^{\infty} \frac{(-1)^k}{(2k+1)^2} | |
Catalan is a singleton, and can be accessed by ``S.Catalan``. | |
Examples | |
======== | |
>>> from sympy import S | |
>>> S.Catalan.is_irrational | |
>>> S.Catalan > 0 | |
True | |
>>> S.Catalan > 1 | |
False | |
References | |
========== | |
.. [1] https://en.wikipedia.org/wiki/Catalan%27s_constant | |
""" | |
is_real = True | |
is_positive = True | |
is_negative = False | |
is_irrational = None | |
is_number = True | |
__slots__ = () | |
def __int__(self): | |
return 0 | |
def _as_mpf_val(self, prec): | |
# XXX track down why this has to be increased | |
v = mlib.catalan_fixed(prec + 10) | |
rv = mlib.from_man_exp(v, -prec - 10) | |
return mpf_norm(rv, prec) | |
def approximation_interval(self, number_cls): | |
if issubclass(number_cls, Integer): | |
return (S.Zero, S.One) | |
elif issubclass(number_cls, Rational): | |
return (Rational(9, 10, 1), S.One) | |
def _eval_rewrite_as_Sum(self, k_sym=None, symbols=None, **hints): | |
if (k_sym is not None) or (symbols is not None): | |
return self | |
from .symbol import Dummy | |
from sympy.concrete.summations import Sum | |
k = Dummy('k', integer=True, nonnegative=True) | |
return Sum(S.NegativeOne**k / (2*k+1)**2, (k, 0, S.Infinity)) | |
def _latex(self, printer): | |
return "G" | |
class ImaginaryUnit(AtomicExpr, metaclass=Singleton): | |
r"""The imaginary unit, `i = \sqrt{-1}`. | |
I is a singleton, and can be accessed by ``S.I``, or can be | |
imported as ``I``. | |
Examples | |
======== | |
>>> from sympy import I, sqrt | |
>>> sqrt(-1) | |
I | |
>>> I*I | |
-1 | |
>>> 1/I | |
-I | |
References | |
========== | |
.. [1] https://en.wikipedia.org/wiki/Imaginary_unit | |
""" | |
is_commutative = True | |
is_imaginary = True | |
is_finite = True | |
is_number = True | |
is_algebraic = True | |
is_transcendental = False | |
kind = NumberKind | |
__slots__ = () | |
def _latex(self, printer): | |
return printer._settings['imaginary_unit_latex'] | |
def __abs__(): | |
return S.One | |
def _eval_evalf(self, prec): | |
return self | |
def _eval_conjugate(self): | |
return -S.ImaginaryUnit | |
def _eval_power(self, expt): | |
""" | |
b is I = sqrt(-1) | |
e is symbolic object but not equal to 0, 1 | |
I**r -> (-1)**(r/2) -> exp(r/2*Pi*I) -> sin(Pi*r/2) + cos(Pi*r/2)*I, r is decimal | |
I**0 mod 4 -> 1 | |
I**1 mod 4 -> I | |
I**2 mod 4 -> -1 | |
I**3 mod 4 -> -I | |
""" | |
if isinstance(expt, Integer): | |
expt = expt % 4 | |
if expt == 0: | |
return S.One | |
elif expt == 1: | |
return S.ImaginaryUnit | |
elif expt == 2: | |
return S.NegativeOne | |
elif expt == 3: | |
return -S.ImaginaryUnit | |
if isinstance(expt, Rational): | |
i, r = divmod(expt, 2) | |
rv = Pow(S.ImaginaryUnit, r, evaluate=False) | |
if i % 2: | |
return Mul(S.NegativeOne, rv, evaluate=False) | |
return rv | |
def as_base_exp(self): | |
return S.NegativeOne, S.Half | |
def _mpc_(self): | |
return (Float(0)._mpf_, Float(1)._mpf_) | |
I = S.ImaginaryUnit | |
def int_valued(x): | |
"""return True only for a literal Number whose internal | |
representation as a fraction has a denominator of 1, | |
else False, i.e. integer, with no fractional part. | |
""" | |
if isinstance(x, (SYMPY_INTS, int)): | |
return True | |
if type(x) is float: | |
return x.is_integer() | |
if isinstance(x, Integer): | |
return True | |
if isinstance(x, Float): | |
# x = s*m*2**p; _mpf_ = s,m,e,p | |
return x._mpf_[2] >= 0 | |
return False # or add new types to recognize | |
def equal_valued(x, y): | |
"""Compare expressions treating plain floats as rationals. | |
Examples | |
======== | |
>>> from sympy import S, symbols, Rational, Float | |
>>> from sympy.core.numbers import equal_valued | |
>>> equal_valued(1, 2) | |
False | |
>>> equal_valued(1, 1) | |
True | |
In SymPy expressions with Floats compare unequal to corresponding | |
expressions with rationals: | |
>>> x = symbols('x') | |
>>> x**2 == x**2.0 | |
False | |
However an individual Float compares equal to a Rational: | |
>>> Rational(1, 2) == Float(0.5) | |
False | |
In a future version of SymPy this might change so that Rational and Float | |
compare unequal. This function provides the behavior currently expected of | |
``==`` so that it could still be used if the behavior of ``==`` were to | |
change in future. | |
>>> equal_valued(1, 1.0) # Float vs Rational | |
True | |
>>> equal_valued(S(1).n(3), S(1).n(5)) # Floats of different precision | |
True | |
Explanation | |
=========== | |
In future SymPy verions Float and Rational might compare unequal and floats | |
with different precisions might compare unequal. In that context a function | |
is needed that can check if a number is equal to 1 or 0 etc. The idea is | |
that instead of testing ``if x == 1:`` if we want to accept floats like | |
``1.0`` as well then the test can be written as ``if equal_valued(x, 1):`` | |
or ``if equal_valued(x, 2):``. Since this function is intended to be used | |
in situations where one or both operands are expected to be concrete | |
numbers like 1 or 0 the function does not recurse through the args of any | |
compound expression to compare any nested floats. | |
References | |
========== | |
.. [1] https://github.com/sympy/sympy/pull/20033 | |
""" | |
x = _sympify(x) | |
y = _sympify(y) | |
# Handle everything except Float/Rational first | |
if not x.is_Float and not y.is_Float: | |
return x == y | |
elif x.is_Float and y.is_Float: | |
# Compare values without regard for precision | |
return x._mpf_ == y._mpf_ | |
elif x.is_Float: | |
x, y = y, x | |
if not x.is_Rational: | |
return False | |
# Now y is Float and x is Rational. A simple approach at this point would | |
# just be x == Rational(y) but if y has a large exponent creating a | |
# Rational could be prohibitively expensive. | |
sign, man, exp, _ = y._mpf_ | |
p, q = x.p, x.q | |
if sign: | |
man = -man | |
if exp == 0: | |
# y odd integer | |
return q == 1 and man == p | |
elif exp > 0: | |
# y even integer | |
if q != 1: | |
return False | |
if p.bit_length() != man.bit_length() + exp: | |
return False | |
return man << exp == p | |
else: | |
# y non-integer. Need p == man and q == 2**-exp | |
if p != man: | |
return False | |
neg_exp = -exp | |
if q.bit_length() - 1 != neg_exp: | |
return False | |
return (1 << neg_exp) == q | |
def all_close(expr1, expr2, rtol=1e-5, atol=1e-8): | |
"""Return True if expr1 and expr2 are numerically close. | |
The expressions must have the same structure, but any Rational, Integer, or | |
Float numbers they contain are compared approximately using rtol and atol. | |
Any other parts of expressions are compared exactly. | |
Relative tolerance is measured with respect to expr2 so when used in | |
testing expr2 should be the expected correct answer. | |
Examples | |
======== | |
>>> from sympy import exp | |
>>> from sympy.abc import x, y | |
>>> from sympy.core.numbers import all_close | |
>>> expr1 = 0.1*exp(x - y) | |
>>> expr2 = exp(x - y)/10 | |
>>> expr1 | |
0.1*exp(x - y) | |
>>> expr2 | |
exp(x - y)/10 | |
>>> expr1 == expr2 | |
False | |
>>> all_close(expr1, expr2) | |
True | |
""" | |
NUM_TYPES = (Rational, Float) | |
def _all_close(expr1, expr2, rtol, atol): | |
num1 = isinstance(expr1, NUM_TYPES) | |
num2 = isinstance(expr2, NUM_TYPES) | |
if num1 != num2: | |
return False | |
elif num1: | |
return bool(abs(expr1 - expr2) <= atol + rtol*abs(expr2)) | |
elif expr1.is_Atom: | |
return expr1 == expr2 | |
elif expr1.func != expr2.func or len(expr1.args) != len(expr2.args): | |
return False | |
elif expr1.is_Add or expr1.is_Mul: | |
return _all_close_ac(expr1, expr2, rtol, atol) | |
else: | |
args = zip(expr1.args, expr2.args) | |
return all(_all_close(a1, a2, rtol, atol) for a1, a2 in args) | |
def _all_close_ac(expr1, expr2, rtol, atol): | |
# Compare expressions with associative commutative operators for | |
# approximate equality. This could be horribly inefficient for large | |
# expressions e.g. an Add with many terms. | |
args2 = list(expr2.args) | |
for arg1 in expr1.args: | |
for i, arg2 in enumerate(args2): | |
if _all_close(arg1, arg2, rtol, atol): | |
args2.pop(i) | |
break | |
else: | |
return False | |
return True | |
return _all_close(_sympify(expr1), _sympify(expr2), rtol, atol) | |
# type:ignore | |
def _eval_is_eq(self, other): # noqa: F811 | |
return False | |
def sympify_fractions(f): | |
return Rational(f.numerator, f.denominator, 1) | |
_sympy_converter[fractions.Fraction] = sympify_fractions | |
if gmpy is not None: | |
def sympify_mpz(x): | |
return Integer(int(x)) | |
def sympify_mpq(x): | |
return Rational(int(x.numerator), int(x.denominator)) | |
_sympy_converter[type(gmpy.mpz(1))] = sympify_mpz | |
_sympy_converter[type(gmpy.mpq(1, 2))] = sympify_mpq | |
if flint is not None: | |
def sympify_fmpz(x): | |
return Integer(int(x)) | |
def sympify_fmpq(x): | |
return Rational(int(x.numerator), int(x.denominator)) | |
_sympy_converter[type(flint.fmpz(1))] = sympify_fmpz | |
_sympy_converter[type(flint.fmpq(1, 2))] = sympify_fmpq | |
def sympify_mpmath(x): | |
return Expr._from_mpmath(x, x.context.prec) | |
_sympy_converter[mpnumeric] = sympify_mpmath | |
def sympify_complex(a): | |
real, imag = list(map(sympify, (a.real, a.imag))) | |
return real + S.ImaginaryUnit*imag | |
_sympy_converter[complex] = sympify_complex | |
from .power import Pow | |
from .mul import Mul | |
Mul.identity = One() | |
from .add import Add | |
Add.identity = Zero() | |
def _register_classes(): | |
numbers.Number.register(Number) | |
numbers.Real.register(Float) | |
numbers.Rational.register(Rational) | |
numbers.Integral.register(Integer) | |
_register_classes() | |
_illegal = (S.NaN, S.Infinity, S.NegativeInfinity, S.ComplexInfinity) | |