|
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: |
|
|
|
return a == b |
|
|
|
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: |
|
|
|
|
|
|
|
if not bc: |
|
return fzero |
|
else: |
|
|
|
|
|
return mpf |
|
|
|
|
|
from mpmath.libmp.backend import MPZ |
|
rv = mpf_normalize(sign, MPZ(man), expt, bc, prec, rnd) |
|
return rv |
|
|
|
|
|
_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: |
|
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? |
|
""" |
|
|
|
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' |
|
|
|
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: |
|
i = '1' |
|
f = f or '1' |
|
|
|
for n in (i, f, e): |
|
for g in n.split('_'): |
|
if not g or g.translate(_dig): |
|
return False |
|
return True |
|
|
|
|
|
|
|
|
|
|
|
|
|
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__ = () |
|
|
|
|
|
_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() |
|
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) |
|
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 |
|
|
|
return Order(S.One, *symbols) |
|
|
|
def _eval_subs(self, old, new): |
|
if old == -self: |
|
return -new |
|
return self |
|
|
|
@classmethod |
|
def class_key(cls): |
|
return 1, 0, 'Number' |
|
|
|
@cacheit |
|
def sort_key(self, order=None): |
|
return self.class_key(), (0, ()), (), self |
|
|
|
@_sympifyit('other', NotImplemented) |
|
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) |
|
|
|
@_sympifyit('other', NotImplemented) |
|
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) |
|
|
|
@_sympifyit('other', NotImplemented) |
|
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) |
|
|
|
@_sympifyit('other', NotImplemented) |
|
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): |
|
|
|
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): |
|
|
|
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] |
|
|
|
|
|
|
|
|
|
|
|
|
|
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() |
|
num = num.replace(' ', '_').lower() |
|
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): |
|
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: |
|
|
|
|
|
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: |
|
|
|
dps = max(dps, num_digits(num)) |
|
precision = dps_to_prec(dps) |
|
|
|
|
|
|
|
|
|
|
|
|
|
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): |
|
|
|
num = list(num) |
|
|
|
|
|
|
|
if num[1].endswith('L'): |
|
num[1] = num[1][:-1] |
|
|
|
|
|
|
|
|
|
|
|
if num[1].startswith('0x'): |
|
num[1] = num[1][2:] |
|
|
|
num[1] = MPZ(num[1], 16) |
|
_mpf_ = tuple(num) |
|
else: |
|
if len(num) == 4: |
|
|
|
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,)) |
|
|
|
|
|
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) |
|
|
|
@classmethod |
|
def _new(cls, _mpf_, _prec, zero=True): |
|
|
|
if zero and _mpf_ == fzero: |
|
return S.Zero |
|
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() |
|
|
|
@property |
|
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) |
|
|
|
@_sympifyit('other', NotImplemented) |
|
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) |
|
|
|
@_sympifyit('other', NotImplemented) |
|
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) |
|
|
|
@_sympifyit('other', NotImplemented) |
|
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) |
|
|
|
@_sympifyit('other', NotImplemented) |
|
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) |
|
|
|
@_sympifyit('other', NotImplemented) |
|
def __mod__(self, other): |
|
if isinstance(other, Rational) and other.q != 1 and global_parameters.evaluate: |
|
|
|
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) |
|
|
|
@_sympifyit('other', NotImplemented) |
|
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_)) |
|
|
|
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: |
|
|
|
''' |
|
>>> 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) |
|
|
|
|
|
|
|
_sympy_converter[float] = _sympy_converter[decimal.Decimal] = Float |
|
|
|
|
|
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 |
|
|
|
@cacheit |
|
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 |
|
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 |
|
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 |
|
|
|
|
|
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) |
|
|
|
@_sympifyit('other', NotImplemented) |
|
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): |
|
|
|
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__ |
|
|
|
@_sympifyit('other', NotImplemented) |
|
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) |
|
@_sympifyit('other', NotImplemented) |
|
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) |
|
@_sympifyit('other', NotImplemented) |
|
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__ |
|
|
|
@_sympifyit('other', NotImplemented) |
|
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) |
|
@_sympifyit('other', NotImplemented) |
|
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) |
|
|
|
@_sympifyit('other', NotImplemented) |
|
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): |
|
|
|
return Float(self.__mod__(Rational(other)), |
|
precision=other._prec) |
|
return Number.__mod__(self, other) |
|
return Number.__mod__(self, other) |
|
|
|
@_sympifyit('other', NotImplemented) |
|
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: |
|
|
|
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: |
|
if self.p > self.q: |
|
|
|
return S.Infinity |
|
if self.p < -self.q: |
|
|
|
return S.Infinity + S.Infinity*S.ImaginaryUnit |
|
return S.Zero |
|
if isinstance(expt, Integer): |
|
|
|
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): |
|
|
|
|
|
return False |
|
if other.is_NumberSymbol: |
|
if other.is_irrational: |
|
return False |
|
return other.__eq__(self) |
|
if other.is_Rational: |
|
|
|
|
|
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): |
|
|
|
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() |
|
|
|
@property |
|
def numerator(self): |
|
return self.p |
|
|
|
@property |
|
def denominator(self): |
|
return self.q |
|
|
|
@_sympifyit('other', NotImplemented) |
|
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) |
|
|
|
@_sympifyit('other', NotImplemented) |
|
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)) |
|
|
|
@cacheit |
|
def __new__(cls, i): |
|
if isinstance(i, str): |
|
i = i.replace(' ', '') |
|
|
|
|
|
|
|
|
|
|
|
|
|
try: |
|
ival = int(i) |
|
except TypeError: |
|
raise TypeError( |
|
"Argument of Integer should be of numeric type, got %s." % i) |
|
|
|
|
|
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,) |
|
|
|
|
|
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) |
|
|
|
|
|
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 |
|
|
|
return S.Infinity + S.ImaginaryUnit*S.Infinity |
|
if expt is S.NegativeInfinity: |
|
return Rational(1, self, 1)**S.Infinity |
|
if not isinstance(expt, Number): |
|
|
|
|
|
if self.is_negative and expt.is_even: |
|
return (-self)**expt |
|
if isinstance(expt, Float): |
|
|
|
return super()._eval_power(expt) |
|
if not isinstance(expt, Rational): |
|
return |
|
if expt is S.Half and self.is_negative: |
|
|
|
return S.ImaginaryUnit*Pow(-self, expt) |
|
if expt.is_negative: |
|
|
|
ne = -expt |
|
if self.is_negative: |
|
return S.NegativeOne**expt*Rational(1, -self, 1)**ne |
|
else: |
|
return Rational(1, self.p, 1)**ne |
|
|
|
x, xexact = integer_nthroot(abs(self.p), expt.q) |
|
if xexact: |
|
|
|
result = Integer(x**abs(expt.p)) |
|
if self.is_negative: |
|
result *= S.NegativeOne**expt |
|
return result |
|
|
|
|
|
|
|
|
|
|
|
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) |
|
|
|
|
|
out_int = 1 |
|
out_rad = 1 |
|
sqr_int = 1 |
|
sqr_gcd = 0 |
|
sqr_dict = {} |
|
for prime, exponent in dict.items(): |
|
exponent *= expt.p |
|
|
|
div_e, div_m = divmod(exponent, expt.q) |
|
if div_e > 0: |
|
out_int *= prime**div_e |
|
if div_m > 0: |
|
|
|
|
|
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 |
|
|
|
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 |
|
|
|
@_sympifyit('other', NotImplemented) |
|
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) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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) |
|
|
|
|
|
_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 |
|
|
|
|
|
|
|
|
|
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) |
|
|
|
@property |
|
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: |
|
|
|
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) |
|
|
|
@property |
|
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() |
|
|
|
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 () |
|
|
|
@staticmethod |
|
def __abs__(): |
|
return S.Zero |
|
|
|
@staticmethod |
|
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 |
|
|
|
|
|
|
|
|
|
coeff, terms = expt.as_coeff_Mul() |
|
if coeff.is_negative: |
|
return S.ComplexInfinity**terms |
|
if coeff is not S.One: |
|
return self**terms |
|
|
|
def _eval_order(self, *symbols): |
|
|
|
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 () |
|
|
|
@staticmethod |
|
def __abs__(): |
|
return S.One |
|
|
|
@staticmethod |
|
def __neg__(): |
|
return S.NegativeOne |
|
|
|
def _eval_power(self, expt): |
|
return self |
|
|
|
def _eval_order(self, *symbols): |
|
return |
|
|
|
@staticmethod |
|
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 () |
|
|
|
@staticmethod |
|
def __abs__(): |
|
return S.One |
|
|
|
@staticmethod |
|
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 () |
|
|
|
@staticmethod |
|
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) |
|
|
|
@_sympifyit('other', NotImplemented) |
|
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__ |
|
|
|
@_sympifyit('other', NotImplemented) |
|
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) |
|
|
|
@_sympifyit('other', NotImplemented) |
|
def __rsub__(self, other): |
|
return (-self).__add__(other) |
|
|
|
@_sympifyit('other', NotImplemented) |
|
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__ |
|
|
|
@_sympifyit('other', NotImplemented) |
|
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__ |
|
|
|
@_sympifyit('other', NotImplemented) |
|
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) |
|
|
|
@_sympifyit('other', NotImplemented) |
|
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__ |
|
|
|
@_sympifyit('other', NotImplemented) |
|
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) |
|
|
|
@_sympifyit('other', NotImplemented) |
|
def __rsub__(self, other): |
|
return (-self).__add__(other) |
|
|
|
@_sympifyit('other', NotImplemented) |
|
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__ |
|
|
|
@_sympifyit('other', NotImplemented) |
|
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__ |
|
|
|
@_sympifyit('other', NotImplemented) |
|
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 |
|
|
|
@_sympifyit('other', NotImplemented) |
|
def __add__(self, other): |
|
return self |
|
|
|
@_sympifyit('other', NotImplemented) |
|
def __sub__(self, other): |
|
return self |
|
|
|
@_sympifyit('other', NotImplemented) |
|
def __mul__(self, other): |
|
return self |
|
|
|
@_sympifyit('other', NotImplemented) |
|
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): |
|
|
|
return other is S.NaN |
|
|
|
def __ne__(self, other): |
|
return other is not S.NaN |
|
|
|
|
|
__gt__ = Expr.__gt__ |
|
__ge__ = Expr.__ge__ |
|
__lt__ = Expr.__lt__ |
|
__le__ = Expr.__le__ |
|
|
|
nan = S.NaN |
|
|
|
@dispatch(NaN, Expr) |
|
def _eval_is_eq(a, b): |
|
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}" |
|
|
|
@staticmethod |
|
def __abs__(): |
|
return S.Infinity |
|
|
|
def floor(self): |
|
return self |
|
|
|
def ceiling(self): |
|
return self |
|
|
|
@staticmethod |
|
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 |
|
|
|
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): |
|
|
|
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 |
|
is_irrational = True |
|
is_number = True |
|
is_algebraic = False |
|
is_transcendental = True |
|
|
|
__slots__ = () |
|
|
|
def _latex(self, printer): |
|
return r"e" |
|
|
|
@staticmethod |
|
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] |
|
|
|
|
|
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 |
|
if ncoeff > 1: |
|
ncoeff -= 2 |
|
if ncoeff != coeff: |
|
return S.Exp1**(ncoeff*S.Pi*S.ImaginaryUnit) |
|
|
|
|
|
|
|
|
|
|
|
|
|
coeff, terms = arg.as_coeff_Mul() |
|
|
|
|
|
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" |
|
|
|
@staticmethod |
|
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): |
|
|
|
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): |
|
|
|
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): |
|
|
|
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'] |
|
|
|
@staticmethod |
|
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 |
|
|
|
@property |
|
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): |
|
|
|
return x._mpf_[2] >= 0 |
|
return False |
|
|
|
|
|
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) |
|
|
|
|
|
if not x.is_Float and not y.is_Float: |
|
return x == y |
|
elif x.is_Float and y.is_Float: |
|
|
|
return x._mpf_ == y._mpf_ |
|
elif x.is_Float: |
|
x, y = y, x |
|
if not x.is_Rational: |
|
return False |
|
|
|
|
|
|
|
|
|
|
|
sign, man, exp, _ = y._mpf_ |
|
p, q = x.p, x.q |
|
|
|
if sign: |
|
man = -man |
|
|
|
if exp == 0: |
|
|
|
return q == 1 and man == p |
|
elif exp > 0: |
|
|
|
if q != 1: |
|
return False |
|
if p.bit_length() != man.bit_length() + exp: |
|
return False |
|
return man << exp == p |
|
else: |
|
|
|
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): |
|
|
|
|
|
|
|
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) |
|
|
|
|
|
@dispatch(Tuple, Number) |
|
def _eval_is_eq(self, other): |
|
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) |
|
|