4597 lines
136 KiB
Python
4597 lines
136 KiB
Python
|
from __future__ import annotations
|
||
|
|
||
|
import numbers
|
||
|
import decimal
|
||
|
import fractions
|
||
|
import math
|
||
|
import re as regex
|
||
|
import sys
|
||
|
from functools import lru_cache
|
||
|
|
||
|
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 .logic import fuzzy_not
|
||
|
from .kind import NumberKind
|
||
|
from sympy.external.gmpy import SYMPY_INTS, HAS_GMPY, gmpy
|
||
|
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 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 as_int, debug, filldedent
|
||
|
from .parameters import global_parameters
|
||
|
|
||
|
_LOG2 = math.log(2)
|
||
|
|
||
|
|
||
|
def comp(z1, z2, tol=None):
|
||
|
r"""Return a bool indicating whether the error between z1 and z2
|
||
|
is $\le$ ``tol``.
|
||
|
|
||
|
Examples
|
||
|
========
|
||
|
|
||
|
If ``tol`` is ``None`` then ``True`` will be returned if
|
||
|
:math:`|z1 - z2|\times 10^p \le 5` where $p$ is minimum value of the
|
||
|
decimal precision of each value.
|
||
|
|
||
|
>>> from sympy import comp, pi
|
||
|
>>> pi4 = pi.n(4); pi4
|
||
|
3.142
|
||
|
>>> comp(_, 3.142)
|
||
|
True
|
||
|
>>> comp(pi4, 3.141)
|
||
|
False
|
||
|
>>> comp(pi4, 3.143)
|
||
|
False
|
||
|
|
||
|
A comparison of strings will be made
|
||
|
if ``z1`` is a Number and ``z2`` is a string or ``tol`` is ''.
|
||
|
|
||
|
>>> comp(pi4, 3.1415)
|
||
|
True
|
||
|
>>> comp(pi4, 3.1415, '')
|
||
|
False
|
||
|
|
||
|
When ``tol`` is provided and $z2$ is non-zero and
|
||
|
:math:`|z1| > 1` the error is normalized by :math:`|z1|`:
|
||
|
|
||
|
>>> abs(pi4 - 3.14)/pi4
|
||
|
0.000509791731426756
|
||
|
>>> comp(pi4, 3.14, .001) # difference less than 0.1%
|
||
|
True
|
||
|
>>> comp(pi4, 3.14, .0005) # difference less than 0.1%
|
||
|
False
|
||
|
|
||
|
When :math:`|z1| \le 1` the absolute error is used:
|
||
|
|
||
|
>>> 1/pi4
|
||
|
0.3183
|
||
|
>>> abs(1/pi4 - 0.3183)/(1/pi4)
|
||
|
3.07371499106316e-5
|
||
|
>>> abs(1/pi4 - 0.3183)
|
||
|
9.78393554684764e-6
|
||
|
>>> comp(1/pi4, 0.3183, 1e-5)
|
||
|
True
|
||
|
|
||
|
To see if the absolute error between ``z1`` and ``z2`` is less
|
||
|
than or equal to ``tol``, call this as ``comp(z1 - z2, 0, tol)``
|
||
|
or ``comp(z1 - z2, tol=tol)``:
|
||
|
|
||
|
>>> abs(pi4 - 3.14)
|
||
|
0.00160156249999988
|
||
|
>>> comp(pi4 - 3.14, 0, .002)
|
||
|
True
|
||
|
>>> comp(pi4 - 3.14, 0, .001)
|
||
|
False
|
||
|
"""
|
||
|
if isinstance(z2, str):
|
||
|
if not pure_complex(z1, or_real=True):
|
||
|
raise ValueError('when z2 is a str z1 must be a Number')
|
||
|
return str(z1) == z2
|
||
|
if not z1:
|
||
|
z1, z2 = z2, z1
|
||
|
if not z1:
|
||
|
return True
|
||
|
if not tol:
|
||
|
a, b = z1, z2
|
||
|
if tol == '':
|
||
|
return str(a) == str(b)
|
||
|
if tol is None:
|
||
|
a, b = sympify(a), sympify(b)
|
||
|
if not all(i.is_number for i in (a, b)):
|
||
|
raise ValueError('expecting 2 numbers')
|
||
|
fa = a.atoms(Float)
|
||
|
fb = b.atoms(Float)
|
||
|
if not fa and not fb:
|
||
|
# no floats -- compare exactly
|
||
|
return a == b
|
||
|
# get a to be pure_complex
|
||
|
for _ in range(2):
|
||
|
ca = pure_complex(a, or_real=True)
|
||
|
if not ca:
|
||
|
if fa:
|
||
|
a = a.n(prec_to_dps(min([i._prec for i in fa])))
|
||
|
ca = pure_complex(a, or_real=True)
|
||
|
break
|
||
|
else:
|
||
|
fa, fb = fb, fa
|
||
|
a, b = b, a
|
||
|
cb = pure_complex(b)
|
||
|
if not cb and fb:
|
||
|
b = b.n(prec_to_dps(min([i._prec for i in fb])))
|
||
|
cb = pure_complex(b, or_real=True)
|
||
|
if ca and cb and (ca[1] or cb[1]):
|
||
|
return all(comp(i, j) for i, j in zip(ca, cb))
|
||
|
tol = 10**prec_to_dps(min(a._prec, getattr(b, '_prec', a._prec)))
|
||
|
return int(abs(a - b)*tol) <= 5
|
||
|
diff = abs(z1 - z2)
|
||
|
az1 = abs(z1)
|
||
|
if z2 and az1 > 1:
|
||
|
return diff/az1 <= tol
|
||
|
else:
|
||
|
return diff <= tol
|
||
|
|
||
|
|
||
|
def mpf_norm(mpf, prec):
|
||
|
"""Return the mpf tuple normalized appropriately for the indicated
|
||
|
precision after doing a check to see if zero should be returned or
|
||
|
not when the mantissa is 0. ``mpf_normlize`` always assumes that this
|
||
|
is zero, but it may not be since the mantissa for mpf's values "+inf",
|
||
|
"-inf" and "nan" have a mantissa of zero, too.
|
||
|
|
||
|
Note: this is not intended to validate a given mpf tuple, so sending
|
||
|
mpf tuples that were not created by mpmath may produce bad results. This
|
||
|
is only a wrapper to ``mpf_normalize`` which provides the check for non-
|
||
|
zero mpfs that have a 0 for the mantissa.
|
||
|
"""
|
||
|
sign, man, expt, bc = mpf
|
||
|
if not man:
|
||
|
# hack for mpf_normalize which does not do this;
|
||
|
# it assumes that if man is zero the result is 0
|
||
|
# (see issue 6639)
|
||
|
if not bc:
|
||
|
return fzero
|
||
|
else:
|
||
|
# don't change anything; this should already
|
||
|
# be a well formed mpf tuple
|
||
|
return mpf
|
||
|
|
||
|
# Necessary if mpmath is using the gmpy backend
|
||
|
from mpmath.libmp.backend import MPZ
|
||
|
rv = mpf_normalize(sign, MPZ(man), expt, bc, prec, rnd)
|
||
|
return rv
|
||
|
|
||
|
# TODO: we should use the warnings module
|
||
|
_errdict = {"divide": False}
|
||
|
|
||
|
|
||
|
def seterr(divide=False):
|
||
|
"""
|
||
|
Should SymPy raise an exception on 0/0 or return a nan?
|
||
|
|
||
|
divide == True .... raise an exception
|
||
|
divide == False ... return nan
|
||
|
"""
|
||
|
if _errdict["divide"] != divide:
|
||
|
clear_cache()
|
||
|
_errdict["divide"] = divide
|
||
|
|
||
|
|
||
|
def _as_integer_ratio(p):
|
||
|
neg_pow, man, expt, _ = getattr(p, '_mpf_', mpmath.mpf(p)._mpf_)
|
||
|
p = [1, -1][neg_pow % 2]*man
|
||
|
if expt < 0:
|
||
|
q = 2**-expt
|
||
|
else:
|
||
|
q = 1
|
||
|
p *= 2**expt
|
||
|
return int(p), int(q)
|
||
|
|
||
|
|
||
|
def _decimal_to_Rational_prec(dec):
|
||
|
"""Convert an ordinary decimal instance to a Rational."""
|
||
|
if not dec.is_finite():
|
||
|
raise TypeError("dec must be finite, got %s." % dec)
|
||
|
s, d, e = dec.as_tuple()
|
||
|
prec = len(d)
|
||
|
if e >= 0: # it's an integer
|
||
|
rv = Integer(int(dec))
|
||
|
else:
|
||
|
s = (-1)**s
|
||
|
d = sum([di*10**i for i, di in enumerate(reversed(d))])
|
||
|
rv = Rational(s*d, 10**-e)
|
||
|
return rv, prec
|
||
|
|
||
|
|
||
|
_floatpat = regex.compile(r"[-+]?((\d*\.\d+)|(\d+\.?))")
|
||
|
def _literal_float(f):
|
||
|
"""Return True if n starts like a floating point number."""
|
||
|
return bool(_floatpat.match(f))
|
||
|
|
||
|
# (a,b) -> gcd(a,b)
|
||
|
|
||
|
# TODO caching with decorator, but not to degrade performance
|
||
|
|
||
|
@lru_cache(1024)
|
||
|
def igcd(*args):
|
||
|
"""Computes nonnegative integer greatest common divisor.
|
||
|
|
||
|
Explanation
|
||
|
===========
|
||
|
|
||
|
The algorithm is based on the well known Euclid's algorithm [1]_. To
|
||
|
improve speed, ``igcd()`` has its own caching mechanism.
|
||
|
|
||
|
Examples
|
||
|
========
|
||
|
|
||
|
>>> from sympy import igcd
|
||
|
>>> igcd(2, 4)
|
||
|
2
|
||
|
>>> igcd(5, 10, 15)
|
||
|
5
|
||
|
|
||
|
References
|
||
|
==========
|
||
|
|
||
|
.. [1] https://en.wikipedia.org/wiki/Euclidean_algorithm
|
||
|
|
||
|
"""
|
||
|
if len(args) < 2:
|
||
|
raise TypeError(
|
||
|
'igcd() takes at least 2 arguments (%s given)' % len(args))
|
||
|
args_temp = [abs(as_int(i)) for i in args]
|
||
|
if 1 in args_temp:
|
||
|
return 1
|
||
|
a = args_temp.pop()
|
||
|
if HAS_GMPY: # Using gmpy if present to speed up.
|
||
|
for b in args_temp:
|
||
|
a = gmpy.gcd(a, b) if b else a
|
||
|
return as_int(a)
|
||
|
for b in args_temp:
|
||
|
a = math.gcd(a, b)
|
||
|
return a
|
||
|
|
||
|
|
||
|
igcd2 = math.gcd
|
||
|
|
||
|
|
||
|
def igcd_lehmer(a, b):
|
||
|
r"""Computes greatest common divisor of two integers.
|
||
|
|
||
|
Explanation
|
||
|
===========
|
||
|
|
||
|
Euclid's algorithm for the computation of the greatest
|
||
|
common divisor ``gcd(a, b)`` of two (positive) integers
|
||
|
$a$ and $b$ is based on the division identity
|
||
|
$$ a = q \times b + r$$,
|
||
|
where the quotient $q$ and the remainder $r$ are integers
|
||
|
and $0 \le r < b$. Then each common divisor of $a$ and $b$
|
||
|
divides $r$, and it follows that ``gcd(a, b) == gcd(b, r)``.
|
||
|
The algorithm works by constructing the sequence
|
||
|
r0, r1, r2, ..., where r0 = a, r1 = b, and each rn
|
||
|
is the remainder from the division of the two preceding
|
||
|
elements.
|
||
|
|
||
|
In Python, ``q = a // b`` and ``r = a % b`` are obtained by the
|
||
|
floor division and the remainder operations, respectively.
|
||
|
These are the most expensive arithmetic operations, especially
|
||
|
for large a and b.
|
||
|
|
||
|
Lehmer's algorithm [1]_ is based on the observation that the quotients
|
||
|
``qn = r(n-1) // rn`` are in general small integers even
|
||
|
when a and b are very large. Hence the quotients can be
|
||
|
usually determined from a relatively small number of most
|
||
|
significant bits.
|
||
|
|
||
|
The efficiency of the algorithm is further enhanced by not
|
||
|
computing each long remainder in Euclid's sequence. The remainders
|
||
|
are linear combinations of a and b with integer coefficients
|
||
|
derived from the quotients. The coefficients can be computed
|
||
|
as far as the quotients can be determined from the chosen
|
||
|
most significant parts of a and b. Only then a new pair of
|
||
|
consecutive remainders is computed and the algorithm starts
|
||
|
anew with this pair.
|
||
|
|
||
|
References
|
||
|
==========
|
||
|
|
||
|
.. [1] https://en.wikipedia.org/wiki/Lehmer%27s_GCD_algorithm
|
||
|
|
||
|
"""
|
||
|
a, b = abs(as_int(a)), abs(as_int(b))
|
||
|
if a < b:
|
||
|
a, b = b, a
|
||
|
|
||
|
# The algorithm works by using one or two digit division
|
||
|
# whenever possible. The outer loop will replace the
|
||
|
# pair (a, b) with a pair of shorter consecutive elements
|
||
|
# of the Euclidean gcd sequence until a and b
|
||
|
# fit into two Python (long) int digits.
|
||
|
nbits = 2*sys.int_info.bits_per_digit
|
||
|
|
||
|
while a.bit_length() > nbits and b != 0:
|
||
|
# Quotients are mostly small integers that can
|
||
|
# be determined from most significant bits.
|
||
|
n = a.bit_length() - nbits
|
||
|
x, y = int(a >> n), int(b >> n) # most significant bits
|
||
|
|
||
|
# Elements of the Euclidean gcd sequence are linear
|
||
|
# combinations of a and b with integer coefficients.
|
||
|
# Compute the coefficients of consecutive pairs
|
||
|
# a' = A*a + B*b, b' = C*a + D*b
|
||
|
# using small integer arithmetic as far as possible.
|
||
|
A, B, C, D = 1, 0, 0, 1 # initial values
|
||
|
|
||
|
while True:
|
||
|
# The coefficients alternate in sign while looping.
|
||
|
# The inner loop combines two steps to keep track
|
||
|
# of the signs.
|
||
|
|
||
|
# At this point we have
|
||
|
# A > 0, B <= 0, C <= 0, D > 0,
|
||
|
# x' = x + B <= x < x" = x + A,
|
||
|
# y' = y + C <= y < y" = y + D,
|
||
|
# and
|
||
|
# x'*N <= a' < x"*N, y'*N <= b' < y"*N,
|
||
|
# where N = 2**n.
|
||
|
|
||
|
# Now, if y' > 0, and x"//y' and x'//y" agree,
|
||
|
# then their common value is equal to q = a'//b'.
|
||
|
# In addition,
|
||
|
# x'%y" = x' - q*y" < x" - q*y' = x"%y',
|
||
|
# and
|
||
|
# (x'%y")*N < a'%b' < (x"%y')*N.
|
||
|
|
||
|
# On the other hand, we also have x//y == q,
|
||
|
# and therefore
|
||
|
# x'%y" = x + B - q*(y + D) = x%y + B',
|
||
|
# x"%y' = x + A - q*(y + C) = x%y + A',
|
||
|
# where
|
||
|
# B' = B - q*D < 0, A' = A - q*C > 0.
|
||
|
|
||
|
if y + C <= 0:
|
||
|
break
|
||
|
q = (x + A) // (y + C)
|
||
|
|
||
|
# Now x'//y" <= q, and equality holds if
|
||
|
# x' - q*y" = (x - q*y) + (B - q*D) >= 0.
|
||
|
# This is a minor optimization to avoid division.
|
||
|
x_qy, B_qD = x - q*y, B - q*D
|
||
|
if x_qy + B_qD < 0:
|
||
|
break
|
||
|
|
||
|
# Next step in the Euclidean sequence.
|
||
|
x, y = y, x_qy
|
||
|
A, B, C, D = C, D, A - q*C, B_qD
|
||
|
|
||
|
# At this point the signs of the coefficients
|
||
|
# change and their roles are interchanged.
|
||
|
# A <= 0, B > 0, C > 0, D < 0,
|
||
|
# x' = x + A <= x < x" = x + B,
|
||
|
# y' = y + D < y < y" = y + C.
|
||
|
|
||
|
if y + D <= 0:
|
||
|
break
|
||
|
q = (x + B) // (y + D)
|
||
|
x_qy, A_qC = x - q*y, A - q*C
|
||
|
if x_qy + A_qC < 0:
|
||
|
break
|
||
|
|
||
|
x, y = y, x_qy
|
||
|
A, B, C, D = C, D, A_qC, B - q*D
|
||
|
# Now the conditions on top of the loop
|
||
|
# are again satisfied.
|
||
|
# A > 0, B < 0, C < 0, D > 0.
|
||
|
|
||
|
if B == 0:
|
||
|
# This can only happen when y == 0 in the beginning
|
||
|
# and the inner loop does nothing.
|
||
|
# Long division is forced.
|
||
|
a, b = b, a % b
|
||
|
continue
|
||
|
|
||
|
# Compute new long arguments using the coefficients.
|
||
|
a, b = A*a + B*b, C*a + D*b
|
||
|
|
||
|
# Small divisors. Finish with the standard algorithm.
|
||
|
while b:
|
||
|
a, b = b, a % b
|
||
|
|
||
|
return a
|
||
|
|
||
|
|
||
|
def ilcm(*args):
|
||
|
"""Computes integer least common multiple.
|
||
|
|
||
|
Examples
|
||
|
========
|
||
|
|
||
|
>>> from sympy import ilcm
|
||
|
>>> ilcm(5, 10)
|
||
|
10
|
||
|
>>> ilcm(7, 3)
|
||
|
21
|
||
|
>>> ilcm(5, 10, 15)
|
||
|
30
|
||
|
|
||
|
"""
|
||
|
if len(args) < 2:
|
||
|
raise TypeError(
|
||
|
'ilcm() takes at least 2 arguments (%s given)' % len(args))
|
||
|
if 0 in args:
|
||
|
return 0
|
||
|
a = args[0]
|
||
|
for b in args[1:]:
|
||
|
a = a // igcd(a, b) * b # since gcd(a,b) | a
|
||
|
return a
|
||
|
|
||
|
|
||
|
def igcdex(a, b):
|
||
|
"""Returns x, y, g such that g = x*a + y*b = gcd(a, b).
|
||
|
|
||
|
Examples
|
||
|
========
|
||
|
|
||
|
>>> from sympy.core.numbers import igcdex
|
||
|
>>> igcdex(2, 3)
|
||
|
(-1, 1, 1)
|
||
|
>>> igcdex(10, 12)
|
||
|
(-1, 1, 2)
|
||
|
|
||
|
>>> x, y, g = igcdex(100, 2004)
|
||
|
>>> x, y, g
|
||
|
(-20, 1, 4)
|
||
|
>>> x*100 + y*2004
|
||
|
4
|
||
|
|
||
|
"""
|
||
|
if (not a) and (not b):
|
||
|
return (0, 1, 0)
|
||
|
|
||
|
if not a:
|
||
|
return (0, b//abs(b), abs(b))
|
||
|
if not b:
|
||
|
return (a//abs(a), 0, abs(a))
|
||
|
|
||
|
if a < 0:
|
||
|
a, x_sign = -a, -1
|
||
|
else:
|
||
|
x_sign = 1
|
||
|
|
||
|
if b < 0:
|
||
|
b, y_sign = -b, -1
|
||
|
else:
|
||
|
y_sign = 1
|
||
|
|
||
|
x, y, r, s = 1, 0, 0, 1
|
||
|
|
||
|
while b:
|
||
|
(c, q) = (a % b, a // b)
|
||
|
(a, b, r, s, x, y) = (b, c, x - q*r, y - q*s, r, s)
|
||
|
|
||
|
return (x*x_sign, y*y_sign, a)
|
||
|
|
||
|
|
||
|
def mod_inverse(a, m):
|
||
|
r"""
|
||
|
Return the number $c$ such that, $a \times c = 1 \pmod{m}$
|
||
|
where $c$ has the same sign as $m$. If no such value exists,
|
||
|
a ValueError is raised.
|
||
|
|
||
|
Examples
|
||
|
========
|
||
|
|
||
|
>>> from sympy import mod_inverse, S
|
||
|
|
||
|
Suppose we wish to find multiplicative inverse $x$ of
|
||
|
3 modulo 11. This is the same as finding $x$ such
|
||
|
that $3x = 1 \pmod{11}$. One value of x that satisfies
|
||
|
this congruence is 4. Because $3 \times 4 = 12$ and $12 = 1 \pmod{11}$.
|
||
|
This is the value returned by ``mod_inverse``:
|
||
|
|
||
|
>>> mod_inverse(3, 11)
|
||
|
4
|
||
|
>>> mod_inverse(-3, 11)
|
||
|
7
|
||
|
|
||
|
When there is a common factor between the numerators of
|
||
|
`a` and `m` the inverse does not exist:
|
||
|
|
||
|
>>> mod_inverse(2, 4)
|
||
|
Traceback (most recent call last):
|
||
|
...
|
||
|
ValueError: inverse of 2 mod 4 does not exist
|
||
|
|
||
|
>>> mod_inverse(S(2)/7, S(5)/2)
|
||
|
7/2
|
||
|
|
||
|
References
|
||
|
==========
|
||
|
|
||
|
.. [1] https://en.wikipedia.org/wiki/Modular_multiplicative_inverse
|
||
|
.. [2] https://en.wikipedia.org/wiki/Extended_Euclidean_algorithm
|
||
|
"""
|
||
|
c = None
|
||
|
try:
|
||
|
a, m = as_int(a), as_int(m)
|
||
|
if m != 1 and m != -1:
|
||
|
x, _, g = igcdex(a, m)
|
||
|
if g == 1:
|
||
|
c = x % m
|
||
|
except ValueError:
|
||
|
a, m = sympify(a), sympify(m)
|
||
|
if not (a.is_number and m.is_number):
|
||
|
raise TypeError(filldedent('''
|
||
|
Expected numbers for arguments; symbolic `mod_inverse`
|
||
|
is not implemented
|
||
|
but symbolic expressions can be handled with the
|
||
|
similar function,
|
||
|
sympy.polys.polytools.invert'''))
|
||
|
big = (m > 1)
|
||
|
if big not in (S.true, S.false):
|
||
|
raise ValueError('m > 1 did not evaluate; try to simplify %s' % m)
|
||
|
elif big:
|
||
|
c = 1/a
|
||
|
if c is None:
|
||
|
raise ValueError('inverse of %s (mod %s) does not exist' % (a, m))
|
||
|
return c
|
||
|
|
||
|
|
||
|
class Number(AtomicExpr):
|
||
|
"""Represents atomic numbers in SymPy.
|
||
|
|
||
|
Explanation
|
||
|
===========
|
||
|
|
||
|
Floating point numbers are represented by the Float class.
|
||
|
Rational numbers (of any size) are represented by the Rational class.
|
||
|
Integer numbers (of any size) are represented by the Integer class.
|
||
|
Float and Rational are subclasses of Number; Integer is a subclass
|
||
|
of Rational.
|
||
|
|
||
|
For example, ``2/3`` is represented as ``Rational(2, 3)`` which is
|
||
|
a different object from the floating point number obtained with
|
||
|
Python division ``2/3``. Even for numbers that are exactly
|
||
|
represented in binary, there is a difference between how two forms,
|
||
|
such as ``Rational(1, 2)`` and ``Float(0.5)``, are used in SymPy.
|
||
|
The rational form is to be preferred in symbolic computations.
|
||
|
|
||
|
Other kinds of numbers, such as algebraic numbers ``sqrt(2)`` or
|
||
|
complex numbers ``3 + 4*I``, are not instances of Number class as
|
||
|
they are not atomic.
|
||
|
|
||
|
See Also
|
||
|
========
|
||
|
|
||
|
Float, Integer, Rational
|
||
|
"""
|
||
|
is_commutative = True
|
||
|
is_number = True
|
||
|
is_Number = True
|
||
|
|
||
|
__slots__ = ()
|
||
|
|
||
|
# Used to make max(x._prec, y._prec) return x._prec when only x is a float
|
||
|
_prec = -1
|
||
|
|
||
|
kind = NumberKind
|
||
|
|
||
|
def __new__(cls, *obj):
|
||
|
if len(obj) == 1:
|
||
|
obj = obj[0]
|
||
|
|
||
|
if isinstance(obj, Number):
|
||
|
return obj
|
||
|
if isinstance(obj, SYMPY_INTS):
|
||
|
return Integer(obj)
|
||
|
if isinstance(obj, tuple) and len(obj) == 2:
|
||
|
return Rational(*obj)
|
||
|
if isinstance(obj, (float, mpmath.mpf, decimal.Decimal)):
|
||
|
return Float(obj)
|
||
|
if isinstance(obj, str):
|
||
|
_obj = obj.lower() # float('INF') == float('inf')
|
||
|
if _obj == 'nan':
|
||
|
return S.NaN
|
||
|
elif _obj == 'inf':
|
||
|
return S.Infinity
|
||
|
elif _obj == '+inf':
|
||
|
return S.Infinity
|
||
|
elif _obj == '-inf':
|
||
|
return S.NegativeInfinity
|
||
|
val = sympify(obj)
|
||
|
if isinstance(val, Number):
|
||
|
return val
|
||
|
else:
|
||
|
raise ValueError('String "%s" does not denote a Number' % obj)
|
||
|
msg = "expected str|int|long|float|Decimal|Number object but got %r"
|
||
|
raise TypeError(msg % type(obj).__name__)
|
||
|
|
||
|
def could_extract_minus_sign(self):
|
||
|
return bool(self.is_extended_negative)
|
||
|
|
||
|
def invert(self, other, *gens, **args):
|
||
|
from sympy.polys.polytools import invert
|
||
|
if getattr(other, 'is_number', True):
|
||
|
return mod_inverse(self, other)
|
||
|
return invert(self, other, *gens, **args)
|
||
|
|
||
|
def __divmod__(self, other):
|
||
|
from sympy.functions.elementary.complexes import sign
|
||
|
|
||
|
try:
|
||
|
other = Number(other)
|
||
|
if self.is_infinite or S.NaN in (self, other):
|
||
|
return (S.NaN, S.NaN)
|
||
|
except TypeError:
|
||
|
return NotImplemented
|
||
|
if not other:
|
||
|
raise ZeroDivisionError('modulo by zero')
|
||
|
if self.is_Integer and other.is_Integer:
|
||
|
return Tuple(*divmod(self.p, other.p))
|
||
|
elif isinstance(other, Float):
|
||
|
rat = self/Rational(other)
|
||
|
else:
|
||
|
rat = self/other
|
||
|
if other.is_finite:
|
||
|
w = int(rat) if rat >= 0 else int(rat) - 1
|
||
|
r = self - other*w
|
||
|
else:
|
||
|
w = 0 if not self or (sign(self) == sign(other)) else -1
|
||
|
r = other if w else self
|
||
|
return Tuple(w, r)
|
||
|
|
||
|
def __rdivmod__(self, other):
|
||
|
try:
|
||
|
other = Number(other)
|
||
|
except TypeError:
|
||
|
return NotImplemented
|
||
|
return divmod(other, self)
|
||
|
|
||
|
def _as_mpf_val(self, prec):
|
||
|
"""Evaluation of mpf tuple accurate to at least prec bits."""
|
||
|
raise NotImplementedError('%s needs ._as_mpf_val() method' %
|
||
|
(self.__class__.__name__))
|
||
|
|
||
|
def _eval_evalf(self, prec):
|
||
|
return Float._new(self._as_mpf_val(prec), prec)
|
||
|
|
||
|
def _as_mpf_op(self, prec):
|
||
|
prec = max(prec, self._prec)
|
||
|
return self._as_mpf_val(prec), prec
|
||
|
|
||
|
def __float__(self):
|
||
|
return mlib.to_float(self._as_mpf_val(53))
|
||
|
|
||
|
def floor(self):
|
||
|
raise NotImplementedError('%s needs .floor() method' %
|
||
|
(self.__class__.__name__))
|
||
|
|
||
|
def ceiling(self):
|
||
|
raise NotImplementedError('%s needs .ceiling() method' %
|
||
|
(self.__class__.__name__))
|
||
|
|
||
|
def __floor__(self):
|
||
|
return self.floor()
|
||
|
|
||
|
def __ceil__(self):
|
||
|
return self.ceiling()
|
||
|
|
||
|
def _eval_conjugate(self):
|
||
|
return self
|
||
|
|
||
|
def _eval_order(self, *symbols):
|
||
|
from sympy.series.order import Order
|
||
|
# Order(5, x, y) -> Order(1,x,y)
|
||
|
return Order(S.One, *symbols)
|
||
|
|
||
|
def _eval_subs(self, old, new):
|
||
|
if old == -self:
|
||
|
return -new
|
||
|
return self # there is no other possibility
|
||
|
|
||
|
@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):
|
||
|
# a -> c*t
|
||
|
if self.is_Rational or not rational:
|
||
|
return self, ()
|
||
|
elif self.is_negative:
|
||
|
return S.NegativeOne, (-self,)
|
||
|
return S.One, (self,)
|
||
|
|
||
|
def as_coeff_add(self, *deps):
|
||
|
# a -> c + t
|
||
|
if self.is_Rational:
|
||
|
return self, ()
|
||
|
return S.Zero, (self,)
|
||
|
|
||
|
def as_coeff_Mul(self, rational=False):
|
||
|
"""Efficiently extract the coefficient of a product."""
|
||
|
if rational and not self.is_Rational:
|
||
|
return S.One, self
|
||
|
return (self, S.One) if self else (S.One, self)
|
||
|
|
||
|
def as_coeff_Add(self, rational=False):
|
||
|
"""Efficiently extract the coefficient of a summation."""
|
||
|
if not rational:
|
||
|
return self, S.Zero
|
||
|
return S.Zero, self
|
||
|
|
||
|
def gcd(self, other):
|
||
|
"""Compute GCD of `self` and `other`. """
|
||
|
from sympy.polys.polytools import gcd
|
||
|
return gcd(self, other)
|
||
|
|
||
|
def lcm(self, other):
|
||
|
"""Compute LCM of `self` and `other`. """
|
||
|
from sympy.polys.polytools import lcm
|
||
|
return lcm(self, other)
|
||
|
|
||
|
def cofactors(self, other):
|
||
|
"""Compute GCD and cofactors of `self` and `other`. """
|
||
|
from sympy.polys.polytools import cofactors
|
||
|
return cofactors(self, other)
|
||
|
|
||
|
|
||
|
class Float(Number):
|
||
|
"""Represent a floating-point number of arbitrary precision.
|
||
|
|
||
|
Examples
|
||
|
========
|
||
|
|
||
|
>>> from sympy import Float
|
||
|
>>> Float(3.5)
|
||
|
3.50000000000000
|
||
|
>>> Float(3)
|
||
|
3.00000000000000
|
||
|
|
||
|
Creating Floats from strings (and Python ``int`` and ``long``
|
||
|
types) will give a minimum precision of 15 digits, but the
|
||
|
precision will automatically increase to capture all digits
|
||
|
entered.
|
||
|
|
||
|
>>> Float(1)
|
||
|
1.00000000000000
|
||
|
>>> Float(10**20)
|
||
|
100000000000000000000.
|
||
|
>>> Float('1e20')
|
||
|
100000000000000000000.
|
||
|
|
||
|
However, *floating-point* numbers (Python ``float`` types) retain
|
||
|
only 15 digits of precision:
|
||
|
|
||
|
>>> Float(1e20)
|
||
|
1.00000000000000e+20
|
||
|
>>> Float(1.23456789123456789)
|
||
|
1.23456789123457
|
||
|
|
||
|
It may be preferable to enter high-precision decimal numbers
|
||
|
as strings:
|
||
|
|
||
|
>>> Float('1.23456789123456789')
|
||
|
1.23456789123456789
|
||
|
|
||
|
The desired number of digits can also be specified:
|
||
|
|
||
|
>>> Float('1e-3', 3)
|
||
|
0.00100
|
||
|
>>> Float(100, 4)
|
||
|
100.0
|
||
|
|
||
|
Float can automatically count significant figures if a null string
|
||
|
is sent for the precision; spaces or underscores are also allowed. (Auto-
|
||
|
counting is only allowed for strings, ints and longs).
|
||
|
|
||
|
>>> Float('123 456 789.123_456', '')
|
||
|
123456789.123456
|
||
|
>>> Float('12e-3', '')
|
||
|
0.012
|
||
|
>>> Float(3, '')
|
||
|
3.
|
||
|
|
||
|
If a number is written in scientific notation, only the digits before the
|
||
|
exponent are considered significant if a decimal appears, otherwise the
|
||
|
"e" signifies only how to move the decimal:
|
||
|
|
||
|
>>> Float('60.e2', '') # 2 digits significant
|
||
|
6.0e+3
|
||
|
>>> Float('60e2', '') # 4 digits significant
|
||
|
6000.
|
||
|
>>> Float('600e-2', '') # 3 digits significant
|
||
|
6.00
|
||
|
|
||
|
Notes
|
||
|
=====
|
||
|
|
||
|
Floats are inexact by their nature unless their value is a binary-exact
|
||
|
value.
|
||
|
|
||
|
>>> approx, exact = Float(.1, 1), Float(.125, 1)
|
||
|
|
||
|
For calculation purposes, evalf needs to be able to change the precision
|
||
|
but this will not increase the accuracy of the inexact value. The
|
||
|
following is the most accurate 5-digit approximation of a value of 0.1
|
||
|
that had only 1 digit of precision:
|
||
|
|
||
|
>>> approx.evalf(5)
|
||
|
0.099609
|
||
|
|
||
|
By contrast, 0.125 is exact in binary (as it is in base 10) and so it
|
||
|
can be passed to Float or evalf to obtain an arbitrary precision with
|
||
|
matching accuracy:
|
||
|
|
||
|
>>> Float(exact, 5)
|
||
|
0.12500
|
||
|
>>> exact.evalf(20)
|
||
|
0.12500000000000000000
|
||
|
|
||
|
Trying to make a high-precision Float from a float is not disallowed,
|
||
|
but one must keep in mind that the *underlying float* (not the apparent
|
||
|
decimal value) is being obtained with high precision. For example, 0.3
|
||
|
does not have a finite binary representation. The closest rational is
|
||
|
the fraction 5404319552844595/2**54. So if you try to obtain a Float of
|
||
|
0.3 to 20 digits of precision you will not see the same thing as 0.3
|
||
|
followed by 19 zeros:
|
||
|
|
||
|
>>> Float(0.3, 20)
|
||
|
0.29999999999999998890
|
||
|
|
||
|
If you want a 20-digit value of the decimal 0.3 (not the floating point
|
||
|
approximation of 0.3) you should send the 0.3 as a string. The underlying
|
||
|
representation is still binary but a higher precision than Python's float
|
||
|
is used:
|
||
|
|
||
|
>>> Float('0.3', 20)
|
||
|
0.30000000000000000000
|
||
|
|
||
|
Although you can increase the precision of an existing Float using Float
|
||
|
it will not increase the accuracy -- the underlying value is not changed:
|
||
|
|
||
|
>>> def show(f): # binary rep of Float
|
||
|
... from sympy import Mul, Pow
|
||
|
... s, m, e, b = f._mpf_
|
||
|
... v = Mul(int(m), Pow(2, int(e), evaluate=False), evaluate=False)
|
||
|
... print('%s at prec=%s' % (v, f._prec))
|
||
|
...
|
||
|
>>> t = Float('0.3', 3)
|
||
|
>>> show(t)
|
||
|
4915/2**14 at prec=13
|
||
|
>>> show(Float(t, 20)) # higher prec, not higher accuracy
|
||
|
4915/2**14 at prec=70
|
||
|
>>> show(Float(t, 2)) # lower prec
|
||
|
307/2**10 at prec=10
|
||
|
|
||
|
The same thing happens when evalf is used on a Float:
|
||
|
|
||
|
>>> show(t.evalf(20))
|
||
|
4915/2**14 at prec=70
|
||
|
>>> show(t.evalf(2))
|
||
|
307/2**10 at prec=10
|
||
|
|
||
|
Finally, Floats can be instantiated with an mpf tuple (n, c, p) to
|
||
|
produce the number (-1)**n*c*2**p:
|
||
|
|
||
|
>>> n, c, p = 1, 5, 0
|
||
|
>>> (-1)**n*c*2**p
|
||
|
-5
|
||
|
>>> Float((1, 5, 0))
|
||
|
-5.00000000000000
|
||
|
|
||
|
An actual mpf tuple also contains the number of bits in c as the last
|
||
|
element of the tuple:
|
||
|
|
||
|
>>> _._mpf_
|
||
|
(1, 5, 0, 3)
|
||
|
|
||
|
This is not needed for instantiation and is not the same thing as the
|
||
|
precision. The mpf tuple and the precision are two separate quantities
|
||
|
that Float tracks.
|
||
|
|
||
|
In SymPy, a Float is a number that can be computed with arbitrary
|
||
|
precision. Although floating point 'inf' and 'nan' are not such
|
||
|
numbers, Float can create these numbers:
|
||
|
|
||
|
>>> Float('-inf')
|
||
|
-oo
|
||
|
>>> _.is_Float
|
||
|
False
|
||
|
|
||
|
Zero in Float only has a single value. Values are not separate for
|
||
|
positive and negative zeroes.
|
||
|
"""
|
||
|
__slots__ = ('_mpf_', '_prec')
|
||
|
|
||
|
_mpf_: tuple[int, int, int, int]
|
||
|
|
||
|
# A Float represents many real numbers,
|
||
|
# both rational and irrational.
|
||
|
is_rational = None
|
||
|
is_irrational = None
|
||
|
is_number = True
|
||
|
|
||
|
is_real = True
|
||
|
is_extended_real = True
|
||
|
|
||
|
is_Float = True
|
||
|
|
||
|
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):
|
||
|
# Float accepts spaces as digit separators
|
||
|
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 isinstance(num, float) and num == 0:
|
||
|
num = '0'
|
||
|
elif isinstance(num, float) and num == float('inf'):
|
||
|
return S.Infinity
|
||
|
elif isinstance(num, float) and num == float('-inf'):
|
||
|
return S.NegativeInfinity
|
||
|
elif isinstance(num, float) and math.isnan(num):
|
||
|
return S.NaN
|
||
|
elif isinstance(num, (SYMPY_INTS, Integer)):
|
||
|
num = str(num)
|
||
|
elif num is S.Infinity:
|
||
|
return num
|
||
|
elif num is S.NegativeInfinity:
|
||
|
return num
|
||
|
elif num is S.NaN:
|
||
|
return num
|
||
|
elif _is_numpy_instance(num): # support for numpy datatypes
|
||
|
num = _convert_numpy_types(num)
|
||
|
elif isinstance(num, mpmath.mpf):
|
||
|
if precision is None:
|
||
|
if dps is None:
|
||
|
precision = num.context.prec
|
||
|
num = num._mpf_
|
||
|
|
||
|
if dps is None and precision is None:
|
||
|
dps = 15
|
||
|
if isinstance(num, Float):
|
||
|
return num
|
||
|
if isinstance(num, str) and _literal_float(num):
|
||
|
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, len(str(num).lstrip('-')))
|
||
|
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.')
|
||
|
ok = None
|
||
|
if _literal_float(num):
|
||
|
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, len(str(num).lstrip('-')))
|
||
|
precision = dps_to_prec(dps)
|
||
|
ok = True
|
||
|
if ok is None:
|
||
|
raise ValueError('string-float not recognized: %s' % num)
|
||
|
|
||
|
# decimal precision(dps) is set and maybe binary precision(precision)
|
||
|
# as well.From here on binary precision is used to compute the Float.
|
||
|
# Hence, if supplied use binary precision else translate from decimal
|
||
|
# precision.
|
||
|
|
||
|
if precision is None or precision == '':
|
||
|
precision = dps_to_prec(dps)
|
||
|
|
||
|
precision = int(precision)
|
||
|
|
||
|
if isinstance(num, float):
|
||
|
_mpf_ = mlib.from_float(num, precision, rnd)
|
||
|
elif isinstance(num, str):
|
||
|
_mpf_ = mlib.from_str(num, precision, rnd)
|
||
|
elif isinstance(num, decimal.Decimal):
|
||
|
if num.is_finite():
|
||
|
_mpf_ = mlib.from_str(str(num), precision, rnd)
|
||
|
elif num.is_nan():
|
||
|
return S.NaN
|
||
|
elif num.is_infinite():
|
||
|
if num > 0:
|
||
|
return S.Infinity
|
||
|
return S.NegativeInfinity
|
||
|
else:
|
||
|
raise ValueError("unexpected decimal value %s" % str(num))
|
||
|
elif isinstance(num, tuple) and len(num) in (3, 4):
|
||
|
if isinstance(num[1], str):
|
||
|
# it's a hexadecimal (coming from a pickled object)
|
||
|
num = list(num)
|
||
|
# If we're loading an object pickled in Python 2 into
|
||
|
# Python 3, we may need to strip a tailing 'L' because
|
||
|
# of a shim for int on Python 3, see issue #13470.
|
||
|
if num[1].endswith('L'):
|
||
|
num[1] = num[1][:-1]
|
||
|
# Strip leading '0x' - gmpy2 only documents such inputs
|
||
|
# with base prefix as valid when the 2nd argument (base) is 0.
|
||
|
# When mpmath uses Sage as the backend, however, it
|
||
|
# ends up including '0x' when preparing the picklable tuple.
|
||
|
# See issue #19690.
|
||
|
if num[1].startswith('0x'):
|
||
|
num[1] = num[1][2:]
|
||
|
# Now we can assume that it is in standard form
|
||
|
num[1] = MPZ(num[1], 16)
|
||
|
_mpf_ = tuple(num)
|
||
|
else:
|
||
|
if len(num) == 4:
|
||
|
# handle normalization hack
|
||
|
return Float._new(num, precision)
|
||
|
else:
|
||
|
if not all((
|
||
|
num[0] in (0, 1),
|
||
|
num[1] >= 0,
|
||
|
all(type(i) in (int, int) for i in num)
|
||
|
)):
|
||
|
raise ValueError('malformed mpf: %s' % (num,))
|
||
|
# don't compute number or else it may
|
||
|
# over/underflow
|
||
|
return Float._new(
|
||
|
(num[0], num[1], num[2], bitcount(num[1])),
|
||
|
precision)
|
||
|
else:
|
||
|
try:
|
||
|
_mpf_ = num._as_mpf_val(precision)
|
||
|
except (NotImplementedError, AttributeError):
|
||
|
_mpf_ = mpmath.mpf(num, prec=precision)._mpf_
|
||
|
|
||
|
return cls._new(_mpf_, precision, zero=False)
|
||
|
|
||
|
@classmethod
|
||
|
def _new(cls, _mpf_, _prec, zero=True):
|
||
|
# special cases
|
||
|
if zero and _mpf_ == fzero:
|
||
|
return S.Zero # Float(0) -> 0.0; Float._new((0,0,0,0)) -> 0
|
||
|
elif _mpf_ == _mpf_nan:
|
||
|
return S.NaN
|
||
|
elif _mpf_ == _mpf_inf:
|
||
|
return S.Infinity
|
||
|
elif _mpf_ == _mpf_ninf:
|
||
|
return S.NegativeInfinity
|
||
|
|
||
|
obj = Expr.__new__(cls)
|
||
|
obj._mpf_ = mpf_norm(_mpf_, _prec)
|
||
|
obj._prec = _prec
|
||
|
return obj
|
||
|
|
||
|
# mpz can't be pickled
|
||
|
def __getnewargs_ex__(self):
|
||
|
return ((mlib.to_pickable(self._mpf_),), {'precision': self._prec})
|
||
|
|
||
|
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):
|
||
|
return self._mpf_ == fzero
|
||
|
|
||
|
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:
|
||
|
# calculate mod with Rationals, *then* round the result
|
||
|
return Float(Rational.__mod__(Rational(self), other),
|
||
|
precision=self._prec)
|
||
|
if isinstance(other, Float) and global_parameters.evaluate:
|
||
|
r = self/other
|
||
|
if r == int(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_)) # uses round_fast = round_down
|
||
|
|
||
|
def __eq__(self, other):
|
||
|
from sympy.logic.boolalg import Boolean
|
||
|
try:
|
||
|
other = _sympify(other)
|
||
|
except SympifyError:
|
||
|
return NotImplemented
|
||
|
if isinstance(other, Boolean):
|
||
|
return False
|
||
|
if other.is_NumberSymbol:
|
||
|
if other.is_irrational:
|
||
|
return False
|
||
|
return other.__eq__(self)
|
||
|
if other.is_Float:
|
||
|
# comparison is exact
|
||
|
# so Float(.1, 3) != Float(.1, 33)
|
||
|
return self._mpf_ == other._mpf_
|
||
|
if other.is_Rational:
|
||
|
return other.__eq__(self)
|
||
|
if other.is_Number:
|
||
|
# numbers should compare at the same precision;
|
||
|
# all _as_mpf_val routines should be sure to abide
|
||
|
# by the request to change the prec if necessary; if
|
||
|
# they don't, the equality test will fail since it compares
|
||
|
# the mpf tuples
|
||
|
ompf = other._as_mpf_val(self._prec)
|
||
|
return bool(mlib.mpf_eq(self._mpf_, ompf))
|
||
|
if not self:
|
||
|
return not other
|
||
|
return False # Float != non-Number
|
||
|
|
||
|
def __ne__(self, other):
|
||
|
return not self == other
|
||
|
|
||
|
def _Frel(self, other, op):
|
||
|
try:
|
||
|
other = _sympify(other)
|
||
|
except SympifyError:
|
||
|
return NotImplemented
|
||
|
if other.is_Rational:
|
||
|
# test self*other.q <?> other.p without losing precision
|
||
|
'''
|
||
|
>>> f = Float(.1,2)
|
||
|
>>> i = 1234567890
|
||
|
>>> (f*i)._mpf_
|
||
|
(0, 471, 18, 9)
|
||
|
>>> mlib.mpf_mul(f._mpf_, mlib.from_int(i))
|
||
|
(0, 505555550955, -12, 39)
|
||
|
'''
|
||
|
smpf = mlib.mpf_mul(self._mpf_, mlib.from_int(other.q))
|
||
|
ompf = mlib.from_int(other.p)
|
||
|
return _sympify(bool(op(smpf, ompf)))
|
||
|
elif other.is_Float:
|
||
|
return _sympify(bool(
|
||
|
op(self._mpf_, other._mpf_)))
|
||
|
elif other.is_comparable and other not in (
|
||
|
S.Infinity, S.NegativeInfinity):
|
||
|
other = other.evalf(prec_to_dps(self._prec))
|
||
|
if other._prec > 1:
|
||
|
if other.is_Number:
|
||
|
return _sympify(bool(
|
||
|
op(self._mpf_, other._as_mpf_val(self._prec))))
|
||
|
|
||
|
def __gt__(self, other):
|
||
|
if isinstance(other, NumberSymbol):
|
||
|
return other.__lt__(self)
|
||
|
rv = self._Frel(other, mlib.mpf_gt)
|
||
|
if rv is None:
|
||
|
return Expr.__gt__(self, other)
|
||
|
return rv
|
||
|
|
||
|
def __ge__(self, other):
|
||
|
if isinstance(other, NumberSymbol):
|
||
|
return other.__le__(self)
|
||
|
rv = self._Frel(other, mlib.mpf_ge)
|
||
|
if rv is None:
|
||
|
return Expr.__ge__(self, other)
|
||
|
return rv
|
||
|
|
||
|
def __lt__(self, other):
|
||
|
if isinstance(other, NumberSymbol):
|
||
|
return other.__gt__(self)
|
||
|
rv = self._Frel(other, mlib.mpf_lt)
|
||
|
if rv is None:
|
||
|
return Expr.__lt__(self, other)
|
||
|
return rv
|
||
|
|
||
|
def __le__(self, other):
|
||
|
if isinstance(other, NumberSymbol):
|
||
|
return other.__ge__(self)
|
||
|
rv = self._Frel(other, mlib.mpf_le)
|
||
|
if rv is None:
|
||
|
return Expr.__le__(self, other)
|
||
|
return rv
|
||
|
|
||
|
def __hash__(self):
|
||
|
return super().__hash__()
|
||
|
|
||
|
def epsilon_eq(self, other, epsilon="1e-15"):
|
||
|
return abs(self - other) < Float(epsilon)
|
||
|
|
||
|
def __format__(self, format_spec):
|
||
|
return format(decimal.Decimal(str(self)), format_spec)
|
||
|
|
||
|
|
||
|
# Add sympify converters
|
||
|
_sympy_converter[float] = _sympy_converter[decimal.Decimal] = Float
|
||
|
|
||
|
# this is here to work nicely in Sage
|
||
|
RealNumber = Float
|
||
|
|
||
|
|
||
|
class Rational(Number):
|
||
|
"""Represents rational numbers (p/q) of any size.
|
||
|
|
||
|
Examples
|
||
|
========
|
||
|
|
||
|
>>> from sympy import Rational, nsimplify, S, pi
|
||
|
>>> Rational(1, 2)
|
||
|
1/2
|
||
|
|
||
|
Rational is unprejudiced in accepting input. If a float is passed, the
|
||
|
underlying value of the binary representation will be returned:
|
||
|
|
||
|
>>> Rational(.5)
|
||
|
1/2
|
||
|
>>> Rational(.2)
|
||
|
3602879701896397/18014398509481984
|
||
|
|
||
|
If the simpler representation of the float is desired then consider
|
||
|
limiting the denominator to the desired value or convert the float to
|
||
|
a string (which is roughly equivalent to limiting the denominator to
|
||
|
10**12):
|
||
|
|
||
|
>>> Rational(str(.2))
|
||
|
1/5
|
||
|
>>> Rational(.2).limit_denominator(10**12)
|
||
|
1/5
|
||
|
|
||
|
An arbitrarily precise Rational is obtained when a string literal is
|
||
|
passed:
|
||
|
|
||
|
>>> Rational("1.23")
|
||
|
123/100
|
||
|
>>> Rational('1e-2')
|
||
|
1/100
|
||
|
>>> Rational(".1")
|
||
|
1/10
|
||
|
>>> Rational('1e-2/3.2')
|
||
|
1/320
|
||
|
|
||
|
The conversion of other types of strings can be handled by
|
||
|
the sympify() function, and conversion of floats to expressions
|
||
|
or simple fractions can be handled with nsimplify:
|
||
|
|
||
|
>>> S('.[3]') # repeating digits in brackets
|
||
|
1/3
|
||
|
>>> S('3**2/10') # general expressions
|
||
|
9/10
|
||
|
>>> nsimplify(.3) # numbers that have a simple form
|
||
|
3/10
|
||
|
|
||
|
But if the input does not reduce to a literal Rational, an error will
|
||
|
be raised:
|
||
|
|
||
|
>>> Rational(pi)
|
||
|
Traceback (most recent call last):
|
||
|
...
|
||
|
TypeError: invalid input: pi
|
||
|
|
||
|
|
||
|
Low-level
|
||
|
---------
|
||
|
|
||
|
Access numerator and denominator as .p and .q:
|
||
|
|
||
|
>>> r = Rational(3, 4)
|
||
|
>>> r
|
||
|
3/4
|
||
|
>>> r.p
|
||
|
3
|
||
|
>>> r.q
|
||
|
4
|
||
|
|
||
|
Note that p and q return integers (not SymPy Integers) so some care
|
||
|
is needed when using them in expressions:
|
||
|
|
||
|
>>> r.p/r.q
|
||
|
0.75
|
||
|
|
||
|
If an unevaluated Rational is desired, ``gcd=1`` can be passed and
|
||
|
this will keep common divisors of the numerator and denominator
|
||
|
from being eliminated. It is not possible, however, to leave a
|
||
|
negative value in the denominator.
|
||
|
|
||
|
>>> Rational(2, 4, gcd=1)
|
||
|
2/4
|
||
|
>>> Rational(2, -4, gcd=1).q
|
||
|
4
|
||
|
|
||
|
See Also
|
||
|
========
|
||
|
sympy.core.sympify.sympify, sympy.simplify.simplify.nsimplify
|
||
|
"""
|
||
|
is_real = True
|
||
|
is_integer = False
|
||
|
is_rational = True
|
||
|
is_number = True
|
||
|
|
||
|
__slots__ = ('p', 'q')
|
||
|
|
||
|
p: int
|
||
|
q: int
|
||
|
|
||
|
is_Rational = True
|
||
|
|
||
|
@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 # error will raise below
|
||
|
else:
|
||
|
if p.count('/') > 1:
|
||
|
raise TypeError('invalid input: %s' % p)
|
||
|
p = p.replace(' ', '')
|
||
|
pq = p.rsplit('/', 1)
|
||
|
if len(pq) == 2:
|
||
|
p, q = pq
|
||
|
fp = fractions.Fraction(p)
|
||
|
fq = fractions.Fraction(q)
|
||
|
p = fp/fq
|
||
|
try:
|
||
|
p = fractions.Fraction(p)
|
||
|
except ValueError:
|
||
|
pass # error will raise below
|
||
|
else:
|
||
|
return Rational(p.numerator, p.denominator, 1)
|
||
|
|
||
|
if not isinstance(p, Rational):
|
||
|
raise TypeError('invalid input: %s' % p)
|
||
|
|
||
|
q = 1
|
||
|
gcd = 1
|
||
|
Q = 1
|
||
|
|
||
|
if not isinstance(p, SYMPY_INTS):
|
||
|
p = Rational(p)
|
||
|
Q *= p.q
|
||
|
p = p.p
|
||
|
else:
|
||
|
p = int(p)
|
||
|
|
||
|
if not isinstance(q, SYMPY_INTS):
|
||
|
q = Rational(q)
|
||
|
p *= q.q
|
||
|
Q *= q.p
|
||
|
else:
|
||
|
Q *= int(q)
|
||
|
q = Q
|
||
|
|
||
|
# p and q are now ints
|
||
|
if q == 0:
|
||
|
if p == 0:
|
||
|
if _errdict["divide"]:
|
||
|
raise ValueError("Indeterminate 0/0")
|
||
|
else:
|
||
|
return S.NaN
|
||
|
return S.ComplexInfinity
|
||
|
if q < 0:
|
||
|
q = -q
|
||
|
p = -p
|
||
|
if not gcd:
|
||
|
gcd = igcd(abs(p), q)
|
||
|
if gcd > 1:
|
||
|
p //= gcd
|
||
|
q //= gcd
|
||
|
if q == 1:
|
||
|
return Integer(p)
|
||
|
if p == 1 and q == 2:
|
||
|
return S.Half
|
||
|
obj = Expr.__new__(cls)
|
||
|
obj.p = p
|
||
|
obj.q = q
|
||
|
return obj
|
||
|
|
||
|
def limit_denominator(self, max_denominator=1000000):
|
||
|
"""Closest Rational to self with denominator at most max_denominator.
|
||
|
|
||
|
Examples
|
||
|
========
|
||
|
|
||
|
>>> from sympy import Rational
|
||
|
>>> Rational('3.141592653589793').limit_denominator(10)
|
||
|
22/7
|
||
|
>>> Rational('3.141592653589793').limit_denominator(100)
|
||
|
311/99
|
||
|
|
||
|
"""
|
||
|
f = fractions.Fraction(self.p, self.q)
|
||
|
return Rational(f.limit_denominator(fractions.Fraction(int(max_denominator))))
|
||
|
|
||
|
def __getnewargs__(self):
|
||
|
return (self.p, self.q)
|
||
|
|
||
|
def _hashable_content(self):
|
||
|
return (self.p, self.q)
|
||
|
|
||
|
def _eval_is_positive(self):
|
||
|
return self.p > 0
|
||
|
|
||
|
def _eval_is_zero(self):
|
||
|
return self.p == 0
|
||
|
|
||
|
def __neg__(self):
|
||
|
return Rational(-self.p, self.q)
|
||
|
|
||
|
@_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):
|
||
|
#TODO: this can probably be optimized more
|
||
|
return Rational(self.p*other.q + self.q*other.p, self.q*other.q)
|
||
|
elif isinstance(other, Float):
|
||
|
return other + self
|
||
|
else:
|
||
|
return Number.__add__(self, other)
|
||
|
return Number.__add__(self, other)
|
||
|
__radd__ = __add__
|
||
|
|
||
|
@_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):
|
||
|
# calculate mod with Rationals, *then* round the answer
|
||
|
return Float(self.__mod__(Rational(other)),
|
||
|
precision=other._prec)
|
||
|
return Number.__mod__(self, other)
|
||
|
return Number.__mod__(self, other)
|
||
|
|
||
|
@_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:
|
||
|
# (3/4)**-2 -> (4/3)**2
|
||
|
ne = -expt
|
||
|
if (ne is S.One):
|
||
|
return Rational(self.q, self.p)
|
||
|
if self.is_negative:
|
||
|
return S.NegativeOne**expt*Rational(self.q, -self.p)**ne
|
||
|
else:
|
||
|
return Rational(self.q, self.p)**ne
|
||
|
if expt is S.Infinity: # -oo already caught by test for negative
|
||
|
if self.p > self.q:
|
||
|
# (3/2)**oo -> oo
|
||
|
return S.Infinity
|
||
|
if self.p < -self.q:
|
||
|
# (-3/2)**oo -> oo + I*oo
|
||
|
return S.Infinity + S.Infinity*S.ImaginaryUnit
|
||
|
return S.Zero
|
||
|
if isinstance(expt, Integer):
|
||
|
# (4/3)**2 -> 4**2 / 3**2
|
||
|
return Rational(self.p**expt.p, self.q**expt.p, 1)
|
||
|
if isinstance(expt, Rational):
|
||
|
intpart = expt.p // expt.q
|
||
|
if intpart:
|
||
|
intpart += 1
|
||
|
remfracpart = intpart*expt.q - expt.p
|
||
|
ratfracpart = Rational(remfracpart, expt.q)
|
||
|
if self.p != 1:
|
||
|
return Integer(self.p)**expt*Integer(self.q)**ratfracpart*Rational(1, self.q**intpart, 1)
|
||
|
return Integer(self.q)**ratfracpart*Rational(1, self.q**intpart, 1)
|
||
|
else:
|
||
|
remfracpart = expt.q - expt.p
|
||
|
ratfracpart = Rational(remfracpart, expt.q)
|
||
|
if self.p != 1:
|
||
|
return Integer(self.p)**expt*Integer(self.q)**ratfracpart*Rational(1, self.q, 1)
|
||
|
return Integer(self.q)**ratfracpart*Rational(1, self.q, 1)
|
||
|
|
||
|
if self.is_extended_negative and expt.is_even:
|
||
|
return (-self)**expt
|
||
|
|
||
|
return
|
||
|
|
||
|
def _as_mpf_val(self, prec):
|
||
|
return mlib.from_rational(self.p, self.q, prec, rnd)
|
||
|
|
||
|
def _mpmath_(self, prec, rnd):
|
||
|
return mpmath.make_mpf(mlib.from_rational(self.p, self.q, prec, rnd))
|
||
|
|
||
|
def __abs__(self):
|
||
|
return Rational(abs(self.p), self.q)
|
||
|
|
||
|
def __int__(self):
|
||
|
p, q = self.p, self.q
|
||
|
if p < 0:
|
||
|
return -int(-p//q)
|
||
|
return int(p//q)
|
||
|
|
||
|
def floor(self):
|
||
|
return Integer(self.p // self.q)
|
||
|
|
||
|
def ceiling(self):
|
||
|
return -Integer(-self.p // self.q)
|
||
|
|
||
|
def __floor__(self):
|
||
|
return self.floor()
|
||
|
|
||
|
def __ceil__(self):
|
||
|
return self.ceiling()
|
||
|
|
||
|
def __eq__(self, other):
|
||
|
try:
|
||
|
other = _sympify(other)
|
||
|
except SympifyError:
|
||
|
return NotImplemented
|
||
|
if not isinstance(other, Number):
|
||
|
# S(0) == S.false is False
|
||
|
# S(0) == False is True
|
||
|
return False
|
||
|
if not self:
|
||
|
return not other
|
||
|
if other.is_NumberSymbol:
|
||
|
if other.is_irrational:
|
||
|
return False
|
||
|
return other.__eq__(self)
|
||
|
if other.is_Rational:
|
||
|
# a Rational is always in reduced form so will never be 2/4
|
||
|
# so we can just check equivalence of args
|
||
|
return self.p == other.p and self.q == other.q
|
||
|
if other.is_Float:
|
||
|
# all Floats have a denominator that is a power of 2
|
||
|
# so if self doesn't, it can't be equal to other
|
||
|
if self.q & (self.q - 1):
|
||
|
return False
|
||
|
s, m, t = other._mpf_[:3]
|
||
|
if s:
|
||
|
m = -m
|
||
|
if not t:
|
||
|
# other is an odd integer
|
||
|
if not self.is_Integer or self.is_even:
|
||
|
return False
|
||
|
return m == self.p
|
||
|
|
||
|
from .power import integer_log
|
||
|
if t > 0:
|
||
|
# other is an even integer
|
||
|
if not self.is_Integer:
|
||
|
return False
|
||
|
# does m*2**t == self.p
|
||
|
return self.p and not self.p % m and \
|
||
|
integer_log(self.p//m, 2) == (t, True)
|
||
|
# does non-integer s*m/2**-t = p/q?
|
||
|
if self.is_Integer:
|
||
|
return False
|
||
|
return m == self.p and integer_log(self.q, 2) == (-t, True)
|
||
|
return False
|
||
|
|
||
|
def __ne__(self, other):
|
||
|
return not self == other
|
||
|
|
||
|
def _Rrel(self, other, attr):
|
||
|
# if you want self < other, pass self, other, __gt__
|
||
|
try:
|
||
|
other = _sympify(other)
|
||
|
except SympifyError:
|
||
|
return NotImplemented
|
||
|
if other.is_Number:
|
||
|
op = None
|
||
|
s, o = self, other
|
||
|
if other.is_NumberSymbol:
|
||
|
op = getattr(o, attr)
|
||
|
elif other.is_Float:
|
||
|
op = getattr(o, attr)
|
||
|
elif other.is_Rational:
|
||
|
s, o = Integer(s.p*o.q), Integer(s.q*o.p)
|
||
|
op = getattr(o, attr)
|
||
|
if op:
|
||
|
return op(s)
|
||
|
if o.is_number and o.is_extended_real:
|
||
|
return Integer(s.p), s.q*o
|
||
|
|
||
|
def __gt__(self, other):
|
||
|
rv = self._Rrel(other, '__lt__')
|
||
|
if rv is None:
|
||
|
rv = self, other
|
||
|
elif not isinstance(rv, tuple):
|
||
|
return rv
|
||
|
return Expr.__gt__(*rv)
|
||
|
|
||
|
def __ge__(self, other):
|
||
|
rv = self._Rrel(other, '__le__')
|
||
|
if rv is None:
|
||
|
rv = self, other
|
||
|
elif not isinstance(rv, tuple):
|
||
|
return rv
|
||
|
return Expr.__ge__(*rv)
|
||
|
|
||
|
def __lt__(self, other):
|
||
|
rv = self._Rrel(other, '__gt__')
|
||
|
if rv is None:
|
||
|
rv = self, other
|
||
|
elif not isinstance(rv, tuple):
|
||
|
return rv
|
||
|
return Expr.__lt__(*rv)
|
||
|
|
||
|
def __le__(self, other):
|
||
|
rv = self._Rrel(other, '__ge__')
|
||
|
if rv is None:
|
||
|
rv = self, other
|
||
|
elif not isinstance(rv, tuple):
|
||
|
return rv
|
||
|
return Expr.__le__(*rv)
|
||
|
|
||
|
def __hash__(self):
|
||
|
return super().__hash__()
|
||
|
|
||
|
def factors(self, limit=None, use_trial=True, use_rho=False,
|
||
|
use_pm1=False, verbose=False, visual=False):
|
||
|
"""A wrapper to factorint which return factors of self that are
|
||
|
smaller than limit (or cheap to compute). Special methods of
|
||
|
factoring are disabled by default so that only trial division is used.
|
||
|
"""
|
||
|
from sympy.ntheory.factor_ import factorrat
|
||
|
|
||
|
return factorrat(self, limit=limit, use_trial=use_trial,
|
||
|
use_rho=use_rho, use_pm1=use_pm1,
|
||
|
verbose=verbose).copy()
|
||
|
|
||
|
@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(' ', '')
|
||
|
# whereas we cannot, in general, make a Rational from an
|
||
|
# arbitrary expression, we can make an Integer unambiguously
|
||
|
# (except when a non-integer expression happens to round to
|
||
|
# an integer). So we proceed by taking int() of the input and
|
||
|
# let the int routines determine whether the expression can
|
||
|
# be made into an int or whether an error should be raised.
|
||
|
try:
|
||
|
ival = int(i)
|
||
|
except TypeError:
|
||
|
raise TypeError(
|
||
|
"Argument of Integer should be of numeric type, got %s." % i)
|
||
|
# We only work with well-behaved integer types. This converts, for
|
||
|
# example, numpy.int32 instances.
|
||
|
if ival == 1:
|
||
|
return S.One
|
||
|
if ival == -1:
|
||
|
return S.NegativeOne
|
||
|
if ival == 0:
|
||
|
return S.Zero
|
||
|
obj = Expr.__new__(cls)
|
||
|
obj.p = ival
|
||
|
return obj
|
||
|
|
||
|
def __getnewargs__(self):
|
||
|
return (self.p,)
|
||
|
|
||
|
# Arithmetic operations are here for efficiency
|
||
|
def __int__(self):
|
||
|
return self.p
|
||
|
|
||
|
def floor(self):
|
||
|
return Integer(self.p)
|
||
|
|
||
|
def ceiling(self):
|
||
|
return Integer(self.p)
|
||
|
|
||
|
def __floor__(self):
|
||
|
return self.floor()
|
||
|
|
||
|
def __ceil__(self):
|
||
|
return self.ceiling()
|
||
|
|
||
|
def __neg__(self):
|
||
|
return Integer(-self.p)
|
||
|
|
||
|
def __abs__(self):
|
||
|
if self.p >= 0:
|
||
|
return self
|
||
|
else:
|
||
|
return Integer(-self.p)
|
||
|
|
||
|
def __divmod__(self, other):
|
||
|
if isinstance(other, Integer) and global_parameters.evaluate:
|
||
|
return Tuple(*(divmod(self.p, other.p)))
|
||
|
else:
|
||
|
return Number.__divmod__(self, other)
|
||
|
|
||
|
def __rdivmod__(self, other):
|
||
|
if isinstance(other, int) and global_parameters.evaluate:
|
||
|
return Tuple(*(divmod(other, self.p)))
|
||
|
else:
|
||
|
try:
|
||
|
other = Number(other)
|
||
|
except TypeError:
|
||
|
msg = "unsupported operand type(s) for divmod(): '%s' and '%s'"
|
||
|
oname = type(other).__name__
|
||
|
sname = type(self).__name__
|
||
|
raise TypeError(msg % (oname, sname))
|
||
|
return Number.__divmod__(other, self)
|
||
|
|
||
|
# TODO make it decorator + bytecodehacks?
|
||
|
def __add__(self, other):
|
||
|
if global_parameters.evaluate:
|
||
|
if isinstance(other, int):
|
||
|
return Integer(self.p + other)
|
||
|
elif isinstance(other, Integer):
|
||
|
return Integer(self.p + other.p)
|
||
|
elif isinstance(other, Rational):
|
||
|
return Rational(self.p*other.q + other.p, other.q, 1)
|
||
|
return Rational.__add__(self, other)
|
||
|
else:
|
||
|
return Add(self, other)
|
||
|
|
||
|
def __radd__(self, other):
|
||
|
if global_parameters.evaluate:
|
||
|
if isinstance(other, int):
|
||
|
return Integer(other + self.p)
|
||
|
elif isinstance(other, Rational):
|
||
|
return Rational(other.p + self.p*other.q, other.q, 1)
|
||
|
return Rational.__radd__(self, other)
|
||
|
return Rational.__radd__(self, other)
|
||
|
|
||
|
def __sub__(self, other):
|
||
|
if global_parameters.evaluate:
|
||
|
if isinstance(other, int):
|
||
|
return Integer(self.p - other)
|
||
|
elif isinstance(other, Integer):
|
||
|
return Integer(self.p - other.p)
|
||
|
elif isinstance(other, Rational):
|
||
|
return Rational(self.p*other.q - other.p, other.q, 1)
|
||
|
return Rational.__sub__(self, other)
|
||
|
return Rational.__sub__(self, other)
|
||
|
|
||
|
def __rsub__(self, other):
|
||
|
if global_parameters.evaluate:
|
||
|
if isinstance(other, int):
|
||
|
return Integer(other - self.p)
|
||
|
elif isinstance(other, Rational):
|
||
|
return Rational(other.p - self.p*other.q, other.q, 1)
|
||
|
return Rational.__rsub__(self, other)
|
||
|
return Rational.__rsub__(self, other)
|
||
|
|
||
|
def __mul__(self, other):
|
||
|
if global_parameters.evaluate:
|
||
|
if isinstance(other, int):
|
||
|
return Integer(self.p*other)
|
||
|
elif isinstance(other, Integer):
|
||
|
return Integer(self.p*other.p)
|
||
|
elif isinstance(other, Rational):
|
||
|
return Rational(self.p*other.p, other.q, igcd(self.p, other.q))
|
||
|
return Rational.__mul__(self, other)
|
||
|
return Rational.__mul__(self, other)
|
||
|
|
||
|
def __rmul__(self, other):
|
||
|
if global_parameters.evaluate:
|
||
|
if isinstance(other, int):
|
||
|
return Integer(other*self.p)
|
||
|
elif isinstance(other, Rational):
|
||
|
return Rational(other.p*self.p, other.q, igcd(self.p, other.q))
|
||
|
return Rational.__rmul__(self, other)
|
||
|
return Rational.__rmul__(self, other)
|
||
|
|
||
|
def __mod__(self, other):
|
||
|
if global_parameters.evaluate:
|
||
|
if isinstance(other, int):
|
||
|
return Integer(self.p % other)
|
||
|
elif isinstance(other, Integer):
|
||
|
return Integer(self.p % other.p)
|
||
|
return Rational.__mod__(self, other)
|
||
|
return Rational.__mod__(self, other)
|
||
|
|
||
|
def __rmod__(self, other):
|
||
|
if global_parameters.evaluate:
|
||
|
if isinstance(other, int):
|
||
|
return Integer(other % self.p)
|
||
|
elif isinstance(other, Integer):
|
||
|
return Integer(other.p % self.p)
|
||
|
return Rational.__rmod__(self, other)
|
||
|
return Rational.__rmod__(self, other)
|
||
|
|
||
|
def __eq__(self, other):
|
||
|
if isinstance(other, int):
|
||
|
return (self.p == other)
|
||
|
elif isinstance(other, Integer):
|
||
|
return (self.p == other.p)
|
||
|
return Rational.__eq__(self, other)
|
||
|
|
||
|
def __ne__(self, other):
|
||
|
return not self == other
|
||
|
|
||
|
def __gt__(self, other):
|
||
|
try:
|
||
|
other = _sympify(other)
|
||
|
except SympifyError:
|
||
|
return NotImplemented
|
||
|
if other.is_Integer:
|
||
|
return _sympify(self.p > other.p)
|
||
|
return Rational.__gt__(self, other)
|
||
|
|
||
|
def __lt__(self, other):
|
||
|
try:
|
||
|
other = _sympify(other)
|
||
|
except SympifyError:
|
||
|
return NotImplemented
|
||
|
if other.is_Integer:
|
||
|
return _sympify(self.p < other.p)
|
||
|
return Rational.__lt__(self, other)
|
||
|
|
||
|
def __ge__(self, other):
|
||
|
try:
|
||
|
other = _sympify(other)
|
||
|
except SympifyError:
|
||
|
return NotImplemented
|
||
|
if other.is_Integer:
|
||
|
return _sympify(self.p >= other.p)
|
||
|
return Rational.__ge__(self, other)
|
||
|
|
||
|
def __le__(self, other):
|
||
|
try:
|
||
|
other = _sympify(other)
|
||
|
except SympifyError:
|
||
|
return NotImplemented
|
||
|
if other.is_Integer:
|
||
|
return _sympify(self.p <= other.p)
|
||
|
return Rational.__le__(self, other)
|
||
|
|
||
|
def __hash__(self):
|
||
|
return hash(self.p)
|
||
|
|
||
|
def __index__(self):
|
||
|
return self.p
|
||
|
|
||
|
########################################
|
||
|
|
||
|
def _eval_is_odd(self):
|
||
|
return bool(self.p % 2)
|
||
|
|
||
|
def _eval_power(self, expt):
|
||
|
"""
|
||
|
Tries to do some simplifications on self**expt
|
||
|
|
||
|
Returns None if no further simplifications can be done.
|
||
|
|
||
|
Explanation
|
||
|
===========
|
||
|
|
||
|
When exponent is a fraction (so we have for example a square root),
|
||
|
we try to find a simpler representation by factoring the argument
|
||
|
up to factors of 2**15, e.g.
|
||
|
|
||
|
- sqrt(4) becomes 2
|
||
|
- sqrt(-4) becomes 2*I
|
||
|
- (2**(3+7)*3**(6+7))**Rational(1,7) becomes 6*18**(3/7)
|
||
|
|
||
|
Further simplification would require a special call to factorint on
|
||
|
the argument which is not done here for sake of speed.
|
||
|
|
||
|
"""
|
||
|
from sympy.ntheory.factor_ import perfect_power
|
||
|
|
||
|
if expt is S.Infinity:
|
||
|
if self.p > S.One:
|
||
|
return S.Infinity
|
||
|
# cases -1, 0, 1 are done in their respective classes
|
||
|
return S.Infinity + S.ImaginaryUnit*S.Infinity
|
||
|
if expt is S.NegativeInfinity:
|
||
|
return Rational(1, self, 1)**S.Infinity
|
||
|
if not isinstance(expt, Number):
|
||
|
# simplify when expt is even
|
||
|
# (-2)**k --> 2**k
|
||
|
if self.is_negative and expt.is_even:
|
||
|
return (-self)**expt
|
||
|
if isinstance(expt, Float):
|
||
|
# Rational knows how to exponentiate by a Float
|
||
|
return super()._eval_power(expt)
|
||
|
if not isinstance(expt, Rational):
|
||
|
return
|
||
|
if expt is S.Half and self.is_negative:
|
||
|
# we extract I for this special case since everyone is doing so
|
||
|
return S.ImaginaryUnit*Pow(-self, expt)
|
||
|
if expt.is_negative:
|
||
|
# invert base and change sign on exponent
|
||
|
ne = -expt
|
||
|
if self.is_negative:
|
||
|
return S.NegativeOne**expt*Rational(1, -self, 1)**ne
|
||
|
else:
|
||
|
return Rational(1, self.p, 1)**ne
|
||
|
# see if base is a perfect root, sqrt(4) --> 2
|
||
|
x, xexact = integer_nthroot(abs(self.p), expt.q)
|
||
|
if xexact:
|
||
|
# if it's a perfect root we've finished
|
||
|
result = Integer(x**abs(expt.p))
|
||
|
if self.is_negative:
|
||
|
result *= S.NegativeOne**expt
|
||
|
return result
|
||
|
|
||
|
# The following is an algorithm where we collect perfect roots
|
||
|
# from the factors of base.
|
||
|
|
||
|
# if it's not an nth root, it still might be a perfect power
|
||
|
b_pos = int(abs(self.p))
|
||
|
p = perfect_power(b_pos)
|
||
|
if p is not False:
|
||
|
dict = {p[0]: p[1]}
|
||
|
else:
|
||
|
dict = Integer(b_pos).factors(limit=2**15)
|
||
|
|
||
|
# now process the dict of factors
|
||
|
out_int = 1 # integer part
|
||
|
out_rad = 1 # extracted radicals
|
||
|
sqr_int = 1
|
||
|
sqr_gcd = 0
|
||
|
sqr_dict = {}
|
||
|
for prime, exponent in dict.items():
|
||
|
exponent *= expt.p
|
||
|
# remove multiples of expt.q: (2**12)**(1/10) -> 2*(2**2)**(1/10)
|
||
|
div_e, div_m = divmod(exponent, expt.q)
|
||
|
if div_e > 0:
|
||
|
out_int *= prime**div_e
|
||
|
if div_m > 0:
|
||
|
# see if the reduced exponent shares a gcd with e.q
|
||
|
# (2**2)**(1/10) -> 2**(1/5)
|
||
|
g = igcd(div_m, expt.q)
|
||
|
if g != 1:
|
||
|
out_rad *= Pow(prime, Rational(div_m//g, expt.q//g, 1))
|
||
|
else:
|
||
|
sqr_dict[prime] = div_m
|
||
|
# identify gcd of remaining powers
|
||
|
for p, ex in sqr_dict.items():
|
||
|
if sqr_gcd == 0:
|
||
|
sqr_gcd = ex
|
||
|
else:
|
||
|
sqr_gcd = igcd(sqr_gcd, ex)
|
||
|
if sqr_gcd == 1:
|
||
|
break
|
||
|
for k, v in sqr_dict.items():
|
||
|
sqr_int *= k**(v//sqr_gcd)
|
||
|
if sqr_int == b_pos and out_int == 1 and out_rad == 1:
|
||
|
result = None
|
||
|
else:
|
||
|
result = out_int*out_rad*Pow(sqr_int, Rational(sqr_gcd, expt.q))
|
||
|
if self.is_negative:
|
||
|
result *= Pow(S.NegativeOne, expt)
|
||
|
return result
|
||
|
|
||
|
def _eval_is_prime(self):
|
||
|
from sympy.ntheory.primetest import isprime
|
||
|
|
||
|
return isprime(self)
|
||
|
|
||
|
def _eval_is_composite(self):
|
||
|
if self > 1:
|
||
|
return fuzzy_not(self.is_prime)
|
||
|
else:
|
||
|
return False
|
||
|
|
||
|
def as_numer_denom(self):
|
||
|
return self, S.One
|
||
|
|
||
|
@_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)
|
||
|
|
||
|
# These bitwise operations (__lshift__, __rlshift__, ..., __invert__) are defined
|
||
|
# for Integer only and not for general SymPy expressions. This is to achieve
|
||
|
# compatibility with the numbers.Integral ABC which only defines these operations
|
||
|
# among instances of numbers.Integral. Therefore, these methods check explicitly for
|
||
|
# integer types rather than using sympify because they should not accept arbitrary
|
||
|
# symbolic expressions and there is no symbolic analogue of numbers.Integral's
|
||
|
# bitwise operations.
|
||
|
def __lshift__(self, other):
|
||
|
if isinstance(other, (int, Integer, numbers.Integral)):
|
||
|
return Integer(self.p << int(other))
|
||
|
else:
|
||
|
return NotImplemented
|
||
|
|
||
|
def __rlshift__(self, other):
|
||
|
if isinstance(other, (int, numbers.Integral)):
|
||
|
return Integer(int(other) << self.p)
|
||
|
else:
|
||
|
return NotImplemented
|
||
|
|
||
|
def __rshift__(self, other):
|
||
|
if isinstance(other, (int, Integer, numbers.Integral)):
|
||
|
return Integer(self.p >> int(other))
|
||
|
else:
|
||
|
return NotImplemented
|
||
|
|
||
|
def __rrshift__(self, other):
|
||
|
if isinstance(other, (int, numbers.Integral)):
|
||
|
return Integer(int(other) >> self.p)
|
||
|
else:
|
||
|
return NotImplemented
|
||
|
|
||
|
def __and__(self, other):
|
||
|
if isinstance(other, (int, Integer, numbers.Integral)):
|
||
|
return Integer(self.p & int(other))
|
||
|
else:
|
||
|
return NotImplemented
|
||
|
|
||
|
def __rand__(self, other):
|
||
|
if isinstance(other, (int, numbers.Integral)):
|
||
|
return Integer(int(other) & self.p)
|
||
|
else:
|
||
|
return NotImplemented
|
||
|
|
||
|
def __xor__(self, other):
|
||
|
if isinstance(other, (int, Integer, numbers.Integral)):
|
||
|
return Integer(self.p ^ int(other))
|
||
|
else:
|
||
|
return NotImplemented
|
||
|
|
||
|
def __rxor__(self, other):
|
||
|
if isinstance(other, (int, numbers.Integral)):
|
||
|
return Integer(int(other) ^ self.p)
|
||
|
else:
|
||
|
return NotImplemented
|
||
|
|
||
|
def __or__(self, other):
|
||
|
if isinstance(other, (int, Integer, numbers.Integral)):
|
||
|
return Integer(self.p | int(other))
|
||
|
else:
|
||
|
return NotImplemented
|
||
|
|
||
|
def __ror__(self, other):
|
||
|
if isinstance(other, (int, numbers.Integral)):
|
||
|
return Integer(int(other) | self.p)
|
||
|
else:
|
||
|
return NotImplemented
|
||
|
|
||
|
def __invert__(self):
|
||
|
return Integer(~self.p)
|
||
|
|
||
|
# Add sympify converters
|
||
|
_sympy_converter[int] = Integer
|
||
|
|
||
|
|
||
|
class AlgebraicNumber(Expr):
|
||
|
r"""
|
||
|
Class for representing algebraic numbers in SymPy.
|
||
|
|
||
|
Symbolically, an instance of this class represents an element
|
||
|
$\alpha \in \mathbb{Q}(\theta) \hookrightarrow \mathbb{C}$. That is, the
|
||
|
algebraic number $\alpha$ is represented as an element of a particular
|
||
|
number field $\mathbb{Q}(\theta)$, with a particular embedding of this
|
||
|
field into the complex numbers.
|
||
|
|
||
|
Formally, the primitive element $\theta$ is given by two data points: (1)
|
||
|
its minimal polynomial (which defines $\mathbb{Q}(\theta)$), and (2) a
|
||
|
particular complex number that is a root of this polynomial (which defines
|
||
|
the embedding $\mathbb{Q}(\theta) \hookrightarrow \mathbb{C}$). Finally,
|
||
|
the algebraic number $\alpha$ which we represent is then given by the
|
||
|
coefficients of a polynomial in $\theta$.
|
||
|
"""
|
||
|
|
||
|
__slots__ = ('rep', 'root', 'alias', 'minpoly', '_own_minpoly')
|
||
|
|
||
|
is_AlgebraicNumber = True
|
||
|
is_algebraic = True
|
||
|
is_number = True
|
||
|
|
||
|
|
||
|
kind = NumberKind
|
||
|
|
||
|
# Optional alias symbol is not free.
|
||
|
# Actually, alias should be a Str, but some methods
|
||
|
# expect that it be an instance of Expr.
|
||
|
free_symbols: set[Basic] = set()
|
||
|
|
||
|
def __new__(cls, expr, coeffs=None, alias=None, **args):
|
||
|
r"""
|
||
|
Construct a new algebraic number $\alpha$ belonging to a number field
|
||
|
$k = \mathbb{Q}(\theta)$.
|
||
|
|
||
|
There are four instance attributes to be determined:
|
||
|
|
||
|
=========== ============================================================================
|
||
|
Attribute Type/Meaning
|
||
|
=========== ============================================================================
|
||
|
``root`` :py:class:`~.Expr` for $\theta$ as a complex number
|
||
|
``minpoly`` :py:class:`~.Poly`, the minimal polynomial of $\theta$
|
||
|
``rep`` :py:class:`~sympy.polys.polyclasses.DMP` giving $\alpha$ as poly in $\theta$
|
||
|
``alias`` :py:class:`~.Symbol` for $\theta$, or ``None``
|
||
|
=========== ============================================================================
|
||
|
|
||
|
See Parameters section for how they are determined.
|
||
|
|
||
|
Parameters
|
||
|
==========
|
||
|
|
||
|
expr : :py:class:`~.Expr`, or pair $(m, r)$
|
||
|
There are three distinct modes of construction, depending on what
|
||
|
is passed as *expr*.
|
||
|
|
||
|
**(1)** *expr* is an :py:class:`~.AlgebraicNumber`:
|
||
|
In this case we begin by copying all four instance attributes from
|
||
|
*expr*. If *coeffs* were also given, we compose the two coeff
|
||
|
polynomials (see below). If an *alias* was given, it overrides.
|
||
|
|
||
|
**(2)** *expr* is any other type of :py:class:`~.Expr`:
|
||
|
Then ``root`` will equal *expr*. Therefore it
|
||
|
must express an algebraic quantity, and we will compute its
|
||
|
``minpoly``.
|
||
|
|
||
|
**(3)** *expr* is an ordered pair $(m, r)$ giving the
|
||
|
``minpoly`` $m$, and a ``root`` $r$ thereof, which together
|
||
|
define $\theta$. In this case $m$ may be either a univariate
|
||
|
:py:class:`~.Poly` or any :py:class:`~.Expr` which represents the
|
||
|
same, while $r$ must be some :py:class:`~.Expr` representing a
|
||
|
complex number that is a root of $m$, including both explicit
|
||
|
expressions in radicals, and instances of
|
||
|
:py:class:`~.ComplexRootOf` or :py:class:`~.AlgebraicNumber`.
|
||
|
|
||
|
coeffs : list, :py:class:`~.ANP`, None, optional (default=None)
|
||
|
This defines ``rep``, giving the algebraic number $\alpha$ as a
|
||
|
polynomial in $\theta$.
|
||
|
|
||
|
If a list, the elements should be integers or rational numbers.
|
||
|
If an :py:class:`~.ANP`, we take its coefficients (using its
|
||
|
:py:meth:`~.ANP.to_list()` method). If ``None``, then the list of
|
||
|
coefficients defaults to ``[1, 0]``, meaning that $\alpha = \theta$
|
||
|
is the primitive element of the field.
|
||
|
|
||
|
If *expr* was an :py:class:`~.AlgebraicNumber`, let $g(x)$ be its
|
||
|
``rep`` polynomial, and let $f(x)$ be the polynomial defined by
|
||
|
*coeffs*. Then ``self.rep`` will represent the composition
|
||
|
$(f \circ g)(x)$.
|
||
|
|
||
|
alias : str, :py:class:`~.Symbol`, None, optional (default=None)
|
||
|
This is a way to provide a name for the primitive element. We
|
||
|
described several ways in which the *expr* argument can define the
|
||
|
value of the primitive element, but none of these methods gave it
|
||
|
a name. Here, for example, *alias* could be set as
|
||
|
``Symbol('theta')``, in order to make this symbol appear when
|
||
|
$\alpha$ is printed, or rendered as a polynomial, using the
|
||
|
:py:meth:`~.as_poly()` method.
|
||
|
|
||
|
Examples
|
||
|
========
|
||
|
|
||
|
Recall that we are constructing an algebraic number as a field element
|
||
|
$\alpha \in \mathbb{Q}(\theta)$.
|
||
|
|
||
|
>>> from sympy import AlgebraicNumber, sqrt, CRootOf, S
|
||
|
>>> from sympy.abc import x
|
||
|
|
||
|
Example (1): $\alpha = \theta = \sqrt{2}$
|
||
|
|
||
|
>>> a1 = AlgebraicNumber(sqrt(2))
|
||
|
>>> a1.minpoly_of_element().as_expr(x)
|
||
|
x**2 - 2
|
||
|
>>> a1.evalf(10)
|
||
|
1.414213562
|
||
|
|
||
|
Example (2): $\alpha = 3 \sqrt{2} - 5$, $\theta = \sqrt{2}$. We can
|
||
|
either build on the last example:
|
||
|
|
||
|
>>> a2 = AlgebraicNumber(a1, [3, -5])
|
||
|
>>> a2.as_expr()
|
||
|
-5 + 3*sqrt(2)
|
||
|
|
||
|
or start from scratch:
|
||
|
|
||
|
>>> a2 = AlgebraicNumber(sqrt(2), [3, -5])
|
||
|
>>> a2.as_expr()
|
||
|
-5 + 3*sqrt(2)
|
||
|
|
||
|
Example (3): $\alpha = 6 \sqrt{2} - 11$, $\theta = \sqrt{2}$. Again we
|
||
|
can build on the previous example, and we see that the coeff polys are
|
||
|
composed:
|
||
|
|
||
|
>>> a3 = AlgebraicNumber(a2, [2, -1])
|
||
|
>>> a3.as_expr()
|
||
|
-11 + 6*sqrt(2)
|
||
|
|
||
|
reflecting the fact that $(2x - 1) \circ (3x - 5) = 6x - 11$.
|
||
|
|
||
|
Example (4): $\alpha = \sqrt{2}$, $\theta = \sqrt{2} + \sqrt{3}$. The
|
||
|
easiest way is to use the :py:func:`~.to_number_field()` function:
|
||
|
|
||
|
>>> from sympy import to_number_field
|
||
|
>>> a4 = to_number_field(sqrt(2), sqrt(2) + sqrt(3))
|
||
|
>>> a4.minpoly_of_element().as_expr(x)
|
||
|
x**2 - 2
|
||
|
>>> a4.to_root()
|
||
|
sqrt(2)
|
||
|
>>> a4.primitive_element()
|
||
|
sqrt(2) + sqrt(3)
|
||
|
>>> a4.coeffs()
|
||
|
[1/2, 0, -9/2, 0]
|
||
|
|
||
|
but if you already knew the right coefficients, you could construct it
|
||
|
directly:
|
||
|
|
||
|
>>> a4 = AlgebraicNumber(sqrt(2) + sqrt(3), [S(1)/2, 0, S(-9)/2, 0])
|
||
|
>>> a4.to_root()
|
||
|
sqrt(2)
|
||
|
>>> a4.primitive_element()
|
||
|
sqrt(2) + sqrt(3)
|
||
|
|
||
|
Example (5): Construct the Golden Ratio as an element of the 5th
|
||
|
cyclotomic field, supposing we already know its coefficients. This time
|
||
|
we introduce the alias $\zeta$ for the primitive element of the field:
|
||
|
|
||
|
>>> from sympy import cyclotomic_poly
|
||
|
>>> from sympy.abc import zeta
|
||
|
>>> a5 = AlgebraicNumber(CRootOf(cyclotomic_poly(5), -1),
|
||
|
... [-1, -1, 0, 0], alias=zeta)
|
||
|
>>> a5.as_poly().as_expr()
|
||
|
-zeta**3 - zeta**2
|
||
|
>>> a5.evalf()
|
||
|
1.61803398874989
|
||
|
|
||
|
(The index ``-1`` to ``CRootOf`` selects the complex root with the
|
||
|
largest real and imaginary parts, which in this case is
|
||
|
$\mathrm{e}^{2i\pi/5}$. See :py:class:`~.ComplexRootOf`.)
|
||
|
|
||
|
Example (6): Building on the last example, construct the number
|
||
|
$2 \phi \in \mathbb{Q}(\phi)$, where $\phi$ is the Golden Ratio:
|
||
|
|
||
|
>>> from sympy.abc import phi
|
||
|
>>> a6 = AlgebraicNumber(a5.to_root(), coeffs=[2, 0], alias=phi)
|
||
|
>>> a6.as_poly().as_expr()
|
||
|
2*phi
|
||
|
>>> a6.primitive_element().evalf()
|
||
|
1.61803398874989
|
||
|
|
||
|
Note that we needed to use ``a5.to_root()``, since passing ``a5`` as
|
||
|
the first argument would have constructed the number $2 \phi$ as an
|
||
|
element of the field $\mathbb{Q}(\zeta)$:
|
||
|
|
||
|
>>> a6_wrong = AlgebraicNumber(a5, coeffs=[2, 0])
|
||
|
>>> a6_wrong.as_poly().as_expr()
|
||
|
-2*zeta**3 - 2*zeta**2
|
||
|
>>> a6_wrong.primitive_element().evalf()
|
||
|
0.309016994374947 + 0.951056516295154*I
|
||
|
|
||
|
"""
|
||
|
from sympy.polys.polyclasses import ANP, DMP
|
||
|
from sympy.polys.numberfields import minimal_polynomial
|
||
|
|
||
|
expr = sympify(expr)
|
||
|
rep0 = None
|
||
|
alias0 = None
|
||
|
|
||
|
if isinstance(expr, (tuple, Tuple)):
|
||
|
minpoly, root = expr
|
||
|
|
||
|
if not minpoly.is_Poly:
|
||
|
from sympy.polys.polytools import Poly
|
||
|
minpoly = Poly(minpoly)
|
||
|
elif expr.is_AlgebraicNumber:
|
||
|
minpoly, root, rep0, alias0 = (expr.minpoly, expr.root,
|
||
|
expr.rep, expr.alias)
|
||
|
else:
|
||
|
minpoly, root = minimal_polynomial(
|
||
|
expr, args.get('gen'), polys=True), expr
|
||
|
|
||
|
dom = minpoly.get_domain()
|
||
|
|
||
|
if coeffs is not None:
|
||
|
if not isinstance(coeffs, ANP):
|
||
|
rep = DMP.from_sympy_list(sympify(coeffs), 0, dom)
|
||
|
scoeffs = Tuple(*coeffs)
|
||
|
else:
|
||
|
rep = DMP.from_list(coeffs.to_list(), 0, dom)
|
||
|
scoeffs = Tuple(*coeffs.to_list())
|
||
|
|
||
|
else:
|
||
|
rep = DMP.from_list([1, 0], 0, dom)
|
||
|
scoeffs = Tuple(1, 0)
|
||
|
|
||
|
if rep0 is not None:
|
||
|
from sympy.polys.densetools import dup_compose
|
||
|
c = dup_compose(rep.rep, rep0.rep, 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:
|
||
|
# use the matching root if it's simpler
|
||
|
if measure(r) < ratio*measure(self.root):
|
||
|
return AlgebraicNumber(r)
|
||
|
return self
|
||
|
|
||
|
def field_element(self, coeffs):
|
||
|
r"""
|
||
|
Form another element of the same number field.
|
||
|
|
||
|
Explanation
|
||
|
===========
|
||
|
|
||
|
If we represent $\alpha \in \mathbb{Q}(\theta)$, form another element
|
||
|
$\beta \in \mathbb{Q}(\theta)$ of the same number field.
|
||
|
|
||
|
Parameters
|
||
|
==========
|
||
|
|
||
|
coeffs : list, :py:class:`~.ANP`
|
||
|
Like the *coeffs* arg to the class
|
||
|
:py:meth:`constructor<.AlgebraicNumber.__new__>`, defines the
|
||
|
new element as a polynomial in the primitive element.
|
||
|
|
||
|
If a list, the elements should be integers or rational numbers.
|
||
|
If an :py:class:`~.ANP`, we take its coefficients (using its
|
||
|
:py:meth:`~.ANP.to_list()` method).
|
||
|
|
||
|
Examples
|
||
|
========
|
||
|
|
||
|
>>> from sympy import AlgebraicNumber, sqrt
|
||
|
>>> a = AlgebraicNumber(sqrt(5), [-1, 1])
|
||
|
>>> b = a.field_element([3, 2])
|
||
|
>>> print(a)
|
||
|
1 - sqrt(5)
|
||
|
>>> print(b)
|
||
|
2 + 3*sqrt(5)
|
||
|
>>> print(b.primitive_element() == a.primitive_element())
|
||
|
True
|
||
|
|
||
|
See Also
|
||
|
========
|
||
|
|
||
|
AlgebraicNumber
|
||
|
"""
|
||
|
return AlgebraicNumber(
|
||
|
(self.minpoly, self.root), coeffs=coeffs, alias=self.alias)
|
||
|
|
||
|
@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()
|
||
|
# Second case occurs if self.minpoly is linear:
|
||
|
return c == [1, 0] or c == [self.root]
|
||
|
|
||
|
def primitive_element(self):
|
||
|
r"""
|
||
|
Get the primitive element $\theta$ for the number field
|
||
|
$\mathbb{Q}(\theta)$ to which this algebraic number $\alpha$ belongs.
|
||
|
|
||
|
Returns
|
||
|
=======
|
||
|
|
||
|
AlgebraicNumber
|
||
|
|
||
|
"""
|
||
|
if self.is_primitive_element:
|
||
|
return self
|
||
|
return self.field_element([1, 0])
|
||
|
|
||
|
def to_primitive_element(self, radicals=True):
|
||
|
r"""
|
||
|
Convert ``self`` to an :py:class:`~.AlgebraicNumber` instance that is
|
||
|
equal to its own primitive element.
|
||
|
|
||
|
Explanation
|
||
|
===========
|
||
|
|
||
|
If we represent $\alpha \in \mathbb{Q}(\theta)$, $\alpha \neq \theta$,
|
||
|
construct a new :py:class:`~.AlgebraicNumber` that represents
|
||
|
$\alpha \in \mathbb{Q}(\alpha)$.
|
||
|
|
||
|
Examples
|
||
|
========
|
||
|
|
||
|
>>> from sympy import sqrt, to_number_field
|
||
|
>>> from sympy.abc import x
|
||
|
>>> a = to_number_field(sqrt(2), sqrt(2) + sqrt(3))
|
||
|
|
||
|
The :py:class:`~.AlgebraicNumber` ``a`` represents the number
|
||
|
$\sqrt{2}$ in the field $\mathbb{Q}(\sqrt{2} + \sqrt{3})$. Rendering
|
||
|
``a`` as a polynomial,
|
||
|
|
||
|
>>> a.as_poly().as_expr(x)
|
||
|
x**3/2 - 9*x/2
|
||
|
|
||
|
reflects the fact that $\sqrt{2} = \theta^3/2 - 9 \theta/2$, where
|
||
|
$\theta = \sqrt{2} + \sqrt{3}$.
|
||
|
|
||
|
``a`` is not equal to its own primitive element. Its minpoly
|
||
|
|
||
|
>>> a.minpoly.as_poly().as_expr(x)
|
||
|
x**4 - 10*x**2 + 1
|
||
|
|
||
|
is that of $\theta$.
|
||
|
|
||
|
Converting to a primitive element,
|
||
|
|
||
|
>>> a_prim = a.to_primitive_element()
|
||
|
>>> a_prim.minpoly.as_poly().as_expr(x)
|
||
|
x**2 - 2
|
||
|
|
||
|
we obtain an :py:class:`~.AlgebraicNumber` whose ``minpoly`` is that of
|
||
|
the number itself.
|
||
|
|
||
|
Parameters
|
||
|
==========
|
||
|
|
||
|
radicals : boolean, optional (default=True)
|
||
|
If ``True``, then we will try to return an
|
||
|
:py:class:`~.AlgebraicNumber` whose ``root`` is an expression
|
||
|
in radicals. If that is not possible (or if *radicals* is
|
||
|
``False``), ``root`` will be a :py:class:`~.ComplexRootOf`.
|
||
|
|
||
|
Returns
|
||
|
=======
|
||
|
|
||
|
AlgebraicNumber
|
||
|
|
||
|
See Also
|
||
|
========
|
||
|
|
||
|
is_primitive_element
|
||
|
|
||
|
"""
|
||
|
if self.is_primitive_element:
|
||
|
return self
|
||
|
m = self.minpoly_of_element()
|
||
|
r = self.to_root(radicals=radicals)
|
||
|
return AlgebraicNumber((m, r))
|
||
|
|
||
|
def minpoly_of_element(self):
|
||
|
r"""
|
||
|
Compute the minimal polynomial for this algebraic number.
|
||
|
|
||
|
Explanation
|
||
|
===========
|
||
|
|
||
|
Recall that we represent an element $\alpha \in \mathbb{Q}(\theta)$.
|
||
|
Our instance attribute ``self.minpoly`` is the minimal polynomial for
|
||
|
our primitive element $\theta$. This method computes the minimal
|
||
|
polynomial for $\alpha$.
|
||
|
|
||
|
"""
|
||
|
if self._own_minpoly is None:
|
||
|
if self.is_primitive_element:
|
||
|
self._own_minpoly = self.minpoly
|
||
|
else:
|
||
|
from sympy.polys.numberfields.minpoly import minpoly
|
||
|
theta = self.primitive_element()
|
||
|
self._own_minpoly = minpoly(self.as_expr(theta), polys=True)
|
||
|
return self._own_minpoly
|
||
|
|
||
|
def to_root(self, radicals=True, minpoly=None):
|
||
|
"""
|
||
|
Convert to an :py:class:`~.Expr` that is not an
|
||
|
:py:class:`~.AlgebraicNumber`, specifically, either a
|
||
|
:py:class:`~.ComplexRootOf`, or, optionally and where possible, an
|
||
|
expression in radicals.
|
||
|
|
||
|
Parameters
|
||
|
==========
|
||
|
|
||
|
radicals : boolean, optional (default=True)
|
||
|
If ``True``, then we will try to return the root as an expression
|
||
|
in radicals. If that is not possible, we will return a
|
||
|
:py:class:`~.ComplexRootOf`.
|
||
|
|
||
|
minpoly : :py:class:`~.Poly`
|
||
|
If the minimal polynomial for `self` has been pre-computed, it can
|
||
|
be passed in order to save time.
|
||
|
|
||
|
"""
|
||
|
if self.is_primitive_element and not isinstance(self.root, AlgebraicNumber):
|
||
|
return self.root
|
||
|
m = minpoly or self.minpoly_of_element()
|
||
|
roots = m.all_roots(radicals=radicals)
|
||
|
if len(roots) == 1:
|
||
|
return roots[0]
|
||
|
ex = self.as_expr()
|
||
|
for b in roots:
|
||
|
if m.same_root(b, ex):
|
||
|
return b
|
||
|
|
||
|
|
||
|
class RationalConstant(Rational):
|
||
|
"""
|
||
|
Abstract base class for rationals with specific behaviors
|
||
|
|
||
|
Derived classes must define class attributes p and q and should probably all
|
||
|
be singletons.
|
||
|
"""
|
||
|
__slots__ = ()
|
||
|
|
||
|
def __new__(cls):
|
||
|
return AtomicExpr.__new__(cls)
|
||
|
|
||
|
|
||
|
class IntegerConstant(Integer):
|
||
|
__slots__ = ()
|
||
|
|
||
|
def __new__(cls):
|
||
|
return AtomicExpr.__new__(cls)
|
||
|
|
||
|
|
||
|
class Zero(IntegerConstant, metaclass=Singleton):
|
||
|
"""The number zero.
|
||
|
|
||
|
Zero is a singleton, and can be accessed by ``S.Zero``
|
||
|
|
||
|
Examples
|
||
|
========
|
||
|
|
||
|
>>> from sympy import S, Integer
|
||
|
>>> Integer(0) is S.Zero
|
||
|
True
|
||
|
>>> 1/S.Zero
|
||
|
zoo
|
||
|
|
||
|
References
|
||
|
==========
|
||
|
|
||
|
.. [1] https://en.wikipedia.org/wiki/Zero
|
||
|
"""
|
||
|
|
||
|
p = 0
|
||
|
q = 1
|
||
|
is_positive = False
|
||
|
is_negative = False
|
||
|
is_zero = True
|
||
|
is_number = True
|
||
|
is_comparable = True
|
||
|
|
||
|
__slots__ = ()
|
||
|
|
||
|
def __getnewargs__(self):
|
||
|
return ()
|
||
|
|
||
|
@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
|
||
|
|
||
|
# infinities are already handled with pos and neg
|
||
|
# tests above; now throw away leading numbers on Mul
|
||
|
# exponent since 0**-x = zoo**x even when x == 0
|
||
|
coeff, terms = expt.as_coeff_Mul()
|
||
|
if coeff.is_negative:
|
||
|
return S.ComplexInfinity**terms
|
||
|
if coeff is not S.One: # there is a Number to discard
|
||
|
return self**terms
|
||
|
|
||
|
def _eval_order(self, *symbols):
|
||
|
# Order(0,x) -> 0
|
||
|
return self
|
||
|
|
||
|
def __bool__(self):
|
||
|
return False
|
||
|
|
||
|
|
||
|
class One(IntegerConstant, metaclass=Singleton):
|
||
|
"""The number one.
|
||
|
|
||
|
One is a singleton, and can be accessed by ``S.One``.
|
||
|
|
||
|
Examples
|
||
|
========
|
||
|
|
||
|
>>> from sympy import S, Integer
|
||
|
>>> Integer(1) is S.One
|
||
|
True
|
||
|
|
||
|
References
|
||
|
==========
|
||
|
|
||
|
.. [1] https://en.wikipedia.org/wiki/1_%28number%29
|
||
|
"""
|
||
|
is_number = True
|
||
|
is_positive = True
|
||
|
|
||
|
p = 1
|
||
|
q = 1
|
||
|
|
||
|
__slots__ = ()
|
||
|
|
||
|
def __getnewargs__(self):
|
||
|
return ()
|
||
|
|
||
|
@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):
|
||
|
# NaN is structurally equal to another NaN
|
||
|
return other is S.NaN
|
||
|
|
||
|
def __ne__(self, other):
|
||
|
return other is not S.NaN
|
||
|
|
||
|
# Expr will _sympify and raise TypeError
|
||
|
__gt__ = Expr.__gt__
|
||
|
__ge__ = Expr.__ge__
|
||
|
__lt__ = Expr.__lt__
|
||
|
__le__ = Expr.__le__
|
||
|
|
||
|
nan = S.NaN
|
||
|
|
||
|
@dispatch(NaN, Expr) # type:ignore
|
||
|
def _eval_is_eq(a, b): # noqa:F811
|
||
|
return False
|
||
|
|
||
|
|
||
|
class ComplexInfinity(AtomicExpr, metaclass=Singleton):
|
||
|
r"""Complex infinity.
|
||
|
|
||
|
Explanation
|
||
|
===========
|
||
|
|
||
|
In complex analysis the symbol `\tilde\infty`, called "complex
|
||
|
infinity", represents a quantity with infinite magnitude, but
|
||
|
undetermined complex phase.
|
||
|
|
||
|
ComplexInfinity is a singleton, and can be accessed by
|
||
|
``S.ComplexInfinity``, or can be imported as ``zoo``.
|
||
|
|
||
|
Examples
|
||
|
========
|
||
|
|
||
|
>>> from sympy import zoo
|
||
|
>>> zoo + 42
|
||
|
zoo
|
||
|
>>> 42/zoo
|
||
|
0
|
||
|
>>> zoo + zoo
|
||
|
nan
|
||
|
>>> zoo*zoo
|
||
|
zoo
|
||
|
|
||
|
See Also
|
||
|
========
|
||
|
|
||
|
Infinity
|
||
|
"""
|
||
|
|
||
|
is_commutative = True
|
||
|
is_infinite = True
|
||
|
is_number = True
|
||
|
is_prime = False
|
||
|
is_complex = False
|
||
|
is_extended_real = False
|
||
|
|
||
|
kind = NumberKind
|
||
|
|
||
|
__slots__ = ()
|
||
|
|
||
|
def __new__(cls):
|
||
|
return AtomicExpr.__new__(cls)
|
||
|
|
||
|
def _latex(self, printer):
|
||
|
return r"\tilde{\infty}"
|
||
|
|
||
|
@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 # NumberSymbol != non-(Number|self)
|
||
|
|
||
|
def __ne__(self, other):
|
||
|
return not self == other
|
||
|
|
||
|
def __le__(self, other):
|
||
|
if self is other:
|
||
|
return S.true
|
||
|
return Expr.__le__(self, other)
|
||
|
|
||
|
def __ge__(self, other):
|
||
|
if self is other:
|
||
|
return S.true
|
||
|
return Expr.__ge__(self, other)
|
||
|
|
||
|
def __int__(self):
|
||
|
# subclass with appropriate return value
|
||
|
raise NotImplementedError
|
||
|
|
||
|
def __hash__(self):
|
||
|
return super().__hash__()
|
||
|
|
||
|
|
||
|
class Exp1(NumberSymbol, metaclass=Singleton):
|
||
|
r"""The `e` constant.
|
||
|
|
||
|
Explanation
|
||
|
===========
|
||
|
|
||
|
The transcendental number `e = 2.718281828\ldots` is the base of the
|
||
|
natural logarithm and of the exponential function, `e = \exp(1)`.
|
||
|
Sometimes called Euler's number or Napier's constant.
|
||
|
|
||
|
Exp1 is a singleton, and can be accessed by ``S.Exp1``,
|
||
|
or can be imported as ``E``.
|
||
|
|
||
|
Examples
|
||
|
========
|
||
|
|
||
|
>>> from sympy import exp, log, E
|
||
|
>>> E is exp(1)
|
||
|
True
|
||
|
>>> log(E)
|
||
|
1
|
||
|
|
||
|
References
|
||
|
==========
|
||
|
|
||
|
.. [1] https://en.wikipedia.org/wiki/E_%28mathematical_constant%29
|
||
|
"""
|
||
|
|
||
|
is_real = True
|
||
|
is_positive = True
|
||
|
is_negative = False # XXX Forces is_negative/is_nonnegative
|
||
|
is_irrational = True
|
||
|
is_number = True
|
||
|
is_algebraic = False
|
||
|
is_transcendental = True
|
||
|
|
||
|
__slots__ = ()
|
||
|
|
||
|
def _latex(self, printer):
|
||
|
return r"e"
|
||
|
|
||
|
@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]
|
||
|
|
||
|
# don't autoexpand Pow or Mul (see the issue 3351):
|
||
|
elif not arg.is_Add:
|
||
|
Ioo = I*oo
|
||
|
if arg in [Ioo, -Ioo]:
|
||
|
return nan
|
||
|
|
||
|
coeff = arg.coeff(pi*I)
|
||
|
if coeff:
|
||
|
if (2*coeff).is_integer:
|
||
|
if coeff.is_even:
|
||
|
return S.One
|
||
|
elif coeff.is_odd:
|
||
|
return S.NegativeOne
|
||
|
elif (coeff + S.Half).is_even:
|
||
|
return -I
|
||
|
elif (coeff + S.Half).is_odd:
|
||
|
return I
|
||
|
elif coeff.is_Rational:
|
||
|
ncoeff = coeff % 2 # restrict to [0, 2pi)
|
||
|
if ncoeff > 1: # restrict to (-pi, pi]
|
||
|
ncoeff -= 2
|
||
|
if ncoeff != coeff:
|
||
|
return S.Exp1**(ncoeff*S.Pi*S.ImaginaryUnit)
|
||
|
|
||
|
# Warning: code in risch.py will be very sensitive to changes
|
||
|
# in this (see DifferentialExtension).
|
||
|
|
||
|
# look for a single log factor
|
||
|
|
||
|
coeff, terms = arg.as_coeff_Mul()
|
||
|
|
||
|
# but it can't be multiplied by oo
|
||
|
if coeff in (oo, -oo):
|
||
|
return
|
||
|
|
||
|
coeffs, log_term = [coeff], None
|
||
|
for term in Mul.make_args(terms):
|
||
|
if isinstance(term, log):
|
||
|
if log_term is None:
|
||
|
log_term = term.args[0]
|
||
|
else:
|
||
|
return
|
||
|
elif term.is_comparable:
|
||
|
coeffs.append(term)
|
||
|
else:
|
||
|
return
|
||
|
|
||
|
return log_term**Mul(*coeffs) if log_term else None
|
||
|
elif arg.is_Add:
|
||
|
out = []
|
||
|
add = []
|
||
|
argchanged = False
|
||
|
for a in arg.args:
|
||
|
if a is S.One:
|
||
|
add.append(a)
|
||
|
continue
|
||
|
newa = self**a
|
||
|
if isinstance(newa, Pow) and newa.base is self:
|
||
|
if newa.exp != a:
|
||
|
add.append(newa.exp)
|
||
|
argchanged = True
|
||
|
else:
|
||
|
add.append(a)
|
||
|
else:
|
||
|
out.append(newa)
|
||
|
if out or argchanged:
|
||
|
return Mul(*out)*Pow(self, Add(*add), evaluate=False)
|
||
|
elif arg.is_Matrix:
|
||
|
return arg.exp()
|
||
|
|
||
|
def _eval_rewrite_as_sin(self, **kwargs):
|
||
|
from sympy.functions.elementary.trigonometric import sin
|
||
|
return sin(I + S.Pi/2) - I*sin(I)
|
||
|
|
||
|
def _eval_rewrite_as_cos(self, **kwargs):
|
||
|
from sympy.functions.elementary.trigonometric import cos
|
||
|
return cos(I) + I*cos(I + S.Pi/2)
|
||
|
|
||
|
E = S.Exp1
|
||
|
|
||
|
|
||
|
class Pi(NumberSymbol, metaclass=Singleton):
|
||
|
r"""The `\pi` constant.
|
||
|
|
||
|
Explanation
|
||
|
===========
|
||
|
|
||
|
The transcendental number `\pi = 3.141592654\ldots` represents the ratio
|
||
|
of a circle's circumference to its diameter, the area of the unit circle,
|
||
|
the half-period of trigonometric functions, and many other things
|
||
|
in mathematics.
|
||
|
|
||
|
Pi is a singleton, and can be accessed by ``S.Pi``, or can
|
||
|
be imported as ``pi``.
|
||
|
|
||
|
Examples
|
||
|
========
|
||
|
|
||
|
>>> from sympy import S, pi, oo, sin, exp, integrate, Symbol
|
||
|
>>> S.Pi
|
||
|
pi
|
||
|
>>> pi > 3
|
||
|
True
|
||
|
>>> pi.is_irrational
|
||
|
True
|
||
|
>>> x = Symbol('x')
|
||
|
>>> sin(x + 2*pi)
|
||
|
sin(x)
|
||
|
>>> integrate(exp(-x**2), (x, -oo, oo))
|
||
|
sqrt(pi)
|
||
|
|
||
|
References
|
||
|
==========
|
||
|
|
||
|
.. [1] https://en.wikipedia.org/wiki/Pi
|
||
|
"""
|
||
|
|
||
|
is_real = True
|
||
|
is_positive = True
|
||
|
is_negative = False
|
||
|
is_irrational = True
|
||
|
is_number = True
|
||
|
is_algebraic = False
|
||
|
is_transcendental = True
|
||
|
|
||
|
__slots__ = ()
|
||
|
|
||
|
def _latex(self, printer):
|
||
|
return r"\pi"
|
||
|
|
||
|
@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):
|
||
|
# XXX track down why this has to be increased
|
||
|
rv = mlib.from_man_exp(phi_fixed(prec + 10), -prec - 10)
|
||
|
return mpf_norm(rv, prec)
|
||
|
|
||
|
def _eval_expand_func(self, **hints):
|
||
|
from sympy.functions.elementary.miscellaneous import sqrt
|
||
|
return S.Half + S.Half*sqrt(5)
|
||
|
|
||
|
def approximation_interval(self, number_cls):
|
||
|
if issubclass(number_cls, Integer):
|
||
|
return (S.One, Rational(2))
|
||
|
elif issubclass(number_cls, Rational):
|
||
|
pass
|
||
|
|
||
|
_eval_rewrite_as_sqrt = _eval_expand_func
|
||
|
|
||
|
|
||
|
class TribonacciConstant(NumberSymbol, metaclass=Singleton):
|
||
|
r"""The tribonacci constant.
|
||
|
|
||
|
Explanation
|
||
|
===========
|
||
|
|
||
|
The tribonacci numbers are like the Fibonacci numbers, but instead
|
||
|
of starting with two predetermined terms, the sequence starts with
|
||
|
three predetermined terms and each term afterwards is the sum of the
|
||
|
preceding three terms.
|
||
|
|
||
|
The tribonacci constant is the ratio toward which adjacent tribonacci
|
||
|
numbers tend. It is a root of the polynomial `x^3 - x^2 - x - 1 = 0`,
|
||
|
and also satisfies the equation `x + x^{-3} = 2`.
|
||
|
|
||
|
TribonacciConstant is a singleton, and can be accessed
|
||
|
by ``S.TribonacciConstant``.
|
||
|
|
||
|
Examples
|
||
|
========
|
||
|
|
||
|
>>> from sympy import S
|
||
|
>>> S.TribonacciConstant > 1
|
||
|
True
|
||
|
>>> S.TribonacciConstant.expand(func=True)
|
||
|
1/3 + (19 - 3*sqrt(33))**(1/3)/3 + (3*sqrt(33) + 19)**(1/3)/3
|
||
|
>>> S.TribonacciConstant.is_irrational
|
||
|
True
|
||
|
>>> S.TribonacciConstant.n(20)
|
||
|
1.8392867552141611326
|
||
|
|
||
|
References
|
||
|
==========
|
||
|
|
||
|
.. [1] https://en.wikipedia.org/wiki/Generalizations_of_Fibonacci_numbers#Tribonacci_numbers
|
||
|
"""
|
||
|
|
||
|
is_real = True
|
||
|
is_positive = True
|
||
|
is_negative = False
|
||
|
is_irrational = True
|
||
|
is_number = True
|
||
|
is_algebraic = True
|
||
|
is_transcendental = False
|
||
|
|
||
|
__slots__ = ()
|
||
|
|
||
|
def _latex(self, printer):
|
||
|
return r"\text{TribonacciConstant}"
|
||
|
|
||
|
def __int__(self):
|
||
|
return 1
|
||
|
|
||
|
def _eval_evalf(self, prec):
|
||
|
rv = self._eval_expand_func(function=True)._eval_evalf(prec + 4)
|
||
|
return Float(rv, precision=prec)
|
||
|
|
||
|
def _eval_expand_func(self, **hints):
|
||
|
from sympy.functions.elementary.miscellaneous import cbrt, sqrt
|
||
|
return (1 + cbrt(19 - 3*sqrt(33)) + cbrt(19 + 3*sqrt(33))) / 3
|
||
|
|
||
|
def approximation_interval(self, number_cls):
|
||
|
if issubclass(number_cls, Integer):
|
||
|
return (S.One, Rational(2))
|
||
|
elif issubclass(number_cls, Rational):
|
||
|
pass
|
||
|
|
||
|
_eval_rewrite_as_sqrt = _eval_expand_func
|
||
|
|
||
|
|
||
|
class EulerGamma(NumberSymbol, metaclass=Singleton):
|
||
|
r"""The Euler-Mascheroni constant.
|
||
|
|
||
|
Explanation
|
||
|
===========
|
||
|
|
||
|
`\gamma = 0.5772157\ldots` (also called Euler's constant) is a mathematical
|
||
|
constant recurring in analysis and number theory. It is defined as the
|
||
|
limiting difference between the harmonic series and the
|
||
|
natural logarithm:
|
||
|
|
||
|
.. math:: \gamma = \lim\limits_{n\to\infty}
|
||
|
\left(\sum\limits_{k=1}^n\frac{1}{k} - \ln n\right)
|
||
|
|
||
|
EulerGamma is a singleton, and can be accessed by ``S.EulerGamma``.
|
||
|
|
||
|
Examples
|
||
|
========
|
||
|
|
||
|
>>> from sympy import S
|
||
|
>>> S.EulerGamma.is_irrational
|
||
|
>>> S.EulerGamma > 0
|
||
|
True
|
||
|
>>> S.EulerGamma > 1
|
||
|
False
|
||
|
|
||
|
References
|
||
|
==========
|
||
|
|
||
|
.. [1] https://en.wikipedia.org/wiki/Euler%E2%80%93Mascheroni_constant
|
||
|
"""
|
||
|
|
||
|
is_real = True
|
||
|
is_positive = True
|
||
|
is_negative = False
|
||
|
is_irrational = None
|
||
|
is_number = True
|
||
|
|
||
|
__slots__ = ()
|
||
|
|
||
|
def _latex(self, printer):
|
||
|
return r"\gamma"
|
||
|
|
||
|
def __int__(self):
|
||
|
return 0
|
||
|
|
||
|
def _as_mpf_val(self, prec):
|
||
|
# XXX track down why this has to be increased
|
||
|
v = mlib.libhyper.euler_fixed(prec + 10)
|
||
|
rv = mlib.from_man_exp(v, -prec - 10)
|
||
|
return mpf_norm(rv, prec)
|
||
|
|
||
|
def approximation_interval(self, number_cls):
|
||
|
if issubclass(number_cls, Integer):
|
||
|
return (S.Zero, S.One)
|
||
|
elif issubclass(number_cls, Rational):
|
||
|
return (S.Half, Rational(3, 5, 1))
|
||
|
|
||
|
|
||
|
class Catalan(NumberSymbol, metaclass=Singleton):
|
||
|
r"""Catalan's constant.
|
||
|
|
||
|
Explanation
|
||
|
===========
|
||
|
|
||
|
$G = 0.91596559\ldots$ is given by the infinite series
|
||
|
|
||
|
.. math:: G = \sum_{k=0}^{\infty} \frac{(-1)^k}{(2k+1)^2}
|
||
|
|
||
|
Catalan is a singleton, and can be accessed by ``S.Catalan``.
|
||
|
|
||
|
Examples
|
||
|
========
|
||
|
|
||
|
>>> from sympy import S
|
||
|
>>> S.Catalan.is_irrational
|
||
|
>>> S.Catalan > 0
|
||
|
True
|
||
|
>>> S.Catalan > 1
|
||
|
False
|
||
|
|
||
|
References
|
||
|
==========
|
||
|
|
||
|
.. [1] https://en.wikipedia.org/wiki/Catalan%27s_constant
|
||
|
"""
|
||
|
|
||
|
is_real = True
|
||
|
is_positive = True
|
||
|
is_negative = False
|
||
|
is_irrational = None
|
||
|
is_number = True
|
||
|
|
||
|
__slots__ = ()
|
||
|
|
||
|
def __int__(self):
|
||
|
return 0
|
||
|
|
||
|
def _as_mpf_val(self, prec):
|
||
|
# XXX track down why this has to be increased
|
||
|
v = mlib.catalan_fixed(prec + 10)
|
||
|
rv = mlib.from_man_exp(v, -prec - 10)
|
||
|
return mpf_norm(rv, prec)
|
||
|
|
||
|
def approximation_interval(self, number_cls):
|
||
|
if issubclass(number_cls, Integer):
|
||
|
return (S.Zero, S.One)
|
||
|
elif issubclass(number_cls, Rational):
|
||
|
return (Rational(9, 10, 1), S.One)
|
||
|
|
||
|
def _eval_rewrite_as_Sum(self, k_sym=None, symbols=None):
|
||
|
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 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)
|
||
|
True
|
||
|
|
||
|
In a future version of SymPy this might change so that Rational and Float
|
||
|
compare unequal. This function provides the behavior currently expected of
|
||
|
``==`` so that it could still be used if the behavior of ``==`` were to
|
||
|
change in future.
|
||
|
|
||
|
>>> equal_valued(1, 1.0) # Float vs Rational
|
||
|
True
|
||
|
>>> equal_valued(S(1).n(3), S(1).n(5)) # Floats of different precision
|
||
|
True
|
||
|
|
||
|
Explanation
|
||
|
===========
|
||
|
|
||
|
In future SymPy verions Float and Rational might compare unequal and floats
|
||
|
with different precisions might compare unequal. In that context a function
|
||
|
is needed that can check if a number is equal to 1 or 0 etc. The idea is
|
||
|
that instead of testing ``if x == 1:`` if we want to accept floats like
|
||
|
``1.0`` as well then the test can be written as ``if equal_valued(x, 1):``
|
||
|
or ``if equal_valued(x, 2):``. Since this function is intended to be used
|
||
|
in situations where one or both operands are expected to be concrete
|
||
|
numbers like 1 or 0 the function does not recurse through the args of any
|
||
|
compound expression to compare any nested floats.
|
||
|
|
||
|
References
|
||
|
==========
|
||
|
|
||
|
.. [1] https://github.com/sympy/sympy/pull/20033
|
||
|
"""
|
||
|
x = _sympify(x)
|
||
|
y = _sympify(y)
|
||
|
|
||
|
# Handle everything except Float/Rational first
|
||
|
if not x.is_Float and not y.is_Float:
|
||
|
return x == y
|
||
|
elif x.is_Float and y.is_Float:
|
||
|
# Compare values without regard for precision
|
||
|
return x._mpf_ == y._mpf_
|
||
|
elif x.is_Float:
|
||
|
x, y = y, x
|
||
|
if not x.is_Rational:
|
||
|
return False
|
||
|
|
||
|
# Now y is Float and x is Rational. A simple approach at this point would
|
||
|
# just be x == Rational(y) but if y has a large exponent creating a
|
||
|
# Rational could be prohibitively expensive.
|
||
|
|
||
|
sign, man, exp, _ = y._mpf_
|
||
|
p, q = x.p, x.q
|
||
|
|
||
|
if sign:
|
||
|
man = -man
|
||
|
|
||
|
if exp == 0:
|
||
|
# y odd integer
|
||
|
return q == 1 and man == p
|
||
|
elif exp > 0:
|
||
|
# y even integer
|
||
|
if q != 1:
|
||
|
return False
|
||
|
if p.bit_length() != man.bit_length() + exp:
|
||
|
return False
|
||
|
return man << exp == p
|
||
|
else:
|
||
|
# y non-integer. Need p == man and q == 2**-exp
|
||
|
if p != man:
|
||
|
return False
|
||
|
neg_exp = -exp
|
||
|
if q.bit_length() - 1 != neg_exp:
|
||
|
return False
|
||
|
return (1 << neg_exp) == q
|
||
|
|
||
|
|
||
|
@dispatch(Tuple, Number) # type:ignore
|
||
|
def _eval_is_eq(self, other): # noqa: F811
|
||
|
return False
|
||
|
|
||
|
def sympify_fractions(f):
|
||
|
return Rational(f.numerator, f.denominator, 1)
|
||
|
|
||
|
_sympy_converter[fractions.Fraction] = sympify_fractions
|
||
|
|
||
|
if HAS_GMPY:
|
||
|
def sympify_mpz(x):
|
||
|
return Integer(int(x))
|
||
|
|
||
|
# XXX: The sympify_mpq function here was never used because it is
|
||
|
# overridden by the other sympify_mpq function below. Maybe it should just
|
||
|
# be removed or maybe it should be used for something...
|
||
|
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
|
||
|
|
||
|
|
||
|
def sympify_mpmath_mpq(x):
|
||
|
p, q = x._mpq_
|
||
|
return Rational(p, q, 1)
|
||
|
|
||
|
_sympy_converter[type(mpmath.rational.mpq(1, 2))] = sympify_mpmath_mpq
|
||
|
|
||
|
|
||
|
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, integer_nthroot
|
||
|
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)
|