summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorXavier Caruso <xavier.caruso@univ-rennes1.fr>2018-01-17 18:05:46 +0100
committerXavier Caruso <xavier.caruso@univ-rennes1.fr>2018-01-17 18:05:46 +0100
commitb0a8b0b1c1d1fef6d89dc38f440c34e44b5be677 (patch)
tree8413e2b2e52e78935b87687423a6966fdbcc373e
parentImplementation of ZpLF (diff)
Doctest for ZpLF
-rw-r--r--src/sage/rings/padics/factory.py19
-rw-r--r--src/sage/rings/padics/lattice_precision.py289
-rw-r--r--src/sage/rings/padics/padic_base_leaves.py3
-rw-r--r--src/sage/rings/padics/padic_lattice_element.py70
4 files changed, 372 insertions, 9 deletions
diff --git a/src/sage/rings/padics/factory.py b/src/sage/rings/padics/factory.py
index 0b38892..2781a32 100644
--- a/src/sage/rings/padics/factory.py
+++ b/src/sage/rings/padics/factory.py
@@ -1297,7 +1297,7 @@ def QpLC(p, prec = None, *args, **kwds):
sage: R = QpLC(2)
sage: R
- 2-adic Field with lattice precision
+ 2-adic Field with lattice-cap precision
"""
return Qp(p, prec, 'lattice-cap', *args, **kwds)
@@ -2787,6 +2787,23 @@ def ZpLC(p, prec=None, *args, **kwds):
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,
diff --git a/src/sage/rings/padics/lattice_precision.py b/src/sage/rings/padics/lattice_precision.py
index e7762cc..86554d6 100644
--- a/src/sage/rings/padics/lattice_precision.py
+++ b/src/sage/rings/padics/lattice_precision.py
@@ -1412,23 +1412,149 @@ class PrecisionModule(DifferentialPrecisionGeneric):
self._thresold = 1
def internal_prec(self):
+ """
+ Return the relative precision at which computations has handled
+ internally
+
+ It is slightly greater than the actual precision and increases
+ a bit (at a logarithmic rate) when new elements are created
+ and/or computed
+
+ EXAMPLES::
+
+ sage: R = ZpLF(5, prec=20, label='internal_prec')
+ sage: prec = R.precision()
+
+ sage: prec.internal_prec()
+ 25
+
+ sage: L = [ R.random_element() for _ in range(50) ]
+ sage: prec.internal_prec()
+ 28
+ """
return self._internal_prec
def dimension(self):
+ """
+ Return the dimension of this precision module
+
+ EXAMPLES:
+
+ In general, the dimension increases by 1 when a new
+ element with a given precision is created::
+
+ sage: R = ZpLF(2, label='dimension')
+ sage: prec = R.precision()
+
+ sage: prec.dimension()
+ 0
+ sage: x = R.random_element(prec=10)
+ sage: prec.dimension()
+ 1
+ sage: y = R.random_element(prec=10)
+ sage: prec.dimension()
+ 2
+
+ However in general it does not increase while
+ doing computations::
+
+ sage: u = x + y
+ sage: v = x^2 + 3*y + x*y + y^3
+ sage: prec.dimension()
+ 2
+
+ How course, it may also decrease when a sufficient
+ number of variables are collected::
+
+ sage: del x, y, u
+ sage: prec.del_elements()
+ sage: prec.dimension()
+ 1
+
+ sage: del v
+ sage: prec.del_elements()
+ sage: prec.dimension()
+ 0
+ """
if len(self._elements) == 0:
return 0
return len(self._matrix[self._elements[-1]])
def is_lattice(self):
+ """
+ Return ``True`` if this precision module is a lattice
+ (i.e. has maximal dimension)
+
+ EXAMPLES::
+
+ sage: R = ZpLF(2, label='is_lattice')
+ sage: prec = R.precision()
+
+ sage: x = R(1,10)
+ sage: y = R(1,5)
+ sage: prec.is_lattice()
+ True
+
+ sage: u = x + y
+ sage: prec.is_lattice()
+ False
+
+ .. SEEALSO::
+
+ :meth:`dimension`
+ """
return self.dimension() == len(self._elements)
def new_element(self, x, dx, bigoh, dx_mode='linear_combinaison'):
+ """
+ Update the lattice when a new element is created.
+
+ This function is not meant to be called manually.
+ It is automatically called by the parent when a new
+ element is created.
+
+ INPUT:
+
+ - ``x`` -- the newly created element
+
+ - ``dx`` -- a dictionary representing the differential of ``x``
+
+ - ``dx_mode`` -- a string, either ``linear_combinaison`` (the default)
+ or ``values``
+
+ - ``capped`` -- a boolean, whether this element has been capped
+ according to the parent's cap
+
+ If ``dx_mode`` is ``linear_combinaison``, the dictionary ``dx``
+ encodes the expression of the differential of ``x``.
+ For example, if ``x`` was defined as ``x = y*z`` then:
+
+ .. MATH::
+
+ dx = y dz + z dy
+
+ and the corresponding dictionary is ``{z: y, y: z}`` (except
+ that the keys are not the elements themselves but weak references
+ to them).
+
+ If ``dx_mode`` is ``values``, the dictionary ``dx`` directly
+ specifies the entries that have to stored in the precision module.
+ This mode is only used for multiple conversion between different
+ parents (see :meth:`multiple_conversion`).
+
+ TESTS::
+
+ sage: R = ZpLF(2)
+ sage: x = R.random_element()
+ sage: y = R.random_element()
+ sage: z = x*y # indirect doctest
+ """
self.del_elements()
# We first increase the internal prec
self._count += 1
if self._count > self._thresold:
- self._internal_prec += 2
+ self._internal_prec += 1
self._thresold *= self._p
tme = walltime()
@@ -1474,6 +1600,38 @@ class PrecisionModule(DifferentialPrecisionGeneric):
def mark_for_deletion(self, ref):
+ """
+ Mark an element for deletion.
+
+ This function is not meant to be called manually.
+ It is automatically called by the garbage collection when
+ an element is collected.
+
+ INPUT:
+
+ - ``ref`` -- a weak reference to the destroyed element
+
+ NOTE::
+
+ This method may do not update the precision module.
+ The actual update is performed when the method :meth:`del_elements`
+ is called. This is automatically done at the creation of a new
+ element but can be done manually as well.
+
+ EXAMPLES::
+
+ sage: R = ZpLF(2, label='markdel')
+ sage: prec = R.precision()
+ sage: x = R(1,10)
+ sage: prec
+ Precision module on 1 object (label: markdel)
+ sage: del x # indirect doctest: x is here marked for deletion
+ sage: prec
+ Precision module on 1 object (label: markdel)
+ sage: prec.del_elements() # x is indeed deleted
+ sage: prec
+ Precision module on 0 object (label: markdel)
+ """
tme = walltime()
try:
index = self._index(ref)
@@ -1500,6 +1658,40 @@ class PrecisionModule(DifferentialPrecisionGeneric):
def del_elements(self, thresold=None):
+ """
+ Erase columns of the lattice precision matrix corresponding to
+ elements which are marked for deletion and reduce the matrix
+ in order to keep it in echelon form.
+
+ INPUT:
+
+ - ``thresold`` -- an integer or ``None`` (default: ``None``):
+ a column whose distance to the right at greater than the
+ thresold is not erased
+
+ EXAMPLES::
+
+ sage: R = ZpLF(2, label='delelts')
+ sage: prec = R.precision()
+
+ sage: x = R(1,10)
+ sage: prec
+ Precision module on 1 object (label: delelts)
+ sage: prec.precision_lattice()
+ [1024]
+
+ sage: del x
+ sage: prec
+ Precision module on 1 object (label: delelts)
+ sage: prec.precision_lattice()
+ [1024]
+
+ sage: prec.del_elements()
+ sage: prec
+ Precision module on 0 object (label: delelts)
+ sage: prec.precision_lattice()
+ []
+ """
p = self._p
n = len(self._elements)
@@ -1579,12 +1771,103 @@ class PrecisionModule(DifferentialPrecisionGeneric):
self._absolute_precisions[ref] = min( [ c.valuation() for c in col ] )
def precision_absolute(self, x):
+ """
+ Return the absolute precision of the given element
+
+ INPUT:
+
+ - ``x`` -- the element whose absolute precision is requested
+
+ NOTE:
+
+ The absolute precision is obtained by projecting the precision
+ module onto the line of coordinate ``dx``
+
+ NOTE:
+
+ This function is not meant to be called directly.
+ You should prefer call the method :meth:`precision_absolute`
+ of ``x`` instead.
+
+ EXAMPLES::
+
+ sage: R = ZpLF(2)
+ sage: prec = R.precision()
+
+ sage: x = R(1,10); x
+ 1 + O(2^10)
+ sage: y = R(1,5); y
+ 1 + O(2^5)
+ sage: z = x + y; z
+ 2 + O(2^5)
+ sage: z.precision_absolute() # indirect doctest
+ 5
+
+ In some cases, the absolute precision returned by this function
+ may be infinite::
+
+ sage: y = R(1)
+ sage: prec.precision_absolute(y)
+ +Infinity
+
+ However calling the method :meth:`absolute_precision` of the
+ element itself reintroduces a cap::
+
+ sage: y.precision_absolute()
+ 20
+ """
ref = weakref.ref(x)
if not self._absolute_precisions.has_key(ref):
self._compute_precision_absolute(ref)
return self._absolute_precisions[ref]
def precision_lattice(self, elements=None):
+ """
+ Return a matrix representing the precision lattice on a
+ subset of elements.
+
+ INPUT:
+
+ - ``elements`` -- a list of elements or ``None`` (default: ``None``)
+
+ EXAMPLES::
+
+ sage: R = ZpLF(2, label='preclattice')
+ sage: prec = R.precision()
+ sage: x = R(1,10); y = R(1,5)
+ sage: prec.precision_lattice()
+ [1024 0]
+ [ 0 32]
+
+ sage: u = x + y
+ sage: v = x - y
+ sage: prec.precision_lattice([u,v])
+ [ 32 2016]
+ [ 0 2048]
+
+ If the precision module does not project to a lattice,
+ an error is raised
+
+ sage: prec.precision_lattice([x,y,u,v])
+ Traceback (most recent call last):
+ ...
+ PrecisionError: the differential is not surjective
+
+ Here is another example with matrices::
+
+ sage: M = matrix(R, 2, 2, [R(3,5),R(7,5),R(1,5),R(11,1)])
+ sage: N = M^10
+
+ The next syntax provides as easy way to select an interesting
+ subset of variables (the selected subset consists of the four
+ entries of the matrix ``N``)::
+
+ sage: prec.precision_lattice(N)
+ [ 2048 512 28160 230400]
+ [ 0 2048 14336 258048]
+ [ 0 0 65536 65536]
+ [ 0 0 0 262144]
+ """
if elements is None:
elements = self._elements
else:
@@ -1607,6 +1890,10 @@ class PrecisionModule(DifferentialPrecisionGeneric):
n = len(elements)
if len(M.pivots()) < n:
raise PrecisionError("the differential is not surjective")
+ for i in range(n):
+ v = M[i,i].valuation(self._p)
+ M[i,i] = self._p ** v
+ M.echelonize()
M = M.submatrix(0,0,n,n)
if val < 0:
M *= self._p ** val
diff --git a/src/sage/rings/padics/padic_base_leaves.py b/src/sage/rings/padics/padic_base_leaves.py
index 08d5fb2..fdd2460 100644
--- a/src/sage/rings/padics/padic_base_leaves.py
+++ b/src/sage/rings/padics/padic_base_leaves.py
@@ -1177,7 +1177,6 @@ class pAdicLatticeGeneric(pAdicGeneric):
return ans
-
class pAdicRingLattice(pAdicLatticeGeneric, pAdicRingBaseGeneric):
"""
An implementation of the `p`-adic integers with lattice precision
@@ -1281,7 +1280,7 @@ class pAdicRingLattice(pAdicLatticeGeneric, pAdicRingBaseGeneric):
x = ZZ.random_element(p**cap)
v = x.valuation(p)
if prec is None and v > 0:
- x += p**prec * ZZ.random_element(p**v)
+ x += p**cap * ZZ.random_element(p**v)
return self._element_class(self, x, prec=prec)
diff --git a/src/sage/rings/padics/padic_lattice_element.py b/src/sage/rings/padics/padic_lattice_element.py
index 7e92f9d..0ba4ded 100644
--- a/src/sage/rings/padics/padic_lattice_element.py
+++ b/src/sage/rings/padics/padic_lattice_element.py
@@ -33,17 +33,30 @@ class pAdicLatticeElement(pAdicGenericElement):
self._value = x
else:
self._value = pRational(p, QQ(x))
- cap = self._declare_new_element(dx, prec, dx_mode)
+ trunc = self._declare_new_element(dx, prec, dx_mode)
if reduce:
- self._value = self._value.reduce(cap)
+ self._value = self._value.reduce(trunc)
self._approx_zero = pRational(p, 0)
self._approx_one = pRational(p, 1)
self._approx_minusone = pRational(p, -1)
def _declare_new_element(self, dx, prec, dx_mode):
"""
- Declare this element to the precision object
- and return the precision at which this element can be capped safely
+ Declare this element to the precision object and
+ return the precision at which this element can be truncated safely
+
+ Only for internal use!
+
+ TESTS::
+
+ sage: R = ZpLC(17)
+ sage: prec = R.precision()
+
+ sage: prec.del_elements()
+ sage: nb = prec.number_of_tracked_elements()
+ sage: x = R(1,10) # indirect doctest
+ sage: prec.number_of_tracked_elements() == nb + 1
+ True
"""
raise NotImplementError("implement this method in subclasses")
@@ -889,6 +902,23 @@ class pAdicLatticeElement(pAdicGenericElement):
class pAdicLatticeCapElement(pAdicLatticeElement):
def _declare_new_element(self, dx, prec, dx_mode):
+ """
+ Declare this element to the precision object and
+ return the precision at which this element can be truncated safely
+
+ Only for internal use!
+
+ TESTS::
+
+ sage: R = ZpLC(17)
+ sage: prec = R.precision()
+
+ sage: prec.del_elements()
+ sage: nb = prec.number_of_tracked_elements()
+ sage: x = R(1,10) # indirect doctest
+ sage: prec.number_of_tracked_elements() == nb + 1
+ True
+ """
parent = self._parent
cap = min(parent.precision_cap_absolute(), parent.precision_cap_relative() + self._value.valuation())
if prec is None or prec > cap:
@@ -921,6 +951,23 @@ class pAdicLatticeCapElement(pAdicLatticeElement):
class pAdicLatticeFloatElement(pAdicLatticeElement):
def _declare_new_element(self, dx, prec, dx_mode):
+ """
+ Declare this element to the precision object and
+ return the precision at which this element can be truncated safely
+
+ Only for internal use!
+
+ TESTS::
+
+ sage: R = ZpLF(17)
+ sage: prec = R.precision()
+
+ sage: prec.del_elements()
+ sage: nb = prec.number_of_tracked_elements()
+ sage: x = R(1,10) # indirect doctest
+ sage: prec.number_of_tracked_elements() == nb + 1
+ True
+ """
self._precision.new_element(self, dx, bigoh=prec, dx_mode=dx_mode)
cap = self._precision.internal_prec() + self._value.valuation()
if prec is None:
@@ -929,5 +976,18 @@ class pAdicLatticeFloatElement(pAdicLatticeElement):
return min(cap,prec)
def _is_exact_zero(self):
- return self._value.is_zero() and self._precision.precision_absolute(self) is Infinity
+ """
+ Return ``True`` if this element is exactly zero
+
+ EXAMPLES::
+
+ sage: R = ZpLF(5)
+ sage: R(0)._is_exact_zero()
+ True
+ sage: R(0,10)._is_exact_zero()
+ False
+ sage: R(1)._is_exact_zero()
+ False
+ """
+ return self._value.is_zero() and self._precision.precision_absolute(self) is Infinity