Add files using upload-large-folder tool
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- lib/python3.12/site-packages/sympy/polys/domains/__init__.py +57 -0
- lib/python3.12/site-packages/sympy/polys/domains/__pycache__/__init__.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/polys/domains/__pycache__/algebraicfield.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/polys/domains/__pycache__/characteristiczero.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/polys/domains/__pycache__/complexfield.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/polys/domains/__pycache__/compositedomain.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/polys/domains/__pycache__/domain.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/polys/domains/__pycache__/domainelement.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/polys/domains/__pycache__/expressiondomain.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/polys/domains/__pycache__/expressionrawdomain.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/polys/domains/__pycache__/field.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/polys/domains/__pycache__/finitefield.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/polys/domains/__pycache__/fractionfield.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/polys/domains/__pycache__/gaussiandomains.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/polys/domains/__pycache__/gmpyfinitefield.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/polys/domains/__pycache__/gmpyintegerring.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/polys/domains/__pycache__/gmpyrationalfield.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/polys/domains/__pycache__/groundtypes.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/polys/domains/__pycache__/integerring.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/polys/domains/__pycache__/modularinteger.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/polys/domains/__pycache__/mpelements.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/polys/domains/__pycache__/old_fractionfield.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/polys/domains/__pycache__/old_polynomialring.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/polys/domains/__pycache__/polynomialring.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/polys/domains/__pycache__/pythonfinitefield.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/polys/domains/__pycache__/pythonintegerring.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/polys/domains/__pycache__/pythonrational.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/polys/domains/__pycache__/pythonrationalfield.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/polys/domains/__pycache__/quotientring.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/polys/domains/__pycache__/rationalfield.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/polys/domains/__pycache__/realfield.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/polys/domains/__pycache__/ring.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/polys/domains/__pycache__/simpledomain.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/polys/domains/algebraicfield.py +607 -0
- lib/python3.12/site-packages/sympy/polys/domains/characteristiczero.py +15 -0
- lib/python3.12/site-packages/sympy/polys/domains/complexfield.py +185 -0
- lib/python3.12/site-packages/sympy/polys/domains/compositedomain.py +52 -0
- lib/python3.12/site-packages/sympy/polys/domains/domain.py +1372 -0
- lib/python3.12/site-packages/sympy/polys/domains/domainelement.py +38 -0
- lib/python3.12/site-packages/sympy/polys/domains/expressiondomain.py +278 -0
- lib/python3.12/site-packages/sympy/polys/domains/expressionrawdomain.py +57 -0
- lib/python3.12/site-packages/sympy/polys/domains/field.py +104 -0
- lib/python3.12/site-packages/sympy/polys/domains/finitefield.py +328 -0
- lib/python3.12/site-packages/sympy/polys/domains/fractionfield.py +177 -0
- lib/python3.12/site-packages/sympy/polys/domains/gaussiandomains.py +686 -0
- lib/python3.12/site-packages/sympy/polys/domains/gmpyfinitefield.py +16 -0
- lib/python3.12/site-packages/sympy/polys/domains/gmpyintegerring.py +105 -0
- lib/python3.12/site-packages/sympy/polys/domains/gmpyrationalfield.py +100 -0
- lib/python3.12/site-packages/sympy/polys/domains/groundtypes.py +99 -0
- lib/python3.12/site-packages/sympy/polys/domains/integerring.py +276 -0
lib/python3.12/site-packages/sympy/polys/domains/__init__.py
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Implementation of mathematical domains. """
|
| 2 |
+
|
| 3 |
+
__all__ = [
|
| 4 |
+
'Domain', 'FiniteField', 'IntegerRing', 'RationalField', 'RealField',
|
| 5 |
+
'ComplexField', 'AlgebraicField', 'PolynomialRing', 'FractionField',
|
| 6 |
+
'ExpressionDomain', 'PythonRational',
|
| 7 |
+
|
| 8 |
+
'GF', 'FF', 'ZZ', 'QQ', 'ZZ_I', 'QQ_I', 'RR', 'CC', 'EX', 'EXRAW',
|
| 9 |
+
]
|
| 10 |
+
|
| 11 |
+
from .domain import Domain
|
| 12 |
+
from .finitefield import FiniteField, FF, GF
|
| 13 |
+
from .integerring import IntegerRing, ZZ
|
| 14 |
+
from .rationalfield import RationalField, QQ
|
| 15 |
+
from .algebraicfield import AlgebraicField
|
| 16 |
+
from .gaussiandomains import ZZ_I, QQ_I
|
| 17 |
+
from .realfield import RealField, RR
|
| 18 |
+
from .complexfield import ComplexField, CC
|
| 19 |
+
from .polynomialring import PolynomialRing
|
| 20 |
+
from .fractionfield import FractionField
|
| 21 |
+
from .expressiondomain import ExpressionDomain, EX
|
| 22 |
+
from .expressionrawdomain import EXRAW
|
| 23 |
+
from .pythonrational import PythonRational
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
# This is imported purely for backwards compatibility because some parts of
|
| 27 |
+
# the codebase used to import this from here and it's possible that downstream
|
| 28 |
+
# does as well:
|
| 29 |
+
from sympy.external.gmpy import GROUND_TYPES # noqa: F401
|
| 30 |
+
|
| 31 |
+
#
|
| 32 |
+
# The rest of these are obsolete and provided only for backwards
|
| 33 |
+
# compatibility:
|
| 34 |
+
#
|
| 35 |
+
|
| 36 |
+
from .pythonfinitefield import PythonFiniteField
|
| 37 |
+
from .gmpyfinitefield import GMPYFiniteField
|
| 38 |
+
from .pythonintegerring import PythonIntegerRing
|
| 39 |
+
from .gmpyintegerring import GMPYIntegerRing
|
| 40 |
+
from .pythonrationalfield import PythonRationalField
|
| 41 |
+
from .gmpyrationalfield import GMPYRationalField
|
| 42 |
+
|
| 43 |
+
FF_python = PythonFiniteField
|
| 44 |
+
FF_gmpy = GMPYFiniteField
|
| 45 |
+
|
| 46 |
+
ZZ_python = PythonIntegerRing
|
| 47 |
+
ZZ_gmpy = GMPYIntegerRing
|
| 48 |
+
|
| 49 |
+
QQ_python = PythonRationalField
|
| 50 |
+
QQ_gmpy = GMPYRationalField
|
| 51 |
+
|
| 52 |
+
__all__.extend((
|
| 53 |
+
'PythonFiniteField', 'GMPYFiniteField', 'PythonIntegerRing',
|
| 54 |
+
'GMPYIntegerRing', 'PythonRational', 'GMPYRationalField',
|
| 55 |
+
|
| 56 |
+
'FF_python', 'FF_gmpy', 'ZZ_python', 'ZZ_gmpy', 'QQ_python', 'QQ_gmpy',
|
| 57 |
+
))
|
lib/python3.12/site-packages/sympy/polys/domains/__pycache__/__init__.cpython-312.pyc
ADDED
|
Binary file (1.8 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/polys/domains/__pycache__/algebraicfield.cpython-312.pyc
ADDED
|
Binary file (27.4 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/polys/domains/__pycache__/characteristiczero.cpython-312.pyc
ADDED
|
Binary file (880 Bytes). View file
|
|
|
lib/python3.12/site-packages/sympy/polys/domains/__pycache__/complexfield.cpython-312.pyc
ADDED
|
Binary file (10.1 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/polys/domains/__pycache__/compositedomain.cpython-312.pyc
ADDED
|
Binary file (3.35 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/polys/domains/__pycache__/domain.cpython-312.pyc
ADDED
|
Binary file (45.8 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/polys/domains/__pycache__/domainelement.cpython-312.pyc
ADDED
|
Binary file (1.37 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/polys/domains/__pycache__/expressiondomain.cpython-312.pyc
ADDED
|
Binary file (15.4 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/polys/domains/__pycache__/expressionrawdomain.cpython-312.pyc
ADDED
|
Binary file (2.73 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/polys/domains/__pycache__/field.cpython-312.pyc
ADDED
|
Binary file (4.24 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/polys/domains/__pycache__/finitefield.cpython-312.pyc
ADDED
|
Binary file (14.1 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/polys/domains/__pycache__/fractionfield.cpython-312.pyc
ADDED
|
Binary file (10.4 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/polys/domains/__pycache__/gaussiandomains.cpython-312.pyc
ADDED
|
Binary file (28.8 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/polys/domains/__pycache__/gmpyfinitefield.cpython-312.pyc
ADDED
|
Binary file (1.03 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/polys/domains/__pycache__/gmpyintegerring.cpython-312.pyc
ADDED
|
Binary file (5.31 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/polys/domains/__pycache__/gmpyrationalfield.cpython-312.pyc
ADDED
|
Binary file (5.59 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/polys/domains/__pycache__/groundtypes.cpython-312.pyc
ADDED
|
Binary file (2.69 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/polys/domains/__pycache__/integerring.cpython-312.pyc
ADDED
|
Binary file (10.6 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/polys/domains/__pycache__/modularinteger.cpython-312.pyc
ADDED
|
Binary file (10.9 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/polys/domains/__pycache__/mpelements.cpython-312.pyc
ADDED
|
Binary file (8.34 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/polys/domains/__pycache__/old_fractionfield.cpython-312.pyc
ADDED
|
Binary file (12 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/polys/domains/__pycache__/old_polynomialring.cpython-312.pyc
ADDED
|
Binary file (25.9 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/polys/domains/__pycache__/polynomialring.cpython-312.pyc
ADDED
|
Binary file (11.9 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/polys/domains/__pycache__/pythonfinitefield.cpython-312.pyc
ADDED
|
Binary file (1.05 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/polys/domains/__pycache__/pythonintegerring.cpython-312.pyc
ADDED
|
Binary file (5.09 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/polys/domains/__pycache__/pythonrational.cpython-312.pyc
ADDED
|
Binary file (924 Bytes). View file
|
|
|
lib/python3.12/site-packages/sympy/polys/domains/__pycache__/pythonrationalfield.cpython-312.pyc
ADDED
|
Binary file (4.14 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/polys/domains/__pycache__/quotientring.cpython-312.pyc
ADDED
|
Binary file (10.7 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/polys/domains/__pycache__/rationalfield.cpython-312.pyc
ADDED
|
Binary file (9.12 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/polys/domains/__pycache__/realfield.cpython-312.pyc
ADDED
|
Binary file (8.37 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/polys/domains/__pycache__/ring.cpython-312.pyc
ADDED
|
Binary file (4.9 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/polys/domains/__pycache__/simpledomain.cpython-312.pyc
ADDED
|
Binary file (871 Bytes). View file
|
|
|
lib/python3.12/site-packages/sympy/polys/domains/algebraicfield.py
ADDED
|
@@ -0,0 +1,607 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Implementation of :class:`AlgebraicField` class. """
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
from sympy.core.add import Add
|
| 5 |
+
from sympy.core.mul import Mul
|
| 6 |
+
from sympy.core.singleton import S
|
| 7 |
+
from sympy.polys.domains.characteristiczero import CharacteristicZero
|
| 8 |
+
from sympy.polys.domains.field import Field
|
| 9 |
+
from sympy.polys.domains.simpledomain import SimpleDomain
|
| 10 |
+
from sympy.polys.polyclasses import ANP
|
| 11 |
+
from sympy.polys.polyerrors import CoercionFailed, DomainError, NotAlgebraic, IsomorphismFailed
|
| 12 |
+
from sympy.utilities import public
|
| 13 |
+
|
| 14 |
+
@public
|
| 15 |
+
class AlgebraicField(Field, CharacteristicZero, SimpleDomain):
|
| 16 |
+
r"""Algebraic number field :ref:`QQ(a)`
|
| 17 |
+
|
| 18 |
+
A :ref:`QQ(a)` domain represents an `algebraic number field`_
|
| 19 |
+
`\mathbb{Q}(a)` as a :py:class:`~.Domain` in the domain system (see
|
| 20 |
+
:ref:`polys-domainsintro`).
|
| 21 |
+
|
| 22 |
+
A :py:class:`~.Poly` created from an expression involving `algebraic
|
| 23 |
+
numbers`_ will treat the algebraic numbers as generators if the generators
|
| 24 |
+
argument is not specified.
|
| 25 |
+
|
| 26 |
+
>>> from sympy import Poly, Symbol, sqrt
|
| 27 |
+
>>> x = Symbol('x')
|
| 28 |
+
>>> Poly(x**2 + sqrt(2))
|
| 29 |
+
Poly(x**2 + (sqrt(2)), x, sqrt(2), domain='ZZ')
|
| 30 |
+
|
| 31 |
+
That is a multivariate polynomial with ``sqrt(2)`` treated as one of the
|
| 32 |
+
generators (variables). If the generators are explicitly specified then
|
| 33 |
+
``sqrt(2)`` will be considered to be a coefficient but by default the
|
| 34 |
+
:ref:`EX` domain is used. To make a :py:class:`~.Poly` with a :ref:`QQ(a)`
|
| 35 |
+
domain the argument ``extension=True`` can be given.
|
| 36 |
+
|
| 37 |
+
>>> Poly(x**2 + sqrt(2), x)
|
| 38 |
+
Poly(x**2 + sqrt(2), x, domain='EX')
|
| 39 |
+
>>> Poly(x**2 + sqrt(2), x, extension=True)
|
| 40 |
+
Poly(x**2 + sqrt(2), x, domain='QQ<sqrt(2)>')
|
| 41 |
+
|
| 42 |
+
A generator of the algebraic field extension can also be specified
|
| 43 |
+
explicitly which is particularly useful if the coefficients are all
|
| 44 |
+
rational but an extension field is needed (e.g. to factor the
|
| 45 |
+
polynomial).
|
| 46 |
+
|
| 47 |
+
>>> Poly(x**2 + 1)
|
| 48 |
+
Poly(x**2 + 1, x, domain='ZZ')
|
| 49 |
+
>>> Poly(x**2 + 1, extension=sqrt(2))
|
| 50 |
+
Poly(x**2 + 1, x, domain='QQ<sqrt(2)>')
|
| 51 |
+
|
| 52 |
+
It is possible to factorise a polynomial over a :ref:`QQ(a)` domain using
|
| 53 |
+
the ``extension`` argument to :py:func:`~.factor` or by specifying the domain
|
| 54 |
+
explicitly.
|
| 55 |
+
|
| 56 |
+
>>> from sympy import factor, QQ
|
| 57 |
+
>>> factor(x**2 - 2)
|
| 58 |
+
x**2 - 2
|
| 59 |
+
>>> factor(x**2 - 2, extension=sqrt(2))
|
| 60 |
+
(x - sqrt(2))*(x + sqrt(2))
|
| 61 |
+
>>> factor(x**2 - 2, domain='QQ<sqrt(2)>')
|
| 62 |
+
(x - sqrt(2))*(x + sqrt(2))
|
| 63 |
+
>>> factor(x**2 - 2, domain=QQ.algebraic_field(sqrt(2)))
|
| 64 |
+
(x - sqrt(2))*(x + sqrt(2))
|
| 65 |
+
|
| 66 |
+
The ``extension=True`` argument can be used but will only create an
|
| 67 |
+
extension that contains the coefficients which is usually not enough to
|
| 68 |
+
factorise the polynomial.
|
| 69 |
+
|
| 70 |
+
>>> p = x**3 + sqrt(2)*x**2 - 2*x - 2*sqrt(2)
|
| 71 |
+
>>> factor(p) # treats sqrt(2) as a symbol
|
| 72 |
+
(x + sqrt(2))*(x**2 - 2)
|
| 73 |
+
>>> factor(p, extension=True)
|
| 74 |
+
(x - sqrt(2))*(x + sqrt(2))**2
|
| 75 |
+
>>> factor(x**2 - 2, extension=True) # all rational coefficients
|
| 76 |
+
x**2 - 2
|
| 77 |
+
|
| 78 |
+
It is also possible to use :ref:`QQ(a)` with the :py:func:`~.cancel`
|
| 79 |
+
and :py:func:`~.gcd` functions.
|
| 80 |
+
|
| 81 |
+
>>> from sympy import cancel, gcd
|
| 82 |
+
>>> cancel((x**2 - 2)/(x - sqrt(2)))
|
| 83 |
+
(x**2 - 2)/(x - sqrt(2))
|
| 84 |
+
>>> cancel((x**2 - 2)/(x - sqrt(2)), extension=sqrt(2))
|
| 85 |
+
x + sqrt(2)
|
| 86 |
+
>>> gcd(x**2 - 2, x - sqrt(2))
|
| 87 |
+
1
|
| 88 |
+
>>> gcd(x**2 - 2, x - sqrt(2), extension=sqrt(2))
|
| 89 |
+
x - sqrt(2)
|
| 90 |
+
|
| 91 |
+
When using the domain directly :ref:`QQ(a)` can be used as a constructor
|
| 92 |
+
to create instances which then support the operations ``+,-,*,**,/``. The
|
| 93 |
+
:py:meth:`~.Domain.algebraic_field` method is used to construct a
|
| 94 |
+
particular :ref:`QQ(a)` domain. The :py:meth:`~.Domain.from_sympy` method
|
| 95 |
+
can be used to create domain elements from normal SymPy expressions.
|
| 96 |
+
|
| 97 |
+
>>> K = QQ.algebraic_field(sqrt(2))
|
| 98 |
+
>>> K
|
| 99 |
+
QQ<sqrt(2)>
|
| 100 |
+
>>> xk = K.from_sympy(3 + 4*sqrt(2))
|
| 101 |
+
>>> xk # doctest: +SKIP
|
| 102 |
+
ANP([4, 3], [1, 0, -2], QQ)
|
| 103 |
+
|
| 104 |
+
Elements of :ref:`QQ(a)` are instances of :py:class:`~.ANP` which have
|
| 105 |
+
limited printing support. The raw display shows the internal
|
| 106 |
+
representation of the element as the list ``[4, 3]`` representing the
|
| 107 |
+
coefficients of ``1`` and ``sqrt(2)`` for this element in the form
|
| 108 |
+
``a * sqrt(2) + b * 1`` where ``a`` and ``b`` are elements of :ref:`QQ`.
|
| 109 |
+
The minimal polynomial for the generator ``(x**2 - 2)`` is also shown in
|
| 110 |
+
the :ref:`dup-representation` as the list ``[1, 0, -2]``. We can use
|
| 111 |
+
:py:meth:`~.Domain.to_sympy` to get a better printed form for the
|
| 112 |
+
elements and to see the results of operations.
|
| 113 |
+
|
| 114 |
+
>>> xk = K.from_sympy(3 + 4*sqrt(2))
|
| 115 |
+
>>> yk = K.from_sympy(2 + 3*sqrt(2))
|
| 116 |
+
>>> xk * yk # doctest: +SKIP
|
| 117 |
+
ANP([17, 30], [1, 0, -2], QQ)
|
| 118 |
+
>>> K.to_sympy(xk * yk)
|
| 119 |
+
17*sqrt(2) + 30
|
| 120 |
+
>>> K.to_sympy(xk + yk)
|
| 121 |
+
5 + 7*sqrt(2)
|
| 122 |
+
>>> K.to_sympy(xk ** 2)
|
| 123 |
+
24*sqrt(2) + 41
|
| 124 |
+
>>> K.to_sympy(xk / yk)
|
| 125 |
+
sqrt(2)/14 + 9/7
|
| 126 |
+
|
| 127 |
+
Any expression representing an algebraic number can be used to generate
|
| 128 |
+
a :ref:`QQ(a)` domain provided its `minimal polynomial`_ can be computed.
|
| 129 |
+
The function :py:func:`~.minpoly` function is used for this.
|
| 130 |
+
|
| 131 |
+
>>> from sympy import exp, I, pi, minpoly
|
| 132 |
+
>>> g = exp(2*I*pi/3)
|
| 133 |
+
>>> g
|
| 134 |
+
exp(2*I*pi/3)
|
| 135 |
+
>>> g.is_algebraic
|
| 136 |
+
True
|
| 137 |
+
>>> minpoly(g, x)
|
| 138 |
+
x**2 + x + 1
|
| 139 |
+
>>> factor(x**3 - 1, extension=g)
|
| 140 |
+
(x - 1)*(x - exp(2*I*pi/3))*(x + 1 + exp(2*I*pi/3))
|
| 141 |
+
|
| 142 |
+
It is also possible to make an algebraic field from multiple extension
|
| 143 |
+
elements.
|
| 144 |
+
|
| 145 |
+
>>> K = QQ.algebraic_field(sqrt(2), sqrt(3))
|
| 146 |
+
>>> K
|
| 147 |
+
QQ<sqrt(2) + sqrt(3)>
|
| 148 |
+
>>> p = x**4 - 5*x**2 + 6
|
| 149 |
+
>>> factor(p)
|
| 150 |
+
(x**2 - 3)*(x**2 - 2)
|
| 151 |
+
>>> factor(p, domain=K)
|
| 152 |
+
(x - sqrt(2))*(x + sqrt(2))*(x - sqrt(3))*(x + sqrt(3))
|
| 153 |
+
>>> factor(p, extension=[sqrt(2), sqrt(3)])
|
| 154 |
+
(x - sqrt(2))*(x + sqrt(2))*(x - sqrt(3))*(x + sqrt(3))
|
| 155 |
+
|
| 156 |
+
Multiple extension elements are always combined together to make a single
|
| 157 |
+
`primitive element`_. In the case of ``[sqrt(2), sqrt(3)]`` the primitive
|
| 158 |
+
element chosen is ``sqrt(2) + sqrt(3)`` which is why the domain displays
|
| 159 |
+
as ``QQ<sqrt(2) + sqrt(3)>``. The minimal polynomial for the primitive
|
| 160 |
+
element is computed using the :py:func:`~.primitive_element` function.
|
| 161 |
+
|
| 162 |
+
>>> from sympy import primitive_element
|
| 163 |
+
>>> primitive_element([sqrt(2), sqrt(3)], x)
|
| 164 |
+
(x**4 - 10*x**2 + 1, [1, 1])
|
| 165 |
+
>>> minpoly(sqrt(2) + sqrt(3), x)
|
| 166 |
+
x**4 - 10*x**2 + 1
|
| 167 |
+
|
| 168 |
+
The extension elements that generate the domain can be accessed from the
|
| 169 |
+
domain using the :py:attr:`~.ext` and :py:attr:`~.orig_ext` attributes as
|
| 170 |
+
instances of :py:class:`~.AlgebraicNumber`. The minimal polynomial for
|
| 171 |
+
the primitive element as a :py:class:`~.DMP` instance is available as
|
| 172 |
+
:py:attr:`~.mod`.
|
| 173 |
+
|
| 174 |
+
>>> K = QQ.algebraic_field(sqrt(2), sqrt(3))
|
| 175 |
+
>>> K
|
| 176 |
+
QQ<sqrt(2) + sqrt(3)>
|
| 177 |
+
>>> K.ext
|
| 178 |
+
sqrt(2) + sqrt(3)
|
| 179 |
+
>>> K.orig_ext
|
| 180 |
+
(sqrt(2), sqrt(3))
|
| 181 |
+
>>> K.mod # doctest: +SKIP
|
| 182 |
+
DMP_Python([1, 0, -10, 0, 1], QQ)
|
| 183 |
+
|
| 184 |
+
The `discriminant`_ of the field can be obtained from the
|
| 185 |
+
:py:meth:`~.discriminant` method, and an `integral basis`_ from the
|
| 186 |
+
:py:meth:`~.integral_basis` method. The latter returns a list of
|
| 187 |
+
:py:class:`~.ANP` instances by default, but can be made to return instances
|
| 188 |
+
of :py:class:`~.Expr` or :py:class:`~.AlgebraicNumber` by passing a ``fmt``
|
| 189 |
+
argument. The maximal order, or ring of integers, of the field can also be
|
| 190 |
+
obtained from the :py:meth:`~.maximal_order` method, as a
|
| 191 |
+
:py:class:`~sympy.polys.numberfields.modules.Submodule`.
|
| 192 |
+
|
| 193 |
+
>>> zeta5 = exp(2*I*pi/5)
|
| 194 |
+
>>> K = QQ.algebraic_field(zeta5)
|
| 195 |
+
>>> K
|
| 196 |
+
QQ<exp(2*I*pi/5)>
|
| 197 |
+
>>> K.discriminant()
|
| 198 |
+
125
|
| 199 |
+
>>> K = QQ.algebraic_field(sqrt(5))
|
| 200 |
+
>>> K
|
| 201 |
+
QQ<sqrt(5)>
|
| 202 |
+
>>> K.integral_basis(fmt='sympy')
|
| 203 |
+
[1, 1/2 + sqrt(5)/2]
|
| 204 |
+
>>> K.maximal_order()
|
| 205 |
+
Submodule[[2, 0], [1, 1]]/2
|
| 206 |
+
|
| 207 |
+
The factorization of a rational prime into prime ideals of the field is
|
| 208 |
+
computed by the :py:meth:`~.primes_above` method, which returns a list
|
| 209 |
+
of :py:class:`~sympy.polys.numberfields.primes.PrimeIdeal` instances.
|
| 210 |
+
|
| 211 |
+
>>> zeta7 = exp(2*I*pi/7)
|
| 212 |
+
>>> K = QQ.algebraic_field(zeta7)
|
| 213 |
+
>>> K
|
| 214 |
+
QQ<exp(2*I*pi/7)>
|
| 215 |
+
>>> K.primes_above(11)
|
| 216 |
+
[(11, _x**3 + 5*_x**2 + 4*_x - 1), (11, _x**3 - 4*_x**2 - 5*_x - 1)]
|
| 217 |
+
|
| 218 |
+
The Galois group of the Galois closure of the field can be computed (when
|
| 219 |
+
the minimal polynomial of the field is of sufficiently small degree).
|
| 220 |
+
|
| 221 |
+
>>> K.galois_group(by_name=True)[0]
|
| 222 |
+
S6TransitiveSubgroups.C6
|
| 223 |
+
|
| 224 |
+
Notes
|
| 225 |
+
=====
|
| 226 |
+
|
| 227 |
+
It is not currently possible to generate an algebraic extension over any
|
| 228 |
+
domain other than :ref:`QQ`. Ideally it would be possible to generate
|
| 229 |
+
extensions like ``QQ(x)(sqrt(x**2 - 2))``. This is equivalent to the
|
| 230 |
+
quotient ring ``QQ(x)[y]/(y**2 - x**2 + 2)`` and there are two
|
| 231 |
+
implementations of this kind of quotient ring/extension in the
|
| 232 |
+
:py:class:`~.QuotientRing` and :py:class:`~.MonogenicFiniteExtension`
|
| 233 |
+
classes. Each of those implementations needs some work to make them fully
|
| 234 |
+
usable though.
|
| 235 |
+
|
| 236 |
+
.. _algebraic number field: https://en.wikipedia.org/wiki/Algebraic_number_field
|
| 237 |
+
.. _algebraic numbers: https://en.wikipedia.org/wiki/Algebraic_number
|
| 238 |
+
.. _discriminant: https://en.wikipedia.org/wiki/Discriminant_of_an_algebraic_number_field
|
| 239 |
+
.. _integral basis: https://en.wikipedia.org/wiki/Algebraic_number_field#Integral_basis
|
| 240 |
+
.. _minimal polynomial: https://en.wikipedia.org/wiki/Minimal_polynomial_(field_theory)
|
| 241 |
+
.. _primitive element: https://en.wikipedia.org/wiki/Primitive_element_theorem
|
| 242 |
+
"""
|
| 243 |
+
|
| 244 |
+
dtype = ANP
|
| 245 |
+
|
| 246 |
+
is_AlgebraicField = is_Algebraic = True
|
| 247 |
+
is_Numerical = True
|
| 248 |
+
|
| 249 |
+
has_assoc_Ring = False
|
| 250 |
+
has_assoc_Field = True
|
| 251 |
+
|
| 252 |
+
def __init__(self, dom, *ext, alias=None):
|
| 253 |
+
r"""
|
| 254 |
+
Parameters
|
| 255 |
+
==========
|
| 256 |
+
|
| 257 |
+
dom : :py:class:`~.Domain`
|
| 258 |
+
The base field over which this is an extension field.
|
| 259 |
+
Currently only :ref:`QQ` is accepted.
|
| 260 |
+
|
| 261 |
+
*ext : One or more :py:class:`~.Expr`
|
| 262 |
+
Generators of the extension. These should be expressions that are
|
| 263 |
+
algebraic over `\mathbb{Q}`.
|
| 264 |
+
|
| 265 |
+
alias : str, :py:class:`~.Symbol`, None, optional (default=None)
|
| 266 |
+
If provided, this will be used as the alias symbol for the
|
| 267 |
+
primitive element of the :py:class:`~.AlgebraicField`.
|
| 268 |
+
If ``None``, while ``ext`` consists of exactly one
|
| 269 |
+
:py:class:`~.AlgebraicNumber`, its alias (if any) will be used.
|
| 270 |
+
"""
|
| 271 |
+
if not dom.is_QQ:
|
| 272 |
+
raise DomainError("ground domain must be a rational field")
|
| 273 |
+
|
| 274 |
+
from sympy.polys.numberfields import to_number_field
|
| 275 |
+
if len(ext) == 1 and isinstance(ext[0], tuple):
|
| 276 |
+
orig_ext = ext[0][1:]
|
| 277 |
+
else:
|
| 278 |
+
orig_ext = ext
|
| 279 |
+
|
| 280 |
+
if alias is None and len(ext) == 1:
|
| 281 |
+
alias = getattr(ext[0], 'alias', None)
|
| 282 |
+
|
| 283 |
+
self.orig_ext = orig_ext
|
| 284 |
+
"""
|
| 285 |
+
Original elements given to generate the extension.
|
| 286 |
+
|
| 287 |
+
>>> from sympy import QQ, sqrt
|
| 288 |
+
>>> K = QQ.algebraic_field(sqrt(2), sqrt(3))
|
| 289 |
+
>>> K.orig_ext
|
| 290 |
+
(sqrt(2), sqrt(3))
|
| 291 |
+
"""
|
| 292 |
+
|
| 293 |
+
self.ext = to_number_field(ext, alias=alias)
|
| 294 |
+
"""
|
| 295 |
+
Primitive element used for the extension.
|
| 296 |
+
|
| 297 |
+
>>> from sympy import QQ, sqrt
|
| 298 |
+
>>> K = QQ.algebraic_field(sqrt(2), sqrt(3))
|
| 299 |
+
>>> K.ext
|
| 300 |
+
sqrt(2) + sqrt(3)
|
| 301 |
+
"""
|
| 302 |
+
|
| 303 |
+
self.mod = self.ext.minpoly.rep
|
| 304 |
+
"""
|
| 305 |
+
Minimal polynomial for the primitive element of the extension.
|
| 306 |
+
|
| 307 |
+
>>> from sympy import QQ, sqrt
|
| 308 |
+
>>> K = QQ.algebraic_field(sqrt(2))
|
| 309 |
+
>>> K.mod
|
| 310 |
+
DMP([1, 0, -2], QQ)
|
| 311 |
+
"""
|
| 312 |
+
|
| 313 |
+
self.domain = self.dom = dom
|
| 314 |
+
|
| 315 |
+
self.ngens = 1
|
| 316 |
+
self.symbols = self.gens = (self.ext,)
|
| 317 |
+
self.unit = self([dom(1), dom(0)])
|
| 318 |
+
|
| 319 |
+
self.zero = self.dtype.zero(self.mod.to_list(), dom)
|
| 320 |
+
self.one = self.dtype.one(self.mod.to_list(), dom)
|
| 321 |
+
|
| 322 |
+
self._maximal_order = None
|
| 323 |
+
self._discriminant = None
|
| 324 |
+
self._nilradicals_mod_p = {}
|
| 325 |
+
|
| 326 |
+
def new(self, element):
|
| 327 |
+
return self.dtype(element, self.mod.to_list(), self.dom)
|
| 328 |
+
|
| 329 |
+
def __str__(self):
|
| 330 |
+
return str(self.dom) + '<' + str(self.ext) + '>'
|
| 331 |
+
|
| 332 |
+
def __hash__(self):
|
| 333 |
+
return hash((self.__class__.__name__, self.dtype, self.dom, self.ext))
|
| 334 |
+
|
| 335 |
+
def __eq__(self, other):
|
| 336 |
+
"""Returns ``True`` if two domains are equivalent. """
|
| 337 |
+
if isinstance(other, AlgebraicField):
|
| 338 |
+
return self.dtype == other.dtype and self.ext == other.ext
|
| 339 |
+
else:
|
| 340 |
+
return NotImplemented
|
| 341 |
+
|
| 342 |
+
def algebraic_field(self, *extension, alias=None):
|
| 343 |
+
r"""Returns an algebraic field, i.e. `\mathbb{Q}(\alpha, \ldots)`. """
|
| 344 |
+
return AlgebraicField(self.dom, *((self.ext,) + extension), alias=alias)
|
| 345 |
+
|
| 346 |
+
def to_alg_num(self, a):
|
| 347 |
+
"""Convert ``a`` of ``dtype`` to an :py:class:`~.AlgebraicNumber`. """
|
| 348 |
+
return self.ext.field_element(a)
|
| 349 |
+
|
| 350 |
+
def to_sympy(self, a):
|
| 351 |
+
"""Convert ``a`` of ``dtype`` to a SymPy object. """
|
| 352 |
+
# Precompute a converter to be reused:
|
| 353 |
+
if not hasattr(self, '_converter'):
|
| 354 |
+
self._converter = _make_converter(self)
|
| 355 |
+
|
| 356 |
+
return self._converter(a)
|
| 357 |
+
|
| 358 |
+
def from_sympy(self, a):
|
| 359 |
+
"""Convert SymPy's expression to ``dtype``. """
|
| 360 |
+
try:
|
| 361 |
+
return self([self.dom.from_sympy(a)])
|
| 362 |
+
except CoercionFailed:
|
| 363 |
+
pass
|
| 364 |
+
|
| 365 |
+
from sympy.polys.numberfields import to_number_field
|
| 366 |
+
|
| 367 |
+
try:
|
| 368 |
+
return self(to_number_field(a, self.ext).native_coeffs())
|
| 369 |
+
except (NotAlgebraic, IsomorphismFailed):
|
| 370 |
+
raise CoercionFailed(
|
| 371 |
+
"%s is not a valid algebraic number in %s" % (a, self))
|
| 372 |
+
|
| 373 |
+
def from_ZZ(K1, a, K0):
|
| 374 |
+
"""Convert a Python ``int`` object to ``dtype``. """
|
| 375 |
+
return K1(K1.dom.convert(a, K0))
|
| 376 |
+
|
| 377 |
+
def from_ZZ_python(K1, a, K0):
|
| 378 |
+
"""Convert a Python ``int`` object to ``dtype``. """
|
| 379 |
+
return K1(K1.dom.convert(a, K0))
|
| 380 |
+
|
| 381 |
+
def from_QQ(K1, a, K0):
|
| 382 |
+
"""Convert a Python ``Fraction`` object to ``dtype``. """
|
| 383 |
+
return K1(K1.dom.convert(a, K0))
|
| 384 |
+
|
| 385 |
+
def from_QQ_python(K1, a, K0):
|
| 386 |
+
"""Convert a Python ``Fraction`` object to ``dtype``. """
|
| 387 |
+
return K1(K1.dom.convert(a, K0))
|
| 388 |
+
|
| 389 |
+
def from_ZZ_gmpy(K1, a, K0):
|
| 390 |
+
"""Convert a GMPY ``mpz`` object to ``dtype``. """
|
| 391 |
+
return K1(K1.dom.convert(a, K0))
|
| 392 |
+
|
| 393 |
+
def from_QQ_gmpy(K1, a, K0):
|
| 394 |
+
"""Convert a GMPY ``mpq`` object to ``dtype``. """
|
| 395 |
+
return K1(K1.dom.convert(a, K0))
|
| 396 |
+
|
| 397 |
+
def from_RealField(K1, a, K0):
|
| 398 |
+
"""Convert a mpmath ``mpf`` object to ``dtype``. """
|
| 399 |
+
return K1(K1.dom.convert(a, K0))
|
| 400 |
+
|
| 401 |
+
def get_ring(self):
|
| 402 |
+
"""Returns a ring associated with ``self``. """
|
| 403 |
+
raise DomainError('there is no ring associated with %s' % self)
|
| 404 |
+
|
| 405 |
+
def is_positive(self, a):
|
| 406 |
+
"""Returns True if ``a`` is positive. """
|
| 407 |
+
return self.dom.is_positive(a.LC())
|
| 408 |
+
|
| 409 |
+
def is_negative(self, a):
|
| 410 |
+
"""Returns True if ``a`` is negative. """
|
| 411 |
+
return self.dom.is_negative(a.LC())
|
| 412 |
+
|
| 413 |
+
def is_nonpositive(self, a):
|
| 414 |
+
"""Returns True if ``a`` is non-positive. """
|
| 415 |
+
return self.dom.is_nonpositive(a.LC())
|
| 416 |
+
|
| 417 |
+
def is_nonnegative(self, a):
|
| 418 |
+
"""Returns True if ``a`` is non-negative. """
|
| 419 |
+
return self.dom.is_nonnegative(a.LC())
|
| 420 |
+
|
| 421 |
+
def numer(self, a):
|
| 422 |
+
"""Returns numerator of ``a``. """
|
| 423 |
+
return a
|
| 424 |
+
|
| 425 |
+
def denom(self, a):
|
| 426 |
+
"""Returns denominator of ``a``. """
|
| 427 |
+
return self.one
|
| 428 |
+
|
| 429 |
+
def from_AlgebraicField(K1, a, K0):
|
| 430 |
+
"""Convert AlgebraicField element 'a' to another AlgebraicField """
|
| 431 |
+
return K1.from_sympy(K0.to_sympy(a))
|
| 432 |
+
|
| 433 |
+
def from_GaussianIntegerRing(K1, a, K0):
|
| 434 |
+
"""Convert a GaussianInteger element 'a' to ``dtype``. """
|
| 435 |
+
return K1.from_sympy(K0.to_sympy(a))
|
| 436 |
+
|
| 437 |
+
def from_GaussianRationalField(K1, a, K0):
|
| 438 |
+
"""Convert a GaussianRational element 'a' to ``dtype``. """
|
| 439 |
+
return K1.from_sympy(K0.to_sympy(a))
|
| 440 |
+
|
| 441 |
+
def _do_round_two(self):
|
| 442 |
+
from sympy.polys.numberfields.basis import round_two
|
| 443 |
+
ZK, dK = round_two(self, radicals=self._nilradicals_mod_p)
|
| 444 |
+
self._maximal_order = ZK
|
| 445 |
+
self._discriminant = dK
|
| 446 |
+
|
| 447 |
+
def maximal_order(self):
|
| 448 |
+
"""
|
| 449 |
+
Compute the maximal order, or ring of integers, of the field.
|
| 450 |
+
|
| 451 |
+
Returns
|
| 452 |
+
=======
|
| 453 |
+
|
| 454 |
+
:py:class:`~sympy.polys.numberfields.modules.Submodule`.
|
| 455 |
+
|
| 456 |
+
See Also
|
| 457 |
+
========
|
| 458 |
+
|
| 459 |
+
integral_basis
|
| 460 |
+
|
| 461 |
+
"""
|
| 462 |
+
if self._maximal_order is None:
|
| 463 |
+
self._do_round_two()
|
| 464 |
+
return self._maximal_order
|
| 465 |
+
|
| 466 |
+
def integral_basis(self, fmt=None):
|
| 467 |
+
r"""
|
| 468 |
+
Get an integral basis for the field.
|
| 469 |
+
|
| 470 |
+
Parameters
|
| 471 |
+
==========
|
| 472 |
+
|
| 473 |
+
fmt : str, None, optional (default=None)
|
| 474 |
+
If ``None``, return a list of :py:class:`~.ANP` instances.
|
| 475 |
+
If ``"sympy"``, convert each element of the list to an
|
| 476 |
+
:py:class:`~.Expr`, using ``self.to_sympy()``.
|
| 477 |
+
If ``"alg"``, convert each element of the list to an
|
| 478 |
+
:py:class:`~.AlgebraicNumber`, using ``self.to_alg_num()``.
|
| 479 |
+
|
| 480 |
+
Examples
|
| 481 |
+
========
|
| 482 |
+
|
| 483 |
+
>>> from sympy import QQ, AlgebraicNumber, sqrt
|
| 484 |
+
>>> alpha = AlgebraicNumber(sqrt(5), alias='alpha')
|
| 485 |
+
>>> k = QQ.algebraic_field(alpha)
|
| 486 |
+
>>> B0 = k.integral_basis()
|
| 487 |
+
>>> B1 = k.integral_basis(fmt='sympy')
|
| 488 |
+
>>> B2 = k.integral_basis(fmt='alg')
|
| 489 |
+
>>> print(B0[1]) # doctest: +SKIP
|
| 490 |
+
ANP([mpq(1,2), mpq(1,2)], [mpq(1,1), mpq(0,1), mpq(-5,1)], QQ)
|
| 491 |
+
>>> print(B1[1])
|
| 492 |
+
1/2 + alpha/2
|
| 493 |
+
>>> print(B2[1])
|
| 494 |
+
alpha/2 + 1/2
|
| 495 |
+
|
| 496 |
+
In the last two cases we get legible expressions, which print somewhat
|
| 497 |
+
differently because of the different types involved:
|
| 498 |
+
|
| 499 |
+
>>> print(type(B1[1]))
|
| 500 |
+
<class 'sympy.core.add.Add'>
|
| 501 |
+
>>> print(type(B2[1]))
|
| 502 |
+
<class 'sympy.core.numbers.AlgebraicNumber'>
|
| 503 |
+
|
| 504 |
+
See Also
|
| 505 |
+
========
|
| 506 |
+
|
| 507 |
+
to_sympy
|
| 508 |
+
to_alg_num
|
| 509 |
+
maximal_order
|
| 510 |
+
"""
|
| 511 |
+
ZK = self.maximal_order()
|
| 512 |
+
M = ZK.QQ_matrix
|
| 513 |
+
n = M.shape[1]
|
| 514 |
+
B = [self.new(list(reversed(M[:, j].flat()))) for j in range(n)]
|
| 515 |
+
if fmt == 'sympy':
|
| 516 |
+
return [self.to_sympy(b) for b in B]
|
| 517 |
+
elif fmt == 'alg':
|
| 518 |
+
return [self.to_alg_num(b) for b in B]
|
| 519 |
+
return B
|
| 520 |
+
|
| 521 |
+
def discriminant(self):
|
| 522 |
+
"""Get the discriminant of the field."""
|
| 523 |
+
if self._discriminant is None:
|
| 524 |
+
self._do_round_two()
|
| 525 |
+
return self._discriminant
|
| 526 |
+
|
| 527 |
+
def primes_above(self, p):
|
| 528 |
+
"""Compute the prime ideals lying above a given rational prime *p*."""
|
| 529 |
+
from sympy.polys.numberfields.primes import prime_decomp
|
| 530 |
+
ZK = self.maximal_order()
|
| 531 |
+
dK = self.discriminant()
|
| 532 |
+
rad = self._nilradicals_mod_p.get(p)
|
| 533 |
+
return prime_decomp(p, ZK=ZK, dK=dK, radical=rad)
|
| 534 |
+
|
| 535 |
+
def galois_group(self, by_name=False, max_tries=30, randomize=False):
|
| 536 |
+
"""
|
| 537 |
+
Compute the Galois group of the Galois closure of this field.
|
| 538 |
+
|
| 539 |
+
Examples
|
| 540 |
+
========
|
| 541 |
+
|
| 542 |
+
If the field is Galois, the order of the group will equal the degree
|
| 543 |
+
of the field:
|
| 544 |
+
|
| 545 |
+
>>> from sympy import QQ
|
| 546 |
+
>>> from sympy.abc import x
|
| 547 |
+
>>> k = QQ.alg_field_from_poly(x**4 + 1)
|
| 548 |
+
>>> G, _ = k.galois_group()
|
| 549 |
+
>>> G.order()
|
| 550 |
+
4
|
| 551 |
+
|
| 552 |
+
If the field is not Galois, then its Galois closure is a proper
|
| 553 |
+
extension, and the order of the Galois group will be greater than the
|
| 554 |
+
degree of the field:
|
| 555 |
+
|
| 556 |
+
>>> k = QQ.alg_field_from_poly(x**4 - 2)
|
| 557 |
+
>>> G, _ = k.galois_group()
|
| 558 |
+
>>> G.order()
|
| 559 |
+
8
|
| 560 |
+
|
| 561 |
+
See Also
|
| 562 |
+
========
|
| 563 |
+
|
| 564 |
+
sympy.polys.numberfields.galoisgroups.galois_group
|
| 565 |
+
|
| 566 |
+
"""
|
| 567 |
+
return self.ext.minpoly_of_element().galois_group(
|
| 568 |
+
by_name=by_name, max_tries=max_tries, randomize=randomize)
|
| 569 |
+
|
| 570 |
+
|
| 571 |
+
def _make_converter(K):
|
| 572 |
+
"""Construct the converter to convert back to Expr"""
|
| 573 |
+
# Precompute the effect of converting to SymPy and expanding expressions
|
| 574 |
+
# like (sqrt(2) + sqrt(3))**2. Asking Expr to do the expansion on every
|
| 575 |
+
# conversion from K to Expr is slow. Here we compute the expansions for
|
| 576 |
+
# each power of the generator and collect together the resulting algebraic
|
| 577 |
+
# terms and the rational coefficients into a matrix.
|
| 578 |
+
|
| 579 |
+
gen = K.ext.as_expr()
|
| 580 |
+
todom = K.dom.from_sympy
|
| 581 |
+
|
| 582 |
+
# We'll let Expr compute the expansions. We won't make any presumptions
|
| 583 |
+
# about what this results in except that it is QQ-linear in some terms
|
| 584 |
+
# that we will call algebraics. The final result will be expressed in
|
| 585 |
+
# terms of those.
|
| 586 |
+
powers = [S.One, gen]
|
| 587 |
+
for n in range(2, K.mod.degree()):
|
| 588 |
+
powers.append((gen * powers[-1]).expand())
|
| 589 |
+
|
| 590 |
+
# Collect the rational coefficients and algebraic Expr that can
|
| 591 |
+
# map the ANP coefficients into an expanded SymPy expression
|
| 592 |
+
terms = [dict(t.as_coeff_Mul()[::-1] for t in Add.make_args(p)) for p in powers]
|
| 593 |
+
algebraics = set().union(*terms)
|
| 594 |
+
matrix = [[todom(t.get(a, S.Zero)) for t in terms] for a in algebraics]
|
| 595 |
+
|
| 596 |
+
# Create a function to do the conversion efficiently:
|
| 597 |
+
|
| 598 |
+
def converter(a):
|
| 599 |
+
"""Convert a to Expr using converter"""
|
| 600 |
+
ai = a.to_list()[::-1]
|
| 601 |
+
tosympy = K.dom.to_sympy
|
| 602 |
+
coeffs_dom = [sum(mij*aj for mij, aj in zip(mi, ai)) for mi in matrix]
|
| 603 |
+
coeffs_sympy = [tosympy(c) for c in coeffs_dom]
|
| 604 |
+
res = Add(*(Mul(c, a) for c, a in zip(coeffs_sympy, algebraics)))
|
| 605 |
+
return res
|
| 606 |
+
|
| 607 |
+
return converter
|
lib/python3.12/site-packages/sympy/polys/domains/characteristiczero.py
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Implementation of :class:`CharacteristicZero` class. """
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
from sympy.polys.domains.domain import Domain
|
| 5 |
+
from sympy.utilities import public
|
| 6 |
+
|
| 7 |
+
@public
|
| 8 |
+
class CharacteristicZero(Domain):
|
| 9 |
+
"""Domain that has infinite number of elements. """
|
| 10 |
+
|
| 11 |
+
has_CharacteristicZero = True
|
| 12 |
+
|
| 13 |
+
def characteristic(self):
|
| 14 |
+
"""Return the characteristic of this domain. """
|
| 15 |
+
return 0
|
lib/python3.12/site-packages/sympy/polys/domains/complexfield.py
ADDED
|
@@ -0,0 +1,185 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Implementation of :class:`ComplexField` class. """
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
from sympy.external.gmpy import SYMPY_INTS
|
| 5 |
+
from sympy.core.numbers import Float, I
|
| 6 |
+
from sympy.polys.domains.characteristiczero import CharacteristicZero
|
| 7 |
+
from sympy.polys.domains.field import Field
|
| 8 |
+
from sympy.polys.domains.gaussiandomains import QQ_I
|
| 9 |
+
from sympy.polys.domains.mpelements import MPContext
|
| 10 |
+
from sympy.polys.domains.simpledomain import SimpleDomain
|
| 11 |
+
from sympy.polys.polyerrors import DomainError, CoercionFailed
|
| 12 |
+
from sympy.utilities import public
|
| 13 |
+
|
| 14 |
+
@public
|
| 15 |
+
class ComplexField(Field, CharacteristicZero, SimpleDomain):
|
| 16 |
+
"""Complex numbers up to the given precision. """
|
| 17 |
+
|
| 18 |
+
rep = 'CC'
|
| 19 |
+
|
| 20 |
+
is_ComplexField = is_CC = True
|
| 21 |
+
|
| 22 |
+
is_Exact = False
|
| 23 |
+
is_Numerical = True
|
| 24 |
+
|
| 25 |
+
has_assoc_Ring = False
|
| 26 |
+
has_assoc_Field = True
|
| 27 |
+
|
| 28 |
+
_default_precision = 53
|
| 29 |
+
|
| 30 |
+
@property
|
| 31 |
+
def has_default_precision(self):
|
| 32 |
+
return self.precision == self._default_precision
|
| 33 |
+
|
| 34 |
+
@property
|
| 35 |
+
def precision(self):
|
| 36 |
+
return self._context.prec
|
| 37 |
+
|
| 38 |
+
@property
|
| 39 |
+
def dps(self):
|
| 40 |
+
return self._context.dps
|
| 41 |
+
|
| 42 |
+
@property
|
| 43 |
+
def tolerance(self):
|
| 44 |
+
return self._context.tolerance
|
| 45 |
+
|
| 46 |
+
def __init__(self, prec=_default_precision, dps=None, tol=None):
|
| 47 |
+
context = MPContext(prec, dps, tol, False)
|
| 48 |
+
context._parent = self
|
| 49 |
+
self._context = context
|
| 50 |
+
|
| 51 |
+
self._dtype = context.mpc
|
| 52 |
+
self.zero = self.dtype(0)
|
| 53 |
+
self.one = self.dtype(1)
|
| 54 |
+
|
| 55 |
+
@property
|
| 56 |
+
def tp(self):
|
| 57 |
+
# XXX: Domain treats tp as an alis of dtype. Here we need to two
|
| 58 |
+
# separate things: dtype is a callable to make/convert instances.
|
| 59 |
+
# We use tp with isinstance to check if an object is an instance
|
| 60 |
+
# of the domain already.
|
| 61 |
+
return self._dtype
|
| 62 |
+
|
| 63 |
+
def dtype(self, x, y=0):
|
| 64 |
+
# XXX: This is needed because mpmath does not recognise fmpz.
|
| 65 |
+
# It might be better to add conversion routines to mpmath and if that
|
| 66 |
+
# happens then this can be removed.
|
| 67 |
+
if isinstance(x, SYMPY_INTS):
|
| 68 |
+
x = int(x)
|
| 69 |
+
if isinstance(y, SYMPY_INTS):
|
| 70 |
+
y = int(y)
|
| 71 |
+
return self._dtype(x, y)
|
| 72 |
+
|
| 73 |
+
def __eq__(self, other):
|
| 74 |
+
return (isinstance(other, ComplexField)
|
| 75 |
+
and self.precision == other.precision
|
| 76 |
+
and self.tolerance == other.tolerance)
|
| 77 |
+
|
| 78 |
+
def __hash__(self):
|
| 79 |
+
return hash((self.__class__.__name__, self._dtype, self.precision, self.tolerance))
|
| 80 |
+
|
| 81 |
+
def to_sympy(self, element):
|
| 82 |
+
"""Convert ``element`` to SymPy number. """
|
| 83 |
+
return Float(element.real, self.dps) + I*Float(element.imag, self.dps)
|
| 84 |
+
|
| 85 |
+
def from_sympy(self, expr):
|
| 86 |
+
"""Convert SymPy's number to ``dtype``. """
|
| 87 |
+
number = expr.evalf(n=self.dps)
|
| 88 |
+
real, imag = number.as_real_imag()
|
| 89 |
+
|
| 90 |
+
if real.is_Number and imag.is_Number:
|
| 91 |
+
return self.dtype(real, imag)
|
| 92 |
+
else:
|
| 93 |
+
raise CoercionFailed("expected complex number, got %s" % expr)
|
| 94 |
+
|
| 95 |
+
def from_ZZ(self, element, base):
|
| 96 |
+
return self.dtype(element)
|
| 97 |
+
|
| 98 |
+
def from_ZZ_gmpy(self, element, base):
|
| 99 |
+
return self.dtype(int(element))
|
| 100 |
+
|
| 101 |
+
def from_ZZ_python(self, element, base):
|
| 102 |
+
return self.dtype(element)
|
| 103 |
+
|
| 104 |
+
def from_QQ(self, element, base):
|
| 105 |
+
return self.dtype(int(element.numerator)) / int(element.denominator)
|
| 106 |
+
|
| 107 |
+
def from_QQ_python(self, element, base):
|
| 108 |
+
return self.dtype(element.numerator) / element.denominator
|
| 109 |
+
|
| 110 |
+
def from_QQ_gmpy(self, element, base):
|
| 111 |
+
return self.dtype(int(element.numerator)) / int(element.denominator)
|
| 112 |
+
|
| 113 |
+
def from_GaussianIntegerRing(self, element, base):
|
| 114 |
+
return self.dtype(int(element.x), int(element.y))
|
| 115 |
+
|
| 116 |
+
def from_GaussianRationalField(self, element, base):
|
| 117 |
+
x = element.x
|
| 118 |
+
y = element.y
|
| 119 |
+
return (self.dtype(int(x.numerator)) / int(x.denominator) +
|
| 120 |
+
self.dtype(0, int(y.numerator)) / int(y.denominator))
|
| 121 |
+
|
| 122 |
+
def from_AlgebraicField(self, element, base):
|
| 123 |
+
return self.from_sympy(base.to_sympy(element).evalf(self.dps))
|
| 124 |
+
|
| 125 |
+
def from_RealField(self, element, base):
|
| 126 |
+
return self.dtype(element)
|
| 127 |
+
|
| 128 |
+
def from_ComplexField(self, element, base):
|
| 129 |
+
if self == base:
|
| 130 |
+
return element
|
| 131 |
+
else:
|
| 132 |
+
return self.dtype(element)
|
| 133 |
+
|
| 134 |
+
def get_ring(self):
|
| 135 |
+
"""Returns a ring associated with ``self``. """
|
| 136 |
+
raise DomainError("there is no ring associated with %s" % self)
|
| 137 |
+
|
| 138 |
+
def get_exact(self):
|
| 139 |
+
"""Returns an exact domain associated with ``self``. """
|
| 140 |
+
return QQ_I
|
| 141 |
+
|
| 142 |
+
def is_negative(self, element):
|
| 143 |
+
"""Returns ``False`` for any ``ComplexElement``. """
|
| 144 |
+
return False
|
| 145 |
+
|
| 146 |
+
def is_positive(self, element):
|
| 147 |
+
"""Returns ``False`` for any ``ComplexElement``. """
|
| 148 |
+
return False
|
| 149 |
+
|
| 150 |
+
def is_nonnegative(self, element):
|
| 151 |
+
"""Returns ``False`` for any ``ComplexElement``. """
|
| 152 |
+
return False
|
| 153 |
+
|
| 154 |
+
def is_nonpositive(self, element):
|
| 155 |
+
"""Returns ``False`` for any ``ComplexElement``. """
|
| 156 |
+
return False
|
| 157 |
+
|
| 158 |
+
def gcd(self, a, b):
|
| 159 |
+
"""Returns GCD of ``a`` and ``b``. """
|
| 160 |
+
return self.one
|
| 161 |
+
|
| 162 |
+
def lcm(self, a, b):
|
| 163 |
+
"""Returns LCM of ``a`` and ``b``. """
|
| 164 |
+
return a*b
|
| 165 |
+
|
| 166 |
+
def almosteq(self, a, b, tolerance=None):
|
| 167 |
+
"""Check if ``a`` and ``b`` are almost equal. """
|
| 168 |
+
return self._context.almosteq(a, b, tolerance)
|
| 169 |
+
|
| 170 |
+
def is_square(self, a):
|
| 171 |
+
"""Returns ``True``. Every complex number has a complex square root."""
|
| 172 |
+
return True
|
| 173 |
+
|
| 174 |
+
def exsqrt(self, a):
|
| 175 |
+
r"""Returns the principal complex square root of ``a``.
|
| 176 |
+
|
| 177 |
+
Explanation
|
| 178 |
+
===========
|
| 179 |
+
The argument of the principal square root is always within
|
| 180 |
+
$(-\frac{\pi}{2}, \frac{\pi}{2}]$. The square root may be
|
| 181 |
+
slightly inaccurate due to floating point rounding error.
|
| 182 |
+
"""
|
| 183 |
+
return a ** 0.5
|
| 184 |
+
|
| 185 |
+
CC = ComplexField()
|
lib/python3.12/site-packages/sympy/polys/domains/compositedomain.py
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Implementation of :class:`CompositeDomain` class. """
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
from sympy.polys.domains.domain import Domain
|
| 5 |
+
from sympy.polys.polyerrors import GeneratorsError
|
| 6 |
+
|
| 7 |
+
from sympy.utilities import public
|
| 8 |
+
|
| 9 |
+
@public
|
| 10 |
+
class CompositeDomain(Domain):
|
| 11 |
+
"""Base class for composite domains, e.g. ZZ[x], ZZ(X). """
|
| 12 |
+
|
| 13 |
+
is_Composite = True
|
| 14 |
+
|
| 15 |
+
gens, ngens, symbols, domain = [None]*4
|
| 16 |
+
|
| 17 |
+
def inject(self, *symbols):
|
| 18 |
+
"""Inject generators into this domain. """
|
| 19 |
+
if not (set(self.symbols) & set(symbols)):
|
| 20 |
+
return self.__class__(self.domain, self.symbols + symbols, self.order)
|
| 21 |
+
else:
|
| 22 |
+
raise GeneratorsError("common generators in %s and %s" % (self.symbols, symbols))
|
| 23 |
+
|
| 24 |
+
def drop(self, *symbols):
|
| 25 |
+
"""Drop generators from this domain. """
|
| 26 |
+
symset = set(symbols)
|
| 27 |
+
newsyms = tuple(s for s in self.symbols if s not in symset)
|
| 28 |
+
domain = self.domain.drop(*symbols)
|
| 29 |
+
if not newsyms:
|
| 30 |
+
return domain
|
| 31 |
+
else:
|
| 32 |
+
return self.__class__(domain, newsyms, self.order)
|
| 33 |
+
|
| 34 |
+
def set_domain(self, domain):
|
| 35 |
+
"""Set the ground domain of this domain. """
|
| 36 |
+
return self.__class__(domain, self.symbols, self.order)
|
| 37 |
+
|
| 38 |
+
@property
|
| 39 |
+
def is_Exact(self):
|
| 40 |
+
"""Returns ``True`` if this domain is exact. """
|
| 41 |
+
return self.domain.is_Exact
|
| 42 |
+
|
| 43 |
+
def get_exact(self):
|
| 44 |
+
"""Returns an exact version of this domain. """
|
| 45 |
+
return self.set_domain(self.domain.get_exact())
|
| 46 |
+
|
| 47 |
+
@property
|
| 48 |
+
def has_CharacteristicZero(self):
|
| 49 |
+
return self.domain.has_CharacteristicZero
|
| 50 |
+
|
| 51 |
+
def characteristic(self):
|
| 52 |
+
return self.domain.characteristic()
|
lib/python3.12/site-packages/sympy/polys/domains/domain.py
ADDED
|
@@ -0,0 +1,1372 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Implementation of :class:`Domain` class. """
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations
|
| 4 |
+
from typing import Any
|
| 5 |
+
|
| 6 |
+
from sympy.core.numbers import AlgebraicNumber
|
| 7 |
+
from sympy.core import Basic, sympify
|
| 8 |
+
from sympy.core.sorting import ordered
|
| 9 |
+
from sympy.external.gmpy import GROUND_TYPES
|
| 10 |
+
from sympy.polys.domains.domainelement import DomainElement
|
| 11 |
+
from sympy.polys.orderings import lex
|
| 12 |
+
from sympy.polys.polyerrors import UnificationFailed, CoercionFailed, DomainError
|
| 13 |
+
from sympy.polys.polyutils import _unify_gens, _not_a_coeff
|
| 14 |
+
from sympy.utilities import public
|
| 15 |
+
from sympy.utilities.iterables import is_sequence
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
@public
|
| 19 |
+
class Domain:
|
| 20 |
+
"""Superclass for all domains in the polys domains system.
|
| 21 |
+
|
| 22 |
+
See :ref:`polys-domainsintro` for an introductory explanation of the
|
| 23 |
+
domains system.
|
| 24 |
+
|
| 25 |
+
The :py:class:`~.Domain` class is an abstract base class for all of the
|
| 26 |
+
concrete domain types. There are many different :py:class:`~.Domain`
|
| 27 |
+
subclasses each of which has an associated ``dtype`` which is a class
|
| 28 |
+
representing the elements of the domain. The coefficients of a
|
| 29 |
+
:py:class:`~.Poly` are elements of a domain which must be a subclass of
|
| 30 |
+
:py:class:`~.Domain`.
|
| 31 |
+
|
| 32 |
+
Examples
|
| 33 |
+
========
|
| 34 |
+
|
| 35 |
+
The most common example domains are the integers :ref:`ZZ` and the
|
| 36 |
+
rationals :ref:`QQ`.
|
| 37 |
+
|
| 38 |
+
>>> from sympy import Poly, symbols, Domain
|
| 39 |
+
>>> x, y = symbols('x, y')
|
| 40 |
+
>>> p = Poly(x**2 + y)
|
| 41 |
+
>>> p
|
| 42 |
+
Poly(x**2 + y, x, y, domain='ZZ')
|
| 43 |
+
>>> p.domain
|
| 44 |
+
ZZ
|
| 45 |
+
>>> isinstance(p.domain, Domain)
|
| 46 |
+
True
|
| 47 |
+
>>> Poly(x**2 + y/2)
|
| 48 |
+
Poly(x**2 + 1/2*y, x, y, domain='QQ')
|
| 49 |
+
|
| 50 |
+
The domains can be used directly in which case the domain object e.g.
|
| 51 |
+
(:ref:`ZZ` or :ref:`QQ`) can be used as a constructor for elements of
|
| 52 |
+
``dtype``.
|
| 53 |
+
|
| 54 |
+
>>> from sympy import ZZ, QQ
|
| 55 |
+
>>> ZZ(2)
|
| 56 |
+
2
|
| 57 |
+
>>> ZZ.dtype # doctest: +SKIP
|
| 58 |
+
<class 'int'>
|
| 59 |
+
>>> type(ZZ(2)) # doctest: +SKIP
|
| 60 |
+
<class 'int'>
|
| 61 |
+
>>> QQ(1, 2)
|
| 62 |
+
1/2
|
| 63 |
+
>>> type(QQ(1, 2)) # doctest: +SKIP
|
| 64 |
+
<class 'sympy.polys.domains.pythonrational.PythonRational'>
|
| 65 |
+
|
| 66 |
+
The corresponding domain elements can be used with the arithmetic
|
| 67 |
+
operations ``+,-,*,**`` and depending on the domain some combination of
|
| 68 |
+
``/,//,%`` might be usable. For example in :ref:`ZZ` both ``//`` (floor
|
| 69 |
+
division) and ``%`` (modulo division) can be used but ``/`` (true
|
| 70 |
+
division) cannot. Since :ref:`QQ` is a :py:class:`~.Field` its elements
|
| 71 |
+
can be used with ``/`` but ``//`` and ``%`` should not be used. Some
|
| 72 |
+
domains have a :py:meth:`~.Domain.gcd` method.
|
| 73 |
+
|
| 74 |
+
>>> ZZ(2) + ZZ(3)
|
| 75 |
+
5
|
| 76 |
+
>>> ZZ(5) // ZZ(2)
|
| 77 |
+
2
|
| 78 |
+
>>> ZZ(5) % ZZ(2)
|
| 79 |
+
1
|
| 80 |
+
>>> QQ(1, 2) / QQ(2, 3)
|
| 81 |
+
3/4
|
| 82 |
+
>>> ZZ.gcd(ZZ(4), ZZ(2))
|
| 83 |
+
2
|
| 84 |
+
>>> QQ.gcd(QQ(2,7), QQ(5,3))
|
| 85 |
+
1/21
|
| 86 |
+
>>> ZZ.is_Field
|
| 87 |
+
False
|
| 88 |
+
>>> QQ.is_Field
|
| 89 |
+
True
|
| 90 |
+
|
| 91 |
+
There are also many other domains including:
|
| 92 |
+
|
| 93 |
+
1. :ref:`GF(p)` for finite fields of prime order.
|
| 94 |
+
2. :ref:`RR` for real (floating point) numbers.
|
| 95 |
+
3. :ref:`CC` for complex (floating point) numbers.
|
| 96 |
+
4. :ref:`QQ(a)` for algebraic number fields.
|
| 97 |
+
5. :ref:`K[x]` for polynomial rings.
|
| 98 |
+
6. :ref:`K(x)` for rational function fields.
|
| 99 |
+
7. :ref:`EX` for arbitrary expressions.
|
| 100 |
+
|
| 101 |
+
Each domain is represented by a domain object and also an implementation
|
| 102 |
+
class (``dtype``) for the elements of the domain. For example the
|
| 103 |
+
:ref:`K[x]` domains are represented by a domain object which is an
|
| 104 |
+
instance of :py:class:`~.PolynomialRing` and the elements are always
|
| 105 |
+
instances of :py:class:`~.PolyElement`. The implementation class
|
| 106 |
+
represents particular types of mathematical expressions in a way that is
|
| 107 |
+
more efficient than a normal SymPy expression which is of type
|
| 108 |
+
:py:class:`~.Expr`. The domain methods :py:meth:`~.Domain.from_sympy` and
|
| 109 |
+
:py:meth:`~.Domain.to_sympy` are used to convert from :py:class:`~.Expr`
|
| 110 |
+
to a domain element and vice versa.
|
| 111 |
+
|
| 112 |
+
>>> from sympy import Symbol, ZZ, Expr
|
| 113 |
+
>>> x = Symbol('x')
|
| 114 |
+
>>> K = ZZ[x] # polynomial ring domain
|
| 115 |
+
>>> K
|
| 116 |
+
ZZ[x]
|
| 117 |
+
>>> type(K) # class of the domain
|
| 118 |
+
<class 'sympy.polys.domains.polynomialring.PolynomialRing'>
|
| 119 |
+
>>> K.dtype # class of the elements
|
| 120 |
+
<class 'sympy.polys.rings.PolyElement'>
|
| 121 |
+
>>> p_expr = x**2 + 1 # Expr
|
| 122 |
+
>>> p_expr
|
| 123 |
+
x**2 + 1
|
| 124 |
+
>>> type(p_expr)
|
| 125 |
+
<class 'sympy.core.add.Add'>
|
| 126 |
+
>>> isinstance(p_expr, Expr)
|
| 127 |
+
True
|
| 128 |
+
>>> p_domain = K.from_sympy(p_expr)
|
| 129 |
+
>>> p_domain # domain element
|
| 130 |
+
x**2 + 1
|
| 131 |
+
>>> type(p_domain)
|
| 132 |
+
<class 'sympy.polys.rings.PolyElement'>
|
| 133 |
+
>>> K.to_sympy(p_domain) == p_expr
|
| 134 |
+
True
|
| 135 |
+
|
| 136 |
+
The :py:meth:`~.Domain.convert_from` method is used to convert domain
|
| 137 |
+
elements from one domain to another.
|
| 138 |
+
|
| 139 |
+
>>> from sympy import ZZ, QQ
|
| 140 |
+
>>> ez = ZZ(2)
|
| 141 |
+
>>> eq = QQ.convert_from(ez, ZZ)
|
| 142 |
+
>>> type(ez) # doctest: +SKIP
|
| 143 |
+
<class 'int'>
|
| 144 |
+
>>> type(eq) # doctest: +SKIP
|
| 145 |
+
<class 'sympy.polys.domains.pythonrational.PythonRational'>
|
| 146 |
+
|
| 147 |
+
Elements from different domains should not be mixed in arithmetic or other
|
| 148 |
+
operations: they should be converted to a common domain first. The domain
|
| 149 |
+
method :py:meth:`~.Domain.unify` is used to find a domain that can
|
| 150 |
+
represent all the elements of two given domains.
|
| 151 |
+
|
| 152 |
+
>>> from sympy import ZZ, QQ, symbols
|
| 153 |
+
>>> x, y = symbols('x, y')
|
| 154 |
+
>>> ZZ.unify(QQ)
|
| 155 |
+
QQ
|
| 156 |
+
>>> ZZ[x].unify(QQ)
|
| 157 |
+
QQ[x]
|
| 158 |
+
>>> ZZ[x].unify(QQ[y])
|
| 159 |
+
QQ[x,y]
|
| 160 |
+
|
| 161 |
+
If a domain is a :py:class:`~.Ring` then is might have an associated
|
| 162 |
+
:py:class:`~.Field` and vice versa. The :py:meth:`~.Domain.get_field` and
|
| 163 |
+
:py:meth:`~.Domain.get_ring` methods will find or create the associated
|
| 164 |
+
domain.
|
| 165 |
+
|
| 166 |
+
>>> from sympy import ZZ, QQ, Symbol
|
| 167 |
+
>>> x = Symbol('x')
|
| 168 |
+
>>> ZZ.has_assoc_Field
|
| 169 |
+
True
|
| 170 |
+
>>> ZZ.get_field()
|
| 171 |
+
QQ
|
| 172 |
+
>>> QQ.has_assoc_Ring
|
| 173 |
+
True
|
| 174 |
+
>>> QQ.get_ring()
|
| 175 |
+
ZZ
|
| 176 |
+
>>> K = QQ[x]
|
| 177 |
+
>>> K
|
| 178 |
+
QQ[x]
|
| 179 |
+
>>> K.get_field()
|
| 180 |
+
QQ(x)
|
| 181 |
+
|
| 182 |
+
See also
|
| 183 |
+
========
|
| 184 |
+
|
| 185 |
+
DomainElement: abstract base class for domain elements
|
| 186 |
+
construct_domain: construct a minimal domain for some expressions
|
| 187 |
+
|
| 188 |
+
"""
|
| 189 |
+
|
| 190 |
+
dtype: type | None = None
|
| 191 |
+
"""The type (class) of the elements of this :py:class:`~.Domain`:
|
| 192 |
+
|
| 193 |
+
>>> from sympy import ZZ, QQ, Symbol
|
| 194 |
+
>>> ZZ.dtype
|
| 195 |
+
<class 'int'>
|
| 196 |
+
>>> z = ZZ(2)
|
| 197 |
+
>>> z
|
| 198 |
+
2
|
| 199 |
+
>>> type(z)
|
| 200 |
+
<class 'int'>
|
| 201 |
+
>>> type(z) == ZZ.dtype
|
| 202 |
+
True
|
| 203 |
+
|
| 204 |
+
Every domain has an associated **dtype** ("datatype") which is the
|
| 205 |
+
class of the associated domain elements.
|
| 206 |
+
|
| 207 |
+
See also
|
| 208 |
+
========
|
| 209 |
+
|
| 210 |
+
of_type
|
| 211 |
+
"""
|
| 212 |
+
|
| 213 |
+
zero: Any = None
|
| 214 |
+
"""The zero element of the :py:class:`~.Domain`:
|
| 215 |
+
|
| 216 |
+
>>> from sympy import QQ
|
| 217 |
+
>>> QQ.zero
|
| 218 |
+
0
|
| 219 |
+
>>> QQ.of_type(QQ.zero)
|
| 220 |
+
True
|
| 221 |
+
|
| 222 |
+
See also
|
| 223 |
+
========
|
| 224 |
+
|
| 225 |
+
of_type
|
| 226 |
+
one
|
| 227 |
+
"""
|
| 228 |
+
|
| 229 |
+
one: Any = None
|
| 230 |
+
"""The one element of the :py:class:`~.Domain`:
|
| 231 |
+
|
| 232 |
+
>>> from sympy import QQ
|
| 233 |
+
>>> QQ.one
|
| 234 |
+
1
|
| 235 |
+
>>> QQ.of_type(QQ.one)
|
| 236 |
+
True
|
| 237 |
+
|
| 238 |
+
See also
|
| 239 |
+
========
|
| 240 |
+
|
| 241 |
+
of_type
|
| 242 |
+
zero
|
| 243 |
+
"""
|
| 244 |
+
|
| 245 |
+
is_Ring = False
|
| 246 |
+
"""Boolean flag indicating if the domain is a :py:class:`~.Ring`.
|
| 247 |
+
|
| 248 |
+
>>> from sympy import ZZ
|
| 249 |
+
>>> ZZ.is_Ring
|
| 250 |
+
True
|
| 251 |
+
|
| 252 |
+
Basically every :py:class:`~.Domain` represents a ring so this flag is
|
| 253 |
+
not that useful.
|
| 254 |
+
|
| 255 |
+
See also
|
| 256 |
+
========
|
| 257 |
+
|
| 258 |
+
is_PID
|
| 259 |
+
is_Field
|
| 260 |
+
get_ring
|
| 261 |
+
has_assoc_Ring
|
| 262 |
+
"""
|
| 263 |
+
|
| 264 |
+
is_Field = False
|
| 265 |
+
"""Boolean flag indicating if the domain is a :py:class:`~.Field`.
|
| 266 |
+
|
| 267 |
+
>>> from sympy import ZZ, QQ
|
| 268 |
+
>>> ZZ.is_Field
|
| 269 |
+
False
|
| 270 |
+
>>> QQ.is_Field
|
| 271 |
+
True
|
| 272 |
+
|
| 273 |
+
See also
|
| 274 |
+
========
|
| 275 |
+
|
| 276 |
+
is_PID
|
| 277 |
+
is_Ring
|
| 278 |
+
get_field
|
| 279 |
+
has_assoc_Field
|
| 280 |
+
"""
|
| 281 |
+
|
| 282 |
+
has_assoc_Ring = False
|
| 283 |
+
"""Boolean flag indicating if the domain has an associated
|
| 284 |
+
:py:class:`~.Ring`.
|
| 285 |
+
|
| 286 |
+
>>> from sympy import QQ
|
| 287 |
+
>>> QQ.has_assoc_Ring
|
| 288 |
+
True
|
| 289 |
+
>>> QQ.get_ring()
|
| 290 |
+
ZZ
|
| 291 |
+
|
| 292 |
+
See also
|
| 293 |
+
========
|
| 294 |
+
|
| 295 |
+
is_Field
|
| 296 |
+
get_ring
|
| 297 |
+
"""
|
| 298 |
+
|
| 299 |
+
has_assoc_Field = False
|
| 300 |
+
"""Boolean flag indicating if the domain has an associated
|
| 301 |
+
:py:class:`~.Field`.
|
| 302 |
+
|
| 303 |
+
>>> from sympy import ZZ
|
| 304 |
+
>>> ZZ.has_assoc_Field
|
| 305 |
+
True
|
| 306 |
+
>>> ZZ.get_field()
|
| 307 |
+
QQ
|
| 308 |
+
|
| 309 |
+
See also
|
| 310 |
+
========
|
| 311 |
+
|
| 312 |
+
is_Field
|
| 313 |
+
get_field
|
| 314 |
+
"""
|
| 315 |
+
|
| 316 |
+
is_FiniteField = is_FF = False
|
| 317 |
+
is_IntegerRing = is_ZZ = False
|
| 318 |
+
is_RationalField = is_QQ = False
|
| 319 |
+
is_GaussianRing = is_ZZ_I = False
|
| 320 |
+
is_GaussianField = is_QQ_I = False
|
| 321 |
+
is_RealField = is_RR = False
|
| 322 |
+
is_ComplexField = is_CC = False
|
| 323 |
+
is_AlgebraicField = is_Algebraic = False
|
| 324 |
+
is_PolynomialRing = is_Poly = False
|
| 325 |
+
is_FractionField = is_Frac = False
|
| 326 |
+
is_SymbolicDomain = is_EX = False
|
| 327 |
+
is_SymbolicRawDomain = is_EXRAW = False
|
| 328 |
+
is_FiniteExtension = False
|
| 329 |
+
|
| 330 |
+
is_Exact = True
|
| 331 |
+
is_Numerical = False
|
| 332 |
+
|
| 333 |
+
is_Simple = False
|
| 334 |
+
is_Composite = False
|
| 335 |
+
|
| 336 |
+
is_PID = False
|
| 337 |
+
"""Boolean flag indicating if the domain is a `principal ideal domain`_.
|
| 338 |
+
|
| 339 |
+
>>> from sympy import ZZ
|
| 340 |
+
>>> ZZ.has_assoc_Field
|
| 341 |
+
True
|
| 342 |
+
>>> ZZ.get_field()
|
| 343 |
+
QQ
|
| 344 |
+
|
| 345 |
+
.. _principal ideal domain: https://en.wikipedia.org/wiki/Principal_ideal_domain
|
| 346 |
+
|
| 347 |
+
See also
|
| 348 |
+
========
|
| 349 |
+
|
| 350 |
+
is_Field
|
| 351 |
+
get_field
|
| 352 |
+
"""
|
| 353 |
+
|
| 354 |
+
has_CharacteristicZero = False
|
| 355 |
+
|
| 356 |
+
rep: str | None = None
|
| 357 |
+
alias: str | None = None
|
| 358 |
+
|
| 359 |
+
def __init__(self):
|
| 360 |
+
raise NotImplementedError
|
| 361 |
+
|
| 362 |
+
def __str__(self):
|
| 363 |
+
return self.rep
|
| 364 |
+
|
| 365 |
+
def __repr__(self):
|
| 366 |
+
return str(self)
|
| 367 |
+
|
| 368 |
+
def __hash__(self):
|
| 369 |
+
return hash((self.__class__.__name__, self.dtype))
|
| 370 |
+
|
| 371 |
+
def new(self, *args):
|
| 372 |
+
return self.dtype(*args)
|
| 373 |
+
|
| 374 |
+
@property
|
| 375 |
+
def tp(self):
|
| 376 |
+
"""Alias for :py:attr:`~.Domain.dtype`"""
|
| 377 |
+
return self.dtype
|
| 378 |
+
|
| 379 |
+
def __call__(self, *args):
|
| 380 |
+
"""Construct an element of ``self`` domain from ``args``. """
|
| 381 |
+
return self.new(*args)
|
| 382 |
+
|
| 383 |
+
def normal(self, *args):
|
| 384 |
+
return self.dtype(*args)
|
| 385 |
+
|
| 386 |
+
def convert_from(self, element, base):
|
| 387 |
+
"""Convert ``element`` to ``self.dtype`` given the base domain. """
|
| 388 |
+
if base.alias is not None:
|
| 389 |
+
method = "from_" + base.alias
|
| 390 |
+
else:
|
| 391 |
+
method = "from_" + base.__class__.__name__
|
| 392 |
+
|
| 393 |
+
_convert = getattr(self, method)
|
| 394 |
+
|
| 395 |
+
if _convert is not None:
|
| 396 |
+
result = _convert(element, base)
|
| 397 |
+
|
| 398 |
+
if result is not None:
|
| 399 |
+
return result
|
| 400 |
+
|
| 401 |
+
raise CoercionFailed("Cannot convert %s of type %s from %s to %s" % (element, type(element), base, self))
|
| 402 |
+
|
| 403 |
+
def convert(self, element, base=None):
|
| 404 |
+
"""Convert ``element`` to ``self.dtype``. """
|
| 405 |
+
|
| 406 |
+
if base is not None:
|
| 407 |
+
if _not_a_coeff(element):
|
| 408 |
+
raise CoercionFailed('%s is not in any domain' % element)
|
| 409 |
+
return self.convert_from(element, base)
|
| 410 |
+
|
| 411 |
+
if self.of_type(element):
|
| 412 |
+
return element
|
| 413 |
+
|
| 414 |
+
if _not_a_coeff(element):
|
| 415 |
+
raise CoercionFailed('%s is not in any domain' % element)
|
| 416 |
+
|
| 417 |
+
from sympy.polys.domains import ZZ, QQ, RealField, ComplexField
|
| 418 |
+
|
| 419 |
+
if ZZ.of_type(element):
|
| 420 |
+
return self.convert_from(element, ZZ)
|
| 421 |
+
|
| 422 |
+
if isinstance(element, int):
|
| 423 |
+
return self.convert_from(ZZ(element), ZZ)
|
| 424 |
+
|
| 425 |
+
if GROUND_TYPES != 'python':
|
| 426 |
+
if isinstance(element, ZZ.tp):
|
| 427 |
+
return self.convert_from(element, ZZ)
|
| 428 |
+
if isinstance(element, QQ.tp):
|
| 429 |
+
return self.convert_from(element, QQ)
|
| 430 |
+
|
| 431 |
+
if isinstance(element, float):
|
| 432 |
+
parent = RealField(tol=False)
|
| 433 |
+
return self.convert_from(parent(element), parent)
|
| 434 |
+
|
| 435 |
+
if isinstance(element, complex):
|
| 436 |
+
parent = ComplexField(tol=False)
|
| 437 |
+
return self.convert_from(parent(element), parent)
|
| 438 |
+
|
| 439 |
+
if isinstance(element, DomainElement):
|
| 440 |
+
return self.convert_from(element, element.parent())
|
| 441 |
+
|
| 442 |
+
# TODO: implement this in from_ methods
|
| 443 |
+
if self.is_Numerical and getattr(element, 'is_ground', False):
|
| 444 |
+
return self.convert(element.LC())
|
| 445 |
+
|
| 446 |
+
if isinstance(element, Basic):
|
| 447 |
+
try:
|
| 448 |
+
return self.from_sympy(element)
|
| 449 |
+
except (TypeError, ValueError):
|
| 450 |
+
pass
|
| 451 |
+
else: # TODO: remove this branch
|
| 452 |
+
if not is_sequence(element):
|
| 453 |
+
try:
|
| 454 |
+
element = sympify(element, strict=True)
|
| 455 |
+
if isinstance(element, Basic):
|
| 456 |
+
return self.from_sympy(element)
|
| 457 |
+
except (TypeError, ValueError):
|
| 458 |
+
pass
|
| 459 |
+
|
| 460 |
+
raise CoercionFailed("Cannot convert %s of type %s to %s" % (element, type(element), self))
|
| 461 |
+
|
| 462 |
+
def of_type(self, element):
|
| 463 |
+
"""Check if ``a`` is of type ``dtype``. """
|
| 464 |
+
return isinstance(element, self.tp) # XXX: this isn't correct, e.g. PolyElement
|
| 465 |
+
|
| 466 |
+
def __contains__(self, a):
|
| 467 |
+
"""Check if ``a`` belongs to this domain. """
|
| 468 |
+
try:
|
| 469 |
+
if _not_a_coeff(a):
|
| 470 |
+
raise CoercionFailed
|
| 471 |
+
self.convert(a) # this might raise, too
|
| 472 |
+
except CoercionFailed:
|
| 473 |
+
return False
|
| 474 |
+
|
| 475 |
+
return True
|
| 476 |
+
|
| 477 |
+
def to_sympy(self, a):
|
| 478 |
+
"""Convert domain element *a* to a SymPy expression (Expr).
|
| 479 |
+
|
| 480 |
+
Explanation
|
| 481 |
+
===========
|
| 482 |
+
|
| 483 |
+
Convert a :py:class:`~.Domain` element *a* to :py:class:`~.Expr`. Most
|
| 484 |
+
public SymPy functions work with objects of type :py:class:`~.Expr`.
|
| 485 |
+
The elements of a :py:class:`~.Domain` have a different internal
|
| 486 |
+
representation. It is not possible to mix domain elements with
|
| 487 |
+
:py:class:`~.Expr` so each domain has :py:meth:`~.Domain.to_sympy` and
|
| 488 |
+
:py:meth:`~.Domain.from_sympy` methods to convert its domain elements
|
| 489 |
+
to and from :py:class:`~.Expr`.
|
| 490 |
+
|
| 491 |
+
Parameters
|
| 492 |
+
==========
|
| 493 |
+
|
| 494 |
+
a: domain element
|
| 495 |
+
An element of this :py:class:`~.Domain`.
|
| 496 |
+
|
| 497 |
+
Returns
|
| 498 |
+
=======
|
| 499 |
+
|
| 500 |
+
expr: Expr
|
| 501 |
+
A normal SymPy expression of type :py:class:`~.Expr`.
|
| 502 |
+
|
| 503 |
+
Examples
|
| 504 |
+
========
|
| 505 |
+
|
| 506 |
+
Construct an element of the :ref:`QQ` domain and then convert it to
|
| 507 |
+
:py:class:`~.Expr`.
|
| 508 |
+
|
| 509 |
+
>>> from sympy import QQ, Expr
|
| 510 |
+
>>> q_domain = QQ(2)
|
| 511 |
+
>>> q_domain
|
| 512 |
+
2
|
| 513 |
+
>>> q_expr = QQ.to_sympy(q_domain)
|
| 514 |
+
>>> q_expr
|
| 515 |
+
2
|
| 516 |
+
|
| 517 |
+
Although the printed forms look similar these objects are not of the
|
| 518 |
+
same type.
|
| 519 |
+
|
| 520 |
+
>>> isinstance(q_domain, Expr)
|
| 521 |
+
False
|
| 522 |
+
>>> isinstance(q_expr, Expr)
|
| 523 |
+
True
|
| 524 |
+
|
| 525 |
+
Construct an element of :ref:`K[x]` and convert to
|
| 526 |
+
:py:class:`~.Expr`.
|
| 527 |
+
|
| 528 |
+
>>> from sympy import Symbol
|
| 529 |
+
>>> x = Symbol('x')
|
| 530 |
+
>>> K = QQ[x]
|
| 531 |
+
>>> x_domain = K.gens[0] # generator x as a domain element
|
| 532 |
+
>>> p_domain = x_domain**2/3 + 1
|
| 533 |
+
>>> p_domain
|
| 534 |
+
1/3*x**2 + 1
|
| 535 |
+
>>> p_expr = K.to_sympy(p_domain)
|
| 536 |
+
>>> p_expr
|
| 537 |
+
x**2/3 + 1
|
| 538 |
+
|
| 539 |
+
The :py:meth:`~.Domain.from_sympy` method is used for the opposite
|
| 540 |
+
conversion from a normal SymPy expression to a domain element.
|
| 541 |
+
|
| 542 |
+
>>> p_domain == p_expr
|
| 543 |
+
False
|
| 544 |
+
>>> K.from_sympy(p_expr) == p_domain
|
| 545 |
+
True
|
| 546 |
+
>>> K.to_sympy(p_domain) == p_expr
|
| 547 |
+
True
|
| 548 |
+
>>> K.from_sympy(K.to_sympy(p_domain)) == p_domain
|
| 549 |
+
True
|
| 550 |
+
>>> K.to_sympy(K.from_sympy(p_expr)) == p_expr
|
| 551 |
+
True
|
| 552 |
+
|
| 553 |
+
The :py:meth:`~.Domain.from_sympy` method makes it easier to construct
|
| 554 |
+
domain elements interactively.
|
| 555 |
+
|
| 556 |
+
>>> from sympy import Symbol
|
| 557 |
+
>>> x = Symbol('x')
|
| 558 |
+
>>> K = QQ[x]
|
| 559 |
+
>>> K.from_sympy(x**2/3 + 1)
|
| 560 |
+
1/3*x**2 + 1
|
| 561 |
+
|
| 562 |
+
See also
|
| 563 |
+
========
|
| 564 |
+
|
| 565 |
+
from_sympy
|
| 566 |
+
convert_from
|
| 567 |
+
"""
|
| 568 |
+
raise NotImplementedError
|
| 569 |
+
|
| 570 |
+
def from_sympy(self, a):
|
| 571 |
+
"""Convert a SymPy expression to an element of this domain.
|
| 572 |
+
|
| 573 |
+
Explanation
|
| 574 |
+
===========
|
| 575 |
+
|
| 576 |
+
See :py:meth:`~.Domain.to_sympy` for explanation and examples.
|
| 577 |
+
|
| 578 |
+
Parameters
|
| 579 |
+
==========
|
| 580 |
+
|
| 581 |
+
expr: Expr
|
| 582 |
+
A normal SymPy expression of type :py:class:`~.Expr`.
|
| 583 |
+
|
| 584 |
+
Returns
|
| 585 |
+
=======
|
| 586 |
+
|
| 587 |
+
a: domain element
|
| 588 |
+
An element of this :py:class:`~.Domain`.
|
| 589 |
+
|
| 590 |
+
See also
|
| 591 |
+
========
|
| 592 |
+
|
| 593 |
+
to_sympy
|
| 594 |
+
convert_from
|
| 595 |
+
"""
|
| 596 |
+
raise NotImplementedError
|
| 597 |
+
|
| 598 |
+
def sum(self, args):
|
| 599 |
+
return sum(args, start=self.zero)
|
| 600 |
+
|
| 601 |
+
def from_FF(K1, a, K0):
|
| 602 |
+
"""Convert ``ModularInteger(int)`` to ``dtype``. """
|
| 603 |
+
return None
|
| 604 |
+
|
| 605 |
+
def from_FF_python(K1, a, K0):
|
| 606 |
+
"""Convert ``ModularInteger(int)`` to ``dtype``. """
|
| 607 |
+
return None
|
| 608 |
+
|
| 609 |
+
def from_ZZ_python(K1, a, K0):
|
| 610 |
+
"""Convert a Python ``int`` object to ``dtype``. """
|
| 611 |
+
return None
|
| 612 |
+
|
| 613 |
+
def from_QQ_python(K1, a, K0):
|
| 614 |
+
"""Convert a Python ``Fraction`` object to ``dtype``. """
|
| 615 |
+
return None
|
| 616 |
+
|
| 617 |
+
def from_FF_gmpy(K1, a, K0):
|
| 618 |
+
"""Convert ``ModularInteger(mpz)`` to ``dtype``. """
|
| 619 |
+
return None
|
| 620 |
+
|
| 621 |
+
def from_ZZ_gmpy(K1, a, K0):
|
| 622 |
+
"""Convert a GMPY ``mpz`` object to ``dtype``. """
|
| 623 |
+
return None
|
| 624 |
+
|
| 625 |
+
def from_QQ_gmpy(K1, a, K0):
|
| 626 |
+
"""Convert a GMPY ``mpq`` object to ``dtype``. """
|
| 627 |
+
return None
|
| 628 |
+
|
| 629 |
+
def from_RealField(K1, a, K0):
|
| 630 |
+
"""Convert a real element object to ``dtype``. """
|
| 631 |
+
return None
|
| 632 |
+
|
| 633 |
+
def from_ComplexField(K1, a, K0):
|
| 634 |
+
"""Convert a complex element to ``dtype``. """
|
| 635 |
+
return None
|
| 636 |
+
|
| 637 |
+
def from_AlgebraicField(K1, a, K0):
|
| 638 |
+
"""Convert an algebraic number to ``dtype``. """
|
| 639 |
+
return None
|
| 640 |
+
|
| 641 |
+
def from_PolynomialRing(K1, a, K0):
|
| 642 |
+
"""Convert a polynomial to ``dtype``. """
|
| 643 |
+
if a.is_ground:
|
| 644 |
+
return K1.convert(a.LC, K0.dom)
|
| 645 |
+
|
| 646 |
+
def from_FractionField(K1, a, K0):
|
| 647 |
+
"""Convert a rational function to ``dtype``. """
|
| 648 |
+
return None
|
| 649 |
+
|
| 650 |
+
def from_MonogenicFiniteExtension(K1, a, K0):
|
| 651 |
+
"""Convert an ``ExtensionElement`` to ``dtype``. """
|
| 652 |
+
return K1.convert_from(a.rep, K0.ring)
|
| 653 |
+
|
| 654 |
+
def from_ExpressionDomain(K1, a, K0):
|
| 655 |
+
"""Convert a ``EX`` object to ``dtype``. """
|
| 656 |
+
return K1.from_sympy(a.ex)
|
| 657 |
+
|
| 658 |
+
def from_ExpressionRawDomain(K1, a, K0):
|
| 659 |
+
"""Convert a ``EX`` object to ``dtype``. """
|
| 660 |
+
return K1.from_sympy(a)
|
| 661 |
+
|
| 662 |
+
def from_GlobalPolynomialRing(K1, a, K0):
|
| 663 |
+
"""Convert a polynomial to ``dtype``. """
|
| 664 |
+
if a.degree() <= 0:
|
| 665 |
+
return K1.convert(a.LC(), K0.dom)
|
| 666 |
+
|
| 667 |
+
def from_GeneralizedPolynomialRing(K1, a, K0):
|
| 668 |
+
return K1.from_FractionField(a, K0)
|
| 669 |
+
|
| 670 |
+
def unify_with_symbols(K0, K1, symbols):
|
| 671 |
+
if (K0.is_Composite and (set(K0.symbols) & set(symbols))) or (K1.is_Composite and (set(K1.symbols) & set(symbols))):
|
| 672 |
+
raise UnificationFailed("Cannot unify %s with %s, given %s generators" % (K0, K1, tuple(symbols)))
|
| 673 |
+
|
| 674 |
+
return K0.unify(K1)
|
| 675 |
+
|
| 676 |
+
def unify_composite(K0, K1):
|
| 677 |
+
"""Unify two domains where at least one is composite."""
|
| 678 |
+
K0_ground = K0.dom if K0.is_Composite else K0
|
| 679 |
+
K1_ground = K1.dom if K1.is_Composite else K1
|
| 680 |
+
|
| 681 |
+
K0_symbols = K0.symbols if K0.is_Composite else ()
|
| 682 |
+
K1_symbols = K1.symbols if K1.is_Composite else ()
|
| 683 |
+
|
| 684 |
+
domain = K0_ground.unify(K1_ground)
|
| 685 |
+
symbols = _unify_gens(K0_symbols, K1_symbols)
|
| 686 |
+
order = K0.order if K0.is_Composite else K1.order
|
| 687 |
+
|
| 688 |
+
# E.g. ZZ[x].unify(QQ.frac_field(x)) -> ZZ.frac_field(x)
|
| 689 |
+
if ((K0.is_FractionField and K1.is_PolynomialRing or
|
| 690 |
+
K1.is_FractionField and K0.is_PolynomialRing) and
|
| 691 |
+
(not K0_ground.is_Field or not K1_ground.is_Field) and domain.is_Field
|
| 692 |
+
and domain.has_assoc_Ring):
|
| 693 |
+
domain = domain.get_ring()
|
| 694 |
+
|
| 695 |
+
if K0.is_Composite and (not K1.is_Composite or K0.is_FractionField or K1.is_PolynomialRing):
|
| 696 |
+
cls = K0.__class__
|
| 697 |
+
else:
|
| 698 |
+
cls = K1.__class__
|
| 699 |
+
|
| 700 |
+
# Here cls might be PolynomialRing, FractionField, GlobalPolynomialRing
|
| 701 |
+
# (dense/old Polynomialring) or dense/old FractionField.
|
| 702 |
+
|
| 703 |
+
from sympy.polys.domains.old_polynomialring import GlobalPolynomialRing
|
| 704 |
+
if cls == GlobalPolynomialRing:
|
| 705 |
+
return cls(domain, symbols)
|
| 706 |
+
|
| 707 |
+
return cls(domain, symbols, order)
|
| 708 |
+
|
| 709 |
+
def unify(K0, K1, symbols=None):
|
| 710 |
+
"""
|
| 711 |
+
Construct a minimal domain that contains elements of ``K0`` and ``K1``.
|
| 712 |
+
|
| 713 |
+
Known domains (from smallest to largest):
|
| 714 |
+
|
| 715 |
+
- ``GF(p)``
|
| 716 |
+
- ``ZZ``
|
| 717 |
+
- ``QQ``
|
| 718 |
+
- ``RR(prec, tol)``
|
| 719 |
+
- ``CC(prec, tol)``
|
| 720 |
+
- ``ALG(a, b, c)``
|
| 721 |
+
- ``K[x, y, z]``
|
| 722 |
+
- ``K(x, y, z)``
|
| 723 |
+
- ``EX``
|
| 724 |
+
|
| 725 |
+
"""
|
| 726 |
+
if symbols is not None:
|
| 727 |
+
return K0.unify_with_symbols(K1, symbols)
|
| 728 |
+
|
| 729 |
+
if K0 == K1:
|
| 730 |
+
return K0
|
| 731 |
+
|
| 732 |
+
if not (K0.has_CharacteristicZero and K1.has_CharacteristicZero):
|
| 733 |
+
# Reject unification of domains with different characteristics.
|
| 734 |
+
if K0.characteristic() != K1.characteristic():
|
| 735 |
+
raise UnificationFailed("Cannot unify %s with %s" % (K0, K1))
|
| 736 |
+
|
| 737 |
+
# We do not get here if K0 == K1. The two domains have the same
|
| 738 |
+
# characteristic but are unequal so at least one is composite and
|
| 739 |
+
# we are unifying something like GF(3).unify(GF(3)[x]).
|
| 740 |
+
return K0.unify_composite(K1)
|
| 741 |
+
|
| 742 |
+
# From here we know both domains have characteristic zero and it can be
|
| 743 |
+
# acceptable to fall back on EX.
|
| 744 |
+
|
| 745 |
+
if K0.is_EXRAW:
|
| 746 |
+
return K0
|
| 747 |
+
if K1.is_EXRAW:
|
| 748 |
+
return K1
|
| 749 |
+
|
| 750 |
+
if K0.is_EX:
|
| 751 |
+
return K0
|
| 752 |
+
if K1.is_EX:
|
| 753 |
+
return K1
|
| 754 |
+
|
| 755 |
+
if K0.is_FiniteExtension or K1.is_FiniteExtension:
|
| 756 |
+
if K1.is_FiniteExtension:
|
| 757 |
+
K0, K1 = K1, K0
|
| 758 |
+
if K1.is_FiniteExtension:
|
| 759 |
+
# Unifying two extensions.
|
| 760 |
+
# Try to ensure that K0.unify(K1) == K1.unify(K0)
|
| 761 |
+
if list(ordered([K0.modulus, K1.modulus]))[1] == K0.modulus:
|
| 762 |
+
K0, K1 = K1, K0
|
| 763 |
+
return K1.set_domain(K0)
|
| 764 |
+
else:
|
| 765 |
+
# Drop the generator from other and unify with the base domain
|
| 766 |
+
K1 = K1.drop(K0.symbol)
|
| 767 |
+
K1 = K0.domain.unify(K1)
|
| 768 |
+
return K0.set_domain(K1)
|
| 769 |
+
|
| 770 |
+
if K0.is_Composite or K1.is_Composite:
|
| 771 |
+
return K0.unify_composite(K1)
|
| 772 |
+
|
| 773 |
+
def mkinexact(cls, K0, K1):
|
| 774 |
+
prec = max(K0.precision, K1.precision)
|
| 775 |
+
tol = max(K0.tolerance, K1.tolerance)
|
| 776 |
+
return cls(prec=prec, tol=tol)
|
| 777 |
+
|
| 778 |
+
if K1.is_ComplexField:
|
| 779 |
+
K0, K1 = K1, K0
|
| 780 |
+
if K0.is_ComplexField:
|
| 781 |
+
if K1.is_ComplexField or K1.is_RealField:
|
| 782 |
+
return mkinexact(K0.__class__, K0, K1)
|
| 783 |
+
else:
|
| 784 |
+
return K0
|
| 785 |
+
|
| 786 |
+
if K1.is_RealField:
|
| 787 |
+
K0, K1 = K1, K0
|
| 788 |
+
if K0.is_RealField:
|
| 789 |
+
if K1.is_RealField:
|
| 790 |
+
return mkinexact(K0.__class__, K0, K1)
|
| 791 |
+
elif K1.is_GaussianRing or K1.is_GaussianField:
|
| 792 |
+
from sympy.polys.domains.complexfield import ComplexField
|
| 793 |
+
return ComplexField(prec=K0.precision, tol=K0.tolerance)
|
| 794 |
+
else:
|
| 795 |
+
return K0
|
| 796 |
+
|
| 797 |
+
if K1.is_AlgebraicField:
|
| 798 |
+
K0, K1 = K1, K0
|
| 799 |
+
if K0.is_AlgebraicField:
|
| 800 |
+
if K1.is_GaussianRing:
|
| 801 |
+
K1 = K1.get_field()
|
| 802 |
+
if K1.is_GaussianField:
|
| 803 |
+
K1 = K1.as_AlgebraicField()
|
| 804 |
+
if K1.is_AlgebraicField:
|
| 805 |
+
return K0.__class__(K0.dom.unify(K1.dom), *_unify_gens(K0.orig_ext, K1.orig_ext))
|
| 806 |
+
else:
|
| 807 |
+
return K0
|
| 808 |
+
|
| 809 |
+
if K0.is_GaussianField:
|
| 810 |
+
return K0
|
| 811 |
+
if K1.is_GaussianField:
|
| 812 |
+
return K1
|
| 813 |
+
|
| 814 |
+
if K0.is_GaussianRing:
|
| 815 |
+
if K1.is_RationalField:
|
| 816 |
+
K0 = K0.get_field()
|
| 817 |
+
return K0
|
| 818 |
+
if K1.is_GaussianRing:
|
| 819 |
+
if K0.is_RationalField:
|
| 820 |
+
K1 = K1.get_field()
|
| 821 |
+
return K1
|
| 822 |
+
|
| 823 |
+
if K0.is_RationalField:
|
| 824 |
+
return K0
|
| 825 |
+
if K1.is_RationalField:
|
| 826 |
+
return K1
|
| 827 |
+
|
| 828 |
+
if K0.is_IntegerRing:
|
| 829 |
+
return K0
|
| 830 |
+
if K1.is_IntegerRing:
|
| 831 |
+
return K1
|
| 832 |
+
|
| 833 |
+
from sympy.polys.domains import EX
|
| 834 |
+
return EX
|
| 835 |
+
|
| 836 |
+
def __eq__(self, other):
|
| 837 |
+
"""Returns ``True`` if two domains are equivalent. """
|
| 838 |
+
# XXX: Remove this.
|
| 839 |
+
return isinstance(other, Domain) and self.dtype == other.dtype
|
| 840 |
+
|
| 841 |
+
def __ne__(self, other):
|
| 842 |
+
"""Returns ``False`` if two domains are equivalent. """
|
| 843 |
+
return not self == other
|
| 844 |
+
|
| 845 |
+
def map(self, seq):
|
| 846 |
+
"""Rersively apply ``self`` to all elements of ``seq``. """
|
| 847 |
+
result = []
|
| 848 |
+
|
| 849 |
+
for elt in seq:
|
| 850 |
+
if isinstance(elt, list):
|
| 851 |
+
result.append(self.map(elt))
|
| 852 |
+
else:
|
| 853 |
+
result.append(self(elt))
|
| 854 |
+
|
| 855 |
+
return result
|
| 856 |
+
|
| 857 |
+
def get_ring(self):
|
| 858 |
+
"""Returns a ring associated with ``self``. """
|
| 859 |
+
raise DomainError('there is no ring associated with %s' % self)
|
| 860 |
+
|
| 861 |
+
def get_field(self):
|
| 862 |
+
"""Returns a field associated with ``self``. """
|
| 863 |
+
raise DomainError('there is no field associated with %s' % self)
|
| 864 |
+
|
| 865 |
+
def get_exact(self):
|
| 866 |
+
"""Returns an exact domain associated with ``self``. """
|
| 867 |
+
return self
|
| 868 |
+
|
| 869 |
+
def __getitem__(self, symbols):
|
| 870 |
+
"""The mathematical way to make a polynomial ring. """
|
| 871 |
+
if hasattr(symbols, '__iter__'):
|
| 872 |
+
return self.poly_ring(*symbols)
|
| 873 |
+
else:
|
| 874 |
+
return self.poly_ring(symbols)
|
| 875 |
+
|
| 876 |
+
def poly_ring(self, *symbols, order=lex):
|
| 877 |
+
"""Returns a polynomial ring, i.e. `K[X]`. """
|
| 878 |
+
from sympy.polys.domains.polynomialring import PolynomialRing
|
| 879 |
+
return PolynomialRing(self, symbols, order)
|
| 880 |
+
|
| 881 |
+
def frac_field(self, *symbols, order=lex):
|
| 882 |
+
"""Returns a fraction field, i.e. `K(X)`. """
|
| 883 |
+
from sympy.polys.domains.fractionfield import FractionField
|
| 884 |
+
return FractionField(self, symbols, order)
|
| 885 |
+
|
| 886 |
+
def old_poly_ring(self, *symbols, **kwargs):
|
| 887 |
+
"""Returns a polynomial ring, i.e. `K[X]`. """
|
| 888 |
+
from sympy.polys.domains.old_polynomialring import PolynomialRing
|
| 889 |
+
return PolynomialRing(self, *symbols, **kwargs)
|
| 890 |
+
|
| 891 |
+
def old_frac_field(self, *symbols, **kwargs):
|
| 892 |
+
"""Returns a fraction field, i.e. `K(X)`. """
|
| 893 |
+
from sympy.polys.domains.old_fractionfield import FractionField
|
| 894 |
+
return FractionField(self, *symbols, **kwargs)
|
| 895 |
+
|
| 896 |
+
def algebraic_field(self, *extension, alias=None):
|
| 897 |
+
r"""Returns an algebraic field, i.e. `K(\alpha, \ldots)`. """
|
| 898 |
+
raise DomainError("Cannot create algebraic field over %s" % self)
|
| 899 |
+
|
| 900 |
+
def alg_field_from_poly(self, poly, alias=None, root_index=-1):
|
| 901 |
+
r"""
|
| 902 |
+
Convenience method to construct an algebraic extension on a root of a
|
| 903 |
+
polynomial, chosen by root index.
|
| 904 |
+
|
| 905 |
+
Parameters
|
| 906 |
+
==========
|
| 907 |
+
|
| 908 |
+
poly : :py:class:`~.Poly`
|
| 909 |
+
The polynomial whose root generates the extension.
|
| 910 |
+
alias : str, optional (default=None)
|
| 911 |
+
Symbol name for the generator of the extension.
|
| 912 |
+
E.g. "alpha" or "theta".
|
| 913 |
+
root_index : int, optional (default=-1)
|
| 914 |
+
Specifies which root of the polynomial is desired. The ordering is
|
| 915 |
+
as defined by the :py:class:`~.ComplexRootOf` class. The default of
|
| 916 |
+
``-1`` selects the most natural choice in the common cases of
|
| 917 |
+
quadratic and cyclotomic fields (the square root on the positive
|
| 918 |
+
real or imaginary axis, resp. $\mathrm{e}^{2\pi i/n}$).
|
| 919 |
+
|
| 920 |
+
Examples
|
| 921 |
+
========
|
| 922 |
+
|
| 923 |
+
>>> from sympy import QQ, Poly
|
| 924 |
+
>>> from sympy.abc import x
|
| 925 |
+
>>> f = Poly(x**2 - 2)
|
| 926 |
+
>>> K = QQ.alg_field_from_poly(f)
|
| 927 |
+
>>> K.ext.minpoly == f
|
| 928 |
+
True
|
| 929 |
+
>>> g = Poly(8*x**3 - 6*x - 1)
|
| 930 |
+
>>> L = QQ.alg_field_from_poly(g, "alpha")
|
| 931 |
+
>>> L.ext.minpoly == g
|
| 932 |
+
True
|
| 933 |
+
>>> L.to_sympy(L([1, 1, 1]))
|
| 934 |
+
alpha**2 + alpha + 1
|
| 935 |
+
|
| 936 |
+
"""
|
| 937 |
+
from sympy.polys.rootoftools import CRootOf
|
| 938 |
+
root = CRootOf(poly, root_index)
|
| 939 |
+
alpha = AlgebraicNumber(root, alias=alias)
|
| 940 |
+
return self.algebraic_field(alpha, alias=alias)
|
| 941 |
+
|
| 942 |
+
def cyclotomic_field(self, n, ss=False, alias="zeta", gen=None, root_index=-1):
|
| 943 |
+
r"""
|
| 944 |
+
Convenience method to construct a cyclotomic field.
|
| 945 |
+
|
| 946 |
+
Parameters
|
| 947 |
+
==========
|
| 948 |
+
|
| 949 |
+
n : int
|
| 950 |
+
Construct the nth cyclotomic field.
|
| 951 |
+
ss : boolean, optional (default=False)
|
| 952 |
+
If True, append *n* as a subscript on the alias string.
|
| 953 |
+
alias : str, optional (default="zeta")
|
| 954 |
+
Symbol name for the generator.
|
| 955 |
+
gen : :py:class:`~.Symbol`, optional (default=None)
|
| 956 |
+
Desired variable for the cyclotomic polynomial that defines the
|
| 957 |
+
field. If ``None``, a dummy variable will be used.
|
| 958 |
+
root_index : int, optional (default=-1)
|
| 959 |
+
Specifies which root of the polynomial is desired. The ordering is
|
| 960 |
+
as defined by the :py:class:`~.ComplexRootOf` class. The default of
|
| 961 |
+
``-1`` selects the root $\mathrm{e}^{2\pi i/n}$.
|
| 962 |
+
|
| 963 |
+
Examples
|
| 964 |
+
========
|
| 965 |
+
|
| 966 |
+
>>> from sympy import QQ, latex
|
| 967 |
+
>>> K = QQ.cyclotomic_field(5)
|
| 968 |
+
>>> K.to_sympy(K([-1, 1]))
|
| 969 |
+
1 - zeta
|
| 970 |
+
>>> L = QQ.cyclotomic_field(7, True)
|
| 971 |
+
>>> a = L.to_sympy(L([-1, 1]))
|
| 972 |
+
>>> print(a)
|
| 973 |
+
1 - zeta7
|
| 974 |
+
>>> print(latex(a))
|
| 975 |
+
1 - \zeta_{7}
|
| 976 |
+
|
| 977 |
+
"""
|
| 978 |
+
from sympy.polys.specialpolys import cyclotomic_poly
|
| 979 |
+
if ss:
|
| 980 |
+
alias += str(n)
|
| 981 |
+
return self.alg_field_from_poly(cyclotomic_poly(n, gen), alias=alias,
|
| 982 |
+
root_index=root_index)
|
| 983 |
+
|
| 984 |
+
def inject(self, *symbols):
|
| 985 |
+
"""Inject generators into this domain. """
|
| 986 |
+
raise NotImplementedError
|
| 987 |
+
|
| 988 |
+
def drop(self, *symbols):
|
| 989 |
+
"""Drop generators from this domain. """
|
| 990 |
+
if self.is_Simple:
|
| 991 |
+
return self
|
| 992 |
+
raise NotImplementedError # pragma: no cover
|
| 993 |
+
|
| 994 |
+
def is_zero(self, a):
|
| 995 |
+
"""Returns True if ``a`` is zero. """
|
| 996 |
+
return not a
|
| 997 |
+
|
| 998 |
+
def is_one(self, a):
|
| 999 |
+
"""Returns True if ``a`` is one. """
|
| 1000 |
+
return a == self.one
|
| 1001 |
+
|
| 1002 |
+
def is_positive(self, a):
|
| 1003 |
+
"""Returns True if ``a`` is positive. """
|
| 1004 |
+
return a > 0
|
| 1005 |
+
|
| 1006 |
+
def is_negative(self, a):
|
| 1007 |
+
"""Returns True if ``a`` is negative. """
|
| 1008 |
+
return a < 0
|
| 1009 |
+
|
| 1010 |
+
def is_nonpositive(self, a):
|
| 1011 |
+
"""Returns True if ``a`` is non-positive. """
|
| 1012 |
+
return a <= 0
|
| 1013 |
+
|
| 1014 |
+
def is_nonnegative(self, a):
|
| 1015 |
+
"""Returns True if ``a`` is non-negative. """
|
| 1016 |
+
return a >= 0
|
| 1017 |
+
|
| 1018 |
+
def canonical_unit(self, a):
|
| 1019 |
+
if self.is_negative(a):
|
| 1020 |
+
return -self.one
|
| 1021 |
+
else:
|
| 1022 |
+
return self.one
|
| 1023 |
+
|
| 1024 |
+
def abs(self, a):
|
| 1025 |
+
"""Absolute value of ``a``, implies ``__abs__``. """
|
| 1026 |
+
return abs(a)
|
| 1027 |
+
|
| 1028 |
+
def neg(self, a):
|
| 1029 |
+
"""Returns ``a`` negated, implies ``__neg__``. """
|
| 1030 |
+
return -a
|
| 1031 |
+
|
| 1032 |
+
def pos(self, a):
|
| 1033 |
+
"""Returns ``a`` positive, implies ``__pos__``. """
|
| 1034 |
+
return +a
|
| 1035 |
+
|
| 1036 |
+
def add(self, a, b):
|
| 1037 |
+
"""Sum of ``a`` and ``b``, implies ``__add__``. """
|
| 1038 |
+
return a + b
|
| 1039 |
+
|
| 1040 |
+
def sub(self, a, b):
|
| 1041 |
+
"""Difference of ``a`` and ``b``, implies ``__sub__``. """
|
| 1042 |
+
return a - b
|
| 1043 |
+
|
| 1044 |
+
def mul(self, a, b):
|
| 1045 |
+
"""Product of ``a`` and ``b``, implies ``__mul__``. """
|
| 1046 |
+
return a * b
|
| 1047 |
+
|
| 1048 |
+
def pow(self, a, b):
|
| 1049 |
+
"""Raise ``a`` to power ``b``, implies ``__pow__``. """
|
| 1050 |
+
return a ** b
|
| 1051 |
+
|
| 1052 |
+
def exquo(self, a, b):
|
| 1053 |
+
"""Exact quotient of *a* and *b*. Analogue of ``a / b``.
|
| 1054 |
+
|
| 1055 |
+
Explanation
|
| 1056 |
+
===========
|
| 1057 |
+
|
| 1058 |
+
This is essentially the same as ``a / b`` except that an error will be
|
| 1059 |
+
raised if the division is inexact (if there is any remainder) and the
|
| 1060 |
+
result will always be a domain element. When working in a
|
| 1061 |
+
:py:class:`~.Domain` that is not a :py:class:`~.Field` (e.g. :ref:`ZZ`
|
| 1062 |
+
or :ref:`K[x]`) ``exquo`` should be used instead of ``/``.
|
| 1063 |
+
|
| 1064 |
+
The key invariant is that if ``q = K.exquo(a, b)`` (and ``exquo`` does
|
| 1065 |
+
not raise an exception) then ``a == b*q``.
|
| 1066 |
+
|
| 1067 |
+
Examples
|
| 1068 |
+
========
|
| 1069 |
+
|
| 1070 |
+
We can use ``K.exquo`` instead of ``/`` for exact division.
|
| 1071 |
+
|
| 1072 |
+
>>> from sympy import ZZ
|
| 1073 |
+
>>> ZZ.exquo(ZZ(4), ZZ(2))
|
| 1074 |
+
2
|
| 1075 |
+
>>> ZZ.exquo(ZZ(5), ZZ(2))
|
| 1076 |
+
Traceback (most recent call last):
|
| 1077 |
+
...
|
| 1078 |
+
ExactQuotientFailed: 2 does not divide 5 in ZZ
|
| 1079 |
+
|
| 1080 |
+
Over a :py:class:`~.Field` such as :ref:`QQ`, division (with nonzero
|
| 1081 |
+
divisor) is always exact so in that case ``/`` can be used instead of
|
| 1082 |
+
:py:meth:`~.Domain.exquo`.
|
| 1083 |
+
|
| 1084 |
+
>>> from sympy import QQ
|
| 1085 |
+
>>> QQ.exquo(QQ(5), QQ(2))
|
| 1086 |
+
5/2
|
| 1087 |
+
>>> QQ(5) / QQ(2)
|
| 1088 |
+
5/2
|
| 1089 |
+
|
| 1090 |
+
Parameters
|
| 1091 |
+
==========
|
| 1092 |
+
|
| 1093 |
+
a: domain element
|
| 1094 |
+
The dividend
|
| 1095 |
+
b: domain element
|
| 1096 |
+
The divisor
|
| 1097 |
+
|
| 1098 |
+
Returns
|
| 1099 |
+
=======
|
| 1100 |
+
|
| 1101 |
+
q: domain element
|
| 1102 |
+
The exact quotient
|
| 1103 |
+
|
| 1104 |
+
Raises
|
| 1105 |
+
======
|
| 1106 |
+
|
| 1107 |
+
ExactQuotientFailed: if exact division is not possible.
|
| 1108 |
+
ZeroDivisionError: when the divisor is zero.
|
| 1109 |
+
|
| 1110 |
+
See also
|
| 1111 |
+
========
|
| 1112 |
+
|
| 1113 |
+
quo: Analogue of ``a // b``
|
| 1114 |
+
rem: Analogue of ``a % b``
|
| 1115 |
+
div: Analogue of ``divmod(a, b)``
|
| 1116 |
+
|
| 1117 |
+
Notes
|
| 1118 |
+
=====
|
| 1119 |
+
|
| 1120 |
+
Since the default :py:attr:`~.Domain.dtype` for :ref:`ZZ` is ``int``
|
| 1121 |
+
(or ``mpz``) division as ``a / b`` should not be used as it would give
|
| 1122 |
+
a ``float`` which is not a domain element.
|
| 1123 |
+
|
| 1124 |
+
>>> ZZ(4) / ZZ(2) # doctest: +SKIP
|
| 1125 |
+
2.0
|
| 1126 |
+
>>> ZZ(5) / ZZ(2) # doctest: +SKIP
|
| 1127 |
+
2.5
|
| 1128 |
+
|
| 1129 |
+
On the other hand with `SYMPY_GROUND_TYPES=flint` elements of :ref:`ZZ`
|
| 1130 |
+
are ``flint.fmpz`` and division would raise an exception:
|
| 1131 |
+
|
| 1132 |
+
>>> ZZ(4) / ZZ(2) # doctest: +SKIP
|
| 1133 |
+
Traceback (most recent call last):
|
| 1134 |
+
...
|
| 1135 |
+
TypeError: unsupported operand type(s) for /: 'fmpz' and 'fmpz'
|
| 1136 |
+
|
| 1137 |
+
Using ``/`` with :ref:`ZZ` will lead to incorrect results so
|
| 1138 |
+
:py:meth:`~.Domain.exquo` should be used instead.
|
| 1139 |
+
|
| 1140 |
+
"""
|
| 1141 |
+
raise NotImplementedError
|
| 1142 |
+
|
| 1143 |
+
def quo(self, a, b):
|
| 1144 |
+
"""Quotient of *a* and *b*. Analogue of ``a // b``.
|
| 1145 |
+
|
| 1146 |
+
``K.quo(a, b)`` is equivalent to ``K.div(a, b)[0]``. See
|
| 1147 |
+
:py:meth:`~.Domain.div` for more explanation.
|
| 1148 |
+
|
| 1149 |
+
See also
|
| 1150 |
+
========
|
| 1151 |
+
|
| 1152 |
+
rem: Analogue of ``a % b``
|
| 1153 |
+
div: Analogue of ``divmod(a, b)``
|
| 1154 |
+
exquo: Analogue of ``a / b``
|
| 1155 |
+
"""
|
| 1156 |
+
raise NotImplementedError
|
| 1157 |
+
|
| 1158 |
+
def rem(self, a, b):
|
| 1159 |
+
"""Modulo division of *a* and *b*. Analogue of ``a % b``.
|
| 1160 |
+
|
| 1161 |
+
``K.rem(a, b)`` is equivalent to ``K.div(a, b)[1]``. See
|
| 1162 |
+
:py:meth:`~.Domain.div` for more explanation.
|
| 1163 |
+
|
| 1164 |
+
See also
|
| 1165 |
+
========
|
| 1166 |
+
|
| 1167 |
+
quo: Analogue of ``a // b``
|
| 1168 |
+
div: Analogue of ``divmod(a, b)``
|
| 1169 |
+
exquo: Analogue of ``a / b``
|
| 1170 |
+
"""
|
| 1171 |
+
raise NotImplementedError
|
| 1172 |
+
|
| 1173 |
+
def div(self, a, b):
|
| 1174 |
+
"""Quotient and remainder for *a* and *b*. Analogue of ``divmod(a, b)``
|
| 1175 |
+
|
| 1176 |
+
Explanation
|
| 1177 |
+
===========
|
| 1178 |
+
|
| 1179 |
+
This is essentially the same as ``divmod(a, b)`` except that is more
|
| 1180 |
+
consistent when working over some :py:class:`~.Field` domains such as
|
| 1181 |
+
:ref:`QQ`. When working over an arbitrary :py:class:`~.Domain` the
|
| 1182 |
+
:py:meth:`~.Domain.div` method should be used instead of ``divmod``.
|
| 1183 |
+
|
| 1184 |
+
The key invariant is that if ``q, r = K.div(a, b)`` then
|
| 1185 |
+
``a == b*q + r``.
|
| 1186 |
+
|
| 1187 |
+
The result of ``K.div(a, b)`` is the same as the tuple
|
| 1188 |
+
``(K.quo(a, b), K.rem(a, b))`` except that if both quotient and
|
| 1189 |
+
remainder are needed then it is more efficient to use
|
| 1190 |
+
:py:meth:`~.Domain.div`.
|
| 1191 |
+
|
| 1192 |
+
Examples
|
| 1193 |
+
========
|
| 1194 |
+
|
| 1195 |
+
We can use ``K.div`` instead of ``divmod`` for floor division and
|
| 1196 |
+
remainder.
|
| 1197 |
+
|
| 1198 |
+
>>> from sympy import ZZ, QQ
|
| 1199 |
+
>>> ZZ.div(ZZ(5), ZZ(2))
|
| 1200 |
+
(2, 1)
|
| 1201 |
+
|
| 1202 |
+
If ``K`` is a :py:class:`~.Field` then the division is always exact
|
| 1203 |
+
with a remainder of :py:attr:`~.Domain.zero`.
|
| 1204 |
+
|
| 1205 |
+
>>> QQ.div(QQ(5), QQ(2))
|
| 1206 |
+
(5/2, 0)
|
| 1207 |
+
|
| 1208 |
+
Parameters
|
| 1209 |
+
==========
|
| 1210 |
+
|
| 1211 |
+
a: domain element
|
| 1212 |
+
The dividend
|
| 1213 |
+
b: domain element
|
| 1214 |
+
The divisor
|
| 1215 |
+
|
| 1216 |
+
Returns
|
| 1217 |
+
=======
|
| 1218 |
+
|
| 1219 |
+
(q, r): tuple of domain elements
|
| 1220 |
+
The quotient and remainder
|
| 1221 |
+
|
| 1222 |
+
Raises
|
| 1223 |
+
======
|
| 1224 |
+
|
| 1225 |
+
ZeroDivisionError: when the divisor is zero.
|
| 1226 |
+
|
| 1227 |
+
See also
|
| 1228 |
+
========
|
| 1229 |
+
|
| 1230 |
+
quo: Analogue of ``a // b``
|
| 1231 |
+
rem: Analogue of ``a % b``
|
| 1232 |
+
exquo: Analogue of ``a / b``
|
| 1233 |
+
|
| 1234 |
+
Notes
|
| 1235 |
+
=====
|
| 1236 |
+
|
| 1237 |
+
If ``gmpy`` is installed then the ``gmpy.mpq`` type will be used as
|
| 1238 |
+
the :py:attr:`~.Domain.dtype` for :ref:`QQ`. The ``gmpy.mpq`` type
|
| 1239 |
+
defines ``divmod`` in a way that is undesirable so
|
| 1240 |
+
:py:meth:`~.Domain.div` should be used instead of ``divmod``.
|
| 1241 |
+
|
| 1242 |
+
>>> a = QQ(1)
|
| 1243 |
+
>>> b = QQ(3, 2)
|
| 1244 |
+
>>> a # doctest: +SKIP
|
| 1245 |
+
mpq(1,1)
|
| 1246 |
+
>>> b # doctest: +SKIP
|
| 1247 |
+
mpq(3,2)
|
| 1248 |
+
>>> divmod(a, b) # doctest: +SKIP
|
| 1249 |
+
(mpz(0), mpq(1,1))
|
| 1250 |
+
>>> QQ.div(a, b) # doctest: +SKIP
|
| 1251 |
+
(mpq(2,3), mpq(0,1))
|
| 1252 |
+
|
| 1253 |
+
Using ``//`` or ``%`` with :ref:`QQ` will lead to incorrect results so
|
| 1254 |
+
:py:meth:`~.Domain.div` should be used instead.
|
| 1255 |
+
|
| 1256 |
+
"""
|
| 1257 |
+
raise NotImplementedError
|
| 1258 |
+
|
| 1259 |
+
def invert(self, a, b):
|
| 1260 |
+
"""Returns inversion of ``a mod b``, implies something. """
|
| 1261 |
+
raise NotImplementedError
|
| 1262 |
+
|
| 1263 |
+
def revert(self, a):
|
| 1264 |
+
"""Returns ``a**(-1)`` if possible. """
|
| 1265 |
+
raise NotImplementedError
|
| 1266 |
+
|
| 1267 |
+
def numer(self, a):
|
| 1268 |
+
"""Returns numerator of ``a``. """
|
| 1269 |
+
raise NotImplementedError
|
| 1270 |
+
|
| 1271 |
+
def denom(self, a):
|
| 1272 |
+
"""Returns denominator of ``a``. """
|
| 1273 |
+
raise NotImplementedError
|
| 1274 |
+
|
| 1275 |
+
def half_gcdex(self, a, b):
|
| 1276 |
+
"""Half extended GCD of ``a`` and ``b``. """
|
| 1277 |
+
s, t, h = self.gcdex(a, b)
|
| 1278 |
+
return s, h
|
| 1279 |
+
|
| 1280 |
+
def gcdex(self, a, b):
|
| 1281 |
+
"""Extended GCD of ``a`` and ``b``. """
|
| 1282 |
+
raise NotImplementedError
|
| 1283 |
+
|
| 1284 |
+
def cofactors(self, a, b):
|
| 1285 |
+
"""Returns GCD and cofactors of ``a`` and ``b``. """
|
| 1286 |
+
gcd = self.gcd(a, b)
|
| 1287 |
+
cfa = self.quo(a, gcd)
|
| 1288 |
+
cfb = self.quo(b, gcd)
|
| 1289 |
+
return gcd, cfa, cfb
|
| 1290 |
+
|
| 1291 |
+
def gcd(self, a, b):
|
| 1292 |
+
"""Returns GCD of ``a`` and ``b``. """
|
| 1293 |
+
raise NotImplementedError
|
| 1294 |
+
|
| 1295 |
+
def lcm(self, a, b):
|
| 1296 |
+
"""Returns LCM of ``a`` and ``b``. """
|
| 1297 |
+
raise NotImplementedError
|
| 1298 |
+
|
| 1299 |
+
def log(self, a, b):
|
| 1300 |
+
"""Returns b-base logarithm of ``a``. """
|
| 1301 |
+
raise NotImplementedError
|
| 1302 |
+
|
| 1303 |
+
def sqrt(self, a):
|
| 1304 |
+
"""Returns a (possibly inexact) square root of ``a``.
|
| 1305 |
+
|
| 1306 |
+
Explanation
|
| 1307 |
+
===========
|
| 1308 |
+
There is no universal definition of "inexact square root" for all
|
| 1309 |
+
domains. It is not recommended to implement this method for domains
|
| 1310 |
+
other then :ref:`ZZ`.
|
| 1311 |
+
|
| 1312 |
+
See also
|
| 1313 |
+
========
|
| 1314 |
+
exsqrt
|
| 1315 |
+
"""
|
| 1316 |
+
raise NotImplementedError
|
| 1317 |
+
|
| 1318 |
+
def is_square(self, a):
|
| 1319 |
+
"""Returns whether ``a`` is a square in the domain.
|
| 1320 |
+
|
| 1321 |
+
Explanation
|
| 1322 |
+
===========
|
| 1323 |
+
Returns ``True`` if there is an element ``b`` in the domain such that
|
| 1324 |
+
``b * b == a``, otherwise returns ``False``. For inexact domains like
|
| 1325 |
+
:ref:`RR` and :ref:`CC`, a tiny difference in this equality can be
|
| 1326 |
+
tolerated.
|
| 1327 |
+
|
| 1328 |
+
See also
|
| 1329 |
+
========
|
| 1330 |
+
exsqrt
|
| 1331 |
+
"""
|
| 1332 |
+
raise NotImplementedError
|
| 1333 |
+
|
| 1334 |
+
def exsqrt(self, a):
|
| 1335 |
+
"""Principal square root of a within the domain if ``a`` is square.
|
| 1336 |
+
|
| 1337 |
+
Explanation
|
| 1338 |
+
===========
|
| 1339 |
+
The implementation of this method should return an element ``b`` in the
|
| 1340 |
+
domain such that ``b * b == a``, or ``None`` if there is no such ``b``.
|
| 1341 |
+
For inexact domains like :ref:`RR` and :ref:`CC`, a tiny difference in
|
| 1342 |
+
this equality can be tolerated. The choice of a "principal" square root
|
| 1343 |
+
should follow a consistent rule whenever possible.
|
| 1344 |
+
|
| 1345 |
+
See also
|
| 1346 |
+
========
|
| 1347 |
+
sqrt, is_square
|
| 1348 |
+
"""
|
| 1349 |
+
raise NotImplementedError
|
| 1350 |
+
|
| 1351 |
+
def evalf(self, a, prec=None, **options):
|
| 1352 |
+
"""Returns numerical approximation of ``a``. """
|
| 1353 |
+
return self.to_sympy(a).evalf(prec, **options)
|
| 1354 |
+
|
| 1355 |
+
n = evalf
|
| 1356 |
+
|
| 1357 |
+
def real(self, a):
|
| 1358 |
+
return a
|
| 1359 |
+
|
| 1360 |
+
def imag(self, a):
|
| 1361 |
+
return self.zero
|
| 1362 |
+
|
| 1363 |
+
def almosteq(self, a, b, tolerance=None):
|
| 1364 |
+
"""Check if ``a`` and ``b`` are almost equal. """
|
| 1365 |
+
return a == b
|
| 1366 |
+
|
| 1367 |
+
def characteristic(self):
|
| 1368 |
+
"""Return the characteristic of this domain. """
|
| 1369 |
+
raise NotImplementedError('characteristic()')
|
| 1370 |
+
|
| 1371 |
+
|
| 1372 |
+
__all__ = ['Domain']
|
lib/python3.12/site-packages/sympy/polys/domains/domainelement.py
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Trait for implementing domain elements. """
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
from sympy.utilities import public
|
| 5 |
+
|
| 6 |
+
@public
|
| 7 |
+
class DomainElement:
|
| 8 |
+
"""
|
| 9 |
+
Represents an element of a domain.
|
| 10 |
+
|
| 11 |
+
Mix in this trait into a class whose instances should be recognized as
|
| 12 |
+
elements of a domain. Method ``parent()`` gives that domain.
|
| 13 |
+
"""
|
| 14 |
+
|
| 15 |
+
__slots__ = ()
|
| 16 |
+
|
| 17 |
+
def parent(self):
|
| 18 |
+
"""Get the domain associated with ``self``
|
| 19 |
+
|
| 20 |
+
Examples
|
| 21 |
+
========
|
| 22 |
+
|
| 23 |
+
>>> from sympy import ZZ, symbols
|
| 24 |
+
>>> x, y = symbols('x, y')
|
| 25 |
+
>>> K = ZZ[x,y]
|
| 26 |
+
>>> p = K(x)**2 + K(y)**2
|
| 27 |
+
>>> p
|
| 28 |
+
x**2 + y**2
|
| 29 |
+
>>> p.parent()
|
| 30 |
+
ZZ[x,y]
|
| 31 |
+
|
| 32 |
+
Notes
|
| 33 |
+
=====
|
| 34 |
+
|
| 35 |
+
This is used by :py:meth:`~.Domain.convert` to identify the domain
|
| 36 |
+
associated with a domain element.
|
| 37 |
+
"""
|
| 38 |
+
raise NotImplementedError("abstract method")
|
lib/python3.12/site-packages/sympy/polys/domains/expressiondomain.py
ADDED
|
@@ -0,0 +1,278 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Implementation of :class:`ExpressionDomain` class. """
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
from sympy.core import sympify, SympifyError
|
| 5 |
+
from sympy.polys.domains.domainelement import DomainElement
|
| 6 |
+
from sympy.polys.domains.characteristiczero import CharacteristicZero
|
| 7 |
+
from sympy.polys.domains.field import Field
|
| 8 |
+
from sympy.polys.domains.simpledomain import SimpleDomain
|
| 9 |
+
from sympy.polys.polyutils import PicklableWithSlots
|
| 10 |
+
from sympy.utilities import public
|
| 11 |
+
|
| 12 |
+
eflags = {"deep": False, "mul": True, "power_exp": False, "power_base": False,
|
| 13 |
+
"basic": False, "multinomial": False, "log": False}
|
| 14 |
+
|
| 15 |
+
@public
|
| 16 |
+
class ExpressionDomain(Field, CharacteristicZero, SimpleDomain):
|
| 17 |
+
"""A class for arbitrary expressions. """
|
| 18 |
+
|
| 19 |
+
is_SymbolicDomain = is_EX = True
|
| 20 |
+
|
| 21 |
+
class Expression(DomainElement, PicklableWithSlots):
|
| 22 |
+
"""An arbitrary expression. """
|
| 23 |
+
|
| 24 |
+
__slots__ = ('ex',)
|
| 25 |
+
|
| 26 |
+
def __init__(self, ex):
|
| 27 |
+
if not isinstance(ex, self.__class__):
|
| 28 |
+
self.ex = sympify(ex)
|
| 29 |
+
else:
|
| 30 |
+
self.ex = ex.ex
|
| 31 |
+
|
| 32 |
+
def __repr__(f):
|
| 33 |
+
return 'EX(%s)' % repr(f.ex)
|
| 34 |
+
|
| 35 |
+
def __str__(f):
|
| 36 |
+
return 'EX(%s)' % str(f.ex)
|
| 37 |
+
|
| 38 |
+
def __hash__(self):
|
| 39 |
+
return hash((self.__class__.__name__, self.ex))
|
| 40 |
+
|
| 41 |
+
def parent(self):
|
| 42 |
+
return EX
|
| 43 |
+
|
| 44 |
+
def as_expr(f):
|
| 45 |
+
return f.ex
|
| 46 |
+
|
| 47 |
+
def numer(f):
|
| 48 |
+
return f.__class__(f.ex.as_numer_denom()[0])
|
| 49 |
+
|
| 50 |
+
def denom(f):
|
| 51 |
+
return f.__class__(f.ex.as_numer_denom()[1])
|
| 52 |
+
|
| 53 |
+
def simplify(f, ex):
|
| 54 |
+
return f.__class__(ex.cancel().expand(**eflags))
|
| 55 |
+
|
| 56 |
+
def __abs__(f):
|
| 57 |
+
return f.__class__(abs(f.ex))
|
| 58 |
+
|
| 59 |
+
def __neg__(f):
|
| 60 |
+
return f.__class__(-f.ex)
|
| 61 |
+
|
| 62 |
+
def _to_ex(f, g):
|
| 63 |
+
try:
|
| 64 |
+
return f.__class__(g)
|
| 65 |
+
except SympifyError:
|
| 66 |
+
return None
|
| 67 |
+
|
| 68 |
+
def __lt__(f, g):
|
| 69 |
+
return f.ex.sort_key() < g.ex.sort_key()
|
| 70 |
+
|
| 71 |
+
def __add__(f, g):
|
| 72 |
+
g = f._to_ex(g)
|
| 73 |
+
|
| 74 |
+
if g is None:
|
| 75 |
+
return NotImplemented
|
| 76 |
+
elif g == EX.zero:
|
| 77 |
+
return f
|
| 78 |
+
elif f == EX.zero:
|
| 79 |
+
return g
|
| 80 |
+
else:
|
| 81 |
+
return f.simplify(f.ex + g.ex)
|
| 82 |
+
|
| 83 |
+
def __radd__(f, g):
|
| 84 |
+
return f.simplify(f.__class__(g).ex + f.ex)
|
| 85 |
+
|
| 86 |
+
def __sub__(f, g):
|
| 87 |
+
g = f._to_ex(g)
|
| 88 |
+
|
| 89 |
+
if g is None:
|
| 90 |
+
return NotImplemented
|
| 91 |
+
elif g == EX.zero:
|
| 92 |
+
return f
|
| 93 |
+
elif f == EX.zero:
|
| 94 |
+
return -g
|
| 95 |
+
else:
|
| 96 |
+
return f.simplify(f.ex - g.ex)
|
| 97 |
+
|
| 98 |
+
def __rsub__(f, g):
|
| 99 |
+
return f.simplify(f.__class__(g).ex - f.ex)
|
| 100 |
+
|
| 101 |
+
def __mul__(f, g):
|
| 102 |
+
g = f._to_ex(g)
|
| 103 |
+
|
| 104 |
+
if g is None:
|
| 105 |
+
return NotImplemented
|
| 106 |
+
|
| 107 |
+
if EX.zero in (f, g):
|
| 108 |
+
return EX.zero
|
| 109 |
+
elif f.ex.is_Number and g.ex.is_Number:
|
| 110 |
+
return f.__class__(f.ex*g.ex)
|
| 111 |
+
|
| 112 |
+
return f.simplify(f.ex*g.ex)
|
| 113 |
+
|
| 114 |
+
def __rmul__(f, g):
|
| 115 |
+
return f.simplify(f.__class__(g).ex*f.ex)
|
| 116 |
+
|
| 117 |
+
def __pow__(f, n):
|
| 118 |
+
n = f._to_ex(n)
|
| 119 |
+
|
| 120 |
+
if n is not None:
|
| 121 |
+
return f.simplify(f.ex**n.ex)
|
| 122 |
+
else:
|
| 123 |
+
return NotImplemented
|
| 124 |
+
|
| 125 |
+
def __truediv__(f, g):
|
| 126 |
+
g = f._to_ex(g)
|
| 127 |
+
|
| 128 |
+
if g is not None:
|
| 129 |
+
return f.simplify(f.ex/g.ex)
|
| 130 |
+
else:
|
| 131 |
+
return NotImplemented
|
| 132 |
+
|
| 133 |
+
def __rtruediv__(f, g):
|
| 134 |
+
return f.simplify(f.__class__(g).ex/f.ex)
|
| 135 |
+
|
| 136 |
+
def __eq__(f, g):
|
| 137 |
+
return f.ex == f.__class__(g).ex
|
| 138 |
+
|
| 139 |
+
def __ne__(f, g):
|
| 140 |
+
return not f == g
|
| 141 |
+
|
| 142 |
+
def __bool__(f):
|
| 143 |
+
return not f.ex.is_zero
|
| 144 |
+
|
| 145 |
+
def gcd(f, g):
|
| 146 |
+
from sympy.polys import gcd
|
| 147 |
+
return f.__class__(gcd(f.ex, f.__class__(g).ex))
|
| 148 |
+
|
| 149 |
+
def lcm(f, g):
|
| 150 |
+
from sympy.polys import lcm
|
| 151 |
+
return f.__class__(lcm(f.ex, f.__class__(g).ex))
|
| 152 |
+
|
| 153 |
+
dtype = Expression
|
| 154 |
+
|
| 155 |
+
zero = Expression(0)
|
| 156 |
+
one = Expression(1)
|
| 157 |
+
|
| 158 |
+
rep = 'EX'
|
| 159 |
+
|
| 160 |
+
has_assoc_Ring = False
|
| 161 |
+
has_assoc_Field = True
|
| 162 |
+
|
| 163 |
+
def __init__(self):
|
| 164 |
+
pass
|
| 165 |
+
|
| 166 |
+
def __eq__(self, other):
|
| 167 |
+
if isinstance(other, ExpressionDomain):
|
| 168 |
+
return True
|
| 169 |
+
else:
|
| 170 |
+
return NotImplemented
|
| 171 |
+
|
| 172 |
+
def __hash__(self):
|
| 173 |
+
return hash("EX")
|
| 174 |
+
|
| 175 |
+
def to_sympy(self, a):
|
| 176 |
+
"""Convert ``a`` to a SymPy object. """
|
| 177 |
+
return a.as_expr()
|
| 178 |
+
|
| 179 |
+
def from_sympy(self, a):
|
| 180 |
+
"""Convert SymPy's expression to ``dtype``. """
|
| 181 |
+
return self.dtype(a)
|
| 182 |
+
|
| 183 |
+
def from_ZZ(K1, a, K0):
|
| 184 |
+
"""Convert a Python ``int`` object to ``dtype``. """
|
| 185 |
+
return K1(K0.to_sympy(a))
|
| 186 |
+
|
| 187 |
+
def from_ZZ_python(K1, a, K0):
|
| 188 |
+
"""Convert a Python ``int`` object to ``dtype``. """
|
| 189 |
+
return K1(K0.to_sympy(a))
|
| 190 |
+
|
| 191 |
+
def from_QQ(K1, a, K0):
|
| 192 |
+
"""Convert a Python ``Fraction`` object to ``dtype``. """
|
| 193 |
+
return K1(K0.to_sympy(a))
|
| 194 |
+
|
| 195 |
+
def from_QQ_python(K1, a, K0):
|
| 196 |
+
"""Convert a Python ``Fraction`` object to ``dtype``. """
|
| 197 |
+
return K1(K0.to_sympy(a))
|
| 198 |
+
|
| 199 |
+
def from_ZZ_gmpy(K1, a, K0):
|
| 200 |
+
"""Convert a GMPY ``mpz`` object to ``dtype``. """
|
| 201 |
+
return K1(K0.to_sympy(a))
|
| 202 |
+
|
| 203 |
+
def from_QQ_gmpy(K1, a, K0):
|
| 204 |
+
"""Convert a GMPY ``mpq`` object to ``dtype``. """
|
| 205 |
+
return K1(K0.to_sympy(a))
|
| 206 |
+
|
| 207 |
+
def from_GaussianIntegerRing(K1, a, K0):
|
| 208 |
+
"""Convert a ``GaussianRational`` object to ``dtype``. """
|
| 209 |
+
return K1(K0.to_sympy(a))
|
| 210 |
+
|
| 211 |
+
def from_GaussianRationalField(K1, a, K0):
|
| 212 |
+
"""Convert a ``GaussianRational`` object to ``dtype``. """
|
| 213 |
+
return K1(K0.to_sympy(a))
|
| 214 |
+
|
| 215 |
+
def from_AlgebraicField(K1, a, K0):
|
| 216 |
+
"""Convert an ``ANP`` object to ``dtype``. """
|
| 217 |
+
return K1(K0.to_sympy(a))
|
| 218 |
+
|
| 219 |
+
def from_RealField(K1, a, K0):
|
| 220 |
+
"""Convert a mpmath ``mpf`` object to ``dtype``. """
|
| 221 |
+
return K1(K0.to_sympy(a))
|
| 222 |
+
|
| 223 |
+
def from_ComplexField(K1, a, K0):
|
| 224 |
+
"""Convert a mpmath ``mpc`` object to ``dtype``. """
|
| 225 |
+
return K1(K0.to_sympy(a))
|
| 226 |
+
|
| 227 |
+
def from_PolynomialRing(K1, a, K0):
|
| 228 |
+
"""Convert a ``DMP`` object to ``dtype``. """
|
| 229 |
+
return K1(K0.to_sympy(a))
|
| 230 |
+
|
| 231 |
+
def from_FractionField(K1, a, K0):
|
| 232 |
+
"""Convert a ``DMF`` object to ``dtype``. """
|
| 233 |
+
return K1(K0.to_sympy(a))
|
| 234 |
+
|
| 235 |
+
def from_ExpressionDomain(K1, a, K0):
|
| 236 |
+
"""Convert a ``EX`` object to ``dtype``. """
|
| 237 |
+
return a
|
| 238 |
+
|
| 239 |
+
def get_ring(self):
|
| 240 |
+
"""Returns a ring associated with ``self``. """
|
| 241 |
+
return self # XXX: EX is not a ring but we don't have much choice here.
|
| 242 |
+
|
| 243 |
+
def get_field(self):
|
| 244 |
+
"""Returns a field associated with ``self``. """
|
| 245 |
+
return self
|
| 246 |
+
|
| 247 |
+
def is_positive(self, a):
|
| 248 |
+
"""Returns True if ``a`` is positive. """
|
| 249 |
+
return a.ex.as_coeff_mul()[0].is_positive
|
| 250 |
+
|
| 251 |
+
def is_negative(self, a):
|
| 252 |
+
"""Returns True if ``a`` is negative. """
|
| 253 |
+
return a.ex.could_extract_minus_sign()
|
| 254 |
+
|
| 255 |
+
def is_nonpositive(self, a):
|
| 256 |
+
"""Returns True if ``a`` is non-positive. """
|
| 257 |
+
return a.ex.as_coeff_mul()[0].is_nonpositive
|
| 258 |
+
|
| 259 |
+
def is_nonnegative(self, a):
|
| 260 |
+
"""Returns True if ``a`` is non-negative. """
|
| 261 |
+
return a.ex.as_coeff_mul()[0].is_nonnegative
|
| 262 |
+
|
| 263 |
+
def numer(self, a):
|
| 264 |
+
"""Returns numerator of ``a``. """
|
| 265 |
+
return a.numer()
|
| 266 |
+
|
| 267 |
+
def denom(self, a):
|
| 268 |
+
"""Returns denominator of ``a``. """
|
| 269 |
+
return a.denom()
|
| 270 |
+
|
| 271 |
+
def gcd(self, a, b):
|
| 272 |
+
return self(1)
|
| 273 |
+
|
| 274 |
+
def lcm(self, a, b):
|
| 275 |
+
return a.lcm(b)
|
| 276 |
+
|
| 277 |
+
|
| 278 |
+
EX = ExpressionDomain()
|
lib/python3.12/site-packages/sympy/polys/domains/expressionrawdomain.py
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Implementation of :class:`ExpressionRawDomain` class. """
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
from sympy.core import Expr, S, sympify, Add
|
| 5 |
+
from sympy.polys.domains.characteristiczero import CharacteristicZero
|
| 6 |
+
from sympy.polys.domains.field import Field
|
| 7 |
+
from sympy.polys.domains.simpledomain import SimpleDomain
|
| 8 |
+
from sympy.polys.polyerrors import CoercionFailed
|
| 9 |
+
from sympy.utilities import public
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
@public
|
| 13 |
+
class ExpressionRawDomain(Field, CharacteristicZero, SimpleDomain):
|
| 14 |
+
"""A class for arbitrary expressions but without automatic simplification. """
|
| 15 |
+
|
| 16 |
+
is_SymbolicRawDomain = is_EXRAW = True
|
| 17 |
+
|
| 18 |
+
dtype = Expr
|
| 19 |
+
|
| 20 |
+
zero = S.Zero
|
| 21 |
+
one = S.One
|
| 22 |
+
|
| 23 |
+
rep = 'EXRAW'
|
| 24 |
+
|
| 25 |
+
has_assoc_Ring = False
|
| 26 |
+
has_assoc_Field = True
|
| 27 |
+
|
| 28 |
+
def __init__(self):
|
| 29 |
+
pass
|
| 30 |
+
|
| 31 |
+
@classmethod
|
| 32 |
+
def new(self, a):
|
| 33 |
+
return sympify(a)
|
| 34 |
+
|
| 35 |
+
def to_sympy(self, a):
|
| 36 |
+
"""Convert ``a`` to a SymPy object. """
|
| 37 |
+
return a
|
| 38 |
+
|
| 39 |
+
def from_sympy(self, a):
|
| 40 |
+
"""Convert SymPy's expression to ``dtype``. """
|
| 41 |
+
if not isinstance(a, Expr):
|
| 42 |
+
raise CoercionFailed(f"Expecting an Expr instance but found: {type(a).__name__}")
|
| 43 |
+
return a
|
| 44 |
+
|
| 45 |
+
def convert_from(self, a, K):
|
| 46 |
+
"""Convert a domain element from another domain to EXRAW"""
|
| 47 |
+
return K.to_sympy(a)
|
| 48 |
+
|
| 49 |
+
def get_field(self):
|
| 50 |
+
"""Returns a field associated with ``self``. """
|
| 51 |
+
return self
|
| 52 |
+
|
| 53 |
+
def sum(self, items):
|
| 54 |
+
return Add(*items)
|
| 55 |
+
|
| 56 |
+
|
| 57 |
+
EXRAW = ExpressionRawDomain()
|
lib/python3.12/site-packages/sympy/polys/domains/field.py
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Implementation of :class:`Field` class. """
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
from sympy.polys.domains.ring import Ring
|
| 5 |
+
from sympy.polys.polyerrors import NotReversible, DomainError
|
| 6 |
+
from sympy.utilities import public
|
| 7 |
+
|
| 8 |
+
@public
|
| 9 |
+
class Field(Ring):
|
| 10 |
+
"""Represents a field domain. """
|
| 11 |
+
|
| 12 |
+
is_Field = True
|
| 13 |
+
is_PID = True
|
| 14 |
+
|
| 15 |
+
def get_ring(self):
|
| 16 |
+
"""Returns a ring associated with ``self``. """
|
| 17 |
+
raise DomainError('there is no ring associated with %s' % self)
|
| 18 |
+
|
| 19 |
+
def get_field(self):
|
| 20 |
+
"""Returns a field associated with ``self``. """
|
| 21 |
+
return self
|
| 22 |
+
|
| 23 |
+
def exquo(self, a, b):
|
| 24 |
+
"""Exact quotient of ``a`` and ``b``, implies ``__truediv__``. """
|
| 25 |
+
return a / b
|
| 26 |
+
|
| 27 |
+
def quo(self, a, b):
|
| 28 |
+
"""Quotient of ``a`` and ``b``, implies ``__truediv__``. """
|
| 29 |
+
return a / b
|
| 30 |
+
|
| 31 |
+
def rem(self, a, b):
|
| 32 |
+
"""Remainder of ``a`` and ``b``, implies nothing. """
|
| 33 |
+
return self.zero
|
| 34 |
+
|
| 35 |
+
def div(self, a, b):
|
| 36 |
+
"""Division of ``a`` and ``b``, implies ``__truediv__``. """
|
| 37 |
+
return a / b, self.zero
|
| 38 |
+
|
| 39 |
+
def gcd(self, a, b):
|
| 40 |
+
"""
|
| 41 |
+
Returns GCD of ``a`` and ``b``.
|
| 42 |
+
|
| 43 |
+
This definition of GCD over fields allows to clear denominators
|
| 44 |
+
in `primitive()`.
|
| 45 |
+
|
| 46 |
+
Examples
|
| 47 |
+
========
|
| 48 |
+
|
| 49 |
+
>>> from sympy.polys.domains import QQ
|
| 50 |
+
>>> from sympy import S, gcd, primitive
|
| 51 |
+
>>> from sympy.abc import x
|
| 52 |
+
|
| 53 |
+
>>> QQ.gcd(QQ(2, 3), QQ(4, 9))
|
| 54 |
+
2/9
|
| 55 |
+
>>> gcd(S(2)/3, S(4)/9)
|
| 56 |
+
2/9
|
| 57 |
+
>>> primitive(2*x/3 + S(4)/9)
|
| 58 |
+
(2/9, 3*x + 2)
|
| 59 |
+
|
| 60 |
+
"""
|
| 61 |
+
try:
|
| 62 |
+
ring = self.get_ring()
|
| 63 |
+
except DomainError:
|
| 64 |
+
return self.one
|
| 65 |
+
|
| 66 |
+
p = ring.gcd(self.numer(a), self.numer(b))
|
| 67 |
+
q = ring.lcm(self.denom(a), self.denom(b))
|
| 68 |
+
|
| 69 |
+
return self.convert(p, ring)/q
|
| 70 |
+
|
| 71 |
+
def lcm(self, a, b):
|
| 72 |
+
"""
|
| 73 |
+
Returns LCM of ``a`` and ``b``.
|
| 74 |
+
|
| 75 |
+
>>> from sympy.polys.domains import QQ
|
| 76 |
+
>>> from sympy import S, lcm
|
| 77 |
+
|
| 78 |
+
>>> QQ.lcm(QQ(2, 3), QQ(4, 9))
|
| 79 |
+
4/3
|
| 80 |
+
>>> lcm(S(2)/3, S(4)/9)
|
| 81 |
+
4/3
|
| 82 |
+
|
| 83 |
+
"""
|
| 84 |
+
|
| 85 |
+
try:
|
| 86 |
+
ring = self.get_ring()
|
| 87 |
+
except DomainError:
|
| 88 |
+
return a*b
|
| 89 |
+
|
| 90 |
+
p = ring.lcm(self.numer(a), self.numer(b))
|
| 91 |
+
q = ring.gcd(self.denom(a), self.denom(b))
|
| 92 |
+
|
| 93 |
+
return self.convert(p, ring)/q
|
| 94 |
+
|
| 95 |
+
def revert(self, a):
|
| 96 |
+
"""Returns ``a**(-1)`` if possible. """
|
| 97 |
+
if a:
|
| 98 |
+
return 1/a
|
| 99 |
+
else:
|
| 100 |
+
raise NotReversible('zero is not reversible')
|
| 101 |
+
|
| 102 |
+
def is_unit(self, a):
|
| 103 |
+
"""Return true if ``a`` is a invertible"""
|
| 104 |
+
return bool(a)
|
lib/python3.12/site-packages/sympy/polys/domains/finitefield.py
ADDED
|
@@ -0,0 +1,328 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Implementation of :class:`FiniteField` class. """
|
| 2 |
+
|
| 3 |
+
import operator
|
| 4 |
+
|
| 5 |
+
from sympy.external.gmpy import GROUND_TYPES
|
| 6 |
+
from sympy.utilities.decorator import doctest_depends_on
|
| 7 |
+
|
| 8 |
+
from sympy.core.numbers import int_valued
|
| 9 |
+
from sympy.polys.domains.field import Field
|
| 10 |
+
|
| 11 |
+
from sympy.polys.domains.modularinteger import ModularIntegerFactory
|
| 12 |
+
from sympy.polys.domains.simpledomain import SimpleDomain
|
| 13 |
+
from sympy.polys.galoistools import gf_zassenhaus, gf_irred_p_rabin
|
| 14 |
+
from sympy.polys.polyerrors import CoercionFailed
|
| 15 |
+
from sympy.utilities import public
|
| 16 |
+
from sympy.polys.domains.groundtypes import SymPyInteger
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
if GROUND_TYPES == 'flint':
|
| 20 |
+
__doctest_skip__ = ['FiniteField']
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
if GROUND_TYPES == 'flint':
|
| 24 |
+
import flint
|
| 25 |
+
# Don't use python-flint < 0.5.0 because nmod was missing some features in
|
| 26 |
+
# previous versions of python-flint and fmpz_mod was not yet added.
|
| 27 |
+
_major, _minor, *_ = flint.__version__.split('.')
|
| 28 |
+
if (int(_major), int(_minor)) < (0, 5):
|
| 29 |
+
flint = None
|
| 30 |
+
else:
|
| 31 |
+
flint = None
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
def _modular_int_factory(mod, dom, symmetric, self):
|
| 35 |
+
|
| 36 |
+
# Use flint if available
|
| 37 |
+
if flint is not None:
|
| 38 |
+
|
| 39 |
+
nmod = flint.nmod
|
| 40 |
+
fmpz_mod_ctx = flint.fmpz_mod_ctx
|
| 41 |
+
index = operator.index
|
| 42 |
+
|
| 43 |
+
try:
|
| 44 |
+
mod = dom.convert(mod)
|
| 45 |
+
except CoercionFailed:
|
| 46 |
+
raise ValueError('modulus must be an integer, got %s' % mod)
|
| 47 |
+
|
| 48 |
+
# mod might be e.g. Integer
|
| 49 |
+
try:
|
| 50 |
+
fmpz_mod_ctx(mod)
|
| 51 |
+
except TypeError:
|
| 52 |
+
mod = index(mod)
|
| 53 |
+
|
| 54 |
+
# flint's nmod is only for moduli up to 2^64-1 (on a 64-bit machine)
|
| 55 |
+
try:
|
| 56 |
+
nmod(0, mod)
|
| 57 |
+
except OverflowError:
|
| 58 |
+
# Use fmpz_mod
|
| 59 |
+
fctx = fmpz_mod_ctx(mod)
|
| 60 |
+
|
| 61 |
+
def ctx(x):
|
| 62 |
+
try:
|
| 63 |
+
return fctx(x)
|
| 64 |
+
except TypeError:
|
| 65 |
+
# x might be Integer
|
| 66 |
+
return fctx(index(x))
|
| 67 |
+
else:
|
| 68 |
+
# Use nmod
|
| 69 |
+
def ctx(x):
|
| 70 |
+
try:
|
| 71 |
+
return nmod(x, mod)
|
| 72 |
+
except TypeError:
|
| 73 |
+
return nmod(index(x), mod)
|
| 74 |
+
|
| 75 |
+
return ctx
|
| 76 |
+
|
| 77 |
+
# Use the Python implementation
|
| 78 |
+
return ModularIntegerFactory(mod, dom, symmetric, self)
|
| 79 |
+
|
| 80 |
+
|
| 81 |
+
@public
|
| 82 |
+
@doctest_depends_on(modules=['python', 'gmpy'])
|
| 83 |
+
class FiniteField(Field, SimpleDomain):
|
| 84 |
+
r"""Finite field of prime order :ref:`GF(p)`
|
| 85 |
+
|
| 86 |
+
A :ref:`GF(p)` domain represents a `finite field`_ `\mathbb{F}_p` of prime
|
| 87 |
+
order as :py:class:`~.Domain` in the domain system (see
|
| 88 |
+
:ref:`polys-domainsintro`).
|
| 89 |
+
|
| 90 |
+
A :py:class:`~.Poly` created from an expression with integer
|
| 91 |
+
coefficients will have the domain :ref:`ZZ`. However, if the ``modulus=p``
|
| 92 |
+
option is given then the domain will be a finite field instead.
|
| 93 |
+
|
| 94 |
+
>>> from sympy import Poly, Symbol
|
| 95 |
+
>>> x = Symbol('x')
|
| 96 |
+
>>> p = Poly(x**2 + 1)
|
| 97 |
+
>>> p
|
| 98 |
+
Poly(x**2 + 1, x, domain='ZZ')
|
| 99 |
+
>>> p.domain
|
| 100 |
+
ZZ
|
| 101 |
+
>>> p2 = Poly(x**2 + 1, modulus=2)
|
| 102 |
+
>>> p2
|
| 103 |
+
Poly(x**2 + 1, x, modulus=2)
|
| 104 |
+
>>> p2.domain
|
| 105 |
+
GF(2)
|
| 106 |
+
|
| 107 |
+
It is possible to factorise a polynomial over :ref:`GF(p)` using the
|
| 108 |
+
modulus argument to :py:func:`~.factor` or by specifying the domain
|
| 109 |
+
explicitly. The domain can also be given as a string.
|
| 110 |
+
|
| 111 |
+
>>> from sympy import factor, GF
|
| 112 |
+
>>> factor(x**2 + 1)
|
| 113 |
+
x**2 + 1
|
| 114 |
+
>>> factor(x**2 + 1, modulus=2)
|
| 115 |
+
(x + 1)**2
|
| 116 |
+
>>> factor(x**2 + 1, domain=GF(2))
|
| 117 |
+
(x + 1)**2
|
| 118 |
+
>>> factor(x**2 + 1, domain='GF(2)')
|
| 119 |
+
(x + 1)**2
|
| 120 |
+
|
| 121 |
+
It is also possible to use :ref:`GF(p)` with the :py:func:`~.cancel`
|
| 122 |
+
and :py:func:`~.gcd` functions.
|
| 123 |
+
|
| 124 |
+
>>> from sympy import cancel, gcd
|
| 125 |
+
>>> cancel((x**2 + 1)/(x + 1))
|
| 126 |
+
(x**2 + 1)/(x + 1)
|
| 127 |
+
>>> cancel((x**2 + 1)/(x + 1), domain=GF(2))
|
| 128 |
+
x + 1
|
| 129 |
+
>>> gcd(x**2 + 1, x + 1)
|
| 130 |
+
1
|
| 131 |
+
>>> gcd(x**2 + 1, x + 1, domain=GF(2))
|
| 132 |
+
x + 1
|
| 133 |
+
|
| 134 |
+
When using the domain directly :ref:`GF(p)` can be used as a constructor
|
| 135 |
+
to create instances which then support the operations ``+,-,*,**,/``
|
| 136 |
+
|
| 137 |
+
>>> from sympy import GF
|
| 138 |
+
>>> K = GF(5)
|
| 139 |
+
>>> K
|
| 140 |
+
GF(5)
|
| 141 |
+
>>> x = K(3)
|
| 142 |
+
>>> y = K(2)
|
| 143 |
+
>>> x
|
| 144 |
+
3 mod 5
|
| 145 |
+
>>> y
|
| 146 |
+
2 mod 5
|
| 147 |
+
>>> x * y
|
| 148 |
+
1 mod 5
|
| 149 |
+
>>> x / y
|
| 150 |
+
4 mod 5
|
| 151 |
+
|
| 152 |
+
Notes
|
| 153 |
+
=====
|
| 154 |
+
|
| 155 |
+
It is also possible to create a :ref:`GF(p)` domain of **non-prime**
|
| 156 |
+
order but the resulting ring is **not** a field: it is just the ring of
|
| 157 |
+
the integers modulo ``n``.
|
| 158 |
+
|
| 159 |
+
>>> K = GF(9)
|
| 160 |
+
>>> z = K(3)
|
| 161 |
+
>>> z
|
| 162 |
+
3 mod 9
|
| 163 |
+
>>> z**2
|
| 164 |
+
0 mod 9
|
| 165 |
+
|
| 166 |
+
It would be good to have a proper implementation of prime power fields
|
| 167 |
+
(``GF(p**n)``) but these are not yet implemented in SymPY.
|
| 168 |
+
|
| 169 |
+
.. _finite field: https://en.wikipedia.org/wiki/Finite_field
|
| 170 |
+
"""
|
| 171 |
+
|
| 172 |
+
rep = 'FF'
|
| 173 |
+
alias = 'FF'
|
| 174 |
+
|
| 175 |
+
is_FiniteField = is_FF = True
|
| 176 |
+
is_Numerical = True
|
| 177 |
+
|
| 178 |
+
has_assoc_Ring = False
|
| 179 |
+
has_assoc_Field = True
|
| 180 |
+
|
| 181 |
+
dom = None
|
| 182 |
+
mod = None
|
| 183 |
+
|
| 184 |
+
def __init__(self, mod, symmetric=True):
|
| 185 |
+
from sympy.polys.domains import ZZ
|
| 186 |
+
dom = ZZ
|
| 187 |
+
|
| 188 |
+
if mod <= 0:
|
| 189 |
+
raise ValueError('modulus must be a positive integer, got %s' % mod)
|
| 190 |
+
|
| 191 |
+
self.dtype = _modular_int_factory(mod, dom, symmetric, self)
|
| 192 |
+
self.zero = self.dtype(0)
|
| 193 |
+
self.one = self.dtype(1)
|
| 194 |
+
self.dom = dom
|
| 195 |
+
self.mod = mod
|
| 196 |
+
self.sym = symmetric
|
| 197 |
+
self._tp = type(self.zero)
|
| 198 |
+
|
| 199 |
+
@property
|
| 200 |
+
def tp(self):
|
| 201 |
+
return self._tp
|
| 202 |
+
|
| 203 |
+
def __str__(self):
|
| 204 |
+
return 'GF(%s)' % self.mod
|
| 205 |
+
|
| 206 |
+
def __hash__(self):
|
| 207 |
+
return hash((self.__class__.__name__, self.dtype, self.mod, self.dom))
|
| 208 |
+
|
| 209 |
+
def __eq__(self, other):
|
| 210 |
+
"""Returns ``True`` if two domains are equivalent. """
|
| 211 |
+
return isinstance(other, FiniteField) and \
|
| 212 |
+
self.mod == other.mod and self.dom == other.dom
|
| 213 |
+
|
| 214 |
+
def characteristic(self):
|
| 215 |
+
"""Return the characteristic of this domain. """
|
| 216 |
+
return self.mod
|
| 217 |
+
|
| 218 |
+
def get_field(self):
|
| 219 |
+
"""Returns a field associated with ``self``. """
|
| 220 |
+
return self
|
| 221 |
+
|
| 222 |
+
def to_sympy(self, a):
|
| 223 |
+
"""Convert ``a`` to a SymPy object. """
|
| 224 |
+
return SymPyInteger(self.to_int(a))
|
| 225 |
+
|
| 226 |
+
def from_sympy(self, a):
|
| 227 |
+
"""Convert SymPy's Integer to SymPy's ``Integer``. """
|
| 228 |
+
if a.is_Integer:
|
| 229 |
+
return self.dtype(self.dom.dtype(int(a)))
|
| 230 |
+
elif int_valued(a):
|
| 231 |
+
return self.dtype(self.dom.dtype(int(a)))
|
| 232 |
+
else:
|
| 233 |
+
raise CoercionFailed("expected an integer, got %s" % a)
|
| 234 |
+
|
| 235 |
+
def to_int(self, a):
|
| 236 |
+
"""Convert ``val`` to a Python ``int`` object. """
|
| 237 |
+
aval = int(a)
|
| 238 |
+
if self.sym and aval > self.mod // 2:
|
| 239 |
+
aval -= self.mod
|
| 240 |
+
return aval
|
| 241 |
+
|
| 242 |
+
def is_positive(self, a):
|
| 243 |
+
"""Returns True if ``a`` is positive. """
|
| 244 |
+
return bool(a)
|
| 245 |
+
|
| 246 |
+
def is_nonnegative(self, a):
|
| 247 |
+
"""Returns True if ``a`` is non-negative. """
|
| 248 |
+
return True
|
| 249 |
+
|
| 250 |
+
def is_negative(self, a):
|
| 251 |
+
"""Returns True if ``a`` is negative. """
|
| 252 |
+
return False
|
| 253 |
+
|
| 254 |
+
def is_nonpositive(self, a):
|
| 255 |
+
"""Returns True if ``a`` is non-positive. """
|
| 256 |
+
return not a
|
| 257 |
+
|
| 258 |
+
def from_FF(K1, a, K0=None):
|
| 259 |
+
"""Convert ``ModularInteger(int)`` to ``dtype``. """
|
| 260 |
+
return K1.dtype(K1.dom.from_ZZ(int(a), K0.dom))
|
| 261 |
+
|
| 262 |
+
def from_FF_python(K1, a, K0=None):
|
| 263 |
+
"""Convert ``ModularInteger(int)`` to ``dtype``. """
|
| 264 |
+
return K1.dtype(K1.dom.from_ZZ_python(int(a), K0.dom))
|
| 265 |
+
|
| 266 |
+
def from_ZZ(K1, a, K0=None):
|
| 267 |
+
"""Convert Python's ``int`` to ``dtype``. """
|
| 268 |
+
return K1.dtype(K1.dom.from_ZZ_python(a, K0))
|
| 269 |
+
|
| 270 |
+
def from_ZZ_python(K1, a, K0=None):
|
| 271 |
+
"""Convert Python's ``int`` to ``dtype``. """
|
| 272 |
+
return K1.dtype(K1.dom.from_ZZ_python(a, K0))
|
| 273 |
+
|
| 274 |
+
def from_QQ(K1, a, K0=None):
|
| 275 |
+
"""Convert Python's ``Fraction`` to ``dtype``. """
|
| 276 |
+
if a.denominator == 1:
|
| 277 |
+
return K1.from_ZZ_python(a.numerator)
|
| 278 |
+
|
| 279 |
+
def from_QQ_python(K1, a, K0=None):
|
| 280 |
+
"""Convert Python's ``Fraction`` to ``dtype``. """
|
| 281 |
+
if a.denominator == 1:
|
| 282 |
+
return K1.from_ZZ_python(a.numerator)
|
| 283 |
+
|
| 284 |
+
def from_FF_gmpy(K1, a, K0=None):
|
| 285 |
+
"""Convert ``ModularInteger(mpz)`` to ``dtype``. """
|
| 286 |
+
return K1.dtype(K1.dom.from_ZZ_gmpy(a.val, K0.dom))
|
| 287 |
+
|
| 288 |
+
def from_ZZ_gmpy(K1, a, K0=None):
|
| 289 |
+
"""Convert GMPY's ``mpz`` to ``dtype``. """
|
| 290 |
+
return K1.dtype(K1.dom.from_ZZ_gmpy(a, K0))
|
| 291 |
+
|
| 292 |
+
def from_QQ_gmpy(K1, a, K0=None):
|
| 293 |
+
"""Convert GMPY's ``mpq`` to ``dtype``. """
|
| 294 |
+
if a.denominator == 1:
|
| 295 |
+
return K1.from_ZZ_gmpy(a.numerator)
|
| 296 |
+
|
| 297 |
+
def from_RealField(K1, a, K0):
|
| 298 |
+
"""Convert mpmath's ``mpf`` to ``dtype``. """
|
| 299 |
+
p, q = K0.to_rational(a)
|
| 300 |
+
|
| 301 |
+
if q == 1:
|
| 302 |
+
return K1.dtype(K1.dom.dtype(p))
|
| 303 |
+
|
| 304 |
+
def is_square(self, a):
|
| 305 |
+
"""Returns True if ``a`` is a quadratic residue modulo p. """
|
| 306 |
+
# a is not a square <=> x**2-a is irreducible
|
| 307 |
+
poly = [int(x) for x in [self.one, self.zero, -a]]
|
| 308 |
+
return not gf_irred_p_rabin(poly, self.mod, self.dom)
|
| 309 |
+
|
| 310 |
+
def exsqrt(self, a):
|
| 311 |
+
"""Square root modulo p of ``a`` if it is a quadratic residue.
|
| 312 |
+
|
| 313 |
+
Explanation
|
| 314 |
+
===========
|
| 315 |
+
Always returns the square root that is no larger than ``p // 2``.
|
| 316 |
+
"""
|
| 317 |
+
# x**2-a is not square-free if a=0 or the field is characteristic 2
|
| 318 |
+
if self.mod == 2 or a == 0:
|
| 319 |
+
return a
|
| 320 |
+
# Otherwise, use square-free factorization routine to factorize x**2-a
|
| 321 |
+
poly = [int(x) for x in [self.one, self.zero, -a]]
|
| 322 |
+
for factor in gf_zassenhaus(poly, self.mod, self.dom):
|
| 323 |
+
if len(factor) == 2 and factor[1] <= self.mod // 2:
|
| 324 |
+
return self.dtype(factor[1])
|
| 325 |
+
return None
|
| 326 |
+
|
| 327 |
+
|
| 328 |
+
FF = GF = FiniteField
|
lib/python3.12/site-packages/sympy/polys/domains/fractionfield.py
ADDED
|
@@ -0,0 +1,177 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Implementation of :class:`FractionField` class. """
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
from sympy.polys.domains.compositedomain import CompositeDomain
|
| 5 |
+
from sympy.polys.domains.field import Field
|
| 6 |
+
from sympy.polys.polyerrors import CoercionFailed, GeneratorsError
|
| 7 |
+
from sympy.utilities import public
|
| 8 |
+
|
| 9 |
+
@public
|
| 10 |
+
class FractionField(Field, CompositeDomain):
|
| 11 |
+
"""A class for representing multivariate rational function fields. """
|
| 12 |
+
|
| 13 |
+
is_FractionField = is_Frac = True
|
| 14 |
+
|
| 15 |
+
has_assoc_Ring = True
|
| 16 |
+
has_assoc_Field = True
|
| 17 |
+
|
| 18 |
+
def __init__(self, domain_or_field, symbols=None, order=None):
|
| 19 |
+
from sympy.polys.fields import FracField
|
| 20 |
+
|
| 21 |
+
if isinstance(domain_or_field, FracField) and symbols is None and order is None:
|
| 22 |
+
field = domain_or_field
|
| 23 |
+
else:
|
| 24 |
+
field = FracField(symbols, domain_or_field, order)
|
| 25 |
+
|
| 26 |
+
self.field = field
|
| 27 |
+
self.dtype = field.dtype
|
| 28 |
+
|
| 29 |
+
self.gens = field.gens
|
| 30 |
+
self.ngens = field.ngens
|
| 31 |
+
self.symbols = field.symbols
|
| 32 |
+
self.domain = field.domain
|
| 33 |
+
|
| 34 |
+
# TODO: remove this
|
| 35 |
+
self.dom = self.domain
|
| 36 |
+
|
| 37 |
+
def new(self, element):
|
| 38 |
+
return self.field.field_new(element)
|
| 39 |
+
|
| 40 |
+
@property
|
| 41 |
+
def zero(self):
|
| 42 |
+
return self.field.zero
|
| 43 |
+
|
| 44 |
+
@property
|
| 45 |
+
def one(self):
|
| 46 |
+
return self.field.one
|
| 47 |
+
|
| 48 |
+
@property
|
| 49 |
+
def order(self):
|
| 50 |
+
return self.field.order
|
| 51 |
+
|
| 52 |
+
def __str__(self):
|
| 53 |
+
return str(self.domain) + '(' + ','.join(map(str, self.symbols)) + ')'
|
| 54 |
+
|
| 55 |
+
def __hash__(self):
|
| 56 |
+
return hash((self.__class__.__name__, self.dtype.field, self.domain, self.symbols))
|
| 57 |
+
|
| 58 |
+
def __eq__(self, other):
|
| 59 |
+
"""Returns ``True`` if two domains are equivalent. """
|
| 60 |
+
return isinstance(other, FractionField) and \
|
| 61 |
+
(self.dtype.field, self.domain, self.symbols) ==\
|
| 62 |
+
(other.dtype.field, other.domain, other.symbols)
|
| 63 |
+
|
| 64 |
+
def to_sympy(self, a):
|
| 65 |
+
"""Convert ``a`` to a SymPy object. """
|
| 66 |
+
return a.as_expr()
|
| 67 |
+
|
| 68 |
+
def from_sympy(self, a):
|
| 69 |
+
"""Convert SymPy's expression to ``dtype``. """
|
| 70 |
+
return self.field.from_expr(a)
|
| 71 |
+
|
| 72 |
+
def from_ZZ(K1, a, K0):
|
| 73 |
+
"""Convert a Python ``int`` object to ``dtype``. """
|
| 74 |
+
return K1(K1.domain.convert(a, K0))
|
| 75 |
+
|
| 76 |
+
def from_ZZ_python(K1, a, K0):
|
| 77 |
+
"""Convert a Python ``int`` object to ``dtype``. """
|
| 78 |
+
return K1(K1.domain.convert(a, K0))
|
| 79 |
+
|
| 80 |
+
def from_QQ(K1, a, K0):
|
| 81 |
+
"""Convert a Python ``Fraction`` object to ``dtype``. """
|
| 82 |
+
dom = K1.domain
|
| 83 |
+
conv = dom.convert_from
|
| 84 |
+
if dom.is_ZZ:
|
| 85 |
+
return K1(conv(K0.numer(a), K0)) / K1(conv(K0.denom(a), K0))
|
| 86 |
+
else:
|
| 87 |
+
return K1(conv(a, K0))
|
| 88 |
+
|
| 89 |
+
def from_QQ_python(K1, a, K0):
|
| 90 |
+
"""Convert a Python ``Fraction`` object to ``dtype``. """
|
| 91 |
+
return K1(K1.domain.convert(a, K0))
|
| 92 |
+
|
| 93 |
+
def from_ZZ_gmpy(K1, a, K0):
|
| 94 |
+
"""Convert a GMPY ``mpz`` object to ``dtype``. """
|
| 95 |
+
return K1(K1.domain.convert(a, K0))
|
| 96 |
+
|
| 97 |
+
def from_QQ_gmpy(K1, a, K0):
|
| 98 |
+
"""Convert a GMPY ``mpq`` object to ``dtype``. """
|
| 99 |
+
return K1(K1.domain.convert(a, K0))
|
| 100 |
+
|
| 101 |
+
def from_GaussianRationalField(K1, a, K0):
|
| 102 |
+
"""Convert a ``GaussianRational`` object to ``dtype``. """
|
| 103 |
+
return K1(K1.domain.convert(a, K0))
|
| 104 |
+
|
| 105 |
+
def from_GaussianIntegerRing(K1, a, K0):
|
| 106 |
+
"""Convert a ``GaussianInteger`` object to ``dtype``. """
|
| 107 |
+
return K1(K1.domain.convert(a, K0))
|
| 108 |
+
|
| 109 |
+
def from_RealField(K1, a, K0):
|
| 110 |
+
"""Convert a mpmath ``mpf`` object to ``dtype``. """
|
| 111 |
+
return K1(K1.domain.convert(a, K0))
|
| 112 |
+
|
| 113 |
+
def from_ComplexField(K1, a, K0):
|
| 114 |
+
"""Convert a mpmath ``mpf`` object to ``dtype``. """
|
| 115 |
+
return K1(K1.domain.convert(a, K0))
|
| 116 |
+
|
| 117 |
+
def from_AlgebraicField(K1, a, K0):
|
| 118 |
+
"""Convert an algebraic number to ``dtype``. """
|
| 119 |
+
if K1.domain != K0:
|
| 120 |
+
a = K1.domain.convert_from(a, K0)
|
| 121 |
+
if a is not None:
|
| 122 |
+
return K1.new(a)
|
| 123 |
+
|
| 124 |
+
def from_PolynomialRing(K1, a, K0):
|
| 125 |
+
"""Convert a polynomial to ``dtype``. """
|
| 126 |
+
if a.is_ground:
|
| 127 |
+
return K1.convert_from(a.coeff(1), K0.domain)
|
| 128 |
+
try:
|
| 129 |
+
return K1.new(a.set_ring(K1.field.ring))
|
| 130 |
+
except (CoercionFailed, GeneratorsError):
|
| 131 |
+
# XXX: We get here if K1=ZZ(x,y) and K0=QQ[x,y]
|
| 132 |
+
# and the poly a in K0 has non-integer coefficients.
|
| 133 |
+
# It seems that K1.new can handle this but K1.new doesn't work
|
| 134 |
+
# when K0.domain is an algebraic field...
|
| 135 |
+
try:
|
| 136 |
+
return K1.new(a)
|
| 137 |
+
except (CoercionFailed, GeneratorsError):
|
| 138 |
+
return None
|
| 139 |
+
|
| 140 |
+
def from_FractionField(K1, a, K0):
|
| 141 |
+
"""Convert a rational function to ``dtype``. """
|
| 142 |
+
try:
|
| 143 |
+
return a.set_field(K1.field)
|
| 144 |
+
except (CoercionFailed, GeneratorsError):
|
| 145 |
+
return None
|
| 146 |
+
|
| 147 |
+
def get_ring(self):
|
| 148 |
+
"""Returns a field associated with ``self``. """
|
| 149 |
+
return self.field.to_ring().to_domain()
|
| 150 |
+
|
| 151 |
+
def is_positive(self, a):
|
| 152 |
+
"""Returns True if ``LC(a)`` is positive. """
|
| 153 |
+
return self.domain.is_positive(a.numer.LC)
|
| 154 |
+
|
| 155 |
+
def is_negative(self, a):
|
| 156 |
+
"""Returns True if ``LC(a)`` is negative. """
|
| 157 |
+
return self.domain.is_negative(a.numer.LC)
|
| 158 |
+
|
| 159 |
+
def is_nonpositive(self, a):
|
| 160 |
+
"""Returns True if ``LC(a)`` is non-positive. """
|
| 161 |
+
return self.domain.is_nonpositive(a.numer.LC)
|
| 162 |
+
|
| 163 |
+
def is_nonnegative(self, a):
|
| 164 |
+
"""Returns True if ``LC(a)`` is non-negative. """
|
| 165 |
+
return self.domain.is_nonnegative(a.numer.LC)
|
| 166 |
+
|
| 167 |
+
def numer(self, a):
|
| 168 |
+
"""Returns numerator of ``a``. """
|
| 169 |
+
return a.numer
|
| 170 |
+
|
| 171 |
+
def denom(self, a):
|
| 172 |
+
"""Returns denominator of ``a``. """
|
| 173 |
+
return a.denom
|
| 174 |
+
|
| 175 |
+
def factorial(self, a):
|
| 176 |
+
"""Returns factorial of ``a``. """
|
| 177 |
+
return self.dtype(self.domain.factorial(a))
|
lib/python3.12/site-packages/sympy/polys/domains/gaussiandomains.py
ADDED
|
@@ -0,0 +1,686 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Domains of Gaussian type."""
|
| 2 |
+
|
| 3 |
+
from sympy.core.numbers import I
|
| 4 |
+
from sympy.polys.polyerrors import CoercionFailed
|
| 5 |
+
from sympy.polys.domains.integerring import ZZ
|
| 6 |
+
from sympy.polys.domains.rationalfield import QQ
|
| 7 |
+
from sympy.polys.domains.algebraicfield import AlgebraicField
|
| 8 |
+
from sympy.polys.domains.domain import Domain
|
| 9 |
+
from sympy.polys.domains.domainelement import DomainElement
|
| 10 |
+
from sympy.polys.domains.field import Field
|
| 11 |
+
from sympy.polys.domains.ring import Ring
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
class GaussianElement(DomainElement):
|
| 15 |
+
"""Base class for elements of Gaussian type domains."""
|
| 16 |
+
base: Domain
|
| 17 |
+
_parent: Domain
|
| 18 |
+
|
| 19 |
+
__slots__ = ('x', 'y')
|
| 20 |
+
|
| 21 |
+
def __new__(cls, x, y=0):
|
| 22 |
+
conv = cls.base.convert
|
| 23 |
+
return cls.new(conv(x), conv(y))
|
| 24 |
+
|
| 25 |
+
@classmethod
|
| 26 |
+
def new(cls, x, y):
|
| 27 |
+
"""Create a new GaussianElement of the same domain."""
|
| 28 |
+
obj = super().__new__(cls)
|
| 29 |
+
obj.x = x
|
| 30 |
+
obj.y = y
|
| 31 |
+
return obj
|
| 32 |
+
|
| 33 |
+
def parent(self):
|
| 34 |
+
"""The domain that this is an element of (ZZ_I or QQ_I)"""
|
| 35 |
+
return self._parent
|
| 36 |
+
|
| 37 |
+
def __hash__(self):
|
| 38 |
+
return hash((self.x, self.y))
|
| 39 |
+
|
| 40 |
+
def __eq__(self, other):
|
| 41 |
+
if isinstance(other, self.__class__):
|
| 42 |
+
return self.x == other.x and self.y == other.y
|
| 43 |
+
else:
|
| 44 |
+
return NotImplemented
|
| 45 |
+
|
| 46 |
+
def __lt__(self, other):
|
| 47 |
+
if not isinstance(other, GaussianElement):
|
| 48 |
+
return NotImplemented
|
| 49 |
+
return [self.y, self.x] < [other.y, other.x]
|
| 50 |
+
|
| 51 |
+
def __pos__(self):
|
| 52 |
+
return self
|
| 53 |
+
|
| 54 |
+
def __neg__(self):
|
| 55 |
+
return self.new(-self.x, -self.y)
|
| 56 |
+
|
| 57 |
+
def __repr__(self):
|
| 58 |
+
return "%s(%s, %s)" % (self._parent.rep, self.x, self.y)
|
| 59 |
+
|
| 60 |
+
def __str__(self):
|
| 61 |
+
return str(self._parent.to_sympy(self))
|
| 62 |
+
|
| 63 |
+
@classmethod
|
| 64 |
+
def _get_xy(cls, other):
|
| 65 |
+
if not isinstance(other, cls):
|
| 66 |
+
try:
|
| 67 |
+
other = cls._parent.convert(other)
|
| 68 |
+
except CoercionFailed:
|
| 69 |
+
return None, None
|
| 70 |
+
return other.x, other.y
|
| 71 |
+
|
| 72 |
+
def __add__(self, other):
|
| 73 |
+
x, y = self._get_xy(other)
|
| 74 |
+
if x is not None:
|
| 75 |
+
return self.new(self.x + x, self.y + y)
|
| 76 |
+
else:
|
| 77 |
+
return NotImplemented
|
| 78 |
+
|
| 79 |
+
__radd__ = __add__
|
| 80 |
+
|
| 81 |
+
def __sub__(self, other):
|
| 82 |
+
x, y = self._get_xy(other)
|
| 83 |
+
if x is not None:
|
| 84 |
+
return self.new(self.x - x, self.y - y)
|
| 85 |
+
else:
|
| 86 |
+
return NotImplemented
|
| 87 |
+
|
| 88 |
+
def __rsub__(self, other):
|
| 89 |
+
x, y = self._get_xy(other)
|
| 90 |
+
if x is not None:
|
| 91 |
+
return self.new(x - self.x, y - self.y)
|
| 92 |
+
else:
|
| 93 |
+
return NotImplemented
|
| 94 |
+
|
| 95 |
+
def __mul__(self, other):
|
| 96 |
+
x, y = self._get_xy(other)
|
| 97 |
+
if x is not None:
|
| 98 |
+
return self.new(self.x*x - self.y*y, self.x*y + self.y*x)
|
| 99 |
+
else:
|
| 100 |
+
return NotImplemented
|
| 101 |
+
|
| 102 |
+
__rmul__ = __mul__
|
| 103 |
+
|
| 104 |
+
def __pow__(self, exp):
|
| 105 |
+
if exp == 0:
|
| 106 |
+
return self.new(1, 0)
|
| 107 |
+
if exp < 0:
|
| 108 |
+
self, exp = 1/self, -exp
|
| 109 |
+
if exp == 1:
|
| 110 |
+
return self
|
| 111 |
+
pow2 = self
|
| 112 |
+
prod = self if exp % 2 else self._parent.one
|
| 113 |
+
exp //= 2
|
| 114 |
+
while exp:
|
| 115 |
+
pow2 *= pow2
|
| 116 |
+
if exp % 2:
|
| 117 |
+
prod *= pow2
|
| 118 |
+
exp //= 2
|
| 119 |
+
return prod
|
| 120 |
+
|
| 121 |
+
def __bool__(self):
|
| 122 |
+
return bool(self.x) or bool(self.y)
|
| 123 |
+
|
| 124 |
+
def quadrant(self):
|
| 125 |
+
"""Return quadrant index 0-3.
|
| 126 |
+
|
| 127 |
+
0 is included in quadrant 0.
|
| 128 |
+
"""
|
| 129 |
+
if self.y > 0:
|
| 130 |
+
return 0 if self.x > 0 else 1
|
| 131 |
+
elif self.y < 0:
|
| 132 |
+
return 2 if self.x < 0 else 3
|
| 133 |
+
else:
|
| 134 |
+
return 0 if self.x >= 0 else 2
|
| 135 |
+
|
| 136 |
+
def __rdivmod__(self, other):
|
| 137 |
+
try:
|
| 138 |
+
other = self._parent.convert(other)
|
| 139 |
+
except CoercionFailed:
|
| 140 |
+
return NotImplemented
|
| 141 |
+
else:
|
| 142 |
+
return other.__divmod__(self)
|
| 143 |
+
|
| 144 |
+
def __rtruediv__(self, other):
|
| 145 |
+
try:
|
| 146 |
+
other = QQ_I.convert(other)
|
| 147 |
+
except CoercionFailed:
|
| 148 |
+
return NotImplemented
|
| 149 |
+
else:
|
| 150 |
+
return other.__truediv__(self)
|
| 151 |
+
|
| 152 |
+
def __floordiv__(self, other):
|
| 153 |
+
qr = self.__divmod__(other)
|
| 154 |
+
return qr if qr is NotImplemented else qr[0]
|
| 155 |
+
|
| 156 |
+
def __rfloordiv__(self, other):
|
| 157 |
+
qr = self.__rdivmod__(other)
|
| 158 |
+
return qr if qr is NotImplemented else qr[0]
|
| 159 |
+
|
| 160 |
+
def __mod__(self, other):
|
| 161 |
+
qr = self.__divmod__(other)
|
| 162 |
+
return qr if qr is NotImplemented else qr[1]
|
| 163 |
+
|
| 164 |
+
def __rmod__(self, other):
|
| 165 |
+
qr = self.__rdivmod__(other)
|
| 166 |
+
return qr if qr is NotImplemented else qr[1]
|
| 167 |
+
|
| 168 |
+
|
| 169 |
+
class GaussianInteger(GaussianElement):
|
| 170 |
+
"""Gaussian integer: domain element for :ref:`ZZ_I`
|
| 171 |
+
|
| 172 |
+
>>> from sympy import ZZ_I
|
| 173 |
+
>>> z = ZZ_I(2, 3)
|
| 174 |
+
>>> z
|
| 175 |
+
(2 + 3*I)
|
| 176 |
+
>>> type(z)
|
| 177 |
+
<class 'sympy.polys.domains.gaussiandomains.GaussianInteger'>
|
| 178 |
+
"""
|
| 179 |
+
base = ZZ
|
| 180 |
+
|
| 181 |
+
def __truediv__(self, other):
|
| 182 |
+
"""Return a Gaussian rational."""
|
| 183 |
+
return QQ_I.convert(self)/other
|
| 184 |
+
|
| 185 |
+
def __divmod__(self, other):
|
| 186 |
+
if not other:
|
| 187 |
+
raise ZeroDivisionError('divmod({}, 0)'.format(self))
|
| 188 |
+
x, y = self._get_xy(other)
|
| 189 |
+
if x is None:
|
| 190 |
+
return NotImplemented
|
| 191 |
+
|
| 192 |
+
# multiply self and other by x - I*y
|
| 193 |
+
# self/other == (a + I*b)/c
|
| 194 |
+
a, b = self.x*x + self.y*y, -self.x*y + self.y*x
|
| 195 |
+
c = x*x + y*y
|
| 196 |
+
|
| 197 |
+
# find integers qx and qy such that
|
| 198 |
+
# |a - qx*c| <= c/2 and |b - qy*c| <= c/2
|
| 199 |
+
qx = (2*a + c) // (2*c) # -c <= 2*a - qx*2*c < c
|
| 200 |
+
qy = (2*b + c) // (2*c)
|
| 201 |
+
|
| 202 |
+
q = GaussianInteger(qx, qy)
|
| 203 |
+
# |self/other - q| < 1 since
|
| 204 |
+
# |a/c - qx|**2 + |b/c - qy|**2 <= 1/4 + 1/4 < 1
|
| 205 |
+
|
| 206 |
+
return q, self - q*other # |r| < |other|
|
| 207 |
+
|
| 208 |
+
|
| 209 |
+
class GaussianRational(GaussianElement):
|
| 210 |
+
"""Gaussian rational: domain element for :ref:`QQ_I`
|
| 211 |
+
|
| 212 |
+
>>> from sympy import QQ_I, QQ
|
| 213 |
+
>>> z = QQ_I(QQ(2, 3), QQ(4, 5))
|
| 214 |
+
>>> z
|
| 215 |
+
(2/3 + 4/5*I)
|
| 216 |
+
>>> type(z)
|
| 217 |
+
<class 'sympy.polys.domains.gaussiandomains.GaussianRational'>
|
| 218 |
+
"""
|
| 219 |
+
base = QQ
|
| 220 |
+
|
| 221 |
+
def __truediv__(self, other):
|
| 222 |
+
"""Return a Gaussian rational."""
|
| 223 |
+
if not other:
|
| 224 |
+
raise ZeroDivisionError('{} / 0'.format(self))
|
| 225 |
+
x, y = self._get_xy(other)
|
| 226 |
+
if x is None:
|
| 227 |
+
return NotImplemented
|
| 228 |
+
c = x*x + y*y
|
| 229 |
+
|
| 230 |
+
return GaussianRational((self.x*x + self.y*y)/c,
|
| 231 |
+
(-self.x*y + self.y*x)/c)
|
| 232 |
+
|
| 233 |
+
def __divmod__(self, other):
|
| 234 |
+
try:
|
| 235 |
+
other = self._parent.convert(other)
|
| 236 |
+
except CoercionFailed:
|
| 237 |
+
return NotImplemented
|
| 238 |
+
if not other:
|
| 239 |
+
raise ZeroDivisionError('{} % 0'.format(self))
|
| 240 |
+
else:
|
| 241 |
+
return self/other, QQ_I.zero
|
| 242 |
+
|
| 243 |
+
|
| 244 |
+
class GaussianDomain():
|
| 245 |
+
"""Base class for Gaussian domains."""
|
| 246 |
+
dom = None # type: Domain
|
| 247 |
+
|
| 248 |
+
is_Numerical = True
|
| 249 |
+
is_Exact = True
|
| 250 |
+
|
| 251 |
+
has_assoc_Ring = True
|
| 252 |
+
has_assoc_Field = True
|
| 253 |
+
|
| 254 |
+
def to_sympy(self, a):
|
| 255 |
+
"""Convert ``a`` to a SymPy object. """
|
| 256 |
+
conv = self.dom.to_sympy
|
| 257 |
+
return conv(a.x) + I*conv(a.y)
|
| 258 |
+
|
| 259 |
+
def from_sympy(self, a):
|
| 260 |
+
"""Convert a SymPy object to ``self.dtype``."""
|
| 261 |
+
r, b = a.as_coeff_Add()
|
| 262 |
+
x = self.dom.from_sympy(r) # may raise CoercionFailed
|
| 263 |
+
if not b:
|
| 264 |
+
return self.new(x, 0)
|
| 265 |
+
r, b = b.as_coeff_Mul()
|
| 266 |
+
y = self.dom.from_sympy(r)
|
| 267 |
+
if b is I:
|
| 268 |
+
return self.new(x, y)
|
| 269 |
+
else:
|
| 270 |
+
raise CoercionFailed("{} is not Gaussian".format(a))
|
| 271 |
+
|
| 272 |
+
def inject(self, *gens):
|
| 273 |
+
"""Inject generators into this domain. """
|
| 274 |
+
return self.poly_ring(*gens)
|
| 275 |
+
|
| 276 |
+
def canonical_unit(self, d):
|
| 277 |
+
unit = self.units[-d.quadrant()] # - for inverse power
|
| 278 |
+
return unit
|
| 279 |
+
|
| 280 |
+
def is_negative(self, element):
|
| 281 |
+
"""Returns ``False`` for any ``GaussianElement``. """
|
| 282 |
+
return False
|
| 283 |
+
|
| 284 |
+
def is_positive(self, element):
|
| 285 |
+
"""Returns ``False`` for any ``GaussianElement``. """
|
| 286 |
+
return False
|
| 287 |
+
|
| 288 |
+
def is_nonnegative(self, element):
|
| 289 |
+
"""Returns ``False`` for any ``GaussianElement``. """
|
| 290 |
+
return False
|
| 291 |
+
|
| 292 |
+
def is_nonpositive(self, element):
|
| 293 |
+
"""Returns ``False`` for any ``GaussianElement``. """
|
| 294 |
+
return False
|
| 295 |
+
|
| 296 |
+
def from_ZZ_gmpy(K1, a, K0):
|
| 297 |
+
"""Convert a GMPY mpz to ``self.dtype``."""
|
| 298 |
+
return K1(a)
|
| 299 |
+
|
| 300 |
+
def from_ZZ(K1, a, K0):
|
| 301 |
+
"""Convert a ZZ_python element to ``self.dtype``."""
|
| 302 |
+
return K1(a)
|
| 303 |
+
|
| 304 |
+
def from_ZZ_python(K1, a, K0):
|
| 305 |
+
"""Convert a ZZ_python element to ``self.dtype``."""
|
| 306 |
+
return K1(a)
|
| 307 |
+
|
| 308 |
+
def from_QQ(K1, a, K0):
|
| 309 |
+
"""Convert a GMPY mpq to ``self.dtype``."""
|
| 310 |
+
return K1(a)
|
| 311 |
+
|
| 312 |
+
def from_QQ_gmpy(K1, a, K0):
|
| 313 |
+
"""Convert a GMPY mpq to ``self.dtype``."""
|
| 314 |
+
return K1(a)
|
| 315 |
+
|
| 316 |
+
def from_QQ_python(K1, a, K0):
|
| 317 |
+
"""Convert a QQ_python element to ``self.dtype``."""
|
| 318 |
+
return K1(a)
|
| 319 |
+
|
| 320 |
+
def from_AlgebraicField(K1, a, K0):
|
| 321 |
+
"""Convert an element from ZZ<I> or QQ<I> to ``self.dtype``."""
|
| 322 |
+
if K0.ext.args[0] == I:
|
| 323 |
+
return K1.from_sympy(K0.to_sympy(a))
|
| 324 |
+
|
| 325 |
+
|
| 326 |
+
class GaussianIntegerRing(GaussianDomain, Ring):
|
| 327 |
+
r"""Ring of Gaussian integers ``ZZ_I``
|
| 328 |
+
|
| 329 |
+
The :ref:`ZZ_I` domain represents the `Gaussian integers`_ `\mathbb{Z}[i]`
|
| 330 |
+
as a :py:class:`~.Domain` in the domain system (see
|
| 331 |
+
:ref:`polys-domainsintro`).
|
| 332 |
+
|
| 333 |
+
By default a :py:class:`~.Poly` created from an expression with
|
| 334 |
+
coefficients that are combinations of integers and ``I`` (`\sqrt{-1}`)
|
| 335 |
+
will have the domain :ref:`ZZ_I`.
|
| 336 |
+
|
| 337 |
+
>>> from sympy import Poly, Symbol, I
|
| 338 |
+
>>> x = Symbol('x')
|
| 339 |
+
>>> p = Poly(x**2 + I)
|
| 340 |
+
>>> p
|
| 341 |
+
Poly(x**2 + I, x, domain='ZZ_I')
|
| 342 |
+
>>> p.domain
|
| 343 |
+
ZZ_I
|
| 344 |
+
|
| 345 |
+
The :ref:`ZZ_I` domain can be used to factorise polynomials that are
|
| 346 |
+
reducible over the Gaussian integers.
|
| 347 |
+
|
| 348 |
+
>>> from sympy import factor
|
| 349 |
+
>>> factor(x**2 + 1)
|
| 350 |
+
x**2 + 1
|
| 351 |
+
>>> factor(x**2 + 1, domain='ZZ_I')
|
| 352 |
+
(x - I)*(x + I)
|
| 353 |
+
|
| 354 |
+
The corresponding `field of fractions`_ is the domain of the Gaussian
|
| 355 |
+
rationals :ref:`QQ_I`. Conversely :ref:`ZZ_I` is the `ring of integers`_
|
| 356 |
+
of :ref:`QQ_I`.
|
| 357 |
+
|
| 358 |
+
>>> from sympy import ZZ_I, QQ_I
|
| 359 |
+
>>> ZZ_I.get_field()
|
| 360 |
+
QQ_I
|
| 361 |
+
>>> QQ_I.get_ring()
|
| 362 |
+
ZZ_I
|
| 363 |
+
|
| 364 |
+
When using the domain directly :ref:`ZZ_I` can be used as a constructor.
|
| 365 |
+
|
| 366 |
+
>>> ZZ_I(3, 4)
|
| 367 |
+
(3 + 4*I)
|
| 368 |
+
>>> ZZ_I(5)
|
| 369 |
+
(5 + 0*I)
|
| 370 |
+
|
| 371 |
+
The domain elements of :ref:`ZZ_I` are instances of
|
| 372 |
+
:py:class:`~.GaussianInteger` which support the rings operations
|
| 373 |
+
``+,-,*,**``.
|
| 374 |
+
|
| 375 |
+
>>> z1 = ZZ_I(5, 1)
|
| 376 |
+
>>> z2 = ZZ_I(2, 3)
|
| 377 |
+
>>> z1
|
| 378 |
+
(5 + 1*I)
|
| 379 |
+
>>> z2
|
| 380 |
+
(2 + 3*I)
|
| 381 |
+
>>> z1 + z2
|
| 382 |
+
(7 + 4*I)
|
| 383 |
+
>>> z1 * z2
|
| 384 |
+
(7 + 17*I)
|
| 385 |
+
>>> z1 ** 2
|
| 386 |
+
(24 + 10*I)
|
| 387 |
+
|
| 388 |
+
Both floor (``//``) and modulo (``%``) division work with
|
| 389 |
+
:py:class:`~.GaussianInteger` (see the :py:meth:`~.Domain.div` method).
|
| 390 |
+
|
| 391 |
+
>>> z3, z4 = ZZ_I(5), ZZ_I(1, 3)
|
| 392 |
+
>>> z3 // z4 # floor division
|
| 393 |
+
(1 + -1*I)
|
| 394 |
+
>>> z3 % z4 # modulo division (remainder)
|
| 395 |
+
(1 + -2*I)
|
| 396 |
+
>>> (z3//z4)*z4 + z3%z4 == z3
|
| 397 |
+
True
|
| 398 |
+
|
| 399 |
+
True division (``/``) in :ref:`ZZ_I` gives an element of :ref:`QQ_I`. The
|
| 400 |
+
:py:meth:`~.Domain.exquo` method can be used to divide in :ref:`ZZ_I` when
|
| 401 |
+
exact division is possible.
|
| 402 |
+
|
| 403 |
+
>>> z1 / z2
|
| 404 |
+
(1 + -1*I)
|
| 405 |
+
>>> ZZ_I.exquo(z1, z2)
|
| 406 |
+
(1 + -1*I)
|
| 407 |
+
>>> z3 / z4
|
| 408 |
+
(1/2 + -3/2*I)
|
| 409 |
+
>>> ZZ_I.exquo(z3, z4)
|
| 410 |
+
Traceback (most recent call last):
|
| 411 |
+
...
|
| 412 |
+
ExactQuotientFailed: (1 + 3*I) does not divide (5 + 0*I) in ZZ_I
|
| 413 |
+
|
| 414 |
+
The :py:meth:`~.Domain.gcd` method can be used to compute the `gcd`_ of any
|
| 415 |
+
two elements.
|
| 416 |
+
|
| 417 |
+
>>> ZZ_I.gcd(ZZ_I(10), ZZ_I(2))
|
| 418 |
+
(2 + 0*I)
|
| 419 |
+
>>> ZZ_I.gcd(ZZ_I(5), ZZ_I(2, 1))
|
| 420 |
+
(2 + 1*I)
|
| 421 |
+
|
| 422 |
+
.. _Gaussian integers: https://en.wikipedia.org/wiki/Gaussian_integer
|
| 423 |
+
.. _gcd: https://en.wikipedia.org/wiki/Greatest_common_divisor
|
| 424 |
+
|
| 425 |
+
"""
|
| 426 |
+
dom = ZZ
|
| 427 |
+
dtype = GaussianInteger
|
| 428 |
+
zero = dtype(ZZ(0), ZZ(0))
|
| 429 |
+
one = dtype(ZZ(1), ZZ(0))
|
| 430 |
+
imag_unit = dtype(ZZ(0), ZZ(1))
|
| 431 |
+
units = (one, imag_unit, -one, -imag_unit) # powers of i
|
| 432 |
+
|
| 433 |
+
rep = 'ZZ_I'
|
| 434 |
+
|
| 435 |
+
is_GaussianRing = True
|
| 436 |
+
is_ZZ_I = True
|
| 437 |
+
|
| 438 |
+
def __init__(self): # override Domain.__init__
|
| 439 |
+
"""For constructing ZZ_I."""
|
| 440 |
+
|
| 441 |
+
def __eq__(self, other):
|
| 442 |
+
"""Returns ``True`` if two domains are equivalent. """
|
| 443 |
+
if isinstance(other, GaussianIntegerRing):
|
| 444 |
+
return True
|
| 445 |
+
else:
|
| 446 |
+
return NotImplemented
|
| 447 |
+
|
| 448 |
+
def __hash__(self):
|
| 449 |
+
"""Compute hash code of ``self``. """
|
| 450 |
+
return hash('ZZ_I')
|
| 451 |
+
|
| 452 |
+
@property
|
| 453 |
+
def has_CharacteristicZero(self):
|
| 454 |
+
return True
|
| 455 |
+
|
| 456 |
+
def characteristic(self):
|
| 457 |
+
return 0
|
| 458 |
+
|
| 459 |
+
def get_ring(self):
|
| 460 |
+
"""Returns a ring associated with ``self``. """
|
| 461 |
+
return self
|
| 462 |
+
|
| 463 |
+
def get_field(self):
|
| 464 |
+
"""Returns a field associated with ``self``. """
|
| 465 |
+
return QQ_I
|
| 466 |
+
|
| 467 |
+
def normalize(self, d, *args):
|
| 468 |
+
"""Return first quadrant element associated with ``d``.
|
| 469 |
+
|
| 470 |
+
Also multiply the other arguments by the same power of i.
|
| 471 |
+
"""
|
| 472 |
+
unit = self.canonical_unit(d)
|
| 473 |
+
d *= unit
|
| 474 |
+
args = tuple(a*unit for a in args)
|
| 475 |
+
return (d,) + args if args else d
|
| 476 |
+
|
| 477 |
+
def gcd(self, a, b):
|
| 478 |
+
"""Greatest common divisor of a and b over ZZ_I."""
|
| 479 |
+
while b:
|
| 480 |
+
a, b = b, a % b
|
| 481 |
+
return self.normalize(a)
|
| 482 |
+
|
| 483 |
+
def lcm(self, a, b):
|
| 484 |
+
"""Least common multiple of a and b over ZZ_I."""
|
| 485 |
+
return (a * b) // self.gcd(a, b)
|
| 486 |
+
|
| 487 |
+
def from_GaussianIntegerRing(K1, a, K0):
|
| 488 |
+
"""Convert a ZZ_I element to ZZ_I."""
|
| 489 |
+
return a
|
| 490 |
+
|
| 491 |
+
def from_GaussianRationalField(K1, a, K0):
|
| 492 |
+
"""Convert a QQ_I element to ZZ_I."""
|
| 493 |
+
return K1.new(ZZ.convert(a.x), ZZ.convert(a.y))
|
| 494 |
+
|
| 495 |
+
ZZ_I = GaussianInteger._parent = GaussianIntegerRing()
|
| 496 |
+
|
| 497 |
+
|
| 498 |
+
class GaussianRationalField(GaussianDomain, Field):
|
| 499 |
+
r"""Field of Gaussian rationals ``QQ_I``
|
| 500 |
+
|
| 501 |
+
The :ref:`QQ_I` domain represents the `Gaussian rationals`_ `\mathbb{Q}(i)`
|
| 502 |
+
as a :py:class:`~.Domain` in the domain system (see
|
| 503 |
+
:ref:`polys-domainsintro`).
|
| 504 |
+
|
| 505 |
+
By default a :py:class:`~.Poly` created from an expression with
|
| 506 |
+
coefficients that are combinations of rationals and ``I`` (`\sqrt{-1}`)
|
| 507 |
+
will have the domain :ref:`QQ_I`.
|
| 508 |
+
|
| 509 |
+
>>> from sympy import Poly, Symbol, I
|
| 510 |
+
>>> x = Symbol('x')
|
| 511 |
+
>>> p = Poly(x**2 + I/2)
|
| 512 |
+
>>> p
|
| 513 |
+
Poly(x**2 + I/2, x, domain='QQ_I')
|
| 514 |
+
>>> p.domain
|
| 515 |
+
QQ_I
|
| 516 |
+
|
| 517 |
+
The polys option ``gaussian=True`` can be used to specify that the domain
|
| 518 |
+
should be :ref:`QQ_I` even if the coefficients do not contain ``I`` or are
|
| 519 |
+
all integers.
|
| 520 |
+
|
| 521 |
+
>>> Poly(x**2)
|
| 522 |
+
Poly(x**2, x, domain='ZZ')
|
| 523 |
+
>>> Poly(x**2 + I)
|
| 524 |
+
Poly(x**2 + I, x, domain='ZZ_I')
|
| 525 |
+
>>> Poly(x**2/2)
|
| 526 |
+
Poly(1/2*x**2, x, domain='QQ')
|
| 527 |
+
>>> Poly(x**2, gaussian=True)
|
| 528 |
+
Poly(x**2, x, domain='QQ_I')
|
| 529 |
+
>>> Poly(x**2 + I, gaussian=True)
|
| 530 |
+
Poly(x**2 + I, x, domain='QQ_I')
|
| 531 |
+
>>> Poly(x**2/2, gaussian=True)
|
| 532 |
+
Poly(1/2*x**2, x, domain='QQ_I')
|
| 533 |
+
|
| 534 |
+
The :ref:`QQ_I` domain can be used to factorise polynomials that are
|
| 535 |
+
reducible over the Gaussian rationals.
|
| 536 |
+
|
| 537 |
+
>>> from sympy import factor, QQ_I
|
| 538 |
+
>>> factor(x**2/4 + 1)
|
| 539 |
+
(x**2 + 4)/4
|
| 540 |
+
>>> factor(x**2/4 + 1, domain='QQ_I')
|
| 541 |
+
(x - 2*I)*(x + 2*I)/4
|
| 542 |
+
>>> factor(x**2/4 + 1, domain=QQ_I)
|
| 543 |
+
(x - 2*I)*(x + 2*I)/4
|
| 544 |
+
|
| 545 |
+
It is also possible to specify the :ref:`QQ_I` domain explicitly with
|
| 546 |
+
polys functions like :py:func:`~.apart`.
|
| 547 |
+
|
| 548 |
+
>>> from sympy import apart
|
| 549 |
+
>>> apart(1/(1 + x**2))
|
| 550 |
+
1/(x**2 + 1)
|
| 551 |
+
>>> apart(1/(1 + x**2), domain=QQ_I)
|
| 552 |
+
I/(2*(x + I)) - I/(2*(x - I))
|
| 553 |
+
|
| 554 |
+
The corresponding `ring of integers`_ is the domain of the Gaussian
|
| 555 |
+
integers :ref:`ZZ_I`. Conversely :ref:`QQ_I` is the `field of fractions`_
|
| 556 |
+
of :ref:`ZZ_I`.
|
| 557 |
+
|
| 558 |
+
>>> from sympy import ZZ_I, QQ_I, QQ
|
| 559 |
+
>>> ZZ_I.get_field()
|
| 560 |
+
QQ_I
|
| 561 |
+
>>> QQ_I.get_ring()
|
| 562 |
+
ZZ_I
|
| 563 |
+
|
| 564 |
+
When using the domain directly :ref:`QQ_I` can be used as a constructor.
|
| 565 |
+
|
| 566 |
+
>>> QQ_I(3, 4)
|
| 567 |
+
(3 + 4*I)
|
| 568 |
+
>>> QQ_I(5)
|
| 569 |
+
(5 + 0*I)
|
| 570 |
+
>>> QQ_I(QQ(2, 3), QQ(4, 5))
|
| 571 |
+
(2/3 + 4/5*I)
|
| 572 |
+
|
| 573 |
+
The domain elements of :ref:`QQ_I` are instances of
|
| 574 |
+
:py:class:`~.GaussianRational` which support the field operations
|
| 575 |
+
``+,-,*,**,/``.
|
| 576 |
+
|
| 577 |
+
>>> z1 = QQ_I(5, 1)
|
| 578 |
+
>>> z2 = QQ_I(2, QQ(1, 2))
|
| 579 |
+
>>> z1
|
| 580 |
+
(5 + 1*I)
|
| 581 |
+
>>> z2
|
| 582 |
+
(2 + 1/2*I)
|
| 583 |
+
>>> z1 + z2
|
| 584 |
+
(7 + 3/2*I)
|
| 585 |
+
>>> z1 * z2
|
| 586 |
+
(19/2 + 9/2*I)
|
| 587 |
+
>>> z2 ** 2
|
| 588 |
+
(15/4 + 2*I)
|
| 589 |
+
|
| 590 |
+
True division (``/``) in :ref:`QQ_I` gives an element of :ref:`QQ_I` and
|
| 591 |
+
is always exact.
|
| 592 |
+
|
| 593 |
+
>>> z1 / z2
|
| 594 |
+
(42/17 + -2/17*I)
|
| 595 |
+
>>> QQ_I.exquo(z1, z2)
|
| 596 |
+
(42/17 + -2/17*I)
|
| 597 |
+
>>> z1 == (z1/z2)*z2
|
| 598 |
+
True
|
| 599 |
+
|
| 600 |
+
Both floor (``//``) and modulo (``%``) division can be used with
|
| 601 |
+
:py:class:`~.GaussianRational` (see :py:meth:`~.Domain.div`)
|
| 602 |
+
but division is always exact so there is no remainder.
|
| 603 |
+
|
| 604 |
+
>>> z1 // z2
|
| 605 |
+
(42/17 + -2/17*I)
|
| 606 |
+
>>> z1 % z2
|
| 607 |
+
(0 + 0*I)
|
| 608 |
+
>>> QQ_I.div(z1, z2)
|
| 609 |
+
((42/17 + -2/17*I), (0 + 0*I))
|
| 610 |
+
>>> (z1//z2)*z2 + z1%z2 == z1
|
| 611 |
+
True
|
| 612 |
+
|
| 613 |
+
.. _Gaussian rationals: https://en.wikipedia.org/wiki/Gaussian_rational
|
| 614 |
+
"""
|
| 615 |
+
dom = QQ
|
| 616 |
+
dtype = GaussianRational
|
| 617 |
+
zero = dtype(QQ(0), QQ(0))
|
| 618 |
+
one = dtype(QQ(1), QQ(0))
|
| 619 |
+
imag_unit = dtype(QQ(0), QQ(1))
|
| 620 |
+
units = (one, imag_unit, -one, -imag_unit) # powers of i
|
| 621 |
+
|
| 622 |
+
rep = 'QQ_I'
|
| 623 |
+
|
| 624 |
+
is_GaussianField = True
|
| 625 |
+
is_QQ_I = True
|
| 626 |
+
|
| 627 |
+
def __init__(self): # override Domain.__init__
|
| 628 |
+
"""For constructing QQ_I."""
|
| 629 |
+
|
| 630 |
+
def __eq__(self, other):
|
| 631 |
+
"""Returns ``True`` if two domains are equivalent. """
|
| 632 |
+
if isinstance(other, GaussianRationalField):
|
| 633 |
+
return True
|
| 634 |
+
else:
|
| 635 |
+
return NotImplemented
|
| 636 |
+
|
| 637 |
+
def __hash__(self):
|
| 638 |
+
"""Compute hash code of ``self``. """
|
| 639 |
+
return hash('QQ_I')
|
| 640 |
+
|
| 641 |
+
@property
|
| 642 |
+
def has_CharacteristicZero(self):
|
| 643 |
+
return True
|
| 644 |
+
|
| 645 |
+
def characteristic(self):
|
| 646 |
+
return 0
|
| 647 |
+
|
| 648 |
+
def get_ring(self):
|
| 649 |
+
"""Returns a ring associated with ``self``. """
|
| 650 |
+
return ZZ_I
|
| 651 |
+
|
| 652 |
+
def get_field(self):
|
| 653 |
+
"""Returns a field associated with ``self``. """
|
| 654 |
+
return self
|
| 655 |
+
|
| 656 |
+
def as_AlgebraicField(self):
|
| 657 |
+
"""Get equivalent domain as an ``AlgebraicField``. """
|
| 658 |
+
return AlgebraicField(self.dom, I)
|
| 659 |
+
|
| 660 |
+
def numer(self, a):
|
| 661 |
+
"""Get the numerator of ``a``."""
|
| 662 |
+
ZZ_I = self.get_ring()
|
| 663 |
+
return ZZ_I.convert(a * self.denom(a))
|
| 664 |
+
|
| 665 |
+
def denom(self, a):
|
| 666 |
+
"""Get the denominator of ``a``."""
|
| 667 |
+
ZZ = self.dom.get_ring()
|
| 668 |
+
QQ = self.dom
|
| 669 |
+
ZZ_I = self.get_ring()
|
| 670 |
+
denom_ZZ = ZZ.lcm(QQ.denom(a.x), QQ.denom(a.y))
|
| 671 |
+
return ZZ_I(denom_ZZ, ZZ.zero)
|
| 672 |
+
|
| 673 |
+
def from_GaussianIntegerRing(K1, a, K0):
|
| 674 |
+
"""Convert a ZZ_I element to QQ_I."""
|
| 675 |
+
return K1.new(a.x, a.y)
|
| 676 |
+
|
| 677 |
+
def from_GaussianRationalField(K1, a, K0):
|
| 678 |
+
"""Convert a QQ_I element to QQ_I."""
|
| 679 |
+
return a
|
| 680 |
+
|
| 681 |
+
def from_ComplexField(K1, a, K0):
|
| 682 |
+
"""Convert a ComplexField element to QQ_I."""
|
| 683 |
+
return K1.new(QQ.convert(a.real), QQ.convert(a.imag))
|
| 684 |
+
|
| 685 |
+
|
| 686 |
+
QQ_I = GaussianRational._parent = GaussianRationalField()
|
lib/python3.12/site-packages/sympy/polys/domains/gmpyfinitefield.py
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Implementation of :class:`GMPYFiniteField` class. """
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
from sympy.polys.domains.finitefield import FiniteField
|
| 5 |
+
from sympy.polys.domains.gmpyintegerring import GMPYIntegerRing
|
| 6 |
+
|
| 7 |
+
from sympy.utilities import public
|
| 8 |
+
|
| 9 |
+
@public
|
| 10 |
+
class GMPYFiniteField(FiniteField):
|
| 11 |
+
"""Finite field based on GMPY integers. """
|
| 12 |
+
|
| 13 |
+
alias = 'FF_gmpy'
|
| 14 |
+
|
| 15 |
+
def __init__(self, mod, symmetric=True):
|
| 16 |
+
super().__init__(mod, GMPYIntegerRing(), symmetric)
|
lib/python3.12/site-packages/sympy/polys/domains/gmpyintegerring.py
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Implementation of :class:`GMPYIntegerRing` class. """
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
from sympy.polys.domains.groundtypes import (
|
| 5 |
+
GMPYInteger, SymPyInteger,
|
| 6 |
+
factorial as gmpy_factorial,
|
| 7 |
+
gmpy_gcdex, gmpy_gcd, gmpy_lcm, sqrt as gmpy_sqrt,
|
| 8 |
+
)
|
| 9 |
+
from sympy.core.numbers import int_valued
|
| 10 |
+
from sympy.polys.domains.integerring import IntegerRing
|
| 11 |
+
from sympy.polys.polyerrors import CoercionFailed
|
| 12 |
+
from sympy.utilities import public
|
| 13 |
+
|
| 14 |
+
@public
|
| 15 |
+
class GMPYIntegerRing(IntegerRing):
|
| 16 |
+
"""Integer ring based on GMPY's ``mpz`` type.
|
| 17 |
+
|
| 18 |
+
This will be the implementation of :ref:`ZZ` if ``gmpy`` or ``gmpy2`` is
|
| 19 |
+
installed. Elements will be of type ``gmpy.mpz``.
|
| 20 |
+
"""
|
| 21 |
+
|
| 22 |
+
dtype = GMPYInteger
|
| 23 |
+
zero = dtype(0)
|
| 24 |
+
one = dtype(1)
|
| 25 |
+
tp = type(one)
|
| 26 |
+
alias = 'ZZ_gmpy'
|
| 27 |
+
|
| 28 |
+
def __init__(self):
|
| 29 |
+
"""Allow instantiation of this domain. """
|
| 30 |
+
|
| 31 |
+
def to_sympy(self, a):
|
| 32 |
+
"""Convert ``a`` to a SymPy object. """
|
| 33 |
+
return SymPyInteger(int(a))
|
| 34 |
+
|
| 35 |
+
def from_sympy(self, a):
|
| 36 |
+
"""Convert SymPy's Integer to ``dtype``. """
|
| 37 |
+
if a.is_Integer:
|
| 38 |
+
return GMPYInteger(a.p)
|
| 39 |
+
elif int_valued(a):
|
| 40 |
+
return GMPYInteger(int(a))
|
| 41 |
+
else:
|
| 42 |
+
raise CoercionFailed("expected an integer, got %s" % a)
|
| 43 |
+
|
| 44 |
+
def from_FF_python(K1, a, K0):
|
| 45 |
+
"""Convert ``ModularInteger(int)`` to GMPY's ``mpz``. """
|
| 46 |
+
return K0.to_int(a)
|
| 47 |
+
|
| 48 |
+
def from_ZZ_python(K1, a, K0):
|
| 49 |
+
"""Convert Python's ``int`` to GMPY's ``mpz``. """
|
| 50 |
+
return GMPYInteger(a)
|
| 51 |
+
|
| 52 |
+
def from_QQ(K1, a, K0):
|
| 53 |
+
"""Convert Python's ``Fraction`` to GMPY's ``mpz``. """
|
| 54 |
+
if a.denominator == 1:
|
| 55 |
+
return GMPYInteger(a.numerator)
|
| 56 |
+
|
| 57 |
+
def from_QQ_python(K1, a, K0):
|
| 58 |
+
"""Convert Python's ``Fraction`` to GMPY's ``mpz``. """
|
| 59 |
+
if a.denominator == 1:
|
| 60 |
+
return GMPYInteger(a.numerator)
|
| 61 |
+
|
| 62 |
+
def from_FF_gmpy(K1, a, K0):
|
| 63 |
+
"""Convert ``ModularInteger(mpz)`` to GMPY's ``mpz``. """
|
| 64 |
+
return K0.to_int(a)
|
| 65 |
+
|
| 66 |
+
def from_ZZ_gmpy(K1, a, K0):
|
| 67 |
+
"""Convert GMPY's ``mpz`` to GMPY's ``mpz``. """
|
| 68 |
+
return a
|
| 69 |
+
|
| 70 |
+
def from_QQ_gmpy(K1, a, K0):
|
| 71 |
+
"""Convert GMPY ``mpq`` to GMPY's ``mpz``. """
|
| 72 |
+
if a.denominator == 1:
|
| 73 |
+
return a.numerator
|
| 74 |
+
|
| 75 |
+
def from_RealField(K1, a, K0):
|
| 76 |
+
"""Convert mpmath's ``mpf`` to GMPY's ``mpz``. """
|
| 77 |
+
p, q = K0.to_rational(a)
|
| 78 |
+
|
| 79 |
+
if q == 1:
|
| 80 |
+
return GMPYInteger(p)
|
| 81 |
+
|
| 82 |
+
def from_GaussianIntegerRing(K1, a, K0):
|
| 83 |
+
if a.y == 0:
|
| 84 |
+
return a.x
|
| 85 |
+
|
| 86 |
+
def gcdex(self, a, b):
|
| 87 |
+
"""Compute extended GCD of ``a`` and ``b``. """
|
| 88 |
+
h, s, t = gmpy_gcdex(a, b)
|
| 89 |
+
return s, t, h
|
| 90 |
+
|
| 91 |
+
def gcd(self, a, b):
|
| 92 |
+
"""Compute GCD of ``a`` and ``b``. """
|
| 93 |
+
return gmpy_gcd(a, b)
|
| 94 |
+
|
| 95 |
+
def lcm(self, a, b):
|
| 96 |
+
"""Compute LCM of ``a`` and ``b``. """
|
| 97 |
+
return gmpy_lcm(a, b)
|
| 98 |
+
|
| 99 |
+
def sqrt(self, a):
|
| 100 |
+
"""Compute square root of ``a``. """
|
| 101 |
+
return gmpy_sqrt(a)
|
| 102 |
+
|
| 103 |
+
def factorial(self, a):
|
| 104 |
+
"""Compute factorial of ``a``. """
|
| 105 |
+
return gmpy_factorial(a)
|
lib/python3.12/site-packages/sympy/polys/domains/gmpyrationalfield.py
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Implementation of :class:`GMPYRationalField` class. """
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
from sympy.polys.domains.groundtypes import (
|
| 5 |
+
GMPYRational, SymPyRational,
|
| 6 |
+
gmpy_numer, gmpy_denom, factorial as gmpy_factorial,
|
| 7 |
+
)
|
| 8 |
+
from sympy.polys.domains.rationalfield import RationalField
|
| 9 |
+
from sympy.polys.polyerrors import CoercionFailed
|
| 10 |
+
from sympy.utilities import public
|
| 11 |
+
|
| 12 |
+
@public
|
| 13 |
+
class GMPYRationalField(RationalField):
|
| 14 |
+
"""Rational field based on GMPY's ``mpq`` type.
|
| 15 |
+
|
| 16 |
+
This will be the implementation of :ref:`QQ` if ``gmpy`` or ``gmpy2`` is
|
| 17 |
+
installed. Elements will be of type ``gmpy.mpq``.
|
| 18 |
+
"""
|
| 19 |
+
|
| 20 |
+
dtype = GMPYRational
|
| 21 |
+
zero = dtype(0)
|
| 22 |
+
one = dtype(1)
|
| 23 |
+
tp = type(one)
|
| 24 |
+
alias = 'QQ_gmpy'
|
| 25 |
+
|
| 26 |
+
def __init__(self):
|
| 27 |
+
pass
|
| 28 |
+
|
| 29 |
+
def get_ring(self):
|
| 30 |
+
"""Returns ring associated with ``self``. """
|
| 31 |
+
from sympy.polys.domains import GMPYIntegerRing
|
| 32 |
+
return GMPYIntegerRing()
|
| 33 |
+
|
| 34 |
+
def to_sympy(self, a):
|
| 35 |
+
"""Convert ``a`` to a SymPy object. """
|
| 36 |
+
return SymPyRational(int(gmpy_numer(a)),
|
| 37 |
+
int(gmpy_denom(a)))
|
| 38 |
+
|
| 39 |
+
def from_sympy(self, a):
|
| 40 |
+
"""Convert SymPy's Integer to ``dtype``. """
|
| 41 |
+
if a.is_Rational:
|
| 42 |
+
return GMPYRational(a.p, a.q)
|
| 43 |
+
elif a.is_Float:
|
| 44 |
+
from sympy.polys.domains import RR
|
| 45 |
+
return GMPYRational(*map(int, RR.to_rational(a)))
|
| 46 |
+
else:
|
| 47 |
+
raise CoercionFailed("expected ``Rational`` object, got %s" % a)
|
| 48 |
+
|
| 49 |
+
def from_ZZ_python(K1, a, K0):
|
| 50 |
+
"""Convert a Python ``int`` object to ``dtype``. """
|
| 51 |
+
return GMPYRational(a)
|
| 52 |
+
|
| 53 |
+
def from_QQ_python(K1, a, K0):
|
| 54 |
+
"""Convert a Python ``Fraction`` object to ``dtype``. """
|
| 55 |
+
return GMPYRational(a.numerator, a.denominator)
|
| 56 |
+
|
| 57 |
+
def from_ZZ_gmpy(K1, a, K0):
|
| 58 |
+
"""Convert a GMPY ``mpz`` object to ``dtype``. """
|
| 59 |
+
return GMPYRational(a)
|
| 60 |
+
|
| 61 |
+
def from_QQ_gmpy(K1, a, K0):
|
| 62 |
+
"""Convert a GMPY ``mpq`` object to ``dtype``. """
|
| 63 |
+
return a
|
| 64 |
+
|
| 65 |
+
def from_GaussianRationalField(K1, a, K0):
|
| 66 |
+
"""Convert a ``GaussianElement`` object to ``dtype``. """
|
| 67 |
+
if a.y == 0:
|
| 68 |
+
return GMPYRational(a.x)
|
| 69 |
+
|
| 70 |
+
def from_RealField(K1, a, K0):
|
| 71 |
+
"""Convert a mpmath ``mpf`` object to ``dtype``. """
|
| 72 |
+
return GMPYRational(*map(int, K0.to_rational(a)))
|
| 73 |
+
|
| 74 |
+
def exquo(self, a, b):
|
| 75 |
+
"""Exact quotient of ``a`` and ``b``, implies ``__truediv__``. """
|
| 76 |
+
return GMPYRational(a) / GMPYRational(b)
|
| 77 |
+
|
| 78 |
+
def quo(self, a, b):
|
| 79 |
+
"""Quotient of ``a`` and ``b``, implies ``__truediv__``. """
|
| 80 |
+
return GMPYRational(a) / GMPYRational(b)
|
| 81 |
+
|
| 82 |
+
def rem(self, a, b):
|
| 83 |
+
"""Remainder of ``a`` and ``b``, implies nothing. """
|
| 84 |
+
return self.zero
|
| 85 |
+
|
| 86 |
+
def div(self, a, b):
|
| 87 |
+
"""Division of ``a`` and ``b``, implies ``__truediv__``. """
|
| 88 |
+
return GMPYRational(a) / GMPYRational(b), self.zero
|
| 89 |
+
|
| 90 |
+
def numer(self, a):
|
| 91 |
+
"""Returns numerator of ``a``. """
|
| 92 |
+
return a.numerator
|
| 93 |
+
|
| 94 |
+
def denom(self, a):
|
| 95 |
+
"""Returns denominator of ``a``. """
|
| 96 |
+
return a.denominator
|
| 97 |
+
|
| 98 |
+
def factorial(self, a):
|
| 99 |
+
"""Returns factorial of ``a``. """
|
| 100 |
+
return GMPYRational(gmpy_factorial(int(a)))
|
lib/python3.12/site-packages/sympy/polys/domains/groundtypes.py
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Ground types for various mathematical domains in SymPy. """
|
| 2 |
+
|
| 3 |
+
import builtins
|
| 4 |
+
from sympy.external.gmpy import GROUND_TYPES, factorial, sqrt, is_square, sqrtrem
|
| 5 |
+
|
| 6 |
+
PythonInteger = builtins.int
|
| 7 |
+
PythonReal = builtins.float
|
| 8 |
+
PythonComplex = builtins.complex
|
| 9 |
+
|
| 10 |
+
from .pythonrational import PythonRational
|
| 11 |
+
|
| 12 |
+
from sympy.core.intfunc import (
|
| 13 |
+
igcdex as python_gcdex,
|
| 14 |
+
igcd2 as python_gcd,
|
| 15 |
+
ilcm as python_lcm,
|
| 16 |
+
)
|
| 17 |
+
|
| 18 |
+
from sympy.core.numbers import (Float as SymPyReal, Integer as SymPyInteger, Rational as SymPyRational)
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
class _GMPYInteger:
|
| 22 |
+
def __init__(self, obj):
|
| 23 |
+
pass
|
| 24 |
+
|
| 25 |
+
class _GMPYRational:
|
| 26 |
+
def __init__(self, obj):
|
| 27 |
+
pass
|
| 28 |
+
|
| 29 |
+
|
| 30 |
+
if GROUND_TYPES == 'gmpy':
|
| 31 |
+
|
| 32 |
+
from gmpy2 import (
|
| 33 |
+
mpz as GMPYInteger,
|
| 34 |
+
mpq as GMPYRational,
|
| 35 |
+
numer as gmpy_numer,
|
| 36 |
+
denom as gmpy_denom,
|
| 37 |
+
gcdext as gmpy_gcdex,
|
| 38 |
+
gcd as gmpy_gcd,
|
| 39 |
+
lcm as gmpy_lcm,
|
| 40 |
+
qdiv as gmpy_qdiv,
|
| 41 |
+
)
|
| 42 |
+
gcdex = gmpy_gcdex
|
| 43 |
+
gcd = gmpy_gcd
|
| 44 |
+
lcm = gmpy_lcm
|
| 45 |
+
|
| 46 |
+
elif GROUND_TYPES == 'flint':
|
| 47 |
+
|
| 48 |
+
from flint import fmpz as _fmpz
|
| 49 |
+
|
| 50 |
+
GMPYInteger = _GMPYInteger
|
| 51 |
+
GMPYRational = _GMPYRational
|
| 52 |
+
gmpy_numer = None
|
| 53 |
+
gmpy_denom = None
|
| 54 |
+
gmpy_gcdex = None
|
| 55 |
+
gmpy_gcd = None
|
| 56 |
+
gmpy_lcm = None
|
| 57 |
+
gmpy_qdiv = None
|
| 58 |
+
|
| 59 |
+
def gcd(a, b):
|
| 60 |
+
return a.gcd(b)
|
| 61 |
+
|
| 62 |
+
def gcdex(a, b):
|
| 63 |
+
x, y, g = python_gcdex(a, b)
|
| 64 |
+
return _fmpz(x), _fmpz(y), _fmpz(g)
|
| 65 |
+
|
| 66 |
+
def lcm(a, b):
|
| 67 |
+
return a.lcm(b)
|
| 68 |
+
|
| 69 |
+
else:
|
| 70 |
+
GMPYInteger = _GMPYInteger
|
| 71 |
+
GMPYRational = _GMPYRational
|
| 72 |
+
gmpy_numer = None
|
| 73 |
+
gmpy_denom = None
|
| 74 |
+
gmpy_gcdex = None
|
| 75 |
+
gmpy_gcd = None
|
| 76 |
+
gmpy_lcm = None
|
| 77 |
+
gmpy_qdiv = None
|
| 78 |
+
gcdex = python_gcdex
|
| 79 |
+
gcd = python_gcd
|
| 80 |
+
lcm = python_lcm
|
| 81 |
+
|
| 82 |
+
|
| 83 |
+
__all__ = [
|
| 84 |
+
'PythonInteger', 'PythonReal', 'PythonComplex',
|
| 85 |
+
|
| 86 |
+
'PythonRational',
|
| 87 |
+
|
| 88 |
+
'python_gcdex', 'python_gcd', 'python_lcm',
|
| 89 |
+
|
| 90 |
+
'SymPyReal', 'SymPyInteger', 'SymPyRational',
|
| 91 |
+
|
| 92 |
+
'GMPYInteger', 'GMPYRational', 'gmpy_numer',
|
| 93 |
+
'gmpy_denom', 'gmpy_gcdex', 'gmpy_gcd', 'gmpy_lcm',
|
| 94 |
+
'gmpy_qdiv',
|
| 95 |
+
|
| 96 |
+
'factorial', 'sqrt', 'is_square', 'sqrtrem',
|
| 97 |
+
|
| 98 |
+
'GMPYInteger', 'GMPYRational',
|
| 99 |
+
]
|
lib/python3.12/site-packages/sympy/polys/domains/integerring.py
ADDED
|
@@ -0,0 +1,276 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Implementation of :class:`IntegerRing` class. """
|
| 2 |
+
|
| 3 |
+
from sympy.external.gmpy import MPZ, GROUND_TYPES
|
| 4 |
+
|
| 5 |
+
from sympy.core.numbers import int_valued
|
| 6 |
+
from sympy.polys.domains.groundtypes import (
|
| 7 |
+
SymPyInteger,
|
| 8 |
+
factorial,
|
| 9 |
+
gcdex, gcd, lcm, sqrt, is_square, sqrtrem,
|
| 10 |
+
)
|
| 11 |
+
|
| 12 |
+
from sympy.polys.domains.characteristiczero import CharacteristicZero
|
| 13 |
+
from sympy.polys.domains.ring import Ring
|
| 14 |
+
from sympy.polys.domains.simpledomain import SimpleDomain
|
| 15 |
+
from sympy.polys.polyerrors import CoercionFailed
|
| 16 |
+
from sympy.utilities import public
|
| 17 |
+
|
| 18 |
+
import math
|
| 19 |
+
|
| 20 |
+
@public
|
| 21 |
+
class IntegerRing(Ring, CharacteristicZero, SimpleDomain):
|
| 22 |
+
r"""The domain ``ZZ`` representing the integers `\mathbb{Z}`.
|
| 23 |
+
|
| 24 |
+
The :py:class:`IntegerRing` class represents the ring of integers as a
|
| 25 |
+
:py:class:`~.Domain` in the domain system. :py:class:`IntegerRing` is a
|
| 26 |
+
super class of :py:class:`PythonIntegerRing` and
|
| 27 |
+
:py:class:`GMPYIntegerRing` one of which will be the implementation for
|
| 28 |
+
:ref:`ZZ` depending on whether or not ``gmpy`` or ``gmpy2`` is installed.
|
| 29 |
+
|
| 30 |
+
See also
|
| 31 |
+
========
|
| 32 |
+
|
| 33 |
+
Domain
|
| 34 |
+
"""
|
| 35 |
+
|
| 36 |
+
rep = 'ZZ'
|
| 37 |
+
alias = 'ZZ'
|
| 38 |
+
dtype = MPZ
|
| 39 |
+
zero = dtype(0)
|
| 40 |
+
one = dtype(1)
|
| 41 |
+
tp = type(one)
|
| 42 |
+
|
| 43 |
+
|
| 44 |
+
is_IntegerRing = is_ZZ = True
|
| 45 |
+
is_Numerical = True
|
| 46 |
+
is_PID = True
|
| 47 |
+
|
| 48 |
+
has_assoc_Ring = True
|
| 49 |
+
has_assoc_Field = True
|
| 50 |
+
|
| 51 |
+
def __init__(self):
|
| 52 |
+
"""Allow instantiation of this domain. """
|
| 53 |
+
|
| 54 |
+
def __eq__(self, other):
|
| 55 |
+
"""Returns ``True`` if two domains are equivalent. """
|
| 56 |
+
if isinstance(other, IntegerRing):
|
| 57 |
+
return True
|
| 58 |
+
else:
|
| 59 |
+
return NotImplemented
|
| 60 |
+
|
| 61 |
+
def __hash__(self):
|
| 62 |
+
"""Compute a hash value for this domain. """
|
| 63 |
+
return hash('ZZ')
|
| 64 |
+
|
| 65 |
+
def to_sympy(self, a):
|
| 66 |
+
"""Convert ``a`` to a SymPy object. """
|
| 67 |
+
return SymPyInteger(int(a))
|
| 68 |
+
|
| 69 |
+
def from_sympy(self, a):
|
| 70 |
+
"""Convert SymPy's Integer to ``dtype``. """
|
| 71 |
+
if a.is_Integer:
|
| 72 |
+
return MPZ(a.p)
|
| 73 |
+
elif int_valued(a):
|
| 74 |
+
return MPZ(int(a))
|
| 75 |
+
else:
|
| 76 |
+
raise CoercionFailed("expected an integer, got %s" % a)
|
| 77 |
+
|
| 78 |
+
def get_field(self):
|
| 79 |
+
r"""Return the associated field of fractions :ref:`QQ`
|
| 80 |
+
|
| 81 |
+
Returns
|
| 82 |
+
=======
|
| 83 |
+
|
| 84 |
+
:ref:`QQ`:
|
| 85 |
+
The associated field of fractions :ref:`QQ`, a
|
| 86 |
+
:py:class:`~.Domain` representing the rational numbers
|
| 87 |
+
`\mathbb{Q}`.
|
| 88 |
+
|
| 89 |
+
Examples
|
| 90 |
+
========
|
| 91 |
+
|
| 92 |
+
>>> from sympy import ZZ
|
| 93 |
+
>>> ZZ.get_field()
|
| 94 |
+
QQ
|
| 95 |
+
"""
|
| 96 |
+
from sympy.polys.domains import QQ
|
| 97 |
+
return QQ
|
| 98 |
+
|
| 99 |
+
def algebraic_field(self, *extension, alias=None):
|
| 100 |
+
r"""Returns an algebraic field, i.e. `\mathbb{Q}(\alpha, \ldots)`.
|
| 101 |
+
|
| 102 |
+
Parameters
|
| 103 |
+
==========
|
| 104 |
+
|
| 105 |
+
*extension : One or more :py:class:`~.Expr`.
|
| 106 |
+
Generators of the extension. These should be expressions that are
|
| 107 |
+
algebraic over `\mathbb{Q}`.
|
| 108 |
+
|
| 109 |
+
alias : str, :py:class:`~.Symbol`, None, optional (default=None)
|
| 110 |
+
If provided, this will be used as the alias symbol for the
|
| 111 |
+
primitive element of the returned :py:class:`~.AlgebraicField`.
|
| 112 |
+
|
| 113 |
+
Returns
|
| 114 |
+
=======
|
| 115 |
+
|
| 116 |
+
:py:class:`~.AlgebraicField`
|
| 117 |
+
A :py:class:`~.Domain` representing the algebraic field extension.
|
| 118 |
+
|
| 119 |
+
Examples
|
| 120 |
+
========
|
| 121 |
+
|
| 122 |
+
>>> from sympy import ZZ, sqrt
|
| 123 |
+
>>> ZZ.algebraic_field(sqrt(2))
|
| 124 |
+
QQ<sqrt(2)>
|
| 125 |
+
"""
|
| 126 |
+
return self.get_field().algebraic_field(*extension, alias=alias)
|
| 127 |
+
|
| 128 |
+
def from_AlgebraicField(K1, a, K0):
|
| 129 |
+
"""Convert a :py:class:`~.ANP` object to :ref:`ZZ`.
|
| 130 |
+
|
| 131 |
+
See :py:meth:`~.Domain.convert`.
|
| 132 |
+
"""
|
| 133 |
+
if a.is_ground:
|
| 134 |
+
return K1.convert(a.LC(), K0.dom)
|
| 135 |
+
|
| 136 |
+
def log(self, a, b):
|
| 137 |
+
r"""Logarithm of *a* to the base *b*.
|
| 138 |
+
|
| 139 |
+
Parameters
|
| 140 |
+
==========
|
| 141 |
+
|
| 142 |
+
a: number
|
| 143 |
+
b: number
|
| 144 |
+
|
| 145 |
+
Returns
|
| 146 |
+
=======
|
| 147 |
+
|
| 148 |
+
$\\lfloor\log(a, b)\\rfloor$:
|
| 149 |
+
Floor of the logarithm of *a* to the base *b*
|
| 150 |
+
|
| 151 |
+
Examples
|
| 152 |
+
========
|
| 153 |
+
|
| 154 |
+
>>> from sympy import ZZ
|
| 155 |
+
>>> ZZ.log(ZZ(8), ZZ(2))
|
| 156 |
+
3
|
| 157 |
+
>>> ZZ.log(ZZ(9), ZZ(2))
|
| 158 |
+
3
|
| 159 |
+
|
| 160 |
+
Notes
|
| 161 |
+
=====
|
| 162 |
+
|
| 163 |
+
This function uses ``math.log`` which is based on ``float`` so it will
|
| 164 |
+
fail for large integer arguments.
|
| 165 |
+
"""
|
| 166 |
+
return self.dtype(int(math.log(int(a), b)))
|
| 167 |
+
|
| 168 |
+
def from_FF(K1, a, K0):
|
| 169 |
+
"""Convert ``ModularInteger(int)`` to GMPY's ``mpz``. """
|
| 170 |
+
return MPZ(K0.to_int(a))
|
| 171 |
+
|
| 172 |
+
def from_FF_python(K1, a, K0):
|
| 173 |
+
"""Convert ``ModularInteger(int)`` to GMPY's ``mpz``. """
|
| 174 |
+
return MPZ(K0.to_int(a))
|
| 175 |
+
|
| 176 |
+
def from_ZZ(K1, a, K0):
|
| 177 |
+
"""Convert Python's ``int`` to GMPY's ``mpz``. """
|
| 178 |
+
return MPZ(a)
|
| 179 |
+
|
| 180 |
+
def from_ZZ_python(K1, a, K0):
|
| 181 |
+
"""Convert Python's ``int`` to GMPY's ``mpz``. """
|
| 182 |
+
return MPZ(a)
|
| 183 |
+
|
| 184 |
+
def from_QQ(K1, a, K0):
|
| 185 |
+
"""Convert Python's ``Fraction`` to GMPY's ``mpz``. """
|
| 186 |
+
if a.denominator == 1:
|
| 187 |
+
return MPZ(a.numerator)
|
| 188 |
+
|
| 189 |
+
def from_QQ_python(K1, a, K0):
|
| 190 |
+
"""Convert Python's ``Fraction`` to GMPY's ``mpz``. """
|
| 191 |
+
if a.denominator == 1:
|
| 192 |
+
return MPZ(a.numerator)
|
| 193 |
+
|
| 194 |
+
def from_FF_gmpy(K1, a, K0):
|
| 195 |
+
"""Convert ``ModularInteger(mpz)`` to GMPY's ``mpz``. """
|
| 196 |
+
return MPZ(K0.to_int(a))
|
| 197 |
+
|
| 198 |
+
def from_ZZ_gmpy(K1, a, K0):
|
| 199 |
+
"""Convert GMPY's ``mpz`` to GMPY's ``mpz``. """
|
| 200 |
+
return a
|
| 201 |
+
|
| 202 |
+
def from_QQ_gmpy(K1, a, K0):
|
| 203 |
+
"""Convert GMPY ``mpq`` to GMPY's ``mpz``. """
|
| 204 |
+
if a.denominator == 1:
|
| 205 |
+
return a.numerator
|
| 206 |
+
|
| 207 |
+
def from_RealField(K1, a, K0):
|
| 208 |
+
"""Convert mpmath's ``mpf`` to GMPY's ``mpz``. """
|
| 209 |
+
p, q = K0.to_rational(a)
|
| 210 |
+
|
| 211 |
+
if q == 1:
|
| 212 |
+
# XXX: If MPZ is flint.fmpz and p is a gmpy2.mpz, then we need
|
| 213 |
+
# to convert via int because fmpz and mpz do not know about each
|
| 214 |
+
# other.
|
| 215 |
+
return MPZ(int(p))
|
| 216 |
+
|
| 217 |
+
def from_GaussianIntegerRing(K1, a, K0):
|
| 218 |
+
if a.y == 0:
|
| 219 |
+
return a.x
|
| 220 |
+
|
| 221 |
+
def from_EX(K1, a, K0):
|
| 222 |
+
"""Convert ``Expression`` to GMPY's ``mpz``. """
|
| 223 |
+
if a.is_Integer:
|
| 224 |
+
return K1.from_sympy(a)
|
| 225 |
+
|
| 226 |
+
def gcdex(self, a, b):
|
| 227 |
+
"""Compute extended GCD of ``a`` and ``b``. """
|
| 228 |
+
h, s, t = gcdex(a, b)
|
| 229 |
+
# XXX: This conditional logic should be handled somewhere else.
|
| 230 |
+
if GROUND_TYPES == 'gmpy':
|
| 231 |
+
return s, t, h
|
| 232 |
+
else:
|
| 233 |
+
return h, s, t
|
| 234 |
+
|
| 235 |
+
def gcd(self, a, b):
|
| 236 |
+
"""Compute GCD of ``a`` and ``b``. """
|
| 237 |
+
return gcd(a, b)
|
| 238 |
+
|
| 239 |
+
def lcm(self, a, b):
|
| 240 |
+
"""Compute LCM of ``a`` and ``b``. """
|
| 241 |
+
return lcm(a, b)
|
| 242 |
+
|
| 243 |
+
def sqrt(self, a):
|
| 244 |
+
"""Compute square root of ``a``. """
|
| 245 |
+
return sqrt(a)
|
| 246 |
+
|
| 247 |
+
def is_square(self, a):
|
| 248 |
+
"""Return ``True`` if ``a`` is a square.
|
| 249 |
+
|
| 250 |
+
Explanation
|
| 251 |
+
===========
|
| 252 |
+
An integer is a square if and only if there exists an integer
|
| 253 |
+
``b`` such that ``b * b == a``.
|
| 254 |
+
"""
|
| 255 |
+
return is_square(a)
|
| 256 |
+
|
| 257 |
+
def exsqrt(self, a):
|
| 258 |
+
"""Non-negative square root of ``a`` if ``a`` is a square.
|
| 259 |
+
|
| 260 |
+
See also
|
| 261 |
+
========
|
| 262 |
+
is_square
|
| 263 |
+
"""
|
| 264 |
+
if a < 0:
|
| 265 |
+
return None
|
| 266 |
+
root, rem = sqrtrem(a)
|
| 267 |
+
if rem != 0:
|
| 268 |
+
return None
|
| 269 |
+
return root
|
| 270 |
+
|
| 271 |
+
def factorial(self, a):
|
| 272 |
+
"""Compute factorial of ``a``. """
|
| 273 |
+
return factorial(a)
|
| 274 |
+
|
| 275 |
+
|
| 276 |
+
ZZ = IntegerRing()
|