summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/sage/categories/pushout.py3
-rw-r--r--src/sage/rings/padics/all.py4
-rw-r--r--src/sage/rings/padics/factory.py539
-rw-r--r--src/sage/rings/padics/generic_nodes.py10
-rw-r--r--src/sage/rings/padics/lattice_precision.py2461
-rw-r--r--src/sage/rings/padics/padic_base_generic.py11
-rw-r--r--src/sage/rings/padics/padic_base_leaves.py736
-rw-r--r--src/sage/rings/padics/padic_lattice_element.py1023
-rw-r--r--src/sage/rings/padics/padic_printing.pyx7
9 files changed, 4747 insertions, 47 deletions
diff --git a/src/sage/categories/pushout.py b/src/sage/categories/pushout.py
index 602117f..338a485 100644
--- a/src/sage/categories/pushout.py
+++ b/src/sage/categories/pushout.py
@@ -2472,6 +2472,9 @@ class CompletionFunctor(ConstructionFunctor):
"""
return not (self == other)
+ _real_types = ['Interval','Ball','MPFR','RDF','RLF']
+ _dvr_types = [None, 'fixed-mod','floating-point','capped-abs','capped-rel','lazy','lattice-cap','lattice-float']
+
def merge(self, other):
"""
Two Completion functors are merged, if they are equal. If the precisions of
diff --git a/src/sage/rings/padics/all.py b/src/sage/rings/padics/all.py
index 809b4aa..8b6d85e 100644
--- a/src/sage/rings/padics/all.py
+++ b/src/sage/rings/padics/all.py
@@ -1,7 +1,7 @@
from __future__ import absolute_import
from .generic_nodes import is_pAdicField, is_pAdicRing
-from .factory import Zp, Zq, Zp as pAdicRing, ZpCR, ZpCA, ZpFM, ZpFP, ZqCR, ZqCA, ZqFM, ZqFP #, ZpL, ZqL
-from .factory import Qp, Qq, Qp as pAdicField, QpCR, QpFP, QqCR, QqFP #, QpL, QqL
+from .factory import Zp, Zq, Zp as pAdicRing, ZpCR, ZpCA, ZpFM, ZpFP, ZpLC, ZpLF, ZqCR, ZqCA, ZqFM, ZqFP #, ZpL, ZqL
+from .factory import Qp, Qq, Qp as pAdicField, QpCR, QpFP, QpLC, QpLF, QqCR, QqFP #, QpL, QqL
from .factory import pAdicExtension
from .padic_generic import local_print_mode
from .pow_computer import PowComputer
diff --git a/src/sage/rings/padics/factory.py b/src/sage/rings/padics/factory.py
index 291172c..2781a32 100644
--- a/src/sage/rings/padics/factory.py
+++ b/src/sage/rings/padics/factory.py
@@ -22,6 +22,7 @@ from __future__ import absolute_import, print_function
from sage.structure.factory import UniqueFactory
from sage.rings.integer import Integer
+from sage.rings.infinity import Infinity
from sage.structure.factorization import Factorization
from sage.rings.integer_ring import ZZ
from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
@@ -31,8 +32,10 @@ from .padic_base_leaves import (pAdicRingCappedRelative,
pAdicRingCappedAbsolute,
pAdicRingFixedMod,
pAdicRingFloatingPoint,
+ pAdicRingLattice,
pAdicFieldCappedRelative,
- pAdicFieldFloatingPoint)
+ pAdicFieldFloatingPoint,
+ pAdicFieldLattice)
from . import padic_printing
######################################################
@@ -75,7 +78,8 @@ def _default_show_prec(type, print_mode):
INPUT:
- - ``type`` -- a string: ``'capped-rel'``, ``'capped-abs'``, ``'fixed-mod'`` or ``'floating-point'``
+ - ``type`` -- a string: ``'capped-rel'``, ``'capped-abs'``, ``'fixed-mod'``, ``'floating-point'``,
+ ``'lattice-cap'`` or ``'lattice-float'``
- ``print_mode`` -- a string: ``'series'``, ``'terse'``, ``'val-unit'``, ``'digits'``, ``'bars'``
EXAMPLES::
@@ -95,7 +99,7 @@ def _default_show_prec(type, print_mode):
else:
return False
-def get_key_base(p, prec, type, print_mode, names, ram_name, print_pos, print_sep, print_alphabet, print_max_terms, show_prec, check, valid_non_lazy_types):
+def get_key_base(p, prec, type, print_mode, names, ram_name, print_pos, print_sep, print_alphabet, print_max_terms, show_prec, check, valid_non_lazy_types, label=None, absprec=None, relprec=None):
"""
This implements create_key for Zp and Qp: moving it here prevents code duplication.
@@ -105,19 +109,72 @@ def get_key_base(p, prec, type, print_mode, names, ram_name, print_pos, print_se
sage: from sage.rings.padics.factory import get_key_base
sage: get_key_base(11, 5, 'capped-rel', None, None, None, None, ':', None, None, False, True, ['capped-rel'])
- (11, 5, 'capped-rel', 'series', '11', True, '|', (), -1, False)
+ (11, 5, 'capped-rel', 'series', '11', True, '|', (), -1, False, None)
sage: get_key_base(12, 5, 'capped-rel', 'digits', None, None, None, None, None, None, True, False, ['capped-rel'])
- (12, 5, 'capped-rel', 'digits', '12', True, '|', ('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B'), -1, True)
+ (12,
+ 5,
+ 'capped-rel',
+ 'digits',
+ '12',
+ True,
+ '|',
+ ('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B'),
+ -1,
+ True,
+ None)
"""
- if prec is None:
- prec = DEFAULT_PREC
if check:
if not isinstance(p, Integer):
p = Integer(p)
- if not isinstance(prec, Integer):
- prec = Integer(prec)
if not p.is_prime():
raise ValueError("p must be prime")
+ if type == 'lattice-cap':
+ relative_cap = absolute_cap = None
+ if prec is not None:
+ # We first try to unpack
+ try:
+ relative_cap, absolute_cap = prec
+ except (ValueError, TypeError):
+ relative_cap = prec
+ if absprec is not None:
+ if absolute_cap is None:
+ absolute_cap = absprec
+ else:
+ raise ValueError("absolute cap specified twice")
+ if relprec is not None:
+ if relative_cap is None:
+ relative_cap = relprec
+ else:
+ raise ValueError("relative cap specified twice")
+ if relative_cap is not None:
+ if relative_cap is not Infinity:
+ try:
+ relative_cap = Integer(relative_cap)
+ except TypeError:
+ raise TypeError("relative cap must be either a positive integer or infinity")
+ if relative_cap <= 0:
+ raise ValueError("relative cap must be positive")
+ if absolute_cap is not None:
+ try:
+ absolute_cap = Integer(absolute_cap)
+ except TypeError:
+ raise TypeError("absolute cap must be an integer")
+ if relative_cap is None and absolute_cap is None:
+ relative_cap = DEFAULT_PREC
+ absolute_cap = 2 * DEFAULT_PREC
+ elif relative_cap is None:
+ relative_cap = Infinity
+ elif absolute_cap is None:
+ absolute_cap = 2 * relative_cap
+ prec = (relative_cap, absolute_cap)
+ else:
+ if prec is not None:
+ prec = Integer(prec)
+ if prec is None:
+ if type == 'lattice-cap':
+ prec = (DEFAULT_PREC, 2*DEFAULT_PREC)
+ else:
+ prec = DEFAULT_PREC
print_ram_name = ram_name
if isinstance(print_mode, dict):
if 'pos' in print_mode:
@@ -186,9 +243,8 @@ def get_key_base(p, prec, type, print_mode, names, ram_name, print_pos, print_se
if show_prec is None:
show_prec = _default_show_prec(type, print_mode)
if type in valid_non_lazy_types:
- key = (p, prec, type, print_mode, name, print_pos, print_sep, tuple(print_alphabet), print_max_terms, show_prec)
+ key = (p, prec, type, print_mode, name, print_pos, print_sep, tuple(print_alphabet), print_max_terms, show_prec, label)
else:
- print(type)
raise ValueError("type must be %s"%(", ".join(valid_non_lazy_types)))
return key
@@ -217,8 +273,9 @@ class Qp_class(UniqueFactory):
TYPES and PRECISION below.
- ``type`` -- string (default: ``'capped-rel'``) Valid types are
- ``'capped-rel'``, ``'floating-point'`` and ``'lazy'`` (though ``'lazy'`` currently
- doesn't work). See TYPES and PRECISION below
+ ``'capped-rel'``, ``'floating-point'``, ``'lattice-cap'``, ``'lattice-float'``
+ and ``'lazy'`` (though ``'lazy'`` currently doesn't work).
+ See TYPES and PRECISION below
- ``print_mode`` -- string (default: ``None``). Valid modes are 'series',
'val-unit', 'terse', 'digits', and 'bars'. See PRINTING below
@@ -537,7 +594,8 @@ class Qp_class(UniqueFactory):
"""
def create_key(self, p, prec = None, type = 'capped-rel', print_mode = None,
names = None, ram_name = None, print_pos = None,
- print_sep = None, print_alphabet = None, print_max_terms = None, show_prec=None, check = True):
+ print_sep = None, print_alphabet = None, print_max_terms = None, show_prec = None, check = True,
+ label = None, relprec = None, absprec = None): # specific to Lattice precision
"""
Creates a key from input parameters for ``Qp``.
@@ -546,7 +604,7 @@ class Qp_class(UniqueFactory):
TESTS::
sage: Qp.create_key(5,40)
- (5, 40, 'capped-rel', 'series', '5', True, '|', (), -1, True)
+ (5, 40, 'capped-rel', 'series', '5', True, '|', (), -1, True, None)
"""
if isinstance(names, (int, Integer)):
# old pickle; names is what used to be halt.
@@ -556,7 +614,7 @@ class Qp_class(UniqueFactory):
print_alphabet = print_max_terms
print_max_terms = check
check = True
- return get_key_base(p, prec, type, print_mode, names, ram_name, print_pos, print_sep, print_alphabet, print_max_terms, show_prec, check, ['capped-rel', 'floating-point'])
+ return get_key_base(p, prec, type, print_mode, names, ram_name, print_pos, print_sep, print_alphabet, print_max_terms, show_prec, check, ['capped-rel', 'floating-point', 'lattice-cap', 'lattice-float'], label, relprec, absprec)
def create_object(self, version, key):
"""
@@ -575,8 +633,9 @@ class Qp_class(UniqueFactory):
elif version[0] < 8:
p, prec, type, print_mode, name, print_pos, print_sep, print_alphabet, print_max_terms = key
show_prec = None
+ label = None
else:
- p, prec, type, print_mode, name, print_pos, print_sep, print_alphabet, print_max_terms, show_prec = key
+ p, prec, type, print_mode, name, print_pos, print_sep, print_alphabet, print_max_terms, show_prec, label = key
if isinstance(type, Integer):
# lazy
raise NotImplementedError("lazy p-adics need more work. Sorry.")
@@ -592,7 +651,7 @@ class Qp_class(UniqueFactory):
return obj
except KeyError:
pass
- p, prec, type, print_mode, name, print_pos, print_sep, print_alphabet, print_max_terms, show_prec = key
+ p, prec, type, print_mode, name, print_pos, print_sep, print_alphabet, print_max_terms, show_prec, label = key
if type == 'capped-rel':
if print_mode == 'terse':
@@ -608,6 +667,14 @@ class Qp_class(UniqueFactory):
else:
return pAdicFieldFloatingPoint(p, prec, {'mode': print_mode, 'pos': print_pos, 'sep': print_sep, 'alphabet': print_alphabet,
'ram_name': name, 'max_ram_terms': print_max_terms, 'show_prec': show_prec}, name)
+ elif type[:8] == 'lattice-':
+ subtype = type[8:]
+ if print_mode == 'terse':
+ return pAdicFieldLattice(p, prec, subtype, {'mode': print_mode, 'pos': print_pos, 'sep': print_sep, 'alphabet': print_alphabet,
+ 'ram_name': name, 'max_terse_terms': print_max_terms, 'show_prec': show_prec}, name, label)
+ else:
+ return pAdicFieldLattice(p, prec, subtype, {'mode': print_mode, 'pos': print_pos, 'sep': print_sep, 'alphabet': print_alphabet,
+ 'ram_name': name, 'max_ram_terms': print_max_terms, 'show_prec': show_prec}, name, label)
else:
raise ValueError("unexpected type")
@@ -1181,6 +1248,15 @@ def QpFP(p, prec = None, *args, **kwds):
"""
return Qp(p, prec, 'floating-point', *args, **kwds)
+#def QpL(p, prec = DEFAULT_PREC, print_mode = None, halt = DEFAULT_HALT, names = None, print_pos = None,
+# print_sep = None, print_alphabet = None, print_max_terms = None, check=True):
+# """
+# A shortcut function to create lazy p-adic fields.
+
+# Currently deactivated. See documentation for Qp for a description of the input parameters.
+
+# EXAMPLES::
+
def QqCR(q, prec = None, *args, **kwds):
"""
A shortcut function to create capped relative unramified `p`-adic
@@ -1211,6 +1287,25 @@ def QqFP(q, prec = None, *args, **kwds):
"""
return Qq(q, prec, 'floating-point', *args, **kwds)
+def QpLC(p, prec = None, *args, **kwds):
+ """
+ A shortcut function to create `p`-adic fields with lattice precision.
+
+ See :func:`ZpLC` for more information about this model of precision.
+
+ EXAMPLES::
+
+ sage: R = QpLC(2)
+ sage: R
+ 2-adic Field with lattice-cap precision
+
+ """
+ return Qp(p, prec, 'lattice-cap', *args, **kwds)
+
+def QpLF(p, prec = None, *args, **kwds):
+ return Qp(p, prec, 'lattice-float', *args, **kwds)
+
+
#######################################################################################################
#
# p-Adic Rings
@@ -1229,14 +1324,16 @@ class Zp_class(UniqueFactory):
- ``p`` -- integer: the `p` in `\mathbb{Z}_p`
- ``prec`` -- integer (default: ``20``) the precision cap of the
- ring. Except for the fixed modulus case, individual elements
+ ring. In the lattice capped case, ``prec`` can either be a
+ pair (``relative_cap``, ``absolute_cap``).
+ Except for the fixed modulus case, individual elements
keep track of their own precision. See TYPES and PRECISION
below.
- ``type`` -- string (default: ``'capped-rel'``) Valid types are
``'capped-rel'``, ``'capped-abs'``, ``'fixed-mod'``,
- ``'floating-point'`` and ``'lazy'`` (though lazy is not yet
- implemented). See TYPES and PRECISION below
+ ``'floating-point'``, ``'lattice-cap'``, ``'lattice-float'``
+ See TYPES and PRECISION below
- ``print_mode`` -- string (default: ``None``). Valid modes are
``'series'``, ``'val-unit'``, ``'terse'``, ``'digits'``, and
@@ -1271,9 +1368,9 @@ class Zp_class(UniqueFactory):
TYPES AND PRECISION:
- There are two types of precision for a `p`-adic element. The first
- is relative precision, which gives the number of known `p`-adic
- digits::
+ There are three types of precision.
+ The first is relative precision; it gives the number of known
+ `p`-adic digits::
sage: R = Zp(5, 20, 'capped-rel', 'series'); a = R(675); a
2*5^2 + 5^4 + O(5^22)
@@ -1286,10 +1383,29 @@ class Zp_class(UniqueFactory):
sage: a.precision_absolute()
22
- There are five types of `p`-adic rings: capped relative rings
+ The third one is lattice precision.
+ It is not attached to a single `p`-adic number but is a unique
+ object modeling the precision on a set of `p`-adics, which is
+ typically the set of all elements within the same parent.
+
+ sage: R = ZpLC(17)
+ sage: x = R(1,10); y = R(1,5)
+ sage: R.precision()
+ Precision lattice on 2 objects
+ sage: R.precision().precision_lattice()
+ [2015993900449 0]
+ [ 0 1419857]
+
+ We refer to the documentation of the function :func:`ZpLC` for
+ more information about this precision model.
+
+ ::
+
+ There are seven types of `p`-adic rings: capped relative rings
(type= ``'capped-rel'``), capped absolute rings
(type= ``'capped-abs'``), fixed modulus rings (type= ``'fixed-mod'``),
- floating point rings (type=``'floating-point'``),
+ floating point rings (type=``'floating-point'``), lattice capped rings
+ (type=``'lattice-cap'``), lattice float rings (type=``'lattice-float'``)
and lazy rings (type= ``'lazy'``).
In the capped relative case, the relative precision of an element
@@ -1337,10 +1453,16 @@ class Zp_class(UniqueFactory):
in that elements do not trac their own precision. However, relative
precision is truncated with each operation rather than absolute precision.
+ On the contrary, the lattice type tracks precision using lattices
+ and automatic differentiation. It is rather slow but provides sharp
+ (often optimal) results regarding precision.
+ We refer to the documentation of the function :func:`ZpLC` for a
+ small demonstration of the capabilities of this precision model.
+
The lazy case will eventually support elements that can increase
their precision upon request. It is not currently implemented.
- PRINTING
+ PRINTING:
There are many different ways to print `p`-adic elements. The
way elements of a given ring print is controlled by options
@@ -1603,7 +1725,8 @@ class Zp_class(UniqueFactory):
"""
def create_key(self, p, prec = None, type = 'capped-rel', print_mode = None,
names = None, ram_name = None, print_pos = None, print_sep = None, print_alphabet = None,
- print_max_terms = None, show_prec = None, check = True):
+ print_max_terms = None, show_prec = None, check = True,
+ label = None, relprec = None, absprec = None):
"""
Creates a key from input parameters for ``Zp``.
@@ -1612,9 +1735,19 @@ class Zp_class(UniqueFactory):
TESTS::
sage: Zp.create_key(5,40)
- (5, 40, 'capped-rel', 'series', '5', True, '|', (), -1, True)
+ (5, 40, 'capped-rel', 'series', '5', True, '|', (), -1, True, None)
sage: Zp.create_key(5,40,print_mode='digits')
- (5, 40, 'capped-rel', 'digits', '5', True, '|', ('0', '1', '2', '3', '4'), -1, False)
+ (5,
+ 40,
+ 'capped-rel',
+ 'digits',
+ '5',
+ True,
+ '|',
+ ('0', '1', '2', '3', '4'),
+ -1,
+ False,
+ None)
"""
if isinstance(names, (int, Integer)):
# old pickle; names is what used to be halt.
@@ -1625,7 +1758,9 @@ class Zp_class(UniqueFactory):
print_max_terms = check
check = True
return get_key_base(p, prec, type, print_mode, names, ram_name, print_pos, print_sep, print_alphabet,
- print_max_terms, show_prec, check, ['capped-rel', 'fixed-mod', 'capped-abs', 'floating-point'])
+ print_max_terms, show_prec, check,
+ ['capped-rel', 'fixed-mod', 'capped-abs', 'floating-point', 'lattice-cap', 'lattice-float'],
+ label=label, relprec=relprec, absprec=absprec)
def create_object(self, version, key):
"""
@@ -1645,8 +1780,9 @@ class Zp_class(UniqueFactory):
elif version[0] < 8:
p, prec, type, print_mode, name, print_pos, print_sep, print_alphabet, print_max_terms = key
show_prec = None
+ label = None
else:
- p, prec, type, print_mode, name, print_pos, print_sep, print_alphabet, print_max_terms, show_prec = key
+ p, prec, type, print_mode, name, print_pos, print_sep, print_alphabet, print_max_terms, show_prec, label = key
if isinstance(type, Integer):
# lazy
raise NotImplementedError("lazy p-adics need more work. Sorry.")
@@ -1655,14 +1791,14 @@ class Zp_class(UniqueFactory):
# keys changed in order to reduce irrelevant duplications: e.g. two Zps with print_mode 'series'
# that are identical except for different 'print_alphabet' now return the same object.
key = get_key_base(p, prec, type, print_mode, name, None, print_pos, print_sep, print_alphabet,
- print_max_terms, None, False, ['capped-rel', 'fixed-mod', 'capped-abs'])
+ print_max_terms, None, False, ['capped-rel', 'fixed-mod', 'capped-abs', 'lattice-cap', 'lattice-float'])
try:
obj = self._cache[version, key]()
if obj is not None:
return obj
except KeyError:
pass
- p, prec, type, print_mode, name, print_pos, print_sep, print_alphabet, print_max_terms, show_prec = key
+ p, prec, type, print_mode, name, print_pos, print_sep, print_alphabet, print_max_terms, show_prec, label = key
if type == 'capped-rel':
return pAdicRingCappedRelative(p, prec, {'mode': print_mode, 'pos': print_pos, 'sep': print_sep, 'alphabet': print_alphabet,
'ram_name': name, 'max_ram_terms': print_max_terms, 'show_prec': show_prec}, name)
@@ -1675,6 +1811,10 @@ class Zp_class(UniqueFactory):
elif type == 'floating-point':
return pAdicRingFloatingPoint(p, prec, {'mode': print_mode, 'pos': print_pos, 'sep': print_sep, 'alphabet': print_alphabet,
'ram_name': name, 'max_ram_terms': print_max_terms, 'show_prec': show_prec}, name)
+ elif type[:8] == 'lattice-':
+ subtype = type[8:]
+ return pAdicRingLattice(p, prec, subtype, {'mode': print_mode, 'pos': print_pos, 'sep': print_sep, 'alphabet': print_alphabet,
+ 'ram_name': name, 'max_ram_terms': print_max_terms, 'show_prec': show_prec}, name, label)
else:
raise ValueError("unexpected type")
@@ -2343,6 +2483,337 @@ def ZqFP(q, prec = None, *args, **kwds):
"""
return Zq(q, prec, 'floating-point', *args, **kwds)
+def ZpLC(p, prec=None, *args, **kwds):
+ """
+ A shortcut function to create `p`-adic rings with lattice precision
+ with cap.
+
+ DEMONSTRATION:
+
+ Below is a small demo of the features by this model of precision::
+
+ sage: R = ZpLC(3, print_mode='terse')
+ sage: x = R(1,10)
+
+ Of course, when we multiply by 3, we gain one digit of absolute
+ precision::
+
+ sage: 3*x
+ 3 + O(3^11)
+
+ The lattice precision machinery sees this even if we decompose
+ the computation into several steps::
+
+ sage: y = x+x
+ sage: y
+ 2 + O(3^10)
+ sage: x + y
+ 3 + O(3^11)
+
+ The same works for the multiplication::
+
+ sage: z = x^2
+ sage: z
+ 1 + O(3^10)
+ sage: x*z
+ 1 + O(3^11)
+
+ This comes more funny when we are working with elements given
+ at different precisions::
+
+ sage: R = ZpLC(2, print_mode='terse')
+ sage: x = R(1,10)
+ sage: y = R(1,5)
+ sage: z = x+y; z
+ 2 + O(2^5)
+ sage: t = x-y; t
+ 0 + O(2^5)
+ sage: z+t # observe that z+t = 2*x
+ 2 + O(2^11)
+ sage: z-t # observe that z-t = 2*y
+ 2 + O(2^6)
+
+ sage: x = R(28888,15)
+ sage: y = R(204,10)
+ sage: z = x/y; z
+ 242 + O(2^9)
+ sage: z*y # which is x
+ 28888 + O(2^15)
+
+ The SOMOS sequence is the sequence defined by the recurrence::
+
+ ..MATH::
+
+ u_n = \frac {u_{n-1} u_{n-3} + u_{n-2}^2} {u_{n-4}}
+
+ It is known for its numerical instability.
+ On the one hand, one can show that if the initial values are
+ invertible in `\mathbb{Z}_p` and known at precision `O(p^N)`
+ then all the next terms of the SOMOS sequence will be known
+ at the same precision as well.
+ On the other hand, because of the division, when we unroll
+ the recurrence, we loose a lot of precision. Observe::
+
+ sage: R = Zp(2, 30, print_mode='terse')
+ sage: a,b,c,d = R(1,15), R(1,15), R(1,15), R(3,15)
+ sage: a,b,c,d = b,c,d,(b*d+c*c)/a; print(d)
+ 4 + O(2^15)
+ sage: a,b,c,d = b,c,d,(b*d+c*c)/a; print(d)
+ 13 + O(2^15)
+ sage: a,b,c,d = b,c,d,(b*d+c*c)/a; print(d)
+ 55 + O(2^15)
+ sage: a,b,c,d = b,c,d,(b*d+c*c)/a; print(d)
+ 21975 + O(2^15)
+ sage: a,b,c,d = b,c,d,(b*d+c*c)/a; print(d)
+ 6639 + O(2^13)
+ sage: a,b,c,d = b,c,d,(b*d+c*c)/a; print(d)
+ 7186 + O(2^13)
+ sage: a,b,c,d = b,c,d,(b*d+c*c)/a; print(d)
+ 569 + O(2^13)
+ sage: a,b,c,d = b,c,d,(b*d+c*c)/a; print(d)
+ 253 + O(2^13)
+ sage: a,b,c,d = b,c,d,(b*d+c*c)/a; print(d)
+ 4149 + O(2^13)
+ sage: a,b,c,d = b,c,d,(b*d+c*c)/a; print(d)
+ 2899 + O(2^12)
+ sage: a,b,c,d = b,c,d,(b*d+c*c)/a; print(d)
+ 3072 + O(2^12)
+ sage: a,b,c,d = b,c,d,(b*d+c*c)/a; print(d)
+ 349 + O(2^12)
+ sage: a,b,c,d = b,c,d,(b*d+c*c)/a; print(d)
+ 619 + O(2^12)
+ sage: a,b,c,d = b,c,d,(b*d+c*c)/a; print(d)
+ 243 + O(2^12)
+ sage: a,b,c,d = b,c,d,(b*d+c*c)/a; print(d)
+ 3 + O(2^2)
+ sage: a,b,c,d = b,c,d,(b*d+c*c)/a; print(d)
+ 2 + O(2^2)
+
+ If instead, we use the lattice precision, everything goes well::
+
+ sage: R = ZpLC(2, 30, print_mode='terse')
+ sage: a,b,c,d = R(1,15), R(1,15), R(1,15), R(3,15)
+ sage: a,b,c,d = b,c,d,(b*d+c*c)/a; print(d)
+ 4 + O(2^15)
+ sage: a,b,c,d = b,c,d,(b*d+c*c)/a; print(d)
+ 13 + O(2^15)
+ sage: a,b,c,d = b,c,d,(b*d+c*c)/a; print(d)
+ 55 + O(2^15)
+ sage: a,b,c,d = b,c,d,(b*d+c*c)/a; print(d)
+ 21975 + O(2^15)
+ sage: a,b,c,d = b,c,d,(b*d+c*c)/a; print(d)
+ 23023 + O(2^15)
+ sage: a,b,c,d = b,c,d,(b*d+c*c)/a; print(d)
+ 31762 + O(2^15)
+ sage: a,b,c,d = b,c,d,(b*d+c*c)/a; print(d)
+ 16953 + O(2^15)
+ sage: a,b,c,d = b,c,d,(b*d+c*c)/a; print(d)
+ 16637 + O(2^15)
+
+ sage: for _ in range(100):
+ ....: a,b,c,d = b,c,d,(b*d+c*c)/a
+ sage: a
+ 15519 + O(2^15)
+ sage: b
+ 32042 + O(2^15)
+ sage: c
+ 17769 + O(2^15)
+ sage: d
+ 20949 + O(2^15)
+
+ BEHIND THE SCENE:
+
+ The precision is global.
+ It is encoded by a lattice in a huge vector space whose dimension
+ is the number of elements having this parent.
+
+ Concretely, this precision datum is an instance of the class
+ :class:`sage.rings.padic.lattice_precision.PrecisionLattice`.
+ It is attached to the parent and is created at the same time
+ as the parent.
+ (It is actually a bit more subtle because two different parents
+ may share the same instance; this happens for instance for a
+ `p`-adic ring and its field of fractions.)
+
+ This precision datum is accessible through the method :meth:`precision`::
+
+ sage: R = ZpLC(5, print_mode='terse')
+ sage: prec = R.precision()
+ sage: prec
+ Precision lattice on 0 object
+
+ This instance knows about all elements of the parent, it is
+ automatically updated when a new element (of this parent) is
+ created::
+
+ sage: x = R(3513,10)
+ sage: prec
+ Precision lattice on 1 object
+ sage: y = R(176,5)
+ sage: prec
+ Precision lattice on 2 objects
+ sage: z = R.random_element()
+ sage: prec
+ Precision lattice on 3 objects
+
+ The method :meth:`tracked_elements` provides the list of all
+ tracked elements::
+
+ sage: prec.tracked_elements()
+ [3513 + O(5^10), 176 + O(5^5), ...]
+
+ Similarly, when a variable is collected by the garbage collector,
+ the precision lattice is updated. Note however that the update
+ might be delayed. We can force it with the method :meth:`del_elements`::
+
+ sage: z = 0
+ sage: prec
+ Precision lattice on 3 objects
+ sage: prec.del_elements()
+ sage: prec
+ Precision lattice on 2 objects
+
+ The method :meth:`precision_lattice` returns (a matrix defining)
+ the lattice that models the precision. Here we have::
+
+ sage: prec.precision_lattice()
+ [9765625 0]
+ [ 0 3125]
+
+ Observe that `5^10 = 9765625` and `5^5 = 3125`.
+ The above matrix then reflects the precision on `x` and `y`.
+
+ Now, observe how the precision lattice changes while performing
+ computations::
+
+ sage: x, y = 3*x+2*y, 2*(x-y)
+ sage: prec.del_elements()
+ sage: prec.precision_lattice()
+ [ 3125 48825000]
+ [ 0 48828125]
+
+ The matrix we get is no longer diagonal, meaning that some digits
+ of precision are diffused among the two new elements `x` and `y`.
+ They nevertheless show up when we compute for instance `x+y`::
+
+ sage: x
+ 1516 + O(5^5)
+ sage: y
+ 424 + O(5^5)
+ sage: x+y
+ 17565 + O(5^11)
+
+ These diffused digits of precision (which are tracked but
+ do not appear on the printing) allow to be always sharp on
+ precision.
+
+ PERFORMANCES:
+
+ Each elementary operation requires significant manipulations
+ on the lattice precision and then is costly. Precisely:
+
+ - The creation of a new element has a cost `O(n)` where `n`
+ is the number of tracked elements.
+
+ - The destruction of one element has a cost `O(m^2)` where
+ `m` is the distance between the destroyed element and
+ the last one. Fortunately, it seems that `m` tends to
+ be small in general (the dynamics of the list of tracked
+ elements is rather close to that of a stack).
+
+ It is nevertheless still possible to manipulate several
+ hundred variables (e.g. square matrices of size 5 or
+ polynomials of degree 20 are accessible).
+
+ The class :class:`PrecisionLattice` provides several
+ features for introspection (especially concerning timings).
+ If enabled, it maintains an history of all actions and stores
+ the wall time of each of them::
+
+ sage: R = ZpLC(3)
+ sage: prec = R.precision()
+ sage: prec.history_enable()
+ sage: M = random_matrix(R, 5)
+ sage: d = M.determinant()
+ sage: print(prec.history()) # somewhat random
+ ---
+ 0.004212s oooooooooooooooooooooooooooooooooooo
+ 0.000003s oooooooooooooooooooooooooooooooooo~~
+ 0.000010s oooooooooooooooooooooooooooooooooo
+ 0.001560s ooooooooooooooooooooooooooooooooooooooooo
+ 0.000004s ooooooooooooooooooooooooooooo~oooo~oooo~o
+ 0.002168s oooooooooooooooooooooooooooooooooooooo
+ 0.001787s ooooooooooooooooooooooooooooooooooooooooo
+ 0.000004s oooooooooooooooooooooooooooooooooooooo~~o
+ 0.000198s ooooooooooooooooooooooooooooooooooooooo
+ 0.001152s ooooooooooooooooooooooooooooooooooooooooo
+ 0.000005s ooooooooooooooooooooooooooooooooo~oooo~~o
+ 0.000853s oooooooooooooooooooooooooooooooooooooo
+ 0.000610s ooooooooooooooooooooooooooooooooooooooo
+ ...
+ 0.003879s ooooooooooooooooooooooooooooooooooooooooooooooooooooooooo
+ 0.000006s oooooooooooooooooooooooooooooooooooooooooooooooooooo~~~~~
+ 0.000036s oooooooooooooooooooooooooooooooooooooooooooooooooooo
+ 0.006737s oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo
+ 0.000005s oooooooooooooooooooooooooooooooooooooooooooooooooooo~~~~~ooooo
+ 0.002637s ooooooooooooooooooooooooooooooooooooooooooooooooooooooooo
+ 0.007118s ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo
+ 0.000008s oooooooooooooooooooooooooooooooooooooooooooooooooooo~~~~o~~~~oooo
+ 0.003504s ooooooooooooooooooooooooooooooooooooooooooooooooooooooooo
+ 0.005371s ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo
+ 0.000006s ooooooooooooooooooooooooooooooooooooooooooooooooooooo~~~o~~~ooo
+ 0.001858s ooooooooooooooooooooooooooooooooooooooooooooooooooooooooo
+ 0.003584s ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo
+ 0.000004s oooooooooooooooooooooooooooooooooooooooooooooooooooooo~~o~~oo
+ 0.000801s ooooooooooooooooooooooooooooooooooooooooooooooooooooooooo
+ 0.001916s ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo
+ 0.000022s ooooooooooooooooooooooooooooo~~~~~~~~~~~~~~~~~~~~~~oooo~o~o
+ 0.014705s ooooooooooooooooooooooooooooooooooo
+ 0.001292s ooooooooooooooooooooooooooooooooooooo
+ 0.000002s ooooooooooooooooooooooooooooooooooo~o
+
+ The symbol `o` symbolized a tracked element.
+ The symbol `~` means that the element is marked for deletion.
+
+ The global timings are also accessible as follows::
+
+ sage: prec.timings() # somewhat random
+ {'add': 0.25049376487731934,
+ 'del': 0.11911273002624512,
+ 'mark': 0.0004909038543701172,
+ 'partial reduce': 0.0917658805847168}
+
+ """
+ return Zp(p, prec, 'lattice-cap', *args, **kwds)
+
+def ZpLF(p, prec=None, *args, **kwds):
+ """
+ A shortcut function to create `p`-adic rings with precision
+ tracked through automatic differentiation.
+ Floating point `p`-adic numbers are used for the computation
+ of the differential (which is then not exact).
+
+ See documentation for ``Zp`` for a description of the input parameters.
+
+ EXAMPLES::
+
+ sage: ZpLF(5, 40)
+ 5-adic Ring with lattice-float precision
+
+ .. SEEALSO::
+
+ :func:`ZpLC`
+ """
+ return Zp(p, prec, 'lattice-float', *args, **kwds)
+
+#def ZpL(p, prec = DEFAULT_PREC, print_mode = None, halt = DEFAULT_HALT, names = None, print_pos = None,
+# print_sep = None, print_alphabet = None, print_max_terms = None, check=True):
+# """
+# A shortcut function to create lazy `p`-adic rings.
+
+# Currently deactivated. See documentation for Zp for a description of the input parameters.
+
+
#######################################################################################################
#
# The Extension Factory -- creates extensions of p-adic rings and fields
diff --git a/src/sage/rings/padics/generic_nodes.py b/src/sage/rings/padics/generic_nodes.py
index 656faaa..ba6ad41 100644
--- a/src/sage/rings/padics/generic_nodes.py
+++ b/src/sage/rings/padics/generic_nodes.py
@@ -389,10 +389,12 @@ class pAdicRingBaseGeneric(pAdicBaseGeneric, pAdicRingGeneric):
True
"""
from sage.categories.pushout import CompletionFunctor
- return (CompletionFunctor(self.prime(),
- self.precision_cap(),
- {'print_mode':self._printer.dict(), 'type':self._prec_type(), 'names':self._names}),
- ZZ)
+ extras = {'print_mode':self._printer.dict(), 'type':self._prec_type(), 'names':self._names}
+ try:
+ extras['label'] = self._label
+ except AttributeError:
+ pass
+ return (CompletionFunctor(self.prime(), self.precision_cap(), extras), ZZ)
def random_element(self, algorithm='default'):
r"""
diff --git a/src/sage/rings/padics/lattice_precision.py b/src/sage/rings/padics/lattice_precision.py
new file mode 100644
index 00000000..5c69780
--- /dev/null
+++ b/src/sage/rings/padics/lattice_precision.py
@@ -0,0 +1,2461 @@
+import _weakref as weakref
+from sage.misc.misc import walltime
+
+from sage.structure.sage_object import SageObject
+from sage.structure.unique_representation import UniqueRepresentation
+
+from sage.rings.integer_ring import ZZ
+from sage.rings.rational_field import QQ
+from sage.rings.infinity import Infinity
+
+from sage.rings.padics.precision_error import PrecisionError
+
+
+# Global variables
+
+DEFAULT_THRESOLD_DELETION = 50
+STARTING_ADDITIONAL_PREC = 5
+
+
+
+# Class pRational
+#################
+
+class pRational:
+ """
+ This class implements rational numbers as approximations
+ of `p`-adic numbers.
+
+ Only for internal use.
+ """
+ def __init__(self, p, x, exponent=0, valuation=None):
+ """
+ Construct the element ``x * p^exponent``
+
+ INPUT:
+
+ - ``p`` -- a prime number
+
+ - ``x`` -- a rational number
+
+ - ``exponent`` -- a relative integer (default: 0)
+
+ - ``valuation`` -- an integer or None (default: ``None``),
+ the ``p``-adic valuation of this element
+
+ If not ``None``, this method trusts the given value to the
+ attribute ``valuation``.
+
+ TESTS::
+
+ sage: from sage.rings.padics.lattice_precision import pRational
+ sage: pRational(2, 5)
+ 5
+ sage: pRational(2, 5/3, 2)
+ 2^2 * 5/3
+ """
+ self.p = p
+ if x in ZZ:
+ self.x = ZZ(x)
+ else:
+ self.x = x
+ self.exponent = exponent
+ self._valuation = valuation
+
+ def __repr__(self):
+ """
+ Return a string representation of this element
+
+ TESTS::
+
+ sage: from sage.rings.padics.lattice_precision import pRational
+ sage: pRational(2, 5, 2) # indirect doctest
+ 2^2 * 5
+ """
+ if self.exponent == 0:
+ return str(self.x)
+ else:
+ return "%s^%s * %s" % (self.p, self.exponent, self.x)
+
+ def reduce(self, prec):
+ """
+ Return this element reduced modulo ``p^prec``
+
+ INPUT:
+
+ - ``prec`` -- a relative integer
+
+ TESTS::
+
+ sage: from sage.rings.padics.lattice_precision import pRational
+ sage: x = pRational(2, 1234567); x
+ 1234567
+ sage: x.reduce(12)
+ 1671
+
+ sage: x = pRational(2, 1234/567); x
+ 1234/567
+ sage: x.reduce(12)
+ 190
+ """
+ if prec is Infinity:
+ return self
+ x = self.x
+ exp = self.exponent
+ if x.parent() is ZZ:
+ if prec > exp:
+ x = x % (self.p ** (prec-exp))
+ else:
+ x = 0
+ elif x.parent() is QQ:
+ num = x.numerator()
+ denom = x.denominator()
+ valdenom = denom.valuation(self.p)
+ denom /= self.p ** valdenom
+ exp -= valdenom
+ modulo = self.p ** (prec - exp)
+ # probably we should use Newton iteration instead
+ # (but it is actually slower for now - Python implementation)
+ _, inv, _ = denom.xgcd(modulo)
+ x = (num*inv) % modulo
+ if self.x == 0:
+ val = Infinity
+ else:
+ val = self._valuation
+ return self.__class__(self.p, x, exp, valuation=val)
+
+ def reduce_relative(self, prec):
+ """
+ Return this element reduced modulo ``p^n`` where ``n = prec + val(x)``
+
+ INPUT:
+
+ - ``prec`` -- a nonnegative integer
+
+ TESTS::
+
+ sage: from sage.rings.padics.lattice_precision import pRational
+ sage: x = pRational(2, 1234567); x
+ 1234567
+ sage: x.reduce_relative(12)
+ 1671
+
+ sage: x = pRational(2, 1234/567); x
+ 1234/567
+ sage: x.reduce_relative(12)
+ 190
+ """
+ v = self.valuation()
+ if v is Infinity:
+ return self
+ return self.reduce(prec+v)
+
+ def normalize(self):
+ """
+ Normalize this element, i.e. write it as ``p^v * u`` where
+ ``u`` is coprime to ``p``
+
+ TESTS::
+
+ sage: from sage.rings.padics.lattice_precision import pRational
+ sage: x = pRational(2, 123456, 7); x
+ 2^7 * 123456
+ sage: x.normalize(); x
+ 2^13 * 1929
+ """
+ if self.x == 0:
+ self.exponent = 0
+ else:
+ val = self.valuation()
+ exp = self.exponent
+ self.x /= self.p ** (val-exp)
+ if self.x in ZZ:
+ self.x = ZZ(self.x)
+ self.exponent = val
+
+ def valuation(self):
+ """
+ Return the ``p``-adic valuation of this element
+
+ TESTS::
+
+ sage: from sage.rings.padics.lattice_precision import pRational
+ sage: x = pRational(2, 123456, 7); x
+ 2^7 * 123456
+ sage: x.valuation()
+ 13
+ """
+ if self._valuation is None:
+ valx = self.x.valuation(self.p)
+ self._valuation = self.exponent + valx
+ return self._valuation
+
+ def is_p_power(self):
+ """
+ Return true if this element is a power of ``p``
+
+ TESTS::
+
+ sage: from sage.rings.padics.lattice_precision import pRational
+ sage: x = pRational(2, 1024, 2); x
+ 2^2 * 1024
+ sage: x.is_p_power()
+ True
+
+ sage: y = pRational(2, 123456, 7); y
+ 2^7 * 123456
+ sage: y.is_p_power()
+ False
+ """
+ self.normalize()
+ return self.x == 1
+
+ def is_zero(self):
+ """
+ Return true if this element vanishes
+
+ TESTS::
+
+ sage: from sage.rings.padics.lattice_precision import pRational
+ sage: x = pRational(2, 123456, 7); x
+ 2^7 * 123456
+ sage: x.is_zero()
+ False
+
+ sage: (x-x).is_zero()
+ True
+ """
+ return self.x == 0
+
+ def __add__(self, other):
+ """
+ Return the sum of ``self`` and ``other``
+
+ TESTS::
+
+ sage: from sage.rings.padics.lattice_precision import pRational
+ sage: x = pRational(2, 123456, 7); x
+ 2^7 * 123456
+ sage: y = pRational(2, 891011, 12); y
+ 2^12 * 891011
+ sage: x + y
+ 2^7 * 28635808
+ """
+ p = self.p
+ sexp = self.exponent
+ oexp = other.exponent
+ if self._valuation is None or other._valuation is None:
+ val = None
+ elif self._valuation < other._valuation:
+ val = self._valuation
+ elif self._valuation > other._valuation:
+ val = other._valuation
+ else:
+ val = None
+ if sexp < oexp:
+ return self.__class__(p, self.x + other.x * p**(oexp-sexp), sexp, valuation=val)
+ else:
+ return self.__class__(p, self.x * p**(sexp-oexp) + other.x, oexp, valuation=val)
+
+ def __sub__(self, other):
+ """
+ Return the subtraction of ``self`` by ``other``
+
+ TESTS::
+
+ sage: from sage.rings.padics.lattice_precision import pRational
+ sage: x = pRational(2, 123456, 7); x
+ 2^7 * 123456
+ sage: y = pRational(2, 891011, 12); y
+ 2^12 * 891011
+ sage: x - y
+ 2^7 * -28388896
+ """
+ p = self.p
+ sexp = self.exponent
+ oexp = other.exponent
+ if self._valuation is None or other._valuation is None:
+ val = None
+ elif self._valuation < other._valuation:
+ val = self._valuation
+ elif self._valuation > other._valuation:
+ val = other._valuation
+ else:
+ val = None
+ if sexp < oexp:
+ return self.__class__(p, self.x - other.x * p**(oexp-sexp), sexp, valuation=val)
+ else:
+ return self.__class__(p, self.x * p**(sexp-oexp) - other.x, oexp, valuation=val)
+
+ def __neg__(self):
+ """
+ Return the opposite of this element
+
+ TESTS::
+
+ sage: from sage.rings.padics.lattice_precision import pRational
+ sage: x = pRational(2, 123456, 7); x
+ 2^7 * 123456
+ sage: -x
+ 2^7 * -123456
+ """
+ return self.__class__(self.p, -self.x, self.exponent, valuation=self._valuation)
+
+ def __mul__(self, other):
+ """
+ Return the product of ``self`` and ``other``
+
+ TESTS::
+
+ sage: from sage.rings.padics.lattice_precision import pRational
+ sage: x = pRational(2, 123456, 7); x
+ 2^7 * 123456
+ sage: y = pRational(2, 891011, 12); y
+ 2^12 * 891011
+ sage: x * y
+ 2^19 * 110000654016
+ """
+ if self._valuation is None or other._valuation is None:
+ val = None
+ else:
+ val = self._valuation + other._valuation
+ return self.__class__(self.p, self.x * other.x, self.exponent + other.exponent, valuation=val)
+
+ def __div__(self, other):
+ """
+ Return the quotient of ``self`` by ``other``
+
+ TESTS::
+
+ sage: from sage.rings.padics.lattice_precision import pRational
+ sage: x = pRational(2, 123456, 7); x
+ 2^7 * 123456
+ sage: y = pRational(2, 891011, 12); y
+ 2^12 * 891011
+ sage: x / y
+ 2^-5 * 123456/891011
+ """
+ if self._valuation is None or other._valuation is None:
+ val = None
+ else:
+ val = self._valuation - other._valuation
+ return self.__class__(self.p, self.x / other.x, self.exponent - other.exponent, valuation=val)
+
+ def __lshift__(self, n):
+ """
+ Return the product of this element by ``p^n``
+
+ INPUT:
+
+ - ``n`` -- a relative integer
+
+ TESTS::
+
+ sage: from sage.rings.padics.lattice_precision import pRational
+ sage: x = pRational(2, 123456, 7); x
+ 2^7 * 123456
+ sage: x << 10
+ 2^17 * 123456
+ """
+ if self._valuation is None:
+ val = None
+ else:
+ val = self._valuation + n
+ return self.__class__(self.p, self.x, self.exponent + n, valuation=val)
+
+ def __rshift__(self, n):
+ """
+ Return the quotient of this element by ``p^n``
+
+ INPUT:
+
+ - ``n`` -- a relative integer
+
+ TESTS::
+
+ sage: from sage.rings.padics.lattice_precision import pRational
+ sage: x = pRational(2, 123456, 7); x
+ 2^7 * 123456
+ sage: x >> 10
+ 2^-3 * 123456
+ """
+ return self << (-n)
+
+ def unit_part(self):
+ """
+ Return the unit part of this element, that is the part ``u``
+ in the writing ``u * p^v`` with ``u`` coprime to ``p``
+
+ TESTS::
+
+ sage: from sage.rings.padics.lattice_precision import pRational
+ sage: x = pRational(2, 123456, 7); x
+ 2^7 * 123456
+ sage: x.unit_part()
+ 1929
+ """
+ if self.is_zero():
+ raise ValueError("the unit part of zero is not defined")
+ p = self.p
+ val = self.valuation()
+ x = self.x / (p ** (val-self.exponent))
+ return self.__class__(p, x, 0, valuation=0)
+
+ def xgcd(self,other):
+ """
+ Return the gcd of ``self`` and ``other`` together with two
+ element ``u`` and ``v`` such that ``u*self + v*other = gcd``
+
+ The ``gcd`` is normalized so that it is a power of ``p``
+
+ TESTS::
+
+ sage: from sage.rings.padics.lattice_precision import pRational
+ sage: x = pRational(2, 123456, 7); x
+ 2^7 * 123456
+ sage: y = pRational(2, 891011, 12); y
+ 2^12 * 891011
+
+ sage: d, u, v = x.xgcd(y)
+ sage: d
+ 2^7 * 32
+ sage: d.normalize(); d
+ 2^12 * 1
+
+ sage: u*x + v*y
+ 2^7 * 32
+ """
+ p = self.p
+ sexp = self.exponent
+ oexp = other.exponent
+ if sexp < oexp:
+ a = ZZ(self.x)
+ b = ZZ(other.x * (p ** (oexp-sexp)))
+ exp = sexp
+ else:
+ a = ZZ(self.x * (p ** (sexp-oexp)))
+ b = ZZ(other.x)
+ exp = oexp
+ d, u, v = a.xgcd(b)
+ if self._valuation is None or other._valuation is None:
+ val = None
+ else:
+ val = min(self._valuation, other._valuation)
+ d = self.__class__(p, d, exp, valuation=val)
+ u = self.__class__(p, u)
+ v = self.__class__(p, v)
+ return d, u, v
+
+ def value(self):
+ """
+ Return this element as a rational number
+
+ TESTS::
+
+ sage: from sage.rings.padics.lattice_precision import pRational
+ sage: x = pRational(2, 123456, 7); x
+ 2^7 * 123456
+ sage: x.value()
+ 15802368
+ """
+ return (self.p ** self.exponent) * self.x
+
+ def list(self, prec):
+ """
+ Return the list of the digits of this element (written in radix
+ ``p``) up to position ``prec``.
+
+ The first zeros are omitted.
+
+ TESTS::
+
+ sage: from sage.rings.padics.lattice_precision import pRational
+ sage: x = pRational(2, 123456); x
+ 123456
+ sage: x.list(5)
+ []
+ sage: x.list(20)
+ [1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0]
+
+ sage: y = pRational(2, 123/456); y
+ 41/152
+ sage: y.list(10)
+ [1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1]
+ """
+ if self.x not in ZZ:
+ self = self.reduce(prec)
+ val = self.valuation()
+ p = self.p
+ x = ZZ(self.x * p**(self.exponent - val))
+ l = [ ]; i = val
+ while i < prec:
+ x, digit = x.quo_rem(p)
+ l.append(digit)
+ i += 1
+ return l
+
+
+# Helper function
+#################
+
+def list_of_padics(elements):
+ """
+ Convert a list of p-adic composed elements (as polynomials, matrices)
+ to a list of weak refererences of their p-adic coefficients.
+
+ This is an helper function for the methods :meth:`precision_lattice`
+
+ TESTS::
+
+ sage: from sage.rings.padics.lattice_precision import list_of_padics
+ sage: R = ZpLC(2)
+ sage: M = random_matrix(R,2,2)
+ sage: list_of_padics(M)
+ [<weakref at 0x...; to 'pAdicLatticeCapElement' at 0x...>,
+ <weakref at 0x...; to 'pAdicLatticeCapElement' at 0x...>,
+ <weakref at 0x...; to 'pAdicLatticeCapElement' at 0x...>,
+ <weakref at 0x...; to 'pAdicLatticeCapElement' at 0x...>]
+ """
+ from sage.rings.padics.padic_lattice_element import pAdicLatticeElement
+ if isinstance(elements, pAdicLatticeElement):
+ return [ weakref.ref(elements) ]
+ if not isinstance(elements, list):
+ elements = list(elements)
+ ans = [ ]
+ for x in elements:
+ ans += list_of_padics(x)
+ return ans
+
+def format_history(tme, status, timings):
+ """
+ Return a formated output for the history.
+
+ This is an helper function for the methods :meth:`history`.
+
+ TESTS::
+
+ sage: from sage.rings.padics.lattice_precision import format_history
+ sage: format_history(1.23456789, ['o','o','o','o','o','o','~','o','o'], true)
+ '1.234568s oooooo~oo'
+ sage: format_history(1.23456789, ['o','o','o','o','o','o','~','o','o'], false)
+ 'oooooo~oo'
+
+ sage: format_history(12.3456789, ['o','o','o','o','o','o','~','o','o'], true)
+ ' >= 10s oooooo~oo'
+ sage: format_history(10^(-10), ['o','o','o','o','o','o','~','o','o'], true)
+ ' --- oooooo~oo'
+ sage: format_history(-1, ['o','o','o','o','o','o','~','o','o'], true)
+ ' Timings oooooo~oo'
+ """
+ status = ''.join(status)
+ if timings:
+ if tme < 0:
+ s = " Timings "
+ elif tme < 0.000001:
+ s = " --- "
+ elif tme >= 10:
+ s = " >= 10s "
+ else:
+ s = "%.6fs" % tme
+ return s + " " + status
+ else:
+ return status
+
+
+class DifferentialPrecisionGeneric(UniqueRepresentation, SageObject):
+ """
+ A generic class for precision objects obtained by automatic
+ differentiation
+ """
+ def __init__(self, p, type, label):
+ """
+ Initialize this precision module
+
+ INPUT:
+
+ - ``p`` -- a prime number
+
+ - ``type`` -- either ``lattice`` or ``module``
+
+ - ``label`` -- a string, the label of the parents to which belong
+ the elements tracked by this precision module
+
+ NOTE:
+
+ The precision module is automatically initialized at the
+ creation of the parent.
+
+ TESTS::
+
+ sage: R = ZpLC(2, label='init') # indirect doctest
+ sage: prec = R.precision()
+ sage: prec
+ Precision lattice on 0 object (label: init)
+ sage: prec._type
+ 'lattice'
+ sage: prec.label()
+ 'init'
+ """
+ self._p = p
+ self._label = label
+ self._type = type
+ self._elements = [ ] # Probably better to use a double chained list
+ self._matrix = { }
+ self._marked_for_deletion = [ ]
+ self._approx_zero = pRational(p, ZZ(0))
+ self._thresold_deleti