From f94651ea2c2f0112e277f90bafda389f4527e4a8 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Fri, 4 Mar 2016 21:08:05 -0600 Subject: [PATCH 001/406] Caching alphacheck and some other micro-optimizations for LS paths. --- src/sage/combinat/crystals/littelmann_path.py | 29 +++++++++---------- .../root_system/root_lattice_realizations.py | 13 +++++---- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/sage/combinat/crystals/littelmann_path.py b/src/sage/combinat/crystals/littelmann_path.py index 0797003bb5f..2a90817a09e 100644 --- a/src/sage/combinat/crystals/littelmann_path.py +++ b/src/sage/combinat/crystals/littelmann_path.py @@ -303,12 +303,12 @@ def compress(self): return self q = [] curr = self.value[0] - for i in range(1,len(self.value)): - if positively_parallel_weights(curr,self.value[i]): - curr = curr + self.value[i] + for v in self.value[1:]: + if positively_parallel_weights(curr, v): + curr = curr + v else: q.append(curr) - curr = self.value[i] + curr = v q.append(curr) return self.parent()(tuple(q)) @@ -328,9 +328,9 @@ def split_step(self, which_step, r): sage: b.split_step(0,1/3) (1/3*Lambda[1] + 1/3*Lambda[2], 2/3*Lambda[1] + 2/3*Lambda[2]) """ - assert which_step in range(len(self.value)) + assert 0 <= which_step and which_step <= len(self.value) v = self.value[which_step] - return self.parent()(self.value[:which_step]+tuple([r*v,(1-r)*v])+self.value[which_step+1:]) + return self.parent()(self.value[:which_step] + (r*v,(1-r)*v) + self.value[which_step+1:]) def reflect_step(self, which_step, i): r""" @@ -346,7 +346,7 @@ def reflect_step(self, which_step, i): (2*Lambda[1] - Lambda[2],) """ assert i in self.index_set() - assert which_step in range(len(self.value)) + assert 0 <= which_step and which_step <= len(self.value) return self.parent()(self.value[:which_step]+tuple([self.value[which_step].simple_reflection(i)])+self.value[which_step+1:]) def _string_data(self, i): @@ -366,7 +366,7 @@ def _string_data(self, i): sage: b.f(1).f(2)._string_data(2) ((0, -1, -1),) """ - if len(self.value) == 0: + if not self.value: return () # get the i-th simple coroot alv = self.value[0].parent().alphacheck()[i] @@ -376,10 +376,10 @@ def _string_data(self, i): minima_pos = [] ps = 0 psmin = 0 - for ix in range(len(steps)): - ps = ps + steps[ix] + for ix, step in enumerate(steps): + ps = ps + step if ps < psmin: - minima_pos.append((ix,ps,steps[ix])) + minima_pos.append((ix,ps,step)) psmin = ps return tuple(minima_pos) @@ -449,7 +449,7 @@ def e(self, i, power=1, to_string_end=False, length_only=False): assert i in self.index_set() data = self._string_data(i) # compute the minimum i-height M on the path - if len(data) == 0: + if not data: M = 0 else: M = data[-1][1] @@ -508,10 +508,9 @@ def dualize(self): (-Lambda[1] + 1/2*Lambda[2], Lambda[1] - 1/2*Lambda[2]) (-Lambda[1] + 1/2*Lambda[2], Lambda[1] - 1/2*Lambda[2]) (-2*Lambda[1] + Lambda[2],) (2*Lambda[1] - Lambda[2],) """ - if len(self.value) == 0: + if not self.value: return self - dual_path = [-v for v in self.value] - dual_path.reverse() + dual_path = [-v for v in reversed(self.value)] return self.parent()(tuple(dual_path)) def f(self, i, power=1, to_string_end=False, length_only=False): diff --git a/src/sage/combinat/root_system/root_lattice_realizations.py b/src/sage/combinat/root_system/root_lattice_realizations.py index 1fae1573a31..4f5fa1e99b8 100644 --- a/src/sage/combinat/root_system/root_lattice_realizations.py +++ b/src/sage/combinat/root_system/root_lattice_realizations.py @@ -1265,13 +1265,14 @@ def simple_coroots(self): # break some doctests return self.cache_simple_coroots + @cached_method def alphacheck(self): r""" - Returns the family `( \alpha^\vee_i)_{i\in I}` of the simple - coroots, with the extra feature that, for simple irreducible + Return the family `(\alpha^\vee_i)_{i \in I}` of the simple + coroots, with the extra feature that, for simple irreducible root systems, `\alpha^\vee_0` yields the coroot associated to - the opposite of the highest root (caveat: for non simply laced - root systems, this is not the opposite of the highest coroot!) + the opposite of the highest root (caveat: for non-simply-laced + root systems, this is not the opposite of the highest coroot!). EXAMPLES:: @@ -1295,8 +1296,8 @@ def alphacheck(self): """ if self.root_system.is_finite() and self.root_system.is_irreducible(): - return Family(self.index_set(), self.simple_coroot, \ - hidden_keys = [0], hidden_function = lambda i: - self.cohighest_root()) + return Family(self.index_set(), self.simple_coroot, + hidden_keys=[0], hidden_function=lambda i: - self.cohighest_root()) else: return self.simple_coroots() From 468864d8c08774d2fdf4b4c30ab0e7e93c1cbb58 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Thu, 11 Aug 2016 11:07:31 +0200 Subject: [PATCH 002/406] recognizeable series --- src/sage/combinat/recognizable_series.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/sage/combinat/recognizable_series.py diff --git a/src/sage/combinat/recognizable_series.py b/src/sage/combinat/recognizable_series.py new file mode 100644 index 00000000000..e69de29bb2d From b1d64af52b0ffbc384cbc780710005133c6584ee Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Thu, 11 Aug 2016 19:26:36 +0200 Subject: [PATCH 003/406] first draft --- src/sage/combinat/all.py | 1 + src/sage/combinat/recognizable_series.py | 455 +++++++++++++++++++++++ 2 files changed, 456 insertions(+) diff --git a/src/sage/combinat/all.py b/src/sage/combinat/all.py index 56134dc3a08..21d1a714500 100644 --- a/src/sage/combinat/all.py +++ b/src/sage/combinat/all.py @@ -178,6 +178,7 @@ ['automata', 'transducers']) # Binary Recurrence Sequences from binary_recurrence_sequences import BinaryRecurrenceSequence +lazy_import('sage.combinat.recognizable_series', 'RecognizableSeriesSpace') # Six Vertex Model lazy_import('sage.combinat.six_vertex_model', 'SixVertexModel') diff --git a/src/sage/combinat/recognizable_series.py b/src/sage/combinat/recognizable_series.py index e69de29bb2d..7a3009da057 100644 --- a/src/sage/combinat/recognizable_series.py +++ b/src/sage/combinat/recognizable_series.py @@ -0,0 +1,455 @@ +r""" +Recognizable Series + + +Various +======= + +REFERENCES: + +.. [BR2010] Jean Berstel, Christophe Reutenauer, + *Noncommutative Rational Series With Applications*, + Cambridge, 2010. + +AUTHORS: + +- Daniel Krenn (2016) + +ACKNOWLEDGEMENT: + +- Daniel Krenn is supported by the + Austrian Science Fund (FWF): P 24644-N26. + + +Classes and Methods +=================== +""" +#***************************************************************************** +# Copyright (C) 2016 Daniel Krenn +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# http://www.gnu.org/licenses/ +#***************************************************************************** + +from sage.misc.cachefunc import cached_method +from sage.structure.element import Element + + +def lazy_dict(data, **kwds): + if isinstance(data, dict): + return lazy_dict_from_dict(data, **kwds) + elif isinstance(data, (tuple, list)): + return lazy_dict_from_list(data, **kwds) + else: + raise NotImplementedError('TODO') + +class lazy_dict_generic(object): + + def __init__(self, cache=None, value=None, keys=None): + if cache is None: + cache = dict() + self.cache = cache + if value is not None: + self.value = value + if keys is not None: + self.keys = keys + + def __repr__(self): + if hasattr(self, 'keys'): + return '{' + ', '.join('{}: {}'.format(key, self.cache[key]) + for key in keys) + '}' + else: + return repr(self.cache) + + def __getitem__(self, key): + # TODO: reload... + if hasattr(self, 'value'): + return self.value(self.cache[key]) + else: + return self.cache[key] + + def __iter__(self): + return iter(self.cache) + + def items(self): + return iter(self.cache.iteritems()) + + def values(self): + return iter(self.cache.itervalues()) + +class lazy_dict_from_dict(lazy_dict_generic): + def __init__(self, data, key=None, **kwds): + super(lazy_dict_from_dict, self).__init__(**kwds) + if key is None: + key = lambda k: k + self.cache.update((key(k), v) for k, v in data.iteritems()) # TODO: make lazy + +class lazy_dict_from_list(lazy_dict_generic): + def __init__(self, data, key=None, **kwds): + super(lazy_dict_from_list, self).__init__(**kwds) + if key is None: + key = lambda k: k + self.cache.update((key(k), v) for k, v in enumerate(data)) # TODO: make lazy + + +class RecognizableSeries(Element): + + def __init__(self, parent, mu, left=None, right=None, transpose=False): + r""" + A recognizable series. + + TODO-INPUT: + + - ``parent`` -- an instance of :class:`kRegularSequenceSpace`. + + - ``matrices`` -- a tuple or other iterable of square matrices, + all of which have the same dimension. + + - ``initial`` -- (default: ``None``) a vector. + When evaluating the sequence, this vector is multiplied + from the left to the matrix product. If ``None``, then this + multiplication is skipped. + + - ``selection`` -- (default: ``None``) a vector. + When evaluating the sequence, this vector is multiplied + from the left to the matrix product. If ``None``, then this + multiplication is skipped. + + - ``output_function`` -- (default: ``None``) a function, which is + applied after evaluating the sequence. This may be used to + extract the value of a 1x1 matrix. + + - ``transpose`` -- (default: ``False``) a boolean. If set, then + each of the ``matrices``. Additionally the vectors ``initial`` + and ``selection`` are switched and (if possible) + transposed as well. + + EXAMPLES:: + + sage: Rec = RecognizableSeriesSpace(ZZ, [0, 1]) + sage: Rec((Matrix([[3, 6], [0, 1]]), Matrix([[0, -6], [1, 5]])), + ....: vector([0, 1]), vector([1, 0]), + ....: transpose=True) + [1] + 3*[01] + [10] + 5*[11] + 9*[001] + 3*[010] + ... + + Using an output function:: + + sage: Rec = RecognizableSeriesSpace( + ....: ZZ, [0, 1], output_function=lambda o: o[0, 0]) + sage: Rec((Matrix([[3, 6], [0, 1]]), Matrix([[0, -6], [1, 5]])), + ....: Matrix([[0, 1]]), Matrix([[1], [0]]), + ....: transpose=True) + [1] + 3*[01] + [10] + 5*[11] + 9*[001] + 3*[010] + ... + + .. SEEALSO:: + + :doc:`k-regular sequence `, + :class:`kRegularSequenceSpace`. + + TESTS:: + + sage: M0 = Matrix([[1, 0], [0, 1]]) + sage: M1 = Matrix([[0, -1], [1, 2]]) + sage: S = Rec(mu=(M0, M1)) # not tested + """ + super(RecognizableSeries, self).__init__(parent=parent) + + from sage.sets.family import Family + + def tr(M): + try: + return M.transpose() + except AttributeError: + return M + + A = self.parent().alphabet() + if isinstance(mu, (list, tuple)): + if len(A) != len(mu): + raise ValueError('TODO') + mu = dict(zip(A, mu)) + mu = Family(mu) + # TODO some check that alphabet is correct + #if A != self.mu: + # raise ValueError('TODO') + + #self.k = len(self.matrices) + #self.d = self.matrices[0].nrows() + #if not all(M.dimensions() == (self.d, self.d) for M in self.matrices): + # raise ValueError # TODO + + if not transpose: + self.left = left + self.mu = mu + self.right = right + else: + self.left = tr(right) + self.mu = mu.map(tr) + self.right = tr(left) + + + def _repr_(self): + r""" + Return a representation string of this recognizable series. + + OUTPUT: + + A string + + TESTS:: + + sage: Rec = RecognizableSeriesSpace(ZZ, [0, 1]) + sage: S = Rec((Matrix([[3, 6], [0, 1]]), Matrix([[0, -6], [1, 5]])), + ....: vector([0, 1]), vector([1, 0]), transpose=True) + sage: repr(S) # indirect doctest + '[1] + 3*[01] + [10] + 5*[11] + 9*[001] + 3*[010] + ...' + """ + from itertools import islice + A = self.parent()._algebra_ + B = A.basis() + return repr(sum((c * w for c, w in islice(self, 10)), + A.zero())) + ' + ...' + + + @cached_method + def __getitem__(self, w): + r""" + Return the coefficient to word `w` of this sequence. + + INPUT: + + - ``n`` -- a nonnegative integer. + + OUTPUT: + + An element of the universe of the sequence. + + EXAMPLES:: + + sage: Rec = RecognizableSeriesSpace(ZZ, [0, 1]) + sage: W = Rec.indices() + sage: S = Rec((Matrix([[1, 0], [0, 1]]), Matrix([[0, -1], [1, 2]])), + ....: left=vector([0, 1]), right=vector([1, 0])) + sage: S[W(7.digits(2))] + 3 + """ + result = self._product_of_matrices_(w) + if self.left is not None: + result = self.left * result + if self.right is not None: + result = result * self.right + return self.parent()._output_function_(result) + + + @cached_method + def _mu_empty_(self): + eps = self.parent().indices()() + try: + return self.mu[eps] + except KeyError: + return next(iter(self.mu.values())).parent().one() + + + @cached_method + def _product_of_matrices_(self, w): + r""" + Return the product of matrices according to the `k`-ary + digit expansion of `m`. + + INPUT: + + - ``m`` -- a nonnegative integer. + + OUTPUT: + + A matrix. + + TESTS:: + + sage: Rec = RecognizableSeriesSpace(ZZ, [0, 1]) + sage: W = Rec.indices() + sage: M0 = Matrix([[1, 0], [0, 1]]) + sage: M1 = Matrix([[0, -1], [1, 2]]) + sage: S = Rec((M0, M1)) + sage: S._product_of_matrices_(W([0])) == M0 + True + sage: S._product_of_matrices_(W([1])) == M1 + True + sage: S._product_of_matrices_(W(3.digits(2))) == M1^2 + True + + :: + + sage: S._product_of_matrices_(-1) + Traceback (most recent call last): + ... + ValueError: m=-1 is not a nonnegative integer. + """ + if w not in self.parent().indices(): + raise ValueError('TODO') + from sage.misc.misc_c import prod + return prod(tuple(self.mu[a] for a in w), z=self._mu_empty_()) + + + def __iter__(self): + r""" + Return an iterator. + + EXAMPLES:: + + sage: Rec = RecognizableSeriesSpace(ZZ, [0, 1]) + sage: S = Rec((Matrix([[1, 0], [0, 1]]), Matrix([[0, -1], [1, 2]])), + ....: left=vector([0, 1]), right=vector([1, 0])) + sage: from itertools import islice + sage: tuple(islice(S, 10)) + ((1, [1]), + (1, [01]), + (1, [10]), + (2, [11]), + (1, [001]), + (1, [010]), + (2, [011]), + (1, [100]), + (2, [101]), + (2, [110])) + + TESTS:: + + sage: it = iter(S) + sage: iter(it) is it + True + sage: iter(S) is not it + True + """ + # TODO self == 0: return iter() + A = self.parent()._algebra_ + B = A.basis() + return iter((self[w], B[w]) + for w in self.parent().indices() if self[w] != 0) + + +from sage.structure.unique_representation import UniqueRepresentation +from sage.structure.parent import Parent + +class RecognizableSeriesSpace(UniqueRepresentation, Parent): + + Element = RecognizableSeries + + @staticmethod + def __classcall__(cls, + coefficients=None, alphabet=None, + indices=None, algebra=None, + output_function=None, category=None): + r""" + Normalizes the input in order to ensure a unique + representation. + + For more information see :class:`kRegularSequenceSpace`. + + TESTS:: + + sage: Rec = RecognizableSeriesSpace(ZZ, [0, 1]) + sage: Rec.category() + Category of set algebras over Integer Ring + + """ + if algebra is None: + if indices is None: + if alphabet is None: + raise ValueError('TODO') + from sage.combinat.words.words import Words + indices = Words(alphabet, infinite=False) + from sage.combinat.words.word_options import WordOptions + WordOptions(identifier='') + if coefficients is None: + raise ValueError('TODO') + algebra = indices.algebra(coefficients) + def key_to_cmp(a, b, key): + return (key(a) > key(b)) - (key(a) < key(b)) + algebra.print_options( + prefix='', + generator_cmp=lambda a, b: key_to_cmp(a, b, key=lambda k: (len(k), k))) + + from sage.categories.sets_cat import Sets + category = category or algebra.category() + + return super(RecognizableSeriesSpace, cls).__classcall__( + cls, algebra, output_function, category) + + + def __init__(self, algebra, output_function, category): + r""" + The space of `k`-regular Sequences over the given ``universe``. + + INPUT: + + - ``k`` -- an integer at least `2` specifying the base. + + - ``universe`` -- (default: ``None``) a object (e.g. a SageMath parent) + in which the entries of a sequence live. + If ``None``, then the integer ring `\ZZ` is used. + + - ``category`` -- (default: ``None``) the category of the + sequence space. If ``None``, then the category of + :class:`~sage.categories.sets_cat.Sets` is used. + + EXAMPLES:: + + sage: S1 = RecognizableSeriesSpace(ZZ, [0, 1]) + sage: S1 + Space of recognizable series on {0, 1} with coefficients in Integer Ring + sage: S2 = RecognizableSeriesSpace(coefficients=ZZ, alphabet=[0, 1]) + sage: S2 + Space of recognizable series on {0, 1} with coefficients in Integer Ring + sage: S3 = RecognizableSeriesSpace(ZZ, indices=Words([0, 1], infinite=False)) + sage: S3 + Space of recognizable series on {0, 1} with coefficients in Integer Ring + sage: S4 = RecognizableSeriesSpace(algebra=Words([0, 1], infinite=False).algebra(ZZ)) + sage: S4 + Space of recognizable series on {0, 1} with coefficients in Integer Ring + sage: S1 is S2 is S3 is S4 + True + + + .. SEEALSO:: + + :doc:`k-regular sequence `, + :class:`kRegularSequence`. + """ + self._algebra_ = algebra + if output_function is None: + self._output_function_ = lambda o: o + else: + self._output_function_ = output_function + super(RecognizableSeriesSpace, self).__init__( + category=category, base=algebra.base()) + + + def alphabet(self): + return self._algebra_.indices().alphabet() + + def indices(self): + return self._algebra_.indices() + + def coefficients(self): + return self.base() + + def _repr_(self): + r""" + Return a representation string of this `k`-regular sequence space. + + OUTPUT: + + A string + + TESTS:: + + sage: repr(RecognizableSeriesSpace(ZZ, [0, 1])) # indirect doctest + 'Space of recognizable series on {0, 1} with coefficients in Integer Ring' + """ + return 'Space of recognizable series on {} ' \ + 'with coefficients in {}'.format(self.alphabet(), + self.coefficients()) + From 4443035ec18177b32d7ba69ff3e857b1890dfa7c Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Fri, 12 Aug 2016 16:49:35 +0200 Subject: [PATCH 004/406] remove lazy_dict --- src/sage/combinat/recognizable_series.py | 57 ------------------------ 1 file changed, 57 deletions(-) diff --git a/src/sage/combinat/recognizable_series.py b/src/sage/combinat/recognizable_series.py index 7a3009da057..b1e19591c5d 100644 --- a/src/sage/combinat/recognizable_series.py +++ b/src/sage/combinat/recognizable_series.py @@ -38,63 +38,6 @@ from sage.structure.element import Element -def lazy_dict(data, **kwds): - if isinstance(data, dict): - return lazy_dict_from_dict(data, **kwds) - elif isinstance(data, (tuple, list)): - return lazy_dict_from_list(data, **kwds) - else: - raise NotImplementedError('TODO') - -class lazy_dict_generic(object): - - def __init__(self, cache=None, value=None, keys=None): - if cache is None: - cache = dict() - self.cache = cache - if value is not None: - self.value = value - if keys is not None: - self.keys = keys - - def __repr__(self): - if hasattr(self, 'keys'): - return '{' + ', '.join('{}: {}'.format(key, self.cache[key]) - for key in keys) + '}' - else: - return repr(self.cache) - - def __getitem__(self, key): - # TODO: reload... - if hasattr(self, 'value'): - return self.value(self.cache[key]) - else: - return self.cache[key] - - def __iter__(self): - return iter(self.cache) - - def items(self): - return iter(self.cache.iteritems()) - - def values(self): - return iter(self.cache.itervalues()) - -class lazy_dict_from_dict(lazy_dict_generic): - def __init__(self, data, key=None, **kwds): - super(lazy_dict_from_dict, self).__init__(**kwds) - if key is None: - key = lambda k: k - self.cache.update((key(k), v) for k, v in data.iteritems()) # TODO: make lazy - -class lazy_dict_from_list(lazy_dict_generic): - def __init__(self, data, key=None, **kwds): - super(lazy_dict_from_list, self).__init__(**kwds) - if key is None: - key = lambda k: k - self.cache.update((key(k), v) for k, v in enumerate(data)) # TODO: make lazy - - class RecognizableSeries(Element): def __init__(self, parent, mu, left=None, right=None, transpose=False): From 185789b5c4ef462b547a536ed0a02b77ebe283a2 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Fri, 12 Aug 2016 18:15:46 +0200 Subject: [PATCH 005/406] many improvments, manly docstrings --- src/sage/combinat/recognizable_series.py | 255 ++++++++++++++++------- 1 file changed, 183 insertions(+), 72 deletions(-) diff --git a/src/sage/combinat/recognizable_series.py b/src/sage/combinat/recognizable_series.py index b1e19591c5d..815c8d909b2 100644 --- a/src/sage/combinat/recognizable_series.py +++ b/src/sage/combinat/recognizable_series.py @@ -44,31 +44,29 @@ def __init__(self, parent, mu, left=None, right=None, transpose=False): r""" A recognizable series. - TODO-INPUT: - - - ``parent`` -- an instance of :class:`kRegularSequenceSpace`. - - - ``matrices`` -- a tuple or other iterable of square matrices, - all of which have the same dimension. - - - ``initial`` -- (default: ``None``) a vector. - When evaluating the sequence, this vector is multiplied - from the left to the matrix product. If ``None``, then this - multiplication is skipped. - - - ``selection`` -- (default: ``None``) a vector. - When evaluating the sequence, this vector is multiplied - from the left to the matrix product. If ``None``, then this - multiplication is skipped. - - - ``output_function`` -- (default: ``None``) a function, which is - applied after evaluating the sequence. This may be used to - extract the value of a 1x1 matrix. - - - ``transpose`` -- (default: ``False``) a boolean. If set, then - each of the ``matrices``. Additionally the vectors ``initial`` - and ``selection`` are switched and (if possible) - transposed as well. + - ``parent`` -- an instance of :class:`RecognizableSeriesSpace`. + + - ``mu`` -- a family of square matrices, all of which have the + same dimension. The indices of this family are the alphabet. + ``mu`` may be a list or tuple of the same cardinality as the + alphabet as well. See :meth:`mu` for more details. + + - ``left`` -- (default: ``None``) a vector. When evaluating a + coefficient, this vector is multiplied from the left to the + matrix obtained from :meth:`mu` applying on a word. If + ``None``, then this multiplication is skipped. + See :meth:`left` for more details. + + - ``right`` -- (default: ``None``) a vector. When evaluating a + coefficient, this vector is multiplied from the right to the + matrix obtained from :meth:`mu` applying on a word. If + ``None``, then this multiplication is skipped. + See :meth:`right` for more details. + + - ``transpose`` -- (default: ``False``) a boolean. If set, + then each of the ``matrices`` is transposed. Additionally + the vectors ``left`` and ``right`` are switched and (if + possible) transposed as well. EXAMPLES:: @@ -89,25 +87,19 @@ def __init__(self, parent, mu, left=None, right=None, transpose=False): .. SEEALSO:: - :doc:`k-regular sequence `, - :class:`kRegularSequenceSpace`. + :doc:`recognizable series `, + :class:`RecognizableSeriesSpace`. TESTS:: sage: M0 = Matrix([[1, 0], [0, 1]]) sage: M1 = Matrix([[0, -1], [1, 2]]) - sage: S = Rec(mu=(M0, M1)) # not tested + sage: S = Rec(mu=(M0, M1)) # not tested TODO """ super(RecognizableSeries, self).__init__(parent=parent) from sage.sets.family import Family - def tr(M): - try: - return M.transpose() - except AttributeError: - return M - A = self.parent().alphabet() if isinstance(mu, (list, tuple)): if len(A) != len(mu): @@ -124,13 +116,50 @@ def tr(M): # raise ValueError # TODO if not transpose: - self.left = left - self.mu = mu - self.right = right + self._left_ = left + self._mu_ = mu + self._right_ = right else: - self.left = tr(right) - self.mu = mu.map(tr) - self.right = tr(left) + def tr(M): + try: + return M.transpose() + except AttributeError: + return M + + self._left_ = tr(right) + self._mu_ = mu.map(tr) + self._right_ = tr(left) + + + @property + def mu(self): + r""" + When evaluating a coefficient, this is applied on each letter + of a word; the result is a matrix. + This extends :meth:`mu` to words over the parent's + :meth:`~RecognizableSeriesSpace.alphabet`. + """ + return self._mu_ + + + @property + def left(self): + r""" + When evaluating a coefficient, this vector is multiplied from + the left to the matrix obtained from :meth:`mu` applied on a + word. + """ + return self._left_ + + + @property + def right(self): + r""" + When evaluating a coefficient, this vector is multiplied from + the right to the matrix obtained from :meth:`mu` applied on a + word. + """ + return self._right_ def _repr_(self): @@ -139,7 +168,7 @@ def _repr_(self): OUTPUT: - A string + A string. TESTS:: @@ -159,15 +188,17 @@ def _repr_(self): @cached_method def __getitem__(self, w): r""" - Return the coefficient to word `w` of this sequence. + Return the coefficient to word `w` of this series. INPUT: - - ``n`` -- a nonnegative integer. + - ``w`` -- a word over the parent's + :meth:`~RecognizableSeriesSpace.alphabet`. OUTPUT: - An element of the universe of the sequence. + An element in the parent's + :meth:`~RecognizableSeriesSpace.coefficients`. EXAMPLES:: @@ -178,7 +209,7 @@ def __getitem__(self, w): sage: S[W(7.digits(2))] 3 """ - result = self._product_of_matrices_(w) + result = self._mu_of_word_(w) if self.left is not None: result = self.left * result if self.right is not None: @@ -187,23 +218,48 @@ def __getitem__(self, w): @cached_method - def _mu_empty_(self): + def _mu_of_empty_word_(self): + r""" + Return :meth:`mu` applied on the empty word. + + OUTPUT: + + A matrix. + + TESTS:: + + sage: Rec = RecognizableSeriesSpace(ZZ, [0, 1]) + sage: W = Rec.indices() + sage: M0 = Matrix([[1, 0], [0, 1]]) + sage: M1 = Matrix([[0, -1], [1, 2]]) + sage: S = Rec({W([0]): M0, W([1]): M1}) + sage: S._mu_of_empty_word_() + [1 0] + [0 1] + sage: I = Matrix([[1, 0], [0, 1]]) + sage: T = Rec({W([]): I, W([0]): M0, W([1]): M1}) + sage: T._mu_of_empty_word_() + [1 0] + [0 1] + sage: _ is I + True + """ eps = self.parent().indices()() try: return self.mu[eps] except KeyError: - return next(iter(self.mu.values())).parent().one() + return next(iter(self.mu)).parent().one() @cached_method - def _product_of_matrices_(self, w): + def _mu_of_word_(self, w): r""" - Return the product of matrices according to the `k`-ary - digit expansion of `m`. + Return :meth:`mu` applied on the word `w`. INPUT: - - ``m`` -- a nonnegative integer. + - ``w`` -- a word over the parent's + :meth:`~RecognizableSeriesSpace.alphabet`. OUTPUT: @@ -216,16 +272,16 @@ def _product_of_matrices_(self, w): sage: M0 = Matrix([[1, 0], [0, 1]]) sage: M1 = Matrix([[0, -1], [1, 2]]) sage: S = Rec((M0, M1)) - sage: S._product_of_matrices_(W([0])) == M0 + sage: S._mu_of_word_(W([0])) == M0 True - sage: S._product_of_matrices_(W([1])) == M1 + sage: S._mu_of_word_(W([1])) == M1 True - sage: S._product_of_matrices_(W(3.digits(2))) == M1^2 + sage: S._mu_of_word_(W(3.digits(2))) == M1^2 True :: - sage: S._product_of_matrices_(-1) + sage: S._mu_of_word_(-1) Traceback (most recent call last): ... ValueError: m=-1 is not a nonnegative integer. @@ -233,12 +289,12 @@ def _product_of_matrices_(self, w): if w not in self.parent().indices(): raise ValueError('TODO') from sage.misc.misc_c import prod - return prod(tuple(self.mu[a] for a in w), z=self._mu_empty_()) + return prod(tuple(self.mu[a] for a in w), z=self._mu_of_empty_word_()) def __iter__(self): r""" - Return an iterator. + Return an iterator over pairs ``(coefficient, basis(index))``. EXAMPLES:: @@ -280,6 +336,7 @@ class RecognizableSeriesSpace(UniqueRepresentation, Parent): Element = RecognizableSeries + @staticmethod def __classcall__(cls, coefficients=None, alphabet=None, @@ -289,7 +346,7 @@ def __classcall__(cls, Normalizes the input in order to ensure a unique representation. - For more information see :class:`kRegularSequenceSpace`. + For more information see :class:`ReconizableSeriesSpace`. TESTS:: @@ -324,19 +381,29 @@ def key_to_cmp(a, b, key): def __init__(self, algebra, output_function, category): r""" - The space of `k`-regular Sequences over the given ``universe``. + The space of recognizable series on the given alphabet and + with the given coefficients. INPUT: - - ``k`` -- an integer at least `2` specifying the base. + - ``coefficients`` -- a (semi-)ring. - - ``universe`` -- (default: ``None``) a object (e.g. a SageMath parent) - in which the entries of a sequence live. - If ``None``, then the integer ring `\ZZ` is used. + - ``alphabet`` -- a tuple, list or totally ordered set. + If specified, then the ``indices`` are the + finite words over this ``alphabet``. - - ``category`` -- (default: ``None``) the category of the - sequence space. If ``None``, then the category of - :class:`~sage.categories.sets_cat.Sets` is used. + - ``indices`` -- a SageMath parent. + + - ``algebra`` -- a SageMath parent. + If specified, then ``coefficients`` + and ``indices`` are determined by this ``algebra``. + + - ``output_function`` -- (default: ``None'') If specified, + then this is applied on each coefficient. + + - ``category`` -- (default: ``None``) the category of this + space. If ``None``, then the category of the ``algebra`` + is used. EXAMPLES:: @@ -355,11 +422,10 @@ def __init__(self, algebra, output_function, category): sage: S1 is S2 is S3 is S4 True - .. SEEALSO:: - :doc:`k-regular sequence `, - :class:`kRegularSequence`. + :doc:`recognizable series `, + :class:`RecognizableSeries`. """ self._algebra_ = algebra if output_function is None: @@ -371,21 +437,66 @@ def __init__(self, algebra, output_function, category): def alphabet(self): + r""" + Return the alphabet of this recognizable series space. + + OUTPUT: + + A totally ordered set. + + EXAMPLES:: + + sage: RecognizableSeriesSpace(ZZ, [0, 1]).alphabet() + {0, 1} + + TESTS:: + + sage: type(RecognizableSeriesSpace(ZZ, [0, 1]).alphabet()) + + """ return self._algebra_.indices().alphabet() + def indices(self): + r""" + Return the indices of the recognizable series. + + OUTPUT: + + The set of finite words over the alphabet. + + EXAMPLES:: + + sage: RecognizableSeriesSpace(ZZ, [0, 1]).indices() + Finite words over {0, 1} + """ return self._algebra_.indices() + def coefficients(self): + r""" + Return the coefficients of this recognizable series space. + + OUTPUT: + + A (semi-)ring. + + EXAMPLES:: + + sage: RecognizableSeriesSpace(ZZ, [0, 1]).coefficients() + Integer Ring + """ return self.base() + def _repr_(self): r""" - Return a representation string of this `k`-regular sequence space. + Return a representation string of this recognizable sequence + space. OUTPUT: - A string + A string. TESTS:: @@ -394,5 +505,5 @@ def _repr_(self): """ return 'Space of recognizable series on {} ' \ 'with coefficients in {}'.format(self.alphabet(), - self.coefficients()) + self.coefficients()) From 3a06c693b9d9a50cc1577e15f4771b2ba95b6e1f Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Fri, 12 Aug 2016 20:00:41 +0200 Subject: [PATCH 006/406] prefix-closed sets --- src/sage/combinat/recognizable_series.py | 137 +++++++++++++++++++++++ 1 file changed, 137 insertions(+) diff --git a/src/sage/combinat/recognizable_series.py b/src/sage/combinat/recognizable_series.py index 815c8d909b2..17d3c1f2519 100644 --- a/src/sage/combinat/recognizable_series.py +++ b/src/sage/combinat/recognizable_series.py @@ -38,6 +38,143 @@ from sage.structure.element import Element +class PrefixClosedSet(object): + + def __init__(self, alphabet=None, words=None): + r""" + A prefix-closed set. + + Creation of this prefix-closed sets is interactive + iteratively. + + INPUT: + + - ``alphabet`` -- finite words over this ``alphabet`` + will be created. + + - ``words`` -- specify the finite words directly + (instead of via ``alphabet``). + + EXAMPLES:: + + sage: from sage.combinat.recognizable_series import PrefixClosedSet + sage: P = PrefixClosedSet(alphabet=[0, 1]); P + [word: ] + + See :meth:`populate_interactive` for further examples. + """ + if alphabet is not None: + from sage.combinat.words.words import Words + self.words = Words(alphabet, infinite=False) + else: + self.words = words + self.elements = [self.words([])] + + + def __repr__(self): + r""" + A representation string of this prefix-closed set + + OUTPUT: + + A string. + + EXAMPLES:: + + sage: from sage.combinat.recognizable_series import PrefixClosedSet + sage: P = PrefixClosedSet(alphabet=[0, 1]) + sage: repr(P) # indirect doctest + '[word: ]' + """ + return repr(self.elements) + + + def add(self, w, check=True): + r""" + Add a word to this prefix-closed set. + + INPUT: + + - ``w`` -- a word. + + - ``check`` -- (default: ``True``) if set, then it is verified + whether all proper prefixes of ``w`` are already in this + prefix-closed set. + + OUTPUT: + + Nothing, but a + :python:`RuntimeError` + is raised if the check fails. + + EXAMPLES:: + + sage: from sage.combinat.recognizable_series import PrefixClosedSet + sage: P = PrefixClosedSet(alphabet=[0, 1]) + sage: W = P.words + sage: P.add(W([0])); P + [word: , word: 0] + sage: P.add(W([0, 1])); P + [word: , word: 0, word: 01] + sage: P.add(W([1, 1])) + Traceback (most recent call last): + ... + ValueError: Cannot add as not all prefixes of 11 are included yet. + """ + if check and any(p not in self.elements + for p in w.prefixes_iterator() + if p != w): + raise ValueError('Cannot add as not all prefixes of ' + '{} are included yet.'.format(w)) + self.elements.append(w) + + + def populate_interactive(self): + r""" + Return an iterator over possible new elements. + + OUTPUT: + + An iterator. + + EXAMPLES:: + + sage: from sage.combinat.recognizable_series import PrefixClosedSet + sage: P = PrefixClosedSet(alphabet=[0, 1]); P + [word: ] + sage: for n, p in enumerate(P.populate_interactive()): + ....: print('{}?'.format(p)) + ....: if n in (0, 2, 3, 5): + ....: P.add(p) + ....: print('...added') + 0? + ...added + 1? + 00? + ...added + 01? + ...added + 000? + 001? + ...added + 010? + 011? + 0010? + 0011? + sage: P.elements + [word: , word: 0, word: 00, word: 01, word: 001] + """ + n = 0 + it = iter(self.words.iterate_by_length(1)) + while n < len(self.elements): + try: + nn = next(it) + yield self.elements[n] + nn #next(it) + except StopIteration: + n += 1 + it = iter(self.words.iterate_by_length(1)) + + class RecognizableSeries(Element): def __init__(self, parent, mu, left=None, right=None, transpose=False): From 6a045072c75ed4df2881151df1b042ce51e98b0f Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Mon, 15 Aug 2016 17:36:31 +0200 Subject: [PATCH 007/406] remove not needed iter(...) --- src/sage/combinat/recognizable_series.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/combinat/recognizable_series.py b/src/sage/combinat/recognizable_series.py index 17d3c1f2519..7c677aaefd0 100644 --- a/src/sage/combinat/recognizable_series.py +++ b/src/sage/combinat/recognizable_series.py @@ -165,14 +165,14 @@ def populate_interactive(self): [word: , word: 0, word: 00, word: 01, word: 001] """ n = 0 - it = iter(self.words.iterate_by_length(1)) + it = self.words.iterate_by_length(1) while n < len(self.elements): try: nn = next(it) yield self.elements[n] + nn #next(it) except StopIteration: n += 1 - it = iter(self.words.iterate_by_length(1)) + it = self.words.iterate_by_length(1) class RecognizableSeries(Element): From 2e94c8baf14b8268c948dea59373affb548fa5c4 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Mon, 15 Aug 2016 17:38:11 +0200 Subject: [PATCH 008/406] prefix_set --- src/sage/combinat/recognizable_series.py | 29 ++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/sage/combinat/recognizable_series.py b/src/sage/combinat/recognizable_series.py index 7c677aaefd0..c8af3689a19 100644 --- a/src/sage/combinat/recognizable_series.py +++ b/src/sage/combinat/recognizable_series.py @@ -175,6 +175,35 @@ def populate_interactive(self): it = self.words.iterate_by_length(1) + def prefix_set(self): + r""" + Return the prefix set corresponding to this prefix + closed set. + + OUTPUT: + + A list. + + EXAMPLES:: + + sage: from sage.combinat.recognizable_series import PrefixClosedSet + sage: P = PrefixClosedSet(alphabet=[0, 1]); P + [word: ] + sage: for n, p in enumerate(P.populate_interactive()): + ....: if n in (0, 1, 2, 4, 6): + ....: P.add(p) + sage: P + [word: , word: 0, word: 1, word: 00, word: 10, word: 000] + sage: P.prefix_set() + [word: 01, word: 11, word: 001, word: 100, + word: 101, word: 0000, word: 0001] + """ + return [p + a + for p in self.elements + for a in self.words.iterate_by_length(1) + if p + a not in self.elements] + + class RecognizableSeries(Element): def __init__(self, parent, mu, left=None, right=None, transpose=False): From 69059ff37ecf632b93eeac984da52efb8d9eaf8e Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Tue, 16 Aug 2016 11:18:48 +0200 Subject: [PATCH 009/406] method transposed --- src/sage/combinat/recognizable_series.py | 43 ++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/src/sage/combinat/recognizable_series.py b/src/sage/combinat/recognizable_series.py index c8af3689a19..e3ca5ba5732 100644 --- a/src/sage/combinat/recognizable_series.py +++ b/src/sage/combinat/recognizable_series.py @@ -495,6 +495,49 @@ def __iter__(self): for w in self.parent().indices() if self[w] != 0) + def transposed(self): + r""" + Return the transposed series. + + OUTPUT: + + A :class:`RecognizableSeries`. + + Each of the ``matrices`` is transposed. Additionally + the vectors ``left`` and ``right`` are switched and (if + possible) transposed as well. + + EXAMPLES:: + + sage: Rec = RecognizableSeriesSpace(ZZ, [0, 1]) + sage: S = Rec((Matrix([[3, 6], [0, 1]]), Matrix([[0, -6], [1, 5]])), + ....: vector([0, 1]), vector([1, 0]), transpose=True) + sage: S + [1] + 3*[01] + [10] + 5*[11] + 9*[001] + 3*[010] + + 15*[011] + [100] + 11*[101] + 5*[110] + ... + sage: S.mu[0], S.mu[1], S.left, S.right + ( + [3 0] [ 0 1] + [6 1], [-6 5], (1, 0), (0, 1) + ) + sage: T = S.transposed() + sage: T + [1] + [01] + 3*[10] + 5*[11] + [001] + 3*[010] + + 5*[011] + 9*[100] + 11*[101] + 15*[110] + ... + sage: T.mu[0], T.mu[1], T.left, T.right + ( + [3 6] [ 0 -6] + [0 1], [ 1 5], (0, 1), (1, 0) + ) + """ + def tr(M): + try: + return M.transpose() + except AttributeError: + return M + return self.parent()(self.mu.map(tr), + left=tr(self.right), + right=tr(self.left)) from sage.structure.unique_representation import UniqueRepresentation from sage.structure.parent import Parent From ca44fc4cf8ecffc581c57047ca6e15de4fa321c0 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Tue, 16 Aug 2016 11:19:51 +0200 Subject: [PATCH 010/406] doctest for PrefixClosedSet --- src/sage/combinat/recognizable_series.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/sage/combinat/recognizable_series.py b/src/sage/combinat/recognizable_series.py index e3ca5ba5732..a32fab7c9d3 100644 --- a/src/sage/combinat/recognizable_series.py +++ b/src/sage/combinat/recognizable_series.py @@ -62,6 +62,12 @@ def __init__(self, alphabet=None, words=None): [word: ] See :meth:`populate_interactive` for further examples. + + TESTS:: + + sage: P = PrefixClosedSet( + ....: words=Words([0, 1], infinite=False)); P + [word: ] """ if alphabet is not None: from sage.combinat.words.words import Words From cafa62c118a964cb884a23dfe778e6be9223113e Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Tue, 16 Aug 2016 11:20:26 +0200 Subject: [PATCH 011/406] rewrite creation of transpositioned series --- src/sage/combinat/recognizable_series.py | 24 ++++-------------------- 1 file changed, 4 insertions(+), 20 deletions(-) diff --git a/src/sage/combinat/recognizable_series.py b/src/sage/combinat/recognizable_series.py index a32fab7c9d3..6e48e260967 100644 --- a/src/sage/combinat/recognizable_series.py +++ b/src/sage/combinat/recognizable_series.py @@ -212,7 +212,7 @@ def prefix_set(self): class RecognizableSeries(Element): - def __init__(self, parent, mu, left=None, right=None, transpose=False): + def __init__(self, parent, mu, left=None, right=None): r""" A recognizable series. @@ -235,11 +235,6 @@ def __init__(self, parent, mu, left=None, right=None, transpose=False): ``None``, then this multiplication is skipped. See :meth:`right` for more details. - - ``transpose`` -- (default: ``False``) a boolean. If set, - then each of the ``matrices`` is transposed. Additionally - the vectors ``left`` and ``right`` are switched and (if - possible) transposed as well. - EXAMPLES:: sage: Rec = RecognizableSeriesSpace(ZZ, [0, 1]) @@ -287,20 +282,9 @@ def __init__(self, parent, mu, left=None, right=None, transpose=False): #if not all(M.dimensions() == (self.d, self.d) for M in self.matrices): # raise ValueError # TODO - if not transpose: - self._left_ = left - self._mu_ = mu - self._right_ = right - else: - def tr(M): - try: - return M.transpose() - except AttributeError: - return M - - self._left_ = tr(right) - self._mu_ = mu.map(tr) - self._right_ = tr(left) + self._left_ = left + self._mu_ = mu + self._right_ = right @property From b9b0aaf8206ed6c49dd1b4e943ec2ae1f3fc674a Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Tue, 16 Aug 2016 11:20:56 +0200 Subject: [PATCH 012/406] element constructor --- src/sage/combinat/recognizable_series.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/sage/combinat/recognizable_series.py b/src/sage/combinat/recognizable_series.py index 6e48e260967..4a4624682c1 100644 --- a/src/sage/combinat/recognizable_series.py +++ b/src/sage/combinat/recognizable_series.py @@ -706,3 +706,13 @@ def _repr_(self): 'with coefficients in {}'.format(self.alphabet(), self.coefficients()) + + def _element_constructor_(self, mu, + left=None, right=None, + transpose=False): + element = self.element_class(self, mu, left, right) + if transpose: + return element.transposed() + else: + return element + From aee2a42627ba4d3c1248ff9762b53df070e306bf Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Tue, 16 Aug 2016 13:48:32 +0200 Subject: [PATCH 013/406] zero (element constructor) --- src/sage/combinat/recognizable_series.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/sage/combinat/recognizable_series.py b/src/sage/combinat/recognizable_series.py index 4a4624682c1..201fce3d6f3 100644 --- a/src/sage/combinat/recognizable_series.py +++ b/src/sage/combinat/recognizable_series.py @@ -710,6 +710,25 @@ def _repr_(self): def _element_constructor_(self, mu, left=None, right=None, transpose=False): + r""" + + TESTS:: + + sage: Rec = RecognizableSeriesSpace(ZZ, [0, 1]) + sage: Rec.zero() + 0 + sage: type(_) + + """ + if isinstance(mu, int) and mu == 0: + from sage.matrix.constructor import Matrix + from sage.modules.free_module_element import vector + from sage.sets.family import Family + + return self.element_class( + self, Family(self.alphabet(), lambda a: Matrix()), + vector([]), vector([])) + element = self.element_class(self, mu, left, right) if transpose: return element.transposed() From c10a56f946a35cda7ec210cbeb95e1b18b576403 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Tue, 16 Aug 2016 13:48:58 +0200 Subject: [PATCH 014/406] minimization algorithm --- src/sage/combinat/recognizable_series.py | 94 ++++++++++++++++++++++++ 1 file changed, 94 insertions(+) diff --git a/src/sage/combinat/recognizable_series.py b/src/sage/combinat/recognizable_series.py index 201fce3d6f3..2af6e9701ef 100644 --- a/src/sage/combinat/recognizable_series.py +++ b/src/sage/combinat/recognizable_series.py @@ -528,6 +528,100 @@ def tr(M): return self.parent()(self.mu.map(tr), left=tr(self.right), right=tr(self.left)) + def _minimized_left_(self): + r""" + + TESTS:: + + sage: Rec = RecognizableSeriesSpace(ZZ, [0, 1]) + sage: S = Rec((Matrix([[0, 0], [0, 0]]), Matrix([[0, 0], [0, 0]])), + ....: vector([1, 1]), vector([1, 1])) + sage: M = S._minimized_left_() + sage: M.mu[0], M.mu[1], M.left, M.right + ([0], [0], (1), (2)) + sage: M = S.minimized() + sage: M.mu[0], M.mu[1], M.left, M.right + ([0], [0], (1), (2)) + + :: + + sage: S = Rec((Matrix([[1, 0], [0, 1]]), Matrix([[1, 0], [0, 1]])), + ....: vector([1, -1]), vector([1, 1]))._minimized_left_() + sage: S.mu[0], S.mu[1], S.left, S.right + ([1], [1], (1), (0)) + sage: M = S.minimized() + sage: M.mu[0], M.mu[1], M.left, M.right + ([], [], (), ()) + + sage: S = Rec((Matrix([[1, 0], [0, 1]]), Matrix([[1, 0], [0, 1]])), + ....: vector([1, 1]), vector([1, -1])) + sage: M = S._minimized_left_() + sage: M.mu[0], M.mu[1], M.left, M.right + ([1], [1], (1), (0)) + sage: M = S.minimized() + sage: M.mu[0], M.mu[1], M.left, M.right + ([], [], (), ()) + + sage: S = Rec((Matrix([[1, 0], [0, 1]]), Matrix([[1, 0], [0, 1]])), + ....: left=vector([0, 1]), right=vector([1, 0])) + sage: M = S._minimized_left_() + sage: M.mu[0], M.mu[1], M.left, M.right + ([1], [1], (1), (0)) + sage: M = S.minimized() + sage: M.mu[0], M.mu[1], M.left, M.right + ([], [], (), ()) + """ + from sage.matrix.constructor import Matrix + from sage.modules.free_module_element import vector + from sage.rings.integer_ring import ZZ + + pcs = PrefixClosedSet(self.parent().indices()) + left = self.left * self._mu_of_word_(pcs.elements[0]) + if left.is_zero(): + return self.parent().zero() + Left = [left] + for p in pcs.populate_interactive(): + left = self.left * self._mu_of_word_(p) + try: + Matrix(Left).solve_left(left) + except ValueError: + # no solution found + pcs.add(p) + Left.append(left) + P = pcs.elements + C = pcs.prefix_set() + + ML = Matrix(Left) + + def alpha(c): + return ML.solve_left(self.left * self._mu_of_word_(c)) + + def mu_prime_entry(a, p, q, iq): + c = p + a + if c == q: + return ZZ(1) + elif c in C: + return alpha(c)[iq] + else: + return ZZ(0) + + mu_prime = [] + for a in self.parent().alphabet(): + a = self.parent().indices()([a]) + M = [[mu_prime_entry(a, p, q, iq) for iq, q in enumerate(P)] + for p in P] + mu_prime.append(Matrix(M)) + + left_prime = vector([ZZ(1)] + (len(P)-1)*[ZZ(0)]) + if self.right is None: + right_prime = None + else: + right_prime = vector(self[p] for p in P) + + return self.parent().element_class( + self.parent(), mu_prime, left_prime, right_prime) + + from sage.structure.unique_representation import UniqueRepresentation from sage.structure.parent import Parent From 8a20128808cfd52deeea67d80f0e1fefa86da567 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Tue, 16 Aug 2016 13:49:09 +0200 Subject: [PATCH 015/406] interface for minimization --- src/sage/combinat/recognizable_series.py | 31 ++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/src/sage/combinat/recognizable_series.py b/src/sage/combinat/recognizable_series.py index 2af6e9701ef..57c7fab61cb 100644 --- a/src/sage/combinat/recognizable_series.py +++ b/src/sage/combinat/recognizable_series.py @@ -528,6 +528,37 @@ def tr(M): return self.parent()(self.mu.map(tr), left=tr(self.right), right=tr(self.left)) + + + def minimized(self): + r""" + + EXAMPLES:: + + sage: Rec = RecognizableSeriesSpace(ZZ, [0, 1]) + sage: S = Rec((Matrix([[3, 6], [0, 1]]), Matrix([[0, -6], [1, 5]])), + ....: vector([0, 1]), vector([1, 0]), + ....: transpose=True) + sage: S + [1] + 3*[01] + [10] + 5*[11] + 9*[001] + 3*[010] + + 15*[011] + [100] + 11*[101] + 5*[110] + ... + sage: M = S.minimized() + sage: M + sage: M.mu[0], M.mu[1], M.left, M.right + ( + [3 0] [ 0 1] + [6 1], [-6 5], (1, 0), (0, 1) + ) + """ + if self.left is None or self.right is None: + raise ValueError("Cannot minmize as 'left' or 'right' is None.") + return self._minimized_right_()._minimized_left_() + + + def _minimized_right_(self): + return self.transposed()._minimized_left_().transposed() + + def _minimized_left_(self): r""" From 0bfabe5075820626b2c950d79bc01c953c5e70b7 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Tue, 16 Aug 2016 13:49:44 +0200 Subject: [PATCH 016/406] is_trivial_zero --- src/sage/combinat/recognizable_series.py | 59 ++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/src/sage/combinat/recognizable_series.py b/src/sage/combinat/recognizable_series.py index 57c7fab61cb..484c36846f6 100644 --- a/src/sage/combinat/recognizable_series.py +++ b/src/sage/combinat/recognizable_series.py @@ -485,6 +485,65 @@ def __iter__(self): for w in self.parent().indices() if self[w] != 0) + def is_trivial_zero(self): + r""" + Return whether this recognizable series is trivially equal to + zero (without any :meth:`minimization `). + + EXAMPLES:: + + sage: Rec = RecognizableSeriesSpace(ZZ, [0, 1]) + sage: Rec((Matrix([[1, 0], [0, 1]]), Matrix([[1, 0], [0, 1]])), + ....: left=vector([0, 1]), right=vector([1, 0])).is_trivial_zero() + False + sage: Rec((Matrix([[1, 0], [0, 1]]), Matrix([[1, 0], [0, 1]])), + ....: left=vector([0, 0]), right=vector([1, 0])).is_trivial_zero() + True + sage: Rec((Matrix([[1, 0], [0, 1]]), Matrix([[1, 0], [0, 1]])), + ....: left=vector([0, 1]), right=vector([0, 0])).is_trivial_zero() + True + + The following two differ in the coefficient of the empty word:: + + sage: Rec((Matrix([[0, 0], [0, 0]]), Matrix([[0, 0], [0, 0]])), + ....: left=vector([0, 1]), right=vector([1, 0])).is_trivial_zero() + True + sage: Rec((Matrix([[0, 0], [0, 0]]), Matrix([[0, 0], [0, 0]])), + ....: left=vector([1, 1]), right=vector([1, 1])).is_trivial_zero() + False + + TESTS:: + + sage: Rec.zero().is_trivial_zero() + True + + The following are nonzero as the coefficient of the empty word + is nonzero:: + + sage: Rec((Matrix([[0, 0], [0, 0]]), Matrix([[0, 0], [0, 0]])), + ....: left=None, right=vector([1, 0])).is_trivial_zero() + False + sage: Rec((Matrix([[0, 0], [0, 0]]), Matrix([[0, 0], [0, 0]])), + ....: left=None, right=None).is_trivial_zero() + False + sage: Rec((Matrix([[0, 0], [0, 0]]), Matrix([[0, 0], [0, 0]])), + ....: left=vector([0, 1]), right=None).is_trivial_zero() + False + + The following is zero, but not trivially zero:: + + sage: S = Rec((Matrix([[1, 0], [0, 0]]), Matrix([[1, 0], [0, 0]])), + ....: left=vector([0, 1]), right=vector([1, 0])) + sage: S.is_trivial_zero() + False + sage: S.is_zero() + True + """ + return (self.left is not None and not self.left) or \ + (self.right is not None and not self.right) or \ + (all(not self.mu[a] for a in self.parent().alphabet()) and + not self[self.parent().indices()()]) + def transposed(self): r""" Return the transposed series. From bd8a472d7e97d059abe6452e254383e890a0305f Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Tue, 16 Aug 2016 13:49:52 +0200 Subject: [PATCH 017/406] nonzero --- src/sage/combinat/recognizable_series.py | 42 ++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/src/sage/combinat/recognizable_series.py b/src/sage/combinat/recognizable_series.py index 484c36846f6..80de94d9cf6 100644 --- a/src/sage/combinat/recognizable_series.py +++ b/src/sage/combinat/recognizable_series.py @@ -544,6 +544,48 @@ def is_trivial_zero(self): (all(not self.mu[a] for a in self.parent().alphabet()) and not self[self.parent().indices()()]) + + + def __nonzero__(self): + r""" + Return whether this recognizable series is nonzero. + + TESTS:: + + sage: Rec = RecognizableSeriesSpace(ZZ, [0, 1]) + sage: bool(Rec((Matrix([[1, 0], [0, 1]]), Matrix([[1, 0], [0, 1]])), + ....: left=vector([0, 1]), right=vector([1, 0]))) + False + sage: bool(Rec((Matrix([[0, 0], [0, 0]]), Matrix([[0, 0], [0, 0]])), + ....: left=vector([0, 1]), right=vector([1, 0]))) + False + sage: bool(Rec((Matrix([[1, 0], [0, 1]]), Matrix([[1, 0], [0, 1]])), + ....: left=vector([0, 0]), right=vector([1, 0]))) + False + sage: bool(Rec((Matrix([[1, 0], [0, 1]]), Matrix([[1, 0], [0, 1]])), + ....: left=vector([0, 1]), right=vector([0, 0]))) + False + + :: + + sage: S = Rec((Matrix([[1, 0], [0, 0]]), Matrix([[1, 0], [0, 0]])), + ....: left=vector([0, 1]), right=vector([1, 0])) + sage: bool(S) + False + sage: S # not tested + """ + if self.is_trivial_zero(): + return False + try: + M = self.minimized() + except ValueError: + pass + else: + if M.is_trivial_zero(): + return False + return True + + def transposed(self): r""" Return the transposed series. From 112daccd0035aff346065d4d5d5da3dfb8f849c8 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Tue, 16 Aug 2016 13:50:02 +0200 Subject: [PATCH 018/406] use __nonzero__ --- src/sage/combinat/recognizable_series.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/sage/combinat/recognizable_series.py b/src/sage/combinat/recognizable_series.py index 80de94d9cf6..ac12f6b68a1 100644 --- a/src/sage/combinat/recognizable_series.py +++ b/src/sage/combinat/recognizable_series.py @@ -334,6 +334,8 @@ def _repr_(self): sage: repr(S) # indirect doctest '[1] + 3*[01] + [10] + 5*[11] + 9*[001] + 3*[010] + ...' """ + if not self: + return '0' from itertools import islice A = self.parent()._algebra_ B = A.basis() @@ -478,7 +480,8 @@ def __iter__(self): sage: iter(S) is not it True """ - # TODO self == 0: return iter() + if not self: + return iter([]) A = self.parent()._algebra_ B = A.basis() return iter((self[w], B[w]) From e2e8885450684463318e94a2174a84fdc02215c2 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Tue, 16 Aug 2016 17:11:35 +0200 Subject: [PATCH 019/406] doctests for minimized --- src/sage/combinat/recognizable_series.py | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/sage/combinat/recognizable_series.py b/src/sage/combinat/recognizable_series.py index ac12f6b68a1..cc5ec9920c7 100644 --- a/src/sage/combinat/recognizable_series.py +++ b/src/sage/combinat/recognizable_series.py @@ -639,7 +639,9 @@ def minimized(self): EXAMPLES:: + sage: from itertools import islice, izip sage: Rec = RecognizableSeriesSpace(ZZ, [0, 1]) + sage: S = Rec((Matrix([[3, 6], [0, 1]]), Matrix([[0, -6], [1, 5]])), ....: vector([0, 1]), vector([1, 0]), ....: transpose=True) @@ -647,12 +649,26 @@ def minimized(self): [1] + 3*[01] + [10] + 5*[11] + 9*[001] + 3*[010] + 15*[011] + [100] + 11*[101] + 5*[110] + ... sage: M = S.minimized() - sage: M sage: M.mu[0], M.mu[1], M.left, M.right ( [3 0] [ 0 1] [6 1], [-6 5], (1, 0), (0, 1) ) + sage: all(c == d and v == w + ....: for (c, v), (d, w) in islice(izip(iter(S), iter(M)), 20)) + True + + sage: S = Rec((Matrix([[2, 0], [1, 1]]), Matrix([[2, 0], [2, 1]])), + ....: vector([1, 0]), vector([1, 1])) + sage: S + [] + 2*[0] + 2*[1] + 4*[00] + 4*[01] + 4*[10] + 4*[11] + + 8*[000] + 8*[001] + 8*[010] + ... + sage: M = S.minimized() + sage: M.mu[0], M.mu[1], M.left, M.right + ([2], [2], (1), (1)) + sage: all(c == d and v == w + ....: for (c, v), (d, w) in islice(izip(iter(S), iter(M)), 20)) + True """ if self.left is None or self.right is None: raise ValueError("Cannot minmize as 'left' or 'right' is None.") From a2c8e91ae33d9a5b2900e5ea846409f5bca878e4 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Tue, 16 Aug 2016 17:11:43 +0200 Subject: [PATCH 020/406] new repr --- src/sage/combinat/recognizable_series.py | 48 ++++++++++++++++++++---- 1 file changed, 41 insertions(+), 7 deletions(-) diff --git a/src/sage/combinat/recognizable_series.py b/src/sage/combinat/recognizable_series.py index cc5ec9920c7..22e47e2fc39 100644 --- a/src/sage/combinat/recognizable_series.py +++ b/src/sage/combinat/recognizable_series.py @@ -318,15 +318,20 @@ def right(self): return self._right_ - def _repr_(self): + def _repr_(self, latex=False): r""" - Return a representation string of this recognizable series. + A representation string for this recognizable series. + + INPUT: + + - ``latex`` -- (default: ``False``) a boolean. If set, then + LaTeX-output is returned. OUTPUT: A string. - TESTS:: + EXAMPLES:: sage: Rec = RecognizableSeriesSpace(ZZ, [0, 1]) sage: S = Rec((Matrix([[3, 6], [0, 1]]), Matrix([[0, -6], [1, 5]])), @@ -336,11 +341,40 @@ def _repr_(self): """ if not self: return '0' + from itertools import islice - A = self.parent()._algebra_ - B = A.basis() - return repr(sum((c * w for c, w in islice(self, 10)), - A.zero())) + ' + ...' + + if latex: + from sage.misc.latex import latex as latex_repr + f = latex_repr + times = ' ' + else: + f = repr + times = '*' + + def summand(c, w): + if c == 1: + return f(w) + return f(c) + times + f(w) + + s = ' + '.join(summand(c, w) + for c, w in islice(self, 10)) + s = s.replace('+ -', '- ') + return s + ' + ...' + + + def _latex_(self): + r""" + A LaTeX-representation string for this recognizable series. + + OUTPUT: + + A string. + + TESTS:: + + """ + return self._repr_(latex=True) @cached_method From 089a0eaff7c851b52be1abf512a1ac2e09d97727 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Tue, 16 Aug 2016 17:15:26 +0200 Subject: [PATCH 021/406] remove output_function --- src/sage/combinat/recognizable_series.py | 24 ++++-------------------- 1 file changed, 4 insertions(+), 20 deletions(-) diff --git a/src/sage/combinat/recognizable_series.py b/src/sage/combinat/recognizable_series.py index 22e47e2fc39..04ca10da9d1 100644 --- a/src/sage/combinat/recognizable_series.py +++ b/src/sage/combinat/recognizable_series.py @@ -243,15 +243,6 @@ def __init__(self, parent, mu, left=None, right=None): ....: transpose=True) [1] + 3*[01] + [10] + 5*[11] + 9*[001] + 3*[010] + ... - Using an output function:: - - sage: Rec = RecognizableSeriesSpace( - ....: ZZ, [0, 1], output_function=lambda o: o[0, 0]) - sage: Rec((Matrix([[3, 6], [0, 1]]), Matrix([[0, -6], [1, 5]])), - ....: Matrix([[0, 1]]), Matrix([[1], [0]]), - ....: transpose=True) - [1] + 3*[01] + [10] + 5*[11] + 9*[001] + 3*[010] + ... - .. SEEALSO:: :doc:`recognizable series `, @@ -406,7 +397,7 @@ def __getitem__(self, w): result = self.left * result if self.right is not None: result = result * self.right - return self.parent()._output_function_(result) + return result @cached_method @@ -819,7 +810,7 @@ class RecognizableSeriesSpace(UniqueRepresentation, Parent): def __classcall__(cls, coefficients=None, alphabet=None, indices=None, algebra=None, - output_function=None, category=None): + category=None): r""" Normalizes the input in order to ensure a unique representation. @@ -854,10 +845,10 @@ def key_to_cmp(a, b, key): category = category or algebra.category() return super(RecognizableSeriesSpace, cls).__classcall__( - cls, algebra, output_function, category) + cls, algebra, category) - def __init__(self, algebra, output_function, category): + def __init__(self, algebra, category): r""" The space of recognizable series on the given alphabet and with the given coefficients. @@ -876,9 +867,6 @@ def __init__(self, algebra, output_function, category): If specified, then ``coefficients`` and ``indices`` are determined by this ``algebra``. - - ``output_function`` -- (default: ``None'') If specified, - then this is applied on each coefficient. - - ``category`` -- (default: ``None``) the category of this space. If ``None``, then the category of the ``algebra`` is used. @@ -906,10 +894,6 @@ def __init__(self, algebra, output_function, category): :class:`RecognizableSeries`. """ self._algebra_ = algebra - if output_function is None: - self._output_function_ = lambda o: o - else: - self._output_function_ = output_function super(RecognizableSeriesSpace, self).__init__( category=category, base=algebra.base()) From 6b67851fba9f72e281580ea63fc08b3704c4112c Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Tue, 16 Aug 2016 17:16:48 +0200 Subject: [PATCH 022/406] adapt docstring of transposed --- src/sage/combinat/recognizable_series.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/sage/combinat/recognizable_series.py b/src/sage/combinat/recognizable_series.py index 04ca10da9d1..fbdf22e4fd5 100644 --- a/src/sage/combinat/recognizable_series.py +++ b/src/sage/combinat/recognizable_series.py @@ -623,8 +623,7 @@ def transposed(self): A :class:`RecognizableSeries`. Each of the ``matrices`` is transposed. Additionally - the vectors ``left`` and ``right`` are switched and (if - possible) transposed as well. + the vectors ``left`` and ``right`` are switched. EXAMPLES:: From 5cf8f564b8d071f18212cce2c21248498b7d5565 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Thu, 18 Aug 2016 16:58:50 +0200 Subject: [PATCH 023/406] split __normalize__ from __classcall__ (to deal better with inheritance) --- src/sage/combinat/recognizable_series.py | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/sage/combinat/recognizable_series.py b/src/sage/combinat/recognizable_series.py index fbdf22e4fd5..3292a0db6c4 100644 --- a/src/sage/combinat/recognizable_series.py +++ b/src/sage/combinat/recognizable_series.py @@ -806,7 +806,20 @@ class RecognizableSeriesSpace(UniqueRepresentation, Parent): @staticmethod - def __classcall__(cls, + def __classcall__(cls, *args, **kwds): + r""" + Prepare normalizing the input in order to ensure a + unique representation. + + For more information see :class:`ReconizableSeriesSpace` + and :meth:`__normalize__'. + """ + return super(RecognizableSeriesSpace, cls).__classcall__( + cls, *cls.__normalize__(*args, **kwds)) + + + @classmethod + def __normalize__(cls, coefficients=None, alphabet=None, indices=None, algebra=None, category=None): @@ -843,8 +856,7 @@ def key_to_cmp(a, b, key): from sage.categories.sets_cat import Sets category = category or algebra.category() - return super(RecognizableSeriesSpace, cls).__classcall__( - cls, algebra, category) + return (algebra, category) def __init__(self, algebra, category): From be013594f2a9f1e62f04c1613803079d7bbe4d13 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Thu, 18 Aug 2016 17:08:20 +0200 Subject: [PATCH 024/406] small rewrite in _repr_ --- src/sage/combinat/recognizable_series.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/combinat/recognizable_series.py b/src/sage/combinat/recognizable_series.py index 3292a0db6c4..5d44bf6c60f 100644 --- a/src/sage/combinat/recognizable_series.py +++ b/src/sage/combinat/recognizable_series.py @@ -345,8 +345,8 @@ def _repr_(self, latex=False): def summand(c, w): if c == 1: - return f(w) - return f(c) + times + f(w) + return '[{w}]'.format(w=f(w)) + return '{c}{times}[{w}]'.format(c=f(c), times=times, w=f(w)) s = ' + '.join(summand(c, w) for c, w in islice(self, 10)) @@ -509,7 +509,7 @@ def __iter__(self): return iter([]) A = self.parent()._algebra_ B = A.basis() - return iter((self[w], B[w]) + return iter((self[w], w) for w in self.parent().indices() if self[w] != 0) From b00d2606a3d11568f19bac41025c4e0545b6cfcc Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Thu, 18 Aug 2016 17:11:10 +0200 Subject: [PATCH 025/406] switch tuple entries in output of __iter__ --- src/sage/combinat/recognizable_series.py | 32 ++++++++++++------------ 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/sage/combinat/recognizable_series.py b/src/sage/combinat/recognizable_series.py index 5d44bf6c60f..aefbf7330ae 100644 --- a/src/sage/combinat/recognizable_series.py +++ b/src/sage/combinat/recognizable_series.py @@ -343,13 +343,13 @@ def _repr_(self, latex=False): f = repr times = '*' - def summand(c, w): + def summand(w, c): if c == 1: return '[{w}]'.format(w=f(w)) return '{c}{times}[{w}]'.format(c=f(c), times=times, w=f(w)) - s = ' + '.join(summand(c, w) - for c, w in islice(self, 10)) + s = ' + '.join(summand(w, c) + for w, c in islice(self, 10)) s = s.replace('+ -', '- ') return s + ' + ...' @@ -477,7 +477,7 @@ def _mu_of_word_(self, w): def __iter__(self): r""" - Return an iterator over pairs ``(coefficient, basis(index))``. + Return an iterator over pairs ``(index, coefficient)``. EXAMPLES:: @@ -485,17 +485,17 @@ def __iter__(self): sage: S = Rec((Matrix([[1, 0], [0, 1]]), Matrix([[0, -1], [1, 2]])), ....: left=vector([0, 1]), right=vector([1, 0])) sage: from itertools import islice - sage: tuple(islice(S, 10)) - ((1, [1]), - (1, [01]), - (1, [10]), - (2, [11]), - (1, [001]), - (1, [010]), - (2, [011]), - (1, [100]), - (2, [101]), - (2, [110])) + sage: list(islice(S, 10)) + [(1, 1), + (01, 1), + (10, 1), + (11, 2), + (001, 1), + (010, 1), + (011, 2), + (100, 1), + (101, 2), + (110, 2)] TESTS:: @@ -509,7 +509,7 @@ def __iter__(self): return iter([]) A = self.parent()._algebra_ B = A.basis() - return iter((self[w], w) + return iter((w, self[w]) for w in self.parent().indices() if self[w] != 0) From 0afeb61e903cd79ce7706836cf03ac1c6addde7e Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Thu, 18 Aug 2016 17:13:42 +0200 Subject: [PATCH 026/406] delete un-needed code --- src/sage/combinat/recognizable_series.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/sage/combinat/recognizable_series.py b/src/sage/combinat/recognizable_series.py index aefbf7330ae..b26946322a1 100644 --- a/src/sage/combinat/recognizable_series.py +++ b/src/sage/combinat/recognizable_series.py @@ -507,8 +507,6 @@ def __iter__(self): """ if not self: return iter([]) - A = self.parent()._algebra_ - B = A.basis() return iter((w, self[w]) for w in self.parent().indices() if self[w] != 0) @@ -573,7 +571,6 @@ def is_trivial_zero(self): not self[self.parent().indices()()]) - def __nonzero__(self): r""" Return whether this recognizable series is nonzero. From 715351984771c5257cbebab6e3617db88874b984 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Thu, 18 Aug 2016 21:22:15 +0200 Subject: [PATCH 027/406] change category --- src/sage/combinat/recognizable_series.py | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/sage/combinat/recognizable_series.py b/src/sage/combinat/recognizable_series.py index b26946322a1..2b296a95676 100644 --- a/src/sage/combinat/recognizable_series.py +++ b/src/sage/combinat/recognizable_series.py @@ -830,7 +830,7 @@ def __normalize__(cls, sage: Rec = RecognizableSeriesSpace(ZZ, [0, 1]) sage: Rec.category() - Category of set algebras over Integer Ring + Category of sets """ if algebra is None: @@ -851,7 +851,7 @@ def key_to_cmp(a, b, key): generator_cmp=lambda a, b: key_to_cmp(a, b, key=lambda k: (len(k), k))) from sage.categories.sets_cat import Sets - category = category or algebra.category() + category = category or Sets() return (algebra, category) @@ -978,6 +978,22 @@ def _repr_(self): self.coefficients()) + def zero(self): + """ + Return the zero of this recognizable series space. + + This can be removed once this recognizable series space is + at least an additive magma. + + EXAMPLES:: + + sage: Rec = RecognizableSeriesSpace(ZZ, [0, 1]) + sage: Rec.zero() + 0 + """ + return self(0) + + def _element_constructor_(self, mu, left=None, right=None, transpose=False): From 28158621e388b928b69d4ace8841db6ab8ff3706 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Thu, 18 Aug 2016 21:22:39 +0200 Subject: [PATCH 028/406] remove algebra and use indices and coefficients directly --- src/sage/combinat/recognizable_series.py | 61 ++++++++++-------------- 1 file changed, 26 insertions(+), 35 deletions(-) diff --git a/src/sage/combinat/recognizable_series.py b/src/sage/combinat/recognizable_series.py index 2b296a95676..31afdf37fb1 100644 --- a/src/sage/combinat/recognizable_series.py +++ b/src/sage/combinat/recognizable_series.py @@ -817,8 +817,8 @@ def __classcall__(cls, *args, **kwds): @classmethod def __normalize__(cls, - coefficients=None, alphabet=None, - indices=None, algebra=None, + coefficients=None, + alphabet=None, indices=None, category=None): r""" Normalizes the input in order to ensure a unique @@ -833,30 +833,24 @@ def __normalize__(cls, Category of sets """ - if algebra is None: - if indices is None: - if alphabet is None: - raise ValueError('TODO') - from sage.combinat.words.words import Words - indices = Words(alphabet, infinite=False) - from sage.combinat.words.word_options import WordOptions - WordOptions(identifier='') - if coefficients is None: + if indices is None: + if alphabet is None: raise ValueError('TODO') - algebra = indices.algebra(coefficients) - def key_to_cmp(a, b, key): - return (key(a) > key(b)) - (key(a) < key(b)) - algebra.print_options( - prefix='', - generator_cmp=lambda a, b: key_to_cmp(a, b, key=lambda k: (len(k), k))) + from sage.combinat.words.words import Words + indices = Words(alphabet, infinite=False) + from sage.combinat.words.word_options import WordOptions + WordOptions(identifier='') + + if coefficients is None: + raise ValueError('TODO') from sage.categories.sets_cat import Sets category = category or Sets() - return (algebra, category) + return (coefficients, indices, category) - def __init__(self, algebra, category): + def __init__(self, coefficients, indices, category): r""" The space of recognizable series on the given alphabet and with the given coefficients. @@ -865,19 +859,19 @@ def __init__(self, algebra, category): - ``coefficients`` -- a (semi-)ring. - - ``alphabet`` -- a tuple, list or totally ordered set. + - ``alphabet`` -- a tuple, list or + :class:`~sage.sets.totally_ordered_finite_set.TotallyOrderedFiniteSet`. If specified, then the ``indices`` are the finite words over this ``alphabet``. + ``alphabet`` and ``indices`` cannot be specified + at the same time. - - ``indices`` -- a SageMath parent. - - - ``algebra`` -- a SageMath parent. - If specified, then ``coefficients`` - and ``indices`` are determined by this ``algebra``. + - ``indices`` -- a SageMath-parent of finite words over an alphabet. + ``alphabet`` and ``indices`` cannot be specified + at the same time. - ``category`` -- (default: ``None``) the category of this - space. If ``None``, then the category of the ``algebra`` - is used. + space. EXAMPLES:: @@ -890,10 +884,7 @@ def __init__(self, algebra, category): sage: S3 = RecognizableSeriesSpace(ZZ, indices=Words([0, 1], infinite=False)) sage: S3 Space of recognizable series on {0, 1} with coefficients in Integer Ring - sage: S4 = RecognizableSeriesSpace(algebra=Words([0, 1], infinite=False).algebra(ZZ)) - sage: S4 - Space of recognizable series on {0, 1} with coefficients in Integer Ring - sage: S1 is S2 is S3 is S4 + sage: S1 is S2 is S3 True .. SEEALSO:: @@ -901,9 +892,9 @@ def __init__(self, algebra, category): :doc:`recognizable series `, :class:`RecognizableSeries`. """ - self._algebra_ = algebra + self._indices_ = indices super(RecognizableSeriesSpace, self).__init__( - category=category, base=algebra.base()) + category=category, base=coefficients) def alphabet(self): @@ -924,7 +915,7 @@ def alphabet(self): sage: type(RecognizableSeriesSpace(ZZ, [0, 1]).alphabet()) """ - return self._algebra_.indices().alphabet() + return self.indices().alphabet() def indices(self): @@ -940,7 +931,7 @@ def indices(self): sage: RecognizableSeriesSpace(ZZ, [0, 1]).indices() Finite words over {0, 1} """ - return self._algebra_.indices() + return self._indices_ def coefficients(self): From 5f06dac08b5a4b33c855303b4b27d4ef8aacbec0 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Fri, 19 Aug 2016 12:20:15 +0200 Subject: [PATCH 029/406] minor rewrite in _repr_ --- src/sage/combinat/recognizable_series.py | 32 ++++++++++++------------ 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/sage/combinat/recognizable_series.py b/src/sage/combinat/recognizable_series.py index 31afdf37fb1..d98a5f384b2 100644 --- a/src/sage/combinat/recognizable_series.py +++ b/src/sage/combinat/recognizable_series.py @@ -337,16 +337,18 @@ def _repr_(self, latex=False): if latex: from sage.misc.latex import latex as latex_repr - f = latex_repr + fr = latex_repr + fs = latex_repr times = ' ' else: - f = repr + fr = repr + fs = str times = '*' def summand(w, c): if c == 1: - return '[{w}]'.format(w=f(w)) - return '{c}{times}[{w}]'.format(c=f(c), times=times, w=f(w)) + return '[{w}]'.format(w=fs(w)) + return '{c}{times}[{w}]'.format(c=fr(c), times=times, w=fs(w)) s = ' + '.join(summand(w, c) for w, c in islice(self, 10)) @@ -486,16 +488,16 @@ def __iter__(self): ....: left=vector([0, 1]), right=vector([1, 0])) sage: from itertools import islice sage: list(islice(S, 10)) - [(1, 1), - (01, 1), - (10, 1), - (11, 2), - (001, 1), - (010, 1), - (011, 2), - (100, 1), - (101, 2), - (110, 2)] + [(word: 1, 1), + (word: 01, 1), + (word: 10, 1), + (word: 11, 2), + (word: 001, 1), + (word: 010, 1), + (word: 011, 2), + (word: 100, 1), + (word: 101, 2), + (word: 110, 2)] TESTS:: @@ -838,8 +840,6 @@ def __normalize__(cls, raise ValueError('TODO') from sage.combinat.words.words import Words indices = Words(alphabet, infinite=False) - from sage.combinat.words.word_options import WordOptions - WordOptions(identifier='') if coefficients is None: raise ValueError('TODO') From b18d3aaa11369a03bad45f8b4c4b3651e6cb756e Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Fri, 19 Aug 2016 12:21:12 +0200 Subject: [PATCH 030/406] check input of RecognizableSequenceSpace and document it --- src/sage/combinat/recognizable_series.py | 123 +++++++++++++++++------ 1 file changed, 92 insertions(+), 31 deletions(-) diff --git a/src/sage/combinat/recognizable_series.py b/src/sage/combinat/recognizable_series.py index d98a5f384b2..5f211116898 100644 --- a/src/sage/combinat/recognizable_series.py +++ b/src/sage/combinat/recognizable_series.py @@ -800,6 +800,47 @@ def mu_prime_entry(a, p, q, iq): from sage.structure.parent import Parent class RecognizableSeriesSpace(UniqueRepresentation, Parent): + r""" + The space of recognizable series on the given alphabet and + with the given coefficients. + + INPUT: + + - ``coefficients`` -- a (semi-)ring. + + - ``alphabet`` -- a tuple, list or + :class:`~sage.sets.totally_ordered_finite_set.TotallyOrderedFiniteSet`. + If specified, then the ``indices`` are the + finite words over this ``alphabet``. + ``alphabet`` and ``indices`` cannot be specified + at the same time. + + - ``indices`` -- a SageMath-parent of finite words over an alphabet. + ``alphabet`` and ``indices`` cannot be specified + at the same time. + + - ``category`` -- (default: ``None``) the category of this + space. + + EXAMPLES: + + All of the following examples create the same space:: + + sage: S1 = RecognizableSeriesSpace(ZZ, [0, 1]) + sage: S1 + Space of recognizable series on {0, 1} with coefficients in Integer Ring + sage: S2 = RecognizableSeriesSpace(coefficients=ZZ, alphabet=[0, 1]) + sage: S2 + Space of recognizable series on {0, 1} with coefficients in Integer Ring + sage: S3 = RecognizableSeriesSpace(ZZ, indices=Words([0, 1], infinite=False)) + sage: S3 + Space of recognizable series on {0, 1} with coefficients in Integer Ring + + .. SEEALSO:: + + :doc:`recognizable series `, + :class:`RecognizableSeries`. + """ Element = RecognizableSeries @@ -812,6 +853,20 @@ def __classcall__(cls, *args, **kwds): For more information see :class:`ReconizableSeriesSpace` and :meth:`__normalize__'. + + TESTS:: + + sage: S1 = RecognizableSeriesSpace(ZZ, [0, 1]) + sage: S1 + Space of recognizable series on {0, 1} with coefficients in Integer Ring + sage: S2 = RecognizableSeriesSpace(coefficients=ZZ, alphabet=[0, 1]) + sage: S2 + Space of recognizable series on {0, 1} with coefficients in Integer Ring + sage: S3 = RecognizableSeriesSpace(ZZ, indices=Words([0, 1], infinite=False)) + sage: S3 + Space of recognizable series on {0, 1} with coefficients in Integer Ring + sage: S1 is S2 is S3 + True """ return super(RecognizableSeriesSpace, cls).__classcall__( cls, *cls.__normalize__(*args, **kwds)) @@ -830,19 +885,49 @@ def __normalize__(cls, TESTS:: - sage: Rec = RecognizableSeriesSpace(ZZ, [0, 1]) + sage: Rec = RecognizableSeriesSpace(ZZ, [0, 1]) # indirect doctest sage: Rec.category() Category of sets + sage: RecognizableSeriesSpace([0, 1], [0, 1]) + Traceback (most recent call last): + ... + ValueError: Coefficients [0, 1] are not a semiring. + + :: + sage: W = Words([0, 1], infinite=False) + sage: RecognizableSeriesSpace(ZZ) + Traceback (most recent call last): + ... + ValueError: Specify either 'alphabet' or 'indices'. + sage: RecognizableSeriesSpace(ZZ, alphabet=[0, 1], indices=W) + Traceback (most recent call last): + ... + ValueError: Specify either 'alphabet' or 'indices'. + sage: RecognizableSeriesSpace(alphabet=[0, 1]) + Traceback (most recent call last): + ... + ValueError: No coefficients speficied. + sage: RecognizableSeriesSpace(ZZ, indices=Words(ZZ)) + Traceback (most recent call last): + ... + NotImplementedError: Alphabet is not finite. """ + if (alphabet is None) == (indices is None): + raise ValueError("Specify either 'alphabet' or 'indices'.") + if indices is None: - if alphabet is None: - raise ValueError('TODO') from sage.combinat.words.words import Words indices = Words(alphabet, infinite=False) + if not indices.alphabet().is_finite(): + raise NotImplementedError('Alphabet is not finite.') if coefficients is None: - raise ValueError('TODO') + raise ValueError('No coefficients speficied.') + from sage.categories.semirings import Semirings + if coefficients not in Semirings: + raise ValueError( + 'Coefficients {} are not a semiring.'.format(coefficients)) from sage.categories.sets_cat import Sets category = category or Sets() @@ -852,45 +937,21 @@ def __normalize__(cls, def __init__(self, coefficients, indices, category): r""" - The space of recognizable series on the given alphabet and - with the given coefficients. + See :class`RecognizableSeriesSpace` for details. INPUT: - ``coefficients`` -- a (semi-)ring. - - ``alphabet`` -- a tuple, list or - :class:`~sage.sets.totally_ordered_finite_set.TotallyOrderedFiniteSet`. - If specified, then the ``indices`` are the - finite words over this ``alphabet``. - ``alphabet`` and ``indices`` cannot be specified - at the same time. - - ``indices`` -- a SageMath-parent of finite words over an alphabet. - ``alphabet`` and ``indices`` cannot be specified - at the same time. - ``category`` -- (default: ``None``) the category of this space. - EXAMPLES:: + TESTS:: - sage: S1 = RecognizableSeriesSpace(ZZ, [0, 1]) - sage: S1 - Space of recognizable series on {0, 1} with coefficients in Integer Ring - sage: S2 = RecognizableSeriesSpace(coefficients=ZZ, alphabet=[0, 1]) - sage: S2 - Space of recognizable series on {0, 1} with coefficients in Integer Ring - sage: S3 = RecognizableSeriesSpace(ZZ, indices=Words([0, 1], infinite=False)) - sage: S3 + sage: RecognizableSeriesSpace(ZZ, [0, 1]) Space of recognizable series on {0, 1} with coefficients in Integer Ring - sage: S1 is S2 is S3 - True - - .. SEEALSO:: - - :doc:`recognizable series `, - :class:`RecognizableSeries`. """ self._indices_ = indices super(RecognizableSeriesSpace, self).__init__( From 85c1a03482907201f998f7cb42985e3485f83e29 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Fri, 19 Aug 2016 12:32:35 +0200 Subject: [PATCH 031/406] checks for creating recognizable series --- src/sage/combinat/recognizable_series.py | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/src/sage/combinat/recognizable_series.py b/src/sage/combinat/recognizable_series.py index 5f211116898..d4453c16766 100644 --- a/src/sage/combinat/recognizable_series.py +++ b/src/sage/combinat/recognizable_series.py @@ -260,18 +260,10 @@ def __init__(self, parent, mu, left=None, right=None): A = self.parent().alphabet() if isinstance(mu, (list, tuple)): - if len(A) != len(mu): - raise ValueError('TODO') mu = dict(zip(A, mu)) mu = Family(mu) - # TODO some check that alphabet is correct - #if A != self.mu: - # raise ValueError('TODO') - - #self.k = len(self.matrices) - #self.d = self.matrices[0].nrows() - #if not all(M.dimensions() == (self.d, self.d) for M in self.matrices): - # raise ValueError # TODO + if not mu.is_finite(): + raise NotImplementedError('mu is not a finite family of matrices.') self._left_ = left self._mu_ = mu From eec776e8c019139a5589baa8accd20a05e4ca5ed Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Fri, 19 Aug 2016 12:46:55 +0200 Subject: [PATCH 032/406] fix doctests --- src/sage/combinat/recognizable_series.py | 38 ++++++++++++++++++------ 1 file changed, 29 insertions(+), 9 deletions(-) diff --git a/src/sage/combinat/recognizable_series.py b/src/sage/combinat/recognizable_series.py index d4453c16766..4b1c2c00fc9 100644 --- a/src/sage/combinat/recognizable_series.py +++ b/src/sage/combinat/recognizable_series.py @@ -247,12 +247,6 @@ def __init__(self, parent, mu, left=None, right=None): :doc:`recognizable series `, :class:`RecognizableSeriesSpace`. - - TESTS:: - - sage: M0 = Matrix([[1, 0], [0, 1]]) - sage: M1 = Matrix([[0, -1], [1, 2]]) - sage: S = Rec(mu=(M0, M1)) # not tested TODO """ super(RecognizableSeries, self).__init__(parent=parent) @@ -277,6 +271,15 @@ def mu(self): of a word; the result is a matrix. This extends :meth:`mu` to words over the parent's :meth:`~RecognizableSeriesSpace.alphabet`. + + TESTS:: + + sage: Rec = RecognizableSeriesSpace(ZZ, [0, 1]) + sage: M0 = Matrix([[1, 0], [0, 1]]) + sage: M1 = Matrix([[0, -1], [1, 2]]) + sage: S = Rec((M0, M1), [0, 1]) + sage: S.mu[0] == M0 and S.mu[1] == M1 + True """ return self._mu_ @@ -287,6 +290,14 @@ def left(self): When evaluating a coefficient, this vector is multiplied from the left to the matrix obtained from :meth:`mu` applied on a word. + + TESTS:: + + sage: Rec = RecognizableSeriesSpace(ZZ, [0, 1]) + sage: Rec((Matrix([[3, 6], [0, 1]]), Matrix([[0, -6], [1, 5]])), + ....: vector([0, 1]), vector([1, 0]), + ....: transpose=True).left + (1, 0) """ return self._left_ @@ -297,6 +308,14 @@ def right(self): When evaluating a coefficient, this vector is multiplied from the right to the matrix obtained from :meth:`mu` applied on a word. + + TESTS:: + + sage: Rec = RecognizableSeriesSpace(ZZ, [0, 1]) + sage: Rec((Matrix([[3, 6], [0, 1]]), Matrix([[0, -6], [1, 5]])), + ....: vector([0, 1]), vector([1, 0]), + ....: transpose=True).right + (0, 1) """ return self._right_ @@ -461,10 +480,11 @@ def _mu_of_word_(self, w): sage: S._mu_of_word_(-1) Traceback (most recent call last): ... - ValueError: m=-1 is not a nonnegative integer. + ValueError: Index -1 is not in Finite words over {0, 1}. """ - if w not in self.parent().indices(): - raise ValueError('TODO') + W = self.parent().indices() + if w not in W: + raise ValueError('Index {} is not in {}.'.format(w, W)) from sage.misc.misc_c import prod return prod(tuple(self.mu[a] for a in w), z=self._mu_of_empty_word_()) From 7d96697ddc2994062c894ee78edca2c26708e922 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Fri, 19 Aug 2016 12:57:29 +0200 Subject: [PATCH 033/406] coverage to 100% --- src/sage/combinat/recognizable_series.py | 67 +++++++++++++++++++++++- 1 file changed, 65 insertions(+), 2 deletions(-) diff --git a/src/sage/combinat/recognizable_series.py b/src/sage/combinat/recognizable_series.py index 4b1c2c00fc9..5fd35e26439 100644 --- a/src/sage/combinat/recognizable_series.py +++ b/src/sage/combinat/recognizable_series.py @@ -377,6 +377,12 @@ def _latex_(self): TESTS:: + sage: Rec = RecognizableSeriesSpace(ZZ, [0, 1]) + sage: S = Rec((Matrix([[3, 6], [0, 1]]), Matrix([[0, -6], [1, 5]])), + ....: vector([0, 1]), vector([1, 0]), transpose=True) + sage: latex(S) # indirect doctest + [1] + 3 [01] + [10] + 5 [11] + 9 [001] + 3 [010] + + 15 [011] + [100] + 11 [101] + 5 [110] + ... """ return self._repr_(latex=True) @@ -671,6 +677,17 @@ def tr(M): def minimized(self): r""" + Return a recognizable series equivalent to this series, but + with a minimized linear representation. + + OUTPUT: + + A :class:`RecognizableSeries`. + + ALOGRITHM: + + This method implements the minimization algorithm presented in + Chapter 2 of [BR2010]_. EXAMPLES:: @@ -705,17 +722,52 @@ def minimized(self): ....: for (c, v), (d, w) in islice(izip(iter(S), iter(M)), 20)) True """ - if self.left is None or self.right is None: - raise ValueError("Cannot minmize as 'left' or 'right' is None.") return self._minimized_right_()._minimized_left_() def _minimized_right_(self): + r""" + Return a recognizable series equivalent to this series, but + with a right minimized linear representation. + + OUTPUT: + + A :class:`RecognizableSeries`. + + See :meth:`minimized` for details. + + TESTS:: + + sage: Rec = RecognizableSeriesSpace(ZZ, [0, 1]) + sage: S = Rec((Matrix([[0, 0], [0, 0]]), Matrix([[0, 0], [0, 0]])), + ....: vector([1, 1]), vector([1, 1])) + sage: M = S._minimized_right_() + sage: M.mu[0], M.mu[1], M.left, M.right + ([0], [0], (2), (1)) + + :: + + sage: S = Rec((Matrix([[0, 0], [0, 0]]), Matrix([[0, 0], [0, 0]]))) + sage: S._minimized_right_() + Traceback (most recent call last): + ... + ValueError: Cannot minmize as 'right' is None. + """ + if self.right is None: + raise ValueError("Cannot minmize as 'right' is None.") return self.transposed()._minimized_left_().transposed() def _minimized_left_(self): r""" + Return a recognizable series equivalent to this series, but + with a left minimized linear representation. + + OUTPUT: + + A :class:`RecognizableSeries`. + + See :meth:`minimized` for details. TESTS:: @@ -756,7 +808,18 @@ def _minimized_left_(self): sage: M = S.minimized() sage: M.mu[0], M.mu[1], M.left, M.right ([], [], (), ()) + + :: + + sage: S = Rec((Matrix([[0, 0], [0, 0]]), Matrix([[0, 0], [0, 0]]))) + sage: S._minimized_left_() + Traceback (most recent call last): + ... + ValueError: Cannot minmize as 'left' is None. """ + if self.left is None: + raise ValueError("Cannot minmize as 'left' is None.") + from sage.matrix.constructor import Matrix from sage.modules.free_module_element import vector from sage.rings.integer_ring import ZZ From 4a1db0e9407df74e3fa36a666e688be0d8537504 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Fri, 19 Aug 2016 12:58:06 +0200 Subject: [PATCH 034/406] reorder imports --- src/sage/combinat/recognizable_series.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/sage/combinat/recognizable_series.py b/src/sage/combinat/recognizable_series.py index 5fd35e26439..ccf9d614918 100644 --- a/src/sage/combinat/recognizable_series.py +++ b/src/sage/combinat/recognizable_series.py @@ -36,6 +36,8 @@ from sage.misc.cachefunc import cached_method from sage.structure.element import Element +from sage.structure.parent import Parent +from sage.structure.unique_representation import UniqueRepresentation class PrefixClosedSet(object): @@ -871,9 +873,6 @@ def mu_prime_entry(a, p, q, iq): self.parent(), mu_prime, left_prime, right_prime) -from sage.structure.unique_representation import UniqueRepresentation -from sage.structure.parent import Parent - class RecognizableSeriesSpace(UniqueRepresentation, Parent): r""" The space of recognizable series on the given alphabet and From 62ebcda81801ae3321e3e43edb58a55dcfa51fab Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Fri, 19 Aug 2016 13:34:36 +0200 Subject: [PATCH 035/406] complete documentation --- src/doc/en/reference/combinat/module_list.rst | 1 + src/sage/combinat/recognizable_series.py | 52 ++++++++++++++----- 2 files changed, 40 insertions(+), 13 deletions(-) diff --git a/src/doc/en/reference/combinat/module_list.rst b/src/doc/en/reference/combinat/module_list.rst index 16c87d06180..576249c0a07 100644 --- a/src/doc/en/reference/combinat/module_list.rst +++ b/src/doc/en/reference/combinat/module_list.rst @@ -178,6 +178,7 @@ Comprehensive Module list sage/combinat/q_bernoulli sage/combinat/quickref sage/combinat/ranker + sage/combinat/recognizable_series sage/combinat/restricted_growth sage/combinat/ribbon sage/combinat/ribbon_shaped_tableau diff --git a/src/sage/combinat/recognizable_series.py b/src/sage/combinat/recognizable_series.py index ccf9d614918..5b95cd8bac4 100644 --- a/src/sage/combinat/recognizable_series.py +++ b/src/sage/combinat/recognizable_series.py @@ -1,6 +1,23 @@ r""" Recognizable Series +Let `A` be an alphabet and `K` a semiring. Then a formal series `S` +with coefficients in `K` and indices in the words `A^*` is called +recognizable if it has a linear representation, i.e., there exists + +- a nonnegative integer `n` + +and there exists + +- two vectors `\mathit{left}` and `\mathit{right}` of dimension `n` and + +- a morphism of monoids `\mu` from `A^*` to `n\times n` matrices over `K` + +such that the coefficient corresponing to a word `w\in A^*` equals + +.. MATH:: + + \mathit{left} \, \mu(w) \, \mathit{right}. Various ======= @@ -223,19 +240,19 @@ def __init__(self, parent, mu, left=None, right=None): - ``mu`` -- a family of square matrices, all of which have the same dimension. The indices of this family are the alphabet. ``mu`` may be a list or tuple of the same cardinality as the - alphabet as well. See :meth:`mu` for more details. + alphabet as well. See :meth:`mu ` for more details. - ``left`` -- (default: ``None``) a vector. When evaluating a coefficient, this vector is multiplied from the left to the - matrix obtained from :meth:`mu` applying on a word. If + matrix obtained from :meth:`mu ` applying on a word. If ``None``, then this multiplication is skipped. - See :meth:`left` for more details. + See :meth`left ` for more details. - ``right`` -- (default: ``None``) a vector. When evaluating a coefficient, this vector is multiplied from the right to the - matrix obtained from :meth:`mu` applying on a word. If + matrix obtained from :meth:`mu ` applying on a word. If ``None``, then this multiplication is skipped. - See :meth:`right` for more details. + See :meth:`right ` for more details. EXAMPLES:: @@ -271,7 +288,7 @@ def mu(self): r""" When evaluating a coefficient, this is applied on each letter of a word; the result is a matrix. - This extends :meth:`mu` to words over the parent's + This extends :meth:`mu ` to words over the parent's :meth:`~RecognizableSeriesSpace.alphabet`. TESTS:: @@ -290,7 +307,7 @@ def mu(self): def left(self): r""" When evaluating a coefficient, this vector is multiplied from - the left to the matrix obtained from :meth:`mu` applied on a + the left to the matrix obtained from :meth:`mu ` applied on a word. TESTS:: @@ -308,7 +325,7 @@ def left(self): def right(self): r""" When evaluating a coefficient, this vector is multiplied from - the right to the matrix obtained from :meth:`mu` applied on a + the right to the matrix obtained from :meth:`mu ` applied on a word. TESTS:: @@ -424,7 +441,7 @@ def __getitem__(self, w): @cached_method def _mu_of_empty_word_(self): r""" - Return :meth:`mu` applied on the empty word. + Return :meth:`mu ` applied on the empty word. OUTPUT: @@ -458,7 +475,7 @@ def _mu_of_empty_word_(self): @cached_method def _mu_of_word_(self, w): r""" - Return :meth:`mu` applied on the word `w`. + Return :meth:`mu ` applied on the word `w`. INPUT: @@ -536,7 +553,7 @@ def __iter__(self): def is_trivial_zero(self): r""" Return whether this recognizable series is trivially equal to - zero (without any :meth:`minimization `). + zero (without any :meth:`minimization `). EXAMPLES:: @@ -641,8 +658,8 @@ def transposed(self): A :class:`RecognizableSeries`. - Each of the ``matrices`` is transposed. Additionally - the vectors ``left`` and ``right`` are switched. + Each of the matrices in :meth:`mu ` is transposed. Additionally + the vectors :meth`left ` and :meth:`right ` are switched. EXAMPLES:: @@ -677,6 +694,7 @@ def tr(M): right=tr(self.left)) + @cached_method def minimized(self): r""" Return a recognizable series equivalent to this series, but @@ -896,6 +914,11 @@ class RecognizableSeriesSpace(UniqueRepresentation, Parent): - ``category`` -- (default: ``None``) the category of this space. + - ``transpose`` -- (default: ``False``) a boolean. If set, then + each of the matrices in :meth:`mu ` is transposed. Additionally + the vectors :meth`left ` and :meth:`right ` are switched. + (This is done by calling :meth:`~RecognizableSeries.transposed`.) + EXAMPLES: All of the following examples create the same space:: @@ -1124,6 +1147,9 @@ def _element_constructor_(self, mu, left=None, right=None, transpose=False): r""" + Return a recognizable series. + + See :class:`RecognizableSeriesSpace` for details. TESTS:: From fd4f5414050ce650eeedca54580535aefeb008a1 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Fri, 19 Aug 2016 14:27:39 +0200 Subject: [PATCH 036/406] move "transpose" in docstring to correct place --- src/sage/combinat/recognizable_series.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/sage/combinat/recognizable_series.py b/src/sage/combinat/recognizable_series.py index 5b95cd8bac4..7339dfad127 100644 --- a/src/sage/combinat/recognizable_series.py +++ b/src/sage/combinat/recognizable_series.py @@ -254,6 +254,14 @@ def __init__(self, parent, mu, left=None, right=None): ``None``, then this multiplication is skipped. See :meth:`right ` for more details. + When created via the parent :class:`RecognizableSeriesSpace`, then + the following option is available. + + - ``transpose`` -- (default: ``False``) a boolean. If set, then + each of the matrices in :meth:`mu ` is transposed. Additionally + the vectors :meth`left ` and :meth:`right ` are switched. + (This is done by calling :meth:`transposed`.) + EXAMPLES:: sage: Rec = RecognizableSeriesSpace(ZZ, [0, 1]) @@ -914,11 +922,6 @@ class RecognizableSeriesSpace(UniqueRepresentation, Parent): - ``category`` -- (default: ``None``) the category of this space. - - ``transpose`` -- (default: ``False``) a boolean. If set, then - each of the matrices in :meth:`mu ` is transposed. Additionally - the vectors :meth`left ` and :meth:`right ` are switched. - (This is done by calling :meth:`~RecognizableSeries.transposed`.) - EXAMPLES: All of the following examples create the same space:: From 863d57ac89c7af9abbba9a6ebcbaecf8a734f7b4 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Mon, 22 Aug 2016 14:44:21 +0200 Subject: [PATCH 037/406] remove None-sense to simplify code --- src/sage/combinat/recognizable_series.py | 80 +++++------------------- 1 file changed, 15 insertions(+), 65 deletions(-) diff --git a/src/sage/combinat/recognizable_series.py b/src/sage/combinat/recognizable_series.py index 7339dfad127..d0cd8396098 100644 --- a/src/sage/combinat/recognizable_series.py +++ b/src/sage/combinat/recognizable_series.py @@ -231,7 +231,7 @@ def prefix_set(self): class RecognizableSeries(Element): - def __init__(self, parent, mu, left=None, right=None): + def __init__(self, parent, mu, left, right): r""" A recognizable series. @@ -242,16 +242,14 @@ def __init__(self, parent, mu, left=None, right=None): ``mu`` may be a list or tuple of the same cardinality as the alphabet as well. See :meth:`mu ` for more details. - - ``left`` -- (default: ``None``) a vector. When evaluating a + - ``left`` -- a vector. When evaluating a coefficient, this vector is multiplied from the left to the - matrix obtained from :meth:`mu ` applying on a word. If - ``None``, then this multiplication is skipped. + matrix obtained from :meth:`mu ` applying on a word. See :meth`left ` for more details. - - ``right`` -- (default: ``None``) a vector. When evaluating a + - ``right`` -- a vector. When evaluating a coefficient, this vector is multiplied from the right to the - matrix obtained from :meth:`mu ` applying on a word. If - ``None``, then this multiplication is skipped. + matrix obtained from :meth:`mu ` applying on a word. See :meth:`right ` for more details. When created via the parent :class:`RecognizableSeriesSpace`, then @@ -304,7 +302,7 @@ def mu(self): sage: Rec = RecognizableSeriesSpace(ZZ, [0, 1]) sage: M0 = Matrix([[1, 0], [0, 1]]) sage: M1 = Matrix([[0, -1], [1, 2]]) - sage: S = Rec((M0, M1), [0, 1]) + sage: S = Rec((M0, M1), [0, 1], [1, 1]) sage: S.mu[0] == M0 and S.mu[1] == M1 True """ @@ -438,12 +436,7 @@ def __getitem__(self, w): sage: S[W(7.digits(2))] 3 """ - result = self._mu_of_word_(w) - if self.left is not None: - result = self.left * result - if self.right is not None: - result = result * self.right - return result + return self.left * self._mu_of_word_(w) * self.right @cached_method @@ -461,12 +454,12 @@ def _mu_of_empty_word_(self): sage: W = Rec.indices() sage: M0 = Matrix([[1, 0], [0, 1]]) sage: M1 = Matrix([[0, -1], [1, 2]]) - sage: S = Rec({W([0]): M0, W([1]): M1}) + sage: S = Rec({W([0]): M0, W([1]): M1}, [0, 1], [1, 1]) sage: S._mu_of_empty_word_() [1 0] [0 1] sage: I = Matrix([[1, 0], [0, 1]]) - sage: T = Rec({W([]): I, W([0]): M0, W([1]): M1}) + sage: T = Rec({W([]): I, W([0]): M0, W([1]): M1}, [0, 1], [1, 1]) sage: T._mu_of_empty_word_() [1 0] [0 1] @@ -500,7 +493,7 @@ def _mu_of_word_(self, w): sage: W = Rec.indices() sage: M0 = Matrix([[1, 0], [0, 1]]) sage: M1 = Matrix([[0, -1], [1, 2]]) - sage: S = Rec((M0, M1)) + sage: S = Rec((M0, M1), [0, 1], [1, 1]) sage: S._mu_of_word_(W([0])) == M0 True sage: S._mu_of_word_(W([1])) == M1 @@ -590,19 +583,6 @@ def is_trivial_zero(self): sage: Rec.zero().is_trivial_zero() True - The following are nonzero as the coefficient of the empty word - is nonzero:: - - sage: Rec((Matrix([[0, 0], [0, 0]]), Matrix([[0, 0], [0, 0]])), - ....: left=None, right=vector([1, 0])).is_trivial_zero() - False - sage: Rec((Matrix([[0, 0], [0, 0]]), Matrix([[0, 0], [0, 0]])), - ....: left=None, right=None).is_trivial_zero() - False - sage: Rec((Matrix([[0, 0], [0, 0]]), Matrix([[0, 0], [0, 0]])), - ....: left=vector([0, 1]), right=None).is_trivial_zero() - False - The following is zero, but not trivially zero:: sage: S = Rec((Matrix([[1, 0], [0, 0]]), Matrix([[1, 0], [0, 0]])), @@ -612,8 +592,7 @@ def is_trivial_zero(self): sage: S.is_zero() True """ - return (self.left is not None and not self.left) or \ - (self.right is not None and not self.right) or \ + return not self.left or not self.right or \ (all(not self.mu[a] for a in self.parent().alphabet()) and not self[self.parent().indices()()]) @@ -692,14 +671,9 @@ def transposed(self): [0 1], [ 1 5], (0, 1), (1, 0) ) """ - def tr(M): - try: - return M.transpose() - except AttributeError: - return M - return self.parent()(self.mu.map(tr), - left=tr(self.right), - right=tr(self.left)) + return self.parent()(self.mu.map(lambda M: M.transpose()), + left=self.right, + right=self.left) @cached_method @@ -772,17 +746,7 @@ def _minimized_right_(self): sage: M = S._minimized_right_() sage: M.mu[0], M.mu[1], M.left, M.right ([0], [0], (2), (1)) - - :: - - sage: S = Rec((Matrix([[0, 0], [0, 0]]), Matrix([[0, 0], [0, 0]]))) - sage: S._minimized_right_() - Traceback (most recent call last): - ... - ValueError: Cannot minmize as 'right' is None. """ - if self.right is None: - raise ValueError("Cannot minmize as 'right' is None.") return self.transposed()._minimized_left_().transposed() @@ -836,18 +800,7 @@ def _minimized_left_(self): sage: M = S.minimized() sage: M.mu[0], M.mu[1], M.left, M.right ([], [], (), ()) - - :: - - sage: S = Rec((Matrix([[0, 0], [0, 0]]), Matrix([[0, 0], [0, 0]]))) - sage: S._minimized_left_() - Traceback (most recent call last): - ... - ValueError: Cannot minmize as 'left' is None. """ - if self.left is None: - raise ValueError("Cannot minmize as 'left' is None.") - from sage.matrix.constructor import Matrix from sage.modules.free_module_element import vector from sage.rings.integer_ring import ZZ @@ -890,10 +843,7 @@ def mu_prime_entry(a, p, q, iq): mu_prime.append(Matrix(M)) left_prime = vector([ZZ(1)] + (len(P)-1)*[ZZ(0)]) - if self.right is None: - right_prime = None - else: - right_prime = vector(self[p] for p in P) + right_prime = vector(self[p] for p in P) return self.parent().element_class( self.parent(), mu_prime, left_prime, right_prime) From aec95cef075b54c022d3479cab376e6adc1ee011 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Mon, 22 Aug 2016 14:46:52 +0200 Subject: [PATCH 038/406] extend element constructor --- src/sage/combinat/recognizable_series.py | 44 ++++++++++++++++++++++-- 1 file changed, 41 insertions(+), 3 deletions(-) diff --git a/src/sage/combinat/recognizable_series.py b/src/sage/combinat/recognizable_series.py index d0cd8396098..e2c6ccb192d 100644 --- a/src/sage/combinat/recognizable_series.py +++ b/src/sage/combinat/recognizable_series.py @@ -1096,7 +1096,7 @@ def zero(self): return self(0) - def _element_constructor_(self, mu, + def _element_constructor_(self, data, left=None, right=None, transpose=False): r""" @@ -1107,12 +1107,38 @@ def _element_constructor_(self, mu, TESTS:: sage: Rec = RecognizableSeriesSpace(ZZ, [0, 1]) + sage: Rec.zero() 0 sage: type(_) + + :: + + sage: M0 = Matrix([[1, 0], [0, 1]]) + sage: M1 = Matrix([[0, -1], [1, 2]]) + sage: S = Rec((M0, M1), [0, 1], [1, 1]) + sage: Rec(S) is S + True + + sage: Rec((M0, M1)) + Traceback (most recent call last): + ... + ValueError: Left or right vector is None. + sage: Rec((M0, M1), [0, 1]) + Traceback (most recent call last): + ... + ValueError: Left or right vector is None. + sage: Rec((M0, M1), left=[0, 1]) + Traceback (most recent call last): + ... + ValueError: Left or right vector is None. + sage: Rec((M0, M1), right=[0, 1]) + Traceback (most recent call last): + ... + ValueError: Left or right vector is None. """ - if isinstance(mu, int) and mu == 0: + if isinstance(data, int) and data == 0: from sage.matrix.constructor import Matrix from sage.modules.free_module_element import vector from sage.sets.family import Family @@ -1121,7 +1147,19 @@ def _element_constructor_(self, mu, self, Family(self.alphabet(), lambda a: Matrix()), vector([]), vector([])) - element = self.element_class(self, mu, left, right) + if type(data) == self.element_class and data.parent() == self: + element = data + + elif isinstance(data, RecognizableSeries): + element = self.element_class(self, data.mu, data.left, data.right) + + else: + mu = data + if left is None or right is None: + raise ValueError('Left or right vector is None.') + + element = self.element_class(self, mu, left, right) + if transpose: return element.transposed() else: From 9e12383a4d19664c152c26248768c69dc1c893e8 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Mon, 22 Aug 2016 14:53:00 +0200 Subject: [PATCH 039/406] add experimental warning --- src/sage/combinat/recognizable_series.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/sage/combinat/recognizable_series.py b/src/sage/combinat/recognizable_series.py index e2c6ccb192d..677e16c1890 100644 --- a/src/sage/combinat/recognizable_series.py +++ b/src/sage/combinat/recognizable_series.py @@ -19,6 +19,22 @@ \mathit{left} \, \mu(w) \, \mathit{right}. + +.. WARNING:: + + As this code is experimental, warnings are thrown when a a + recognizable series space is created for the first time in a + session (see :class:`sage.misc.superseded.experimental`). + + TESTS:: + + sage: Rec = RecognizableSeriesSpace(ZZ, [0, 1]) + doctest:...: FutureWarning: This class/method/function is + marked as experimental. It, its functionality or its interface + might change without a formal deprecation. + See http://trac.sagemath.org/21202 for details. + + Various ======= @@ -52,6 +68,7 @@ #***************************************************************************** from sage.misc.cachefunc import cached_method +from sage.misc.superseded import experimental from sage.structure.element import Element from sage.structure.parent import Parent from sage.structure.unique_representation import UniqueRepresentation @@ -985,6 +1002,7 @@ def __normalize__(cls, return (coefficients, indices, category) + @experimental(trac_number=21202) def __init__(self, coefficients, indices, category): r""" See :class`RecognizableSeriesSpace` for details. From 41874bafbd0fdd2faafbd499a0933f8ca502843e Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Mon, 22 Aug 2016 14:56:23 +0200 Subject: [PATCH 040/406] fix a typo --- src/sage/combinat/recognizable_series.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/combinat/recognizable_series.py b/src/sage/combinat/recognizable_series.py index 677e16c1890..02459d74ca3 100644 --- a/src/sage/combinat/recognizable_series.py +++ b/src/sage/combinat/recognizable_series.py @@ -22,7 +22,7 @@ .. WARNING:: - As this code is experimental, warnings are thrown when a a + As this code is experimental, warnings are thrown when a recognizable series space is created for the first time in a session (see :class:`sage.misc.superseded.experimental`). From a6b55613133efc2d002a8ae4b4f194621fd3717d Mon Sep 17 00:00:00 2001 From: Clemens Heuberger Date: Thu, 12 Jan 2017 16:15:31 +0100 Subject: [PATCH 041/406] Trac #21295: fix ReST error --- src/sage/combinat/recognizable_series.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/combinat/recognizable_series.py b/src/sage/combinat/recognizable_series.py index 02459d74ca3..c0fa31b4de3 100644 --- a/src/sage/combinat/recognizable_series.py +++ b/src/sage/combinat/recognizable_series.py @@ -919,7 +919,7 @@ def __classcall__(cls, *args, **kwds): unique representation. For more information see :class:`ReconizableSeriesSpace` - and :meth:`__normalize__'. + and :meth:`__normalize__`. TESTS:: From c9c3408af4edead15b16886353258731d35092e0 Mon Sep 17 00:00:00 2001 From: Clemens Heuberger Date: Thu, 12 Jan 2017 16:42:20 +0100 Subject: [PATCH 042/406] Trac #21295: fix typos --- src/sage/combinat/recognizable_series.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/combinat/recognizable_series.py b/src/sage/combinat/recognizable_series.py index c0fa31b4de3..9a204e137f5 100644 --- a/src/sage/combinat/recognizable_series.py +++ b/src/sage/combinat/recognizable_series.py @@ -7,7 +7,7 @@ - a nonnegative integer `n` -and there exists +and there exist - two vectors `\mathit{left}` and `\mathit{right}` of dimension `n` and @@ -918,7 +918,7 @@ def __classcall__(cls, *args, **kwds): Prepare normalizing the input in order to ensure a unique representation. - For more information see :class:`ReconizableSeriesSpace` + For more information see :class:`RecognizableSeriesSpace` and :meth:`__normalize__`. TESTS:: From 584835376cfeca4b9b0d2aa1f25169f6380cc66b Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Tue, 24 Jan 2017 13:53:02 +0100 Subject: [PATCH 043/406] Python3: __nonzero__ --- src/sage/combinat/recognizable_series.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/sage/combinat/recognizable_series.py b/src/sage/combinat/recognizable_series.py index 9a204e137f5..34d4f340697 100644 --- a/src/sage/combinat/recognizable_series.py +++ b/src/sage/combinat/recognizable_series.py @@ -614,7 +614,7 @@ def is_trivial_zero(self): not self[self.parent().indices()()]) - def __nonzero__(self): + def __bool__(self): r""" Return whether this recognizable series is nonzero. @@ -654,6 +654,9 @@ def __nonzero__(self): return True + __nonzero__ = __bool__ + + def transposed(self): r""" Return the transposed series. From 4a43407d37361ab2e24b965e13358ab408ef894a Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Tue, 24 Jan 2017 13:53:11 +0100 Subject: [PATCH 044/406] Python3: izip --- src/sage/combinat/recognizable_series.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/sage/combinat/recognizable_series.py b/src/sage/combinat/recognizable_series.py index 34d4f340697..cd782c2f51f 100644 --- a/src/sage/combinat/recognizable_series.py +++ b/src/sage/combinat/recognizable_series.py @@ -713,7 +713,8 @@ def minimized(self): EXAMPLES:: - sage: from itertools import islice, izip + sage: from itertools import islice + sage: from six.moves import zip sage: Rec = RecognizableSeriesSpace(ZZ, [0, 1]) sage: S = Rec((Matrix([[3, 6], [0, 1]]), Matrix([[0, -6], [1, 5]])), @@ -729,7 +730,7 @@ def minimized(self): [6 1], [-6 5], (1, 0), (0, 1) ) sage: all(c == d and v == w - ....: for (c, v), (d, w) in islice(izip(iter(S), iter(M)), 20)) + ....: for (c, v), (d, w) in islice(zip(iter(S), iter(M)), 20)) True sage: S = Rec((Matrix([[2, 0], [1, 1]]), Matrix([[2, 0], [2, 1]])), @@ -741,7 +742,7 @@ def minimized(self): sage: M.mu[0], M.mu[1], M.left, M.right ([2], [2], (1), (1)) sage: all(c == d and v == w - ....: for (c, v), (d, w) in islice(izip(iter(S), iter(M)), 20)) + ....: for (c, v), (d, w) in islice(zip(iter(S), iter(M)), 20)) True """ return self._minimized_right_()._minimized_left_() From 4970cf5984fb88c379748c29fd3824981389468e Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Tue, 24 Jan 2017 13:58:28 +0100 Subject: [PATCH 045/406] Python3: absolute import --- src/sage/combinat/recognizable_series.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sage/combinat/recognizable_series.py b/src/sage/combinat/recognizable_series.py index cd782c2f51f..9cd48d81784 100644 --- a/src/sage/combinat/recognizable_series.py +++ b/src/sage/combinat/recognizable_series.py @@ -66,6 +66,7 @@ # (at your option) any later version. # http://www.gnu.org/licenses/ #***************************************************************************** +from __future__ import absolute_import from sage.misc.cachefunc import cached_method from sage.misc.superseded import experimental From 9a96ea10e09e9231a81ad1d544fe271407e40194 Mon Sep 17 00:00:00 2001 From: Lorenz Panny Date: Fri, 29 Sep 2017 13:53:33 +0200 Subject: [PATCH 046/406] implement proper black-box discrete logarithm for AdditiveAbelianGroupWrapper --- src/doc/en/reference/references/index.rst | 5 + .../additive_abelian_wrapper.py | 132 ++++++++++++++++-- 2 files changed, 125 insertions(+), 12 deletions(-) diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst index e14bc17d65e..36ca121bec8 100644 --- a/src/doc/en/reference/references/index.rst +++ b/src/doc/en/reference/references/index.rst @@ -1631,6 +1631,11 @@ REFERENCES: .. [sudoku:wikipedia] "Near worst case", :wikipedia:`Algorithmics_of_sudoku` +.. [Suth2008] Andrew V. Sutherland, *Structure computation and discrete + logarithms in finite abelian p-groups*. + Mathematics of Computation **80** (2011), pp. 477-500. + :arxiv:`0809.3413v3`. + .. [SW2002] William Stein and Mark Watkins, *A database of elliptic curves---first report*. In *Algorithmic number theory (ANTS V), Sydney, 2002*, Lecture Notes in Computer Science diff --git a/src/sage/groups/additive_abelian/additive_abelian_wrapper.py b/src/sage/groups/additive_abelian/additive_abelian_wrapper.py index 34fa42c221c..09da2a56b94 100644 --- a/src/sage/groups/additive_abelian/additive_abelian_wrapper.py +++ b/src/sage/groups/additive_abelian/additive_abelian_wrapper.py @@ -38,10 +38,7 @@ TODO: -- Implement proper black-box discrete logarithm (using baby-step giant-step). - The discrete_exp function can also potentially be speeded up substantially - via caching. - +- The discrete_exp function can potentially be sped up substantially via caching. - Think about subgroups and quotients, which probably won't work in the current implementation -- some fiddly adjustments will be needed in order to be able to pass extra arguments to the subquotient's init method. @@ -53,6 +50,7 @@ from sage.misc.misc import verbose from sage.categories.morphism import Morphism from sage.structure.element import parent +from sage.modules.free_module_element import vector class UnwrappingMorphism(Morphism): r""" @@ -227,13 +225,101 @@ def _discrete_exp(self, v): # DUMB IMPLEMENTATION! return sum([self._gen_elements[i] * ZZ(v[i]) for i in range(len(v))], self.universe()(0)) - def _discrete_log(self,x): + def _discrete_log_pgroup(self, p, aa, b): + from sage.arith.misc import valuation + from sage.functions.other import ceil, sqrt + from itertools import product as iproduct + r""" + Attempt to express an element of p-power order in terms of + generators of a p-subgroup of self. + + Used as a subroutine in the _discrete_log() method. + + ALGORITHM: + + This implements a basic version of the recursive algorithm + from [Suth2008]_. + The base cases are handled using a variant of Shanks' + baby-step giant-step algorithm for products of cyclic groups. + + EXAMPLES:: + sage: G = AdditiveAbelianGroup([5, 5**2, 5**4, 5**4]) + sage: (a, b, c, d) = gs = G.gens() + sage: A = AdditiveAbelianGroupWrapper(a.parent(), gs, [g.order() for g in gs]) + sage: A._discrete_log_pgroup(5, gs, a + 17 * b + 123 * c + 456 * d) + (1, 17, 123, 456) + """ + + vals = [valuation(a.order(), p) for a in aa] + qq = lambda j, k: vector(p ** (j + max(0, v - k)) for a, v in zip(aa, vals)) + subbasis = lambda j, k: [q * a for q, a in zip(qq(j, k), aa)] + dotprod = lambda xs, ys: sum(x * y for x, y in zip(xs, ys)) + + def _base(j, k, c): + + assert k - j == 1 + aajk = subbasis(j, k) + assert all(a.order() in (1, p) for a in aajk) + idxs = [i for i, a in enumerate(aajk) if a.order() == p] + + rs = [([0], [0]) for i in range(len(aajk))] + for i in range(len(idxs)): + rs[idxs[i]] = (range(p), [0]) if i % 2 else ([0], range(p)) + if len(idxs) % 2: + m = ceil(sqrt(p)) + rs[idxs[-1]] = range(0, p, m), range(m) + + tab = {} + for x in iproduct(*(r for r, _ in rs)): + key = dotprod(x, aajk) + if hasattr(key, 'set_immutable'): key.set_immutable() + tab[key] = vector(x) + for y in iproduct(*(r for _, r in rs)): + key = c - dotprod(y, aajk) + if hasattr(key, 'set_immutable'): key.set_immutable() + try: + return tab[key] + vector(y) + except KeyError: + pass + + raise TypeError('Not in group') + + def _rec(j, k, c): + + assert 0 <= j < k + + if k - j <= 1: # base case + return _base(j, k, c) + + w = 2 + js = list(range(j, k, (k-j+w-1) // w)) + [k] + assert len(js) == w + 1 + + x = vector([0] * len(aa)) + for i in reversed(range(w)): + + gamma = p ** (js[i] - j) * c - dotprod(x, subbasis(js[i], k)) + + v = _rec(js[i], js[i+1], gamma) + + assert not any(q1 % q2 for q1, q2 in zip(qq(js[i], js[i+1]), qq(js[i], k))) + x += vector(q1 // q2 * r for q1, q2, r in zip(qq(js[i], js[i+1]), qq(js[i], k), v)) + + return x + + return _rec(0, max(vals), b) + + def _discrete_log(self, x, gens=None): r""" - Given an element of the ambient group, attempt to express it in terms of the - generators of self. + Given an element of the ambient group, attempt to express it in terms + of the generators of self or the given generators of a subgroup. EXAMPLES:: + sage: G = AdditiveAbelianGroup([2, 2*3, 2*3*5, 2*3*5*7, 2*3*5*7*11]) + sage: A = AdditiveAbelianGroupWrapper(G.0.parent(), G.gens(), [g.order() for g in G.gens()]) + sage: A._discrete_log(G.0 + 5 * G.1 + 23 * G.2 + 127 * G.3 + 539 * G.4) + (1, 5, 23, 127, 539) sage: V = Zmod(8)**2; G = AdditiveAbelianGroupWrapper(V, [[2,2],[4,0]], [4, 2]) sage: G._discrete_log(V([6, 2])) (1, 1) @@ -241,20 +327,42 @@ def _discrete_log(self,x): Traceback (most recent call last): ... TypeError: Not in group + sage: F. = GF(1009**2, modulus=x**2+11); E = EllipticCurve(j=F(940)) + sage: P, Q = E(900*t + 228, 974*t + 185), E(1007*t + 214, 865*t + 802) + sage: E.abelian_group()._discrete_log(123 * P + 777 * Q, [P, Q]) + (123, 777) sage: G = AdditiveAbelianGroupWrapper(QQbar, [sqrt(2)], [0]) sage: G._discrete_log(QQbar(2*sqrt(2))) Traceback (most recent call last): ... NotImplementedError: No black-box discrete log for infinite abelian groups """ - # EVEN DUMBER IMPLEMENTATION! + from sage.arith.misc import CRT_list from sage.rings.infinity import Infinity + if self.order() == Infinity: raise NotImplementedError("No black-box discrete log for infinite abelian groups") - u = [y for y in self.list() if y.element() == x] - if len(u) == 0: raise TypeError("Not in group") - if len(u) > 1: raise NotImplementedError - return u[0].vector() + + if gens is None: + gens = self.gens() + + gens = [g if parent(g) is self.universe() else g.element() for g in gens] + x = x if parent(x) is self.universe() else x.element() + + crt_data = [[] for _ in gens] + for p, e in self.order().factor(): + cofactor = self.order() // p ** e + pgens = [cofactor * g for g in gens] + y = cofactor * x + + plog = self._discrete_log_pgroup(p, pgens, y) + + for i, (r, g) in enumerate(zip(plog, pgens)): + crt_data[i].append((r, ZZ(g.order()))) + + res = vector(CRT_list(*map(list, zip(*l))) for l in crt_data) + assert x == sum(r * g for r, g in zip(res, gens)) + return res def _element_constructor_(self, x, check=False): r""" From 923edc15766f2c032da9d33c445e8726eff73c89 Mon Sep 17 00:00:00 2001 From: Lorenz Panny Date: Fri, 29 Sep 2017 14:37:51 +0200 Subject: [PATCH 047/406] move imports below docstring --- .../groups/additive_abelian/additive_abelian_wrapper.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/groups/additive_abelian/additive_abelian_wrapper.py b/src/sage/groups/additive_abelian/additive_abelian_wrapper.py index 09da2a56b94..cf02196c107 100644 --- a/src/sage/groups/additive_abelian/additive_abelian_wrapper.py +++ b/src/sage/groups/additive_abelian/additive_abelian_wrapper.py @@ -226,9 +226,6 @@ def _discrete_exp(self, v): return sum([self._gen_elements[i] * ZZ(v[i]) for i in range(len(v))], self.universe()(0)) def _discrete_log_pgroup(self, p, aa, b): - from sage.arith.misc import valuation - from sage.functions.other import ceil, sqrt - from itertools import product as iproduct r""" Attempt to express an element of p-power order in terms of generators of a p-subgroup of self. @@ -249,6 +246,9 @@ def _discrete_log_pgroup(self, p, aa, b): sage: A._discrete_log_pgroup(5, gs, a + 17 * b + 123 * c + 456 * d) (1, 17, 123, 456) """ + from sage.arith.misc import valuation + from sage.functions.other import ceil, sqrt + from itertools import product as iproduct vals = [valuation(a.order(), p) for a in aa] qq = lambda j, k: vector(p ** (j + max(0, v - k)) for a, v in zip(aa, vals)) From 3cf6024a2aa0a8812ffe815136e4a2f788130f66 Mon Sep 17 00:00:00 2001 From: Lorenz Panny Date: Fri, 29 Sep 2017 15:37:34 +0200 Subject: [PATCH 048/406] add missing linebreak in docstring --- src/sage/groups/additive_abelian/additive_abelian_wrapper.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sage/groups/additive_abelian/additive_abelian_wrapper.py b/src/sage/groups/additive_abelian/additive_abelian_wrapper.py index cf02196c107..41ddd8cd4fd 100644 --- a/src/sage/groups/additive_abelian/additive_abelian_wrapper.py +++ b/src/sage/groups/additive_abelian/additive_abelian_wrapper.py @@ -240,6 +240,7 @@ def _discrete_log_pgroup(self, p, aa, b): baby-step giant-step algorithm for products of cyclic groups. EXAMPLES:: + sage: G = AdditiveAbelianGroup([5, 5**2, 5**4, 5**4]) sage: (a, b, c, d) = gs = G.gens() sage: A = AdditiveAbelianGroupWrapper(a.parent(), gs, [g.order() for g in gs]) From 12fd85501b7af909b1bbee4cc9f5b4ae9be3400b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Thu, 30 Aug 2018 02:36:55 +0200 Subject: [PATCH 049/406] Refine polynomial quotient ring to the finite subcategory on initialization if it is finite --- .../polynomial/polynomial_quotient_ring.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/sage/rings/polynomial/polynomial_quotient_ring.py b/src/sage/rings/polynomial/polynomial_quotient_ring.py index 50aeeaf63d9..31f9100a148 100644 --- a/src/sage/rings/polynomial/polynomial_quotient_ring.py +++ b/src/sage/rings/polynomial/polynomial_quotient_ring.py @@ -387,6 +387,18 @@ def __init__(self, ring, polynomial, name=None, category=None): sage: S == PolynomialQuotientRing_generic(R,x^2-4,'xbar') True + Check that :trac:`26161` has been resolved:: + + sage: R. = GF(2)[] + sage: S = R.quo(x) + sage: S in FiniteFields() + True + sage: type(S).mro() + [, + ... + , + ... + """ if not isinstance(ring, PolynomialRing_commutative): raise TypeError("R must be a univariate polynomial ring.") @@ -400,6 +412,11 @@ def __init__(self, ring, polynomial, name=None, category=None): self.__ring = ring self.__polynomial = polynomial category = CommutativeAlgebras(ring.base_ring()).Quotients().or_subcategory(category) + if self.is_finite(): + # We refine the category for finite quotients. + # Note that is_finite() is cheap so it does not seem to do a lazy + # _refine_category_() in is_finite() as we do for is_field() + category = category.Finite() CommutativeRing.__init__(self, ring, names=name, category=category) def _element_constructor_(self, x): From 0c054d0026019f30b737cac867c305590fcc76b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Thu, 20 Dec 2018 18:33:20 +0100 Subject: [PATCH 050/406] Make morphisms between finite fields work again Such morphisms were broken when the codomain was not implemented as a finite field but just belonged to the category of finite fields. --- src/sage/rings/finite_rings/finite_field_base.pyx | 12 ++++++------ src/sage/rings/finite_rings/hom_finite_field.pyx | 12 ++++++------ src/sage/rings/finite_rings/homset.py | 11 ++++++++++- 3 files changed, 22 insertions(+), 13 deletions(-) diff --git a/src/sage/rings/finite_rings/finite_field_base.pyx b/src/sage/rings/finite_rings/finite_field_base.pyx index af90c0b323c..7e148cfd21f 100644 --- a/src/sage/rings/finite_rings/finite_field_base.pyx +++ b/src/sage/rings/finite_rings/finite_field_base.pyx @@ -1811,9 +1811,10 @@ register_unpickle_override( 'sage.rings.ring', 'unpickle_FiniteField_prm', unpickle_FiniteField_prm) -def is_FiniteField(x): - """ - Return ``True`` if ``x`` is of type finite field, and ``False`` otherwise. +def is_FiniteField(R): + r""" + Return whether the implementation of ``R`` has the interface provided by + the standard finite field implementation. EXAMPLES:: @@ -1823,10 +1824,9 @@ def is_FiniteField(x): sage: is_FiniteField(GF(next_prime(10^10))) True - Note that the integers modulo n are not of type finite field, - so this function returns ``False``:: + Note that the integers modulo n are not backed by the finite field type:: sage: is_FiniteField(Integers(7)) False """ - return isinstance(x, FiniteField) + return isinstance(R, FiniteField) diff --git a/src/sage/rings/finite_rings/hom_finite_field.pyx b/src/sage/rings/finite_rings/hom_finite_field.pyx index 01ba534aef3..12394f6d3e9 100644 --- a/src/sage/rings/finite_rings/hom_finite_field.pyx +++ b/src/sage/rings/finite_rings/hom_finite_field.pyx @@ -209,20 +209,20 @@ cdef class FiniteFieldHomomorphism_generic(RingHomomorphism_im_gens): sage: FiniteFieldHomomorphism_generic(Hom(ZZ, QQ)) Traceback (most recent call last): ... - TypeError: The domain is not a finite field + TypeError: The domain is not a finite field or does not provide the required interface for finite fields sage: R. = k[] sage: FiniteFieldHomomorphism_generic(Hom(k, R)) Traceback (most recent call last): ... - TypeError: The codomain is not a finite field + TypeError: The codomain is not a finite field or does not provide the required interface for finite fields """ domain = parent.domain() codomain = parent.codomain() if not is_FiniteField(domain): - raise TypeError("The domain is not a finite field") + raise TypeError("The domain is not a finite field or does not provide the required interface for finite fields") if not is_FiniteField(codomain): - raise TypeError("The codomain is not a finite field") + raise TypeError("The codomain is not a finite field or does not provide the required interface for finite fields") if domain.characteristic() != codomain.characteristic() or codomain.degree() % domain.degree() != 0: raise ValueError("No embedding of %s into %s" % (domain, codomain)) if im_gens is None: @@ -383,10 +383,10 @@ cdef class FrobeniusEndomorphism_finite_field(FrobeniusEndomorphism_generic): sage: FrobeniusEndomorphism_finite_field(k['x']) Traceback (most recent call last): ... - TypeError: The domain must be a finite field + TypeError: The domain is not a finite field or does not provide the required interface for finite fields """ if not is_FiniteField(domain): - raise TypeError("The domain must be a finite field") + raise TypeError("The domain is not a finite field or does not provide the required interface for finite fields") try: n = Integer(n) except TypeError: diff --git a/src/sage/rings/finite_rings/homset.py b/src/sage/rings/finite_rings/homset.py index 5bd8fa58f97..6afadc5a4e5 100644 --- a/src/sage/rings/finite_rings/homset.py +++ b/src/sage/rings/finite_rings/homset.py @@ -37,7 +37,9 @@ from sage.rings.homset import RingHomset_generic from sage.rings.finite_rings.hom_finite_field import FiniteFieldHomomorphism_generic +from sage.rings.finite_rings.finite_field_base import is_FiniteField from sage.rings.integer import Integer +from sage.rings.morphism import RingHomomorphism_im_gens from sage.structure.sequence import Sequence class FiniteFieldHomset(RingHomset_generic): @@ -92,7 +94,14 @@ def __call__(self, im_gens, check=True): if self.domain().degree() == 1: from sage.rings.finite_rings.hom_prime_finite_field import FiniteFieldHomomorphism_prime return FiniteFieldHomomorphism_prime(self, im_gens, check=check) - return FiniteFieldHomomorphism_generic(self, im_gens, check=check) + if is_FiniteField(self.codomain()): + return FiniteFieldHomomorphism_generic(self, im_gens, check=check) + # Currently, FiniteFieldHomomorphism_generic does not work if + # the codomain is not derived from the finite field base class; + # in that case, we have to fall back to the generic + # implementation for rings + else: + return RingHomomorphism_im_gens(self, im_gens, check=check) except (NotImplementedError, ValueError): try: return self._coerce_impl(im_gens) From 9bedce610130acbfbeb5904ab67284efde65c200 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Tue, 26 Feb 2019 16:23:33 +0100 Subject: [PATCH 051/406] Trac #27366: new output parameters for affine_hull --- src/sage/geometry/polyhedron/base.py | 113 +++++++++++++++++++++++---- 1 file changed, 96 insertions(+), 17 deletions(-) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 9e74262fdd3..14bbf7bfe47 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -6571,7 +6571,9 @@ def is_combinatorially_isomorphic(self, other, algorithm='bipartite_graph'): else: return self.face_lattice().is_isomorphic(other.face_lattice()) - def affine_hull(self, as_affine_map=False, orthogonal=False, orthonormal=False, extend=False): + def affine_hull(self, as_polyhedron=None, as_affine_map=False, + orthogonal=False, orthonormal=False, extend=False, + return_all_data=False): """ Return the affine hull. @@ -6586,30 +6588,60 @@ def affine_hull(self, as_affine_map=False, orthogonal=False, orthonormal=False, INPUT: - - ``as_affine_map`` (boolean, default = False) -- If ``False``, return - a polyhedron. If ``True``, return the affine transformation, - that sends the embedded polytope to a fulldimensional one. + - ``as_polyhedron`` (boolean or the default ``None``) and + ``as_affine_map`` (boolean, default ``False``) -- control the output + + The default ``as_polyhedron=None`` translates to + ``as_polyhedron=not as_affine_map``, + therefore to ``as_polyhedron=True`` if nothing is specified. + + If exactly one of either ``as_polyhedron`` or ``as_affine_map`` is + set, then either a polyhedron or the affine transformation + is returned. The affine transformation + sends the embedded polytope to a fulldimensional one. It is given as a pair ``(A, b)``, where A is a linear transformation and ``b`` is a vector, and the affine transformation sends ``v`` to ``A(v)+b``. - - ``orthogonal`` (boolean, default = False) -- if ``True``, + If both ``as_polyhedron`` and ``as_affine_map`` are set, then + both are returned, encapsulated in a dictionary. + + - ``orthogonal`` (boolean, default ``False``) -- if ``True``, provide an orthogonal transformation. - - ``orthonormal`` (boolean, default = False) -- if ``True``, + - ``orthonormal`` (boolean, default ``False``) -- if ``True``, provide an orthonormal transformation. If the base ring does not provide the neccessary square roots, the extend parameter needs to be set to ``True``. - - ``extend`` (boolean, default = False) -- if ``True``, + - ``extend`` (boolean, default ``False``) -- if ``True``, allow base ring to be extended if neccessary. This becomes relevant when requiering an orthonormal transformation. + - ``return_all_data`` (boolean, default ``False``) + + If set, then ``as_polyhedron`` and ``as_affine_map` will set + (possibly overridden) and additional (internal) data concerning + the transformation is returned. Everything is encapsulated + in a dictionary in this case. + OUTPUT: A full-dimensional polyhedron or a linear transformation, - depending on the parameter ``as_affine_map``. + depending on the parameters ``as_polyhedron`` and ``as_affine_map``, + or a dictionary containing all data (parameter ``return_all_data``). + In case the output is a dictionary, the following entries might + be included: + + - ``polyhedron`` -- the affine hull of the original polyhedron + + - ``linear_transformation`` and ``shift`` -- the affine map + + - ``polyhedron_base`` and ``polyhedron_base_vertices`` -- the points + and vertices used in the transformation. The original polyhedron + equals the polyhedron created by the ``polyhedron_base_vertices`` + and then shifted by ``polyhedron_base``. .. TODO: @@ -6841,24 +6873,71 @@ def affine_hull(self, as_affine_map=False, orthogonal=False, orthonormal=False, sage: Polyhedron([(2,3,4)]).affine_hull() A 0-dimensional polyhedron in ZZ^0 defined as the convex hull of 1 vertex - """ + + Return polyhedron and affine map:: + + sage: S = polytopes.simplex(2) + sage: S.affine_hull(orthogonal=True, + ....: as_polyhedron=True, as_affine_map=True) + {'linear_transformation': Vector space morphism represented by the matrix: + [ 0 1] + [ 1 -1/2] + [ -1 -1/2] + Domain: Vector space of dimension 3 over Rational Field + Codomain: Vector space of dimension 2 over Rational Field, + 'polyhedron': A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 3 vertices, + 'shift': (1, 1/2)} + + Return additional data:: + + sage: S.affine_hull(orthogonal=True, return_all_data=True) + {'linear_transformation': Vector space morphism represented by the matrix: + [ 0 1] + [ 1 -1/2] + [ -1 -1/2] + Domain: Vector space of dimension 3 over Rational Field + Codomain: Vector space of dimension 2 over Rational Field, + 'polyhedron': A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 3 vertices, + 'polyhedron_base': (0, 0, 1), + 'polyhedron_base_vertices': [A vertex at (0, 1, -1), + A vertex at (1, 0, -1)], + 'shift': (1, 1/2)} + """ + if as_polyhedron is None: + as_polyhedron = not as_affine_map + if not as_affine_map and not as_polyhedron: + raise ValueError('combining "as_affine_map=False" and ' + '"as_polyhedron=False" not allowed') + if return_all_data: + as_polyhedron = True + as_affine_map = True + + result = {} + # handle trivial full-dimensional case if self.ambient_dim() == self.dim(): - if as_affine_map: - return linear_transformation(matrix(self.base_ring(), self.dim(), self.dim(), self.base_ring().one())), self.ambient_space().zero() - return self + result['polyhedron'] = self + if as_affine_map or return_all_data: + result['linear_transformation'] = linear_transformation(matrix(self.base_ring(), self.dim(), self.dim(), self.base_ring().one())) + result['shift'] = self.ambient_space().zero() - if orthogonal or orthonormal: + elif orthogonal or orthonormal: # see TODO if not self.is_compact(): raise NotImplementedError('"orthogonal=True" and "orthonormal=True" work only for compact polyhedra') # translate 0th vertex to the origin - Q = self.translation(-vector(self.vertices()[0])) - v = next((_ for _ in Q.vertices() if _.vector() == Q.ambient_space().zero()), None) + v0 = vector(self.vertices()[0]) + Q = self.translation(-v0) + q0 = next((_ for _ in Q.vertices() if _.vector() == Q.ambient_space().zero()), None) # finding the zero in Q; checking that Q actually has a vertex zero - assert v.vector() == Q.ambient_space().zero() + assert q0.vector() == Q.ambient_space().zero() + q0_neighbors = list(itertools.islice(q0.neighbors(), self.dim())) # choose as an affine basis the neighbors of the origin vertex in Q - M = matrix(self.base_ring(), self.dim(), self.ambient_dim(), [list(w) for w in itertools.islice(v.neighbors(), self.dim())]) + M = matrix(self.base_ring(), self.dim(), self.ambient_dim(), + [list(w) for w in q0_neighbors]) + if return_all_data: + result['polyhedron_base'] = v0 + result['polyhedron_base_vertices'] = q0_neighbors # Switch base_ring to AA if neccessary, # since gram_schmidt needs to be able to take square roots. # Pick orthonormal basis and transform all vertices accordingly From 2075a6bc042a4e1bcf9941fa52e542fd29e76124 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Tue, 26 Feb 2019 16:24:22 +0100 Subject: [PATCH 052/406] Trac #27366: use new parameter-options in .volume --- src/sage/geometry/polyhedron/base.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 14bbf7bfe47..7a67e5c2ef0 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -5048,9 +5048,13 @@ def volume(self, measure='ambient', engine='auto', **kwds): from sage.rings.infinity import infinity return infinity # use an orthogonal transformation, which preserves volume up to a factor provided by the transformation matrix - A, b = self.affine_hull(orthogonal=True, as_affine_map=True) - Adet = (A.matrix().transpose() * A.matrix()).det() - return self.affine_hull(orthogonal=True).volume(measure='ambient', engine=engine, **kwds) / sqrt(Adet) + affine_hull = self.affine_hull( + orthogonal=True, as_polyhedron=True, as_affine_map=True) + polyhedron = affine_hull['polyhedron'] + A = affine_hull['linear_transformation'].matrix() + b = affine_hull['shift'] + Adet = (A.transpose() * A).det() + return polyhedron.volume(measure='ambient', engine=engine, **kwds) / sqrt(Adet) elif measure == 'induced_rational': if self.dim() < self.ambient_dim() and engine != 'latte': raise TypeError("The induced rational measure can only be computed with the engine set to `auto` or `latte`") From e9ed7ee67fc08c2368aac374fe126e9989f6271f Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Tue, 26 Feb 2019 16:31:55 +0100 Subject: [PATCH 053/406] Trac #27366: fixup (lines forgotten to commit in ~2) --- src/sage/geometry/polyhedron/base.py | 66 +++++++++++++++++----------- 1 file changed, 41 insertions(+), 25 deletions(-) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 7a67e5c2ef0..141b9738bc3 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -6953,32 +6953,48 @@ def affine_hull(self, as_polyhedron=None, as_affine_map=False, raise ValueError('The base ring needs to be extended; try with "extend=True"') M = matrix(AA, M) A = M.gram_schmidt(orthonormal=orthonormal)[0] + if as_polyhedron: + result['polyhedron'] = Polyhedron( + [A*vector(A.base_ring(), v) for v in Q.vertices()], + base_ring=A.base_ring()) if as_affine_map: - return linear_transformation(A, side='right'), -A*vector(A.base_ring(), self.vertices()[0]) - return Polyhedron([A*vector(A.base_ring(), v) for v in Q.vertices()], base_ring=A.base_ring()) - - # translate one vertex to the origin - v0 = self.vertices()[0].vector() - gens = [] - for v in self.vertices()[1:]: - gens.append(v.vector() - v0) - for r in self.rays(): - gens.append(r.vector()) - for l in self.lines(): - gens.append(l.vector()) - - # Pick subset of coordinates to coordinatize the affine span - pivots = matrix(gens).pivots() - - def pivot(indexed): - return [indexed[i] for i in pivots] - - vertices = [pivot(_) for _ in self.vertices()] - rays = [pivot(_) for _ in self.rays()] - lines = [pivot(_) for _ in self.lines()] - if as_affine_map: - raise NotImplementedError('"as_affine_map=True" only works with "orthogonal=True" and "orthonormal=True"') - return Polyhedron(vertices=vertices, rays=rays, lines=lines, base_ring=self.base_ring()) + result['linear_transformation'] = linear_transformation(A, side='right') + result['shift'] = -A*vector(A.base_ring(), self.vertices()[0]) + + else: + # translate one vertex to the origin + v0 = self.vertices()[0].vector() + gens = [] + for v in self.vertices()[1:]: + gens.append(v.vector() - v0) + for r in self.rays(): + gens.append(r.vector()) + for l in self.lines(): + gens.append(l.vector()) + + # Pick subset of coordinates to coordinatize the affine span + pivots = matrix(gens).pivots() + + def pivot(indexed): + return [indexed[i] for i in pivots] + + vertices = [pivot(_) for _ in self.vertices()] + rays = [pivot(_) for _ in self.rays()] + lines = [pivot(_) for _ in self.lines()] + if as_affine_map: + raise NotImplementedError('"as_affine_map=True" only works with "orthogonal=True" and "orthonormal=True"') + + result['polyhedron'] = Polyhedron( + vertices=vertices, rays=rays, lines=lines, + base_ring=self.base_ring()) + + # assemble result + if return_all_data or (as_polyhedron and as_affine_map): + return result + elif as_affine_map: + return result['linear_transformation'], result['shift'] + else: + return result['polyhedron'] def _polymake_init_(self): """ From 0cf6bc5b1307db2c4561c9b05babd9e0889e14e0 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Tue, 26 Feb 2019 16:50:46 +0100 Subject: [PATCH 054/406] Trac #27366: docstring fixup --- src/sage/geometry/polyhedron/base.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 141b9738bc3..df406cf0f7e 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -6603,9 +6603,9 @@ def affine_hull(self, as_polyhedron=None, as_affine_map=False, set, then either a polyhedron or the affine transformation is returned. The affine transformation sends the embedded polytope to a fulldimensional one. - It is given as a pair ``(A, b)``, where A is a linear transformation - and ``b`` is a vector, and the affine transformation sends ``v`` to - ``A(v)+b``. + It is given as a pair `(A, b)`, where A is a linear transformation + and `b` is a vector, and the affine transformation sends `v` to + `A(v)+b`. If both ``as_polyhedron`` and ``as_affine_map`` are set, then both are returned, encapsulated in a dictionary. @@ -6624,7 +6624,7 @@ def affine_hull(self, as_polyhedron=None, as_affine_map=False, - ``return_all_data`` (boolean, default ``False``) - If set, then ``as_polyhedron`` and ``as_affine_map` will set + If set, then ``as_polyhedron`` and ``as_affine_map`` will set (possibly overridden) and additional (internal) data concerning the transformation is returned. Everything is encapsulated in a dictionary in this case. From f8cb228c757af09b788991a02c9bbc8989773458 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Thu, 7 Mar 2019 10:42:10 +0100 Subject: [PATCH 055/406] Trac #27366 factor out parametric_form --- src/sage/geometry/polyhedron/base.py | 32 ++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index df406cf0f7e..44051392300 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -6575,6 +6575,38 @@ def is_combinatorially_isomorphic(self, other, algorithm='bipartite_graph'): else: return self.face_lattice().is_isomorphic(other.face_lattice()) + def parametric_form(self): + r""" + Return a parametric form of this polyhedron. + + This method is, for example, called when creating the + :meth:`affine_hull`. + + OUTPUT: + + A pair `(v_0, V)` where `v_0` is a vector and `V` a tuple of vectors. + The original polyhedron equals the polyhedron created by the + vertices `V` and then shifted by `v_0`, i.e., + the original polyhedron equals + `v_0 + \sum_{v \in V} t_v v` with `t_v \in [0,1]`. + + EXAMPLES:: + + sage: polytopes.simplex(2).parametric_form() + ((0, 0, 1), ((0, 1, -1), (1, 0, -1))) + """ + if not self.is_compact(): + raise NotImplementedError('method works only for compact polyhedra') + # translate 0th vertex to the origin + v0 = vector(self.vertices()[0]) + Q = self.translation(-v0) + q0 = next((_ for _ in Q.vertices() if _.vector() == Q.ambient_space().zero()), None) + # finding the zero in Q; checking that Q actually has a vertex zero + assert q0.vector() == Q.ambient_space().zero() + q0_neighbors = tuple(vector(n) for n in + itertools.islice(q0.neighbors(), self.dim())) + return v0, q0_neighbors + def affine_hull(self, as_polyhedron=None, as_affine_map=False, orthogonal=False, orthonormal=False, extend=False, return_all_data=False): From 8fa37df46b34a787eb6cc7cc00b4b97589af8b84 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Thu, 7 Mar 2019 10:42:51 +0100 Subject: [PATCH 056/406] Trac #27366 use new .parametric_form --- src/sage/geometry/polyhedron/base.py | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 44051392300..8da6d3dea5b 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -6961,19 +6961,12 @@ def affine_hull(self, as_polyhedron=None, as_affine_map=False, # see TODO if not self.is_compact(): raise NotImplementedError('"orthogonal=True" and "orthonormal=True" work only for compact polyhedra') - # translate 0th vertex to the origin - v0 = vector(self.vertices()[0]) - Q = self.translation(-v0) - q0 = next((_ for _ in Q.vertices() if _.vector() == Q.ambient_space().zero()), None) - # finding the zero in Q; checking that Q actually has a vertex zero - assert q0.vector() == Q.ambient_space().zero() - q0_neighbors = list(itertools.islice(q0.neighbors(), self.dim())) - # choose as an affine basis the neighbors of the origin vertex in Q - M = matrix(self.base_ring(), self.dim(), self.ambient_dim(), - [list(w) for w in q0_neighbors]) + parametric_form = self.parametric_form() if return_all_data: - result['polyhedron_base'] = v0 - result['polyhedron_base_vertices'] = q0_neighbors + result['parametric_form'] = parametric_form + v0, vi = parametric_form + # choose as an affine basis the neighbors of the origin vertex + M = matrix(self.base_ring(), self.dim(), self.ambient_dim(), vi) # Switch base_ring to AA if neccessary, # since gram_schmidt needs to be able to take square roots. # Pick orthonormal basis and transform all vertices accordingly From 294fbe1d3381694e6efbd74b25a6e4337628d392 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Thu, 7 Mar 2019 10:43:29 +0100 Subject: [PATCH 057/406] Trac #27366 output affine_map --- src/sage/geometry/polyhedron/base.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 8da6d3dea5b..9bed81bddc6 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -6954,8 +6954,8 @@ def affine_hull(self, as_polyhedron=None, as_affine_map=False, if self.ambient_dim() == self.dim(): result['polyhedron'] = self if as_affine_map or return_all_data: - result['linear_transformation'] = linear_transformation(matrix(self.base_ring(), self.dim(), self.dim(), self.base_ring().one())) - result['shift'] = self.ambient_space().zero() + result['affine_map'] = (linear_transformation(matrix(self.base_ring(), self.dim(), self.dim(), self.base_ring().one())), + self.ambient_space().zero()) elif orthogonal or orthonormal: # see TODO @@ -6980,11 +6980,12 @@ def affine_hull(self, as_polyhedron=None, as_affine_map=False, A = M.gram_schmidt(orthonormal=orthonormal)[0] if as_polyhedron: result['polyhedron'] = Polyhedron( - [A*vector(A.base_ring(), v) for v in Q.vertices()], + [A*vector(A.base_ring(), v) + for v in self.translation(-v0).vertices()], base_ring=A.base_ring()) if as_affine_map: - result['linear_transformation'] = linear_transformation(A, side='right') - result['shift'] = -A*vector(A.base_ring(), self.vertices()[0]) + L = linear_transformation(A, side='right') + result['affine_map'] = (L, -A*vector(A.base_ring(), self.vertices()[0])) else: # translate one vertex to the origin @@ -7017,7 +7018,7 @@ def pivot(indexed): if return_all_data or (as_polyhedron and as_affine_map): return result elif as_affine_map: - return result['linear_transformation'], result['shift'] + return result['affine_map'] else: return result['polyhedron'] From 6cf71c79eb5cc28782fcfc466c7887e3e8da66ea Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Thu, 7 Mar 2019 10:44:19 +0100 Subject: [PATCH 058/406] Trac #27366 update .volume (to new parameter set) --- src/sage/geometry/polyhedron/base.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 9bed81bddc6..de0bf1f9acb 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -5051,8 +5051,7 @@ def volume(self, measure='ambient', engine='auto', **kwds): affine_hull = self.affine_hull( orthogonal=True, as_polyhedron=True, as_affine_map=True) polyhedron = affine_hull['polyhedron'] - A = affine_hull['linear_transformation'].matrix() - b = affine_hull['shift'] + A = affine_hull['affine_map'][0].matrix() Adet = (A.transpose() * A).det() return polyhedron.volume(measure='ambient', engine=engine, **kwds) / sqrt(Adet) elif measure == 'induced_rational': From 477923ba02d85b7c193536397f1665171d4cc799 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Thu, 7 Mar 2019 10:44:50 +0100 Subject: [PATCH 059/406] Trac #27366 compute coordinate images --- src/sage/geometry/polyhedron/base.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index de0bf1f9acb..e7fda29ba6a 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -6985,6 +6985,17 @@ def affine_hull(self, as_polyhedron=None, as_affine_map=False, if as_affine_map: L = linear_transformation(A, side='right') result['affine_map'] = (L, -A*vector(A.base_ring(), self.vertices()[0])) + if return_all_data: + from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing + # columns of W are equal to the vertices of affine_hull['polyhedron'] + # in an order compatible with the vectors vi + W = matrix([list(L(v)) for v in vi]).transpose() + + # transform the coordinates + t = vector(PolynomialRing(self.base_ring(), 't', len(vi)).gens()) + beta = W.inverse() * t + coordinate_images = v0 + sum(b * v for b, v in zip(beta, vi)) + result['coordinate_images'] = tuple(coordinate_images) else: # translate one vertex to the origin From 2bbfdc2df0fb5a2ef869ce0c2bb13c7ef8e47d69 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Thu, 7 Mar 2019 10:45:27 +0100 Subject: [PATCH 060/406] Trac #27366: update docs --- src/sage/geometry/polyhedron/base.py | 57 ++++++++++++++++------------ 1 file changed, 33 insertions(+), 24 deletions(-) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index e7fda29ba6a..8545599e1f9 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -6671,12 +6671,24 @@ def affine_hull(self, as_polyhedron=None, as_affine_map=False, - ``polyhedron`` -- the affine hull of the original polyhedron - - ``linear_transformation`` and ``shift`` -- the affine map + - ``affine_map`` -- the affine map as a pair whose first component + is a linear transformation and its second component a shift; + see above. - - ``polyhedron_base`` and ``polyhedron_base_vertices`` -- the points - and vertices used in the transformation. The original polyhedron - equals the polyhedron created by the ``polyhedron_base_vertices`` - and then shifted by ``polyhedron_base``. + - ``parametric_form`` -- the points and vertices (as a pair) + used in the transformation. This is the output of + :meth:`parametric_form` which is called when determining the + affine hull. + + - ``coordinate_images`` -- a tuple of the images of the variables + in the standard coordinate system of the ambient space. These + images are degree one polynomials and are used for mapping a + function in the ambient space to a function in the affine hull + such that the values of this function are preserved. + + Note that all entries of this dictionary are compatible (in the + sense that the order of points/columns/etc are compatible) + with each other. .. TODO: @@ -6914,29 +6926,26 @@ def affine_hull(self, as_polyhedron=None, as_affine_map=False, sage: S = polytopes.simplex(2) sage: S.affine_hull(orthogonal=True, ....: as_polyhedron=True, as_affine_map=True) - {'linear_transformation': Vector space morphism represented by the matrix: - [ 0 1] - [ 1 -1/2] - [ -1 -1/2] - Domain: Vector space of dimension 3 over Rational Field - Codomain: Vector space of dimension 2 over Rational Field, - 'polyhedron': A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 3 vertices, - 'shift': (1, 1/2)} + {'affine_map': (Vector space morphism represented by the matrix: + [ 0 1] + [ 1 -1/2] + [ -1 -1/2] + Domain: Vector space of dimension 3 over Rational Field + Codomain: Vector space of dimension 2 over Rational Field, (1, 1/2)), + 'polyhedron': A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 3 vertices} Return additional data:: sage: S.affine_hull(orthogonal=True, return_all_data=True) - {'linear_transformation': Vector space morphism represented by the matrix: - [ 0 1] - [ 1 -1/2] - [ -1 -1/2] - Domain: Vector space of dimension 3 over Rational Field - Codomain: Vector space of dimension 2 over Rational Field, - 'polyhedron': A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 3 vertices, - 'polyhedron_base': (0, 0, 1), - 'polyhedron_base_vertices': [A vertex at (0, 1, -1), - A vertex at (1, 0, -1)], - 'shift': (1, 1/2)} + {'affine_map': (Vector space morphism represented by the matrix: + [ 0 1] + [ 1 -1/2] + [ -1 -1/2] + Domain: Vector space of dimension 3 over Rational Field + Codomain: Vector space of dimension 2 over Rational Field, (1, 1/2)), + 'coordinate_images': (2/3*t1, 1/2*t0 - 1/3*t1, -1/2*t0 - 1/3*t1 + 1), + 'parametric_form': ((0, 0, 1), ((0, 1, -1), (1, 0, -1))), + 'polyhedron': A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 3 vertices} """ if as_polyhedron is None: as_polyhedron = not as_affine_map From 3c09477e154b426b44c23a754a19605b1a668951 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Fri, 8 Mar 2019 14:18:57 +0100 Subject: [PATCH 061/406] Trac #27366: fix no-variables-bug-wrong-parent bug --- src/sage/geometry/polyhedron/base.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 8545599e1f9..e7c9a5ba6c1 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -7001,9 +7001,10 @@ def affine_hull(self, as_polyhedron=None, as_affine_map=False, W = matrix([list(L(v)) for v in vi]).transpose() # transform the coordinates - t = vector(PolynomialRing(self.base_ring(), 't', len(vi)).gens()) + T = PolynomialRing(self.base_ring(), 't', len(vi)) + t = vector(T.gens()) beta = W.inverse() * t - coordinate_images = v0 + sum(b * v for b, v in zip(beta, vi)) + coordinate_images = v0.change_ring(T) + sum(b * v for b, v in zip(beta, vi)) result['coordinate_images'] = tuple(coordinate_images) else: From 82ad45bc966b0aa83369ea78d9db90f354a0b1b2 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Tue, 12 Mar 2019 12:17:43 +0100 Subject: [PATCH 062/406] Trac #27366: cache affine hull --- src/sage/geometry/polyhedron/base.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index e7c9a5ba6c1..4790817c89e 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -6606,6 +6606,7 @@ def parametric_form(self): itertools.islice(q0.neighbors(), self.dim())) return v0, q0_neighbors + @cached_method def affine_hull(self, as_polyhedron=None, as_affine_map=False, orthogonal=False, orthonormal=False, extend=False, return_all_data=False): From 3bdea15eafc77d3d9cd593933097934ae3d68f1b Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Tue, 12 Mar 2019 12:18:45 +0100 Subject: [PATCH 063/406] Trac #27366: reintegrate parametric form and solve dimension bug --- src/sage/geometry/polyhedron/base.py | 86 +++++++++++++--------------- 1 file changed, 41 insertions(+), 45 deletions(-) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 4790817c89e..bb3515938f3 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -6574,38 +6574,6 @@ def is_combinatorially_isomorphic(self, other, algorithm='bipartite_graph'): else: return self.face_lattice().is_isomorphic(other.face_lattice()) - def parametric_form(self): - r""" - Return a parametric form of this polyhedron. - - This method is, for example, called when creating the - :meth:`affine_hull`. - - OUTPUT: - - A pair `(v_0, V)` where `v_0` is a vector and `V` a tuple of vectors. - The original polyhedron equals the polyhedron created by the - vertices `V` and then shifted by `v_0`, i.e., - the original polyhedron equals - `v_0 + \sum_{v \in V} t_v v` with `t_v \in [0,1]`. - - EXAMPLES:: - - sage: polytopes.simplex(2).parametric_form() - ((0, 0, 1), ((0, 1, -1), (1, 0, -1))) - """ - if not self.is_compact(): - raise NotImplementedError('method works only for compact polyhedra') - # translate 0th vertex to the origin - v0 = vector(self.vertices()[0]) - Q = self.translation(-v0) - q0 = next((_ for _ in Q.vertices() if _.vector() == Q.ambient_space().zero()), None) - # finding the zero in Q; checking that Q actually has a vertex zero - assert q0.vector() == Q.ambient_space().zero() - q0_neighbors = tuple(vector(n) for n in - itertools.islice(q0.neighbors(), self.dim())) - return v0, q0_neighbors - @cached_method def affine_hull(self, as_polyhedron=None, as_affine_map=False, orthogonal=False, orthonormal=False, extend=False, @@ -6676,10 +6644,10 @@ def affine_hull(self, as_polyhedron=None, as_affine_map=False, is a linear transformation and its second component a shift; see above. - - ``parametric_form`` -- the points and vertices (as a pair) - used in the transformation. This is the output of - :meth:`parametric_form` which is called when determining the - affine hull. + - ``parametric_form`` -- a pair `(v_0, V)` where `v_0` is a vector + and `V` a tuple of vectors. `v_0` is used as a base point + in the transformation and the vectors `V` are its neighbors + (in the already shifted polyhedron). - ``coordinate_images`` -- a tuple of the images of the variables in the standard coordinate system of the ambient space. These @@ -6947,6 +6915,25 @@ def affine_hull(self, as_polyhedron=None, as_affine_map=False, 'coordinate_images': (2/3*t1, 1/2*t0 - 1/3*t1, -1/2*t0 - 1/3*t1 + 1), 'parametric_form': ((0, 0, 1), ((0, 1, -1), (1, 0, -1))), 'polyhedron': A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 3 vertices} + + :: + + sage: P0 = Polyhedron( + ....: ieqs=[(0, -1, 0, 1, 1, 1), (0, 1, 1, 0, -1, -1), (0, -1, 1, 1, 0, 0), + ....: (0, 1, 0, 0, 0, 0), (0, 0, 1, 1, -1, -1), (0, 0, 0, 0, 0, 1), + ....: (0, 0, 0, 0, 1, 0), (0, 0, 0, 1, 0, -1), (0, 0, 1, 0, 0, 0)]) + sage: P = P0.intersection(Polyhedron(eqns=[(-1, 1, 1, 1, 1, 1)])) + sage: P.dim() + 4 + sage: P.affine_hull(orthogonal=True, as_affine_map=True)[0] + Vector space morphism represented by the matrix: + [ -1/3 -1/3 -1/6 1/12] + [ 1/2 0 1/6 1/12] + [ -1/3 2/3 0 0] + [ 1/2 0 -1/6 -1/12] + [ -1/3 -1/3 1/6 -1/12] + Domain: Vector space of dimension 5 over Rational Field + Codomain: Vector space of dimension 4 over Rational Field """ if as_polyhedron is None: as_polyhedron = not as_affine_map @@ -6970,23 +6957,28 @@ def affine_hull(self, as_polyhedron=None, as_affine_map=False, # see TODO if not self.is_compact(): raise NotImplementedError('"orthogonal=True" and "orthonormal=True" work only for compact polyhedra') - parametric_form = self.parametric_form() + # translate 0th vertex to the origin + v0 = vector(self.vertices()[0]) + Q = self.translation(-v0) + q0 = next((_ for _ in Q.vertices() if _.vector() == Q.ambient_space().zero()), None) + # finding the zero in Q; checking that Q actually has a vertex zero + assert q0.vector() == Q.ambient_space().zero() + vi = tuple(n.vector() for n in q0.neighbors()) if return_all_data: - result['parametric_form'] = parametric_form - v0, vi = parametric_form + result['parametric_form'] = (v0, vi) # choose as an affine basis the neighbors of the origin vertex - M = matrix(self.base_ring(), self.dim(), self.ambient_dim(), vi) + M = matrix(self.base_ring(), len(vi), self.ambient_dim(), vi) # Switch base_ring to AA if neccessary, # since gram_schmidt needs to be able to take square roots. # Pick orthonormal basis and transform all vertices accordingly # if the orthonormal transform makes it neccessary, change base ring. try: - A = M.gram_schmidt(orthonormal=orthonormal)[0] + A, G = M.gram_schmidt(orthonormal=orthonormal) except TypeError: if not extend: raise ValueError('The base ring needs to be extended; try with "extend=True"') M = matrix(AA, M) - A = M.gram_schmidt(orthonormal=orthonormal)[0] + A, G = M.gram_schmidt(orthonormal=orthonormal) if as_polyhedron: result['polyhedron'] = Polyhedron( [A*vector(A.base_ring(), v) @@ -6999,13 +6991,17 @@ def affine_hull(self, as_polyhedron=None, as_affine_map=False, from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing # columns of W are equal to the vertices of affine_hull['polyhedron'] # in an order compatible with the vectors vi - W = matrix([list(L(v)) for v in vi]).transpose() + def index_neq0(C): + return next(i for i, c in enumerate(C) if c != 0) + independent_vi = tuple(vi[index_neq0(tuple(c))] + for c in G.columns()) + W = matrix([list(L(v)) for v in independent_vi]).transpose() # transform the coordinates - T = PolynomialRing(self.base_ring(), 't', len(vi)) + T = PolynomialRing(self.base_ring(), 't', len(independent_vi)) t = vector(T.gens()) beta = W.inverse() * t - coordinate_images = v0.change_ring(T) + sum(b * v for b, v in zip(beta, vi)) + coordinate_images = v0.change_ring(T) + sum(b * v for b, v in zip(beta, independent_vi)) result['coordinate_images'] = tuple(coordinate_images) else: From ae5caadc312b0b7fec66fb9ee2fca3a0b35928b0 Mon Sep 17 00:00:00 2001 From: Travis Scholl Date: Fri, 12 Jun 2020 09:40:50 -0400 Subject: [PATCH 064/406] Replace finite/infinite generator selection in subgroups of abelian groups --- src/sage/groups/abelian_gps/abelian_group.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/sage/groups/abelian_gps/abelian_group.py b/src/sage/groups/abelian_gps/abelian_group.py index e89982d0596..a1f351cf46a 100644 --- a/src/sage/groups/abelian_gps/abelian_group.py +++ b/src/sage/groups/abelian_gps/abelian_group.py @@ -1521,6 +1521,12 @@ def __init__(self, ambient, gens, names="f"): (a, b^2, c) sage: F.order() +Infinity + + Testing ticket #18863:: + + sage: G = AbelianGroup(5,[2]) + sage: G.subgroup([prod(g^k for g,k in zip(G.gens(),[1,-2,3,-4,5]))]) + Multiplicative Abelian subgroup isomorphic to Z generated by {f0*f1^-2*f2^3*f3^-4*f4} """ from sage.interfaces.all import gap if not isinstance(ambient, AbelianGroup_class): @@ -1546,9 +1552,11 @@ def __init__(self, ambient, gens, names="f"): Gf = AbelianGroup(invsf, names=Gfgens) s1 = "G:= %s; gens := GeneratorsOfGroup(G)"%Gf._gap_init_() gap.eval(s1) - Hgensf = [x for x in Hgens if len(set(Ggens0).intersection(set(list(str(x)))))==0] - # computes the gens of H which do not occur ^^ in the infinite part of G - Hgens0 = [x for x in Hgens if not(x in Hgensf)] + Hgens0 = [ + x for x in Hgens if + any(e!=0 and (g.order() not in ZZ) for e,g in zip(x.exponents(),ambient.gens())) + ] + Hgensf = [x for x in Hgens if x not in Hgens0] # the "infinite" generators of H for i in range(len(Gfgens)): cmd = ("%s := gens["+str(i+1)+"]")%Gfgens[i] From 5eef20d721d67b86604715336f1436ce3fbe88bb Mon Sep 17 00:00:00 2001 From: Travis Scholl Date: Fri, 12 Jun 2020 09:46:21 -0400 Subject: [PATCH 065/406] I think this is the proper way to link to trac tickets in docs --- src/sage/groups/abelian_gps/abelian_group.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/groups/abelian_gps/abelian_group.py b/src/sage/groups/abelian_gps/abelian_group.py index a1f351cf46a..db9a0dba623 100644 --- a/src/sage/groups/abelian_gps/abelian_group.py +++ b/src/sage/groups/abelian_gps/abelian_group.py @@ -1522,7 +1522,7 @@ def __init__(self, ambient, gens, names="f"): sage: F.order() +Infinity - Testing ticket #18863:: + Testing ticket :trac:`18863`:: sage: G = AbelianGroup(5,[2]) sage: G.subgroup([prod(g^k for g,k in zip(G.gens(),[1,-2,3,-4,5]))]) From 6972030d8a15bf6c9371658826e35de746e4ab1b Mon Sep 17 00:00:00 2001 From: Michael Jung Date: Fri, 24 Jul 2020 12:49:54 +0200 Subject: [PATCH 066/406] Trac #30211: sequence function added --- .../differentiable/characteristic_class.py | 91 +++++++++++++++++++ 1 file changed, 91 insertions(+) diff --git a/src/sage/manifolds/differentiable/characteristic_class.py b/src/sage/manifolds/differentiable/characteristic_class.py index da2e2299430..6957dd357d0 100644 --- a/src/sage/manifolds/differentiable/characteristic_class.py +++ b/src/sage/manifolds/differentiable/characteristic_class.py @@ -319,6 +319,7 @@ #****************************************************************************** from sage.structure.unique_representation import UniqueRepresentation +from sage.misc.cachefunc import cached_method from sage.structure.sage_object import SageObject from sage.symbolic.ring import SR @@ -622,6 +623,96 @@ def function(self): """ return self._func + @cached_method + def sequence(self, ring=SR): + r""" + Return the multiplicative/additive sequence (depending on the class + type of ``self``) of ``self.function`` in terms of elementary symmetric + functions `e_i`. + + If `f(x)` is the function with respect to ``self`` then its + multiplicative sequence is given by + + .. MATH:: + + \Pi_{i = 1}^n f(x_i) = \sum^n_{i=0} c_i \, e_i(x_1, \ldots, x_n) + + whereas its additive sequence is given by + + .. MATH:: + + \sum_{i = 1}^n f(x_i) = \sum^n_{i=0} c_i \, e_i(x_1, \ldots, x_n). + + Here, `e_i` denotes the `i`-th elementary symmetric function. + + INPUT: + + - ``ring`` -- (default: ``SR``) the base ring of the symmetric + function ring; in most cases, one can assume ``QQ`` which is + supposed to work faster + + OUTPUT: + + - a symmetric function in the elementary symmetric basis represented + by an instance of + :class:`~sage.combinat.sf.elementary.SymmetricFunctionAlgebra_elementary` + + EXAMPLE: + + Consider the multiplicative sequence of the `\hat{A}` class:: + + sage: M = Manifold(8, 'M') + sage: A = M.tangent_bundle().characteristic_class('AHat') + sage: A.sequence() + e[] - 1/24*e[1] + 7/5760*e[1, 1] - 1/1440*e[2] + + This is an element of the symmetric functions over the symbolic ring:: + + sage: A.sequence().parent() + Symmetric Functions over Symbolic Ring in the elementary basis + + To get the sequence as an element of usual polynomial ring, we can do + the following:: + + sage: P = PolynomialRing(SR, 'e', 3) + sage: poly = P(sum(c * prod(P.gens()[i] for i in p) + ....: for p, c in A.sequence())) + sage: poly + 7/5760*e1^2 + (-1/24)*e1 + (-1/1440)*e2 + 1 + + ..SEEALSO:: + + See :class:`~sage.combinat.sf.elementary.SymmetricFunctionAlgebra_elementary` + for detailed information about elementary symmetric functions. + + """ + if self._class_type == 'Pfaffian': + return NotImplementedError('this functionality is not supported ' + 'for characteristic classes of ' + 'Pfaffian type') + + from sage.combinat.sf.sf import SymmetricFunctions + from sage.misc.misc_c import prod + + Sym = SymmetricFunctions(ring) + + coeff = self._get_coeff_list(distinct_real=False) + if self._class_type == 'multiplicative': + from sage.combinat.partition import Partitions + # Get the multiplicative sequence in the monomial basis: + mon_pol = Sym.m().sum(prod(ring(coeff[i]) for i in p) * Sym.m()[p] + for k in range(len(coeff)) + for p in Partitions(k)) + elif self._class_type == 'additive': + # Express the additive sequence in the monomial basis: + summands = [ring(coeff[k]) * Sym.m()[k] + for k in range(1, len(coeff))] + # The 0th order term must be treated separately: + summands.append(self._vbundle._rank * coeff[0] * Sym.m()[0]) + mon_pol = Sym.m().sum(summands) + # Convert to elementary symmetric polynomials: + return Sym.e()(mon_pol) + def get_form(self, connection, cmatrices=None): r""" Return the form representing ``self`` with respect to the given From c7414bdcd94df01d5ca6eacad9344f6243457b64 Mon Sep 17 00:00:00 2001 From: Michael Jung Date: Tue, 28 Jul 2020 12:42:04 +0200 Subject: [PATCH 067/406] Trac #30211: docstring syntax --- src/sage/manifolds/differentiable/characteristic_class.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/manifolds/differentiable/characteristic_class.py b/src/sage/manifolds/differentiable/characteristic_class.py index 6957dd357d0..9781a0f0e17 100644 --- a/src/sage/manifolds/differentiable/characteristic_class.py +++ b/src/sage/manifolds/differentiable/characteristic_class.py @@ -657,7 +657,7 @@ def sequence(self, ring=SR): by an instance of :class:`~sage.combinat.sf.elementary.SymmetricFunctionAlgebra_elementary` - EXAMPLE: + EXAMPLES: Consider the multiplicative sequence of the `\hat{A}` class:: @@ -680,7 +680,7 @@ def sequence(self, ring=SR): sage: poly 7/5760*e1^2 + (-1/24)*e1 + (-1/1440)*e2 + 1 - ..SEEALSO:: + .. SEEALSO:: See :class:`~sage.combinat.sf.elementary.SymmetricFunctionAlgebra_elementary` for detailed information about elementary symmetric functions. From 21fee92369a6b47b5e07ba272e72bc195dd6c1f1 Mon Sep 17 00:00:00 2001 From: Michael Jung Date: Sun, 25 Oct 2020 10:48:27 +0100 Subject: [PATCH 068/406] Trac #30211: "ring=QQ" --- src/sage/manifolds/differentiable/characteristic_class.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sage/manifolds/differentiable/characteristic_class.py b/src/sage/manifolds/differentiable/characteristic_class.py index 9781a0f0e17..87f97eee0f5 100644 --- a/src/sage/manifolds/differentiable/characteristic_class.py +++ b/src/sage/manifolds/differentiable/characteristic_class.py @@ -321,7 +321,7 @@ from sage.structure.unique_representation import UniqueRepresentation from sage.misc.cachefunc import cached_method from sage.structure.sage_object import SageObject -from sage.symbolic.ring import SR +from sage.rings.rational_field import QQ ################################################################################ ## Separate functions @@ -624,7 +624,7 @@ def function(self): return self._func @cached_method - def sequence(self, ring=SR): + def sequence(self, ring=QQ): r""" Return the multiplicative/additive sequence (depending on the class type of ``self``) of ``self.function`` in terms of elementary symmetric @@ -647,9 +647,9 @@ def sequence(self, ring=SR): INPUT: - - ``ring`` -- (default: ``SR``) the base ring of the symmetric + - ``ring`` -- (default: ``QQ``) the base ring of the symmetric function ring; in most cases, one can assume ``QQ`` which is - supposed to work faster + supposed to work faster, if it doesn't work, try ``SR`` instead. OUTPUT: From 4765f29e000ba667e8099ff030d87ffe4bc66373 Mon Sep 17 00:00:00 2001 From: Michael Jung Date: Sun, 25 Oct 2020 11:20:49 +0100 Subject: [PATCH 069/406] Trac #30211: distinct real --- .../differentiable/characteristic_class.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/sage/manifolds/differentiable/characteristic_class.py b/src/sage/manifolds/differentiable/characteristic_class.py index 195f78cfda5..b610b4a9c4f 100644 --- a/src/sage/manifolds/differentiable/characteristic_class.py +++ b/src/sage/manifolds/differentiable/characteristic_class.py @@ -322,6 +322,7 @@ from sage.structure.unique_representation import UniqueRepresentation from sage.misc.cachefunc import cached_method from sage.structure.sage_object import SageObject +from sage.symbolic.ring import SR from sage.rings.rational_field import QQ ################################################################################ @@ -474,7 +475,7 @@ def __init__(self, vbundle, func, class_type='multiplicative', name=None, self._coeff_list = self._get_coeff_list() self._init_derived() - def _get_coeff_list(self): + def _get_coeff_list(self, distinct_real=True): r""" Return the list of coefficients of the Taylor expansion at zero of the function. @@ -492,7 +493,7 @@ def _get_coeff_list(self): def_var = self._func.default_variable() # Use a complex variable without affecting the old one: new_var = SR.symbol('x_char_class_', domain='complex') - if self._vbundle._field_type == 'real': + if self._vbundle._field_type == 'real' and distinct_real: if self._class_type == 'additive': func = self._func.subs({def_var: new_var ** 2}) / 2 elif self._class_type == 'multiplicative': @@ -509,6 +510,9 @@ def _get_coeff_list(self): else: func = self._func.subs({def_var: new_var}) + if self._vbundle._field_type == 'real' and not distinct_real: + pow_range = pow_range // 2 + return func.taylor(new_var, 0, pow_range).coefficients(sparse=False) def _init_derived(self): @@ -667,19 +671,19 @@ def sequence(self, ring=QQ): sage: A.sequence() e[] - 1/24*e[1] + 7/5760*e[1, 1] - 1/1440*e[2] - This is an element of the symmetric functions over the symbolic ring:: + This is an element of the symmetric functions over the rational field:: sage: A.sequence().parent() - Symmetric Functions over Symbolic Ring in the elementary basis + Symmetric Functions over Rational Field in the elementary basis To get the sequence as an element of usual polynomial ring, we can do the following:: - sage: P = PolynomialRing(SR, 'e', 3) + sage: P = PolynomialRing(QQ, 'e', 3) sage: poly = P(sum(c * prod(P.gens()[i] for i in p) ....: for p, c in A.sequence())) sage: poly - 7/5760*e1^2 + (-1/24)*e1 + (-1/1440)*e2 + 1 + 7/5760*e1^2 - 1/24*e1 - 1/1440*e2 + 1 .. SEEALSO:: From 7aa0ec5d36f81048c53beca87bd2adfffbebba6f Mon Sep 17 00:00:00 2001 From: Michael Jung Date: Tue, 27 Oct 2020 20:01:50 +0100 Subject: [PATCH 070/406] Trac #30211: symmetric polynomial via _from_dict --- .../differentiable/characteristic_class.py | 30 ++++++++++++------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/src/sage/manifolds/differentiable/characteristic_class.py b/src/sage/manifolds/differentiable/characteristic_class.py index b610b4a9c4f..74084b186f0 100644 --- a/src/sage/manifolds/differentiable/characteristic_class.py +++ b/src/sage/manifolds/differentiable/characteristic_class.py @@ -685,6 +685,15 @@ def sequence(self, ring=QQ): sage: poly 7/5760*e1^2 - 1/24*e1 - 1/1440*e2 + 1 + Get an additive sequence:: + + sage: E = M.vector_bundle(2, 'E', field='complex') + sage: ch = E.characteristic_class('ChernChar') + sage: ch.sequence() + 2*e[] + e[1] + 1/2*e[1, 1] + 1/6*e[1, 1, 1] + 1/24*e[1, 1, 1, 1] + - e[2] - 1/2*e[2, 1] - 1/6*e[2, 1, 1] + 1/12*e[2, 2] + 1/2*e[3] + + 1/6*e[3, 1] - 1/6*e[4] + .. SEEALSO:: See :class:`~sage.combinat.sf.elementary.SymmetricFunctionAlgebra_elementary` @@ -702,19 +711,20 @@ def sequence(self, ring=QQ): Sym = SymmetricFunctions(ring) coeff = self._get_coeff_list(distinct_real=False) + from sage.combinat.partition import Partitions + m = Sym.m() if self._class_type == 'multiplicative': - from sage.combinat.partition import Partitions # Get the multiplicative sequence in the monomial basis: - mon_pol = Sym.m().sum(prod(ring(coeff[i]) for i in p) * Sym.m()[p] - for k in range(len(coeff)) - for p in Partitions(k)) + mon_pol = m._from_dict({p: prod(ring(coeff[i]) for i in p) + for k in range(len(coeff)) + for p in Partitions(k)}) elif self._class_type == 'additive': - # Express the additive sequence in the monomial basis: - summands = [ring(coeff[k]) * Sym.m()[k] - for k in range(1, len(coeff))] - # The 0th order term must be treated separately: - summands.append(self._vbundle._rank * coeff[0] * Sym.m()[0]) - mon_pol = Sym.m().sum(summands) + # Express the additive sequence in the monomial basis, the 0th + # order term must be treated separately: + + m_dict = {Partitions(0)([]): self._vbundle._rank * ring(coeff[0])} + m_dict.update({Partitions(k)([k]): ring(coeff[k]) for k in range(1, len(coeff))}) + mon_pol = m._from_dict(m_dict) # Convert to elementary symmetric polynomials: return Sym.e()(mon_pol) From 7229cefe8a60f1482f91d2670863843513791b43 Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Fri, 25 Dec 2020 21:37:30 +0100 Subject: [PATCH 071/406] first implementation of lazy p-adics --- src/sage/rings/padics/all.py | 2 +- src/sage/rings/padics/factory.py | 12 +- src/sage/rings/padics/padic_generic.py | 3 +- src/sage/rings/padics/padic_lazy.py | 184 +++++++++++ src/sage/rings/padics/padic_lazy_element.py | 342 ++++++++++++++++++++ 5 files changed, 538 insertions(+), 5 deletions(-) create mode 100644 src/sage/rings/padics/padic_lazy.py create mode 100644 src/sage/rings/padics/padic_lazy_element.py diff --git a/src/sage/rings/padics/all.py b/src/sage/rings/padics/all.py index 758fb2f69c5..32b77f9c65b 100644 --- a/src/sage/rings/padics/all.py +++ b/src/sage/rings/padics/all.py @@ -1,5 +1,5 @@ from .generic_nodes import is_pAdicField, is_pAdicRing -from .factory import Zp, Zq, Zp as pAdicRing, ZpCR, ZpCA, ZpFM, ZpFP, ZpLC, ZpLF, ZqCR, ZqCA, ZqFM, ZqFP #, ZpL, ZqL +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 diff --git a/src/sage/rings/padics/factory.py b/src/sage/rings/padics/factory.py index 6990884468e..ffcaa4ccf5f 100644 --- a/src/sage/rings/padics/factory.py +++ b/src/sage/rings/padics/factory.py @@ -45,6 +45,7 @@ pAdicFieldCappedRelative, pAdicFieldFloatingPoint, pAdicFieldLattice) +from .padic_lazy import pAdicRingLazy from . import padic_printing ###################################################### @@ -696,7 +697,7 @@ def create_key(self, p, prec = None, type = 'capped-rel', print_mode = None, check = True if label is not None and type not in ['lattice-cap','lattice-float']: raise ValueError("label keyword only supported for lattice precision") - 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) + 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', 'lazy'], label) def create_object(self, version, key): r""" @@ -1873,7 +1874,7 @@ def create_key(self, p, prec = None, type = 'capped-rel', print_mode = None, raise ValueError("label keyword only supported for lattice precision") 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', 'lattice-cap', 'lattice-float'], + ['capped-rel', 'fixed-mod', 'capped-abs', 'floating-point', 'lattice-cap', 'lattice-float', 'lazy'], label=label) def create_object(self, version, key): @@ -1902,7 +1903,7 @@ def create_object(self, version, key): # 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', 'lattice-cap', 'lattice-float']) + print_max_terms, None, False, ['capped-rel', 'fixed-mod', 'capped-abs', 'lattice-cap', 'lattice-float', 'lazy']) try: obj = self._cache[version, key]() if obj is not None: @@ -1922,6 +1923,8 @@ def create_object(self, version, key): 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 == 'lazy': + return pAdicRingLazy(p, prec) elif type[:8] == 'lattice-': subtype = type[8:] return pAdicRingLattice(p, prec, subtype, {'mode': print_mode, 'pos': print_pos, 'sep': print_sep, 'alphabet': print_alphabet, @@ -2889,6 +2892,9 @@ def ZpLF(p, prec=None, *args, **kwds): """ return Zp(p, prec, 'lattice-float', *args, **kwds) +def ZpL(p, prec=None, *args, **kwds): + return Zp(p, prec, 'lazy', *args, **kwds) + ####################################################################################################### # diff --git a/src/sage/rings/padics/padic_generic.py b/src/sage/rings/padics/padic_generic.py index 650b5137bf5..099911bea23 100644 --- a/src/sage/rings/padics/padic_generic.py +++ b/src/sage/rings/padics/padic_generic.py @@ -66,7 +66,8 @@ def __init__(self, base, p, prec, print_mode, names, element_class, category=Non category = PrincipalIdealDomains() category = category.Metric().Complete() LocalGeneric.__init__(self, base, prec, names, element_class, category) - self._printer = pAdicPrinter(self, print_mode) + if print_mode is not None: + self._printer = pAdicPrinter(self, print_mode) self._qth_roots_of_unity = [ (1, Infinity) ] def some_elements(self): diff --git a/src/sage/rings/padics/padic_lazy.py b/src/sage/rings/padics/padic_lazy.py new file mode 100644 index 00000000000..46937c89c14 --- /dev/null +++ b/src/sage/rings/padics/padic_lazy.py @@ -0,0 +1,184 @@ +r""" +This module provides basic support for lazy p-adic integers. + + sage: R = ZpL(5) + sage: R + Lazy 5-adic Ring + +One creates elements as usual:: + + sage: a = R(17/42) + sage: a + ...00244200244200244201 + + sage: R.random_element() # random + ...21013213133412431402 + +By default, 20 digits of precision are computed (and printed). +If more (or less) digits are needed, one can specify it as follows:: + + sage: b = R(42/17, prec=30) + sage: b + ...104201213402432310420121340301 + +Alternatively, one can increase the precision using the method meth:`jump`:: + + sage: a.jump(30) + sage: a + ...244200244200244200244200244201 + +Standard operations are implemented:: + + sage: a + b + ...03232011214322140002 + sage: a - b + ...42311334324023403400 + + sage: a * b + ...00000000000000000001 + sage: a / b + ...12442142113021233401 + +We observe again that only 20 digits are computed. +If we need more, we have to create a new variable:: + + sage: c = a / b + sage: c.jump(40) + sage: c + ...4230030104200433321312442142113021233401 + +Note that this automatically increases the precision on `a` and `b`:: + + sage: a + ...4200244200244200244200244200244200244201 + sage: b + ...2134024323104201213402432310420121340301 + +Equality test works but equality is only checked up to the +*minimal* current precision of the elements:: + + sage: c == R(289/1764, prec=100) + True + sage: c == R(289/1764 + 5^50, prec=100) + True + + sage: c.jump(100) + sage: c == R(289/1764 + 5^50, prec=100) + False + sage: c == R(289/1764 + 5^50) + True + +A quite interesting feature with lazy p-adics is the possibility to +create (in somes cases) self-referrent numbers. Here is an example. +We first declare a new variable as follows:: + + sage: x = R() + sage: x + O(1) + +We then write down an equation satisfied by `x`:: + + sage: x == 1 + 5*x^2 + True + +The variable `x` now contains the unique solution of the above equation:: + + sage: x + ...04222412141121000211 + +This works because the `n`-th digit of the right hand size of the +defining equation only involves the `i`-th digits of `x` with `i < n` +(this is due to the factor `5`). + +As a comparison, the following produces an error:: + + sage: y = R() + sage: y == 1 + 3*y^2 + Traceback (most recent call last): + ... + RecursionError: definition looks circular + +Previous self-referrent definitions also work with system of equations:: + + sage: u = R(); v = R(); w = R() + + sage: u == 1 + 2*v + 3*w^2 + 5*u*v*w + True + sage: v == 2 + 4/w^3 + 10*(u + v + w)^2 + True + sage: w == 3 + 25*(u*v + v*w + u*w) + True + + sage: u + ...21111231040212114001 + sage: v + ...04313122332303332234 + sage: w + ...30020001022124410403 + +""" + +# **************************************************************************** +# Copyright (C) 2021 Xavier Caruso +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# http://www.gnu.org/licenses/ +# **************************************************************************** + + +from sage.rings.all import ZZ, QQ +from sage.rings.padics.generic_nodes import pAdicRingGeneric +from sage.rings.padics.padic_generic_element import pAdicGenericElement + +from sage.rings.padics.padic_lazy_element import pAdicLazyElement +from sage.rings.padics.padic_lazy_element import pAdicLazyElement_value +from sage.rings.padics.padic_lazy_element import pAdicLazyElement_random +from sage.rings.padics.padic_lazy_element import pAdicLazyElement_selfref + + +class pAdicRingLazy(pAdicRingGeneric): + def __init__(self, p, prec): + pAdicRingGeneric.__init__(self, ZZ, p, prec, None, None, None) + self._p = p + + def prime(self): + return self._p + + def _repr_(self): + return "Lazy %s-adic Ring" % self.prime() + + def _coerce_map_from_(self, R): + # why do I need this? + if R is ZZ: + return True + + def _element_constructor_(self, x, prec=None): + if type(x) is int and x == 0: + return pAdicLazyElement_selfref(self) + if isinstance(x, pAdicLazyElement): + return x + if isinstance(x, pAdicGenericElement): + return pAdicLazyElement_value(self, ZZ(x), prec, maxprec=x.precision_absolute()) + if prec is None: + prec = self.precision_cap() + if x in ZZ: + return pAdicLazyElement_value(self, ZZ(x), prec) + if x in QQ: + num = x.numerator() + denom = x.denominator() + if denom % self._p == 0: + raise ValueError("denominator is not a unit") + num = pAdicLazyElement_value(self, num) + denom = pAdicLazyElement_value(self, denom) + x = num / denom + x.jump(prec) + return x + + def _an_element_(self): + return pAdicLazyElement_value(self, 0) + + def random_element(self): + return pAdicLazyElement_random(self) diff --git a/src/sage/rings/padics/padic_lazy_element.py b/src/sage/rings/padics/padic_lazy_element.py new file mode 100644 index 00000000000..244cccb15f2 --- /dev/null +++ b/src/sage/rings/padics/padic_lazy_element.py @@ -0,0 +1,342 @@ +# **************************************************************************** +# Copyright (C) 2021 Xavier Caruso +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# http://www.gnu.org/licenses/ +# **************************************************************************** + + +from sage.structure.element import coerce_binop + +from sage.rings.all import ZZ +from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing +from sage.rings.padics.padic_generic_element import pAdicGenericElement + +from sage.rings.padics.precision_error import PrecisionError + + +class pAdicLazyElement(pAdicGenericElement): + def __init__(self, parent): + pAdicGenericElement.__init__(self, parent) + self._digits = [ ] + self._precision = 0 + + def prime(self): + return self.parent().prime() + + def next(self): + raise NotImplementedError("must be implemented in subclasses") + + def jump(self, prec): + while self._precision < prec: + self.next() + + def _repr_(self): + try: + self.jump(self.parent().precision_cap()) + except PrecisionError: + pass + if self._precision == 0: + return "O(1)" + s = "..." + for i in range(self._precision - 1, -1, -1): + if self.prime() > 9: + s += "|" + s += str(self._digits[i]) + return s + + def __getitem__(self, i): + if isinstance(i, slice): + if i.step is not None or i.stop is None: + raise IndexError + start = i.start + stop = i.stop + else: + start = i + stop = i + 1 + if start < 0: + start = 0 + if stop < 0 or start >= stop: + return ZZ(0) + self.jump(stop) + if isinstance(i, slice): + return self._digits[start:stop] + else: + return self._digits[start] + + @coerce_binop + def equal_at_current_precision(self, other): + for i in range(min(self._precision, other._precision)): + if self[i] != other[i]: + return False + return True + + def __eq__(self, other): + return self.equal_at_current_precision(other) + + def __lshift__(self, s): + return pAdicLazyElement_shift(self.parent(), self, ZZ(s)) + + def __rshift__(self, s): + return pAdicLazyElement_shift(self.parent(), self, ZZ(-s)) + + def _add_(self, other): + summands = [ ] + coeffs = [ ] + if isinstance(self, pAdicLazyElement_add): + summands.extend(self._summands) + coeffs.extend(self._coeffs) + else: + summands.append(self) + coeffs.append(1) + if isinstance(other, pAdicLazyElement_add): + summands.extend(other._summands) + coeffs.extend(other._coeffs) + else: + summands.append(other) + coeffs.append(1) + return pAdicLazyElement_add(self.parent(), summands, coeffs) + + def _sub_(self, other): + summands = [ ] + coeffs = [ ] + if isinstance(self, pAdicLazyElement_add): + summands.extend(self._summands) + coeffs.extend(self._coeffs) + else: + summands.append(self) + coeffs.append(1) + if isinstance(other, pAdicLazyElement_add): + summands.extend(other._summands) + coeffs.extend([ -c for c in other._coeffs ]) + else: + summands.append(other) + coeffs.append(-1) + return pAdicLazyElement_add(self.parent(), summands, coeffs) + + def _neg_(self): + return pAdicLazyElement_add(self.parent(), self, [-1]) + + def valuation_at_current_precision(self): + for i in range(self._precision): + if self._digits[i] != 0: + return i + return self._precision + + def _mul_(self, other): + vs = self.valuation_at_current_precision() + vo = other.valuation_at_current_precision() + return pAdicLazyElement_mul(self.parent(), self >> vs, other >> vo) << (vs + vo) + + def _div_(self, other): + v = self.valuation_at_current_precision() + return pAdicLazyElement_div(self.parent(), self >> v, other) << v + + def __invert__(self): + return pAdicLazyElement_div(self.parent(), None, self) + + def convert(self, ring, prec=None): + if prec is None: + prec = self.parent().prec() + digits = self[:prec] + value = 0 + for d in reversed(digits): + value *= self.prime() + value += d + return ring(value) + + +# Value + +class pAdicLazyElement_value(pAdicLazyElement): + def __init__(self, parent, value, prec=None, maxprec=None): + pAdicLazyElement.__init__(self, parent) + self._digits = [ ZZ(value) ] + self._maxprec = maxprec + if prec is None: + if maxprec is None: + prec = parent.precision_cap() + else: + prec = maxprec + try: + self.jump(prec) + except PrecisionError: + pass + + def next(self): + if self._maxprec is not None and self._precision >= self._maxprec: + raise PrecisionError("not enough precision") + carry, digit = self._digits[-1].quo_rem(self.prime()) + self._digits[-1] = digit + self._digits.append(carry) + self._precision += 1 + +# Random + +class pAdicLazyElement_random(pAdicLazyElement): + def next(self): + d = ZZ.random_element(0, self.prime()) + self._digits.append(d) + self._precision += 1 + + +# Shift + +class pAdicLazyElement_shift(pAdicLazyElement): + def __init__(self, parent, x, s): + pAdicLazyElement.__init__(self, parent) + self._x = x + self._s = s + if s > 0: + self._digits = s * [ZZ(0)] + self._precision = s + + def next(self): + n = self._precision + self._digits.append(self._x[n - self._s]) + self._precision += 1 + + +# Addition + +class pAdicLazyElement_add(pAdicLazyElement): + def __init__(self, parent, summands, coeffs): + pAdicLazyElement.__init__(self, parent) + self._summands = summands + self._coeffs = coeffs + self._digits = [ ZZ(0) ] + + def next(self): + n = self._precision + v = self._digits[-1] + for i in range(len(self._summands)): + v += self._coeffs[i] * self._summands[i][n] + carry, digit = v.quo_rem(self.prime()) + self._digits[-1] = digit + self._digits.append(carry) + self._precision += 1 + + +# Multiplication + +class pAdicLazyElement_mul(pAdicLazyElement): + _Zt = PolynomialRing(ZZ, 't') + + def __init__(self, parent, x, y): + pAdicLazyElement.__init__(self, parent) + self._x = x + self._y = y + self._digits = [ ZZ(0) ] + + def next(self): + x = self._x + y = self._y + Zt = self._Zt + n = self._precision + m = n + 2 + ell = 0 + S = Zt(0) + while m > 1: + P = Zt(x[2**ell - 1 : 2**(ell+1) - 1]) + Q = Zt(y[(m-1)*2**ell - 1 : m*2**ell - 1]) + S += P * Q + if m > 2: + P = Zt(y[2**ell - 1 : 2**(ell+1) - 1]) + Q = Zt(x[(m-1)*2**ell - 1 : m*2**ell - 1]) + S += P * Q + if m % 2 == 1: + break + m = m // 2 + ell += 1 + i = 0 + j = n + digits = self._digits + while i <= S.degree() and j < len(digits): + digits[j] += S[i] + i += 1 + j += 1 + while i <= S.degree(): + digits.append(S[i]) + i += 1 + carry, digit = digits[n].quo_rem(self.prime()) + digits[n] = digit + if len(digits) < n+2: + digits.append(carry) + else: + digits[n+1] += carry + self._precision += 1 + + +# Division + +class pAdicLazyElement_div(pAdicLazyElement): + def __init__(self, parent, x, y): + pAdicLazyElement.__init__(self, parent) + self._x = x + self._y = y + try: + self._bootstrap() + self._ready = True + except PrecisionError: + self._ready = False + + def _bootstrap(self): + parent = self.parent() + d, u, _ = self._y[0].xgcd(parent.prime()) + if d > 1: + raise ValueError("divisor is not a unit") + denom = pAdicLazyElement_add(parent, [self._y], [u]) + if self._x is None: + num = pAdicLazyElement_value(parent, u) + else: + num = pAdicLazyElement_add(parent, [self._x], [u]) + self._definition = num - (denom >> 1) * (self << 1) + + def next(self): + if not self._ready: + self._bootstrap() + self._ready = True + self._digits.append(self._definition[self._precision]) + self._precision += 1 + + +# Self-referent definition + +class pAdicLazyElement_selfref(pAdicLazyElement): + def __init__(self, parent): + pAdicLazyElement.__init__(self, parent) + self._definition = None + self._next = False + + def set(self, definition): + if self._definition is not None: + raise ValueError("this self-referent number is already defined") + self._definition = definition + try: + self.next() + except PrecisionError: + pass + except RecursionError: + self._definition = None + raise + + def __eq__(self, other): + if self._definition is None: + self.set(other) + return True + return pAdicLazyElement.__eq__(self, other) + + def next(self): + if self._definition is None: + raise PrecisionError("this self-referent number is not defined") + if self._next: + raise RecursionError("definition looks circular") + self._next = True + try: + self._digits.append(self._definition[self._precision]) + self._precision += 1 + finally: + self._next = False From 1fb0d59cbce16d1d7d6a423474301a6c060f9b43 Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Sat, 26 Dec 2020 10:40:21 +0100 Subject: [PATCH 072/406] rough implementation of square roots --- src/sage/rings/padics/padic_lazy.py | 4 +-- src/sage/rings/padics/padic_lazy_element.py | 35 +++++++++++++++++++++ 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/src/sage/rings/padics/padic_lazy.py b/src/sage/rings/padics/padic_lazy.py index 46937c89c14..abd6a8a1063 100644 --- a/src/sage/rings/padics/padic_lazy.py +++ b/src/sage/rings/padics/padic_lazy.py @@ -69,7 +69,7 @@ True A quite interesting feature with lazy p-adics is the possibility to -create (in somes cases) self-referrent numbers. Here is an example. +create (in somes cases) self-referent numbers. Here is an example. We first declare a new variable as follows:: sage: x = R() @@ -98,7 +98,7 @@ ... RecursionError: definition looks circular -Previous self-referrent definitions also work with system of equations:: +Self-referent definitions also work with systems of equations:: sage: u = R(); v = R(); w = R() diff --git a/src/sage/rings/padics/padic_lazy_element.py b/src/sage/rings/padics/padic_lazy_element.py index 244cccb15f2..91c3e5d960e 100644 --- a/src/sage/rings/padics/padic_lazy_element.py +++ b/src/sage/rings/padics/padic_lazy_element.py @@ -138,6 +138,9 @@ def _div_(self, other): def __invert__(self): return pAdicLazyElement_div(self.parent(), None, self) + def sqrt(self): + return pAdicLazyElement_sqrt(self.parent(), self) + def convert(self, ring, prec=None): if prec is None: prec = self.parent().prec() @@ -303,6 +306,38 @@ def next(self): self._precision += 1 +# Square root + +class pAdicLazyElement_sqrt(pAdicLazyElement): + def __init__(self, parent, x): + if parent.prime() == 2: + raise NotImplementedError + pAdicLazyElement.__init__(self, parent) + self._x = x + try: + self._bootstrap() + self._ready = True + except PrecisionError: + self._ready = False + + def _bootstrap(self): + parent = self.parent() + a = parent.residue_field()(self._x[0]) + if a == 0: + raise NotImplementedError + b = ZZ(a.sqrt(extend=False)) + c = b + (self._x - ZZ(a)) / (2*b) + s1 = self >> 1 + self._definition = c - ((s1**2 / (2*b)) << 2) + + def next(self): + if not self._ready: + self._bootstrap() + self._ready = True + self._digits.append(self._definition[self._precision]) + self._precision += 1 + + # Self-referent definition class pAdicLazyElement_selfref(pAdicLazyElement): From 5fd49b716529d6e7a7a94f1302897f2fbd17e6b5 Mon Sep 17 00:00:00 2001 From: Tobias Diez Date: Sat, 26 Dec 2020 16:12:11 +0100 Subject: [PATCH 073/406] Migrate some tests to pytest --- src/sage/numerical/backends/cvxopt_backend.pyx | 5 ----- .../numerical/backends/cvxopt_backend_test.py | 15 +++++++++++++++ src/sage/numerical/backends/generic_backend.pyx | 5 ----- .../numerical/backends/generic_backend_test.py | 16 ++++++++++++++++ src/sage/numerical/backends/glpk_backend.pyx | 7 ------- src/sage/numerical/backends/glpk_backend_test.py | 10 ++++++++++ .../numerical/backends/glpk_exact_backend.pyx | 7 ------- .../backends/glpk_exact_backend_test.py | 10 ++++++++++ .../numerical/backends/interactivelp_backend.pyx | 8 -------- .../backends/interactivelp_backend_test.py | 10 ++++++++++ src/sage/numerical/backends/ppl_backend.pyx | 6 ------ src/sage/numerical/backends/ppl_backend_test.py | 10 ++++++++++ 12 files changed, 71 insertions(+), 38 deletions(-) create mode 100644 src/sage/numerical/backends/cvxopt_backend_test.py create mode 100644 src/sage/numerical/backends/generic_backend_test.py create mode 100644 src/sage/numerical/backends/glpk_backend_test.py create mode 100644 src/sage/numerical/backends/glpk_exact_backend_test.py create mode 100644 src/sage/numerical/backends/interactivelp_backend_test.py create mode 100644 src/sage/numerical/backends/ppl_backend_test.py diff --git a/src/sage/numerical/backends/cvxopt_backend.pyx b/src/sage/numerical/backends/cvxopt_backend.pyx index 865f058b502..8273744c883 100644 --- a/src/sage/numerical/backends/cvxopt_backend.pyx +++ b/src/sage/numerical/backends/cvxopt_backend.pyx @@ -38,11 +38,6 @@ cdef class CVXOPTBackend(GenericBackend): sage: p Mixed Integer Program (no objective, 0 variables, 0 constraints) - - General backend testsuite:: - - sage: p = MixedIntegerLinearProgram(solver="CVXOPT") - sage: TestSuite(p.get_backend()).run(skip=("_test_pickling","_test_solve","_test_solve_trac_18572")) """ cdef list objective_function #c_matrix diff --git a/src/sage/numerical/backends/cvxopt_backend_test.py b/src/sage/numerical/backends/cvxopt_backend_test.py new file mode 100644 index 00000000000..3076b3ca790 --- /dev/null +++ b/src/sage/numerical/backends/cvxopt_backend_test.py @@ -0,0 +1,15 @@ +import pytest +from sage.numerical.backends.generic_backend_test import GenericBackendTests +from sage.numerical.backends.generic_backend import GenericBackend +from sage.numerical.mip import MixedIntegerLinearProgram + +class TestCVXOPTBackend(GenericBackendTests): + + @pytest.fixture + def backend(self) -> GenericBackend: + return MixedIntegerLinearProgram(solver="CVXOPT").get_backend() + + def test_old_testsuite(self, backend: GenericBackend): + # TODO: Remove this test as soon as all old test methods are migrated + from sage.misc.sage_unittest import TestSuite + TestSuite(backend).run(verbose=True, raise_on_failure=True, skip=("_test_pickling","_test_solve","_test_solve_trac_18572")) diff --git a/src/sage/numerical/backends/generic_backend.pyx b/src/sage/numerical/backends/generic_backend.pyx index 5609de574e4..8c27abb0a02 100644 --- a/src/sage/numerical/backends/generic_backend.pyx +++ b/src/sage/numerical/backends/generic_backend.pyx @@ -867,11 +867,6 @@ cdef class GenericBackend: """ raise NotImplementedError() - - def _test_ncols_nonnegative(self, **options): - tester = self._tester(**options) - p = self - tester.assertGreaterEqual(self.ncols(), 0) cpdef int nrows(self): """ diff --git a/src/sage/numerical/backends/generic_backend_test.py b/src/sage/numerical/backends/generic_backend_test.py new file mode 100644 index 00000000000..04dd99de775 --- /dev/null +++ b/src/sage/numerical/backends/generic_backend_test.py @@ -0,0 +1,16 @@ +import pytest +from .generic_backend import GenericBackend + +class GenericBackendTests: + + @pytest.fixture + def backend(self) -> GenericBackend: + raise NotImplementedError + + def test_ncols_nonnegative(self, backend: GenericBackend): + assert backend.ncols() >= 0 + + def test_old_testsuite(self, backend: GenericBackend): + # TODO: Remove this test as soon as all old test methods are migrated + from sage.misc.sage_unittest import TestSuite + TestSuite(backend).run(verbose=True, raise_on_failure=True, skip="_test_pickling") diff --git a/src/sage/numerical/backends/glpk_backend.pyx b/src/sage/numerical/backends/glpk_backend.pyx index e4ec46b93ff..d490818ce75 100644 --- a/src/sage/numerical/backends/glpk_backend.pyx +++ b/src/sage/numerical/backends/glpk_backend.pyx @@ -36,13 +36,6 @@ cdef class GLPKBackend(GenericBackend): """ MIP Backend that uses the GLPK solver. - - TESTS: - - General backend testsuite:: - - sage: p = MixedIntegerLinearProgram(solver="GLPK") - sage: TestSuite(p.get_backend()).run(skip="_test_pickling") """ def __cinit__(self, maximization = True): diff --git a/src/sage/numerical/backends/glpk_backend_test.py b/src/sage/numerical/backends/glpk_backend_test.py new file mode 100644 index 00000000000..ec37cd25f36 --- /dev/null +++ b/src/sage/numerical/backends/glpk_backend_test.py @@ -0,0 +1,10 @@ +import pytest +from sage.numerical.backends.generic_backend_test import GenericBackendTests +from sage.numerical.backends.generic_backend import GenericBackend +from sage.numerical.mip import MixedIntegerLinearProgram + +class TestGLPKBackend(GenericBackendTests): + + @pytest.fixture + def backend(self) -> GenericBackend: + return MixedIntegerLinearProgram(solver="GLPK").get_backend() diff --git a/src/sage/numerical/backends/glpk_exact_backend.pyx b/src/sage/numerical/backends/glpk_exact_backend.pyx index 078f72ac4be..0da98460382 100644 --- a/src/sage/numerical/backends/glpk_exact_backend.pyx +++ b/src/sage/numerical/backends/glpk_exact_backend.pyx @@ -24,13 +24,6 @@ cdef class GLPKExactBackend(GLPKBackend): as doubles. There is no support for integer variables. - - TESTS: - - General backend testsuite:: - - sage: p = MixedIntegerLinearProgram(solver="GLPK/exact") - sage: TestSuite(p.get_backend()).run(skip="_test_pickling") """ def __cinit__(self, maximization = True): """ diff --git a/src/sage/numerical/backends/glpk_exact_backend_test.py b/src/sage/numerical/backends/glpk_exact_backend_test.py new file mode 100644 index 00000000000..cffc87e3844 --- /dev/null +++ b/src/sage/numerical/backends/glpk_exact_backend_test.py @@ -0,0 +1,10 @@ +import pytest +from sage.numerical.backends.generic_backend_test import GenericBackendTests +from sage.numerical.backends.generic_backend import GenericBackend +from sage.numerical.mip import MixedIntegerLinearProgram + +class TestGLPKExactBackend(GenericBackendTests): + + @pytest.fixture + def backend(self) -> GenericBackend: + return MixedIntegerLinearProgram(solver="GLPK/exact").get_backend() diff --git a/src/sage/numerical/backends/interactivelp_backend.pyx b/src/sage/numerical/backends/interactivelp_backend.pyx index 2a7432a9ddb..42a035a3230 100644 --- a/src/sage/numerical/backends/interactivelp_backend.pyx +++ b/src/sage/numerical/backends/interactivelp_backend.pyx @@ -40,14 +40,6 @@ cdef class InteractiveLPBackend: sage: from sage.numerical.backends.generic_backend import get_solver sage: p = get_solver(solver = "InteractiveLP") - - TESTS: - - General backend testsuite:: - - sage: p = MixedIntegerLinearProgram(solver="InteractiveLP") - sage: TestSuite(p.get_backend()).run(skip="_test_pickling") - """ def __cinit__(self, maximization = True, base_ring = None): diff --git a/src/sage/numerical/backends/interactivelp_backend_test.py b/src/sage/numerical/backends/interactivelp_backend_test.py new file mode 100644 index 00000000000..0f0af51250d --- /dev/null +++ b/src/sage/numerical/backends/interactivelp_backend_test.py @@ -0,0 +1,10 @@ +import pytest +from sage.numerical.backends.generic_backend_test import GenericBackendTests +from sage.numerical.backends.generic_backend import GenericBackend +from sage.numerical.mip import MixedIntegerLinearProgram + +class TestInteractiveLPBackend(GenericBackendTests): + + @pytest.fixture + def backend(self) -> GenericBackend: + return MixedIntegerLinearProgram(solver="InteractiveLP").get_backend() diff --git a/src/sage/numerical/backends/ppl_backend.pyx b/src/sage/numerical/backends/ppl_backend.pyx index ec453136dbf..8f7154137ad 100644 --- a/src/sage/numerical/backends/ppl_backend.pyx +++ b/src/sage/numerical/backends/ppl_backend.pyx @@ -30,12 +30,6 @@ cdef class PPLBackend(GenericBackend): """ MIP Backend that uses the exact MIP solver from the Parma Polyhedra Library. - - General backend testsuite:: - - sage: from sage.numerical.backends.generic_backend import get_solver - sage: p = get_solver(solver = "PPL") - sage: TestSuite(p).run(skip="_test_pickling") """ cdef object mip diff --git a/src/sage/numerical/backends/ppl_backend_test.py b/src/sage/numerical/backends/ppl_backend_test.py new file mode 100644 index 00000000000..ac241275ee2 --- /dev/null +++ b/src/sage/numerical/backends/ppl_backend_test.py @@ -0,0 +1,10 @@ +import pytest +from sage.numerical.backends.generic_backend_test import GenericBackendTests +from sage.numerical.backends.generic_backend import GenericBackend +from sage.numerical.mip import MixedIntegerLinearProgram + +class TestPPLBackend(GenericBackendTests): + + @pytest.fixture + def backend(self) -> GenericBackend: + return MixedIntegerLinearProgram(solver="PPL").get_backend() From 719e442e5d3a3eaadab28bc835b68d46a4a2d12d Mon Sep 17 00:00:00 2001 From: Tobias Diez Date: Sat, 26 Dec 2020 16:12:24 +0100 Subject: [PATCH 074/406] Run pytest as part of sage -t --- src/bin/sage-runtests | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/bin/sage-runtests b/src/bin/sage-runtests index be4e52f5701..6a6cd6654d8 100755 --- a/src/bin/sage-runtests +++ b/src/bin/sage-runtests @@ -181,4 +181,13 @@ if __name__ == "__main__": DC = DocTestController(options, args) err = DC.run() - sys.exit(err) + import pytest + pytest_options = [] + if options.verbose: + pytest_options.append("-v") + exit_code_pytest = pytest.main(pytest_options + args) + + if err == 0: + sys.exit(exit_code_pytest) + else: + sys.exit(err) From d1f14f011c23a1a218400af7400edb7e1c613c0b Mon Sep 17 00:00:00 2001 From: Tobias Diez Date: Sat, 26 Dec 2020 20:27:41 +0100 Subject: [PATCH 075/406] Incorporate feedback --- src/bin/sage-runtests | 15 ++++++++++----- .../numerical/backends/cvxopt_backend_test.py | 2 +- .../numerical/backends/generic_backend_test.py | 2 +- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/bin/sage-runtests b/src/bin/sage-runtests index 6a6cd6654d8..4ea557fe194 100755 --- a/src/bin/sage-runtests +++ b/src/bin/sage-runtests @@ -181,11 +181,16 @@ if __name__ == "__main__": DC = DocTestController(options, args) err = DC.run() - import pytest - pytest_options = [] - if options.verbose: - pytest_options.append("-v") - exit_code_pytest = pytest.main(pytest_options + args) + try: + import pytest + pytest_options = [] + if options.verbose: + pytest_options.append("-v") + exit_code_pytest = pytest.main(pytest_options + args) + except ModuleNotFoundError: + print("Pytest is not installed, skip checking tests that rely on it.") + + if err == 0: sys.exit(exit_code_pytest) diff --git a/src/sage/numerical/backends/cvxopt_backend_test.py b/src/sage/numerical/backends/cvxopt_backend_test.py index 3076b3ca790..4e34a246e0d 100644 --- a/src/sage/numerical/backends/cvxopt_backend_test.py +++ b/src/sage/numerical/backends/cvxopt_backend_test.py @@ -9,7 +9,7 @@ class TestCVXOPTBackend(GenericBackendTests): def backend(self) -> GenericBackend: return MixedIntegerLinearProgram(solver="CVXOPT").get_backend() - def test_old_testsuite(self, backend: GenericBackend): + def test_sage_unittest_testsuite(self, backend: GenericBackend): # TODO: Remove this test as soon as all old test methods are migrated from sage.misc.sage_unittest import TestSuite TestSuite(backend).run(verbose=True, raise_on_failure=True, skip=("_test_pickling","_test_solve","_test_solve_trac_18572")) diff --git a/src/sage/numerical/backends/generic_backend_test.py b/src/sage/numerical/backends/generic_backend_test.py index 04dd99de775..d8d0bba99cd 100644 --- a/src/sage/numerical/backends/generic_backend_test.py +++ b/src/sage/numerical/backends/generic_backend_test.py @@ -10,7 +10,7 @@ def backend(self) -> GenericBackend: def test_ncols_nonnegative(self, backend: GenericBackend): assert backend.ncols() >= 0 - def test_old_testsuite(self, backend: GenericBackend): + def test_sage_unittest_testsuite(self, backend: GenericBackend): # TODO: Remove this test as soon as all old test methods are migrated from sage.misc.sage_unittest import TestSuite TestSuite(backend).run(verbose=True, raise_on_failure=True, skip="_test_pickling") From f4a0c4a19b7cb07df3cb95295fc5d3b794e0c7d5 Mon Sep 17 00:00:00 2001 From: Tobias Diez Date: Sat, 26 Dec 2020 21:17:24 +0100 Subject: [PATCH 076/406] Create SageObJectTest --- src/sage/numerical/backends/cvxopt_backend_test.py | 4 ++-- .../numerical/backends/generic_backend_test.py | 14 ++++++++++---- src/sage/structure/sage_object_test.py | 14 ++++++++++++++ 3 files changed, 26 insertions(+), 6 deletions(-) create mode 100644 src/sage/structure/sage_object_test.py diff --git a/src/sage/numerical/backends/cvxopt_backend_test.py b/src/sage/numerical/backends/cvxopt_backend_test.py index 4e34a246e0d..4f238377181 100644 --- a/src/sage/numerical/backends/cvxopt_backend_test.py +++ b/src/sage/numerical/backends/cvxopt_backend_test.py @@ -9,7 +9,7 @@ class TestCVXOPTBackend(GenericBackendTests): def backend(self) -> GenericBackend: return MixedIntegerLinearProgram(solver="CVXOPT").get_backend() - def test_sage_unittest_testsuite(self, backend: GenericBackend): + def test_sage_unittest_testsuite(self, sage_object: SageObject): # TODO: Remove this test as soon as all old test methods are migrated from sage.misc.sage_unittest import TestSuite - TestSuite(backend).run(verbose=True, raise_on_failure=True, skip=("_test_pickling","_test_solve","_test_solve_trac_18572")) + TestSuite(sage_object).run(verbose=True, raise_on_failure=True, skip=("_test_pickling","_test_solve","_test_solve_trac_18572")) diff --git a/src/sage/numerical/backends/generic_backend_test.py b/src/sage/numerical/backends/generic_backend_test.py index d8d0bba99cd..94fd09f390e 100644 --- a/src/sage/numerical/backends/generic_backend_test.py +++ b/src/sage/numerical/backends/generic_backend_test.py @@ -1,16 +1,22 @@ import pytest from .generic_backend import GenericBackend +from sage.structure.sage_object import SageObject +from sage.structure.sage_object_test import SageObjectTests -class GenericBackendTests: +class GenericBackendTests(SageObjectTests): @pytest.fixture - def backend(self) -> GenericBackend: + def backend(self, *args, **kwargs) -> GenericBackend: raise NotImplementedError + @pytest.fixture + def sage_object(self, backend) -> SageObject: + return backend + def test_ncols_nonnegative(self, backend: GenericBackend): assert backend.ncols() >= 0 - def test_sage_unittest_testsuite(self, backend: GenericBackend): + def test_sage_unittest_testsuite(self, sage_object: SageObject): # TODO: Remove this test as soon as all old test methods are migrated from sage.misc.sage_unittest import TestSuite - TestSuite(backend).run(verbose=True, raise_on_failure=True, skip="_test_pickling") + TestSuite(sage_object).run(verbose=True, raise_on_failure=True, skip="_test_pickling") diff --git a/src/sage/structure/sage_object_test.py b/src/sage/structure/sage_object_test.py new file mode 100644 index 00000000000..31fadf21409 --- /dev/null +++ b/src/sage/structure/sage_object_test.py @@ -0,0 +1,14 @@ + +import pytest +from .sage_object import SageObject + +class SageObjectTests: + + @pytest.fixture + def sage_object(self, *args, **kwargs) -> SageObject: + raise NotImplementedError + + def test_sage_unittest_testsuite(self, sage_object: SageObject): + # TODO: Remove this test as soon as all old test methods are migrated + from sage.misc.sage_unittest import TestSuite + TestSuite(sage_object).run(verbose=True, raise_on_failure=True) From 3c06234e703737f5ddaeb09caa4161ab26578ba7 Mon Sep 17 00:00:00 2001 From: Tobias Diez Date: Sun, 27 Dec 2020 10:44:00 +0100 Subject: [PATCH 077/406] Add docstring --- src/sage/structure/sage_object_test.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/sage/structure/sage_object_test.py b/src/sage/structure/sage_object_test.py index 31fadf21409..0922545a62c 100644 --- a/src/sage/structure/sage_object_test.py +++ b/src/sage/structure/sage_object_test.py @@ -9,6 +9,9 @@ def sage_object(self, *args, **kwargs) -> SageObject: raise NotImplementedError def test_sage_unittest_testsuite(self, sage_object: SageObject): + """ + Subclasses should override this method if they need to skip some tests. + """ # TODO: Remove this test as soon as all old test methods are migrated from sage.misc.sage_unittest import TestSuite TestSuite(sage_object).run(verbose=True, raise_on_failure=True) From b69ba5acbc71204fe113a4df7ffb4fc2fca0a27b Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Tue, 29 Dec 2020 21:42:06 +0100 Subject: [PATCH 078/406] method selfref --- src/sage/rings/padics/padic_lazy.py | 5 +++-- src/sage/rings/padics/padic_lazy_element.py | 4 ++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/sage/rings/padics/padic_lazy.py b/src/sage/rings/padics/padic_lazy.py index abd6a8a1063..0fe5ed2b8ca 100644 --- a/src/sage/rings/padics/padic_lazy.py +++ b/src/sage/rings/padics/padic_lazy.py @@ -156,8 +156,6 @@ def _coerce_map_from_(self, R): return True def _element_constructor_(self, x, prec=None): - if type(x) is int and x == 0: - return pAdicLazyElement_selfref(self) if isinstance(x, pAdicLazyElement): return x if isinstance(x, pAdicGenericElement): @@ -177,6 +175,9 @@ def _element_constructor_(self, x, prec=None): x.jump(prec) return x + def selfref(self): + return pAdicLazyElement_selfref(self) + def _an_element_(self): return pAdicLazyElement_value(self, 0) diff --git a/src/sage/rings/padics/padic_lazy_element.py b/src/sage/rings/padics/padic_lazy_element.py index 91c3e5d960e..53dd466ce80 100644 --- a/src/sage/rings/padics/padic_lazy_element.py +++ b/src/sage/rings/padics/padic_lazy_element.py @@ -78,9 +78,13 @@ def __eq__(self, other): return self.equal_at_current_precision(other) def __lshift__(self, s): + if s == 0: + return self return pAdicLazyElement_shift(self.parent(), self, ZZ(s)) def __rshift__(self, s): + if s == 0: + return self return pAdicLazyElement_shift(self.parent(), self, ZZ(-s)) def _add_(self, other): From 4044cadcc2792847530c6163ab0977c32c56ee45 Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Sun, 3 Jan 2021 00:30:08 +0100 Subject: [PATCH 079/406] rewrite in cython+flint (not finished) --- src/sage/rings/padics/factory.py | 3 +- src/sage/rings/padics/misc.py | 3 +- src/sage/rings/padics/padic_base_generic.py | 3 + src/sage/rings/padics/padic_lazy.py | 42 +- src/sage/rings/padics/padic_lazy_element.pxd | 57 +++ src/sage/rings/padics/padic_lazy_element.py | 381 -------------- src/sage/rings/padics/padic_lazy_element.pyx | 512 +++++++++++++++++++ 7 files changed, 603 insertions(+), 398 deletions(-) create mode 100644 src/sage/rings/padics/padic_lazy_element.pxd delete mode 100644 src/sage/rings/padics/padic_lazy_element.py create mode 100644 src/sage/rings/padics/padic_lazy_element.pyx diff --git a/src/sage/rings/padics/factory.py b/src/sage/rings/padics/factory.py index ffcaa4ccf5f..cd77468e9b7 100644 --- a/src/sage/rings/padics/factory.py +++ b/src/sage/rings/padics/factory.py @@ -1924,7 +1924,8 @@ def create_object(self, version, key): 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 == 'lazy': - return pAdicRingLazy(p, prec) + return pAdicRingLazy(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, diff --git a/src/sage/rings/padics/misc.py b/src/sage/rings/padics/misc.py index e92da6b1ec4..837c4686c6d 100644 --- a/src/sage/rings/padics/misc.py +++ b/src/sage/rings/padics/misc.py @@ -206,7 +206,8 @@ def precprint(prec_type, prec_cap, p): 'floating-point':'with floating precision %s'%prec_cap, 'fixed-mod':'of fixed modulus %s^%s'%(p, prec_cap), 'lattice-cap':'with lattice-cap precision', - 'lattice-float':'with lattice-float precision'} + 'lattice-float':'with lattice-float precision', + 'lazy':'with lazy precision'} return precD[prec_type] def trim_zeros(L): diff --git a/src/sage/rings/padics/padic_base_generic.py b/src/sage/rings/padics/padic_base_generic.py index 1c3fd85dad4..d75d99b217c 100644 --- a/src/sage/rings/padics/padic_base_generic.py +++ b/src/sage/rings/padics/padic_base_generic.py @@ -69,6 +69,9 @@ def __init__(self, p, prec, print_mode, names, element_class): elif self.is_lattice_prec(): coerce_list = [ZZ] convert_list = [QQ] + elif self.is_lazy(): + coerce_list = [ZZ] + convert_list = [QQ] else: raise RuntimeError self.Element = element_class diff --git a/src/sage/rings/padics/padic_lazy.py b/src/sage/rings/padics/padic_lazy.py index 0fe5ed2b8ca..721316fffc0 100644 --- a/src/sage/rings/padics/padic_lazy.py +++ b/src/sage/rings/padics/padic_lazy.py @@ -130,40 +130,52 @@ from sage.rings.all import ZZ, QQ -from sage.rings.padics.generic_nodes import pAdicRingGeneric +from sage.rings.infinity import Infinity +from sage.rings.padics.generic_nodes import pAdicRingBaseGeneric from sage.rings.padics.padic_generic_element import pAdicGenericElement -from sage.rings.padics.padic_lazy_element import pAdicLazyElement -from sage.rings.padics.padic_lazy_element import pAdicLazyElement_value -from sage.rings.padics.padic_lazy_element import pAdicLazyElement_random -from sage.rings.padics.padic_lazy_element import pAdicLazyElement_selfref +from sage.rings.padics.padic_lazy_element import * -class pAdicRingLazy(pAdicRingGeneric): - def __init__(self, p, prec): - pAdicRingGeneric.__init__(self, ZZ, p, prec, None, None, None) +class pAdicRingLazy(pAdicRingBaseGeneric): + def __init__(self, p, prec, print_mode, names): + pAdicRingBaseGeneric.__init__(self, p, 0, print_mode, names, None) self._p = p + self._zero_element = pAdicLazyElement_zero(self) + self._default_prec = ZZ(prec) def prime(self): return self._p - def _repr_(self): - return "Lazy %s-adic Ring" % self.prime() + def _prec_type(self): + return "lazy" + + def is_lazy(self): + return True + + def default_prec(self): + return self._default_prec + + def precision_cap(self): + return Infinity def _coerce_map_from_(self, R): # why do I need this? if R is ZZ: return True - def _element_constructor_(self, x, prec=None): + def _element_constructor_(self, x): if isinstance(x, pAdicLazyElement): return x if isinstance(x, pAdicGenericElement): - return pAdicLazyElement_value(self, ZZ(x), prec, maxprec=x.precision_absolute()) - if prec is None: - prec = self.precision_cap() + return pAdicLazyElement_value(self, ZZ(x), maxprec=x.precision_absolute()) if x in ZZ: - return pAdicLazyElement_value(self, ZZ(x), prec) + if x == 0: + return self._zero_element + elif x == 1: + return pAdicLazyElement_one(self) + else: + return pAdicLazyElement_value(self, ZZ(x)) if x in QQ: num = x.numerator() denom = x.denominator() diff --git a/src/sage/rings/padics/padic_lazy_element.pxd b/src/sage/rings/padics/padic_lazy_element.pxd new file mode 100644 index 00000000000..e3de8447dbc --- /dev/null +++ b/src/sage/rings/padics/padic_lazy_element.pxd @@ -0,0 +1,57 @@ +from sage.libs.flint.types cimport slong +from sage.libs.flint.types cimport flint_rand_t +from sage.libs.flint.types cimport fmpz, fmpz_t, fmpz_poly_t + +from sage.rings.integer cimport Integer +from sage.rings.padics.padic_generic_element cimport pAdicGenericElement + + +cdef class pAdicLazyElement(pAdicGenericElement): + cdef fmpz_t _prime + cdef fmpz_poly_t _digits + cdef slong _valuation + cdef slong _precrel + + cpdef jump(self, prec) + cdef bint next(self) except -1 + cdef Integer _digit(self, i) + + cdef bint _is_equal(self, pAdicLazyElement right, slong prec) + + + +cdef class pAdicLazyElement_zero(pAdicLazyElement): + pass + +cdef class pAdicLazyElement_one(pAdicLazyElement): + pass + +cdef class pAdicLazyElement_value(pAdicLazyElement): + cdef _maxprec + +cdef class pAdicLazyElement_random(pAdicLazyElement): + cdef flint_rand_t _randstate + + + +cdef class pAdicLazyElement_shift(pAdicLazyElement): + cdef pAdicLazyElement _x + cdef slong _shift + +cdef class pAdicLazyElement_add(pAdicLazyElement): + cdef list _summands + cdef list _signs + +cdef class pAdicLazyElement_mul(pAdicLazyElement): + cdef pAdicLazyElement _x + cdef pAdicLazyElement _y + cdef fmpz_poly_t _tmp + +#cdef class pAdicLazyElement_div(pAdicLazyElement) +#cdef class pAdicLazyElement_sqrt(pAdicLazyElement) + + + +cdef class pAdicLazyElement_selfref(pAdicLazyElement): + cdef pAdicLazyElement _definition + cdef bint _next diff --git a/src/sage/rings/padics/padic_lazy_element.py b/src/sage/rings/padics/padic_lazy_element.py deleted file mode 100644 index 53dd466ce80..00000000000 --- a/src/sage/rings/padics/padic_lazy_element.py +++ /dev/null @@ -1,381 +0,0 @@ -# **************************************************************************** -# Copyright (C) 2021 Xavier Caruso -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 2 of the License, or -# (at your option) any later version. -# http://www.gnu.org/licenses/ -# **************************************************************************** - - -from sage.structure.element import coerce_binop - -from sage.rings.all import ZZ -from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing -from sage.rings.padics.padic_generic_element import pAdicGenericElement - -from sage.rings.padics.precision_error import PrecisionError - - -class pAdicLazyElement(pAdicGenericElement): - def __init__(self, parent): - pAdicGenericElement.__init__(self, parent) - self._digits = [ ] - self._precision = 0 - - def prime(self): - return self.parent().prime() - - def next(self): - raise NotImplementedError("must be implemented in subclasses") - - def jump(self, prec): - while self._precision < prec: - self.next() - - def _repr_(self): - try: - self.jump(self.parent().precision_cap()) - except PrecisionError: - pass - if self._precision == 0: - return "O(1)" - s = "..." - for i in range(self._precision - 1, -1, -1): - if self.prime() > 9: - s += "|" - s += str(self._digits[i]) - return s - - def __getitem__(self, i): - if isinstance(i, slice): - if i.step is not None or i.stop is None: - raise IndexError - start = i.start - stop = i.stop - else: - start = i - stop = i + 1 - if start < 0: - start = 0 - if stop < 0 or start >= stop: - return ZZ(0) - self.jump(stop) - if isinstance(i, slice): - return self._digits[start:stop] - else: - return self._digits[start] - - @coerce_binop - def equal_at_current_precision(self, other): - for i in range(min(self._precision, other._precision)): - if self[i] != other[i]: - return False - return True - - def __eq__(self, other): - return self.equal_at_current_precision(other) - - def __lshift__(self, s): - if s == 0: - return self - return pAdicLazyElement_shift(self.parent(), self, ZZ(s)) - - def __rshift__(self, s): - if s == 0: - return self - return pAdicLazyElement_shift(self.parent(), self, ZZ(-s)) - - def _add_(self, other): - summands = [ ] - coeffs = [ ] - if isinstance(self, pAdicLazyElement_add): - summands.extend(self._summands) - coeffs.extend(self._coeffs) - else: - summands.append(self) - coeffs.append(1) - if isinstance(other, pAdicLazyElement_add): - summands.extend(other._summands) - coeffs.extend(other._coeffs) - else: - summands.append(other) - coeffs.append(1) - return pAdicLazyElement_add(self.parent(), summands, coeffs) - - def _sub_(self, other): - summands = [ ] - coeffs = [ ] - if isinstance(self, pAdicLazyElement_add): - summands.extend(self._summands) - coeffs.extend(self._coeffs) - else: - summands.append(self) - coeffs.append(1) - if isinstance(other, pAdicLazyElement_add): - summands.extend(other._summands) - coeffs.extend([ -c for c in other._coeffs ]) - else: - summands.append(other) - coeffs.append(-1) - return pAdicLazyElement_add(self.parent(), summands, coeffs) - - def _neg_(self): - return pAdicLazyElement_add(self.parent(), self, [-1]) - - def valuation_at_current_precision(self): - for i in range(self._precision): - if self._digits[i] != 0: - return i - return self._precision - - def _mul_(self, other): - vs = self.valuation_at_current_precision() - vo = other.valuation_at_current_precision() - return pAdicLazyElement_mul(self.parent(), self >> vs, other >> vo) << (vs + vo) - - def _div_(self, other): - v = self.valuation_at_current_precision() - return pAdicLazyElement_div(self.parent(), self >> v, other) << v - - def __invert__(self): - return pAdicLazyElement_div(self.parent(), None, self) - - def sqrt(self): - return pAdicLazyElement_sqrt(self.parent(), self) - - def convert(self, ring, prec=None): - if prec is None: - prec = self.parent().prec() - digits = self[:prec] - value = 0 - for d in reversed(digits): - value *= self.prime() - value += d - return ring(value) - - -# Value - -class pAdicLazyElement_value(pAdicLazyElement): - def __init__(self, parent, value, prec=None, maxprec=None): - pAdicLazyElement.__init__(self, parent) - self._digits = [ ZZ(value) ] - self._maxprec = maxprec - if prec is None: - if maxprec is None: - prec = parent.precision_cap() - else: - prec = maxprec - try: - self.jump(prec) - except PrecisionError: - pass - - def next(self): - if self._maxprec is not None and self._precision >= self._maxprec: - raise PrecisionError("not enough precision") - carry, digit = self._digits[-1].quo_rem(self.prime()) - self._digits[-1] = digit - self._digits.append(carry) - self._precision += 1 - -# Random - -class pAdicLazyElement_random(pAdicLazyElement): - def next(self): - d = ZZ.random_element(0, self.prime()) - self._digits.append(d) - self._precision += 1 - - -# Shift - -class pAdicLazyElement_shift(pAdicLazyElement): - def __init__(self, parent, x, s): - pAdicLazyElement.__init__(self, parent) - self._x = x - self._s = s - if s > 0: - self._digits = s * [ZZ(0)] - self._precision = s - - def next(self): - n = self._precision - self._digits.append(self._x[n - self._s]) - self._precision += 1 - - -# Addition - -class pAdicLazyElement_add(pAdicLazyElement): - def __init__(self, parent, summands, coeffs): - pAdicLazyElement.__init__(self, parent) - self._summands = summands - self._coeffs = coeffs - self._digits = [ ZZ(0) ] - - def next(self): - n = self._precision - v = self._digits[-1] - for i in range(len(self._summands)): - v += self._coeffs[i] * self._summands[i][n] - carry, digit = v.quo_rem(self.prime()) - self._digits[-1] = digit - self._digits.append(carry) - self._precision += 1 - - -# Multiplication - -class pAdicLazyElement_mul(pAdicLazyElement): - _Zt = PolynomialRing(ZZ, 't') - - def __init__(self, parent, x, y): - pAdicLazyElement.__init__(self, parent) - self._x = x - self._y = y - self._digits = [ ZZ(0) ] - - def next(self): - x = self._x - y = self._y - Zt = self._Zt - n = self._precision - m = n + 2 - ell = 0 - S = Zt(0) - while m > 1: - P = Zt(x[2**ell - 1 : 2**(ell+1) - 1]) - Q = Zt(y[(m-1)*2**ell - 1 : m*2**ell - 1]) - S += P * Q - if m > 2: - P = Zt(y[2**ell - 1 : 2**(ell+1) - 1]) - Q = Zt(x[(m-1)*2**ell - 1 : m*2**ell - 1]) - S += P * Q - if m % 2 == 1: - break - m = m // 2 - ell += 1 - i = 0 - j = n - digits = self._digits - while i <= S.degree() and j < len(digits): - digits[j] += S[i] - i += 1 - j += 1 - while i <= S.degree(): - digits.append(S[i]) - i += 1 - carry, digit = digits[n].quo_rem(self.prime()) - digits[n] = digit - if len(digits) < n+2: - digits.append(carry) - else: - digits[n+1] += carry - self._precision += 1 - - -# Division - -class pAdicLazyElement_div(pAdicLazyElement): - def __init__(self, parent, x, y): - pAdicLazyElement.__init__(self, parent) - self._x = x - self._y = y - try: - self._bootstrap() - self._ready = True - except PrecisionError: - self._ready = False - - def _bootstrap(self): - parent = self.parent() - d, u, _ = self._y[0].xgcd(parent.prime()) - if d > 1: - raise ValueError("divisor is not a unit") - denom = pAdicLazyElement_add(parent, [self._y], [u]) - if self._x is None: - num = pAdicLazyElement_value(parent, u) - else: - num = pAdicLazyElement_add(parent, [self._x], [u]) - self._definition = num - (denom >> 1) * (self << 1) - - def next(self): - if not self._ready: - self._bootstrap() - self._ready = True - self._digits.append(self._definition[self._precision]) - self._precision += 1 - - -# Square root - -class pAdicLazyElement_sqrt(pAdicLazyElement): - def __init__(self, parent, x): - if parent.prime() == 2: - raise NotImplementedError - pAdicLazyElement.__init__(self, parent) - self._x = x - try: - self._bootstrap() - self._ready = True - except PrecisionError: - self._ready = False - - def _bootstrap(self): - parent = self.parent() - a = parent.residue_field()(self._x[0]) - if a == 0: - raise NotImplementedError - b = ZZ(a.sqrt(extend=False)) - c = b + (self._x - ZZ(a)) / (2*b) - s1 = self >> 1 - self._definition = c - ((s1**2 / (2*b)) << 2) - - def next(self): - if not self._ready: - self._bootstrap() - self._ready = True - self._digits.append(self._definition[self._precision]) - self._precision += 1 - - -# Self-referent definition - -class pAdicLazyElement_selfref(pAdicLazyElement): - def __init__(self, parent): - pAdicLazyElement.__init__(self, parent) - self._definition = None - self._next = False - - def set(self, definition): - if self._definition is not None: - raise ValueError("this self-referent number is already defined") - self._definition = definition - try: - self.next() - except PrecisionError: - pass - except RecursionError: - self._definition = None - raise - - def __eq__(self, other): - if self._definition is None: - self.set(other) - return True - return pAdicLazyElement.__eq__(self, other) - - def next(self): - if self._definition is None: - raise PrecisionError("this self-referent number is not defined") - if self._next: - raise RecursionError("definition looks circular") - self._next = True - try: - self._digits.append(self._definition[self._precision]) - self._precision += 1 - finally: - self._next = False diff --git a/src/sage/rings/padics/padic_lazy_element.pyx b/src/sage/rings/padics/padic_lazy_element.pyx new file mode 100644 index 00000000000..bec93baf782 --- /dev/null +++ b/src/sage/rings/padics/padic_lazy_element.pyx @@ -0,0 +1,512 @@ +# **************************************************************************** +# Copyright (C) 2021 Xavier Caruso +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# http://www.gnu.org/licenses/ +# **************************************************************************** + + +from sage.libs.flint.types cimport * +from sage.libs.flint.fmpz cimport * +from sage.libs.flint.fmpz_poly cimport * + +cdef extern from "sage/rings/padics/padic_lazy_element_helper.c": + cdef void flint_randinit(flint_rand_t state) + cdef void flint_randclear(flint_rand_t state) + cdef fmpz* get_coeff(fmpz_poly_t poly, slong i) + cdef void get_slice(fmpz_poly_t slice, fmpz_poly_t poly, slong start, slong length) + cdef void iadd_coeff(fmpz_poly_t poly, fmpz_t summand, slong i) + cdef void isub_coeff(fmpz_poly_t poly, fmpz_t summand, slong i) + cdef void iadd_shifted(fmpz_poly_t poly, fmpz_poly_t summand, slong shift) + cdef void reduce_coeff(fmpz_poly_t poly, slong i, fmpz_t modulus) + +from sage.ext.stdsage cimport PY_NEW +from sage.structure.element import coerce_binop + +from sage.rings.all import ZZ +from sage.rings.integer cimport Integer +from sage.rings.infinity import Infinity + +from sage.rings.padics.padic_generic_element cimport pAdicGenericElement +from sage.rings.padics.precision_error import PrecisionError + +cdef long maxordp = (1L << (sizeof(long) * 8 - 2)) - 1 +MAXORDP = ZZ(maxordp) + + +cdef class pAdicLazyElement(pAdicGenericElement): + def __cinit__(self): + fmpz_poly_init(self._digits) + + def __dealloc__(self): + fmpz_poly_clear(self._digits) + + def __init__(self, parent): + pAdicGenericElement.__init__(self, parent) + cdef Integer p = self._parent.prime() + fmpz_set_mpz(self._prime, p.value) + self._valuation = 0 + self._precrel = 0 + + def prime(self): + return self._parent.prime() + + cdef bint next(self) except -1: + raise NotImplementedError("must be implemented in subclasses") + + cpdef jump(self, prec): + cdef slong i, stop + stop = min(prec, maxordp) - self._precrel - self._valuation + for i in range(stop): + if not self.next(): + return False + return prec < maxordp + + def expansion(self, n=None): + if n is None: + return ExpansionIter(self, self._valuation, self._valuation + self._precrel) + if isinstance(n, slice): + if n.step is not None: + raise NotImplementedError + return ExpansionIter(self, n.start, n.stop) + else: + return self._digit(n) + + cdef Integer _digit(self, i): + if not self.jump(i+1): + raise PrecisionError + cdef Integer ans = PY_NEW(Integer) + cdef fmpz* coeff = get_coeff(self._digits, i - self._valuation) + fmpz_get_mpz(ans.value, coeff) + return ans + + def _repr_(self): + self.jump(self._parent.default_prec()) + return pAdicGenericElement._repr_(self) + + cdef bint _is_equal(self, pAdicLazyElement right, slong prec): + cdef slong i + self.jump(prec) + right.jump(prec) + if self._valuation >= prec: + return right._valuation >= prec + if right._valuation >= prec: + return False + if self._valuation != right._valuation: + return False + for i in range(prec - self._valuation): + if not fmpz_equal(get_coeff(self._digits, i), get_coeff(right._digits, i)): + return False + return True + + @coerce_binop + def is_equal_at_precision(self, other, prec=None): + if prec is None: + prec = min(self.precision_absolute(), other.precision_absolute()) + prec = max(prec, self._parent.default_prec()) + return self._is_equal(other, long(prec)) + + def __eq__(self, other): + return self.is_equal_at_precision(other) + + cpdef bint _is_exact_zero(self) except -1: + return isinstance(self, pAdicLazyElement_zero) + + cpdef bint _is_inexact_zero(self) except -1: + return self._precrel == 0 + + def is_exact(self): + return self._is_exact_zero() + + def precision_absolute(self): + if self._is_exact_zero(): + return Infinity + return ZZ(self._precrel + self._valuation) + + def precision_relative(self): + return ZZ(self._precrel) + + def valuation(self): + if self._is_exact_zero(): + return Infinity + return ZZ(self._valuation) + + def unit_part(self): + if self._precrel == 0: + raise PrecisionError + return self >> self._valuation + + def __lshift__(self, s): + cdef slong shift = long(s) + if shift: + return pAdicLazyElement_shift((self)._parent, self, -shift) + else: + return self + + def __rshift__(self, s): + cdef slong shift = long(s) + if shift: + return pAdicLazyElement_shift((self)._parent, self, shift) + else: + return self + + cpdef _add_(self, other): + cdef list summands + cdef list signs + if isinstance(self, pAdicLazyElement_zero): + return other + if isinstance(other, pAdicLazyElement_zero): + return self + if isinstance(self, pAdicLazyElement_add): + summands = list((self)._summands) + signs = list((self)._signs) + else: + summands = [self] + signs = [True] + if isinstance(other, pAdicLazyElement_add): + summands.extend((other)._summands) + signs.extend((other)._signs) + else: + summands.append(other) + signs.append(True) + return pAdicLazyElement_add(self._parent, summands, signs) + + cpdef _sub_(self, other): + cdef list summands + cdef list signs + if isinstance(self, pAdicLazyElement_zero): + return -other + if isinstance(other, pAdicLazyElement_zero): + return self + if isinstance(self, pAdicLazyElement_add): + summands = list((self)._summands) + signs = list((self)._signs) + else: + summands = [self] + signs = [True] + if isinstance(other, pAdicLazyElement_add): + summands.extend((other)._summands) + signs.extend([ not sign for sign in (other)._signs ]) + else: + summands.append(other) + signs.append(False) + return pAdicLazyElement_add(self._parent, summands, signs) + + cpdef _neg_(self): + cdef list summands + cdef list signs + if isinstance(self, pAdicLazyElement_add): + summands = list((self)._summands) + signs = [ not sign for sign in (self)._signs ] + else: + summands = [self] + signs = [False] + return pAdicLazyElement_add(self._parent, summands, signs) + + cpdef _mul_(self, other): + if isinstance(self, pAdicLazyElement_zero) or isinstance(other, pAdicLazyElement_one): + return self + if isinstance(self, pAdicLazyElement_one) or isinstance(other, pAdicLazyElement_zero): + return other + return pAdicLazyElement_mul(self._parent, self, other) + + +# Assignations +############## + +# Zero + +cdef class pAdicLazyElement_zero(pAdicLazyElement): + def __init__(self, parent): + pAdicLazyElement.__init__(self, parent) + self._valuation = maxordp + + cpdef jump(self, prec): + return True + + cdef bint next(self) except -1: + return True + + +# One + +cdef class pAdicLazyElement_one(pAdicLazyElement): + def __init__(self, parent): + pAdicLazyElement.__init__(self, parent) + fmpz_poly_set_ui(self._digits, 1) + + cpdef jump(self, prec): + if self._precrel < prec: + self._precrel = prec + return True + + cdef bint next(self) except -1: + self._precrel += 1 + return True + + +# Value + +cdef class pAdicLazyElement_value(pAdicLazyElement): + def __init__(self, parent, value, prec=None, maxprec=None): + pAdicLazyElement.__init__(self, parent) + cdef Integer x = ZZ(value) + fmpz_poly_set_mpz(self._digits, x.value) + self._maxprec = maxprec + + cdef bint next(self) except -1: + if (self._maxprec is not None) and (self._valuation + self._precrel >= self._maxprec): + return False + reduce_coeff(self._digits, self._precrel, self._prime) + if self._precrel == 0 and fmpz_is_zero(get_coeff(self._digits, 0)): + self._valuation += 1 + fmpz_poly_shift_right(self._digits, self._digits, 1) + else: + self._precrel += 1 + return True + + +# Random + +cdef class pAdicLazyElement_random(pAdicLazyElement): + def __cinit__(self): + flint_randinit(self._randstate) + + def __dealloc__(self): + flint_randclear(self._randstate) + + def __init__(self, parent): + pAdicLazyElement.__init__(self, parent) + + cdef bint next(self) except -1: + cdef fmpz_t r; + fmpz_randm(r, self._randstate, self._prime) + if self._precrel == 0 and fmpz_is_zero(r): + self._valuation += 1 + else: + fmpz_poly_set_coeff_fmpz(self._digits, self._precrel, r); + self._precrel += 1 + return True + + +# Operations +############ + +# Shift + +cdef class pAdicLazyElement_shift(pAdicLazyElement): + def __init__(self, parent, pAdicLazyElement x, slong s): + pAdicLazyElement.__init__(self, parent) + self._x = x + self._shift = s + self._valuation = x._valuation - s + self._precrel = x._precrel + fmpz_poly_set(self._digits, x._digits) + + cdef bint next(self) except -1: + cdef slong n = self._valuation + self._precrel + cdef pAdicLazyElement x = self._x + cdef fmpz* digit + + if not x.jump(n + self._shift + 1): + return False + digit = get_coeff(x._digits, n + self._shift - x._valuation) + fmpz_poly_set_coeff_fmpz(self._digits, self._precrel, digit) + if self._precrel == 0 and fmpz_is_zero(get_coeff(self._digits, 0)): + self._valuation += 1 + fmpz_poly_shift_right(self._digits, self._digits, 1) + else: + self._precrel += 1 + return True + + +# Addition + +cdef class pAdicLazyElement_add(pAdicLazyElement): + def __init__(self, parent, summands, signs): + pAdicLazyElement.__init__(self, parent) + self._summands = summands + self._signs = signs + + cdef bint next(self) except -1: + cdef pAdicLazyElement summand + cdef slong n = self._valuation + self._precrel + cdef fmpz* coeff + for summand in self._summands: + if not summand.jump(n+1): + return False + cdef slong i + for i in range(len(self._summands)): + summand = self._summands[i] + coeff = get_coeff(summand._digits, n - summand._valuation) + if self._signs[i]: + iadd_coeff(self._digits, coeff, n - self._valuation) + else: + isub_coeff(self._digits, coeff, n - self._valuation) + reduce_coeff(self._digits, self._precrel, self._prime) + if self._precrel == 0 and fmpz_is_zero(get_coeff(self._digits, 0)): + self._valuation += 1 + fmpz_poly_shift_right(self._digits, self._digits, 1) + else: + self._precrel += 1 + return True + + +# Multiplication + +cdef class pAdicLazyElement_mul(pAdicLazyElement): + def __cinit__(self): + fmpz_poly_init(self._tmp) + + def __dealloc__(self): + fmpz_poly_clear(self._tmp) + + def __init__(self, parent, x, y): + pAdicLazyElement.__init__(self, parent) + self._x = x + self._y = y + self._valuation = self._x._valuation + self._y._valuation + + cdef bint next(self) except -1: + cdef pAdicLazyElement x = self._x + cdef pAdicLazyElement y = self._y + cdef slong n = self._valuation + self._precrel + + cdef bint success = (x.jump(n - y._valuation + 1) + and y.jump(n - x._valuation + 1)) + if self._precrel == 0: + self._valuation = x._valuation + y._valuation + if self._valuation > n: + return True + if self._valuation < n or x._precrel == 0 or y._precrel == 0: + return False + elif not success: + return False + + n = self._precrel + cdef slong m = n + 2 + cdef slong len = 1 + cdef fmpz_poly_t slicex, slicey + while m > 1: + get_slice(slicex, x._digits, len - 1, len) + get_slice(slicey, y._digits, (m-1)*len - 1, len) + fmpz_poly_mul(self._tmp, slicex, slicey) + iadd_shifted(self._digits, self._tmp, n) + if m > 2: + get_slice(slicex, x._digits, (m-1)*len - 1, len) + get_slice(slicey, y._digits, len - 1, len) + fmpz_poly_mul(self._tmp, slicex, slicey) + iadd_shifted(self._digits, self._tmp, n) + if m & 1: + break + m >>= 1 + len <<= 1 + reduce_coeff(self._digits, self._precrel, self._prime) + self._precrel += 1 + return True + + +# Division + +#cdef class pAdicLazyElement_div(pAdicLazyElement): + + +# Square root + +#cdef class pAdicLazyElement_sqrt(pAdicLazyElement): + + + +# Self-referent definitions +########################### + +cdef class pAdicLazyElement_selfref(pAdicLazyElement): + def __init__(self, parent): + pAdicLazyElement.__init__(self, parent) + self._definition = None + self._next = False + + def set(self, definition): + if self._definition is not None: + raise ValueError("this self-referent number is already defined") + self._definition = self._parent(definition) + try: + self.next() + except RecursionError: + self._definition = None + self._valuation = 0 + self._precrel = 0 + raise + + def __eq__(self, other): + if self._definition is None: + self.set(other) + return True + return pAdicLazyElement.__eq__(self, other) + + cdef bint next(self) except -1: + cdef pAdicLazyElement definition = self._definition + cdef fmpz* digit + + if definition is None: + return False + if self._next: + raise RecursionError("definition looks circular") + + self._next = True + success = definition.jump(self._valuation + self._precrel + 1) + if success: + if definition._valuation > self._valuation: + self._valuation = definition._valuation + else: + digit = get_coeff(definition._digits, self._precrel) + fmpz_poly_set_coeff_fmpz(self._digits, self._precrel, digit); + self._precrel += 1 + self._next = False + return success + + +# Expansion +########### + +cdef class ExpansionIter(object): + cdef pAdicLazyElement elt + cdef slong start + cdef slong stop + cdef slong current + + def __init__(self, pAdicLazyElement elt, start, stop): + self.elt = elt + if start is None: + self.start = 0 + else: + self.start = long(start) + if stop is None: + self.stop = self.start - 1 + else: + self.stop = long(stop) + if self.stop <= self.start: + self.stop = self.start + self.current = self.start + + def __repr__(self): + return "%s-adic expansion of %s" % (self.elt._parent.prime(), self.elt) + + def __len__(self): + if self.stop < self.start: + raise NotImplementedError("infinite sequence") + return ZZ(self.stop - self.start) + + def __iter__(self): + return self + + def __next__(self): + if self.stop < self.start or self.current < self.stop: + digit = self.elt._digit(self.current) + self.current += 1 + return digit + else: + raise StopIteration From 5649641b48dc6bbec3206c6886b1c46bc5286a45 Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Sun, 3 Jan 2021 10:53:35 +0100 Subject: [PATCH 080/406] cdef jump + improve shift --- src/sage/rings/padics/padic_lazy_element.pxd | 9 +- src/sage/rings/padics/padic_lazy_element.pyx | 142 ++++++++++++------- 2 files changed, 97 insertions(+), 54 deletions(-) diff --git a/src/sage/rings/padics/padic_lazy_element.pxd b/src/sage/rings/padics/padic_lazy_element.pxd index e3de8447dbc..03848b9c82a 100644 --- a/src/sage/rings/padics/padic_lazy_element.pxd +++ b/src/sage/rings/padics/padic_lazy_element.pxd @@ -12,9 +12,9 @@ cdef class pAdicLazyElement(pAdicGenericElement): cdef slong _valuation cdef slong _precrel - cpdef jump(self, prec) - cdef bint next(self) except -1 - cdef Integer _digit(self, i) + cdef bint _jump_c(self, slong prec) + cdef bint _next_c(self) except -1 + cdef Integer _digit(self, slong i) cdef bint _is_equal(self, pAdicLazyElement right, slong prec) @@ -45,7 +45,8 @@ cdef class pAdicLazyElement_add(pAdicLazyElement): cdef class pAdicLazyElement_mul(pAdicLazyElement): cdef pAdicLazyElement _x cdef pAdicLazyElement _y - cdef fmpz_poly_t _tmp + cdef fmpz_t _tmp_coeff + cdef fmpz_poly_t _tmp_poly #cdef class pAdicLazyElement_div(pAdicLazyElement) #cdef class pAdicLazyElement_sqrt(pAdicLazyElement) diff --git a/src/sage/rings/padics/padic_lazy_element.pyx b/src/sage/rings/padics/padic_lazy_element.pyx index bec93baf782..bee2c7edb96 100644 --- a/src/sage/rings/padics/padic_lazy_element.pyx +++ b/src/sage/rings/padics/padic_lazy_element.pyx @@ -34,7 +34,6 @@ from sage.rings.padics.padic_generic_element cimport pAdicGenericElement from sage.rings.padics.precision_error import PrecisionError cdef long maxordp = (1L << (sizeof(long) * 8 - 2)) - 1 -MAXORDP = ZZ(maxordp) cdef class pAdicLazyElement(pAdicGenericElement): @@ -54,29 +53,54 @@ cdef class pAdicLazyElement(pAdicGenericElement): def prime(self): return self._parent.prime() - cdef bint next(self) except -1: + cdef bint _next_c(self) except -1: raise NotImplementedError("must be implemented in subclasses") - cpdef jump(self, prec): + cdef bint _jump_c(self, slong prec): cdef slong i, stop stop = min(prec, maxordp) - self._precrel - self._valuation for i in range(stop): - if not self.next(): + if not self._next_c(): return False return prec < maxordp + def jump(self, prec): + if prec not in ZZ: + raise ValueError("precision must be an integer") + prec = ZZ(prec) + if prec >= maxordp: + raise OverflowError + return self._jump_c(long(prec)) + def expansion(self, n=None): if n is None: return ExpansionIter(self, self._valuation, self._valuation + self._precrel) if isinstance(n, slice): if n.step is not None: raise NotImplementedError - return ExpansionIter(self, n.start, n.stop) + if n.start is None: + start = 0 + elif n.start in ZZ: + start = ZZ(n.start) + else: + raise ValueError + if n.stop is None: + stop = None + elif n.stop in ZZ: + start = ZZ(n.stop) + else: + raise ValueError + return ExpansionIter(self, start, stop) else: - return self._digit(n) - - cdef Integer _digit(self, i): - if not self.jump(i+1): + if n not in ZZ: + raise IndexError("index must be an integer") + n = ZZ(n) + if n >= maxordp: + raise OverflowError + return self._digit(long(n)) + + cdef Integer _digit(self, slong i): + if not self._jump_c(i+1): raise PrecisionError cdef Integer ans = PY_NEW(Integer) cdef fmpz* coeff = get_coeff(self._digits, i - self._valuation) @@ -89,8 +113,8 @@ cdef class pAdicLazyElement(pAdicGenericElement): cdef bint _is_equal(self, pAdicLazyElement right, slong prec): cdef slong i - self.jump(prec) - right.jump(prec) + self._jump_c(prec) + right._jump_c(prec) if self._valuation >= prec: return right._valuation >= prec if right._valuation >= prec: @@ -139,20 +163,16 @@ cdef class pAdicLazyElement(pAdicGenericElement): raise PrecisionError return self >> self._valuation - def __lshift__(self, s): - cdef slong shift = long(s) - if shift: - return pAdicLazyElement_shift((self)._parent, self, -shift) - else: - return self - def __rshift__(self, s): cdef slong shift = long(s) if shift: - return pAdicLazyElement_shift((self)._parent, self, shift) + return pAdicLazyElement_shift((self)._parent, self, shift, True) else: return self + def __lshift__(self, s): + return self.__rshift__(-s) + cpdef _add_(self, other): cdef list summands cdef list signs @@ -224,10 +244,10 @@ cdef class pAdicLazyElement_zero(pAdicLazyElement): pAdicLazyElement.__init__(self, parent) self._valuation = maxordp - cpdef jump(self, prec): + cdef bint _jump_c(self, slong prec): return True - cdef bint next(self) except -1: + cdef bint _next_c(self) except -1: return True @@ -238,12 +258,12 @@ cdef class pAdicLazyElement_one(pAdicLazyElement): pAdicLazyElement.__init__(self, parent) fmpz_poly_set_ui(self._digits, 1) - cpdef jump(self, prec): + cdef bint _jump_c(self, slong prec): if self._precrel < prec: self._precrel = prec return True - cdef bint next(self) except -1: + cdef bint _next_c(self) except -1: self._precrel += 1 return True @@ -257,7 +277,7 @@ cdef class pAdicLazyElement_value(pAdicLazyElement): fmpz_poly_set_mpz(self._digits, x.value) self._maxprec = maxprec - cdef bint next(self) except -1: + cdef bint _next_c(self) except -1: if (self._maxprec is not None) and (self._valuation + self._precrel >= self._maxprec): return False reduce_coeff(self._digits, self._precrel, self._prime) @@ -281,7 +301,7 @@ cdef class pAdicLazyElement_random(pAdicLazyElement): def __init__(self, parent): pAdicLazyElement.__init__(self, parent) - cdef bint next(self) except -1: + cdef bint _next_c(self) except -1: cdef fmpz_t r; fmpz_randm(r, self._randstate, self._prime) if self._precrel == 0 and fmpz_is_zero(r): @@ -298,20 +318,31 @@ cdef class pAdicLazyElement_random(pAdicLazyElement): # Shift cdef class pAdicLazyElement_shift(pAdicLazyElement): - def __init__(self, parent, pAdicLazyElement x, slong s): + def __init__(self, parent, pAdicLazyElement x, slong s, bint truncate): + cdef slong start = 0 + cdef fmpz_poly_t digits; pAdicLazyElement.__init__(self, parent) self._x = x self._shift = s - self._valuation = x._valuation - s - self._precrel = x._precrel - fmpz_poly_set(self._digits, x._digits) + if truncate and x._valuation < s: + start = s - x._valuation + self._valuation = 0 + self._precrel = x._precrel - start + else: + self._valuation = x._valuation - s + self._precrel = x._precrel + if self._precrel < 0: + self._precrel = 0 + else: + get_slice(digits, x._digits, start, self._precrel) + fmpz_poly_set(self._digits, digits) - cdef bint next(self) except -1: + cdef bint _next_c(self) except -1: cdef slong n = self._valuation + self._precrel cdef pAdicLazyElement x = self._x cdef fmpz* digit - if not x.jump(n + self._shift + 1): + if not x._jump_c(n + self._shift + 1): return False digit = get_coeff(x._digits, n + self._shift - x._valuation) fmpz_poly_set_coeff_fmpz(self._digits, self._precrel, digit) @@ -331,12 +362,12 @@ cdef class pAdicLazyElement_add(pAdicLazyElement): self._summands = summands self._signs = signs - cdef bint next(self) except -1: + cdef bint _next_c(self) except -1: cdef pAdicLazyElement summand cdef slong n = self._valuation + self._precrel cdef fmpz* coeff for summand in self._summands: - if not summand.jump(n+1): + if not summand._jump_c(n+1): return False cdef slong i for i in range(len(self._summands)): @@ -359,10 +390,12 @@ cdef class pAdicLazyElement_add(pAdicLazyElement): cdef class pAdicLazyElement_mul(pAdicLazyElement): def __cinit__(self): - fmpz_poly_init(self._tmp) + fmpz_init(self._tmp_coeff) + fmpz_poly_init(self._tmp_poly) def __dealloc__(self): - fmpz_poly_clear(self._tmp) + fmpz_clear(self._tmp_coeff) + fmpz_poly_clear(self._tmp_poly) def __init__(self, parent, x, y): pAdicLazyElement.__init__(self, parent) @@ -370,13 +403,13 @@ cdef class pAdicLazyElement_mul(pAdicLazyElement): self._y = y self._valuation = self._x._valuation + self._y._valuation - cdef bint next(self) except -1: + cdef bint _next_c(self) except -1: cdef pAdicLazyElement x = self._x cdef pAdicLazyElement y = self._y cdef slong n = self._valuation + self._precrel - cdef bint success = (x.jump(n - y._valuation + 1) - and y.jump(n - x._valuation + 1)) + cdef bint success = (x._jump_c(n - y._valuation + 1) + and y._jump_c(n - x._valuation + 1)) if self._precrel == 0: self._valuation = x._valuation + y._valuation if self._valuation > n: @@ -387,23 +420,28 @@ cdef class pAdicLazyElement_mul(pAdicLazyElement): return False n = self._precrel + fmpz_mul(self._tmp_coeff, get_coeff(x._digits, 0), get_coeff(y._digits, n)) + iadd_coeff(self._digits, self._tmp_coeff, n) + if n: + fmpz_mul(self._tmp_coeff, get_coeff(x._digits, n), get_coeff(y._digits, 0)) + iadd_coeff(self._digits, self._tmp_coeff, n) + cdef slong m = n + 2 cdef slong len = 1 cdef fmpz_poly_t slicex, slicey - while m > 1: + while (m & 1 == 0) and (m > 3): + m >>= 1 + len <<= 1 get_slice(slicex, x._digits, len - 1, len) get_slice(slicey, y._digits, (m-1)*len - 1, len) - fmpz_poly_mul(self._tmp, slicex, slicey) - iadd_shifted(self._digits, self._tmp, n) + fmpz_poly_mul(self._tmp_poly, slicex, slicey) + iadd_shifted(self._digits, self._tmp_poly, n) if m > 2: get_slice(slicex, x._digits, (m-1)*len - 1, len) get_slice(slicey, y._digits, len - 1, len) - fmpz_poly_mul(self._tmp, slicex, slicey) - iadd_shifted(self._digits, self._tmp, n) - if m & 1: - break - m >>= 1 - len <<= 1 + fmpz_poly_mul(self._tmp_poly, slicex, slicey) + iadd_shifted(self._digits, self._tmp_poly, n) + reduce_coeff(self._digits, self._precrel, self._prime) self._precrel += 1 return True @@ -434,7 +472,7 @@ cdef class pAdicLazyElement_selfref(pAdicLazyElement): raise ValueError("this self-referent number is already defined") self._definition = self._parent(definition) try: - self.next() + self._next_c() except RecursionError: self._definition = None self._valuation = 0 @@ -447,7 +485,7 @@ cdef class pAdicLazyElement_selfref(pAdicLazyElement): return True return pAdicLazyElement.__eq__(self, other) - cdef bint next(self) except -1: + cdef bint _next_c(self) except -1: cdef pAdicLazyElement definition = self._definition cdef fmpz* digit @@ -457,7 +495,7 @@ cdef class pAdicLazyElement_selfref(pAdicLazyElement): raise RecursionError("definition looks circular") self._next = True - success = definition.jump(self._valuation + self._precrel + 1) + success = definition._jump_c(self._valuation + self._precrel + 1) if success: if definition._valuation > self._valuation: self._valuation = definition._valuation @@ -483,10 +521,14 @@ cdef class ExpansionIter(object): if start is None: self.start = 0 else: + if start >= maxordp: + raise OverflowError self.start = long(start) if stop is None: self.stop = self.start - 1 else: + if stop >= maxordp: + raise OverflowError self.stop = long(stop) if self.stop <= self.start: self.stop = self.start From 5413afdebae5107b754a63ae1dd9026e0da98833 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCnter=20Rote?= Date: Mon, 4 Jan 2021 02:06:30 +0100 Subject: [PATCH 081/406] inserting camera parameters for tachyon and fixing other parameters --- src/sage/plot/plot3d/base.pyx | 161 ++++++++++++++++++++++++++++------ 1 file changed, 132 insertions(+), 29 deletions(-) diff --git a/src/sage/plot/plot3d/base.pyx b/src/sage/plot/plot3d/base.pyx index ee708d98d76..11e5efc7444 100644 --- a/src/sage/plot/plot3d/base.pyx +++ b/src/sage/plot/plot3d/base.pyx @@ -13,9 +13,27 @@ AUTHORS: - Joshua Campbell (2020): Three.js animation support +- Günter Rote (2021): camera and light for tachyon (needs cleanup) + .. TODO:: - finish integrating tachyon -- good default lights, camera + finish integrating tachyon -- good default lights +""" + +##### BUGS?? tachyon scales everything to a unit box +# threejs does not distort, by default. +""" +p=plot3d(lambda u,v:(cos(u)-cos(v)), (-0.2,0.2),(-0.2,0.2)) +p.show() +p.show(viewer="tachyon") +p.show(viewer="tachyon",zoom=0.5) +""" +""" +Warning: + camera_center + light_position +are relative to some +they are, for example affected by zoom.?? """ # **************************************************************************** @@ -203,13 +221,18 @@ cdef class Graphics3d(SageObject): opts = self._process_viewing_options(kwds) T = self._prepare_for_tachyon( opts['frame'], opts['axes'], opts['frame_aspect_ratio'], - opts['aspect_ratio'], opts['zoom'] + opts['aspect_ratio'], + 1 # opts['zoom'] + # let zoom be handled by tachyon. + # We don't want the perspective to change by zooming ) - x, y = opts['figsize'][0]*100, opts['figsize'][1]*100 - if DOCTEST_MODE: - x, y = 10, 10 - tachyon_rt(T.tachyon(), filename, opts['verbosity'], - '-res %s %s' % (x, y)) +# x, y = opts['figsize'][0]*100, opts['figsize'][1]*100 +# if DOCTEST_MODE: +# x, y = 10, 10 + + tachyon_args = dict((key,val) for key,val in opts.items() if key in Graphics3d.tachyon_keywords) + tachyon_rt(T.tachyon(**tachyon_args), filename, opts['verbosity']) +# '-res %s %s' % (x, y)) ## handled by the tachyon method. from sage.repl.rich_output.buffer import OutputBuffer import sage.repl.rich_output.output_catalog as catalog import PIL.Image as Image @@ -931,9 +954,41 @@ cdef class Graphics3d(SageObject): """%(self.viewpoint().x3d_str(), self.x3d_str()) - def tachyon(self): + +################ TACHYON ################ + + ####### insertion of camera parameters + + tachyon_keywords = ("antialiasing",# "aspectratio", + "zoom", # zoom is already handled directly by scaling the scene: DISABLED + "raydepth", "figsize", "light_position", + "camera_center","updir", + #"look_at", # omit look_at. viewdir is sufficient + "viewdir") + # The tachyon "aspectratio" parameter is outdated for normal users: + # From the tachyon documentation: + # "By using the aspect ratio parameter, one can produce images which look correct on + # any screen. Aspect ratio alters the relative width of the image plane, while keeping + # the height of the image plane constant. Ingeneral, most workstation displays + # have an aspect ratio of 1.0." + + # aspectratio should rather be set to match nonsquare drawing area in "figsize" + + def tachyon(self, + zoom=1.0, # parameters are stolen from tachyion.py + antialiasing=False, +# aspectratio=1.0, + figsize=(4,4), # resolution = 100*figsize + raydepth=8, + camera_center=(2.3, 2.4, 2.0), # old default values + updir=(0, 0, 1), + # look_at=(0, 0, 0), + light_position=(4.0, 3.0, 2.0), + viewdir=None, + #projection='PERSPECTIVE', + ): """ - An tachyon input file (as a string) containing the this object. + A tachyon input file (as a string) containing the this object. EXAMPLES:: @@ -972,40 +1027,71 @@ cdef class Graphics3d(SageObject): render_params = self.default_render_params() # switch from LH to RH coords to be consistent with java rendition render_params.push_transform(Transformation(scale=[1,-1,1])) + + if len(camera_center)!=3: + raise ValueError('Camera center must consist of three numbers') + + if viewdir is None: +# viewdir = [float(look_at[i] - camera_center[i]) for i in range(3)] + viewdir = [float(- camera_center[i]) for i in range(3)] + if viewdir == [0.0,0.0,0.0]: + # print("Warning: camera_center and look_at coincide") + #print("Warning: camera_center at origin") + viewdir = (1,0,0) + + # switch from LH to RH coords to be consistent with java rendition + viewdir = flip_orientation(viewdir) + updir = flip_orientation(updir) + camera_center = flip_orientation(camera_center) + light_position = flip_orientation(light_position) + return """ begin_scene -resolution 400 400 +resolution {resolution_x:d} {resolution_y:d} camera - zoom 1.0 - aspectratio 1.0 - antialiasing %s - raydepth 8 - center 2.3 2.4 2.0 - viewdir -2.3 -2.4 -2.0 - updir 0.0 0.0 1.0 + zoom {zoom:f} + aspectratio {aspectratio:f} + antialiasing {antialiasing:d} + raydepth {raydepth:d} + center {camera_center} + viewdir {viewdir} + updir {updir} end_camera - - light center 4.0 3.0 2.0 + light center {light_position} rad 0.2 color 1.0 1.0 1.0 plane - center -2000 -1000 -500 - normal 2.3 2.4 2.0 + center {viewdir1000} + normal {viewdir} TEXTURE - AMBIENT 1.0 DIFFUSE 1.0 SPECULAR 1.0 OPACITY 1.0 + AMBIENT 1.0 DIFFUSE 0.0 SPECULAR 0.0 OPACITY 1.0 COLOR 1.0 1.0 1.0 TEXFUNC 0 - %s - - %s - -end_scene""" % (render_params.antialiasing, - "\n".join(sorted([t.tachyon_str() for t in self.texture_set()])), - "\n".join(flatten_list(self.tachyon_repr(render_params)))) + {scene} + + {render_parameters} + +end_scene""".format( + #render_params.antialiasing, this only provided the default value of 8 + scene = "\n".join(sorted([t.tachyon_str() for t in self.texture_set()])), + render_parameters = + "\n".join(flatten_list(self.tachyon_repr(render_params))), + viewdir1000=_tostring(1000*vector(viewdir).normalized()), + viewdir=_tostring(viewdir), + camera_center=_tostring(camera_center), + updir=_tostring(updir), + light_position=_tostring(light_position), + zoom=zoom, + antialiasing=antialiasing, + resolution_x=figsize[0]*100, + resolution_y=figsize[1]*100, + aspectratio=float(figsize[1])/float(figsize[0]), + raydepth=raydepth, + ) def obj(self): """ @@ -3145,3 +3231,20 @@ def optimal_extra_kwds(v): for k, w in b.iteritems(): a[k] = w return a + +def _tostring(s): + r""" + Converts vector information to a space-separated string. + + EXAMPLES:: + + sage: _tostring((1.0,1.0,1.0)) + '1.0 1.0 1.0' + """ +# if isinstance(s, str): +# return s + return ' '.join(map(str,s)) + +def flip_orientation(v): + "switch from LH to RH coords to be consistent with java rendition" + return (v[0],-v[1],v[2]) From c29270abf13d97bbf78f871918d606c9c604f973 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCnter=20Rote?= Date: Mon, 4 Jan 2021 02:12:35 +0100 Subject: [PATCH 082/406] option to retrieve camera position from threejs after rotation; requires still more post-processing --- src/sage/ext_data/threejs/threejs_template.html | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/sage/ext_data/threejs/threejs_template.html b/src/sage/ext_data/threejs/threejs_template.html index c9f2726a894..e499f4711aa 100644 --- a/src/sage/ext_data/threejs/threejs_template.html +++ b/src/sage/ext_data/threejs/threejs_template.html @@ -520,6 +520,21 @@ } + function getCamera() { + + function roundTo( x, n ) { return +x.toFixed(n); } + + var pos = camera.position; + var pos_r = [ roundTo( pos.x, 4 ), roundTo( pos.y, 4 ), roundTo( pos.z, 4 ) ]; + // var up = camera.up; // up is always (0,0,1) + var textArea = document.createElement('textarea'); + var cam_position = JSON.stringify(pos_r); + textArea.textContent = ',camera_center=' + cam_position; + textArea.style.csstext = 'position: absolute; top: -100%'; + document.body.append( textArea ); + textArea.select(); + document.execCommand( 'copy' ); + From 688061e7745fde93a24760d424c2412fa2dcb029 Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Mon, 4 Jan 2021 10:04:22 +0100 Subject: [PATCH 083/406] implement Qp and fraction_field --- src/sage/categories/pushout.py | 2 +- src/sage/rings/padics/all.py | 2 +- src/sage/rings/padics/factory.py | 11 +- src/sage/rings/padics/padic_base_generic.py | 3 + src/sage/rings/padics/padic_lazy.py | 93 +++++++--- src/sage/rings/padics/padic_lazy_element.pxd | 11 +- src/sage/rings/padics/padic_lazy_element.pyx | 171 +++++++++++-------- 7 files changed, 198 insertions(+), 95 deletions(-) diff --git a/src/sage/categories/pushout.py b/src/sage/categories/pushout.py index 2d4eb607f6b..38137b93119 100644 --- a/src/sage/categories/pushout.py +++ b/src/sage/categories/pushout.py @@ -2351,7 +2351,7 @@ class CompletionFunctor(ConstructionFunctor): """ rank = 4 _real_types = ['Interval', 'Ball', 'MPFR', 'RDF', 'RLF', 'RR'] - _dvr_types = [None, 'fixed-mod', 'floating-point', 'capped-abs', 'capped-rel', 'lattice-cap', 'lattice-float'] + _dvr_types = [None, 'fixed-mod', 'floating-point', 'capped-abs', 'capped-rel', 'lattice-cap', 'lattice-float', 'lazy'] def __init__(self, p, prec, extras=None): """ diff --git a/src/sage/rings/padics/all.py b/src/sage/rings/padics/all.py index 32b77f9c65b..dbdbeb9c1fb 100644 --- a/src/sage/rings/padics/all.py +++ b/src/sage/rings/padics/all.py @@ -1,6 +1,6 @@ from .generic_nodes import is_pAdicField, is_pAdicRing 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 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 cd77468e9b7..b8ab5e6d3bc 100644 --- a/src/sage/rings/padics/factory.py +++ b/src/sage/rings/padics/factory.py @@ -45,7 +45,7 @@ pAdicFieldCappedRelative, pAdicFieldFloatingPoint, pAdicFieldLattice) -from .padic_lazy import pAdicRingLazy +from .padic_lazy import pAdicRingLazy, pAdicFieldLazy from . import padic_printing ###################################################### @@ -747,6 +747,13 @@ def create_object(self, version, key): 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 == 'lazy': + if print_mode == 'terse': + return pAdicFieldLazy(p, prec, {'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) + else: + return pAdicFieldLazy(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': @@ -1396,6 +1403,8 @@ def QpLF(p, prec = None, *args, **kwds): """ return Qp(p, prec, 'lattice-float', *args, **kwds) +def QpL(p, prec=None, *args, **kwds): + return Qp(p, prec, 'lazy', *args, **kwds) ####################################################################################################### # diff --git a/src/sage/rings/padics/padic_base_generic.py b/src/sage/rings/padics/padic_base_generic.py index d75d99b217c..05de5b17bd7 100644 --- a/src/sage/rings/padics/padic_base_generic.py +++ b/src/sage/rings/padics/padic_base_generic.py @@ -52,6 +52,9 @@ def __init__(self, p, prec, print_mode, names, element_class): elif self.is_lattice_prec(): coerce_list = [QQ] convert_list = [] + elif self.is_lazy(): + coerce_list = [QQ] + convert_list = [] else: raise RuntimeError elif self.is_capped_relative(): diff --git a/src/sage/rings/padics/padic_lazy.py b/src/sage/rings/padics/padic_lazy.py index 721316fffc0..43baaab46f6 100644 --- a/src/sage/rings/padics/padic_lazy.py +++ b/src/sage/rings/padics/padic_lazy.py @@ -1,9 +1,9 @@ r""" This module provides basic support for lazy p-adic integers. - sage: R = ZpL(5) + sage: R = ZpL(5, print_mode="digits") sage: R - Lazy 5-adic Ring + 5-adic Ring with lazy precision One creates elements as usual:: @@ -131,7 +131,7 @@ from sage.rings.all import ZZ, QQ from sage.rings.infinity import Infinity -from sage.rings.padics.generic_nodes import pAdicRingBaseGeneric +from sage.rings.padics.generic_nodes import pAdicRingBaseGeneric, pAdicFieldBaseGeneric from sage.rings.padics.padic_generic_element import pAdicGenericElement from sage.rings.padics.padic_lazy_element import * @@ -139,14 +139,10 @@ class pAdicRingLazy(pAdicRingBaseGeneric): def __init__(self, p, prec, print_mode, names): - pAdicRingBaseGeneric.__init__(self, p, 0, print_mode, names, None) - self._p = p + pAdicRingBaseGeneric.__init__(self, p, prec, print_mode, names, None) self._zero_element = pAdicLazyElement_zero(self) self._default_prec = ZZ(prec) - def prime(self): - return self._p - def _prec_type(self): return "lazy" @@ -159,11 +155,6 @@ def default_prec(self): def precision_cap(self): return Infinity - def _coerce_map_from_(self, R): - # why do I need this? - if R is ZZ: - return True - def _element_constructor_(self, x): if isinstance(x, pAdicLazyElement): return x @@ -176,22 +167,80 @@ def _element_constructor_(self, x): return pAdicLazyElement_one(self) else: return pAdicLazyElement_value(self, ZZ(x)) + raise TypeError("unable to convert '%s' to a lazy %s-adic number" % (x, self.prime())) + + def selfref(self, valuation=0): + if valuation not in ZZ or valuation < 0: + raise ValueError("valuation must be a nonnegative integer") + valuation = ZZ(valuation) + if valuation >= MAXORDP: + raise OverflowError("valuation is too large (maximum is %s)" % MAXORDP) + return pAdicLazyElement_selfref(self, valuation) + + def _an_element_(self): + return self._zero_element + + def random_element(self): + return pAdicLazyElement_random(self, True) + + +class pAdicFieldLazy(pAdicFieldBaseGeneric): + def __init__(self, p, prec, print_mode, names): + pAdicRingBaseGeneric.__init__(self, p, prec, print_mode, names, None) + self._zero_element = pAdicLazyElement_zero(self) + self._default_prec = ZZ(prec) + + def _prec_type(self): + return "lazy" + + def is_lazy(self): + return True + + def default_prec(self): + return self._default_prec + + def precision_cap(self): + return Infinity + + def _element_constructor_(self, x): + if isinstance(x, pAdicLazyElement): + if x.parent() is self: + return x + return pAdicLazyElement_shift(self, x, 0, False) + if isinstance(x, pAdicGenericElement): + return pAdicLazyElement_value(self, ZZ(x), maxprec=x.precision_absolute()) + if x in ZZ: + if x == 0: + return self._zero_element + elif x == 1: + return pAdicLazyElement_one(self) + else: + return pAdicLazyElement_value(self, ZZ(x)) if x in QQ: num = x.numerator() denom = x.denominator() - if denom % self._p == 0: + if denom % self.prime() == 0: raise ValueError("denominator is not a unit") num = pAdicLazyElement_value(self, num) denom = pAdicLazyElement_value(self, denom) - x = num / denom - x.jump(prec) - return x + return num / denom + raise TypeError("unable to convert '%s' to a lazy %s-adic number" % (x, self.prime())) - def selfref(self): - return pAdicLazyElement_selfref(self) + def selfref(self, valuation=0): + if valuation not in ZZ: + raise ValueError("valuation must be an integer") + valuation = ZZ(valuation) + if valuation >= MAXORDP: + raise OverflowError("valuation is too large (maximum is %s)" % MAXORDP) + return pAdicLazyElement_selfref(self, valuation) def _an_element_(self): - return pAdicLazyElement_value(self, 0) + return self._zero_element - def random_element(self): - return pAdicLazyElement_random(self) + def random_element(self, integral=True): + return pAdicLazyElement_random(self, integral) + + def sum(self, summands): + n = len(summands) + signs = n * [True] + return pAdicLazyElement_add(self, summands, signs) diff --git a/src/sage/rings/padics/padic_lazy_element.pxd b/src/sage/rings/padics/padic_lazy_element.pxd index 03848b9c82a..6b659a29ae3 100644 --- a/src/sage/rings/padics/padic_lazy_element.pxd +++ b/src/sage/rings/padics/padic_lazy_element.pxd @@ -6,14 +6,19 @@ from sage.rings.integer cimport Integer from sage.rings.padics.padic_generic_element cimport pAdicGenericElement +cpdef lazy_sum(parent, summands) + + cdef class pAdicLazyElement(pAdicGenericElement): cdef fmpz_t _prime cdef fmpz_poly_t _digits cdef slong _valuation cdef slong _precrel - cdef bint _jump_c(self, slong prec) - cdef bint _next_c(self) except -1 + #cdef pAdicLazyElement _new_c(self, type cls) + + cdef int _jump_c(self, slong prec) + cdef int _next_c(self) cdef Integer _digit(self, slong i) cdef bint _is_equal(self, pAdicLazyElement right, slong prec) @@ -56,3 +61,5 @@ cdef class pAdicLazyElement_mul(pAdicLazyElement): cdef class pAdicLazyElement_selfref(pAdicLazyElement): cdef pAdicLazyElement _definition cdef bint _next + + cpdef set(self, pAdicLazyElement definition) diff --git a/src/sage/rings/padics/padic_lazy_element.pyx b/src/sage/rings/padics/padic_lazy_element.pyx index bee2c7edb96..cfb2774765d 100644 --- a/src/sage/rings/padics/padic_lazy_element.pyx +++ b/src/sage/rings/padics/padic_lazy_element.pyx @@ -34,6 +34,13 @@ from sage.rings.padics.padic_generic_element cimport pAdicGenericElement from sage.rings.padics.precision_error import PrecisionError cdef long maxordp = (1L << (sizeof(long) * 8 - 2)) - 1 +MAXORDP = ZZ(maxordp) + + +cpdef lazy_sum(parent, summands): + n = len(summands) + signs = n * [True] + return pAdicLazyElement_add(parent, summands, signs) cdef class pAdicLazyElement(pAdicGenericElement): @@ -45,32 +52,39 @@ cdef class pAdicLazyElement(pAdicGenericElement): def __init__(self, parent): pAdicGenericElement.__init__(self, parent) - cdef Integer p = self._parent.prime() - fmpz_set_mpz(self._prime, p.value) self._valuation = 0 self._precrel = 0 + # should find something better (prime_pow?) + cdef Integer p = self._parent.prime() + fmpz_set_mpz(self._prime, p.value) def prime(self): return self._parent.prime() - cdef bint _next_c(self) except -1: + cdef int _next_c(self): raise NotImplementedError("must be implemented in subclasses") - cdef bint _jump_c(self, slong prec): + cdef int _jump_c(self, slong prec): + cdef int error cdef slong i, stop stop = min(prec, maxordp) - self._precrel - self._valuation for i in range(stop): - if not self._next_c(): - return False - return prec < maxordp + error = self._next_c() + if error: + return error + return prec >= maxordp - def jump(self, prec): + def jump(self, prec, quiet=True): if prec not in ZZ: raise ValueError("precision must be an integer") prec = ZZ(prec) if prec >= maxordp: - raise OverflowError - return self._jump_c(long(prec)) + raise OverflowError("beyond maximum precision (which is %s)" % maxordp) + error = self._jump_c(long(prec)) + if not quiet and error == 1: + raise PrecisionError + if error == 2: + raise RecursionError("definition looks circular") def expansion(self, n=None): if n is None: @@ -96,12 +110,11 @@ cdef class pAdicLazyElement(pAdicGenericElement): raise IndexError("index must be an integer") n = ZZ(n) if n >= maxordp: - raise OverflowError + raise OverflowError("beyond maximum precision (which is %s)" % maxordp) return self._digit(long(n)) cdef Integer _digit(self, slong i): - if not self._jump_c(i+1): - raise PrecisionError + self.jump(i+1, quiet=False) cdef Integer ans = PY_NEW(Integer) cdef fmpz* coeff = get_coeff(self._digits, i - self._valuation) fmpz_get_mpz(ans.value, coeff) @@ -127,14 +140,13 @@ cdef class pAdicLazyElement(pAdicGenericElement): return True @coerce_binop - def is_equal_at_precision(self, other, prec=None): - if prec is None: - prec = min(self.precision_absolute(), other.precision_absolute()) - prec = max(prec, self._parent.default_prec()) + def is_equal_at_precision(self, other, prec): return self._is_equal(other, long(prec)) def __eq__(self, other): - return self.is_equal_at_precision(other) + prec = min(self.precision_absolute(), other.precision_absolute()) + prec = max(prec, self._parent.default_prec()) + return self.is_equal_at_precision(other, prec) cpdef bint _is_exact_zero(self) except -1: return isinstance(self, pAdicLazyElement_zero) @@ -166,7 +178,7 @@ cdef class pAdicLazyElement(pAdicGenericElement): def __rshift__(self, s): cdef slong shift = long(s) if shift: - return pAdicLazyElement_shift((self)._parent, self, shift, True) + return pAdicLazyElement_shift((self)._parent, self, shift, not (self)._parent.is_field()) else: return self @@ -233,6 +245,9 @@ cdef class pAdicLazyElement(pAdicGenericElement): return other return pAdicLazyElement_mul(self._parent, self, other) + cpdef _div_(self, other): + raise NotImplementedError("division is not implemented") + # Assignations ############## @@ -244,11 +259,11 @@ cdef class pAdicLazyElement_zero(pAdicLazyElement): pAdicLazyElement.__init__(self, parent) self._valuation = maxordp - cdef bint _jump_c(self, slong prec): - return True + cdef int _jump_c(self, slong prec): + return 0 - cdef bint _next_c(self) except -1: - return True + cdef int _next_c(self): + return 0 # One @@ -258,14 +273,14 @@ cdef class pAdicLazyElement_one(pAdicLazyElement): pAdicLazyElement.__init__(self, parent) fmpz_poly_set_ui(self._digits, 1) - cdef bint _jump_c(self, slong prec): + cdef int _jump_c(self, slong prec): if self._precrel < prec: self._precrel = prec - return True + return 0 - cdef bint _next_c(self) except -1: + cdef int _next_c(self): self._precrel += 1 - return True + return 0 # Value @@ -277,16 +292,16 @@ cdef class pAdicLazyElement_value(pAdicLazyElement): fmpz_poly_set_mpz(self._digits, x.value) self._maxprec = maxprec - cdef bint _next_c(self) except -1: + cdef int _next_c(self): if (self._maxprec is not None) and (self._valuation + self._precrel >= self._maxprec): - return False + return 1 reduce_coeff(self._digits, self._precrel, self._prime) if self._precrel == 0 and fmpz_is_zero(get_coeff(self._digits, 0)): self._valuation += 1 fmpz_poly_shift_right(self._digits, self._digits, 1) else: self._precrel += 1 - return True + return 0 # Random @@ -301,7 +316,7 @@ cdef class pAdicLazyElement_random(pAdicLazyElement): def __init__(self, parent): pAdicLazyElement.__init__(self, parent) - cdef bint _next_c(self) except -1: + cdef int _next_c(self): cdef fmpz_t r; fmpz_randm(r, self._randstate, self._prime) if self._precrel == 0 and fmpz_is_zero(r): @@ -309,7 +324,7 @@ cdef class pAdicLazyElement_random(pAdicLazyElement): else: fmpz_poly_set_coeff_fmpz(self._digits, self._precrel, r); self._precrel += 1 - return True + return 0 # Operations @@ -337,13 +352,15 @@ cdef class pAdicLazyElement_shift(pAdicLazyElement): get_slice(digits, x._digits, start, self._precrel) fmpz_poly_set(self._digits, digits) - cdef bint _next_c(self) except -1: + cdef int _next_c(self): cdef slong n = self._valuation + self._precrel cdef pAdicLazyElement x = self._x cdef fmpz* digit + cdef int error - if not x._jump_c(n + self._shift + 1): - return False + error = x._jump_c(n + self._shift + 1) + if error: + return error digit = get_coeff(x._digits, n + self._shift - x._valuation) fmpz_poly_set_coeff_fmpz(self._digits, self._precrel, digit) if self._precrel == 0 and fmpz_is_zero(get_coeff(self._digits, 0)): @@ -351,7 +368,7 @@ cdef class pAdicLazyElement_shift(pAdicLazyElement): fmpz_poly_shift_right(self._digits, self._digits, 1) else: self._precrel += 1 - return True + return 0 # Addition @@ -359,16 +376,20 @@ cdef class pAdicLazyElement_shift(pAdicLazyElement): cdef class pAdicLazyElement_add(pAdicLazyElement): def __init__(self, parent, summands, signs): pAdicLazyElement.__init__(self, parent) + self._valuation = min((summand)._valuation for summand in summands) self._summands = summands self._signs = signs - cdef bint _next_c(self) except -1: + cdef int _next_c(self): cdef pAdicLazyElement summand cdef slong n = self._valuation + self._precrel cdef fmpz* coeff + cdef int error + for summand in self._summands: - if not summand._jump_c(n+1): - return False + error = summand._jump_c(n+1) + if error: + return error cdef slong i for i in range(len(self._summands)): summand = self._summands[i] @@ -383,7 +404,7 @@ cdef class pAdicLazyElement_add(pAdicLazyElement): fmpz_poly_shift_right(self._digits, self._digits, 1) else: self._precrel += 1 - return True + return 0 # Multiplication @@ -403,21 +424,24 @@ cdef class pAdicLazyElement_mul(pAdicLazyElement): self._y = y self._valuation = self._x._valuation + self._y._valuation - cdef bint _next_c(self) except -1: + cdef int _next_c(self): cdef pAdicLazyElement x = self._x cdef pAdicLazyElement y = self._y cdef slong n = self._valuation + self._precrel - cdef bint success = (x._jump_c(n - y._valuation + 1) - and y._jump_c(n - x._valuation + 1)) + cdef int errorx = x._jump_c(n - y._valuation + 1) + cdef int errory = y._jump_c(n - x._valuation + 1) if self._precrel == 0: self._valuation = x._valuation + y._valuation if self._valuation > n: - return True + return 0 if self._valuation < n or x._precrel == 0 or y._precrel == 0: - return False - elif not success: - return False + if errorx and errory: + return min(errorx, errory) + else: + return max(errorx, errory) + elif errorx or errory: + return max(errorx, errory) n = self._precrel fmpz_mul(self._tmp_coeff, get_coeff(x._digits, 0), get_coeff(y._digits, n)) @@ -444,7 +468,10 @@ cdef class pAdicLazyElement_mul(pAdicLazyElement): reduce_coeff(self._digits, self._precrel, self._prime) self._precrel += 1 - return True + return 0 + + +#cdef class pAdicLazyElement_lmul(pAdicLazyElement): # Division @@ -462,49 +489,57 @@ cdef class pAdicLazyElement_mul(pAdicLazyElement): ########################### cdef class pAdicLazyElement_selfref(pAdicLazyElement): - def __init__(self, parent): + def __init__(self, parent, valuation): pAdicLazyElement.__init__(self, parent) + self._valuation = valuation self._definition = None self._next = False - def set(self, definition): + cpdef set(self, pAdicLazyElement definition): if self._definition is not None: raise ValueError("this self-referent number is already defined") - self._definition = self._parent(definition) - try: - self._next_c() - except RecursionError: + self._definition = definition + cdef slong valsve = self._valuation + cdef int error = self._jump_c(self._valuation + self._parent.default_prec()) + if error == 2: self._definition = None - self._valuation = 0 + self._valuation = valsve self._precrel = 0 - raise + self._next = False + raise RecursionError("definition looks circular") def __eq__(self, other): if self._definition is None: self.set(other) - return True return pAdicLazyElement.__eq__(self, other) - cdef bint _next_c(self) except -1: + cdef int _next_c(self): cdef pAdicLazyElement definition = self._definition cdef fmpz* digit + cdef slong diffval + cdef int error if definition is None: - return False + return 1 if self._next: - raise RecursionError("definition looks circular") + return 2 self._next = True - success = definition._jump_c(self._valuation + self._precrel + 1) - if success: - if definition._valuation > self._valuation: + error = definition._jump_c(self._valuation + self._precrel + 1) + if not error: + diffval = self._valuation - definition._valuation + if diffval < 0: self._valuation = definition._valuation else: - digit = get_coeff(definition._digits, self._precrel) + digit = get_coeff(definition._digits, self._precrel + diffval) fmpz_poly_set_coeff_fmpz(self._digits, self._precrel, digit); - self._precrel += 1 + if self._precrel == 0 and fmpz_is_zero(digit): + self._valuation += 1 + fmpz_poly_shift_right(self._digits, self._digits, 1) + else: + self._precrel += 1 self._next = False - return success + return error # Expansion @@ -522,13 +557,13 @@ cdef class ExpansionIter(object): self.start = 0 else: if start >= maxordp: - raise OverflowError + raise OverflowError("beyond maximum precision (which is %s)" % maxordp) self.start = long(start) if stop is None: self.stop = self.start - 1 else: if stop >= maxordp: - raise OverflowError + raise OverflowError("beyond maximum precision (which is %s)" % maxordp) self.stop = long(stop) if self.stop <= self.start: self.stop = self.start From f3397321658398e03765b57ea852d53fa8e684ab Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Mon, 4 Jan 2021 15:08:05 +0100 Subject: [PATCH 084/406] more shared variables --- src/sage/rings/padics/padic_lazy.py | 3 ++ src/sage/rings/padics/padic_lazy_element.pxd | 7 +-- src/sage/rings/padics/padic_lazy_element.pyx | 50 ++++++++++---------- 3 files changed, 33 insertions(+), 27 deletions(-) diff --git a/src/sage/rings/padics/padic_lazy.py b/src/sage/rings/padics/padic_lazy.py index 43baaab46f6..c62c67089a6 100644 --- a/src/sage/rings/padics/padic_lazy.py +++ b/src/sage/rings/padics/padic_lazy.py @@ -133,6 +133,7 @@ from sage.rings.infinity import Infinity from sage.rings.padics.generic_nodes import pAdicRingBaseGeneric, pAdicFieldBaseGeneric from sage.rings.padics.padic_generic_element import pAdicGenericElement +from sage.rings.padics.pow_computer_flint import PowComputer_flint from sage.rings.padics.padic_lazy_element import * @@ -142,6 +143,7 @@ def __init__(self, p, prec, print_mode, names): pAdicRingBaseGeneric.__init__(self, p, prec, print_mode, names, None) self._zero_element = pAdicLazyElement_zero(self) self._default_prec = ZZ(prec) + self.prime_pow = PowComputer_flint(p, 1, 1, 1, False) def _prec_type(self): return "lazy" @@ -189,6 +191,7 @@ def __init__(self, p, prec, print_mode, names): pAdicRingBaseGeneric.__init__(self, p, prec, print_mode, names, None) self._zero_element = pAdicLazyElement_zero(self) self._default_prec = ZZ(prec) + self.prime_pow = PowComputer_flint(p, 1, 1, 1, True) def _prec_type(self): return "lazy" diff --git a/src/sage/rings/padics/padic_lazy_element.pxd b/src/sage/rings/padics/padic_lazy_element.pxd index 6b659a29ae3..63abacb9b2d 100644 --- a/src/sage/rings/padics/padic_lazy_element.pxd +++ b/src/sage/rings/padics/padic_lazy_element.pxd @@ -3,14 +3,17 @@ from sage.libs.flint.types cimport flint_rand_t from sage.libs.flint.types cimport fmpz, fmpz_t, fmpz_poly_t from sage.rings.integer cimport Integer +from sage.rings.padics.pow_computer_flint cimport PowComputer_flint from sage.rings.padics.padic_generic_element cimport pAdicGenericElement + cpdef lazy_sum(parent, summands) cdef class pAdicLazyElement(pAdicGenericElement): - cdef fmpz_t _prime + cdef PowComputer_flint prime_pow + cdef fmpz_poly_t _digits cdef slong _valuation cdef slong _precrel @@ -50,8 +53,6 @@ cdef class pAdicLazyElement_add(pAdicLazyElement): cdef class pAdicLazyElement_mul(pAdicLazyElement): cdef pAdicLazyElement _x cdef pAdicLazyElement _y - cdef fmpz_t _tmp_coeff - cdef fmpz_poly_t _tmp_poly #cdef class pAdicLazyElement_div(pAdicLazyElement) #cdef class pAdicLazyElement_sqrt(pAdicLazyElement) diff --git a/src/sage/rings/padics/padic_lazy_element.pyx b/src/sage/rings/padics/padic_lazy_element.pyx index cfb2774765d..e25b75f2cdb 100644 --- a/src/sage/rings/padics/padic_lazy_element.pyx +++ b/src/sage/rings/padics/padic_lazy_element.pyx @@ -8,6 +8,7 @@ # http://www.gnu.org/licenses/ # **************************************************************************** +from sage.misc.misc import walltime from sage.libs.flint.types cimport * from sage.libs.flint.fmpz cimport * @@ -30,6 +31,7 @@ from sage.rings.all import ZZ from sage.rings.integer cimport Integer from sage.rings.infinity import Infinity +from sage.rings.padics.pow_computer_flint cimport PowComputer_flint from sage.rings.padics.padic_generic_element cimport pAdicGenericElement from sage.rings.padics.precision_error import PrecisionError @@ -38,11 +40,14 @@ MAXORDP = ZZ(maxordp) cpdef lazy_sum(parent, summands): + if not summands: + return parent.zero() n = len(summands) signs = n * [True] return pAdicLazyElement_add(parent, summands, signs) + cdef class pAdicLazyElement(pAdicGenericElement): def __cinit__(self): fmpz_poly_init(self._digits) @@ -52,11 +57,9 @@ cdef class pAdicLazyElement(pAdicGenericElement): def __init__(self, parent): pAdicGenericElement.__init__(self, parent) + self.prime_pow = self._parent.prime_pow self._valuation = 0 self._precrel = 0 - # should find something better (prime_pow?) - cdef Integer p = self._parent.prime() - fmpz_set_mpz(self._prime, p.value) def prime(self): return self._parent.prime() @@ -295,7 +298,7 @@ cdef class pAdicLazyElement_value(pAdicLazyElement): cdef int _next_c(self): if (self._maxprec is not None) and (self._valuation + self._precrel >= self._maxprec): return 1 - reduce_coeff(self._digits, self._precrel, self._prime) + reduce_coeff(self._digits, self._precrel, self.prime_pow.fprime) if self._precrel == 0 and fmpz_is_zero(get_coeff(self._digits, 0)): self._valuation += 1 fmpz_poly_shift_right(self._digits, self._digits, 1) @@ -318,7 +321,7 @@ cdef class pAdicLazyElement_random(pAdicLazyElement): cdef int _next_c(self): cdef fmpz_t r; - fmpz_randm(r, self._randstate, self._prime) + fmpz_randm(r, self._randstate, self.prime_pow.fprime) if self._precrel == 0 and fmpz_is_zero(r): self._valuation += 1 else: @@ -398,7 +401,7 @@ cdef class pAdicLazyElement_add(pAdicLazyElement): iadd_coeff(self._digits, coeff, n - self._valuation) else: isub_coeff(self._digits, coeff, n - self._valuation) - reduce_coeff(self._digits, self._precrel, self._prime) + reduce_coeff(self._digits, self._precrel, self.prime_pow.fprime) if self._precrel == 0 and fmpz_is_zero(get_coeff(self._digits, 0)): self._valuation += 1 fmpz_poly_shift_right(self._digits, self._digits, 1) @@ -407,17 +410,15 @@ cdef class pAdicLazyElement_add(pAdicLazyElement): return 0 -# Multiplication -cdef class pAdicLazyElement_mul(pAdicLazyElement): - def __cinit__(self): - fmpz_init(self._tmp_coeff) - fmpz_poly_init(self._tmp_poly) +# Multiplication - def __dealloc__(self): - fmpz_clear(self._tmp_coeff) - fmpz_poly_clear(self._tmp_poly) +cdef fmpz_t mul_tmp_coeff +cdef fmpz_poly_t mul_tmp_poly +fmpz_init(mul_tmp_coeff) +fmpz_poly_init(mul_tmp_poly) +cdef class pAdicLazyElement_mul(pAdicLazyElement): def __init__(self, parent, x, y): pAdicLazyElement.__init__(self, parent) self._x = x @@ -425,6 +426,7 @@ cdef class pAdicLazyElement_mul(pAdicLazyElement): self._valuation = self._x._valuation + self._y._valuation cdef int _next_c(self): + global mul_tmp_coeff, mul_tmp_poly cdef pAdicLazyElement x = self._x cdef pAdicLazyElement y = self._y cdef slong n = self._valuation + self._precrel @@ -444,11 +446,11 @@ cdef class pAdicLazyElement_mul(pAdicLazyElement): return max(errorx, errory) n = self._precrel - fmpz_mul(self._tmp_coeff, get_coeff(x._digits, 0), get_coeff(y._digits, n)) - iadd_coeff(self._digits, self._tmp_coeff, n) + fmpz_mul(mul_tmp_coeff, get_coeff(x._digits, 0), get_coeff(y._digits, n)) + iadd_coeff(self._digits, mul_tmp_coeff, n) if n: - fmpz_mul(self._tmp_coeff, get_coeff(x._digits, n), get_coeff(y._digits, 0)) - iadd_coeff(self._digits, self._tmp_coeff, n) + fmpz_mul(mul_tmp_coeff, get_coeff(x._digits, n), get_coeff(y._digits, 0)) + iadd_coeff(self._digits, mul_tmp_coeff, n) cdef slong m = n + 2 cdef slong len = 1 @@ -458,15 +460,15 @@ cdef class pAdicLazyElement_mul(pAdicLazyElement): len <<= 1 get_slice(slicex, x._digits, len - 1, len) get_slice(slicey, y._digits, (m-1)*len - 1, len) - fmpz_poly_mul(self._tmp_poly, slicex, slicey) - iadd_shifted(self._digits, self._tmp_poly, n) + fmpz_poly_mul(mul_tmp_poly, slicex, slicey) + iadd_shifted(self._digits, mul_tmp_poly, n) if m > 2: get_slice(slicex, x._digits, (m-1)*len - 1, len) get_slice(slicey, y._digits, len - 1, len) - fmpz_poly_mul(self._tmp_poly, slicex, slicey) - iadd_shifted(self._digits, self._tmp_poly, n) + fmpz_poly_mul(mul_tmp_poly, slicex, slicey) + iadd_shifted(self._digits, mul_tmp_poly, n) - reduce_coeff(self._digits, self._precrel, self._prime) + reduce_coeff(self._digits, self._precrel, self.prime_pow.fprime) self._precrel += 1 return 0 @@ -498,7 +500,7 @@ cdef class pAdicLazyElement_selfref(pAdicLazyElement): cpdef set(self, pAdicLazyElement definition): if self._definition is not None: raise ValueError("this self-referent number is already defined") - self._definition = definition + self._definition = definition cdef slong valsve = self._valuation cdef int error = self._jump_c(self._valuation + self._parent.default_prec()) if error == 2: From be24dad91dc25179242ca08d9eff43948b80dc1b Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Mon, 4 Jan 2021 22:48:48 +0100 Subject: [PATCH 085/406] implement division --- src/sage/rings/padics/padic_lazy.py | 48 ++-- src/sage/rings/padics/padic_lazy_element.pxd | 16 +- src/sage/rings/padics/padic_lazy_element.pyx | 236 +++++++++++++++---- 3 files changed, 230 insertions(+), 70 deletions(-) diff --git a/src/sage/rings/padics/padic_lazy.py b/src/sage/rings/padics/padic_lazy.py index c62c67089a6..bf2e7195fde 100644 --- a/src/sage/rings/padics/padic_lazy.py +++ b/src/sage/rings/padics/padic_lazy.py @@ -154,6 +154,9 @@ def is_lazy(self): def default_prec(self): return self._default_prec + def max_prec(self): + return 5 * self._default_prec + def precision_cap(self): return Infinity @@ -169,15 +172,23 @@ def _element_constructor_(self, x): return pAdicLazyElement_one(self) else: return pAdicLazyElement_value(self, ZZ(x)) + if x in QQ: + num = x.numerator() + denom = x.denominator() + if denom % self.prime() == 0: + raise ValueError("denominator is not a unit") + num = pAdicLazyElement_value(self, num) + denom = pAdicLazyElement_value(self, denom) + return num / denom raise TypeError("unable to convert '%s' to a lazy %s-adic number" % (x, self.prime())) - def selfref(self, valuation=0): - if valuation not in ZZ or valuation < 0: + def selfref(self, start_val=0): + if start_val not in ZZ or start_val < 0: raise ValueError("valuation must be a nonnegative integer") - valuation = ZZ(valuation) - if valuation >= MAXORDP: + start_val = ZZ(start_val) + if start_val >= MAXORDP: raise OverflowError("valuation is too large (maximum is %s)" % MAXORDP) - return pAdicLazyElement_selfref(self, valuation) + return pAdicLazyElement_selfref(self, start_val) def _an_element_(self): return self._zero_element @@ -202,9 +213,16 @@ def is_lazy(self): def default_prec(self): return self._default_prec + def max_prec(self): + return 5 * self._default_prec + def precision_cap(self): return Infinity + def _coerce_map_from_(self, R): + if self is R.fraction_field(): + return True + def _element_constructor_(self, x): if isinstance(x, pAdicLazyElement): if x.parent() is self: @@ -220,30 +238,24 @@ def _element_constructor_(self, x): else: return pAdicLazyElement_value(self, ZZ(x)) if x in QQ: + x = QQ(x) num = x.numerator() denom = x.denominator() - if denom % self.prime() == 0: - raise ValueError("denominator is not a unit") num = pAdicLazyElement_value(self, num) denom = pAdicLazyElement_value(self, denom) return num / denom raise TypeError("unable to convert '%s' to a lazy %s-adic number" % (x, self.prime())) - def selfref(self, valuation=0): - if valuation not in ZZ: + def selfref(self, start_val=0): + if start_val not in ZZ: raise ValueError("valuation must be an integer") - valuation = ZZ(valuation) - if valuation >= MAXORDP: + start_val = ZZ(start_val) + if start_val >= MAXORDP: raise OverflowError("valuation is too large (maximum is %s)" % MAXORDP) - return pAdicLazyElement_selfref(self, valuation) + return pAdicLazyElement_selfref(self, start_val) def _an_element_(self): return self._zero_element - def random_element(self, integral=True): + def random_element(self, integral=False): return pAdicLazyElement_random(self, integral) - - def sum(self, summands): - n = len(summands) - signs = n * [True] - return pAdicLazyElement_add(self, summands, signs) diff --git a/src/sage/rings/padics/padic_lazy_element.pxd b/src/sage/rings/padics/padic_lazy_element.pxd index 63abacb9b2d..c65b1e5a6df 100644 --- a/src/sage/rings/padics/padic_lazy_element.pxd +++ b/src/sage/rings/padics/padic_lazy_element.pxd @@ -38,7 +38,7 @@ cdef class pAdicLazyElement_value(pAdicLazyElement): cdef _maxprec cdef class pAdicLazyElement_random(pAdicLazyElement): - cdef flint_rand_t _randstate + pass @@ -53,8 +53,20 @@ cdef class pAdicLazyElement_add(pAdicLazyElement): cdef class pAdicLazyElement_mul(pAdicLazyElement): cdef pAdicLazyElement _x cdef pAdicLazyElement _y + +cdef class pAdicLazyElement_muldigit(pAdicLazyElement): + cdef fmpz* _x + cdef pAdicLazyElement _y -#cdef class pAdicLazyElement_div(pAdicLazyElement) +cdef class pAdicLazyElement_div(pAdicLazyElement): + cdef slong _maxprec + cdef fmpz_t _inverse + cdef pAdicLazyElement _num + cdef pAdicLazyElement _denom + cdef pAdicLazyElement _definition + + cdef int _bootstrap_c(self) + #cdef class pAdicLazyElement_sqrt(pAdicLazyElement) diff --git a/src/sage/rings/padics/padic_lazy_element.pyx b/src/sage/rings/padics/padic_lazy_element.pyx index e25b75f2cdb..9b9a3be5158 100644 --- a/src/sage/rings/padics/padic_lazy_element.pyx +++ b/src/sage/rings/padics/padic_lazy_element.pyx @@ -70,24 +70,49 @@ cdef class pAdicLazyElement(pAdicGenericElement): cdef int _jump_c(self, slong prec): cdef int error cdef slong i, stop - stop = min(prec, maxordp) - self._precrel - self._valuation - for i in range(stop): + prec = min(prec, maxordp) + while self._precrel + self._valuation < prec: error = self._next_c() if error: return error return prec >= maxordp - def jump(self, prec, quiet=True): - if prec not in ZZ: + def jump(self, prec=None, raise_error=True): + if prec is None: + default_prec = self._parent.default_prec() + error = 0 + if self._valuation <= -maxordp: + error = self._next_c() + if not error: + if self.prime_pow.in_field: + prec = self._valuation + default_prec + error = self._jump_c(prec) + if not error and self._valuation < prec: + error = self._jump_c(self._valuation + default_prec) + else: + error = self._jump_c(default_prec) + elif prec not in ZZ: raise ValueError("precision must be an integer") - prec = ZZ(prec) - if prec >= maxordp: - raise OverflowError("beyond maximum precision (which is %s)" % maxordp) - error = self._jump_c(long(prec)) - if not quiet and error == 1: - raise PrecisionError - if error == 2: + else: + prec = ZZ(prec) + if prec >= maxordp: + raise OverflowError("beyond maximum precision (which is %s)" % maxordp) + if prec < -maxordp: + prec = -maxordp + error = self._jump_c(prec) + + if not raise_error: + return error + if error & 16: raise RecursionError("definition looks circular") + if error & 8: + raise ZeroDivisionError("cannot divide by something indistinguishable from zero") + if error & 4: + raise PrecisionError("not enough precision") + if error & 2: + raise ValueError("not yet defined") + if error & 1: + raise PrecisionError("cannot prove that the divisor does not vanish; try to increase the precision by hand") def expansion(self, n=None): if n is None: @@ -117,14 +142,22 @@ cdef class pAdicLazyElement(pAdicGenericElement): return self._digit(long(n)) cdef Integer _digit(self, slong i): - self.jump(i+1, quiet=False) + self.jump(i+1) cdef Integer ans = PY_NEW(Integer) cdef fmpz* coeff = get_coeff(self._digits, i - self._valuation) fmpz_get_mpz(ans.value, coeff) return ans def _repr_(self): - self.jump(self._parent.default_prec()) + error = self.jump(raise_error=False) + if error & 16: + return "Error: definition looks circular" + if error & 8: + return "Error: division by something indistinguishable from zero" + if error & 1: + return "cannot prove that the divisor does not vanish; try to increase the precision by hand" + if self._valuation <= -maxordp: + return "valuation not known" return pAdicGenericElement._repr_(self) cdef bint _is_equal(self, pAdicLazyElement right, slong prec): @@ -163,14 +196,20 @@ cdef class pAdicLazyElement(pAdicGenericElement): def precision_absolute(self): if self._is_exact_zero(): return Infinity + if self._valuation <= -maxordp: + raise PrecisionError("no lower bound on the valuation is known") return ZZ(self._precrel + self._valuation) def precision_relative(self): + if self._valuation <= -maxordp: + raise PrecisionError("no lower bound on the valuation is known") return ZZ(self._precrel) def valuation(self): if self._is_exact_zero(): return Infinity + if self._valuation <= -maxordp: + raise PrecisionError("no lower bound on the valuation is known") return ZZ(self._valuation) def unit_part(self): @@ -249,7 +288,18 @@ cdef class pAdicLazyElement(pAdicGenericElement): return pAdicLazyElement_mul(self._parent, self, other) cpdef _div_(self, other): - raise NotImplementedError("division is not implemented") + if isinstance(other, pAdicLazyElement_zero): + return ZeroDivisionError("cannot divide by zero") + if isinstance(other, pAdicLazyElement_one): + return self + return pAdicLazyElement_div(self._parent.fraction_field(), self, other) + + def __invert__(self): + if isinstance(self, pAdicLazyElement_zero): + return ZeroDivisionError("cannot divide by zero") + if isinstance(self, pAdicLazyElement_one): + return self + return pAdicLazyElement_div(self._parent.fraction_field(), self._parent.one(), self) # Assignations @@ -297,7 +347,7 @@ cdef class pAdicLazyElement_value(pAdicLazyElement): cdef int _next_c(self): if (self._maxprec is not None) and (self._valuation + self._precrel >= self._maxprec): - return 1 + return 4 reduce_coeff(self._digits, self._precrel, self.prime_pow.fprime) if self._precrel == 0 and fmpz_is_zero(get_coeff(self._digits, 0)): self._valuation += 1 @@ -309,19 +359,18 @@ cdef class pAdicLazyElement_value(pAdicLazyElement): # Random -cdef class pAdicLazyElement_random(pAdicLazyElement): - def __cinit__(self): - flint_randinit(self._randstate) - - def __dealloc__(self): - flint_randclear(self._randstate) +cdef flint_rand_t flint_randstate +flint_randinit(flint_randstate) - def __init__(self, parent): +cdef class pAdicLazyElement_random(pAdicLazyElement): + def __init__(self, parent, integral): pAdicLazyElement.__init__(self, parent) + if not integral: + self._valuation = ZZ.random_element() cdef int _next_c(self): - cdef fmpz_t r; - fmpz_randm(r, self._randstate, self.prime_pow.fprime) + cdef fmpz_t r + fmpz_randm(r, flint_randstate, self.prime_pow.fprime) if self._precrel == 0 and fmpz_is_zero(r): self._valuation += 1 else: @@ -413,10 +462,10 @@ cdef class pAdicLazyElement_add(pAdicLazyElement): # Multiplication -cdef fmpz_t mul_tmp_coeff -cdef fmpz_poly_t mul_tmp_poly -fmpz_init(mul_tmp_coeff) -fmpz_poly_init(mul_tmp_poly) +cdef fmpz_t tmp_coeff +cdef fmpz_poly_t tmp_poly +fmpz_init(tmp_coeff) +fmpz_poly_init(tmp_poly) cdef class pAdicLazyElement_mul(pAdicLazyElement): def __init__(self, parent, x, y): @@ -426,31 +475,29 @@ cdef class pAdicLazyElement_mul(pAdicLazyElement): self._valuation = self._x._valuation + self._y._valuation cdef int _next_c(self): - global mul_tmp_coeff, mul_tmp_poly + global tmp_coeff, tmp_poly cdef pAdicLazyElement x = self._x cdef pAdicLazyElement y = self._y cdef slong n = self._valuation + self._precrel cdef int errorx = x._jump_c(n - y._valuation + 1) cdef int errory = y._jump_c(n - x._valuation + 1) + cdef int error = errorx & errory if self._precrel == 0: self._valuation = x._valuation + y._valuation if self._valuation > n: return 0 if self._valuation < n or x._precrel == 0 or y._precrel == 0: - if errorx and errory: - return min(errorx, errory) - else: - return max(errorx, errory) - elif errorx or errory: - return max(errorx, errory) + return error | 4 + elif error: + return error n = self._precrel - fmpz_mul(mul_tmp_coeff, get_coeff(x._digits, 0), get_coeff(y._digits, n)) - iadd_coeff(self._digits, mul_tmp_coeff, n) + fmpz_mul(tmp_coeff, get_coeff(x._digits, 0), get_coeff(y._digits, n)) + iadd_coeff(self._digits, tmp_coeff, n) if n: - fmpz_mul(mul_tmp_coeff, get_coeff(x._digits, n), get_coeff(y._digits, 0)) - iadd_coeff(self._digits, mul_tmp_coeff, n) + fmpz_mul(tmp_coeff, get_coeff(x._digits, n), get_coeff(y._digits, 0)) + iadd_coeff(self._digits, tmp_coeff, n) cdef slong m = n + 2 cdef slong len = 1 @@ -460,25 +507,115 @@ cdef class pAdicLazyElement_mul(pAdicLazyElement): len <<= 1 get_slice(slicex, x._digits, len - 1, len) get_slice(slicey, y._digits, (m-1)*len - 1, len) - fmpz_poly_mul(mul_tmp_poly, slicex, slicey) - iadd_shifted(self._digits, mul_tmp_poly, n) + fmpz_poly_mul(tmp_poly, slicex, slicey) + iadd_shifted(self._digits, tmp_poly, n) if m > 2: get_slice(slicex, x._digits, (m-1)*len - 1, len) get_slice(slicey, y._digits, len - 1, len) - fmpz_poly_mul(mul_tmp_poly, slicex, slicey) - iadd_shifted(self._digits, mul_tmp_poly, n) + fmpz_poly_mul(tmp_poly, slicex, slicey) + iadd_shifted(self._digits, tmp_poly, n) + + reduce_coeff(self._digits, self._precrel, self.prime_pow.fprime) + self._precrel += 1 + return 0 + +cdef class pAdicLazyElement_muldigit(pAdicLazyElement): + # compute x*y where x is assumed to be in (0,p) + def __init__(self, parent, pAdicLazyElement_div x, pAdicLazyElement y): + pAdicLazyElement.__init__(self, parent) + self._x = x._inverse + self._y = y + self._valuation = y._valuation + + cdef int _next_c(self): + cdef slong n = self._valuation + self._precrel + cdef int error = self._y._jump_c(n+1) + if error: + return error + fmpz_mul(tmp_coeff, self._x, get_coeff(self._y._digits, n - self._y._valuation)) + iadd_coeff(self._digits, tmp_coeff, self._precrel) reduce_coeff(self._digits, self._precrel, self.prime_pow.fprime) self._precrel += 1 return 0 + def digits(self): + return fmpz_poly_get_str(self._digits) -#cdef class pAdicLazyElement_lmul(pAdicLazyElement): + def next(self): + return self._next_c() # Division -#cdef class pAdicLazyElement_div(pAdicLazyElement): +cdef class pAdicLazyElement_div(pAdicLazyElement): + def __cinit__(self): + fmpz_init(self._inverse) + + def __dealloc__(self): + fmpz_clear(self._inverse) + + def __init__(self, parent, pAdicLazyElement num, pAdicLazyElement denom): + pAdicLazyElement.__init__(self, parent) + self._num = num + self._denom = denom + if denom._valuation <= -maxordp: + self._maxprec = maxordp + 1 + else: + self._maxprec = denom._valuation + self._parent.default_prec() + cdef int error = self._bootstrap_c() + if error & 8: + raise ZeroDivisionError("cannot divide by something indistinguishable from zero") + if error: + self._valuation = -maxordp + + cdef int _bootstrap_c(self): + cdef int error + cdef pAdicLazyElement num = self._num + cdef pAdicLazyElement denom = self._denom + cdef fmpz_t gcd + + while denom._valuation < self._maxprec and denom._precrel == 0: + error = denom._next_c() + if error: + if error & 4: + error |= 8 + return error + if self._maxprec > maxordp and denom._valuation > -maxordp: + self._maxprec = denom._valuation + self._parent.default_prec() + if denom._precrel == 0: + return 1 + + self._valuation = num._valuation - denom._valuation + fmpz_gcdinv(gcd, self._inverse, get_coeff(denom._digits, 0), self.prime_pow.fprime) + cdef pAdicLazyElement a = pAdicLazyElement_muldigit(self._parent, self, num) + cdef pAdicLazyElement b = pAdicLazyElement_muldigit(self._parent, self, denom) + error = b._next_c() + b._valuation += 1 + b._precrel = 0 + fmpz_poly_shift_right(b._digits, b._digits, 1) + self._definition = a - b * self + return 0 + + cdef int _next_c(self): + cdef pAdicLazyElement definition = self._definition + cdef slong val + if definition is None: + error = self._bootstrap_c() + if error: + return error + else: + val = self._valuation + self._denom._valuation + error = definition._jump_c(val + self._precrel + 1) + if error: + return error + if definition._valuation > val: + self._valuation = definition._valuation - self._denom._valuation + else: + digit = get_coeff(definition._digits, self._precrel) + fmpz_poly_set_coeff_fmpz(self._digits, self._precrel, digit); + self._precrel += 1 + return 0 # Square root @@ -503,7 +640,7 @@ cdef class pAdicLazyElement_selfref(pAdicLazyElement): self._definition = definition cdef slong valsve = self._valuation cdef int error = self._jump_c(self._valuation + self._parent.default_prec()) - if error == 2: + if error & 16: self._definition = None self._valuation = valsve self._precrel = 0 @@ -522,9 +659,9 @@ cdef class pAdicLazyElement_selfref(pAdicLazyElement): cdef int error if definition is None: - return 1 - if self._next: return 2 + if self._next: + return 16 self._next = True error = definition._jump_c(self._valuation + self._precrel + 1) @@ -534,11 +671,10 @@ cdef class pAdicLazyElement_selfref(pAdicLazyElement): self._valuation = definition._valuation else: digit = get_coeff(definition._digits, self._precrel + diffval) - fmpz_poly_set_coeff_fmpz(self._digits, self._precrel, digit); if self._precrel == 0 and fmpz_is_zero(digit): self._valuation += 1 - fmpz_poly_shift_right(self._digits, self._digits, 1) else: + fmpz_poly_set_coeff_fmpz(self._digits, self._precrel, digit); self._precrel += 1 self._next = False return error From a35d5d40556bb6136ac7b24aad7defea1a7346f6 Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Mon, 4 Jan 2021 23:01:04 +0100 Subject: [PATCH 086/406] coercion in equality test --- src/sage/rings/padics/padic_lazy_element.pyx | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/sage/rings/padics/padic_lazy_element.pyx b/src/sage/rings/padics/padic_lazy_element.pyx index 9b9a3be5158..c30ec94033a 100644 --- a/src/sage/rings/padics/padic_lazy_element.pyx +++ b/src/sage/rings/padics/padic_lazy_element.pyx @@ -26,6 +26,8 @@ cdef extern from "sage/rings/padics/padic_lazy_element_helper.c": from sage.ext.stdsage cimport PY_NEW from sage.structure.element import coerce_binop +from sage.structure.element cimport have_same_parent +from sage.structure.coerce cimport coercion_model from sage.rings.all import ZZ from sage.rings.integer cimport Integer @@ -180,9 +182,13 @@ cdef class pAdicLazyElement(pAdicGenericElement): return self._is_equal(other, long(prec)) def __eq__(self, other): - prec = min(self.precision_absolute(), other.precision_absolute()) - prec = max(prec, self._parent.default_prec()) - return self.is_equal_at_precision(other, prec) + if have_same_parent(self, other): + prec = min(self.precision_absolute(), other.precision_absolute()) + prec = max(prec, self._parent.default_prec()) + return self.is_equal_at_precision(other, prec) + else: + a, b = coercion_model.canonical_coercion(self, other) + return a == b cpdef bint _is_exact_zero(self) except -1: return isinstance(self, pAdicLazyElement_zero) From 707287b49e4e6d45a9ac9ca20c7bb2d1fdcd7a33 Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Mon, 4 Jan 2021 23:45:46 +0100 Subject: [PATCH 087/406] c helper file --- .../rings/padics/padic_lazy_element_helper.c | 123 ++++++++++++++++++ 1 file changed, 123 insertions(+) create mode 100644 src/sage/rings/padics/padic_lazy_element_helper.c diff --git a/src/sage/rings/padics/padic_lazy_element_helper.c b/src/sage/rings/padics/padic_lazy_element_helper.c new file mode 100644 index 00000000000..c0a85e8e8aa --- /dev/null +++ b/src/sage/rings/padics/padic_lazy_element_helper.c @@ -0,0 +1,123 @@ +#include + +#include "flint/flint.h" +#include "flint/fmpz.h" +#include "flint/fmpz_poly.h" + +#define slong mp_limb_signed_t + + + +fmpz* get_coeff (fmpz_poly_t poly, slong i) +{ + static fmpz* zero; + if (i >= 0 && i < poly->length) + return poly->coeffs + i; + else + { + if (zero == NULL) + { + zero = malloc(sizeof(fmpz)); + fmpz_zero(zero); + } + return zero; + } +} + + +void get_slice(fmpz_poly_t slice, fmpz_poly_t poly, slong start, slong length) +{ + slong len = FLINT_MIN(length, poly->length - start); + if (len < 0) + { + slice->coeffs = NULL; + slice->alloc = 0; + slice->length = 0; + } + else + { + slice->coeffs = poly->coeffs + start; + slice->alloc = len; + slice->length = len; + } +} + + +void iadd_coeff(fmpz_poly_t poly, fmpz_t summand, slong i) +{ + fmpz *coeff; + slong len = i + 1; + if (fmpz_is_zero(summand)) + return; + + if (poly->length < len) + { + fmpz_poly_fit_length(poly, len); + poly->length = len; + coeff = poly->coeffs + i; + fmpz_set(coeff, summand); + } + else + { + coeff = poly->coeffs + i; + fmpz_add(coeff, coeff, summand); + } +} + + +void isub_coeff(fmpz_poly_t poly, fmpz_t summand, slong i) +{ + fmpz *coeff; + slong len = i + 1; + if (fmpz_is_zero(summand)) + return; + + if (poly->length < len) + { + fmpz_poly_fit_length(poly, len); + poly->length = len; + coeff = poly->coeffs + i; + fmpz_neg(coeff, summand); + } + else + { + coeff = poly->coeffs + i; + fmpz_sub(coeff, coeff, summand); + } +} + + +void iadd_shifted(fmpz_poly_t poly, fmpz_poly_t summand, slong shift) +{ + slong len = shift + summand->length; + + if (poly->length < len) + { + fmpz_poly_fit_length(poly, len); + poly->length = len; + } + + fmpz* cpoly = poly->coeffs + shift; + fmpz* lcpoly = cpoly + summand->length; + fmpz* csummand = summand->coeffs; + for ( ; cpoly < lcpoly; cpoly++, csummand++) + fmpz_add(cpoly, cpoly, csummand); +} + + +void reduce_coeff(fmpz_poly_t poly, slong i, fmpz_t modulus) +{ + if (i < poly->length) + { + fmpz_t quo, rem; + fmpz_init(quo); fmpz_init(rem); + fmpz_tdiv_qr(quo, rem, poly->coeffs + i, modulus); + if (fmpz_cmp_si(rem, 0) < 0) + { + fmpz_add(rem, rem, modulus); + fmpz_sub_ui(quo, quo, 1); + } + iadd_coeff(poly, quo, i + 1); + fmpz_poly_set_coeff_fmpz(poly, i, rem); + } +} From b130f140ec7b0835f5ccd5d604c30084792fa15c Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Tue, 5 Jan 2021 10:26:44 +0100 Subject: [PATCH 088/406] fix bugs in element_constructor --- src/sage/rings/padics/padic_lazy.py | 49 +++++---- src/sage/rings/padics/padic_lazy_element.pxd | 6 +- src/sage/rings/padics/padic_lazy_element.pyx | 101 ++++++++++++------ .../rings/padics/padic_lazy_element_helper.c | 20 ++-- 4 files changed, 113 insertions(+), 63 deletions(-) diff --git a/src/sage/rings/padics/padic_lazy.py b/src/sage/rings/padics/padic_lazy.py index bf2e7195fde..d29d691da37 100644 --- a/src/sage/rings/padics/padic_lazy.py +++ b/src/sage/rings/padics/padic_lazy.py @@ -154,33 +154,36 @@ def is_lazy(self): def default_prec(self): return self._default_prec - def max_prec(self): - return 5 * self._default_prec - def precision_cap(self): return Infinity def _element_constructor_(self, x): - if isinstance(x, pAdicLazyElement): - return x - if isinstance(x, pAdicGenericElement): + if isinstance(x, pAdicLazyElement) and x.prime() == self.prime(): + if x.parent() is self: + return x + if not x.is_equal_at_precision(self._zero_element, 0): + raise ValueError("negative valuation") + return pAdicLazyElement_shift(self, x, 0, False) + elif isinstance(x, pAdicGenericElement) and x.parent().prime() == self.prime(): + if x.valuation() < 0: + raise ValueError("negative valuation") return pAdicLazyElement_value(self, ZZ(x), maxprec=x.precision_absolute()) - if x in ZZ: + elif x in ZZ: if x == 0: return self._zero_element elif x == 1: return pAdicLazyElement_one(self) else: return pAdicLazyElement_value(self, ZZ(x)) - if x in QQ: + elif x in QQ: num = x.numerator() denom = x.denominator() if denom % self.prime() == 0: - raise ValueError("denominator is not a unit") + raise ValueError("negative valuation") num = pAdicLazyElement_value(self, num) denom = pAdicLazyElement_value(self, denom) - return num / denom - raise TypeError("unable to convert '%s' to a lazy %s-adic number" % (x, self.prime())) + return pAdicLazyElement_div(self, num, denom) + raise TypeError("unable to convert '%s' to a lazy %s-adic integer" % (x, self.prime())) def selfref(self, start_val=0): if start_val not in ZZ or start_val < 0: @@ -213,37 +216,33 @@ def is_lazy(self): def default_prec(self): return self._default_prec - def max_prec(self): - return 5 * self._default_prec - def precision_cap(self): return Infinity def _coerce_map_from_(self, R): - if self is R.fraction_field(): + if isinstance(R, pAdicRingLazy) and self is R.fraction_field(): return True def _element_constructor_(self, x): - if isinstance(x, pAdicLazyElement): + if isinstance(x, pAdicLazyElement) and x.prime() == self.prime(): if x.parent() is self: return x return pAdicLazyElement_shift(self, x, 0, False) - if isinstance(x, pAdicGenericElement): - return pAdicLazyElement_value(self, ZZ(x), maxprec=x.precision_absolute()) - if x in ZZ: + elif isinstance(x, pAdicGenericElement) and x.parent().prime() == self.prime(): + val, u = x.val_unit() + return pAdicLazyElement_value(self, ZZ(u), shift=val, maxprec=x.precision_absolute()) + elif x in ZZ: if x == 0: return self._zero_element elif x == 1: return pAdicLazyElement_one(self) else: return pAdicLazyElement_value(self, ZZ(x)) - if x in QQ: + elif x in QQ: x = QQ(x) - num = x.numerator() - denom = x.denominator() - num = pAdicLazyElement_value(self, num) - denom = pAdicLazyElement_value(self, denom) - return num / denom + num = pAdicLazyElement_value(self, x.numerator()) + denom = pAdicLazyElement_value(self, x.denominator()) + return pAdicLazyElement_div(self, num, denom) raise TypeError("unable to convert '%s' to a lazy %s-adic number" % (x, self.prime())) def selfref(self, start_val=0): diff --git a/src/sage/rings/padics/padic_lazy_element.pxd b/src/sage/rings/padics/padic_lazy_element.pxd index c65b1e5a6df..5add20694a9 100644 --- a/src/sage/rings/padics/padic_lazy_element.pxd +++ b/src/sage/rings/padics/padic_lazy_element.pxd @@ -24,7 +24,7 @@ cdef class pAdicLazyElement(pAdicGenericElement): cdef int _next_c(self) cdef Integer _digit(self, slong i) - cdef bint _is_equal(self, pAdicLazyElement right, slong prec) + cdef bint _is_equal(self, pAdicLazyElement right, slong prec) except -1 @@ -35,7 +35,9 @@ cdef class pAdicLazyElement_one(pAdicLazyElement): pass cdef class pAdicLazyElement_value(pAdicLazyElement): - cdef _maxprec + cdef slong _maxprec + cdef slong _shift + cdef bint _finished cdef class pAdicLazyElement_random(pAdicLazyElement): pass diff --git a/src/sage/rings/padics/padic_lazy_element.pyx b/src/sage/rings/padics/padic_lazy_element.pyx index c30ec94033a..d118d7c3245 100644 --- a/src/sage/rings/padics/padic_lazy_element.pyx +++ b/src/sage/rings/padics/padic_lazy_element.pyx @@ -77,7 +77,10 @@ cdef class pAdicLazyElement(pAdicGenericElement): error = self._next_c() if error: return error - return prec >= maxordp + if prec < maxordp: + return 0 + return 8 + def jump(self, prec=None, raise_error=True): if prec is None: @@ -102,17 +105,20 @@ cdef class pAdicLazyElement(pAdicGenericElement): if prec < -maxordp: prec = -maxordp error = self._jump_c(prec) - - if not raise_error: + if raise_error: + self._error(error) + else: return error - if error & 16: + + def _error(self, error): + if error & 32: raise RecursionError("definition looks circular") - if error & 8: + if error & 16: raise ZeroDivisionError("cannot divide by something indistinguishable from zero") - if error & 4: + if error & 8: + raise OverflowError + if error & 6: raise PrecisionError("not enough precision") - if error & 2: - raise ValueError("not yet defined") if error & 1: raise PrecisionError("cannot prove that the divisor does not vanish; try to increase the precision by hand") @@ -152,34 +158,51 @@ cdef class pAdicLazyElement(pAdicGenericElement): def _repr_(self): error = self.jump(raise_error=False) - if error & 16: + if error & 32: return "Error: definition looks circular" - if error & 8: + if error & 16: return "Error: division by something indistinguishable from zero" + if error & 8: + return "Error: overflow" if error & 1: return "cannot prove that the divisor does not vanish; try to increase the precision by hand" if self._valuation <= -maxordp: return "valuation not known" return pAdicGenericElement._repr_(self) - cdef bint _is_equal(self, pAdicLazyElement right, slong prec): - cdef slong i - self._jump_c(prec) - right._jump_c(prec) - if self._valuation >= prec: - return right._valuation >= prec - if right._valuation >= prec: - return False - if self._valuation != right._valuation: - return False - for i in range(prec - self._valuation): - if not fmpz_equal(get_coeff(self._digits, i), get_coeff(right._digits, i)): + cdef bint _is_equal(self, pAdicLazyElement right, slong prec) except -1: + if self._valuation <= -maxordp: + error = self._next_c() + if error: + self._error(error) + if right._valuation <= -maxordp: + error = right._next_c() + if error: + self._error(error) + cdef slong i = 0 + cdef slong j = self._valuation - right._valuation + if j > 0: + i = -j + j = 0 + prec -= self._valuation + while i < prec: + if self._precrel <= i: + error = self._next_c() + if error: + self._error(error) + if right._precrel <= j: + error = right._next_c() + if error: + self._error(error) + if not fmpz_equal(get_coeff(self._digits, i), get_coeff(right._digits, j)): return False + i += 1 + j += 1 return True @coerce_binop def is_equal_at_precision(self, other, prec): - return self._is_equal(other, long(prec)) + return self._is_equal(other, min(prec, maxordp)) def __eq__(self, other): if have_same_parent(self, other): @@ -345,11 +368,26 @@ cdef class pAdicLazyElement_one(pAdicLazyElement): # Value cdef class pAdicLazyElement_value(pAdicLazyElement): - def __init__(self, parent, value, prec=None, maxprec=None): + def __init__(self, parent, Integer value, slong shift=0, maxprec=None): pAdicLazyElement.__init__(self, parent) - cdef Integer x = ZZ(value) + cdef Integer x = value fmpz_poly_set_mpz(self._digits, x.value) - self._maxprec = maxprec + if maxprec is None: + self._maxprec = maxordp + else: + self._maxprec = maxprec + self._shift = self._valuation = shift + self._finished = False + + cdef int _jump_c(self, slong prec): + if not self._finished: + return pAdicLazyElement._jump_c(self, prec) + cdef slong precrel = min(prec, maxordp) - self._valuation + if precrel > self._precrel: + self._precrel = precrel + if prec < maxordp: + return 0 + return 8 cdef int _next_c(self): if (self._maxprec is not None) and (self._valuation + self._precrel >= self._maxprec): @@ -360,8 +398,11 @@ cdef class pAdicLazyElement_value(pAdicLazyElement): fmpz_poly_shift_right(self._digits, self._digits, 1) else: self._precrel += 1 + self._finished = fmpz_is_zero(get_coeff(self._digits, self._precrel)) return 0 + def is_finished(self): + return bool(self._finished) # Random @@ -570,7 +611,7 @@ cdef class pAdicLazyElement_div(pAdicLazyElement): else: self._maxprec = denom._valuation + self._parent.default_prec() cdef int error = self._bootstrap_c() - if error & 8: + if error & 16: raise ZeroDivisionError("cannot divide by something indistinguishable from zero") if error: self._valuation = -maxordp @@ -585,7 +626,7 @@ cdef class pAdicLazyElement_div(pAdicLazyElement): error = denom._next_c() if error: if error & 4: - error |= 8 + error |= 16 return error if self._maxprec > maxordp and denom._valuation > -maxordp: self._maxprec = denom._valuation + self._parent.default_prec() @@ -646,7 +687,7 @@ cdef class pAdicLazyElement_selfref(pAdicLazyElement): self._definition = definition cdef slong valsve = self._valuation cdef int error = self._jump_c(self._valuation + self._parent.default_prec()) - if error & 16: + if error & 32: self._definition = None self._valuation = valsve self._precrel = 0 @@ -667,7 +708,7 @@ cdef class pAdicLazyElement_selfref(pAdicLazyElement): if definition is None: return 2 if self._next: - return 16 + return 32 self._next = True error = definition._jump_c(self._valuation + self._precrel + 1) diff --git a/src/sage/rings/padics/padic_lazy_element_helper.c b/src/sage/rings/padics/padic_lazy_element_helper.c index c0a85e8e8aa..f1d99801c6b 100644 --- a/src/sage/rings/padics/padic_lazy_element_helper.c +++ b/src/sage/rings/padics/padic_lazy_element_helper.c @@ -90,18 +90,26 @@ void isub_coeff(fmpz_poly_t poly, fmpz_t summand, slong i) void iadd_shifted(fmpz_poly_t poly, fmpz_poly_t summand, slong shift) { slong len = shift + summand->length; + fmpz *cpoly, *last; + fmpz* csummand = summand->coeffs; if (poly->length < len) { fmpz_poly_fit_length(poly, len); + cpoly = poly->coeffs + shift; + last = poly->coeffs + poly->length; + for ( ; cpoly < last; cpoly++, csummand++) + fmpz_add(cpoly, cpoly, csummand); + last = poly->coeffs + shift + summand->length; + for ( ; cpoly < last; cpoly++, csummand++) + fmpz_set(cpoly, csummand); poly->length = len; + } else { + cpoly = poly->coeffs + shift; + last = cpoly + summand->length; + for ( ; cpoly < last; cpoly++, csummand++) + fmpz_add(cpoly, cpoly, csummand); } - - fmpz* cpoly = poly->coeffs + shift; - fmpz* lcpoly = cpoly + summand->length; - fmpz* csummand = summand->coeffs; - for ( ; cpoly < lcpoly; cpoly++, csummand++) - fmpz_add(cpoly, cpoly, csummand); } From d1193816f210b968853edebee553a9ef182c2c4c Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Tue, 5 Jan 2021 23:43:24 +0100 Subject: [PATCH 089/406] implement square root --- src/sage/rings/padics/padic_lazy_element.pxd | 8 +- src/sage/rings/padics/padic_lazy_element.pyx | 137 +++++++++++++++--- .../rings/padics/padic_lazy_element_helper.c | 6 +- 3 files changed, 127 insertions(+), 24 deletions(-) diff --git a/src/sage/rings/padics/padic_lazy_element.pxd b/src/sage/rings/padics/padic_lazy_element.pxd index 5add20694a9..8b739e46b9b 100644 --- a/src/sage/rings/padics/padic_lazy_element.pxd +++ b/src/sage/rings/padics/padic_lazy_element.pxd @@ -66,11 +66,13 @@ cdef class pAdicLazyElement_div(pAdicLazyElement): cdef pAdicLazyElement _num cdef pAdicLazyElement _denom cdef pAdicLazyElement _definition - cdef int _bootstrap_c(self) -#cdef class pAdicLazyElement_sqrt(pAdicLazyElement) - +cdef class pAdicLazyElement_sqrt(pAdicLazyElement): + cdef slong _maxprec + cdef pAdicLazyElement _x + cdef pAdicLazyElement _definition + cdef int _bootstrap_c(self) cdef class pAdicLazyElement_selfref(pAdicLazyElement): diff --git a/src/sage/rings/padics/padic_lazy_element.pyx b/src/sage/rings/padics/padic_lazy_element.pyx index d118d7c3245..42802291ab0 100644 --- a/src/sage/rings/padics/padic_lazy_element.pyx +++ b/src/sage/rings/padics/padic_lazy_element.pyx @@ -32,6 +32,7 @@ from sage.structure.coerce cimport coercion_model from sage.rings.all import ZZ from sage.rings.integer cimport Integer from sage.rings.infinity import Infinity +from sage.rings.finite_rings.finite_field_constructor import GF from sage.rings.padics.pow_computer_flint cimport PowComputer_flint from sage.rings.padics.padic_generic_element cimport pAdicGenericElement @@ -111,10 +112,12 @@ cdef class pAdicLazyElement(pAdicGenericElement): return error def _error(self, error): - if error & 32: + if error & 64: raise RecursionError("definition looks circular") - if error & 16: + if error & 32: raise ZeroDivisionError("cannot divide by something indistinguishable from zero") + if error & 16: + raise ValueError("not a square") if error & 8: raise OverflowError if error & 6: @@ -158,10 +161,12 @@ cdef class pAdicLazyElement(pAdicGenericElement): def _repr_(self): error = self.jump(raise_error=False) - if error & 32: + if error & 64: return "Error: definition looks circular" - if error & 16: + if error & 32: return "Error: division by something indistinguishable from zero" + if error & 16: + return "Error: not a square" if error & 8: return "Error: overflow" if error & 1: @@ -186,11 +191,11 @@ cdef class pAdicLazyElement(pAdicGenericElement): j = 0 prec -= self._valuation while i < prec: - if self._precrel <= i: + while self._precrel <= i: error = self._next_c() if error: self._error(error) - if right._precrel <= j: + while right._precrel <= j: error = right._next_c() if error: self._error(error) @@ -205,13 +210,28 @@ cdef class pAdicLazyElement(pAdicGenericElement): return self._is_equal(other, min(prec, maxordp)) def __eq__(self, other): - if have_same_parent(self, other): - prec = min(self.precision_absolute(), other.precision_absolute()) - prec = max(prec, self._parent.default_prec()) - return self.is_equal_at_precision(other, prec) - else: + if not have_same_parent(self, other): a, b = coercion_model.canonical_coercion(self, other) return a == b + cdef pAdicLazyElement right = other + if self._valuation <= -maxordp: + error = self._next_c() + if error: + self._error(error) + if right._valuation <= -maxordp: + error = right._next_c() + if error: + right._error(error) + minprec = min(self.precision_absolute(), right.precision_absolute()) + default_prec = self._parent.default_prec() + if self.prime_pow.in_field: + prec = self._valuation + default_prec + if not self._is_equal(right, max(minprec, prec)): + return False + if self._valuation < prec: + return self._is_equal(right, max(minprec, self._valuation + default_prec)) + else: + return self._is_equal(right, max(minprec, default_prec)) cpdef bint _is_exact_zero(self) except -1: return isinstance(self, pAdicLazyElement_zero) @@ -330,6 +350,9 @@ cdef class pAdicLazyElement(pAdicGenericElement): return self return pAdicLazyElement_div(self._parent.fraction_field(), self._parent.one(), self) + def sqrt(self): + return pAdicLazyElement_sqrt(self._parent, self) + # Assignations ############## @@ -401,8 +424,6 @@ cdef class pAdicLazyElement_value(pAdicLazyElement): self._finished = fmpz_is_zero(get_coeff(self._digits, self._precrel)) return 0 - def is_finished(self): - return bool(self._finished) # Random @@ -611,7 +632,7 @@ cdef class pAdicLazyElement_div(pAdicLazyElement): else: self._maxprec = denom._valuation + self._parent.default_prec() cdef int error = self._bootstrap_c() - if error & 16: + if error & 32: raise ZeroDivisionError("cannot divide by something indistinguishable from zero") if error: self._valuation = -maxordp @@ -626,7 +647,7 @@ cdef class pAdicLazyElement_div(pAdicLazyElement): error = denom._next_c() if error: if error & 4: - error |= 16 + error |= 32 return error if self._maxprec > maxordp and denom._valuation > -maxordp: self._maxprec = denom._valuation + self._parent.default_prec() @@ -667,7 +688,87 @@ cdef class pAdicLazyElement_div(pAdicLazyElement): # Square root -#cdef class pAdicLazyElement_sqrt(pAdicLazyElement): +cdef bint fmpz_modp_sqrt(fmpz* ans, fmpz* x, fmpz* p): + # Need to do something better + cdef Integer zx = PY_NEW(Integer) + fmpz_get_mpz(zx.value, x) + cdef Integer zp = PY_NEW(Integer) + fmpz_get_mpz(zp.value, p) + cdef Integer zans + k = GF(zp) + try: + zans = ZZ(k(zx).sqrt(extend=False)) + except ValueError: + return 1 + fmpz_set_mpz(ans, zans.value) + return 0 + + +cdef class pAdicLazyElement_sqrt(pAdicLazyElement): + def __init__(self, parent, pAdicLazyElement x): + pAdicLazyElement.__init__(self, parent) + if parent.prime() == 2: + raise NotImplementedError + self._x = x + if x._valuation <= -maxordp: + self._valuation = -maxordp + self._maxprec = maxordp + 1 + else: + self._valuation = x._valuation >> 1 + self._maxprec = x._valuation + 2*self._parent.default_prec() + cdef int error = self._bootstrap_c() + if error & 16: + raise ValueError("not a square") + + cdef int _bootstrap_c(self): + cdef pAdicLazyElement x = self._x + while x._valuation < self._maxprec and x._precrel == 0: + error = x._next_c() + if error: + return error + if self._maxprec > maxordp and x._valuation > -maxordp: + self._maxprec = x._valuation + 2*self._parent.default_prec() + if x._valuation > -maxordp: + self._valuation = x._valuation >> 1 + if x._precrel == 0: + return 1 + + if x._valuation & 1 != 0: + return 16 + cdef fmpz_t digit + fmpz_init(digit) + if fmpz_modp_sqrt(digit, get_coeff(x._digits, 0), self.prime_pow.fprime): + return 16 + + fmpz_poly_set_coeff_fmpz(self._digits, 0, digit) + cdef parent = self._parent + cdef slong val = self._valuation + cdef Integer zd = PY_NEW(Integer) + fmpz_get_mpz(zd.value, digit) + cdef pAdicLazyElement u = pAdicLazyElement_shift(parent, self, val + 1, True) + cdef pAdicLazyElement y = pAdicLazyElement_shift(parent, x, 2*val + 2, False) + cdef pAdicLazyElement c = pAdicLazyElement_value(parent, zd*zd, shift=-2) + cdef pAdicLazyElement d = pAdicLazyElement_value(parent, 2*zd, shift=-val-2) + self._definition = (y + c - u*u) / d + self._precrel = 1 + return 0 + + cdef int _next_c(self): + cdef pAdicLazyElement x = self._x + cdef pAdicLazyElement definition = self._definition + cdef slong n = self._valuation + self._precrel + cdef int error + if definition is None: + error = self._bootstrap_c() + if error: + return error + else: + error = definition._jump_c(n+1) + if error: + return error + fmpz_poly_set_coeff_fmpz(self._digits, self._precrel, get_coeff(definition._digits, self._precrel)) + self._precrel += 1 + return 0 @@ -687,7 +788,7 @@ cdef class pAdicLazyElement_selfref(pAdicLazyElement): self._definition = definition cdef slong valsve = self._valuation cdef int error = self._jump_c(self._valuation + self._parent.default_prec()) - if error & 32: + if error & 64: self._definition = None self._valuation = valsve self._precrel = 0 @@ -708,7 +809,7 @@ cdef class pAdicLazyElement_selfref(pAdicLazyElement): if definition is None: return 2 if self._next: - return 32 + return 64 self._next = True error = definition._jump_c(self._valuation + self._precrel + 1) diff --git a/src/sage/rings/padics/padic_lazy_element_helper.c b/src/sage/rings/padics/padic_lazy_element_helper.c index f1d99801c6b..ab1e1f67961 100644 --- a/src/sage/rings/padics/padic_lazy_element_helper.c +++ b/src/sage/rings/padics/padic_lazy_element_helper.c @@ -1,5 +1,3 @@ -#include - #include "flint/flint.h" #include "flint/fmpz.h" #include "flint/fmpz_poly.h" @@ -104,7 +102,9 @@ void iadd_shifted(fmpz_poly_t poly, fmpz_poly_t summand, slong shift) for ( ; cpoly < last; cpoly++, csummand++) fmpz_set(cpoly, csummand); poly->length = len; - } else { + } + else + { cpoly = poly->coeffs + shift; last = cpoly + summand->length; for ( ; cpoly < last; cpoly++, csummand++) From ac5dbbc7dc56073ba9830eb2937064f2e91e02eb Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Wed, 6 Jan 2021 00:21:06 +0100 Subject: [PATCH 090/406] implement lift method, so print_mode='terse' now works --- src/sage/rings/padics/padic_lazy_element.pyx | 26 +++++++++++++++++--- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/src/sage/rings/padics/padic_lazy_element.pyx b/src/sage/rings/padics/padic_lazy_element.pyx index 42802291ab0..17c142aab62 100644 --- a/src/sage/rings/padics/padic_lazy_element.pyx +++ b/src/sage/rings/padics/padic_lazy_element.pyx @@ -121,6 +121,7 @@ cdef class pAdicLazyElement(pAdicGenericElement): if error & 8: raise OverflowError if error & 6: + print(error) raise PrecisionError("not enough precision") if error & 1: raise PrecisionError("cannot prove that the divisor does not vanish; try to increase the precision by hand") @@ -140,7 +141,7 @@ cdef class pAdicLazyElement(pAdicGenericElement): if n.stop is None: stop = None elif n.stop in ZZ: - start = ZZ(n.stop) + stop = ZZ(n.stop) else: raise ValueError return ExpansionIter(self, start, stop) @@ -254,18 +255,35 @@ cdef class pAdicLazyElement(pAdicGenericElement): raise PrecisionError("no lower bound on the valuation is known") return ZZ(self._precrel) - def valuation(self): + def valuation(self, secure=False): if self._is_exact_zero(): return Infinity if self._valuation <= -maxordp: raise PrecisionError("no lower bound on the valuation is known") + if secure and self._precrel == 0: + raise PrecisionError("cannot determine the valuation; try to increase the precision") return ZZ(self._valuation) def unit_part(self): if self._precrel == 0: - raise PrecisionError + raise PrecisionError("cannot determine the valuation; try to increase the precision") return self >> self._valuation + def lift(self): + if self._precrel == 0: + return ZZ(0) + cdef fmpz_t fans + fmpz_init(fans) + cdef fmpz_poly_t digits + get_slice(digits, self._digits, 0, self._precrel) + fmpz_poly_evaluate_fmpz(fans, digits, self.prime_pow.fprime) + cdef Integer ans = PY_NEW(Integer) + fmpz_get_mpz(ans.value, fans) + fmpz_clear(fans) + if self._valuation: + ans *= self._parent.prime() ** self._valuation + return ans + def __rshift__(self, s): cdef slong shift = long(s) if shift: @@ -550,7 +568,7 @@ cdef class pAdicLazyElement_mul(pAdicLazyElement): cdef int errorx = x._jump_c(n - y._valuation + 1) cdef int errory = y._jump_c(n - x._valuation + 1) - cdef int error = errorx & errory + cdef int error = errorx | errory if self._precrel == 0: self._valuation = x._valuation + y._valuation if self._valuation > n: From 4f2f8e6c2754355abcdc318c2d38b938d7b47599 Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Wed, 6 Jan 2021 09:13:42 +0100 Subject: [PATCH 091/406] explicit error codes --- src/sage/rings/padics/padic_lazy_element.pyx | 78 ++++++++------------ 1 file changed, 29 insertions(+), 49 deletions(-) diff --git a/src/sage/rings/padics/padic_lazy_element.pyx b/src/sage/rings/padics/padic_lazy_element.pyx index 17c142aab62..3ee54d72e07 100644 --- a/src/sage/rings/padics/padic_lazy_element.pyx +++ b/src/sage/rings/padics/padic_lazy_element.pyx @@ -37,6 +37,8 @@ from sage.rings.finite_rings.finite_field_constructor import GF from sage.rings.padics.pow_computer_flint cimport PowComputer_flint from sage.rings.padics.padic_generic_element cimport pAdicGenericElement from sage.rings.padics.precision_error import PrecisionError +from sage.rings.padics.padic_lazy_errors cimport * +from sage.rings.padics.padic_lazy_errors import raise_error, error_to_str cdef long maxordp = (1L << (sizeof(long) * 8 - 2)) - 1 MAXORDP = ZZ(maxordp) @@ -80,10 +82,9 @@ cdef class pAdicLazyElement(pAdicGenericElement): return error if prec < maxordp: return 0 - return 8 - + return ERROR_OVERFLOW - def jump(self, prec=None, raise_error=True): + def jump(self, prec=None, quiet=False): if prec is None: default_prec = self._parent.default_prec() error = 0 @@ -106,44 +107,29 @@ cdef class pAdicLazyElement(pAdicGenericElement): if prec < -maxordp: prec = -maxordp error = self._jump_c(prec) - if raise_error: - self._error(error) - else: + if quiet: return error - - def _error(self, error): - if error & 64: - raise RecursionError("definition looks circular") - if error & 32: - raise ZeroDivisionError("cannot divide by something indistinguishable from zero") - if error & 16: - raise ValueError("not a square") - if error & 8: - raise OverflowError - if error & 6: - print(error) - raise PrecisionError("not enough precision") - if error & 1: - raise PrecisionError("cannot prove that the divisor does not vanish; try to increase the precision by hand") + else: + raise_error(error) def expansion(self, n=None): if n is None: return ExpansionIter(self, self._valuation, self._valuation + self._precrel) if isinstance(n, slice): if n.step is not None: - raise NotImplementedError + raise NotImplementedError("step is not allowed") if n.start is None: start = 0 elif n.start in ZZ: start = ZZ(n.start) else: - raise ValueError + raise ValueError("invalid slice") if n.stop is None: stop = None elif n.stop in ZZ: stop = ZZ(n.stop) else: - raise ValueError + raise ValueError("invalid slice") return ExpansionIter(self, start, stop) else: if n not in ZZ: @@ -161,17 +147,9 @@ cdef class pAdicLazyElement(pAdicGenericElement): return ans def _repr_(self): - error = self.jump(raise_error=False) - if error & 64: - return "Error: definition looks circular" - if error & 32: - return "Error: division by something indistinguishable from zero" - if error & 16: - return "Error: not a square" - if error & 8: - return "Error: overflow" - if error & 1: - return "cannot prove that the divisor does not vanish; try to increase the precision by hand" + error = self.jump(quiet=True) + if error: + return error_to_str(error) if self._valuation <= -maxordp: return "valuation not known" return pAdicGenericElement._repr_(self) @@ -428,7 +406,7 @@ cdef class pAdicLazyElement_value(pAdicLazyElement): self._precrel = precrel if prec < maxordp: return 0 - return 8 + return ERROR_OVERFLOW cdef int _next_c(self): if (self._maxprec is not None) and (self._valuation + self._precrel >= self._maxprec): @@ -574,7 +552,7 @@ cdef class pAdicLazyElement_mul(pAdicLazyElement): if self._valuation > n: return 0 if self._valuation < n or x._precrel == 0 or y._precrel == 0: - return error | 4 + return error | ERROR_PRECISION elif error: return error @@ -650,7 +628,7 @@ cdef class pAdicLazyElement_div(pAdicLazyElement): else: self._maxprec = denom._valuation + self._parent.default_prec() cdef int error = self._bootstrap_c() - if error & 32: + if error & ERROR_DIVISION: raise ZeroDivisionError("cannot divide by something indistinguishable from zero") if error: self._valuation = -maxordp @@ -664,19 +642,21 @@ cdef class pAdicLazyElement_div(pAdicLazyElement): while denom._valuation < self._maxprec and denom._precrel == 0: error = denom._next_c() if error: - if error & 4: - error |= 32 + if error & ERROR_PRECISION: + error |= ERROR_DIVISION return error if self._maxprec > maxordp and denom._valuation > -maxordp: self._maxprec = denom._valuation + self._parent.default_prec() if denom._precrel == 0: - return 1 + return ERROR_ABANDON self._valuation = num._valuation - denom._valuation fmpz_gcdinv(gcd, self._inverse, get_coeff(denom._digits, 0), self.prime_pow.fprime) cdef pAdicLazyElement a = pAdicLazyElement_muldigit(self._parent, self, num) cdef pAdicLazyElement b = pAdicLazyElement_muldigit(self._parent, self, denom) error = b._next_c() + if error: + return ERROR_UNKNOWN b._valuation += 1 b._precrel = 0 fmpz_poly_shift_right(b._digits, b._digits, 1) @@ -713,8 +693,8 @@ cdef bint fmpz_modp_sqrt(fmpz* ans, fmpz* x, fmpz* p): cdef Integer zp = PY_NEW(Integer) fmpz_get_mpz(zp.value, p) cdef Integer zans - k = GF(zp) try: + k = GF(zp) zans = ZZ(k(zx).sqrt(extend=False)) except ValueError: return 1 @@ -735,7 +715,7 @@ cdef class pAdicLazyElement_sqrt(pAdicLazyElement): self._valuation = x._valuation >> 1 self._maxprec = x._valuation + 2*self._parent.default_prec() cdef int error = self._bootstrap_c() - if error & 16: + if error & ERROR_NOTSQUARE: raise ValueError("not a square") cdef int _bootstrap_c(self): @@ -749,14 +729,14 @@ cdef class pAdicLazyElement_sqrt(pAdicLazyElement): if x._valuation > -maxordp: self._valuation = x._valuation >> 1 if x._precrel == 0: - return 1 + return ERROR_ABANDON if x._valuation & 1 != 0: - return 16 + return ERROR_NOTSQUARE cdef fmpz_t digit fmpz_init(digit) if fmpz_modp_sqrt(digit, get_coeff(x._digits, 0), self.prime_pow.fprime): - return 16 + return ERROR_NOTSQUARE fmpz_poly_set_coeff_fmpz(self._digits, 0, digit) cdef parent = self._parent @@ -806,7 +786,7 @@ cdef class pAdicLazyElement_selfref(pAdicLazyElement): self._definition = definition cdef slong valsve = self._valuation cdef int error = self._jump_c(self._valuation + self._parent.default_prec()) - if error & 64: + if error & ERROR_CIRCULAR: self._definition = None self._valuation = valsve self._precrel = 0 @@ -825,9 +805,9 @@ cdef class pAdicLazyElement_selfref(pAdicLazyElement): cdef int error if definition is None: - return 2 + return ERROR_NOTDEFINED if self._next: - return 64 + return ERROR_CIRCULAR self._next = True error = definition._jump_c(self._valuation + self._precrel + 1) From e23dccf09ef602230d5e70ae45cbb73470853578 Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Wed, 6 Jan 2021 09:29:44 +0100 Subject: [PATCH 092/406] better with files added --- src/sage/rings/padics/padic_lazy_errors.pxd | 9 ++++ src/sage/rings/padics/padic_lazy_errors.pyx | 46 +++++++++++++++++++++ 2 files changed, 55 insertions(+) create mode 100644 src/sage/rings/padics/padic_lazy_errors.pxd create mode 100644 src/sage/rings/padics/padic_lazy_errors.pyx diff --git a/src/sage/rings/padics/padic_lazy_errors.pxd b/src/sage/rings/padics/padic_lazy_errors.pxd new file mode 100644 index 00000000000..a5bfbdeebea --- /dev/null +++ b/src/sage/rings/padics/padic_lazy_errors.pxd @@ -0,0 +1,9 @@ +cdef inline int ERROR_ABANDON +cdef inline int ERROR_NOTDEFINED +cdef inline int ERROR_PRECISION +cdef inline int ERROR_OVERFLOW +cdef inline int ERROR_NOTSQUARE +cdef inline int ERROR_DIVISION +cdef inline int ERROR_CIRCULAR + +cdef inline int ERROR_UNKNOWN diff --git a/src/sage/rings/padics/padic_lazy_errors.pyx b/src/sage/rings/padics/padic_lazy_errors.pyx new file mode 100644 index 00000000000..88a65122f23 --- /dev/null +++ b/src/sage/rings/padics/padic_lazy_errors.pyx @@ -0,0 +1,46 @@ +from sage.rings.padics.precision_error import PrecisionError + + +cdef inline int ERROR_ABANDON = 1 +cdef inline int ERROR_NOTDEFINED = 1 << 1 +cdef inline int ERROR_PRECISION = 1 << 2 +cdef inline int ERROR_OVERFLOW = 1 << 3 +cdef inline int ERROR_NOTSQUARE = 1 << 4 # maybe we should have something more generic here +cdef inline int ERROR_DIVISION = 1 << 5 +cdef inline int ERROR_CIRCULAR = 1 << 6 + +cdef inline int ERROR_UNKNOWN = 1 << 30 + + +def raise_error(error): + if error & ERROR_UNKNOWN: # should never occur + raise RuntimeError + if error & ERROR_CIRCULAR: + raise RecursionError("definition looks circular") + if error & ERROR_DIVISION: + raise ZeroDivisionError("cannot divide by something indistinguishable from zero") + if error & ERROR_NOTSQUARE: + raise ValueError("not a square") + if error & ERROR_OVERFLOW: + raise OverflowError + if error & (ERROR_PRECISION | ERROR_NOTDEFINED): + raise PrecisionError("not enough precision") + if error & ERROR_ABANDON: + raise PrecisionError("computation has been abandonned; try to increase precision by hand") + + +def error_to_str(error): + if error & ERROR_UNKNOWN: # should never occur + raise RuntimeError + if error & ERROR_CIRCULAR: + return "Error: definition looks circular" + if error & ERROR_DIVISION: + return "Error: cannot divide by something indistinguishable from zero" + if error & ERROR_NOTSQUARE: + return "Error: not a square" + if error & ERROR_OVERFLOW: + return "Error: overflow" + if error & (ERROR_PRECISION | ERROR_NOTDEFINED): + return "Error: not enough precision" + if error & ERROR_ABANDON: + return "Abandon; try to increase precision by hand" From f2ccf72e5912ea3dd49cfe69c84c830c6360aebe Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Wed, 6 Jan 2021 16:24:14 +0100 Subject: [PATCH 093/406] fix bugs --- src/sage/rings/padics/padic_lazy_element.pyx | 25 +++++++++------- .../rings/padics/padic_lazy_element_helper.c | 20 +++++-------- src/sage/rings/padics/padic_lazy_errors.pyx | 30 ++++++++++--------- 3 files changed, 37 insertions(+), 38 deletions(-) diff --git a/src/sage/rings/padics/padic_lazy_element.pyx b/src/sage/rings/padics/padic_lazy_element.pyx index 3ee54d72e07..58ee281c98c 100644 --- a/src/sage/rings/padics/padic_lazy_element.pyx +++ b/src/sage/rings/padics/padic_lazy_element.pyx @@ -66,6 +66,9 @@ cdef class pAdicLazyElement(pAdicGenericElement): self._valuation = 0 self._precrel = 0 + cpdef bint _is_base_elt(self, p) except -1: + return True + def prime(self): return self._parent.prime() @@ -86,6 +89,7 @@ cdef class pAdicLazyElement(pAdicGenericElement): def jump(self, prec=None, quiet=False): if prec is None: + permissive = True default_prec = self._parent.default_prec() error = 0 if self._valuation <= -maxordp: @@ -101,6 +105,7 @@ cdef class pAdicLazyElement(pAdicGenericElement): elif prec not in ZZ: raise ValueError("precision must be an integer") else: + permissive = False prec = ZZ(prec) if prec >= maxordp: raise OverflowError("beyond maximum precision (which is %s)" % maxordp) @@ -110,7 +115,7 @@ cdef class pAdicLazyElement(pAdicGenericElement): if quiet: return error else: - raise_error(error) + raise_error(error, permissive) def expansion(self, n=None): if n is None: @@ -148,8 +153,9 @@ cdef class pAdicLazyElement(pAdicGenericElement): def _repr_(self): error = self.jump(quiet=True) - if error: - return error_to_str(error) + s = error_to_str(error, permissive=True) + if s is not None: + return s if self._valuation <= -maxordp: return "valuation not known" return pAdicGenericElement._repr_(self) @@ -401,6 +407,9 @@ cdef class pAdicLazyElement_value(pAdicLazyElement): cdef int _jump_c(self, slong prec): if not self._finished: return pAdicLazyElement._jump_c(self, prec) + if (self._maxprec is not None) and (prec > self._maxprec): + self._precrel = self._maxprec - self._valuation + return ERROR_PRECISION cdef slong precrel = min(prec, maxordp) - self._valuation if precrel > self._precrel: self._precrel = precrel @@ -410,7 +419,7 @@ cdef class pAdicLazyElement_value(pAdicLazyElement): cdef int _next_c(self): if (self._maxprec is not None) and (self._valuation + self._precrel >= self._maxprec): - return 4 + return ERROR_PRECISION reduce_coeff(self._digits, self._precrel, self.prime_pow.fprime) if self._precrel == 0 and fmpz_is_zero(get_coeff(self._digits, 0)): self._valuation += 1 @@ -603,12 +612,6 @@ cdef class pAdicLazyElement_muldigit(pAdicLazyElement): self._precrel += 1 return 0 - def digits(self): - return fmpz_poly_get_str(self._digits) - - def next(self): - return self._next_c() - # Division @@ -791,7 +794,7 @@ cdef class pAdicLazyElement_selfref(pAdicLazyElement): self._valuation = valsve self._precrel = 0 self._next = False - raise RecursionError("definition looks circular") + raise_error(error, permissive=True) def __eq__(self, other): if self._definition is None: diff --git a/src/sage/rings/padics/padic_lazy_element_helper.c b/src/sage/rings/padics/padic_lazy_element_helper.c index ab1e1f67961..6a602174ab2 100644 --- a/src/sage/rings/padics/padic_lazy_element_helper.c +++ b/src/sage/rings/padics/padic_lazy_element_helper.c @@ -41,7 +41,7 @@ void get_slice(fmpz_poly_t slice, fmpz_poly_t poly, slong start, slong length) } -void iadd_coeff(fmpz_poly_t poly, fmpz_t summand, slong i) +void iadd_coeff(fmpz_poly_t poly, const fmpz_t summand, slong i) { fmpz *coeff; slong len = i + 1; @@ -49,12 +49,7 @@ void iadd_coeff(fmpz_poly_t poly, fmpz_t summand, slong i) return; if (poly->length < len) - { - fmpz_poly_fit_length(poly, len); - poly->length = len; - coeff = poly->coeffs + i; - fmpz_set(coeff, summand); - } + fmpz_poly_set_coeff_fmpz(poly, i, summand); else { coeff = poly->coeffs + i; @@ -63,7 +58,7 @@ void iadd_coeff(fmpz_poly_t poly, fmpz_t summand, slong i) } -void isub_coeff(fmpz_poly_t poly, fmpz_t summand, slong i) +void isub_coeff(fmpz_poly_t poly, const fmpz_t summand, slong i) { fmpz *coeff; slong len = i + 1; @@ -72,10 +67,9 @@ void isub_coeff(fmpz_poly_t poly, fmpz_t summand, slong i) if (poly->length < len) { - fmpz_poly_fit_length(poly, len); - poly->length = len; + fmpz_poly_set_coeff_fmpz(poly, i, summand); coeff = poly->coeffs + i; - fmpz_neg(coeff, summand); + fmpz_neg(coeff, coeff); } else { @@ -85,7 +79,7 @@ void isub_coeff(fmpz_poly_t poly, fmpz_t summand, slong i) } -void iadd_shifted(fmpz_poly_t poly, fmpz_poly_t summand, slong shift) +void iadd_shifted(fmpz_poly_t poly, const fmpz_poly_t summand, slong shift) { slong len = shift + summand->length; fmpz *cpoly, *last; @@ -113,7 +107,7 @@ void iadd_shifted(fmpz_poly_t poly, fmpz_poly_t summand, slong shift) } -void reduce_coeff(fmpz_poly_t poly, slong i, fmpz_t modulus) +void reduce_coeff(fmpz_poly_t poly, slong i, const fmpz_t modulus) { if (i < poly->length) { diff --git a/src/sage/rings/padics/padic_lazy_errors.pyx b/src/sage/rings/padics/padic_lazy_errors.pyx index 88a65122f23..115de0a2835 100644 --- a/src/sage/rings/padics/padic_lazy_errors.pyx +++ b/src/sage/rings/padics/padic_lazy_errors.pyx @@ -12,7 +12,7 @@ cdef inline int ERROR_CIRCULAR = 1 << 6 cdef inline int ERROR_UNKNOWN = 1 << 30 -def raise_error(error): +def raise_error(error, permissive=False): if error & ERROR_UNKNOWN: # should never occur raise RuntimeError if error & ERROR_CIRCULAR: @@ -21,15 +21,16 @@ def raise_error(error): raise ZeroDivisionError("cannot divide by something indistinguishable from zero") if error & ERROR_NOTSQUARE: raise ValueError("not a square") - if error & ERROR_OVERFLOW: - raise OverflowError - if error & (ERROR_PRECISION | ERROR_NOTDEFINED): - raise PrecisionError("not enough precision") - if error & ERROR_ABANDON: - raise PrecisionError("computation has been abandonned; try to increase precision by hand") + if not permissive: + if error & ERROR_OVERFLOW: + raise OverflowError + if error & (ERROR_PRECISION | ERROR_NOTDEFINED): + raise PrecisionError("not enough precision") + if error & ERROR_ABANDON: + raise PrecisionError("computation has been abandonned; try to increase precision by hand") -def error_to_str(error): +def error_to_str(error, permissive=False): if error & ERROR_UNKNOWN: # should never occur raise RuntimeError if error & ERROR_CIRCULAR: @@ -38,9 +39,10 @@ def error_to_str(error): return "Error: cannot divide by something indistinguishable from zero" if error & ERROR_NOTSQUARE: return "Error: not a square" - if error & ERROR_OVERFLOW: - return "Error: overflow" - if error & (ERROR_PRECISION | ERROR_NOTDEFINED): - return "Error: not enough precision" - if error & ERROR_ABANDON: - return "Abandon; try to increase precision by hand" + if not permissive: + if error & ERROR_OVERFLOW: + return "Error: overflow" + if error & (ERROR_PRECISION | ERROR_NOTDEFINED): + return "Error: not enough precision" + if error & ERROR_ABANDON: + return "Abandon; try to increase precision by hand" From 596280e920bc8f5a0252327bfa689e540dadaefe Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Wed, 6 Jan 2021 20:26:03 +0100 Subject: [PATCH 094/406] update tutorial --- src/sage/rings/padics/padic_lazy.py | 59 ++++++++++---------- src/sage/rings/padics/padic_lazy_element.pxd | 2 +- src/sage/rings/padics/padic_lazy_element.pyx | 38 +++++++------ 3 files changed, 50 insertions(+), 49 deletions(-) diff --git a/src/sage/rings/padics/padic_lazy.py b/src/sage/rings/padics/padic_lazy.py index d29d691da37..d7d9a3c8a21 100644 --- a/src/sage/rings/padics/padic_lazy.py +++ b/src/sage/rings/padics/padic_lazy.py @@ -14,14 +14,9 @@ sage: R.random_element() # random ...21013213133412431402 -By default, 20 digits of precision are computed (and printed). -If more (or less) digits are needed, one can specify it as follows:: - - sage: b = R(42/17, prec=30) - sage: b - ...104201213402432310420121340301 - -Alternatively, one can increase the precision using the method meth:`jump`:: +By default, 20 digits of precision are printed. +If more digits are needed, one can increase the precision by using the +meth:`jump`:: sage: a.jump(30) sage: a @@ -29,6 +24,8 @@ Standard operations are implemented:: + sage: b = R(42/17) + sage: a + b ...03232011214322140002 sage: a - b @@ -39,8 +36,18 @@ sage: a / b ...12442142113021233401 -We observe again that only 20 digits are computed. -If we need more, we have to create a new variable:: +We observe again that only 20 digits are printed, even if the precision +on the operands is higher:: + + sage: b.jump(30) + sage: a + ...244200244200244200244200244201 + sage: b + ...104201213402432310420121340301 + sage: a / b + ...12442142113021233401 + +If more digits are needed, we have to create a new variable:: sage: c = a / b sage: c.jump(40) @@ -54,27 +61,15 @@ sage: b ...2134024323104201213402432310420121340301 -Equality test works but equality is only checked up to the -*minimal* current precision of the elements:: - - sage: c == R(289/1764, prec=100) - True - sage: c == R(289/1764 + 5^50, prec=100) - True - - sage: c.jump(100) - sage: c == R(289/1764 + 5^50, prec=100) - False - sage: c == R(289/1764 + 5^50) - True +:: A quite interesting feature with lazy p-adics is the possibility to create (in somes cases) self-referent numbers. Here is an example. We first declare a new variable as follows:: - sage: x = R() + sage: x = R.selfref() sage: x - O(1) + ...?.0 We then write down an equation satisfied by `x`:: @@ -92,7 +87,7 @@ As a comparison, the following produces an error:: - sage: y = R() + sage: y = R.selfref() sage: y == 1 + 3*y^2 Traceback (most recent call last): ... @@ -100,21 +95,23 @@ Self-referent definitions also work with systems of equations:: - sage: u = R(); v = R(); w = R() + sage: u = R.selfref() + sage: v = R.selfref() + sage: w = R.selfref() sage: u == 1 + 2*v + 3*w^2 + 5*u*v*w True - sage: v == 2 + 4/w^3 + 10*(u + v + w)^2 + sage: v == 2 + 4*w + sqrt(1 + 5*u + 10*v + 15*w) True sage: w == 3 + 25*(u*v + v*w + u*w) True sage: u - ...21111231040212114001 + ...31203130103131131433 sage: v - ...04313122332303332234 + ...33441043031103114240 sage: w - ...30020001022124410403 + ...30212422041102444403 """ diff --git a/src/sage/rings/padics/padic_lazy_element.pxd b/src/sage/rings/padics/padic_lazy_element.pxd index 8b739e46b9b..782c3e73d40 100644 --- a/src/sage/rings/padics/padic_lazy_element.pxd +++ b/src/sage/rings/padics/padic_lazy_element.pxd @@ -24,7 +24,7 @@ cdef class pAdicLazyElement(pAdicGenericElement): cdef int _next_c(self) cdef Integer _digit(self, slong i) - cdef bint _is_equal(self, pAdicLazyElement right, slong prec) except -1 + cdef bint _is_equal(self, pAdicLazyElement right, slong prec, bint permissive) except -1 diff --git a/src/sage/rings/padics/padic_lazy_element.pyx b/src/sage/rings/padics/padic_lazy_element.pyx index 58ee281c98c..a433df6f797 100644 --- a/src/sage/rings/padics/padic_lazy_element.pyx +++ b/src/sage/rings/padics/padic_lazy_element.pyx @@ -160,15 +160,13 @@ cdef class pAdicLazyElement(pAdicGenericElement): return "valuation not known" return pAdicGenericElement._repr_(self) - cdef bint _is_equal(self, pAdicLazyElement right, slong prec) except -1: + cdef bint _is_equal(self, pAdicLazyElement right, slong prec, bint permissive) except -1: if self._valuation <= -maxordp: error = self._next_c() - if error: - self._error(error) + raise_error(error) if right._valuation <= -maxordp: error = right._next_c() - if error: - self._error(error) + raise_error(error) cdef slong i = 0 cdef slong j = self._valuation - right._valuation if j > 0: @@ -178,12 +176,14 @@ cdef class pAdicLazyElement(pAdicGenericElement): while i < prec: while self._precrel <= i: error = self._next_c() - if error: - self._error(error) + raise_error(error, permissive) + if error: # not enough precision + return True while right._precrel <= j: error = right._next_c() - if error: - self._error(error) + raise_error(error, permissive) + if error: # not enough precision + return True if not fmpz_equal(get_coeff(self._digits, i), get_coeff(right._digits, j)): return False i += 1 @@ -192,7 +192,7 @@ cdef class pAdicLazyElement(pAdicGenericElement): @coerce_binop def is_equal_at_precision(self, other, prec): - return self._is_equal(other, min(prec, maxordp)) + return self._is_equal(other, min(prec, maxordp), False) def __eq__(self, other): if not have_same_parent(self, other): @@ -201,22 +201,20 @@ cdef class pAdicLazyElement(pAdicGenericElement): cdef pAdicLazyElement right = other if self._valuation <= -maxordp: error = self._next_c() - if error: - self._error(error) + raise_error(error, permissive=True) if right._valuation <= -maxordp: error = right._next_c() - if error: - right._error(error) + raise_error(error, permissive=True) minprec = min(self.precision_absolute(), right.precision_absolute()) default_prec = self._parent.default_prec() if self.prime_pow.in_field: prec = self._valuation + default_prec - if not self._is_equal(right, max(minprec, prec)): + if not self._is_equal(right, max(minprec, prec), True): return False if self._valuation < prec: - return self._is_equal(right, max(minprec, self._valuation + default_prec)) + return self._is_equal(right, max(minprec, self._valuation + default_prec), True) else: - return self._is_equal(right, max(minprec, default_prec)) + return self._is_equal(right, max(minprec, default_prec), True) cpdef bint _is_exact_zero(self) except -1: return isinstance(self, pAdicLazyElement_zero) @@ -239,6 +237,9 @@ cdef class pAdicLazyElement(pAdicGenericElement): raise PrecisionError("no lower bound on the valuation is known") return ZZ(self._precrel) + cdef long valuation_c(self): + return self._valuation + def valuation(self, secure=False): if self._is_exact_zero(): return Infinity @@ -253,6 +254,9 @@ cdef class pAdicLazyElement(pAdicGenericElement): raise PrecisionError("cannot determine the valuation; try to increase the precision") return self >> self._valuation + cpdef val_unit(self): + return self.valuation(), self.unit_part() + def lift(self): if self._precrel == 0: return ZZ(0) From 202e160b7973b9068018ebd49c5ea488da87d3b8 Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Thu, 7 Jan 2021 00:20:32 +0100 Subject: [PATCH 095/406] square roots when p=2 --- src/sage/rings/padics/padic_lazy_element.pyx | 61 +++++++++++++------ .../rings/padics/padic_lazy_element_helper.c | 12 +++- src/sage/rings/padics/padic_lazy_errors.pyx | 11 ++++ 3 files changed, 65 insertions(+), 19 deletions(-) diff --git a/src/sage/rings/padics/padic_lazy_element.pyx b/src/sage/rings/padics/padic_lazy_element.pyx index a433df6f797..5a2d10571ce 100644 --- a/src/sage/rings/padics/padic_lazy_element.pyx +++ b/src/sage/rings/padics/padic_lazy_element.pyx @@ -8,8 +8,6 @@ # http://www.gnu.org/licenses/ # **************************************************************************** -from sage.misc.misc import walltime - from sage.libs.flint.types cimport * from sage.libs.flint.fmpz cimport * from sage.libs.flint.fmpz_poly cimport * @@ -712,8 +710,8 @@ cdef bint fmpz_modp_sqrt(fmpz* ans, fmpz* x, fmpz* p): cdef class pAdicLazyElement_sqrt(pAdicLazyElement): def __init__(self, parent, pAdicLazyElement x): pAdicLazyElement.__init__(self, parent) - if parent.prime() == 2: - raise NotImplementedError + #if parent.prime() == 2: + # raise NotImplementedError self._x = x if x._valuation <= -maxordp: self._valuation = -maxordp @@ -737,25 +735,50 @@ cdef class pAdicLazyElement_sqrt(pAdicLazyElement): self._valuation = x._valuation >> 1 if x._precrel == 0: return ERROR_ABANDON - if x._valuation & 1 != 0: return ERROR_NOTSQUARE - cdef fmpz_t digit - fmpz_init(digit) - if fmpz_modp_sqrt(digit, get_coeff(x._digits, 0), self.prime_pow.fprime): - return ERROR_NOTSQUARE - fmpz_poly_set_coeff_fmpz(self._digits, 0, digit) cdef parent = self._parent cdef slong val = self._valuation - cdef Integer zd = PY_NEW(Integer) - fmpz_get_mpz(zd.value, digit) - cdef pAdicLazyElement u = pAdicLazyElement_shift(parent, self, val + 1, True) - cdef pAdicLazyElement y = pAdicLazyElement_shift(parent, x, 2*val + 2, False) - cdef pAdicLazyElement c = pAdicLazyElement_value(parent, zd*zd, shift=-2) - cdef pAdicLazyElement d = pAdicLazyElement_value(parent, 2*zd, shift=-val-2) - self._definition = (y + c - u*u) / d - self._precrel = 1 + cdef fmpz_t digit + cdef Integer zd + cdef pAdicLazyElement u, y, c, d + + if fmpz_equal_ui(self.prime_pow.fprime, 2): + fmpz_poly_set_coeff_ui(self._digits, 0, 1) + self._precrel = 1 + if x._precrel == 1: + error = x._next_c() + if error: + return error + if not fmpz_equal_ui(get_coeff(x._digits, 1), 0): + return ERROR_NOTSQUARE + if x._precrel == 2: + error = x._next_c() + if error: + return error + if not fmpz_equal_ui(get_coeff(x._digits, 2), 0): + return ERROR_NOTSQUARE + zd = Integer(1) + u = pAdicLazyElement_shift(parent, self, val + 2, True) + y = pAdicLazyElement_shift(parent, x, val + 1, False) + c = pAdicLazyElement_value(parent, zd, shift=val-1) + d = pAdicLazyElement_shift(parent, u*u, -val-3, False) + self._definition = y + c - d + else: + fmpz_init(digit) + if fmpz_modp_sqrt(digit, get_coeff(x._digits, 0), self.prime_pow.fprime): + return ERROR_NOTSQUARE + fmpz_poly_set_coeff_fmpz(self._digits, 0, digit) + self._precrel = 1 + zd = PY_NEW(Integer) + fmpz_get_mpz(zd.value, digit) + fmpz_clear(digit) + u = pAdicLazyElement_shift(parent, self, val + 1, True) + y = pAdicLazyElement_shift(parent, x, 2*val + 2, False) + c = pAdicLazyElement_value(parent, zd*zd, shift=-2) + d = pAdicLazyElement_value(parent, 2*zd, shift=-val-2) + self._definition = (y + c - u*u) / d return 0 cdef int _next_c(self): @@ -775,6 +798,8 @@ cdef class pAdicLazyElement_sqrt(pAdicLazyElement): self._precrel += 1 return 0 + def next(self): + return self._next_c() # Self-referent definitions diff --git a/src/sage/rings/padics/padic_lazy_element_helper.c b/src/sage/rings/padics/padic_lazy_element_helper.c index 6a602174ab2..81364b7920b 100644 --- a/src/sage/rings/padics/padic_lazy_element_helper.c +++ b/src/sage/rings/padics/padic_lazy_element_helper.c @@ -1,3 +1,14 @@ +/***************************************************************************** + Copyright (C) 2021 Xavier Caruso + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + http://www.gnu.org/licenses/ + *****************************************************************************/ + + #include "flint/flint.h" #include "flint/fmpz.h" #include "flint/fmpz_poly.h" @@ -5,7 +16,6 @@ #define slong mp_limb_signed_t - fmpz* get_coeff (fmpz_poly_t poly, slong i) { static fmpz* zero; diff --git a/src/sage/rings/padics/padic_lazy_errors.pyx b/src/sage/rings/padics/padic_lazy_errors.pyx index 115de0a2835..628a1756749 100644 --- a/src/sage/rings/padics/padic_lazy_errors.pyx +++ b/src/sage/rings/padics/padic_lazy_errors.pyx @@ -1,3 +1,14 @@ +# **************************************************************************** +# Copyright (C) 2021 Xavier Caruso +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# http://www.gnu.org/licenses/ +# **************************************************************************** + + from sage.rings.padics.precision_error import PrecisionError From d45dffdd3c132d2646a6c0e0548def840bbdd01c Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Fri, 8 Jan 2021 10:05:59 +0100 Subject: [PATCH 096/406] implement Teichmuller lifts --- src/sage/rings/padics/padic_lazy_element.pxd | 12 +- src/sage/rings/padics/padic_lazy_element.pyx | 168 +++++++++++++++++-- src/sage/rings/padics/padic_lazy_errors.pxd | 3 +- src/sage/rings/padics/padic_lazy_errors.pyx | 19 ++- 4 files changed, 175 insertions(+), 27 deletions(-) diff --git a/src/sage/rings/padics/padic_lazy_element.pxd b/src/sage/rings/padics/padic_lazy_element.pxd index 782c3e73d40..d65ee981f44 100644 --- a/src/sage/rings/padics/padic_lazy_element.pxd +++ b/src/sage/rings/padics/padic_lazy_element.pxd @@ -54,11 +54,15 @@ cdef class pAdicLazyElement_add(pAdicLazyElement): cdef class pAdicLazyElement_mul(pAdicLazyElement): cdef pAdicLazyElement _x + cdef fmpz_t _lastdigit_x cdef pAdicLazyElement _y + cdef fmpz_t _lastdigit_y + cdef int _update_last_digit(self) cdef class pAdicLazyElement_muldigit(pAdicLazyElement): cdef fmpz* _x cdef pAdicLazyElement _y + cdef void _erase_first_digit(self) cdef class pAdicLazyElement_div(pAdicLazyElement): cdef slong _maxprec @@ -74,9 +78,15 @@ cdef class pAdicLazyElement_sqrt(pAdicLazyElement): cdef pAdicLazyElement _definition cdef int _bootstrap_c(self) +cdef class pAdicLazyElement_teichmuller(pAdicLazyElement): + cdef bint _trivial + cdef list _xns + cdef pAdicLazyElement _xbar + cdef pAdicLazyElement _xp + cdef int _bootstrap_c(self) + cdef class pAdicLazyElement_selfref(pAdicLazyElement): cdef pAdicLazyElement _definition cdef bint _next - cpdef set(self, pAdicLazyElement definition) diff --git a/src/sage/rings/padics/padic_lazy_element.pyx b/src/sage/rings/padics/padic_lazy_element.pyx index 5a2d10571ce..e105726eed8 100644 --- a/src/sage/rings/padics/padic_lazy_element.pyx +++ b/src/sage/rings/padics/padic_lazy_element.pyx @@ -11,6 +11,9 @@ from sage.libs.flint.types cimport * from sage.libs.flint.fmpz cimport * from sage.libs.flint.fmpz_poly cimport * +cdef extern from "sage/libs/flint/flint_wrap.h": + cdef ulong fmpz_bits(fmpz_t f) + cdef int fmpz_tstbit(fmpz_t f, ulong i) cdef extern from "sage/rings/padics/padic_lazy_element_helper.c": cdef void flint_randinit(flint_rand_t state) @@ -42,6 +45,12 @@ cdef long maxordp = (1L << (sizeof(long) * 8 - 2)) - 1 MAXORDP = ZZ(maxordp) +cdef fmpz_t tmp_coeff +cdef fmpz_poly_t tmp_poly +fmpz_init(tmp_coeff) +fmpz_poly_init(tmp_poly) + + cpdef lazy_sum(parent, summands): if not summands: return parent.zero() @@ -50,7 +59,6 @@ cpdef lazy_sum(parent, summands): return pAdicLazyElement_add(parent, summands, signs) - cdef class pAdicLazyElement(pAdicGenericElement): def __cinit__(self): fmpz_poly_init(self._digits) @@ -215,7 +223,7 @@ cdef class pAdicLazyElement(pAdicGenericElement): return self._is_equal(right, max(minprec, default_prec), True) cpdef bint _is_exact_zero(self) except -1: - return isinstance(self, pAdicLazyElement_zero) + return self._valuation >= maxordp cpdef bint _is_inexact_zero(self) except -1: return self._precrel == 0 @@ -357,6 +365,9 @@ cdef class pAdicLazyElement(pAdicGenericElement): def sqrt(self): return pAdicLazyElement_sqrt(self._parent, self) + def teichmuller(self): + return pAdicLazyElement_teichmuller(self._parent, self) + # Assignations ############## @@ -537,12 +548,15 @@ cdef class pAdicLazyElement_add(pAdicLazyElement): # Multiplication -cdef fmpz_t tmp_coeff -cdef fmpz_poly_t tmp_poly -fmpz_init(tmp_coeff) -fmpz_poly_init(tmp_poly) - cdef class pAdicLazyElement_mul(pAdicLazyElement): + def __cinit__(self): + fmpz_init(self._lastdigit_x) + fmpz_init(self._lastdigit_y) + + def __dealloc__(self): + fmpz_clear(self._lastdigit_x) + fmpz_clear(self._lastdigit_y) + def __init__(self, parent, x, y): pAdicLazyElement.__init__(self, parent) self._x = x @@ -568,10 +582,12 @@ cdef class pAdicLazyElement_mul(pAdicLazyElement): return error n = self._precrel - fmpz_mul(tmp_coeff, get_coeff(x._digits, 0), get_coeff(y._digits, n)) + fmpz_set(self._lastdigit_x, get_coeff(x._digits, n)) + fmpz_set(self._lastdigit_y, get_coeff(y._digits, n)) + fmpz_mul(tmp_coeff, get_coeff(x._digits, 0), self._lastdigit_y) iadd_coeff(self._digits, tmp_coeff, n) if n: - fmpz_mul(tmp_coeff, get_coeff(x._digits, n), get_coeff(y._digits, 0)) + fmpz_mul(tmp_coeff, self._lastdigit_x, get_coeff(y._digits, 0)) iadd_coeff(self._digits, tmp_coeff, n) cdef slong m = n + 2 @@ -590,10 +606,43 @@ cdef class pAdicLazyElement_mul(pAdicLazyElement): fmpz_poly_mul(tmp_poly, slicex, slicey) iadd_shifted(self._digits, tmp_poly, n) - reduce_coeff(self._digits, self._precrel, self.prime_pow.fprime) + reduce_coeff(self._digits, n, self.prime_pow.fprime) self._precrel += 1 return 0 + cdef int _update_last_digit(self): + if self._precrel == 0: + return ERROR_UNEXPECTED + cdef pAdicLazyElement x = self._x + cdef pAdicLazyElement y = self._y + cdef slong n = self._precrel - 1 + cdef slong m = n + 2 + cdef slong len = 2 + cdef fmpz_poly_t slice + while (m & 1 == 0) and (m > 3): + m >>= 1 + len <<= 1 + len -= 1 + + fmpz_sub(tmp_coeff, get_coeff(x._digits, n), self._lastdigit_x) + get_slice(slice, y._digits, 0, len) + fmpz_poly_scalar_mul_fmpz(tmp_poly, slice, tmp_coeff) + iadd_shifted(self._digits, tmp_poly, n) + if m == 2: + len -= 1 + fmpz_sub(tmp_coeff, get_coeff(y._digits, n), self._lastdigit_y) + get_slice(slice, x._digits, 0, len) + fmpz_poly_scalar_mul_fmpz(tmp_poly, slice, tmp_coeff) + iadd_shifted(self._digits, tmp_poly, n) + if m == 2: + fmpz_mul(tmp_coeff, tmp_coeff, self._lastdigit_x) + iadd_coeff(self._digits, tmp_coeff, 2*len) + reduce_coeff(self._digits, n, self.prime_pow.fprime) + + fmpz_set(self._lastdigit_x, get_coeff(x._digits, n)) + fmpz_set(self._lastdigit_y, get_coeff(y._digits, n)) + return 0 + cdef class pAdicLazyElement_muldigit(pAdicLazyElement): # compute x*y where x is assumed to be in (0,p) @@ -614,6 +663,14 @@ cdef class pAdicLazyElement_muldigit(pAdicLazyElement): self._precrel += 1 return 0 + cdef void _erase_first_digit(self): + # This is a awful hack, + # but it's very useful for division + if self._precrel: + self._valuation += 1 + self._precrel -= 1 + fmpz_poly_shift_right(self._digits, self._digits, 1) + # Division @@ -654,17 +711,14 @@ cdef class pAdicLazyElement_div(pAdicLazyElement): self._maxprec = denom._valuation + self._parent.default_prec() if denom._precrel == 0: return ERROR_ABANDON - self._valuation = num._valuation - denom._valuation fmpz_gcdinv(gcd, self._inverse, get_coeff(denom._digits, 0), self.prime_pow.fprime) - cdef pAdicLazyElement a = pAdicLazyElement_muldigit(self._parent, self, num) - cdef pAdicLazyElement b = pAdicLazyElement_muldigit(self._parent, self, denom) + cdef pAdicLazyElement_muldigit a = pAdicLazyElement_muldigit(self._parent, self, num) + cdef pAdicLazyElement_muldigit b = pAdicLazyElement_muldigit(self._parent, self, denom) error = b._next_c() - if error: - return ERROR_UNKNOWN - b._valuation += 1 - b._precrel = 0 - fmpz_poly_shift_right(b._digits, b._digits, 1) + if error or b._precrel != 1: + return error | ERROR_UNEXPECTED + b._erase_first_digit() self._definition = a - b * self return 0 @@ -802,6 +856,84 @@ cdef class pAdicLazyElement_sqrt(pAdicLazyElement): return self._next_c() +# Teichmüller lifts + +cdef class pAdicLazyElement_teichmuller(pAdicLazyElement): + def __init__(self, parent, pAdicLazyElement xbar): + pAdicLazyElement.__init__(self, parent) + self._xbar = xbar + self._xns = [ ] + self._trivial = False + cdef int error = self._bootstrap_c() + if error & ERROR_INTEGRAL: + raise ValueError("not in the integer ring") + + cdef int _bootstrap_c(self): + cdef int error + cdef pAdicLazyElement xbar = self._xbar + while xbar._valuation + xbar._precrel < 1: + error = xbar._next_c() + if error: + return error + if xbar._valuation < 0 and xbar._precrel > 0: + return ERROR_INTEGRAL + + cdef fmpz* digit + if xbar._valuation == 0: + digit = get_coeff(xbar._digits, 0) + fmpz_poly_set_coeff_fmpz(self._digits, 0, digit) + self._trivial = fmpz_equal_ui(digit, 1) + self._precrel += 1 + else: + self._trivial = True + self._valuation = maxordp + + cdef pAdicLazyElement xn = self + cdef slong size = fmpz_bits(self.prime_pow.fprime) + cdef slong i = size - 2 + while i >= 0: + xn = xn * xn + self._xns.append(xn) + if fmpz_tstbit(self.prime_pow.fprime, i): + xn = xn * self + self._xns.append(xn) + i -= 1 + self._xp = xn + error = self._xp._next_c() + if error or self._xp._precrel != 1: + return error | ERROR_UNEXPECTED + return 0 + + cdef int _jump_c(self, slong prec): + if self._trivial: + if self._valuation == 0 and self._precrel < prec: + self._precrel = prec + return 0 + return pAdicLazyElement._jump_c(self, prec) + + cdef int _next_c(self): + if self._trivial: + if self._valuation: + self._precrel += 1 + return 0 + cdef int error + cdef pAdicLazyElement xp = self._xp + cdef pAdicLazyElement_mul xn + if self._xp is None: + error = self._bootstrap_c() + if error: + return error + else: + self._precrel += 1 + xp._next_c() + fmpz_poly_set_coeff_fmpz(self._digits, self._precrel - 1, get_coeff(xp._digits, self._precrel - 1)) + for xn in self._xns: + error = xn._update_last_digit() + if error: + return error + return 0 + + # Self-referent definitions ########################### diff --git a/src/sage/rings/padics/padic_lazy_errors.pxd b/src/sage/rings/padics/padic_lazy_errors.pxd index a5bfbdeebea..bfe375b8eee 100644 --- a/src/sage/rings/padics/padic_lazy_errors.pxd +++ b/src/sage/rings/padics/padic_lazy_errors.pxd @@ -3,7 +3,8 @@ cdef inline int ERROR_NOTDEFINED cdef inline int ERROR_PRECISION cdef inline int ERROR_OVERFLOW cdef inline int ERROR_NOTSQUARE +cdef inline int ERROR_INTEGRAL cdef inline int ERROR_DIVISION cdef inline int ERROR_CIRCULAR -cdef inline int ERROR_UNKNOWN +cdef inline int ERROR_UNEXPECTED diff --git a/src/sage/rings/padics/padic_lazy_errors.pyx b/src/sage/rings/padics/padic_lazy_errors.pyx index 628a1756749..2a18591428b 100644 --- a/src/sage/rings/padics/padic_lazy_errors.pyx +++ b/src/sage/rings/padics/padic_lazy_errors.pyx @@ -17,19 +17,22 @@ cdef inline int ERROR_NOTDEFINED = 1 << 1 cdef inline int ERROR_PRECISION = 1 << 2 cdef inline int ERROR_OVERFLOW = 1 << 3 cdef inline int ERROR_NOTSQUARE = 1 << 4 # maybe we should have something more generic here -cdef inline int ERROR_DIVISION = 1 << 5 -cdef inline int ERROR_CIRCULAR = 1 << 6 +cdef inline int ERROR_INTEGRAL = 1 << 6 +cdef inline int ERROR_DIVISION = 1 << 7 +cdef inline int ERROR_CIRCULAR = 1 << 8 -cdef inline int ERROR_UNKNOWN = 1 << 30 +cdef inline int ERROR_UNEXPECTED = 1 << 30 def raise_error(error, permissive=False): - if error & ERROR_UNKNOWN: # should never occur - raise RuntimeError + if error & ERROR_UNEXPECTED: + raise RuntimeError("error code = %s" % error) if error & ERROR_CIRCULAR: raise RecursionError("definition looks circular") if error & ERROR_DIVISION: raise ZeroDivisionError("cannot divide by something indistinguishable from zero") + if error & ERROR_INTEGRAL: + raise ValueError("not in the ring of integers") if error & ERROR_NOTSQUARE: raise ValueError("not a square") if not permissive: @@ -42,12 +45,14 @@ def raise_error(error, permissive=False): def error_to_str(error, permissive=False): - if error & ERROR_UNKNOWN: # should never occur - raise RuntimeError + if error & ERROR_UNEXPECTED: + raise RuntimeError("error code = %s" % error) if error & ERROR_CIRCULAR: return "Error: definition looks circular" if error & ERROR_DIVISION: return "Error: cannot divide by something indistinguishable from zero" + if error & ERROR_INTEGRAL: + return "Error: not in the ring of integers" if error & ERROR_NOTSQUARE: return "Error: not a square" if not permissive: From 7e7fb0a3413c8bcbff3e49e09bd40c6cd72e3d96 Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Fri, 8 Jan 2021 12:37:57 +0100 Subject: [PATCH 097/406] change construction of Teichmuller lifts --- src/sage/rings/padics/padic_lazy.py | 6 +++ src/sage/rings/padics/padic_lazy_element.pxd | 1 - src/sage/rings/padics/padic_lazy_element.pyx | 57 ++++++-------------- 3 files changed, 23 insertions(+), 41 deletions(-) diff --git a/src/sage/rings/padics/padic_lazy.py b/src/sage/rings/padics/padic_lazy.py index d7d9a3c8a21..47306d9f46a 100644 --- a/src/sage/rings/padics/padic_lazy.py +++ b/src/sage/rings/padics/padic_lazy.py @@ -196,6 +196,9 @@ def _an_element_(self): def random_element(self): return pAdicLazyElement_random(self, True) + def teichmuller(self, x): + return pAdicLazyElement_teichmuller(self, ZZ(x)) + class pAdicFieldLazy(pAdicFieldBaseGeneric): def __init__(self, p, prec, print_mode, names): @@ -255,3 +258,6 @@ def _an_element_(self): def random_element(self, integral=False): return pAdicLazyElement_random(self, integral) + + def teichmuller(self, x): + return pAdicLazyElement_teichmuller(self, ZZ(x)) diff --git a/src/sage/rings/padics/padic_lazy_element.pxd b/src/sage/rings/padics/padic_lazy_element.pxd index d65ee981f44..300152451d9 100644 --- a/src/sage/rings/padics/padic_lazy_element.pxd +++ b/src/sage/rings/padics/padic_lazy_element.pxd @@ -83,7 +83,6 @@ cdef class pAdicLazyElement_teichmuller(pAdicLazyElement): cdef list _xns cdef pAdicLazyElement _xbar cdef pAdicLazyElement _xp - cdef int _bootstrap_c(self) cdef class pAdicLazyElement_selfref(pAdicLazyElement): diff --git a/src/sage/rings/padics/padic_lazy_element.pyx b/src/sage/rings/padics/padic_lazy_element.pyx index e105726eed8..f64654ad386 100644 --- a/src/sage/rings/padics/padic_lazy_element.pyx +++ b/src/sage/rings/padics/padic_lazy_element.pyx @@ -365,9 +365,6 @@ cdef class pAdicLazyElement(pAdicGenericElement): def sqrt(self): return pAdicLazyElement_sqrt(self._parent, self) - def teichmuller(self): - return pAdicLazyElement_teichmuller(self._parent, self) - # Assignations ############## @@ -859,38 +856,24 @@ cdef class pAdicLazyElement_sqrt(pAdicLazyElement): # Teichmüller lifts cdef class pAdicLazyElement_teichmuller(pAdicLazyElement): - def __init__(self, parent, pAdicLazyElement xbar): + def __init__(self, parent, Integer xbar): pAdicLazyElement.__init__(self, parent) - self._xbar = xbar - self._xns = [ ] - self._trivial = False - cdef int error = self._bootstrap_c() - if error & ERROR_INTEGRAL: - raise ValueError("not in the integer ring") - - cdef int _bootstrap_c(self): - cdef int error - cdef pAdicLazyElement xbar = self._xbar - while xbar._valuation + xbar._precrel < 1: - error = xbar._next_c() - if error: - return error - if xbar._valuation < 0 and xbar._precrel > 0: - return ERROR_INTEGRAL - - cdef fmpz* digit - if xbar._valuation == 0: - digit = get_coeff(xbar._digits, 0) + cdef fmpz_t digit + fmpz_init(digit) + fmpz_set_mpz(digit, xbar.value) + fmpz_mod(digit, digit, self.prime_pow.fprime) + if fmpz_equal_ui(digit, 0): + self._trivial = True + self._valuation = maxordp + else: fmpz_poly_set_coeff_fmpz(self._digits, 0, digit) self._trivial = fmpz_equal_ui(digit, 1) self._precrel += 1 - else: - self._trivial = True - self._valuation = maxordp cdef pAdicLazyElement xn = self cdef slong size = fmpz_bits(self.prime_pow.fprime) cdef slong i = size - 2 + self._xns = [ ] while i >= 0: xn = xn * xn self._xns.append(xn) @@ -901,8 +884,7 @@ cdef class pAdicLazyElement_teichmuller(pAdicLazyElement): self._xp = xn error = self._xp._next_c() if error or self._xp._precrel != 1: - return error | ERROR_UNEXPECTED - return 0 + raise_error(ERROR_UNEXPECTED) cdef int _jump_c(self, slong prec): if self._trivial: @@ -919,18 +901,13 @@ cdef class pAdicLazyElement_teichmuller(pAdicLazyElement): cdef int error cdef pAdicLazyElement xp = self._xp cdef pAdicLazyElement_mul xn - if self._xp is None: - error = self._bootstrap_c() + self._precrel += 1 + xp._next_c() + fmpz_poly_set_coeff_fmpz(self._digits, self._precrel - 1, get_coeff(xp._digits, self._precrel - 1)) + for xn in self._xns: + error = xn._update_last_digit() if error: - return error - else: - self._precrel += 1 - xp._next_c() - fmpz_poly_set_coeff_fmpz(self._digits, self._precrel - 1, get_coeff(xp._digits, self._precrel - 1)) - for xn in self._xns: - error = xn._update_last_digit() - if error: - return error + return error | ERROR_UNEXPECTED return 0 From 7e6e696d01878296a910c462fb928e171939348d Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Fri, 8 Jan 2021 14:51:31 +0100 Subject: [PATCH 098/406] implement method residue --- src/sage/rings/padics/padic_lazy.py | 8 +++++++ src/sage/rings/padics/padic_lazy_element.pyx | 24 ++++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/src/sage/rings/padics/padic_lazy.py b/src/sage/rings/padics/padic_lazy.py index 47306d9f46a..a3486566ae9 100644 --- a/src/sage/rings/padics/padic_lazy.py +++ b/src/sage/rings/padics/padic_lazy.py @@ -199,6 +199,10 @@ def random_element(self): def teichmuller(self, x): return pAdicLazyElement_teichmuller(self, ZZ(x)) + def teichmuller_system(self): + R = self.residue_class_field() + return [ self.teichmuller(ZZ(i)) for i in R if i != 0 ] + class pAdicFieldLazy(pAdicFieldBaseGeneric): def __init__(self, p, prec, print_mode, names): @@ -261,3 +265,7 @@ def random_element(self, integral=False): def teichmuller(self, x): return pAdicLazyElement_teichmuller(self, ZZ(x)) + + def teichmuller_system(self): + R = self.residue_class_field() + return [ self.teichmuller(ZZ(i)) for i in R if i != 0 ] diff --git a/src/sage/rings/padics/padic_lazy_element.pyx b/src/sage/rings/padics/padic_lazy_element.pyx index f64654ad386..d6de723848c 100644 --- a/src/sage/rings/padics/padic_lazy_element.pyx +++ b/src/sage/rings/padics/padic_lazy_element.pyx @@ -34,6 +34,7 @@ from sage.rings.all import ZZ from sage.rings.integer cimport Integer from sage.rings.infinity import Infinity from sage.rings.finite_rings.finite_field_constructor import GF +from sage.rings.finite_rings.integer_mod_ring import Integers from sage.rings.padics.pow_computer_flint cimport PowComputer_flint from sage.rings.padics.padic_generic_element cimport pAdicGenericElement @@ -263,6 +264,29 @@ cdef class pAdicLazyElement(pAdicGenericElement): cpdef val_unit(self): return self.valuation(), self.unit_part() + def residue(self, slong prec=1, field=None): + if prec >= maxordp: + raise OverflowError + if prec < 0: + raise ValueError("cannot reduce modulo a negative power of p") + error = self._jump_c(prec) + if error: + raise_error(error) + if self._valuation < 0: + raise ValueError("element must have non-negative valuation in order to compute residue") + cdef fmpz_t fans + fmpz_init(fans) + cdef fmpz_poly_t digits + get_slice(digits, self._digits, 0, prec - self._valuation) + fmpz_poly_evaluate_fmpz(fans, digits, self.prime_pow.fprime) + cdef Integer ans = PY_NEW(Integer) + fmpz_get_mpz(ans.value, fans) + fmpz_clear(fans) + if field and prec == 1: + return self._parent.residue_class_field()(ans) + else: + return Integers(self.prime()**prec)(ans) + def lift(self): if self._precrel == 0: return ZZ(0) From 17996461d34bdce6d77d1d4b57a7214f67180285 Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Mon, 11 Jan 2021 01:49:01 +0100 Subject: [PATCH 099/406] write templates --- src/sage/libs/linkages/padics/lazy/API.pxi | 73 ++ src/sage/libs/linkages/padics/lazy/flint.pxi | 154 +++ .../linkages/padics/lazy/flint_helper.c} | 0 src/sage/rings/padics/lazy_template.pxi | 985 +++++++++++++++ .../rings/padics/lazy_template_header.pxi | 80 ++ src/sage/rings/padics/padic_lazy_element.pxd | 97 +- src/sage/rings/padics/padic_lazy_element.pyx | 1053 +---------------- 7 files changed, 1334 insertions(+), 1108 deletions(-) create mode 100644 src/sage/libs/linkages/padics/lazy/API.pxi create mode 100644 src/sage/libs/linkages/padics/lazy/flint.pxi rename src/sage/{rings/padics/padic_lazy_element_helper.c => libs/linkages/padics/lazy/flint_helper.c} (100%) create mode 100644 src/sage/rings/padics/lazy_template.pxi create mode 100644 src/sage/rings/padics/lazy_template_header.pxi diff --git a/src/sage/libs/linkages/padics/lazy/API.pxi b/src/sage/libs/linkages/padics/lazy/API.pxi new file mode 100644 index 00000000000..e4b1bdb68bb --- /dev/null +++ b/src/sage/libs/linkages/padics/lazy/API.pxi @@ -0,0 +1,73 @@ +# Operations on digits (intended to be small elements in the exact subring) + +cdef inline void digit_init(cdigit a): + pass + +cdef inline void digit_clear(cdigit a): + pass + +cdef inline void digit_set(cdigit a, cdigit b): + pass + +cdef inline void digit_set_ui(cdigit a, slong b): + pass + +cdef inline bint digit_equal(cdigit a, cdigit b): + pass + +cdef inline bint digit_equal_ui(cdigit a, slong b): + pass + +cdef inline void digit_set_sage(cdigit a, elt): + pass + +cdef inline digit_get_sage(cdigit a): + pass + +cdef inline void digit_add(cdigit res, cdigit a, cdigit b): + pass + +cdef inline void digit_sub(cdigit res, cdigit a, cdigit b): + pass + +cdef inline void digit_mul(cdigit res, cdigit a, cdigit b): + pass + +cdef inline void digit_mod(cdigit res, cdigit a, cdigit modulus): + pass + +cdef inline void digit_quorem(cdigit quo, cdigit rem, cdigit a, cdigit modulus): + pass + + +# Operations on elements (represented as series of digits) + +cdef inline void element_init(cdigit a): + pass + +cdef inline void element_clear(cdigit a): + pass + +cdef inline void element_set_coeff(celement x, cdigit a, slong i): + pass + +cdef inline void cdigit_ptr element_get_coeff(celement x, slong i): + pass + +cdef inline void cdigit_ptr element_get_slice(celement x, slong start, slong length): + pass + +cdef inline void element_iadd_coeff(celement x, cdigit a, slong i): + pass + +cdef inline void element_isub_coeff(celement x, cdigit a, slong i): + pass + +cdef inline void element_iadd_slice(celement x, celement slice, slong start): + pass + +cdef inline void element_isub_slice(celement x, celement slice, slong start): + pass + +cdef inline void element_reduce_coeff(celement x, slong i): + pass diff --git a/src/sage/libs/linkages/padics/lazy/flint.pxi b/src/sage/libs/linkages/padics/lazy/flint.pxi new file mode 100644 index 00000000000..34d7f1e22c7 --- /dev/null +++ b/src/sage/libs/linkages/padics/lazy/flint.pxi @@ -0,0 +1,154 @@ +from sage.libs.flint.types cimport flint_rand_t +from sage.libs.flint.fmpz cimport * +from sage.libs.flint.fmpz_poly cimport * +cdef extern from "sage/libs/flint/flint_wrap.h": + cdef ulong fmpz_bits(fmpz_t f) + cdef int fmpz_tstbit(fmpz_t f, ulong i) + +cdef extern from "sage/libs/linkages/padics/lazy/flint_helper.c": + cdef void flint_randinit(flint_rand_t state) + cdef void flint_randclear(flint_rand_t state) + cdef fmpz* get_coeff(fmpz_poly_t poly, slong i) + cdef void get_slice(fmpz_poly_t slice, fmpz_poly_t poly, slong start, slong length) + cdef void iadd_coeff(fmpz_poly_t poly, fmpz_t summand, slong i) + cdef void isub_coeff(fmpz_poly_t poly, fmpz_t summand, slong i) + cdef void iadd_shifted(fmpz_poly_t poly, fmpz_poly_t summand, slong shift) + cdef void reduce_coeff(fmpz_poly_t poly, slong i, fmpz_t modulus) + +from sage.rings.padics.pow_computer cimport PowComputer_class +from sage.rings.padics.pow_computer_flint cimport PowComputer_flint + +from sage.ext.stdsage cimport PY_NEW + + +cdef flint_rand_t flint_randstate +flint_randinit(flint_randstate) + +# Operations on digits (intended to be small elements in the exact subring) + +cdef inline void digit_init(cdigit a): + fmpz_init(a) + +cdef inline void digit_clear(cdigit a): + fmpz_clear(a) + +cdef inline void digit_set(cdigit a, cdigit b): + fmpz_set(a, b) + +cdef inline void digit_set_ui(cdigit a, slong b): + fmpz_set_ui(a, b) + +cdef inline bint digit_equal(cdigit a, cdigit b): + return fmpz_equal(a, b) + +cdef inline bint digit_is_zero(cdigit a): + return fmpz_is_zero(a) + +cdef inline bint digit_equal_ui(cdigit a, slong b): + return fmpz_equal_ui(a, b) + +cdef inline void digit_set_sage(cdigit a, Integer elt): + fmpz_set_mpz(a, elt.value) + +cdef inline Integer digit_get_sage(cdigit a): + cdef Integer elt = PY_NEW(Integer) + fmpz_get_mpz(elt.value, a) + return elt + +cdef inline void digit_random(cdigit res, PowComputer_class prime_pow): + fmpz_randm(res, flint_randstate, (prime_pow).fprime) + +cdef inline void digit_add(cdigit res, cdigit a, cdigit b): + fmpz_add(res, a, b) + +cdef inline void digit_sub(cdigit res, cdigit a, cdigit b): + fmpz_sub(res, a, b) + +cdef inline void digit_mul(cdigit res, cdigit a, cdigit b): + fmpz_mul(res, a, b) + +cdef inline void digit_mod(cdigit res, cdigit a, PowComputer_class prime_pow): + fmpz_mod(res, a, (prime_pow).fprime) + +cdef inline void digit_quorem(cdigit quo, cdigit rem, cdigit a, PowComputer_class prime_pow): + fmpz_tdiv_qr(quo, rem, a, (prime_pow).fprime) + +cdef inline void digit_inv(cdigit res, cdigit a, PowComputer_class prime_pow): + cdef cdigit gcd + fmpz_init(gcd) + fmpz_gcdinv(gcd, res, a, (prime_pow).fprime) + fmpz_clear(gcd) + +cdef bint digit_sqrt(cdigit_ptr ans, cdigit_ptr x, PowComputer_class prime_pow): + # Need to do something better + cdef Integer zx = digit_get_sage(x) + cdef Integer zp = digit_get_sage((prime_pow).fprime) + try: + k = GF(zp) + zans = ZZ(k(zx).sqrt(extend=False)) + except ValueError: + return 1 + digit_set_sage(ans, zans) + return 0 + + +# Operations on elements (represented as series of digits) + +cdef inline void element_init(celement x): + fmpz_poly_init(x) + +cdef inline void element_clear(celement x): + fmpz_poly_clear(x) + +cdef inline void element_set(celement x, celement y): + fmpz_poly_set(x, y) + +cdef inline void element_set_coeff(celement x, cdigit a, slong i): + fmpz_poly_set_coeff_fmpz(x, i, a) + +cdef inline void element_set_coeff_ui(celement x, slong a, slong i): + fmpz_poly_set_coeff_ui(x, i, a) + +cdef inline void element_set_coeff_sage(celement x, Integer a, slong i): + fmpz_poly_set_coeff_mpz(x, i, a.value) + +cdef inline cdigit_ptr element_get_coeff(celement x, slong i): + return get_coeff(x, i) + +cdef inline Integer element_get_sage(celement x, PowComputer_class prime_pow): + cdef fmpz_t value + fmpz_init(value) + fmpz_poly_evaluate_fmpz(value, x, (prime_pow).fprime) + cdef Integer ans = digit_get_sage(value) + fmpz_clear(value) + return ans + +cdef inline Integer element_get_coeff_sage(celement x, slong i): + return digit_get_sage(get_coeff(x, i)) + +cdef inline void element_get_slice(celement res, celement x, slong start, slong length): + get_slice(res, x, start, length) + +cdef inline void element_iadd_coeff(celement x, cdigit a, slong i): + iadd_coeff(x, a, i) + +cdef inline void element_isub_coeff(celement x, cdigit a, slong i): + isub_coeff(x, a, i) + +cdef inline void element_iadd_slice(celement x, celement slice, slong start): + iadd_shifted(x, slice, start) + +cdef inline void element_isub_slice(celement x, celement slice, slong start): + raise NotImplementedError + +cdef inline void element_scalarmul(celement res, celement x, cdigit a): + fmpz_poly_scalar_mul_fmpz(res, x, a) + +cdef inline void element_mul(celement res, celement x, celement y): + fmpz_poly_mul(res, x, y) + +cdef inline void element_reduce_coeff(celement x, slong i, PowComputer_class prime_pow): + reduce_coeff(x, i, (prime_pow).fprime) + +cdef inline void element_shift_right(celement x): + fmpz_poly_shift_right(x, x, 1) diff --git a/src/sage/rings/padics/padic_lazy_element_helper.c b/src/sage/libs/linkages/padics/lazy/flint_helper.c similarity index 100% rename from src/sage/rings/padics/padic_lazy_element_helper.c rename to src/sage/libs/linkages/padics/lazy/flint_helper.c diff --git a/src/sage/rings/padics/lazy_template.pxi b/src/sage/rings/padics/lazy_template.pxi new file mode 100644 index 00000000000..f5ca3ec7569 --- /dev/null +++ b/src/sage/rings/padics/lazy_template.pxi @@ -0,0 +1,985 @@ +# **************************************************************************** +# Copyright (C) 2021 Xavier Caruso +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# http://www.gnu.org/licenses/ +# **************************************************************************** + +from sage.libs.flint.types cimport slong +from sage.libs.gmp.mpz cimport mpz_sizeinbase, mpz_tstbit + +from sage.structure.element import coerce_binop +from sage.structure.element cimport have_same_parent +from sage.structure.coerce cimport coercion_model + +from sage.rings.all import ZZ +from sage.rings.integer cimport Integer +from sage.rings.infinity import Infinity +from sage.rings.finite_rings.finite_field_constructor import GF +from sage.rings.finite_rings.integer_mod_ring import Integers + +from sage.rings.padics.pow_computer cimport PowComputer_class +from sage.rings.padics.padic_generic_element cimport pAdicGenericElement +from sage.rings.padics.precision_error import PrecisionError +from sage.rings.padics.padic_lazy_errors cimport * +from sage.rings.padics.padic_lazy_errors import raise_error, error_to_str + +cdef long maxordp = (1L << (sizeof(long) * 8 - 2)) - 1 +MAXORDP = ZZ(maxordp) + + +cdef cdigit tmp_coeff +cdef celement tmp_poly +digit_init(tmp_coeff) +element_init(tmp_poly) + + +cdef class LazyElement(pAdicGenericElement): + def __cinit__(self): + element_init(self._digits) + + def __dealloc__(self): + element_clear(self._digits) + + def __init__(self, parent): + pAdicGenericElement.__init__(self, parent) + self.prime_pow = self._parent.prime_pow + self._valuation = 0 + self._precrel = 0 + + cpdef bint _is_base_elt(self, p) except -1: + return True + + def prime(self): + return self._parent.prime() + + cdef int _next_c(self): + raise NotImplementedError("must be implemented in subclasses") + + cdef int _jump_c(self, slong prec): + cdef int error + cdef slong i, stop + prec = min(prec, maxordp) + while self._precrel + self._valuation < prec: + error = self._next_c() + if error: + return error + if prec < maxordp: + return 0 + return ERROR_OVERFLOW + + def jump(self, prec=None, quiet=False): + if prec is None: + permissive = True + default_prec = self._parent.default_prec() + error = 0 + if self._valuation <= -maxordp: + error = self._next_c() + if not error: + if self.prime_pow.in_field: + prec = self._valuation + default_prec + error = self._jump_c(prec) + if not error and self._valuation < prec: + error = self._jump_c(self._valuation + default_prec) + else: + error = self._jump_c(default_prec) + elif prec not in ZZ: + raise ValueError("precision must be an integer") + else: + permissive = False + prec = ZZ(prec) + if prec >= maxordp: + raise OverflowError("beyond maximum precision (which is %s)" % maxordp) + if prec < -maxordp: + prec = -maxordp + error = self._jump_c(prec) + if quiet: + return error + else: + raise_error(error, permissive) + + def expansion(self, n=None): + if n is None: + return ExpansionIter(self, self._valuation, self._valuation + self._precrel) + if isinstance(n, slice): + if n.step is not None: + raise NotImplementedError("step is not allowed") + if n.start is None: + start = 0 + elif n.start in ZZ: + start = ZZ(n.start) + else: + raise ValueError("invalid slice") + if n.stop is None: + stop = None + elif n.stop in ZZ: + stop = ZZ(n.stop) + else: + raise ValueError("invalid slice") + return ExpansionIter(self, start, stop) + else: + if n not in ZZ: + raise IndexError("index must be an integer") + n = ZZ(n) + if n >= maxordp: + raise OverflowError("beyond maximum precision (which is %s)" % maxordp) + return self._digit(long(n)) + + cdef Integer _digit(self, slong i): + self.jump(i+1) + cdef cdigit_ptr coeff = element_get_coeff(self._digits, i - self._valuation) + return digit_get_sage(coeff) + + def _repr_(self): + error = self.jump(quiet=True) + s = error_to_str(error, permissive=True) + if s is not None: + return s + if self._valuation <= -maxordp: + return "valuation not known" + return pAdicGenericElement._repr_(self) + + cdef bint _is_equal(self, LazyElement right, slong prec, bint permissive) except -1: + if self._valuation <= -maxordp: + error = self._next_c() + raise_error(error) + if right._valuation <= -maxordp: + error = right._next_c() + raise_error(error) + cdef slong i = 0 + cdef slong j = self._valuation - right._valuation + if j > 0: + i = -j + j = 0 + prec -= self._valuation + while i < prec: + while self._precrel <= i: + error = self._next_c() + raise_error(error, permissive) + if error: # not enough precision + return True + while right._precrel <= j: + error = right._next_c() + raise_error(error, permissive) + if error: # not enough precision + return True + if not digit_equal(element_get_coeff(self._digits, i), element_get_coeff(right._digits, j)): + return False + i += 1 + j += 1 + return True + + @coerce_binop + def is_equal_at_precision(self, other, prec): + return self._is_equal(other, min(prec, maxordp), False) + + def __eq__(self, other): + if not have_same_parent(self, other): + a, b = coercion_model.canonical_coercion(self, other) + return a == b + cdef LazyElement right = other + if self._valuation <= -maxordp: + error = self._next_c() + raise_error(error, permissive=True) + if right._valuation <= -maxordp: + error = right._next_c() + raise_error(error, permissive=True) + minprec = min(self.precision_absolute(), right.precision_absolute()) + default_prec = self._parent.default_prec() + if self.prime_pow.in_field: + prec = self._valuation + default_prec + if not self._is_equal(right, max(minprec, prec), True): + return False + if self._valuation < prec: + return self._is_equal(right, max(minprec, self._valuation + default_prec), True) + else: + return self._is_equal(right, max(minprec, default_prec), True) + + cpdef bint _is_exact_zero(self) except -1: + return self._valuation >= maxordp + + cpdef bint _is_inexact_zero(self) except -1: + return self._precrel == 0 + + def is_exact(self): + return self._is_exact_zero() + + def precision_absolute(self): + if self._is_exact_zero(): + return Infinity + if self._valuation <= -maxordp: + raise PrecisionError("no lower bound on the valuation is known") + return ZZ(self._precrel + self._valuation) + + def precision_relative(self): + if self._valuation <= -maxordp: + raise PrecisionError("no lower bound on the valuation is known") + return ZZ(self._precrel) + + cdef long valuation_c(self): + return self._valuation + + def valuation(self, secure=False): + if self._is_exact_zero(): + return Infinity + if self._valuation <= -maxordp: + raise PrecisionError("no lower bound on the valuation is known") + if secure and self._precrel == 0: + raise PrecisionError("cannot determine the valuation; try to increase the precision") + return ZZ(self._valuation) + + def unit_part(self): + if self._precrel == 0: + raise PrecisionError("cannot determine the valuation; try to increase the precision") + return self >> self._valuation + + cpdef val_unit(self): + return self.valuation(), self.unit_part() + + def residue(self, slong prec=1, field=None): + if prec >= maxordp: + raise OverflowError + if prec < 0: + raise ValueError("cannot reduce modulo a negative power of p") + error = self._jump_c(prec) + if error: + raise_error(error) + if self._valuation < 0: + raise ValueError("element must have non-negative valuation in order to compute residue") + cdef celement digits + element_get_slice(digits, self._digits, 0, prec - self._valuation) + cdef Integer ans = element_get_sage(digits, self.prime_pow) + if field and prec == 1: + return self._parent.residue_class_field()(ans) + else: + return Integers(self.prime()**prec)(ans) + + def lift(self): + if self._precrel == 0: + return ZZ(0) + cdef celement digits + element_get_slice(digits, self._digits, 0, self._precrel) + cdef Integer ans = element_get_sage(digits, self.prime_pow) + if self._valuation: + ans *= self._parent.prime() ** self._valuation + return ans + + def __rshift__(self, s): + cdef slong shift = long(s) + if shift: + return lazy_class_shift((self)._parent, self, shift, not (self)._parent.is_field()) + else: + return self + + def __lshift__(self, s): + return self.__rshift__(-s) + + cpdef _add_(self, other): + cdef list summands + cdef list signs + if isinstance(self, LazyElement_zero): + return other + if isinstance(other, LazyElement_zero): + return self + if isinstance(self, lazy_class_add): + summands = list((self)._summands) + signs = list((self)._signs) + else: + summands = [self] + signs = [True] + if isinstance(other, LazyElement_add): + summands.extend((other)._summands) + signs.extend((other)._signs) + else: + summands.append(other) + signs.append(True) + return lazy_class_add(self._parent, summands, signs) + + cpdef _sub_(self, other): + cdef list summands + cdef list signs + if isinstance(self, LazyElement_zero): + return -other + if isinstance(other, LazyElement_zero): + return self + if isinstance(self, LazyElement_add): + summands = list((self)._summands) + signs = list((self)._signs) + else: + summands = [self] + signs = [True] + if isinstance(other, LazyElement_add): + summands.extend((other)._summands) + signs.extend([ not sign for sign in (other)._signs ]) + else: + summands.append(other) + signs.append(False) + return lazy_class_add(self._parent, summands, signs) + + cpdef _neg_(self): + cdef list summands + cdef list signs + if isinstance(self, LazyElement_add): + summands = list((self)._summands) + signs = [ not sign for sign in (self)._signs ] + else: + summands = [self] + signs = [False] + return lazy_class_add(self._parent, summands, signs) + + cpdef _mul_(self, other): + if isinstance(self, LazyElement_zero) or isinstance(other, LazyElement_one): + return self + if isinstance(self, LazyElement_one) or isinstance(other, LazyElement_zero): + return other + return lazy_class_mul(self._parent, self, other) + + cpdef _div_(self, other): + if isinstance(other, LazyElement_zero): + return ZeroDivisionError("cannot divide by zero") + if isinstance(other, LazyElement_one): + return self + return lazy_class_div(self._parent.fraction_field(), self, other) + + def __invert__(self): + if isinstance(self, LazyElement_zero): + return ZeroDivisionError("cannot divide by zero") + if isinstance(self, LazyElement_one): + return self + return lazy_class_div(self._parent.fraction_field(), self._parent.one(), self) + + def sqrt(self): + return lazy_class_sqrt(self._parent, self) + + +# Assignations +############## + +# Zero + +cdef class LazyElement_zero(LazyElement): + def __init__(self, parent): + LazyElement.__init__(self, parent) + self._valuation = maxordp + + cdef int _jump_c(self, slong prec): + return 0 + + cdef int _next_c(self): + return 0 + + +# One + +cdef class LazyElement_one(LazyElement): + def __init__(self, parent): + LazyElement.__init__(self, parent) + element_set_coeff_ui(self._digits, 1, 0) + + cdef int _jump_c(self, slong prec): + if self._precrel < prec: + self._precrel = prec + return 0 + + cdef int _next_c(self): + self._precrel += 1 + return 0 + + +# Value + +cdef class LazyElement_value(LazyElement): + def __init__(self, parent, Integer value, slong shift=0, maxprec=None): + LazyElement.__init__(self, parent) + element_set_coeff_sage(self._digits, value, 0) + if maxprec is None: + self._maxprec = maxordp + else: + self._maxprec = maxprec + self._shift = self._valuation = shift + self._finished = False + + cdef int _jump_c(self, slong prec): + if not self._finished: + return LazyElement._jump_c(self, prec) + if (self._maxprec is not None) and (prec > self._maxprec): + self._precrel = self._maxprec - self._valuation + return ERROR_PRECISION + cdef slong precrel = min(prec, maxordp) - self._valuation + if precrel > self._precrel: + self._precrel = precrel + if prec < maxordp: + return 0 + return ERROR_OVERFLOW + + cdef int _next_c(self): + if (self._maxprec is not None) and (self._valuation + self._precrel >= self._maxprec): + return ERROR_PRECISION + element_reduce_coeff(self._digits, self._precrel, self.prime_pow) + if self._precrel == 0 and digit_is_zero(element_get_coeff(self._digits, 0)): + self._valuation += 1 + element_shift_right(self._digits) + else: + self._precrel += 1 + self._finished = digit_is_zero(element_get_coeff(self._digits, self._precrel)) + return 0 + + +# Random + +cdef class LazyElement_random(LazyElement): + def __init__(self, parent, integral): + LazyElement.__init__(self, parent) + if not integral: + self._valuation = ZZ.random_element() + + cdef int _next_c(self): + cdef cdigit r + digit_random(r, self.prime_pow) + if self._precrel == 0 and digit_is_zero(r): + self._valuation += 1 + digit_clear(r) + else: + element_set_coeff(self._digits, r, self._precrel) + self._precrel += 1 + return 0 + + +# Operations +############ + +# Shift + +cdef class LazyElement_shift(LazyElement): + def __init__(self, parent, LazyElement x, slong s, bint truncate): + cdef slong start = 0 + cdef celement digits; + LazyElement.__init__(self, parent) + self._x = x + self._shift = s + if truncate and x._valuation < s: + start = s - x._valuation + self._valuation = 0 + self._precrel = x._precrel - start + else: + self._valuation = x._valuation - s + self._precrel = x._precrel + if self._precrel < 0: + self._precrel = 0 + else: + element_get_slice(digits, x._digits, start, self._precrel) + element_set(self._digits, digits) + + cdef int _next_c(self): + cdef slong n = self._valuation + self._precrel + cdef LazyElement x = self._x + cdef cdigit_ptr digit + cdef int error + + error = x._jump_c(n + self._shift + 1) + if error: + return error + digit = element_get_coeff(x._digits, n + self._shift - x._valuation) + element_set_coeff(self._digits, digit, self._precrel) + if self._precrel == 0 and digit_is_zero(element_get_coeff(self._digits, 0)): + self._valuation += 1 + element_shift_right(self._digits) + else: + self._precrel += 1 + return 0 + + +# Addition + +cdef class LazyElement_add(LazyElement): + def __init__(self, parent, summands, signs): + LazyElement.__init__(self, parent) + self._valuation = min((summand)._valuation for summand in summands) + self._summands = summands + self._signs = signs + + cdef int _next_c(self): + cdef LazyElement summand + cdef slong n = self._valuation + self._precrel + cdef cdigit_ptr coeff + cdef int error + + for summand in self._summands: + error = summand._jump_c(n+1) + if error: + return error + cdef slong i + for i in range(len(self._summands)): + summand = self._summands[i] + coeff = element_get_coeff(summand._digits, n - summand._valuation) + if self._signs[i]: + element_iadd_coeff(self._digits, coeff, n - self._valuation) + else: + element_isub_coeff(self._digits, coeff, n - self._valuation) + element_reduce_coeff(self._digits, self._precrel, self.prime_pow) + if self._precrel == 0 and digit_is_zero(element_get_coeff(self._digits, 0)): + self._valuation += 1 + element_shift_right(self._digits) + else: + self._precrel += 1 + return 0 + + + +# Multiplication + +cdef class LazyElement_mul(LazyElement): + def __cinit__(self): + digit_init(self._lastdigit_x) + digit_init(self._lastdigit_y) + + def __dealloc__(self): + digit_clear(self._lastdigit_x) + digit_clear(self._lastdigit_y) + + def __init__(self, parent, x, y): + LazyElement.__init__(self, parent) + self._x = x + self._y = y + self._valuation = self._x._valuation + self._y._valuation + + cdef int _next_c(self): + global tmp_coeff, tmp_poly + cdef LazyElement x = self._x + cdef LazyElement y = self._y + cdef slong n = self._valuation + self._precrel + + cdef int errorx = x._jump_c(n - y._valuation + 1) + cdef int errory = y._jump_c(n - x._valuation + 1) + cdef int error = errorx | errory + if self._precrel == 0: + self._valuation = x._valuation + y._valuation + if self._valuation > n: + return 0 + if self._valuation < n or x._precrel == 0 or y._precrel == 0: + return error | ERROR_PRECISION + elif error: + return error + + n = self._precrel + digit_set(self._lastdigit_x, element_get_coeff(x._digits, n)) + digit_set(self._lastdigit_y, element_get_coeff(y._digits, n)) + digit_mul(tmp_coeff, element_get_coeff(x._digits, 0), self._lastdigit_y) + element_iadd_coeff(self._digits, tmp_coeff, n) + if n: + digit_mul(tmp_coeff, self._lastdigit_x, element_get_coeff(y._digits, 0)) + element_iadd_coeff(self._digits, tmp_coeff, n) + + cdef slong m = n + 2 + cdef slong len = 1 + cdef celement slicex, slicey + while (m & 1 == 0) and (m > 3): + m >>= 1 + len <<= 1 + element_get_slice(slicex, x._digits, len - 1, len) + element_get_slice(slicey, y._digits, (m-1)*len - 1, len) + element_mul(tmp_poly, slicex, slicey) + element_iadd_slice(self._digits, tmp_poly, n) + if m > 2: + element_get_slice(slicex, x._digits, (m-1)*len - 1, len) + element_get_slice(slicey, y._digits, len - 1, len) + element_mul(tmp_poly, slicex, slicey) + element_iadd_slice(self._digits, tmp_poly, n) + + element_reduce_coeff(self._digits, n, self.prime_pow) + self._precrel += 1 + return 0 + + cdef int _update_last_digit(self): + if self._precrel == 0: + return ERROR_UNEXPECTED + cdef LazyElement x = self._x + cdef LazyElement y = self._y + cdef slong n = self._precrel - 1 + cdef slong m = n + 2 + cdef slong len = 2 + cdef celement slice + while (m & 1 == 0) and (m > 3): + m >>= 1 + len <<= 1 + len -= 1 + + digit_sub(tmp_coeff, element_get_coeff(x._digits, n), self._lastdigit_x) + element_get_slice(slice, y._digits, 0, len) + element_scalarmul(tmp_poly, slice, tmp_coeff) + element_iadd_slice(self._digits, tmp_poly, n) + if m == 2: + len -= 1 + digit_sub(tmp_coeff, element_get_coeff(y._digits, n), self._lastdigit_y) + element_get_slice(slice, x._digits, 0, len) + element_scalarmul(tmp_poly, slice, tmp_coeff) + element_iadd_slice(self._digits, tmp_poly, n) + if m == 2: + digit_mul(tmp_coeff, tmp_coeff, self._lastdigit_x) + element_iadd_coeff(self._digits, tmp_coeff, 2*len) + element_reduce_coeff(self._digits, n, self.prime_pow) + + digit_set(self._lastdigit_x, element_get_coeff(x._digits, n)) + digit_set(self._lastdigit_y, element_get_coeff(y._digits, n)) + return 0 + + +cdef class LazyElement_muldigit(LazyElement): + # compute x*y where x is assumed to be in (0,p) + def __init__(self, parent, LazyElement_div x, LazyElement y): + LazyElement.__init__(self, parent) + self._x = x._inverse + self._y = y + self._valuation = y._valuation + + cdef int _next_c(self): + cdef slong n = self._valuation + self._precrel + cdef int error = self._y._jump_c(n+1) + if error: + return error + digit_mul(tmp_coeff, self._x, element_get_coeff(self._y._digits, n - self._y._valuation)) + element_iadd_coeff(self._digits, tmp_coeff, self._precrel) + element_reduce_coeff(self._digits, self._precrel, self.prime_pow) + self._precrel += 1 + return 0 + + cdef void _erase_first_digit(self): + # This is a awful hack, + # but it's very useful for division + if self._precrel: + self._valuation += 1 + self._precrel -= 1 + element_shift_right(self._digits) + + +# Division + +cdef class LazyElement_div(LazyElement): + def __cinit__(self): + digit_init(self._inverse) + + def __dealloc__(self): + digit_clear(self._inverse) + + def __init__(self, parent, LazyElement num, LazyElement denom): + LazyElement.__init__(self, parent) + self._num = num + self._denom = denom + if denom._valuation <= -maxordp: + self._maxprec = maxordp + 1 + else: + self._maxprec = denom._valuation + self._parent.default_prec() + cdef int error = self._bootstrap_c() + if error & ERROR_DIVISION: + raise ZeroDivisionError("cannot divide by something indistinguishable from zero") + if error: + self._valuation = -maxordp + + cdef int _bootstrap_c(self): + cdef int error + cdef LazyElement num = self._num + cdef LazyElement denom = self._denom + cdef cdigit gcd + + while denom._valuation < self._maxprec and denom._precrel == 0: + error = denom._next_c() + if error: + if error & ERROR_PRECISION: + error |= ERROR_DIVISION + return error + if self._maxprec > maxordp and denom._valuation > -maxordp: + self._maxprec = denom._valuation + self._parent.default_prec() + if denom._precrel == 0: + return ERROR_ABANDON + self._valuation = num._valuation - denom._valuation + digit_inv(self._inverse, element_get_coeff(denom._digits, 0), self.prime_pow) + cdef LazyElement_muldigit a = lazy_class_muldigit(self._parent, self, num) + cdef LazyElement_muldigit b = lazy_class_muldigit(self._parent, self, denom) + error = b._next_c() + if error or b._precrel != 1: + return error | ERROR_UNEXPECTED + b._erase_first_digit() + self._definition = a - b * self + return 0 + + cdef int _next_c(self): + cdef LazyElement definition = self._definition + cdef slong val + if definition is None: + error = self._bootstrap_c() + if error: + return error + else: + val = self._valuation + self._denom._valuation + error = definition._jump_c(val + self._precrel + 1) + if error: + return error + if definition._valuation > val: + self._valuation = definition._valuation - self._denom._valuation + else: + digit = element_get_coeff(definition._digits, self._precrel) + element_set_coeff(self._digits, digit, self._precrel) + self._precrel += 1 + return 0 + + +# Square root + +cdef class LazyElement_sqrt(LazyElement): + def __init__(self, parent, LazyElement x): + LazyElement.__init__(self, parent) + #if parent.prime() == 2: + # raise NotImplementedError + self._x = x + if x._valuation <= -maxordp: + self._valuation = -maxordp + self._maxprec = maxordp + 1 + else: + self._valuation = x._valuation >> 1 + self._maxprec = x._valuation + 2*self._parent.default_prec() + cdef int error = self._bootstrap_c() + if error & ERROR_NOTSQUARE: + raise ValueError("not a square") + + cdef int _bootstrap_c(self): + cdef LazyElement x = self._x + while x._valuation < self._maxprec and x._precrel == 0: + error = x._next_c() + if error: + return error + if self._maxprec > maxordp and x._valuation > -maxordp: + self._maxprec = x._valuation + 2*self._parent.default_prec() + if x._valuation > -maxordp: + self._valuation = x._valuation >> 1 + if x._precrel == 0: + return ERROR_ABANDON + if x._valuation & 1 != 0: + return ERROR_NOTSQUARE + + cdef parent = self._parent + cdef slong val = self._valuation + cdef cdigit digit + cdef Integer zd, p = self.prime_pow.prime + cdef LazyElement u, y, c, d + + if p == 2: + element_set_coeff_ui(self._digits, 1, 0) + self._precrel = 1 + if x._precrel == 1: + error = x._next_c() + if error: + return error + if not digit_equal_ui(element_get_coeff(x._digits, 1), 0): + return ERROR_NOTSQUARE + if x._precrel == 2: + error = x._next_c() + if error: + return error + if not digit_equal_ui(element_get_coeff(x._digits, 2), 0): + return ERROR_NOTSQUARE + zd = Integer(1) + u = lazy_class_shift(parent, self, val + 2, True) + y = lazy_class_shift(parent, x, val + 1, False) + c = lazy_class_value(parent, zd, shift=val-1) + d = lazy_class_shift(parent, u*u, -val-3, False) + self._definition = y + c - d + else: + digit_init(digit) + if digit_sqrt(digit, element_get_coeff(x._digits, 0), self.prime_pow): + digit_clear(digit) + return ERROR_NOTSQUARE + element_set_coeff(self._digits, digit, 0) + self._precrel = 1 + zd = digit_get_sage(digit) + u = lazy_class_shift(parent, self, val + 1, True) + y = lazy_class_shift(parent, x, 2*val + 2, False) + c = lazy_class_value(parent, zd*zd, shift=-2) + d = lazy_class_value(parent, 2*zd, shift=-val-2) + self._definition = (y + c - u*u) / d + return 0 + + cdef int _next_c(self): + cdef LazyElement x = self._x + cdef LazyElement definition = self._definition + cdef slong n = self._valuation + self._precrel + cdef int error + if definition is None: + error = self._bootstrap_c() + if error: + return error + else: + error = definition._jump_c(n+1) + if error: + return error + element_set_coeff(self._digits, element_get_coeff(definition._digits, self._precrel), self._precrel) + self._precrel += 1 + return 0 + + def next(self): + return self._next_c() + + +# Teichmüller lifts + +cdef class LazyElement_teichmuller(LazyElement): + def __init__(self, parent, Integer xbar): + LazyElement.__init__(self, parent) + cdef cdigit digit + digit_init(digit) + digit_set_sage(digit, xbar) + digit_mod(digit, digit, self.prime_pow) + if digit_equal_ui(digit, 0): + digit_clear(digit) + self._trivial = True + self._valuation = maxordp + return + element_set_coeff(self._digits, digit, 0) + self._trivial = digit_equal_ui(digit, 1) + self._precrel += 1 + + cdef LazyElement xn = self + cdef Integer p = self.prime_pow.prime + cdef int size = mpz_sizeinbase(p.value, 2) + cdef int i = size - 2 + self._xns = [ ] + while i >= 0: + xn = xn * xn + self._xns.append(xn) + if mpz_tstbit(p.value, i): + xn = xn * self + self._xns.append(xn) + i -= 1 + self._xp = xn + error = self._xp._next_c() + if error or self._xp._precrel != 1: + raise_error(ERROR_UNEXPECTED) + + cdef int _jump_c(self, slong prec): + if self._trivial: + if self._valuation == 0 and self._precrel < prec: + self._precrel = prec + return 0 + return LazyElement._jump_c(self, prec) + + cdef int _next_c(self): + if self._trivial: + if self._valuation: + self._precrel += 1 + return 0 + cdef int error + cdef LazyElement xp = self._xp + cdef LazyElement_mul xn + self._precrel += 1 + xp._next_c() + element_set_coeff(self._digits, element_get_coeff(xp._digits, self._precrel - 1), self._precrel - 1) + for xn in self._xns: + error = xn._update_last_digit() + if error: + return error | ERROR_UNEXPECTED + return 0 + + +# Self-referent definitions +########################### + +cdef class LazyElement_selfref(LazyElement): + def __init__(self, parent, valuation): + LazyElement.__init__(self, parent) + self._valuation = valuation + self._definition = None + self._next = False + + cpdef set(self, LazyElement definition): + if self._definition is not None: + raise ValueError("this self-referent number is already defined") + self._definition = definition + cdef slong valsve = self._valuation + cdef int error = self._jump_c(self._valuation + self._parent.default_prec()) + if error & ERROR_CIRCULAR: + self._definition = None + self._valuation = valsve + self._precrel = 0 + self._next = False + raise_error(error, permissive=True) + + def __eq__(self, other): + if self._definition is None: + self.set(other) + return LazyElement.__eq__(self, other) + + cdef int _next_c(self): + cdef LazyElement definition = self._definition + cdef cdigit_ptr digit + cdef slong diffval + cdef int error + + if definition is None: + return ERROR_NOTDEFINED + if self._next: + return ERROR_CIRCULAR + + self._next = True + error = definition._jump_c(self._valuation + self._precrel + 1) + if not error: + diffval = self._valuation - definition._valuation + if diffval < 0: + self._valuation = definition._valuation + else: + digit = element_get_coeff(definition._digits, self._precrel + diffval) + if self._precrel == 0 and digit_is_zero(digit): + self._valuation += 1 + else: + element_set_coeff(self._digits, digit, self._precrel) + self._precrel += 1 + self._next = False + return error + + +# Expansion +########### + +cdef class ExpansionIter(object): + cdef LazyElement elt + cdef slong start + cdef slong stop + cdef slong current + + def __init__(self, LazyElement elt, start, stop): + self.elt = elt + if start is None: + self.start = 0 + else: + if start >= maxordp: + raise OverflowError("beyond maximum precision (which is %s)" % maxordp) + self.start = long(start) + if stop is None: + self.stop = self.start - 1 + else: + if stop >= maxordp: + raise OverflowError("beyond maximum precision (which is %s)" % maxordp) + self.stop = long(stop) + if self.stop <= self.start: + self.stop = self.start + self.current = self.start + + def __repr__(self): + return "%s-adic expansion of %s" % (self.elt._parent.prime(), self.elt) + + def __len__(self): + if self.stop < self.start: + raise NotImplementedError("infinite sequence") + return ZZ(self.stop - self.start) + + def __iter__(self): + return self + + def __next__(self): + if self.stop < self.start or self.current < self.stop: + digit = self.elt._digit(self.current) + self.current += 1 + return digit + else: + raise StopIteration diff --git a/src/sage/rings/padics/lazy_template_header.pxi b/src/sage/rings/padics/lazy_template_header.pxi new file mode 100644 index 00000000000..4cd691436bb --- /dev/null +++ b/src/sage/rings/padics/lazy_template_header.pxi @@ -0,0 +1,80 @@ +from sage.libs.flint.types cimport slong + +from sage.rings.integer cimport Integer +from sage.rings.padics.pow_computer cimport PowComputer_class +from sage.rings.padics.padic_generic_element cimport pAdicGenericElement + + +cdef class LazyElement(pAdicGenericElement): + cdef celement _digits + cdef slong _valuation + cdef slong _precrel + cdef PowComputer_class prime_pow + + cdef int _jump_c(self, slong prec) + cdef int _next_c(self) + cdef Integer _digit(self, slong i) + cdef bint _is_equal(self, LazyElement right, slong prec, bint permissive) except -1 + + +cdef class LazyElement_zero(LazyElement): + pass + +cdef class LazyElement_one(LazyElement): + pass + +cdef class LazyElement_value(LazyElement): + cdef slong _maxprec + cdef slong _shift + cdef bint _finished + +cdef class LazyElement_random(LazyElement): + pass + + + +cdef class LazyElement_shift(LazyElement): + cdef LazyElement _x + cdef slong _shift + +cdef class LazyElement_add(LazyElement): + cdef list _summands + cdef list _signs + +cdef class LazyElement_mul(LazyElement): + cdef LazyElement _x + cdef cdigit _lastdigit_x + cdef LazyElement _y + cdef cdigit _lastdigit_y + cdef int _update_last_digit(self) + +cdef class LazyElement_muldigit(LazyElement): + cdef cdigit_ptr _x + cdef LazyElement _y + cdef void _erase_first_digit(self) + +cdef class LazyElement_div(LazyElement): + cdef slong _maxprec + cdef cdigit _inverse + cdef LazyElement _num + cdef LazyElement _denom + cdef LazyElement _definition + cdef int _bootstrap_c(self) + +cdef class LazyElement_sqrt(LazyElement): + cdef slong _maxprec + cdef LazyElement _x + cdef LazyElement _definition + cdef int _bootstrap_c(self) + +cdef class LazyElement_teichmuller(LazyElement): + cdef bint _trivial + cdef list _xns + cdef LazyElement _xbar + cdef LazyElement _xp + + +cdef class LazyElement_selfref(LazyElement): + cdef LazyElement _definition + cdef bint _next + cpdef set(self, LazyElement definition) diff --git a/src/sage/rings/padics/padic_lazy_element.pxd b/src/sage/rings/padics/padic_lazy_element.pxd index 300152451d9..fbf1a90041b 100644 --- a/src/sage/rings/padics/padic_lazy_element.pxd +++ b/src/sage/rings/padics/padic_lazy_element.pxd @@ -1,91 +1,48 @@ -from sage.libs.flint.types cimport slong -from sage.libs.flint.types cimport flint_rand_t from sage.libs.flint.types cimport fmpz, fmpz_t, fmpz_poly_t -from sage.rings.integer cimport Integer -from sage.rings.padics.pow_computer_flint cimport PowComputer_flint -from sage.rings.padics.padic_generic_element cimport pAdicGenericElement +ctypedef fmpz_t cdigit +ctypedef fmpz* cdigit_ptr +ctypedef fmpz_poly_t celement +include "lazy_template_header.pxi" -cpdef lazy_sum(parent, summands) +cdef class pAdicLazyElement(LazyElement): + pass -cdef class pAdicLazyElement(pAdicGenericElement): - cdef PowComputer_flint prime_pow - - cdef fmpz_poly_t _digits - cdef slong _valuation - cdef slong _precrel - - #cdef pAdicLazyElement _new_c(self, type cls) +cdef class pAdicLazyElement_zero(LazyElement_zero): + pass - cdef int _jump_c(self, slong prec) - cdef int _next_c(self) - cdef Integer _digit(self, slong i) +cdef class pAdicLazyElement_one(LazyElement_one): + pass - cdef bint _is_equal(self, pAdicLazyElement right, slong prec, bint permissive) except -1 +cdef class pAdicLazyElement_value(LazyElement_value): + pass +cdef class pAdicLazyElement_random(LazyElement_random): + pass +cdef class pAdicLazyElement_shift(LazyElement_shift): + pass -cdef class pAdicLazyElement_zero(pAdicLazyElement): +cdef class pAdicLazyElement_add(LazyElement_add): pass -cdef class pAdicLazyElement_one(pAdicLazyElement): +cdef class pAdicLazyElement_mul(LazyElement_mul): pass -cdef class pAdicLazyElement_value(pAdicLazyElement): - cdef slong _maxprec - cdef slong _shift - cdef bint _finished +cdef class pAdicLazyElement_muldigit(LazyElement_muldigit): + pass -cdef class pAdicLazyElement_random(pAdicLazyElement): +cdef class pAdicLazyElement_div(LazyElement_div): pass +cdef class pAdicLazyElement_sqrt(LazyElement_sqrt): + pass +cdef class pAdicLazyElement_teichmuller(LazyElement_teichmuller): + pass -cdef class pAdicLazyElement_shift(pAdicLazyElement): - cdef pAdicLazyElement _x - cdef slong _shift - -cdef class pAdicLazyElement_add(pAdicLazyElement): - cdef list _summands - cdef list _signs - -cdef class pAdicLazyElement_mul(pAdicLazyElement): - cdef pAdicLazyElement _x - cdef fmpz_t _lastdigit_x - cdef pAdicLazyElement _y - cdef fmpz_t _lastdigit_y - cdef int _update_last_digit(self) - -cdef class pAdicLazyElement_muldigit(pAdicLazyElement): - cdef fmpz* _x - cdef pAdicLazyElement _y - cdef void _erase_first_digit(self) - -cdef class pAdicLazyElement_div(pAdicLazyElement): - cdef slong _maxprec - cdef fmpz_t _inverse - cdef pAdicLazyElement _num - cdef pAdicLazyElement _denom - cdef pAdicLazyElement _definition - cdef int _bootstrap_c(self) - -cdef class pAdicLazyElement_sqrt(pAdicLazyElement): - cdef slong _maxprec - cdef pAdicLazyElement _x - cdef pAdicLazyElement _definition - cdef int _bootstrap_c(self) - -cdef class pAdicLazyElement_teichmuller(pAdicLazyElement): - cdef bint _trivial - cdef list _xns - cdef pAdicLazyElement _xbar - cdef pAdicLazyElement _xp - - -cdef class pAdicLazyElement_selfref(pAdicLazyElement): - cdef pAdicLazyElement _definition - cdef bint _next - cpdef set(self, pAdicLazyElement definition) +cdef class pAdicLazyElement_selfref(LazyElement_selfref): + pass diff --git a/src/sage/rings/padics/padic_lazy_element.pyx b/src/sage/rings/padics/padic_lazy_element.pyx index d6de723848c..582819efd32 100644 --- a/src/sage/rings/padics/padic_lazy_element.pyx +++ b/src/sage/rings/padics/padic_lazy_element.pyx @@ -1,1038 +1,15 @@ -# **************************************************************************** -# Copyright (C) 2021 Xavier Caruso -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 2 of the License, or -# (at your option) any later version. -# http://www.gnu.org/licenses/ -# **************************************************************************** - -from sage.libs.flint.types cimport * -from sage.libs.flint.fmpz cimport * -from sage.libs.flint.fmpz_poly cimport * -cdef extern from "sage/libs/flint/flint_wrap.h": - cdef ulong fmpz_bits(fmpz_t f) - cdef int fmpz_tstbit(fmpz_t f, ulong i) - -cdef extern from "sage/rings/padics/padic_lazy_element_helper.c": - cdef void flint_randinit(flint_rand_t state) - cdef void flint_randclear(flint_rand_t state) - cdef fmpz* get_coeff(fmpz_poly_t poly, slong i) - cdef void get_slice(fmpz_poly_t slice, fmpz_poly_t poly, slong start, slong length) - cdef void iadd_coeff(fmpz_poly_t poly, fmpz_t summand, slong i) - cdef void isub_coeff(fmpz_poly_t poly, fmpz_t summand, slong i) - cdef void iadd_shifted(fmpz_poly_t poly, fmpz_poly_t summand, slong shift) - cdef void reduce_coeff(fmpz_poly_t poly, slong i, fmpz_t modulus) - -from sage.ext.stdsage cimport PY_NEW -from sage.structure.element import coerce_binop -from sage.structure.element cimport have_same_parent -from sage.structure.coerce cimport coercion_model - -from sage.rings.all import ZZ -from sage.rings.integer cimport Integer -from sage.rings.infinity import Infinity -from sage.rings.finite_rings.finite_field_constructor import GF -from sage.rings.finite_rings.integer_mod_ring import Integers - -from sage.rings.padics.pow_computer_flint cimport PowComputer_flint -from sage.rings.padics.padic_generic_element cimport pAdicGenericElement -from sage.rings.padics.precision_error import PrecisionError -from sage.rings.padics.padic_lazy_errors cimport * -from sage.rings.padics.padic_lazy_errors import raise_error, error_to_str - -cdef long maxordp = (1L << (sizeof(long) * 8 - 2)) - 1 -MAXORDP = ZZ(maxordp) - - -cdef fmpz_t tmp_coeff -cdef fmpz_poly_t tmp_poly -fmpz_init(tmp_coeff) -fmpz_poly_init(tmp_poly) - - -cpdef lazy_sum(parent, summands): - if not summands: - return parent.zero() - n = len(summands) - signs = n * [True] - return pAdicLazyElement_add(parent, summands, signs) - - -cdef class pAdicLazyElement(pAdicGenericElement): - def __cinit__(self): - fmpz_poly_init(self._digits) - - def __dealloc__(self): - fmpz_poly_clear(self._digits) - - def __init__(self, parent): - pAdicGenericElement.__init__(self, parent) - self.prime_pow = self._parent.prime_pow - self._valuation = 0 - self._precrel = 0 - - cpdef bint _is_base_elt(self, p) except -1: - return True - - def prime(self): - return self._parent.prime() - - cdef int _next_c(self): - raise NotImplementedError("must be implemented in subclasses") - - cdef int _jump_c(self, slong prec): - cdef int error - cdef slong i, stop - prec = min(prec, maxordp) - while self._precrel + self._valuation < prec: - error = self._next_c() - if error: - return error - if prec < maxordp: - return 0 - return ERROR_OVERFLOW - - def jump(self, prec=None, quiet=False): - if prec is None: - permissive = True - default_prec = self._parent.default_prec() - error = 0 - if self._valuation <= -maxordp: - error = self._next_c() - if not error: - if self.prime_pow.in_field: - prec = self._valuation + default_prec - error = self._jump_c(prec) - if not error and self._valuation < prec: - error = self._jump_c(self._valuation + default_prec) - else: - error = self._jump_c(default_prec) - elif prec not in ZZ: - raise ValueError("precision must be an integer") - else: - permissive = False - prec = ZZ(prec) - if prec >= maxordp: - raise OverflowError("beyond maximum precision (which is %s)" % maxordp) - if prec < -maxordp: - prec = -maxordp - error = self._jump_c(prec) - if quiet: - return error - else: - raise_error(error, permissive) - - def expansion(self, n=None): - if n is None: - return ExpansionIter(self, self._valuation, self._valuation + self._precrel) - if isinstance(n, slice): - if n.step is not None: - raise NotImplementedError("step is not allowed") - if n.start is None: - start = 0 - elif n.start in ZZ: - start = ZZ(n.start) - else: - raise ValueError("invalid slice") - if n.stop is None: - stop = None - elif n.stop in ZZ: - stop = ZZ(n.stop) - else: - raise ValueError("invalid slice") - return ExpansionIter(self, start, stop) - else: - if n not in ZZ: - raise IndexError("index must be an integer") - n = ZZ(n) - if n >= maxordp: - raise OverflowError("beyond maximum precision (which is %s)" % maxordp) - return self._digit(long(n)) - - cdef Integer _digit(self, slong i): - self.jump(i+1) - cdef Integer ans = PY_NEW(Integer) - cdef fmpz* coeff = get_coeff(self._digits, i - self._valuation) - fmpz_get_mpz(ans.value, coeff) - return ans - - def _repr_(self): - error = self.jump(quiet=True) - s = error_to_str(error, permissive=True) - if s is not None: - return s - if self._valuation <= -maxordp: - return "valuation not known" - return pAdicGenericElement._repr_(self) - - cdef bint _is_equal(self, pAdicLazyElement right, slong prec, bint permissive) except -1: - if self._valuation <= -maxordp: - error = self._next_c() - raise_error(error) - if right._valuation <= -maxordp: - error = right._next_c() - raise_error(error) - cdef slong i = 0 - cdef slong j = self._valuation - right._valuation - if j > 0: - i = -j - j = 0 - prec -= self._valuation - while i < prec: - while self._precrel <= i: - error = self._next_c() - raise_error(error, permissive) - if error: # not enough precision - return True - while right._precrel <= j: - error = right._next_c() - raise_error(error, permissive) - if error: # not enough precision - return True - if not fmpz_equal(get_coeff(self._digits, i), get_coeff(right._digits, j)): - return False - i += 1 - j += 1 - return True - - @coerce_binop - def is_equal_at_precision(self, other, prec): - return self._is_equal(other, min(prec, maxordp), False) - - def __eq__(self, other): - if not have_same_parent(self, other): - a, b = coercion_model.canonical_coercion(self, other) - return a == b - cdef pAdicLazyElement right = other - if self._valuation <= -maxordp: - error = self._next_c() - raise_error(error, permissive=True) - if right._valuation <= -maxordp: - error = right._next_c() - raise_error(error, permissive=True) - minprec = min(self.precision_absolute(), right.precision_absolute()) - default_prec = self._parent.default_prec() - if self.prime_pow.in_field: - prec = self._valuation + default_prec - if not self._is_equal(right, max(minprec, prec), True): - return False - if self._valuation < prec: - return self._is_equal(right, max(minprec, self._valuation + default_prec), True) - else: - return self._is_equal(right, max(minprec, default_prec), True) - - cpdef bint _is_exact_zero(self) except -1: - return self._valuation >= maxordp - - cpdef bint _is_inexact_zero(self) except -1: - return self._precrel == 0 - - def is_exact(self): - return self._is_exact_zero() - - def precision_absolute(self): - if self._is_exact_zero(): - return Infinity - if self._valuation <= -maxordp: - raise PrecisionError("no lower bound on the valuation is known") - return ZZ(self._precrel + self._valuation) - - def precision_relative(self): - if self._valuation <= -maxordp: - raise PrecisionError("no lower bound on the valuation is known") - return ZZ(self._precrel) - - cdef long valuation_c(self): - return self._valuation - - def valuation(self, secure=False): - if self._is_exact_zero(): - return Infinity - if self._valuation <= -maxordp: - raise PrecisionError("no lower bound on the valuation is known") - if secure and self._precrel == 0: - raise PrecisionError("cannot determine the valuation; try to increase the precision") - return ZZ(self._valuation) - - def unit_part(self): - if self._precrel == 0: - raise PrecisionError("cannot determine the valuation; try to increase the precision") - return self >> self._valuation - - cpdef val_unit(self): - return self.valuation(), self.unit_part() - - def residue(self, slong prec=1, field=None): - if prec >= maxordp: - raise OverflowError - if prec < 0: - raise ValueError("cannot reduce modulo a negative power of p") - error = self._jump_c(prec) - if error: - raise_error(error) - if self._valuation < 0: - raise ValueError("element must have non-negative valuation in order to compute residue") - cdef fmpz_t fans - fmpz_init(fans) - cdef fmpz_poly_t digits - get_slice(digits, self._digits, 0, prec - self._valuation) - fmpz_poly_evaluate_fmpz(fans, digits, self.prime_pow.fprime) - cdef Integer ans = PY_NEW(Integer) - fmpz_get_mpz(ans.value, fans) - fmpz_clear(fans) - if field and prec == 1: - return self._parent.residue_class_field()(ans) - else: - return Integers(self.prime()**prec)(ans) - - def lift(self): - if self._precrel == 0: - return ZZ(0) - cdef fmpz_t fans - fmpz_init(fans) - cdef fmpz_poly_t digits - get_slice(digits, self._digits, 0, self._precrel) - fmpz_poly_evaluate_fmpz(fans, digits, self.prime_pow.fprime) - cdef Integer ans = PY_NEW(Integer) - fmpz_get_mpz(ans.value, fans) - fmpz_clear(fans) - if self._valuation: - ans *= self._parent.prime() ** self._valuation - return ans - - def __rshift__(self, s): - cdef slong shift = long(s) - if shift: - return pAdicLazyElement_shift((self)._parent, self, shift, not (self)._parent.is_field()) - else: - return self - - def __lshift__(self, s): - return self.__rshift__(-s) - - cpdef _add_(self, other): - cdef list summands - cdef list signs - if isinstance(self, pAdicLazyElement_zero): - return other - if isinstance(other, pAdicLazyElement_zero): - return self - if isinstance(self, pAdicLazyElement_add): - summands = list((self)._summands) - signs = list((self)._signs) - else: - summands = [self] - signs = [True] - if isinstance(other, pAdicLazyElement_add): - summands.extend((other)._summands) - signs.extend((other)._signs) - else: - summands.append(other) - signs.append(True) - return pAdicLazyElement_add(self._parent, summands, signs) - - cpdef _sub_(self, other): - cdef list summands - cdef list signs - if isinstance(self, pAdicLazyElement_zero): - return -other - if isinstance(other, pAdicLazyElement_zero): - return self - if isinstance(self, pAdicLazyElement_add): - summands = list((self)._summands) - signs = list((self)._signs) - else: - summands = [self] - signs = [True] - if isinstance(other, pAdicLazyElement_add): - summands.extend((other)._summands) - signs.extend([ not sign for sign in (other)._signs ]) - else: - summands.append(other) - signs.append(False) - return pAdicLazyElement_add(self._parent, summands, signs) - - cpdef _neg_(self): - cdef list summands - cdef list signs - if isinstance(self, pAdicLazyElement_add): - summands = list((self)._summands) - signs = [ not sign for sign in (self)._signs ] - else: - summands = [self] - signs = [False] - return pAdicLazyElement_add(self._parent, summands, signs) - - cpdef _mul_(self, other): - if isinstance(self, pAdicLazyElement_zero) or isinstance(other, pAdicLazyElement_one): - return self - if isinstance(self, pAdicLazyElement_one) or isinstance(other, pAdicLazyElement_zero): - return other - return pAdicLazyElement_mul(self._parent, self, other) - - cpdef _div_(self, other): - if isinstance(other, pAdicLazyElement_zero): - return ZeroDivisionError("cannot divide by zero") - if isinstance(other, pAdicLazyElement_one): - return self - return pAdicLazyElement_div(self._parent.fraction_field(), self, other) - - def __invert__(self): - if isinstance(self, pAdicLazyElement_zero): - return ZeroDivisionError("cannot divide by zero") - if isinstance(self, pAdicLazyElement_one): - return self - return pAdicLazyElement_div(self._parent.fraction_field(), self._parent.one(), self) - - def sqrt(self): - return pAdicLazyElement_sqrt(self._parent, self) - - -# Assignations -############## - -# Zero - -cdef class pAdicLazyElement_zero(pAdicLazyElement): - def __init__(self, parent): - pAdicLazyElement.__init__(self, parent) - self._valuation = maxordp - - cdef int _jump_c(self, slong prec): - return 0 - - cdef int _next_c(self): - return 0 - - -# One - -cdef class pAdicLazyElement_one(pAdicLazyElement): - def __init__(self, parent): - pAdicLazyElement.__init__(self, parent) - fmpz_poly_set_ui(self._digits, 1) - - cdef int _jump_c(self, slong prec): - if self._precrel < prec: - self._precrel = prec - return 0 - - cdef int _next_c(self): - self._precrel += 1 - return 0 - - -# Value - -cdef class pAdicLazyElement_value(pAdicLazyElement): - def __init__(self, parent, Integer value, slong shift=0, maxprec=None): - pAdicLazyElement.__init__(self, parent) - cdef Integer x = value - fmpz_poly_set_mpz(self._digits, x.value) - if maxprec is None: - self._maxprec = maxordp - else: - self._maxprec = maxprec - self._shift = self._valuation = shift - self._finished = False - - cdef int _jump_c(self, slong prec): - if not self._finished: - return pAdicLazyElement._jump_c(self, prec) - if (self._maxprec is not None) and (prec > self._maxprec): - self._precrel = self._maxprec - self._valuation - return ERROR_PRECISION - cdef slong precrel = min(prec, maxordp) - self._valuation - if precrel > self._precrel: - self._precrel = precrel - if prec < maxordp: - return 0 - return ERROR_OVERFLOW - - cdef int _next_c(self): - if (self._maxprec is not None) and (self._valuation + self._precrel >= self._maxprec): - return ERROR_PRECISION - reduce_coeff(self._digits, self._precrel, self.prime_pow.fprime) - if self._precrel == 0 and fmpz_is_zero(get_coeff(self._digits, 0)): - self._valuation += 1 - fmpz_poly_shift_right(self._digits, self._digits, 1) - else: - self._precrel += 1 - self._finished = fmpz_is_zero(get_coeff(self._digits, self._precrel)) - return 0 - - -# Random - -cdef flint_rand_t flint_randstate -flint_randinit(flint_randstate) - -cdef class pAdicLazyElement_random(pAdicLazyElement): - def __init__(self, parent, integral): - pAdicLazyElement.__init__(self, parent) - if not integral: - self._valuation = ZZ.random_element() - - cdef int _next_c(self): - cdef fmpz_t r - fmpz_randm(r, flint_randstate, self.prime_pow.fprime) - if self._precrel == 0 and fmpz_is_zero(r): - self._valuation += 1 - else: - fmpz_poly_set_coeff_fmpz(self._digits, self._precrel, r); - self._precrel += 1 - return 0 - - -# Operations -############ - -# Shift - -cdef class pAdicLazyElement_shift(pAdicLazyElement): - def __init__(self, parent, pAdicLazyElement x, slong s, bint truncate): - cdef slong start = 0 - cdef fmpz_poly_t digits; - pAdicLazyElement.__init__(self, parent) - self._x = x - self._shift = s - if truncate and x._valuation < s: - start = s - x._valuation - self._valuation = 0 - self._precrel = x._precrel - start - else: - self._valuation = x._valuation - s - self._precrel = x._precrel - if self._precrel < 0: - self._precrel = 0 - else: - get_slice(digits, x._digits, start, self._precrel) - fmpz_poly_set(self._digits, digits) - - cdef int _next_c(self): - cdef slong n = self._valuation + self._precrel - cdef pAdicLazyElement x = self._x - cdef fmpz* digit - cdef int error - - error = x._jump_c(n + self._shift + 1) - if error: - return error - digit = get_coeff(x._digits, n + self._shift - x._valuation) - fmpz_poly_set_coeff_fmpz(self._digits, self._precrel, digit) - if self._precrel == 0 and fmpz_is_zero(get_coeff(self._digits, 0)): - self._valuation += 1 - fmpz_poly_shift_right(self._digits, self._digits, 1) - else: - self._precrel += 1 - return 0 - - -# Addition - -cdef class pAdicLazyElement_add(pAdicLazyElement): - def __init__(self, parent, summands, signs): - pAdicLazyElement.__init__(self, parent) - self._valuation = min((summand)._valuation for summand in summands) - self._summands = summands - self._signs = signs - - cdef int _next_c(self): - cdef pAdicLazyElement summand - cdef slong n = self._valuation + self._precrel - cdef fmpz* coeff - cdef int error - - for summand in self._summands: - error = summand._jump_c(n+1) - if error: - return error - cdef slong i - for i in range(len(self._summands)): - summand = self._summands[i] - coeff = get_coeff(summand._digits, n - summand._valuation) - if self._signs[i]: - iadd_coeff(self._digits, coeff, n - self._valuation) - else: - isub_coeff(self._digits, coeff, n - self._valuation) - reduce_coeff(self._digits, self._precrel, self.prime_pow.fprime) - if self._precrel == 0 and fmpz_is_zero(get_coeff(self._digits, 0)): - self._valuation += 1 - fmpz_poly_shift_right(self._digits, self._digits, 1) - else: - self._precrel += 1 - return 0 - - - -# Multiplication - -cdef class pAdicLazyElement_mul(pAdicLazyElement): - def __cinit__(self): - fmpz_init(self._lastdigit_x) - fmpz_init(self._lastdigit_y) - - def __dealloc__(self): - fmpz_clear(self._lastdigit_x) - fmpz_clear(self._lastdigit_y) - - def __init__(self, parent, x, y): - pAdicLazyElement.__init__(self, parent) - self._x = x - self._y = y - self._valuation = self._x._valuation + self._y._valuation - - cdef int _next_c(self): - global tmp_coeff, tmp_poly - cdef pAdicLazyElement x = self._x - cdef pAdicLazyElement y = self._y - cdef slong n = self._valuation + self._precrel - - cdef int errorx = x._jump_c(n - y._valuation + 1) - cdef int errory = y._jump_c(n - x._valuation + 1) - cdef int error = errorx | errory - if self._precrel == 0: - self._valuation = x._valuation + y._valuation - if self._valuation > n: - return 0 - if self._valuation < n or x._precrel == 0 or y._precrel == 0: - return error | ERROR_PRECISION - elif error: - return error - - n = self._precrel - fmpz_set(self._lastdigit_x, get_coeff(x._digits, n)) - fmpz_set(self._lastdigit_y, get_coeff(y._digits, n)) - fmpz_mul(tmp_coeff, get_coeff(x._digits, 0), self._lastdigit_y) - iadd_coeff(self._digits, tmp_coeff, n) - if n: - fmpz_mul(tmp_coeff, self._lastdigit_x, get_coeff(y._digits, 0)) - iadd_coeff(self._digits, tmp_coeff, n) - - cdef slong m = n + 2 - cdef slong len = 1 - cdef fmpz_poly_t slicex, slicey - while (m & 1 == 0) and (m > 3): - m >>= 1 - len <<= 1 - get_slice(slicex, x._digits, len - 1, len) - get_slice(slicey, y._digits, (m-1)*len - 1, len) - fmpz_poly_mul(tmp_poly, slicex, slicey) - iadd_shifted(self._digits, tmp_poly, n) - if m > 2: - get_slice(slicex, x._digits, (m-1)*len - 1, len) - get_slice(slicey, y._digits, len - 1, len) - fmpz_poly_mul(tmp_poly, slicex, slicey) - iadd_shifted(self._digits, tmp_poly, n) - - reduce_coeff(self._digits, n, self.prime_pow.fprime) - self._precrel += 1 - return 0 - - cdef int _update_last_digit(self): - if self._precrel == 0: - return ERROR_UNEXPECTED - cdef pAdicLazyElement x = self._x - cdef pAdicLazyElement y = self._y - cdef slong n = self._precrel - 1 - cdef slong m = n + 2 - cdef slong len = 2 - cdef fmpz_poly_t slice - while (m & 1 == 0) and (m > 3): - m >>= 1 - len <<= 1 - len -= 1 - - fmpz_sub(tmp_coeff, get_coeff(x._digits, n), self._lastdigit_x) - get_slice(slice, y._digits, 0, len) - fmpz_poly_scalar_mul_fmpz(tmp_poly, slice, tmp_coeff) - iadd_shifted(self._digits, tmp_poly, n) - if m == 2: - len -= 1 - fmpz_sub(tmp_coeff, get_coeff(y._digits, n), self._lastdigit_y) - get_slice(slice, x._digits, 0, len) - fmpz_poly_scalar_mul_fmpz(tmp_poly, slice, tmp_coeff) - iadd_shifted(self._digits, tmp_poly, n) - if m == 2: - fmpz_mul(tmp_coeff, tmp_coeff, self._lastdigit_x) - iadd_coeff(self._digits, tmp_coeff, 2*len) - reduce_coeff(self._digits, n, self.prime_pow.fprime) - - fmpz_set(self._lastdigit_x, get_coeff(x._digits, n)) - fmpz_set(self._lastdigit_y, get_coeff(y._digits, n)) - return 0 - - -cdef class pAdicLazyElement_muldigit(pAdicLazyElement): - # compute x*y where x is assumed to be in (0,p) - def __init__(self, parent, pAdicLazyElement_div x, pAdicLazyElement y): - pAdicLazyElement.__init__(self, parent) - self._x = x._inverse - self._y = y - self._valuation = y._valuation - - cdef int _next_c(self): - cdef slong n = self._valuation + self._precrel - cdef int error = self._y._jump_c(n+1) - if error: - return error - fmpz_mul(tmp_coeff, self._x, get_coeff(self._y._digits, n - self._y._valuation)) - iadd_coeff(self._digits, tmp_coeff, self._precrel) - reduce_coeff(self._digits, self._precrel, self.prime_pow.fprime) - self._precrel += 1 - return 0 - - cdef void _erase_first_digit(self): - # This is a awful hack, - # but it's very useful for division - if self._precrel: - self._valuation += 1 - self._precrel -= 1 - fmpz_poly_shift_right(self._digits, self._digits, 1) - - -# Division - -cdef class pAdicLazyElement_div(pAdicLazyElement): - def __cinit__(self): - fmpz_init(self._inverse) - - def __dealloc__(self): - fmpz_clear(self._inverse) - - def __init__(self, parent, pAdicLazyElement num, pAdicLazyElement denom): - pAdicLazyElement.__init__(self, parent) - self._num = num - self._denom = denom - if denom._valuation <= -maxordp: - self._maxprec = maxordp + 1 - else: - self._maxprec = denom._valuation + self._parent.default_prec() - cdef int error = self._bootstrap_c() - if error & ERROR_DIVISION: - raise ZeroDivisionError("cannot divide by something indistinguishable from zero") - if error: - self._valuation = -maxordp - - cdef int _bootstrap_c(self): - cdef int error - cdef pAdicLazyElement num = self._num - cdef pAdicLazyElement denom = self._denom - cdef fmpz_t gcd - - while denom._valuation < self._maxprec and denom._precrel == 0: - error = denom._next_c() - if error: - if error & ERROR_PRECISION: - error |= ERROR_DIVISION - return error - if self._maxprec > maxordp and denom._valuation > -maxordp: - self._maxprec = denom._valuation + self._parent.default_prec() - if denom._precrel == 0: - return ERROR_ABANDON - self._valuation = num._valuation - denom._valuation - fmpz_gcdinv(gcd, self._inverse, get_coeff(denom._digits, 0), self.prime_pow.fprime) - cdef pAdicLazyElement_muldigit a = pAdicLazyElement_muldigit(self._parent, self, num) - cdef pAdicLazyElement_muldigit b = pAdicLazyElement_muldigit(self._parent, self, denom) - error = b._next_c() - if error or b._precrel != 1: - return error | ERROR_UNEXPECTED - b._erase_first_digit() - self._definition = a - b * self - return 0 - - cdef int _next_c(self): - cdef pAdicLazyElement definition = self._definition - cdef slong val - if definition is None: - error = self._bootstrap_c() - if error: - return error - else: - val = self._valuation + self._denom._valuation - error = definition._jump_c(val + self._precrel + 1) - if error: - return error - if definition._valuation > val: - self._valuation = definition._valuation - self._denom._valuation - else: - digit = get_coeff(definition._digits, self._precrel) - fmpz_poly_set_coeff_fmpz(self._digits, self._precrel, digit); - self._precrel += 1 - return 0 - - -# Square root - -cdef bint fmpz_modp_sqrt(fmpz* ans, fmpz* x, fmpz* p): - # Need to do something better - cdef Integer zx = PY_NEW(Integer) - fmpz_get_mpz(zx.value, x) - cdef Integer zp = PY_NEW(Integer) - fmpz_get_mpz(zp.value, p) - cdef Integer zans - try: - k = GF(zp) - zans = ZZ(k(zx).sqrt(extend=False)) - except ValueError: - return 1 - fmpz_set_mpz(ans, zans.value) - return 0 - - -cdef class pAdicLazyElement_sqrt(pAdicLazyElement): - def __init__(self, parent, pAdicLazyElement x): - pAdicLazyElement.__init__(self, parent) - #if parent.prime() == 2: - # raise NotImplementedError - self._x = x - if x._valuation <= -maxordp: - self._valuation = -maxordp - self._maxprec = maxordp + 1 - else: - self._valuation = x._valuation >> 1 - self._maxprec = x._valuation + 2*self._parent.default_prec() - cdef int error = self._bootstrap_c() - if error & ERROR_NOTSQUARE: - raise ValueError("not a square") - - cdef int _bootstrap_c(self): - cdef pAdicLazyElement x = self._x - while x._valuation < self._maxprec and x._precrel == 0: - error = x._next_c() - if error: - return error - if self._maxprec > maxordp and x._valuation > -maxordp: - self._maxprec = x._valuation + 2*self._parent.default_prec() - if x._valuation > -maxordp: - self._valuation = x._valuation >> 1 - if x._precrel == 0: - return ERROR_ABANDON - if x._valuation & 1 != 0: - return ERROR_NOTSQUARE - - cdef parent = self._parent - cdef slong val = self._valuation - cdef fmpz_t digit - cdef Integer zd - cdef pAdicLazyElement u, y, c, d - - if fmpz_equal_ui(self.prime_pow.fprime, 2): - fmpz_poly_set_coeff_ui(self._digits, 0, 1) - self._precrel = 1 - if x._precrel == 1: - error = x._next_c() - if error: - return error - if not fmpz_equal_ui(get_coeff(x._digits, 1), 0): - return ERROR_NOTSQUARE - if x._precrel == 2: - error = x._next_c() - if error: - return error - if not fmpz_equal_ui(get_coeff(x._digits, 2), 0): - return ERROR_NOTSQUARE - zd = Integer(1) - u = pAdicLazyElement_shift(parent, self, val + 2, True) - y = pAdicLazyElement_shift(parent, x, val + 1, False) - c = pAdicLazyElement_value(parent, zd, shift=val-1) - d = pAdicLazyElement_shift(parent, u*u, -val-3, False) - self._definition = y + c - d - else: - fmpz_init(digit) - if fmpz_modp_sqrt(digit, get_coeff(x._digits, 0), self.prime_pow.fprime): - return ERROR_NOTSQUARE - fmpz_poly_set_coeff_fmpz(self._digits, 0, digit) - self._precrel = 1 - zd = PY_NEW(Integer) - fmpz_get_mpz(zd.value, digit) - fmpz_clear(digit) - u = pAdicLazyElement_shift(parent, self, val + 1, True) - y = pAdicLazyElement_shift(parent, x, 2*val + 2, False) - c = pAdicLazyElement_value(parent, zd*zd, shift=-2) - d = pAdicLazyElement_value(parent, 2*zd, shift=-val-2) - self._definition = (y + c - u*u) / d - return 0 - - cdef int _next_c(self): - cdef pAdicLazyElement x = self._x - cdef pAdicLazyElement definition = self._definition - cdef slong n = self._valuation + self._precrel - cdef int error - if definition is None: - error = self._bootstrap_c() - if error: - return error - else: - error = definition._jump_c(n+1) - if error: - return error - fmpz_poly_set_coeff_fmpz(self._digits, self._precrel, get_coeff(definition._digits, self._precrel)) - self._precrel += 1 - return 0 - - def next(self): - return self._next_c() - - -# Teichmüller lifts - -cdef class pAdicLazyElement_teichmuller(pAdicLazyElement): - def __init__(self, parent, Integer xbar): - pAdicLazyElement.__init__(self, parent) - cdef fmpz_t digit - fmpz_init(digit) - fmpz_set_mpz(digit, xbar.value) - fmpz_mod(digit, digit, self.prime_pow.fprime) - if fmpz_equal_ui(digit, 0): - self._trivial = True - self._valuation = maxordp - else: - fmpz_poly_set_coeff_fmpz(self._digits, 0, digit) - self._trivial = fmpz_equal_ui(digit, 1) - self._precrel += 1 - - cdef pAdicLazyElement xn = self - cdef slong size = fmpz_bits(self.prime_pow.fprime) - cdef slong i = size - 2 - self._xns = [ ] - while i >= 0: - xn = xn * xn - self._xns.append(xn) - if fmpz_tstbit(self.prime_pow.fprime, i): - xn = xn * self - self._xns.append(xn) - i -= 1 - self._xp = xn - error = self._xp._next_c() - if error or self._xp._precrel != 1: - raise_error(ERROR_UNEXPECTED) - - cdef int _jump_c(self, slong prec): - if self._trivial: - if self._valuation == 0 and self._precrel < prec: - self._precrel = prec - return 0 - return pAdicLazyElement._jump_c(self, prec) - - cdef int _next_c(self): - if self._trivial: - if self._valuation: - self._precrel += 1 - return 0 - cdef int error - cdef pAdicLazyElement xp = self._xp - cdef pAdicLazyElement_mul xn - self._precrel += 1 - xp._next_c() - fmpz_poly_set_coeff_fmpz(self._digits, self._precrel - 1, get_coeff(xp._digits, self._precrel - 1)) - for xn in self._xns: - error = xn._update_last_digit() - if error: - return error | ERROR_UNEXPECTED - return 0 - - -# Self-referent definitions -########################### - -cdef class pAdicLazyElement_selfref(pAdicLazyElement): - def __init__(self, parent, valuation): - pAdicLazyElement.__init__(self, parent) - self._valuation = valuation - self._definition = None - self._next = False - - cpdef set(self, pAdicLazyElement definition): - if self._definition is not None: - raise ValueError("this self-referent number is already defined") - self._definition = definition - cdef slong valsve = self._valuation - cdef int error = self._jump_c(self._valuation + self._parent.default_prec()) - if error & ERROR_CIRCULAR: - self._definition = None - self._valuation = valsve - self._precrel = 0 - self._next = False - raise_error(error, permissive=True) - - def __eq__(self, other): - if self._definition is None: - self.set(other) - return pAdicLazyElement.__eq__(self, other) - - cdef int _next_c(self): - cdef pAdicLazyElement definition = self._definition - cdef fmpz* digit - cdef slong diffval - cdef int error - - if definition is None: - return ERROR_NOTDEFINED - if self._next: - return ERROR_CIRCULAR - - self._next = True - error = definition._jump_c(self._valuation + self._precrel + 1) - if not error: - diffval = self._valuation - definition._valuation - if diffval < 0: - self._valuation = definition._valuation - else: - digit = get_coeff(definition._digits, self._precrel + diffval) - if self._precrel == 0 and fmpz_is_zero(digit): - self._valuation += 1 - else: - fmpz_poly_set_coeff_fmpz(self._digits, self._precrel, digit); - self._precrel += 1 - self._next = False - return error - - -# Expansion -########### - -cdef class ExpansionIter(object): - cdef pAdicLazyElement elt - cdef slong start - cdef slong stop - cdef slong current - - def __init__(self, pAdicLazyElement elt, start, stop): - self.elt = elt - if start is None: - self.start = 0 - else: - if start >= maxordp: - raise OverflowError("beyond maximum precision (which is %s)" % maxordp) - self.start = long(start) - if stop is None: - self.stop = self.start - 1 - else: - if stop >= maxordp: - raise OverflowError("beyond maximum precision (which is %s)" % maxordp) - self.stop = long(stop) - if self.stop <= self.start: - self.stop = self.start - self.current = self.start - - def __repr__(self): - return "%s-adic expansion of %s" % (self.elt._parent.prime(), self.elt) - - def __len__(self): - if self.stop < self.start: - raise NotImplementedError("infinite sequence") - return ZZ(self.stop - self.start) - - def __iter__(self): - return self - - def __next__(self): - if self.stop < self.start or self.current < self.stop: - digit = self.elt._digit(self.current) - self.current += 1 - return digit - else: - raise StopIteration +include "sage/libs/linkages/padics/lazy/flint.pxi" +include "lazy_template.pxi" + +cdef type lazy_class_zero = pAdicLazyElement_zero +cdef type lazy_class_one = pAdicLazyElement_one +cdef type lazy_class_value = pAdicLazyElement_value +cdef type lazy_class_random = pAdicLazyElement_random +cdef type lazy_class_shift = pAdicLazyElement_shift +cdef type lazy_class_add = pAdicLazyElement_add +cdef type lazy_class_mul = pAdicLazyElement_mul +cdef type lazy_class_muldigit = pAdicLazyElement_muldigit +cdef type lazy_class_div = pAdicLazyElement_div +cdef type lazy_class_sqrt = pAdicLazyElement_sqrt +cdef type lazy_class_teichmuller = pAdicLazyElement_teichmuller +cdef type lazy_class_selfref = pAdicLazyElement_selfref From 90abf2fe72705de9e22a1c9c58e0e212e55509a8 Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Mon, 11 Jan 2021 08:04:57 +0100 Subject: [PATCH 100/406] make API and implementation coherent --- src/sage/libs/linkages/padics/lazy/API.pxi | 85 +++++++++++++--- src/sage/libs/linkages/padics/lazy/flint.pxi | 79 +++++++++----- src/sage/rings/padics/lazy_template.pxi | 102 +++++++++---------- 3 files changed, 176 insertions(+), 90 deletions(-) diff --git a/src/sage/libs/linkages/padics/lazy/API.pxi b/src/sage/libs/linkages/padics/lazy/API.pxi index e4b1bdb68bb..a33b0c14a38 100644 --- a/src/sage/libs/linkages/padics/lazy/API.pxi +++ b/src/sage/libs/linkages/padics/lazy/API.pxi @@ -1,4 +1,16 @@ +# **************************************************************************** +# Copyright (C) 2021 Xavier Caruso +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# http://www.gnu.org/licenses/ +# **************************************************************************** + + # Operations on digits (intended to be small elements in the exact subring) +########################################################################### cdef inline void digit_init(cdigit a): pass @@ -6,22 +18,34 @@ cdef inline void digit_init(cdigit a): cdef inline void digit_clear(cdigit a): pass +# get and set + +cdef inline Integer digit_get_sage(cdigit a): + pass + cdef inline void digit_set(cdigit a, cdigit b): pass cdef inline void digit_set_ui(cdigit a, slong b): pass +cdef inline void digit_set_sage(cdigit a, Integer elt): + pass + +# comparisons + cdef inline bint digit_equal(cdigit a, cdigit b): pass cdef inline bint digit_equal_ui(cdigit a, slong b): pass -cdef inline void digit_set_sage(cdigit a, elt): +cdef inline bint digit_is_zero(cdigit a): pass -cdef inline digit_get_sage(cdigit a): +# operations + +cdef inline void digit_random(cdigit res, PowComputer_class prime_pow): pass cdef inline void digit_add(cdigit res, cdigit a, cdigit b): @@ -33,34 +57,62 @@ cdef inline void digit_sub(cdigit res, cdigit a, cdigit b): cdef inline void digit_mul(cdigit res, cdigit a, cdigit b): pass -cdef inline void digit_mod(cdigit res, cdigit a, cdigit modulus): +cdef inline void digit_mod(cdigit res, cdigit a, PowComputer_class prime_pow): + pass + +cdef inline void digit_quorem(cdigit quo, cdigit rem, cdigit a, PowComputer_class prime_pow): pass -cdef inline void digit_quorem(cdigit quo, cdigit rem, cdigit a, cdigit modulus): +cdef inline void digit_inv(cdigit res, cdigit a, PowComputer_class prime_pow): +_ pass + +cdef bint digit_sqrt(cdigit_ptr ans, cdigit_ptr x, PowComputer_class prime_pow): pass # Operations on elements (represented as series of digits) +########################################################## -cdef inline void element_init(cdigit a): +cdef inline void element_init(celement x): pass -cdef inline void element_clear(cdigit a): +cdef inline void element_clear(celement x): pass -cdef inline void element_set_coeff(celement x, cdigit a, slong i): +# get and set + +cdef inline element_get_sage(celement x, PowComputer_class prime_pow): + pass + +cdef inline void element_set(celement x, celement y): pass -cdef inline void cdigit_ptr element_get_coeff(celement x, slong i): +# get and set digits + +cdef inline cdigit_ptr element_get_digit(celement x, slong i): pass -cdef inline void cdigit_ptr element_get_slice(celement x, slong start, slong length): +cdef inline Integer element_get_digit_sage(celement x, slong i): pass -cdef inline void element_iadd_coeff(celement x, cdigit a, slong i): +cdef inline void element_get_slice(celement res, celement x, slong start, slong length): pass -cdef inline void element_isub_coeff(celement x, cdigit a, slong i): +cdef inline void element_set_digit(celement x, cdigit a, slong i): + pass + +cdef inline void element_set_digit_ui(celement x, slong a, slong i): + pass + +cdef inline void element_set_digit_sage(celement x, Integer a, slong i): + pass + +# operations + +cdef inline void element_iadd_digit(celement x, cdigit a, slong i): + pass + +cdef inline void element_isub_digit(celement x, cdigit a, slong i): pass cdef inline void element_iadd_slice(celement x, celement slice, slong start): @@ -69,5 +121,14 @@ cdef inline void element_iadd_slice(celement x, celement slice, slong start): cdef inline void element_isub_slice(celement x, celement slice, slong start): pass -cdef inline void element_reduce_coeff(celement x, slong i): +cdef inline void element_scalarmul(celement res, celement x, cdigit a): + pass + +cdef inline void element_mul(celement res, celement x, celement y): + pass + +cdef inline void element_reduce_digit(celement x, slong i, PowComputer_class prime_pow): + pass + +cdef inline void element_shift_right(celement x): pass diff --git a/src/sage/libs/linkages/padics/lazy/flint.pxi b/src/sage/libs/linkages/padics/lazy/flint.pxi index 34d7f1e22c7..f3407fc7ded 100644 --- a/src/sage/libs/linkages/padics/lazy/flint.pxi +++ b/src/sage/libs/linkages/padics/lazy/flint.pxi @@ -1,3 +1,13 @@ +# **************************************************************************** +# Copyright (C) 2021 Xavier Caruso +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# http://www.gnu.org/licenses/ +# **************************************************************************** + from sage.libs.flint.types cimport flint_rand_t from sage.libs.flint.fmpz cimport * from sage.libs.flint.fmpz_poly cimport * @@ -24,7 +34,9 @@ from sage.ext.stdsage cimport PY_NEW cdef flint_rand_t flint_randstate flint_randinit(flint_randstate) + # Operations on digits (intended to be small elements in the exact subring) +########################################################################### cdef inline void digit_init(cdigit a): fmpz_init(a) @@ -32,28 +44,34 @@ cdef inline void digit_init(cdigit a): cdef inline void digit_clear(cdigit a): fmpz_clear(a) +# get and set + +cdef inline Integer digit_get_sage(cdigit a): + cdef Integer elt = PY_NEW(Integer) + fmpz_get_mpz(elt.value, a) + return elt + cdef inline void digit_set(cdigit a, cdigit b): fmpz_set(a, b) cdef inline void digit_set_ui(cdigit a, slong b): fmpz_set_ui(a, b) +cdef inline void digit_set_sage(cdigit a, Integer elt): + fmpz_set_mpz(a, elt.value) + +# comparisons + cdef inline bint digit_equal(cdigit a, cdigit b): return fmpz_equal(a, b) -cdef inline bint digit_is_zero(cdigit a): - return fmpz_is_zero(a) - cdef inline bint digit_equal_ui(cdigit a, slong b): return fmpz_equal_ui(a, b) -cdef inline void digit_set_sage(cdigit a, Integer elt): - fmpz_set_mpz(a, elt.value) +cdef inline bint digit_is_zero(cdigit a): + return fmpz_is_zero(a) -cdef inline Integer digit_get_sage(cdigit a): - cdef Integer elt = PY_NEW(Integer) - fmpz_get_mpz(elt.value, a) - return elt +# operations cdef inline void digit_random(cdigit res, PowComputer_class prime_pow): fmpz_randm(res, flint_randstate, (prime_pow).fprime) @@ -93,6 +111,7 @@ cdef bint digit_sqrt(cdigit_ptr ans, cdigit_ptr x, PowComputer_class prime_pow): # Operations on elements (represented as series of digits) +########################################################## cdef inline void element_init(celement x): fmpz_poly_init(x) @@ -100,20 +119,7 @@ cdef inline void element_init(celement x): cdef inline void element_clear(celement x): fmpz_poly_clear(x) -cdef inline void element_set(celement x, celement y): - fmpz_poly_set(x, y) - -cdef inline void element_set_coeff(celement x, cdigit a, slong i): - fmpz_poly_set_coeff_fmpz(x, i, a) - -cdef inline void element_set_coeff_ui(celement x, slong a, slong i): - fmpz_poly_set_coeff_ui(x, i, a) - -cdef inline void element_set_coeff_sage(celement x, Integer a, slong i): - fmpz_poly_set_coeff_mpz(x, i, a.value) - -cdef inline cdigit_ptr element_get_coeff(celement x, slong i): - return get_coeff(x, i) +# get and set cdef inline Integer element_get_sage(celement x, PowComputer_class prime_pow): cdef fmpz_t value @@ -123,16 +129,35 @@ cdef inline Integer element_get_sage(celement x, PowComputer_class prime_pow): fmpz_clear(value) return ans -cdef inline Integer element_get_coeff_sage(celement x, slong i): +cdef inline void element_set(celement x, celement y): + fmpz_poly_set(x, y) + +# get and set digits + +cdef inline cdigit_ptr element_get_digit(celement x, slong i): + return get_coeff(x, i) + +cdef inline Integer element_get_digit_sage(celement x, slong i): return digit_get_sage(get_coeff(x, i)) cdef inline void element_get_slice(celement res, celement x, slong start, slong length): get_slice(res, x, start, length) -cdef inline void element_iadd_coeff(celement x, cdigit a, slong i): +cdef inline void element_set_digit(celement x, cdigit a, slong i): + fmpz_poly_set_coeff_fmpz(x, i, a) + +cdef inline void element_set_digit_ui(celement x, slong a, slong i): + fmpz_poly_set_coeff_ui(x, i, a) + +cdef inline void element_set_digit_sage(celement x, Integer a, slong i): + fmpz_poly_set_coeff_mpz(x, i, a.value) + +# operations + +cdef inline void element_iadd_digit(celement x, cdigit a, slong i): iadd_coeff(x, a, i) -cdef inline void element_isub_coeff(celement x, cdigit a, slong i): +cdef inline void element_isub_digit(celement x, cdigit a, slong i): isub_coeff(x, a, i) cdef inline void element_iadd_slice(celement x, celement slice, slong start): @@ -147,7 +172,7 @@ cdef inline void element_scalarmul(celement res, celement x, cdigit a): cdef inline void element_mul(celement res, celement x, celement y): fmpz_poly_mul(res, x, y) -cdef inline void element_reduce_coeff(celement x, slong i, PowComputer_class prime_pow): +cdef inline void element_reduce_digit(celement x, slong i, PowComputer_class prime_pow): reduce_coeff(x, i, (prime_pow).fprime) cdef inline void element_shift_right(celement x): diff --git a/src/sage/rings/padics/lazy_template.pxi b/src/sage/rings/padics/lazy_template.pxi index f5ca3ec7569..73bd5dcddcf 100644 --- a/src/sage/rings/padics/lazy_template.pxi +++ b/src/sage/rings/padics/lazy_template.pxi @@ -31,9 +31,9 @@ cdef long maxordp = (1L << (sizeof(long) * 8 - 2)) - 1 MAXORDP = ZZ(maxordp) -cdef cdigit tmp_coeff +cdef cdigit tmp_digit cdef celement tmp_poly -digit_init(tmp_coeff) +digit_init(tmp_digit) element_init(tmp_poly) @@ -130,7 +130,7 @@ cdef class LazyElement(pAdicGenericElement): cdef Integer _digit(self, slong i): self.jump(i+1) - cdef cdigit_ptr coeff = element_get_coeff(self._digits, i - self._valuation) + cdef cdigit_ptr coeff = element_get_digit(self._digits, i - self._valuation) return digit_get_sage(coeff) def _repr_(self): @@ -166,7 +166,7 @@ cdef class LazyElement(pAdicGenericElement): raise_error(error, permissive) if error: # not enough precision return True - if not digit_equal(element_get_coeff(self._digits, i), element_get_coeff(right._digits, j)): + if not digit_equal(element_get_digit(self._digits, i), element_get_digit(right._digits, j)): return False i += 1 j += 1 @@ -377,7 +377,7 @@ cdef class LazyElement_zero(LazyElement): cdef class LazyElement_one(LazyElement): def __init__(self, parent): LazyElement.__init__(self, parent) - element_set_coeff_ui(self._digits, 1, 0) + element_set_digit_ui(self._digits, 1, 0) cdef int _jump_c(self, slong prec): if self._precrel < prec: @@ -394,7 +394,7 @@ cdef class LazyElement_one(LazyElement): cdef class LazyElement_value(LazyElement): def __init__(self, parent, Integer value, slong shift=0, maxprec=None): LazyElement.__init__(self, parent) - element_set_coeff_sage(self._digits, value, 0) + element_set_digit_sage(self._digits, value, 0) if maxprec is None: self._maxprec = maxordp else: @@ -418,13 +418,13 @@ cdef class LazyElement_value(LazyElement): cdef int _next_c(self): if (self._maxprec is not None) and (self._valuation + self._precrel >= self._maxprec): return ERROR_PRECISION - element_reduce_coeff(self._digits, self._precrel, self.prime_pow) - if self._precrel == 0 and digit_is_zero(element_get_coeff(self._digits, 0)): + element_reduce_digit(self._digits, self._precrel, self.prime_pow) + if self._precrel == 0 and digit_is_zero(element_get_digit(self._digits, 0)): self._valuation += 1 element_shift_right(self._digits) else: self._precrel += 1 - self._finished = digit_is_zero(element_get_coeff(self._digits, self._precrel)) + self._finished = digit_is_zero(element_get_digit(self._digits, self._precrel)) return 0 @@ -443,7 +443,7 @@ cdef class LazyElement_random(LazyElement): self._valuation += 1 digit_clear(r) else: - element_set_coeff(self._digits, r, self._precrel) + element_set_digit(self._digits, r, self._precrel) self._precrel += 1 return 0 @@ -482,9 +482,9 @@ cdef class LazyElement_shift(LazyElement): error = x._jump_c(n + self._shift + 1) if error: return error - digit = element_get_coeff(x._digits, n + self._shift - x._valuation) - element_set_coeff(self._digits, digit, self._precrel) - if self._precrel == 0 and digit_is_zero(element_get_coeff(self._digits, 0)): + digit = element_get_digit(x._digits, n + self._shift - x._valuation) + element_set_digit(self._digits, digit, self._precrel) + if self._precrel == 0 and digit_is_zero(element_get_digit(self._digits, 0)): self._valuation += 1 element_shift_right(self._digits) else: @@ -514,13 +514,13 @@ cdef class LazyElement_add(LazyElement): cdef slong i for i in range(len(self._summands)): summand = self._summands[i] - coeff = element_get_coeff(summand._digits, n - summand._valuation) + coeff = element_get_digit(summand._digits, n - summand._valuation) if self._signs[i]: - element_iadd_coeff(self._digits, coeff, n - self._valuation) + element_iadd_digit(self._digits, coeff, n - self._valuation) else: - element_isub_coeff(self._digits, coeff, n - self._valuation) - element_reduce_coeff(self._digits, self._precrel, self.prime_pow) - if self._precrel == 0 and digit_is_zero(element_get_coeff(self._digits, 0)): + element_isub_digit(self._digits, coeff, n - self._valuation) + element_reduce_digit(self._digits, self._precrel, self.prime_pow) + if self._precrel == 0 and digit_is_zero(element_get_digit(self._digits, 0)): self._valuation += 1 element_shift_right(self._digits) else: @@ -547,7 +547,7 @@ cdef class LazyElement_mul(LazyElement): self._valuation = self._x._valuation + self._y._valuation cdef int _next_c(self): - global tmp_coeff, tmp_poly + global tmp_digit, tmp_poly cdef LazyElement x = self._x cdef LazyElement y = self._y cdef slong n = self._valuation + self._precrel @@ -565,13 +565,13 @@ cdef class LazyElement_mul(LazyElement): return error n = self._precrel - digit_set(self._lastdigit_x, element_get_coeff(x._digits, n)) - digit_set(self._lastdigit_y, element_get_coeff(y._digits, n)) - digit_mul(tmp_coeff, element_get_coeff(x._digits, 0), self._lastdigit_y) - element_iadd_coeff(self._digits, tmp_coeff, n) + digit_set(self._lastdigit_x, element_get_digit(x._digits, n)) + digit_set(self._lastdigit_y, element_get_digit(y._digits, n)) + digit_mul(tmp_digit, element_get_digit(x._digits, 0), self._lastdigit_y) + element_iadd_digit(self._digits, tmp_digit, n) if n: - digit_mul(tmp_coeff, self._lastdigit_x, element_get_coeff(y._digits, 0)) - element_iadd_coeff(self._digits, tmp_coeff, n) + digit_mul(tmp_digit, self._lastdigit_x, element_get_digit(y._digits, 0)) + element_iadd_digit(self._digits, tmp_digit, n) cdef slong m = n + 2 cdef slong len = 1 @@ -589,7 +589,7 @@ cdef class LazyElement_mul(LazyElement): element_mul(tmp_poly, slicex, slicey) element_iadd_slice(self._digits, tmp_poly, n) - element_reduce_coeff(self._digits, n, self.prime_pow) + element_reduce_digit(self._digits, n, self.prime_pow) self._precrel += 1 return 0 @@ -607,23 +607,23 @@ cdef class LazyElement_mul(LazyElement): len <<= 1 len -= 1 - digit_sub(tmp_coeff, element_get_coeff(x._digits, n), self._lastdigit_x) + digit_sub(tmp_digit, element_get_digit(x._digits, n), self._lastdigit_x) element_get_slice(slice, y._digits, 0, len) - element_scalarmul(tmp_poly, slice, tmp_coeff) + element_scalarmul(tmp_poly, slice, tmp_digit) element_iadd_slice(self._digits, tmp_poly, n) if m == 2: len -= 1 - digit_sub(tmp_coeff, element_get_coeff(y._digits, n), self._lastdigit_y) + digit_sub(tmp_digit, element_get_digit(y._digits, n), self._lastdigit_y) element_get_slice(slice, x._digits, 0, len) - element_scalarmul(tmp_poly, slice, tmp_coeff) + element_scalarmul(tmp_poly, slice, tmp_digit) element_iadd_slice(self._digits, tmp_poly, n) if m == 2: - digit_mul(tmp_coeff, tmp_coeff, self._lastdigit_x) - element_iadd_coeff(self._digits, tmp_coeff, 2*len) - element_reduce_coeff(self._digits, n, self.prime_pow) + digit_mul(tmp_digit, tmp_digit, self._lastdigit_x) + element_iadd_digit(self._digits, tmp_digit, 2*len) + element_reduce_digit(self._digits, n, self.prime_pow) - digit_set(self._lastdigit_x, element_get_coeff(x._digits, n)) - digit_set(self._lastdigit_y, element_get_coeff(y._digits, n)) + digit_set(self._lastdigit_x, element_get_digit(x._digits, n)) + digit_set(self._lastdigit_y, element_get_digit(y._digits, n)) return 0 @@ -640,9 +640,9 @@ cdef class LazyElement_muldigit(LazyElement): cdef int error = self._y._jump_c(n+1) if error: return error - digit_mul(tmp_coeff, self._x, element_get_coeff(self._y._digits, n - self._y._valuation)) - element_iadd_coeff(self._digits, tmp_coeff, self._precrel) - element_reduce_coeff(self._digits, self._precrel, self.prime_pow) + digit_mul(tmp_digit, self._x, element_get_digit(self._y._digits, n - self._y._valuation)) + element_iadd_digit(self._digits, tmp_digit, self._precrel) + element_reduce_digit(self._digits, self._precrel, self.prime_pow) self._precrel += 1 return 0 @@ -695,7 +695,7 @@ cdef class LazyElement_div(LazyElement): if denom._precrel == 0: return ERROR_ABANDON self._valuation = num._valuation - denom._valuation - digit_inv(self._inverse, element_get_coeff(denom._digits, 0), self.prime_pow) + digit_inv(self._inverse, element_get_digit(denom._digits, 0), self.prime_pow) cdef LazyElement_muldigit a = lazy_class_muldigit(self._parent, self, num) cdef LazyElement_muldigit b = lazy_class_muldigit(self._parent, self, denom) error = b._next_c() @@ -720,8 +720,8 @@ cdef class LazyElement_div(LazyElement): if definition._valuation > val: self._valuation = definition._valuation - self._denom._valuation else: - digit = element_get_coeff(definition._digits, self._precrel) - element_set_coeff(self._digits, digit, self._precrel) + digit = element_get_digit(definition._digits, self._precrel) + element_set_digit(self._digits, digit, self._precrel) self._precrel += 1 return 0 @@ -766,19 +766,19 @@ cdef class LazyElement_sqrt(LazyElement): cdef LazyElement u, y, c, d if p == 2: - element_set_coeff_ui(self._digits, 1, 0) + element_set_digit_ui(self._digits, 1, 0) self._precrel = 1 if x._precrel == 1: error = x._next_c() if error: return error - if not digit_equal_ui(element_get_coeff(x._digits, 1), 0): + if not digit_equal_ui(element_get_digit(x._digits, 1), 0): return ERROR_NOTSQUARE if x._precrel == 2: error = x._next_c() if error: return error - if not digit_equal_ui(element_get_coeff(x._digits, 2), 0): + if not digit_equal_ui(element_get_digit(x._digits, 2), 0): return ERROR_NOTSQUARE zd = Integer(1) u = lazy_class_shift(parent, self, val + 2, True) @@ -788,10 +788,10 @@ cdef class LazyElement_sqrt(LazyElement): self._definition = y + c - d else: digit_init(digit) - if digit_sqrt(digit, element_get_coeff(x._digits, 0), self.prime_pow): + if digit_sqrt(digit, element_get_digit(x._digits, 0), self.prime_pow): digit_clear(digit) return ERROR_NOTSQUARE - element_set_coeff(self._digits, digit, 0) + element_set_digit(self._digits, digit, 0) self._precrel = 1 zd = digit_get_sage(digit) u = lazy_class_shift(parent, self, val + 1, True) @@ -814,7 +814,7 @@ cdef class LazyElement_sqrt(LazyElement): error = definition._jump_c(n+1) if error: return error - element_set_coeff(self._digits, element_get_coeff(definition._digits, self._precrel), self._precrel) + element_set_digit(self._digits, element_get_digit(definition._digits, self._precrel), self._precrel) self._precrel += 1 return 0 @@ -836,7 +836,7 @@ cdef class LazyElement_teichmuller(LazyElement): self._trivial = True self._valuation = maxordp return - element_set_coeff(self._digits, digit, 0) + element_set_digit(self._digits, digit, 0) self._trivial = digit_equal_ui(digit, 1) self._precrel += 1 @@ -874,7 +874,7 @@ cdef class LazyElement_teichmuller(LazyElement): cdef LazyElement_mul xn self._precrel += 1 xp._next_c() - element_set_coeff(self._digits, element_get_coeff(xp._digits, self._precrel - 1), self._precrel - 1) + element_set_digit(self._digits, element_get_digit(xp._digits, self._precrel - 1), self._precrel - 1) for xn in self._xns: error = xn._update_last_digit() if error: @@ -928,11 +928,11 @@ cdef class LazyElement_selfref(LazyElement): if diffval < 0: self._valuation = definition._valuation else: - digit = element_get_coeff(definition._digits, self._precrel + diffval) + digit = element_get_digit(definition._digits, self._precrel + diffval) if self._precrel == 0 and digit_is_zero(digit): self._valuation += 1 else: - element_set_coeff(self._digits, digit, self._precrel) + element_set_digit(self._digits, digit, self._precrel) self._precrel += 1 self._next = False return error From d9aa5d70ec02f4e03f1e434d9317669c5db08085 Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Mon, 11 Jan 2021 08:07:02 +0100 Subject: [PATCH 101/406] cdef inline classes --- src/sage/libs/linkages/padics/lazy/API.pxi | 2 +- src/sage/rings/padics/lazy_template.pxi | 2 +- src/sage/rings/padics/padic_lazy_element.pyx | 24 ++++++++++---------- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/sage/libs/linkages/padics/lazy/API.pxi b/src/sage/libs/linkages/padics/lazy/API.pxi index a33b0c14a38..b2cafd63d10 100644 --- a/src/sage/libs/linkages/padics/lazy/API.pxi +++ b/src/sage/libs/linkages/padics/lazy/API.pxi @@ -64,7 +64,7 @@ cdef inline void digit_quorem(cdigit quo, cdigit rem, cdigit a, PowComputer_clas pass cdef inline void digit_inv(cdigit res, cdigit a, PowComputer_class prime_pow): -_ pass + pass cdef bint digit_sqrt(cdigit_ptr ans, cdigit_ptr x, PowComputer_class prime_pow): pass diff --git a/src/sage/rings/padics/lazy_template.pxi b/src/sage/rings/padics/lazy_template.pxi index 73bd5dcddcf..60bd6073d2b 100644 --- a/src/sage/rings/padics/lazy_template.pxi +++ b/src/sage/rings/padics/lazy_template.pxi @@ -284,7 +284,7 @@ cdef class LazyElement(pAdicGenericElement): return other if isinstance(other, LazyElement_zero): return self - if isinstance(self, lazy_class_add): + if isinstance(self, LazyElement_add): summands = list((self)._summands) signs = list((self)._signs) else: diff --git a/src/sage/rings/padics/padic_lazy_element.pyx b/src/sage/rings/padics/padic_lazy_element.pyx index 582819efd32..06126524be4 100644 --- a/src/sage/rings/padics/padic_lazy_element.pyx +++ b/src/sage/rings/padics/padic_lazy_element.pyx @@ -1,15 +1,15 @@ include "sage/libs/linkages/padics/lazy/flint.pxi" include "lazy_template.pxi" -cdef type lazy_class_zero = pAdicLazyElement_zero -cdef type lazy_class_one = pAdicLazyElement_one -cdef type lazy_class_value = pAdicLazyElement_value -cdef type lazy_class_random = pAdicLazyElement_random -cdef type lazy_class_shift = pAdicLazyElement_shift -cdef type lazy_class_add = pAdicLazyElement_add -cdef type lazy_class_mul = pAdicLazyElement_mul -cdef type lazy_class_muldigit = pAdicLazyElement_muldigit -cdef type lazy_class_div = pAdicLazyElement_div -cdef type lazy_class_sqrt = pAdicLazyElement_sqrt -cdef type lazy_class_teichmuller = pAdicLazyElement_teichmuller -cdef type lazy_class_selfref = pAdicLazyElement_selfref +cdef inline type lazy_class_zero = pAdicLazyElement_zero +cdef inline type lazy_class_one = pAdicLazyElement_one +cdef inline type lazy_class_value = pAdicLazyElement_value +cdef inline type lazy_class_random = pAdicLazyElement_random +cdef inline type lazy_class_shift = pAdicLazyElement_shift +cdef inline type lazy_class_add = pAdicLazyElement_add +cdef inline type lazy_class_mul = pAdicLazyElement_mul +cdef inline type lazy_class_muldigit = pAdicLazyElement_muldigit +cdef inline type lazy_class_div = pAdicLazyElement_div +cdef inline type lazy_class_sqrt = pAdicLazyElement_sqrt +cdef inline type lazy_class_teichmuller = pAdicLazyElement_teichmuller +cdef inline type lazy_class_selfref = pAdicLazyElement_selfref From 2687c79eb52fffb4cb90485d60214c7daa57e931 Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Mon, 11 Jan 2021 14:23:18 +0100 Subject: [PATCH 102/406] move element_constructor --- src/sage/libs/linkages/padics/lazy/flint.pxi | 4 +- src/sage/rings/padics/lazy_template.pxi | 74 ++++++++++++++------ src/sage/rings/padics/padic_lazy.py | 71 +++++-------------- 3 files changed, 70 insertions(+), 79 deletions(-) diff --git a/src/sage/libs/linkages/padics/lazy/flint.pxi b/src/sage/libs/linkages/padics/lazy/flint.pxi index f3407fc7ded..61e1aa388cb 100644 --- a/src/sage/libs/linkages/padics/lazy/flint.pxi +++ b/src/sage/libs/linkages/padics/lazy/flint.pxi @@ -30,6 +30,8 @@ from sage.rings.padics.pow_computer_flint cimport PowComputer_flint from sage.ext.stdsage cimport PY_NEW +from sage.rings.finite_rings.finite_field_constructor import GF + cdef flint_rand_t flint_randstate flint_randinit(flint_randstate) @@ -103,7 +105,7 @@ cdef bint digit_sqrt(cdigit_ptr ans, cdigit_ptr x, PowComputer_class prime_pow): cdef Integer zp = digit_get_sage((prime_pow).fprime) try: k = GF(zp) - zans = ZZ(k(zx).sqrt(extend=False)) + zans = Integer(k(zx).sqrt(extend=False)) except ValueError: return 1 digit_set_sage(ans, zans) diff --git a/src/sage/rings/padics/lazy_template.pxi b/src/sage/rings/padics/lazy_template.pxi index 60bd6073d2b..5e4fab5599e 100644 --- a/src/sage/rings/padics/lazy_template.pxi +++ b/src/sage/rings/padics/lazy_template.pxi @@ -15,11 +15,8 @@ from sage.structure.element import coerce_binop from sage.structure.element cimport have_same_parent from sage.structure.coerce cimport coercion_model -from sage.rings.all import ZZ from sage.rings.integer cimport Integer from sage.rings.infinity import Infinity -from sage.rings.finite_rings.finite_field_constructor import GF -from sage.rings.finite_rings.integer_mod_ring import Integers from sage.rings.padics.pow_computer cimport PowComputer_class from sage.rings.padics.padic_generic_element cimport pAdicGenericElement @@ -28,7 +25,7 @@ from sage.rings.padics.padic_lazy_errors cimport * from sage.rings.padics.padic_lazy_errors import raise_error, error_to_str cdef long maxordp = (1L << (sizeof(long) * 8 - 2)) - 1 -MAXORDP = ZZ(maxordp) +MAXORDP = Integer(maxordp) cdef cdigit tmp_digit @@ -37,6 +34,45 @@ digit_init(tmp_digit) element_init(tmp_poly) +def element_constructor(parent, x): + xparent = x.parent() + if xparent is parent: + return x + elif isinstance(x, LazyElement): + if xparent.Element is parent.Element: + if not parent.is_field() and x.valuation() < 0: + raise ValueError("negative valuation") + return lazy_class_shift(parent, x, 0, False) + raise NotImplementedError + elif isinstance(x, pAdicGenericElement): + if parent._convert_map_from_(xparent): + if not parent.is_field() and x.valuation() < 0: + raise ValueError("negative valuation") + return lazy_class_value(parent, x.lift(), maxprec=x.precision_absolute()) + elif x == 0: + return parent._zero_element + elif x == 1: + return lazy_class_one(parent) + else: + try: + x = parent.exact_ring()(x) + return lazy_class_value(parent, x) + except (TypeError, ValueError): + pass + try: + x = parent.exact_field()(x) + num = x.numerator() + denom = x.denominator() + except (TypeError, ValueError, AttributeError): + pass + else: + if denom % parent.prime() == 0: + raise ValueError("negative valuation") + num = lazy_class_value(parent, num) + denom = lazy_class_value(parent, denom) + return lazy_class_div(parent, num, denom) + raise TypeError("unable to convert '%s' to a lazy %s-adic integer" % (x, parent.prime())) + cdef class LazyElement(pAdicGenericElement): def __cinit__(self): element_init(self._digits) @@ -86,11 +122,9 @@ cdef class LazyElement(pAdicGenericElement): error = self._jump_c(self._valuation + default_prec) else: error = self._jump_c(default_prec) - elif prec not in ZZ: - raise ValueError("precision must be an integer") else: permissive = False - prec = ZZ(prec) + prec = Integer(prec) if prec >= maxordp: raise OverflowError("beyond maximum precision (which is %s)" % maxordp) if prec < -maxordp: @@ -109,21 +143,15 @@ cdef class LazyElement(pAdicGenericElement): raise NotImplementedError("step is not allowed") if n.start is None: start = 0 - elif n.start in ZZ: - start = ZZ(n.start) else: - raise ValueError("invalid slice") + start = Integer(n.start) if n.stop is None: stop = None - elif n.stop in ZZ: - stop = ZZ(n.stop) else: - raise ValueError("invalid slice") + stop = Integer(n.stop) return ExpansionIter(self, start, stop) else: - if n not in ZZ: - raise IndexError("index must be an integer") - n = ZZ(n) + n = Integer(n) if n >= maxordp: raise OverflowError("beyond maximum precision (which is %s)" % maxordp) return self._digit(long(n)) @@ -212,12 +240,12 @@ cdef class LazyElement(pAdicGenericElement): return Infinity if self._valuation <= -maxordp: raise PrecisionError("no lower bound on the valuation is known") - return ZZ(self._precrel + self._valuation) + return Integer(self._precrel + self._valuation) def precision_relative(self): if self._valuation <= -maxordp: raise PrecisionError("no lower bound on the valuation is known") - return ZZ(self._precrel) + return Integer(self._precrel) cdef long valuation_c(self): return self._valuation @@ -229,7 +257,7 @@ cdef class LazyElement(pAdicGenericElement): raise PrecisionError("no lower bound on the valuation is known") if secure and self._precrel == 0: raise PrecisionError("cannot determine the valuation; try to increase the precision") - return ZZ(self._valuation) + return Integer(self._valuation) def unit_part(self): if self._precrel == 0: @@ -255,11 +283,11 @@ cdef class LazyElement(pAdicGenericElement): if field and prec == 1: return self._parent.residue_class_field()(ans) else: - return Integers(self.prime()**prec)(ans) + return self._parent.residue_ring(prec)(ans) def lift(self): if self._precrel == 0: - return ZZ(0) + return Integer(0) cdef celement digits element_get_slice(digits, self._digits, 0, self._precrel) cdef Integer ans = element_get_sage(digits, self.prime_pow) @@ -434,7 +462,7 @@ cdef class LazyElement_random(LazyElement): def __init__(self, parent, integral): LazyElement.__init__(self, parent) if not integral: - self._valuation = ZZ.random_element() + self._valuation = Integer.random_element() cdef int _next_c(self): cdef cdigit r @@ -971,7 +999,7 @@ cdef class ExpansionIter(object): def __len__(self): if self.stop < self.start: raise NotImplementedError("infinite sequence") - return ZZ(self.stop - self.start) + return Integer(self.stop - self.start) def __iter__(self): return self diff --git a/src/sage/rings/padics/padic_lazy.py b/src/sage/rings/padics/padic_lazy.py index a3486566ae9..4a018afaf20 100644 --- a/src/sage/rings/padics/padic_lazy.py +++ b/src/sage/rings/padics/padic_lazy.py @@ -132,12 +132,17 @@ from sage.rings.padics.padic_generic_element import pAdicGenericElement from sage.rings.padics.pow_computer_flint import PowComputer_flint -from sage.rings.padics.padic_lazy_element import * +from sage.rings.padics.padic_lazy_element import pAdicLazyElement +from sage.rings.padics.padic_lazy_element import pAdicLazyElement_zero +from sage.rings.padics.padic_lazy_element import pAdicLazyElement_random +from sage.rings.padics.padic_lazy_element import pAdicLazyElement_selfref +from sage.rings.padics.padic_lazy_element import pAdicLazyElement_teichmuller +from sage.rings.padics.padic_lazy_element import element_constructor class pAdicRingLazy(pAdicRingBaseGeneric): def __init__(self, p, prec, print_mode, names): - pAdicRingBaseGeneric.__init__(self, p, prec, print_mode, names, None) + pAdicRingBaseGeneric.__init__(self, p, prec, print_mode, names, pAdicLazyElement) self._zero_element = pAdicLazyElement_zero(self) self._default_prec = ZZ(prec) self.prime_pow = PowComputer_flint(p, 1, 1, 1, False) @@ -154,33 +159,11 @@ def default_prec(self): def precision_cap(self): return Infinity + def _an_element_(self): + return self._zero_element + def _element_constructor_(self, x): - if isinstance(x, pAdicLazyElement) and x.prime() == self.prime(): - if x.parent() is self: - return x - if not x.is_equal_at_precision(self._zero_element, 0): - raise ValueError("negative valuation") - return pAdicLazyElement_shift(self, x, 0, False) - elif isinstance(x, pAdicGenericElement) and x.parent().prime() == self.prime(): - if x.valuation() < 0: - raise ValueError("negative valuation") - return pAdicLazyElement_value(self, ZZ(x), maxprec=x.precision_absolute()) - elif x in ZZ: - if x == 0: - return self._zero_element - elif x == 1: - return pAdicLazyElement_one(self) - else: - return pAdicLazyElement_value(self, ZZ(x)) - elif x in QQ: - num = x.numerator() - denom = x.denominator() - if denom % self.prime() == 0: - raise ValueError("negative valuation") - num = pAdicLazyElement_value(self, num) - denom = pAdicLazyElement_value(self, denom) - return pAdicLazyElement_div(self, num, denom) - raise TypeError("unable to convert '%s' to a lazy %s-adic integer" % (x, self.prime())) + return element_constructor(self, x) def selfref(self, start_val=0): if start_val not in ZZ or start_val < 0: @@ -190,9 +173,6 @@ def selfref(self, start_val=0): raise OverflowError("valuation is too large (maximum is %s)" % MAXORDP) return pAdicLazyElement_selfref(self, start_val) - def _an_element_(self): - return self._zero_element - def random_element(self): return pAdicLazyElement_random(self, True) @@ -206,7 +186,7 @@ def teichmuller_system(self): class pAdicFieldLazy(pAdicFieldBaseGeneric): def __init__(self, p, prec, print_mode, names): - pAdicRingBaseGeneric.__init__(self, p, prec, print_mode, names, None) + pAdicRingBaseGeneric.__init__(self, p, prec, print_mode, names, pAdicLazyElement) self._zero_element = pAdicLazyElement_zero(self) self._default_prec = ZZ(prec) self.prime_pow = PowComputer_flint(p, 1, 1, 1, True) @@ -227,27 +207,11 @@ def _coerce_map_from_(self, R): if isinstance(R, pAdicRingLazy) and self is R.fraction_field(): return True + def _an_element_(self): + return self._zero_element + def _element_constructor_(self, x): - if isinstance(x, pAdicLazyElement) and x.prime() == self.prime(): - if x.parent() is self: - return x - return pAdicLazyElement_shift(self, x, 0, False) - elif isinstance(x, pAdicGenericElement) and x.parent().prime() == self.prime(): - val, u = x.val_unit() - return pAdicLazyElement_value(self, ZZ(u), shift=val, maxprec=x.precision_absolute()) - elif x in ZZ: - if x == 0: - return self._zero_element - elif x == 1: - return pAdicLazyElement_one(self) - else: - return pAdicLazyElement_value(self, ZZ(x)) - elif x in QQ: - x = QQ(x) - num = pAdicLazyElement_value(self, x.numerator()) - denom = pAdicLazyElement_value(self, x.denominator()) - return pAdicLazyElement_div(self, num, denom) - raise TypeError("unable to convert '%s' to a lazy %s-adic number" % (x, self.prime())) + return element_constructor(self, x) def selfref(self, start_val=0): if start_val not in ZZ: @@ -257,9 +221,6 @@ def selfref(self, start_val=0): raise OverflowError("valuation is too large (maximum is %s)" % MAXORDP) return pAdicLazyElement_selfref(self, start_val) - def _an_element_(self): - return self._zero_element - def random_element(self, integral=False): return pAdicLazyElement_random(self, integral) From b2b377aabdd18dbc87d324fc736ac7abeeec3be6 Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Mon, 11 Jan 2021 16:20:46 +0100 Subject: [PATCH 103/406] reorganize classes --- src/sage/rings/padics/factory.py | 5 +- src/sage/rings/padics/generic_nodes.py | 75 ++++++ src/sage/rings/padics/lazy_template.pxi | 80 ++----- src/sage/rings/padics/padic_base_generic.py | 6 +- src/sage/rings/padics/padic_base_leaves.py | 47 +++- src/sage/rings/padics/padic_lazy.py | 232 ------------------- src/sage/rings/padics/padic_lazy_element.pyx | 26 +-- src/sage/rings/padics/tutorial.py | 203 ++++++++++------ 8 files changed, 296 insertions(+), 378 deletions(-) delete mode 100644 src/sage/rings/padics/padic_lazy.py diff --git a/src/sage/rings/padics/factory.py b/src/sage/rings/padics/factory.py index b8ab5e6d3bc..30d38407919 100644 --- a/src/sage/rings/padics/factory.py +++ b/src/sage/rings/padics/factory.py @@ -42,10 +42,11 @@ pAdicRingFixedMod, pAdicRingFloatingPoint, pAdicRingLattice, + pAdicRingLazy, pAdicFieldCappedRelative, pAdicFieldFloatingPoint, - pAdicFieldLattice) -from .padic_lazy import pAdicRingLazy, pAdicFieldLazy + pAdicFieldLattice, + pAdicFieldLazy) from . import padic_printing ###################################################### diff --git a/src/sage/rings/padics/generic_nodes.py b/src/sage/rings/padics/generic_nodes.py index 23059cf3933..a94ae17fbc9 100644 --- a/src/sage/rings/padics/generic_nodes.py +++ b/src/sage/rings/padics/generic_nodes.py @@ -699,6 +699,81 @@ def convert_multiple(self, *elts): # We return the created elements return ans +class pAdicLazyGeneric(pAdicGeneric): + def _prec_type(self): + return "lazy" + + def is_lazy(self): + return True + + def default_prec(self): + return self._prec + + def precision_cap(self): + return infinity + + def _coerce_map_from_(self, R): + if isinstance(R, pAdicLazyGeneric) and self is R.fraction_field(): + return True + + def _element_constructor_(self, x): + parent = x.parent() + if parent is self: + return x + elif isinstance(parent, pAdicLazyGeneric): + if parent.Element is self.Element: + if not self.is_field() and x.valuation() < 0: + raise ValueError("negative valuation") + return self._element_classes['shift'](self, x, 0, False) + raise NotImplementedError + elif isinstance(parent, pAdicGeneric): + if not self.is_field() and x.valuation() < 0: + raise ValueError("negative valuation") + return self._element_classes['value'](self, x.lift(), maxprec=x.precision_absolute()) + elif x == 0: + return self._element_classes['zero'](self) + elif x == 1: + return self._element_classes['one'](self) + else: + try: + x = self.exact_ring()(x) + return self._element_classes['value'](self, x) + except (TypeError, ValueError): + pass + try: + x = self.exact_field()(x) + num = x.numerator() + denom = x.denominator() + except (TypeError, ValueError, AttributeError): + pass + else: + if denom % self.prime() == 0: + raise ValueError("negative valuation") + num = self._element_classes['value'](self, num) + denom = self._element_classes['value'](self, denom) + return self._element_classes['div'](self, num, denom) + raise TypeError("unable to convert '%s' to a lazy %s-adic integer" % (x, self.prime())) + + def selfref(self, start_val=0): + if start_val not in ZZ: + raise ValueError("valuation must be an integer") + start_val = ZZ(start_val) + if self.is_field() and start_val < 0: + raise ValueError("valuation must be nonnegative") + return self._element_classes['selfref'](self, start_val) + + def random_element(self, integral=False): + integral = integral or (not self.is_field()) + return self._element_classes['random'](self, integral) + + def teichmuller(self, x): + return self._element_classes['teichmuller'](self, ZZ(x)) + + def teichmuller_system(self): + R = self.residue_class_field() + return [ self.teichmuller(ZZ(i)) for i in R if i != 0 ] + + def is_pAdicRing(R): """ Returns ``True`` if and only if ``R`` is a `p`-adic ring (not a diff --git a/src/sage/rings/padics/lazy_template.pxi b/src/sage/rings/padics/lazy_template.pxi index 5e4fab5599e..2452adae4d5 100644 --- a/src/sage/rings/padics/lazy_template.pxi +++ b/src/sage/rings/padics/lazy_template.pxi @@ -25,7 +25,6 @@ from sage.rings.padics.padic_lazy_errors cimport * from sage.rings.padics.padic_lazy_errors import raise_error, error_to_str cdef long maxordp = (1L << (sizeof(long) * 8 - 2)) - 1 -MAXORDP = Integer(maxordp) cdef cdigit tmp_digit @@ -34,45 +33,6 @@ digit_init(tmp_digit) element_init(tmp_poly) -def element_constructor(parent, x): - xparent = x.parent() - if xparent is parent: - return x - elif isinstance(x, LazyElement): - if xparent.Element is parent.Element: - if not parent.is_field() and x.valuation() < 0: - raise ValueError("negative valuation") - return lazy_class_shift(parent, x, 0, False) - raise NotImplementedError - elif isinstance(x, pAdicGenericElement): - if parent._convert_map_from_(xparent): - if not parent.is_field() and x.valuation() < 0: - raise ValueError("negative valuation") - return lazy_class_value(parent, x.lift(), maxprec=x.precision_absolute()) - elif x == 0: - return parent._zero_element - elif x == 1: - return lazy_class_one(parent) - else: - try: - x = parent.exact_ring()(x) - return lazy_class_value(parent, x) - except (TypeError, ValueError): - pass - try: - x = parent.exact_field()(x) - num = x.numerator() - denom = x.denominator() - except (TypeError, ValueError, AttributeError): - pass - else: - if denom % parent.prime() == 0: - raise ValueError("negative valuation") - num = lazy_class_value(parent, num) - denom = lazy_class_value(parent, denom) - return lazy_class_div(parent, num, denom) - raise TypeError("unable to convert '%s' to a lazy %s-adic integer" % (x, parent.prime())) - cdef class LazyElement(pAdicGenericElement): def __cinit__(self): element_init(self._digits) @@ -298,7 +258,7 @@ cdef class LazyElement(pAdicGenericElement): def __rshift__(self, s): cdef slong shift = long(s) if shift: - return lazy_class_shift((self)._parent, self, shift, not (self)._parent.is_field()) + return element_class_shift((self)._parent, self, shift, not (self)._parent.is_field()) else: return self @@ -324,7 +284,7 @@ cdef class LazyElement(pAdicGenericElement): else: summands.append(other) signs.append(True) - return lazy_class_add(self._parent, summands, signs) + return element_class_add(self._parent, summands, signs) cpdef _sub_(self, other): cdef list summands @@ -345,7 +305,7 @@ cdef class LazyElement(pAdicGenericElement): else: summands.append(other) signs.append(False) - return lazy_class_add(self._parent, summands, signs) + return element_class_add(self._parent, summands, signs) cpdef _neg_(self): cdef list summands @@ -356,31 +316,31 @@ cdef class LazyElement(pAdicGenericElement): else: summands = [self] signs = [False] - return lazy_class_add(self._parent, summands, signs) + return element_class_add(self._parent, summands, signs) cpdef _mul_(self, other): if isinstance(self, LazyElement_zero) or isinstance(other, LazyElement_one): return self if isinstance(self, LazyElement_one) or isinstance(other, LazyElement_zero): return other - return lazy_class_mul(self._parent, self, other) + return element_class_mul(self._parent, self, other) cpdef _div_(self, other): if isinstance(other, LazyElement_zero): return ZeroDivisionError("cannot divide by zero") if isinstance(other, LazyElement_one): return self - return lazy_class_div(self._parent.fraction_field(), self, other) + return element_class_div(self._parent.fraction_field(), self, other) def __invert__(self): if isinstance(self, LazyElement_zero): return ZeroDivisionError("cannot divide by zero") if isinstance(self, LazyElement_one): return self - return lazy_class_div(self._parent.fraction_field(), self._parent.one(), self) + return element_class_div(self._parent.fraction_field(), self._parent.one(), self) def sqrt(self): - return lazy_class_sqrt(self._parent, self) + return element_class_sqrt(self._parent, self) # Assignations @@ -724,8 +684,8 @@ cdef class LazyElement_div(LazyElement): return ERROR_ABANDON self._valuation = num._valuation - denom._valuation digit_inv(self._inverse, element_get_digit(denom._digits, 0), self.prime_pow) - cdef LazyElement_muldigit a = lazy_class_muldigit(self._parent, self, num) - cdef LazyElement_muldigit b = lazy_class_muldigit(self._parent, self, denom) + cdef LazyElement_muldigit a = element_class_muldigit(self._parent, self, num) + cdef LazyElement_muldigit b = element_class_muldigit(self._parent, self, denom) error = b._next_c() if error or b._precrel != 1: return error | ERROR_UNEXPECTED @@ -809,10 +769,10 @@ cdef class LazyElement_sqrt(LazyElement): if not digit_equal_ui(element_get_digit(x._digits, 2), 0): return ERROR_NOTSQUARE zd = Integer(1) - u = lazy_class_shift(parent, self, val + 2, True) - y = lazy_class_shift(parent, x, val + 1, False) - c = lazy_class_value(parent, zd, shift=val-1) - d = lazy_class_shift(parent, u*u, -val-3, False) + u = element_class_shift(parent, self, val + 2, True) + y = element_class_shift(parent, x, val + 1, False) + c = element_class_value(parent, zd, shift=val-1) + d = element_class_shift(parent, u*u, -val-3, False) self._definition = y + c - d else: digit_init(digit) @@ -822,10 +782,10 @@ cdef class LazyElement_sqrt(LazyElement): element_set_digit(self._digits, digit, 0) self._precrel = 1 zd = digit_get_sage(digit) - u = lazy_class_shift(parent, self, val + 1, True) - y = lazy_class_shift(parent, x, 2*val + 2, False) - c = lazy_class_value(parent, zd*zd, shift=-2) - d = lazy_class_value(parent, 2*zd, shift=-val-2) + u = element_class_shift(parent, self, val + 1, True) + y = element_class_shift(parent, x, 2*val + 2, False) + c = element_class_value(parent, zd*zd, shift=-2) + d = element_class_value(parent, 2*zd, shift=-val-2) self._definition = (y + c - u*u) / d return 0 @@ -914,8 +874,10 @@ cdef class LazyElement_teichmuller(LazyElement): ########################### cdef class LazyElement_selfref(LazyElement): - def __init__(self, parent, valuation): + def __init__(self, parent, Integer valuation): LazyElement.__init__(self, parent) + if valuation >= maxordp: + raise OverflowError("valuation is too large (maximum is %s)" % maxordp) self._valuation = valuation self._definition = None self._next = False diff --git a/src/sage/rings/padics/padic_base_generic.py b/src/sage/rings/padics/padic_base_generic.py index 05de5b17bd7..16e7a528fb0 100644 --- a/src/sage/rings/padics/padic_base_generic.py +++ b/src/sage/rings/padics/padic_base_generic.py @@ -40,7 +40,11 @@ def __init__(self, p, prec, print_mode, names, element_class): sage: R = Zp(5) #indirect doctest """ - self.prime_pow = PowComputer(p, max(min(prec - 1, 30), 1), prec, self.is_field(), self._prec_type()) + if self.is_lazy(): + from sage.rings.padics.pow_computer_flint import PowComputer_flint + self.prime_pow = PowComputer_flint(p, 1, 1, 1, self.is_field()) + else: + self.prime_pow = PowComputer(p, max(min(prec - 1, 30), 1), prec, self.is_field(), self._prec_type()) pAdicGeneric.__init__(self, self, p, prec, print_mode, names, element_class) if self.is_field(): if self.is_capped_relative(): diff --git a/src/sage/rings/padics/padic_base_leaves.py b/src/sage/rings/padics/padic_base_leaves.py index a8eba1f3bf2..e2b1a112901 100644 --- a/src/sage/rings/padics/padic_base_leaves.py +++ b/src/sage/rings/padics/padic_base_leaves.py @@ -197,7 +197,8 @@ class names.:: pAdicCappedAbsoluteRingGeneric, \ pAdicFloatingPointRingGeneric, \ pAdicFloatingPointFieldGeneric, \ - pAdicLatticeGeneric + pAdicLatticeGeneric, \ + pAdicLazyGeneric from .padic_capped_relative_element import pAdicCappedRelativeElement from .padic_capped_absolute_element import pAdicCappedAbsoluteElement from .padic_fixed_mod_element import pAdicFixedModElement @@ -1095,3 +1096,47 @@ def random_element(self, prec=None, integral=False): if relcap < prec: prec = relcap return self._element_class(self, x*(p**val), prec=prec) + +# Lazy +###### + +class pAdicRingLazy(pAdicLazyGeneric, pAdicRingBaseGeneric): + def __init__(self, p, prec, print_mode, names): + from sage.rings.padics import padic_lazy_element + pAdicRingBaseGeneric.__init__(self, p, prec, print_mode, names, padic_lazy_element.pAdicLazyElement) + self._element_classes = { + 'generic': padic_lazy_element.pAdicLazyElement, + 'zero': padic_lazy_element.pAdicLazyElement_zero, + 'one': padic_lazy_element.pAdicLazyElement_one, + 'value': padic_lazy_element.pAdicLazyElement_value, + 'random': padic_lazy_element.pAdicLazyElement_random, + 'shift': padic_lazy_element.pAdicLazyElement_shift, + 'add': padic_lazy_element.pAdicLazyElement_add, + 'mul': padic_lazy_element.pAdicLazyElement_mul, + 'muldigit': padic_lazy_element.pAdicLazyElement_muldigit, + 'div': padic_lazy_element.pAdicLazyElement_div, + 'sqrt': padic_lazy_element.pAdicLazyElement_sqrt, + 'teichmuller': padic_lazy_element.pAdicLazyElement_teichmuller, + 'selfref': padic_lazy_element.pAdicLazyElement_selfref + } + +class pAdicFieldLazy(pAdicLazyGeneric, pAdicFieldBaseGeneric): + def __init__(self, p, prec, print_mode, names): + from sage.rings.padics import padic_lazy_element + pAdicFieldBaseGeneric.__init__(self, p, prec, print_mode, names, padic_lazy_element.pAdicLazyElement) + self._element_classes = { + 'generic': padic_lazy_element.pAdicLazyElement, + 'zero': padic_lazy_element.pAdicLazyElement_zero, + 'one': padic_lazy_element.pAdicLazyElement_one, + 'value': padic_lazy_element.pAdicLazyElement_value, + 'random': padic_lazy_element.pAdicLazyElement_random, + 'shift': padic_lazy_element.pAdicLazyElement_shift, + 'add': padic_lazy_element.pAdicLazyElement_add, + 'mul': padic_lazy_element.pAdicLazyElement_mul, + 'muldigit': padic_lazy_element.pAdicLazyElement_muldigit, + 'div': padic_lazy_element.pAdicLazyElement_div, + 'sqrt': padic_lazy_element.pAdicLazyElement_sqrt, + 'teichmuller': padic_lazy_element.pAdicLazyElement_teichmuller, + 'selfref': padic_lazy_element.pAdicLazyElement_selfref + } + diff --git a/src/sage/rings/padics/padic_lazy.py b/src/sage/rings/padics/padic_lazy.py deleted file mode 100644 index 4a018afaf20..00000000000 --- a/src/sage/rings/padics/padic_lazy.py +++ /dev/null @@ -1,232 +0,0 @@ -r""" -This module provides basic support for lazy p-adic integers. - - sage: R = ZpL(5, print_mode="digits") - sage: R - 5-adic Ring with lazy precision - -One creates elements as usual:: - - sage: a = R(17/42) - sage: a - ...00244200244200244201 - - sage: R.random_element() # random - ...21013213133412431402 - -By default, 20 digits of precision are printed. -If more digits are needed, one can increase the precision by using the -meth:`jump`:: - - sage: a.jump(30) - sage: a - ...244200244200244200244200244201 - -Standard operations are implemented:: - - sage: b = R(42/17) - - sage: a + b - ...03232011214322140002 - sage: a - b - ...42311334324023403400 - - sage: a * b - ...00000000000000000001 - sage: a / b - ...12442142113021233401 - -We observe again that only 20 digits are printed, even if the precision -on the operands is higher:: - - sage: b.jump(30) - sage: a - ...244200244200244200244200244201 - sage: b - ...104201213402432310420121340301 - sage: a / b - ...12442142113021233401 - -If more digits are needed, we have to create a new variable:: - - sage: c = a / b - sage: c.jump(40) - sage: c - ...4230030104200433321312442142113021233401 - -Note that this automatically increases the precision on `a` and `b`:: - - sage: a - ...4200244200244200244200244200244200244201 - sage: b - ...2134024323104201213402432310420121340301 - -:: - -A quite interesting feature with lazy p-adics is the possibility to -create (in somes cases) self-referent numbers. Here is an example. -We first declare a new variable as follows:: - - sage: x = R.selfref() - sage: x - ...?.0 - -We then write down an equation satisfied by `x`:: - - sage: x == 1 + 5*x^2 - True - -The variable `x` now contains the unique solution of the above equation:: - - sage: x - ...04222412141121000211 - -This works because the `n`-th digit of the right hand size of the -defining equation only involves the `i`-th digits of `x` with `i < n` -(this is due to the factor `5`). - -As a comparison, the following produces an error:: - - sage: y = R.selfref() - sage: y == 1 + 3*y^2 - Traceback (most recent call last): - ... - RecursionError: definition looks circular - -Self-referent definitions also work with systems of equations:: - - sage: u = R.selfref() - sage: v = R.selfref() - sage: w = R.selfref() - - sage: u == 1 + 2*v + 3*w^2 + 5*u*v*w - True - sage: v == 2 + 4*w + sqrt(1 + 5*u + 10*v + 15*w) - True - sage: w == 3 + 25*(u*v + v*w + u*w) - True - - sage: u - ...31203130103131131433 - sage: v - ...33441043031103114240 - sage: w - ...30212422041102444403 - -""" - -# **************************************************************************** -# Copyright (C) 2021 Xavier Caruso -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 2 of the License, or -# (at your option) any later version. -# http://www.gnu.org/licenses/ -# **************************************************************************** - - -from sage.rings.all import ZZ, QQ -from sage.rings.infinity import Infinity -from sage.rings.padics.generic_nodes import pAdicRingBaseGeneric, pAdicFieldBaseGeneric -from sage.rings.padics.padic_generic_element import pAdicGenericElement -from sage.rings.padics.pow_computer_flint import PowComputer_flint - -from sage.rings.padics.padic_lazy_element import pAdicLazyElement -from sage.rings.padics.padic_lazy_element import pAdicLazyElement_zero -from sage.rings.padics.padic_lazy_element import pAdicLazyElement_random -from sage.rings.padics.padic_lazy_element import pAdicLazyElement_selfref -from sage.rings.padics.padic_lazy_element import pAdicLazyElement_teichmuller -from sage.rings.padics.padic_lazy_element import element_constructor - - -class pAdicRingLazy(pAdicRingBaseGeneric): - def __init__(self, p, prec, print_mode, names): - pAdicRingBaseGeneric.__init__(self, p, prec, print_mode, names, pAdicLazyElement) - self._zero_element = pAdicLazyElement_zero(self) - self._default_prec = ZZ(prec) - self.prime_pow = PowComputer_flint(p, 1, 1, 1, False) - - def _prec_type(self): - return "lazy" - - def is_lazy(self): - return True - - def default_prec(self): - return self._default_prec - - def precision_cap(self): - return Infinity - - def _an_element_(self): - return self._zero_element - - def _element_constructor_(self, x): - return element_constructor(self, x) - - def selfref(self, start_val=0): - if start_val not in ZZ or start_val < 0: - raise ValueError("valuation must be a nonnegative integer") - start_val = ZZ(start_val) - if start_val >= MAXORDP: - raise OverflowError("valuation is too large (maximum is %s)" % MAXORDP) - return pAdicLazyElement_selfref(self, start_val) - - def random_element(self): - return pAdicLazyElement_random(self, True) - - def teichmuller(self, x): - return pAdicLazyElement_teichmuller(self, ZZ(x)) - - def teichmuller_system(self): - R = self.residue_class_field() - return [ self.teichmuller(ZZ(i)) for i in R if i != 0 ] - - -class pAdicFieldLazy(pAdicFieldBaseGeneric): - def __init__(self, p, prec, print_mode, names): - pAdicRingBaseGeneric.__init__(self, p, prec, print_mode, names, pAdicLazyElement) - self._zero_element = pAdicLazyElement_zero(self) - self._default_prec = ZZ(prec) - self.prime_pow = PowComputer_flint(p, 1, 1, 1, True) - - def _prec_type(self): - return "lazy" - - def is_lazy(self): - return True - - def default_prec(self): - return self._default_prec - - def precision_cap(self): - return Infinity - - def _coerce_map_from_(self, R): - if isinstance(R, pAdicRingLazy) and self is R.fraction_field(): - return True - - def _an_element_(self): - return self._zero_element - - def _element_constructor_(self, x): - return element_constructor(self, x) - - def selfref(self, start_val=0): - if start_val not in ZZ: - raise ValueError("valuation must be an integer") - start_val = ZZ(start_val) - if start_val >= MAXORDP: - raise OverflowError("valuation is too large (maximum is %s)" % MAXORDP) - return pAdicLazyElement_selfref(self, start_val) - - def random_element(self, integral=False): - return pAdicLazyElement_random(self, integral) - - def teichmuller(self, x): - return pAdicLazyElement_teichmuller(self, ZZ(x)) - - def teichmuller_system(self): - R = self.residue_class_field() - return [ self.teichmuller(ZZ(i)) for i in R if i != 0 ] diff --git a/src/sage/rings/padics/padic_lazy_element.pyx b/src/sage/rings/padics/padic_lazy_element.pyx index 06126524be4..83c8eb58f69 100644 --- a/src/sage/rings/padics/padic_lazy_element.pyx +++ b/src/sage/rings/padics/padic_lazy_element.pyx @@ -1,15 +1,15 @@ +cdef inline type element_class_zero = pAdicLazyElement_zero +cdef inline type element_class_one = pAdicLazyElement_one +cdef inline type element_class_value = pAdicLazyElement_value +cdef inline type element_class_random = pAdicLazyElement_random +cdef inline type element_class_shift = pAdicLazyElement_shift +cdef inline type element_class_add = pAdicLazyElement_add +cdef inline type element_class_mul = pAdicLazyElement_mul +cdef inline type element_class_muldigit = pAdicLazyElement_muldigit +cdef inline type element_class_div = pAdicLazyElement_div +cdef inline type element_class_sqrt = pAdicLazyElement_sqrt +cdef inline type element_class_teichmuller = pAdicLazyElement_teichmuller +cdef inline type element_class_selfref = pAdicLazyElement_selfref + include "sage/libs/linkages/padics/lazy/flint.pxi" include "lazy_template.pxi" - -cdef inline type lazy_class_zero = pAdicLazyElement_zero -cdef inline type lazy_class_one = pAdicLazyElement_one -cdef inline type lazy_class_value = pAdicLazyElement_value -cdef inline type lazy_class_random = pAdicLazyElement_random -cdef inline type lazy_class_shift = pAdicLazyElement_shift -cdef inline type lazy_class_add = pAdicLazyElement_add -cdef inline type lazy_class_mul = pAdicLazyElement_mul -cdef inline type lazy_class_muldigit = pAdicLazyElement_muldigit -cdef inline type lazy_class_div = pAdicLazyElement_div -cdef inline type lazy_class_sqrt = pAdicLazyElement_sqrt -cdef inline type lazy_class_teichmuller = pAdicLazyElement_teichmuller -cdef inline type lazy_class_selfref = pAdicLazyElement_selfref diff --git a/src/sage/rings/padics/tutorial.py b/src/sage/rings/padics/tutorial.py index cf8c3684383..0616ab29f01 100644 --- a/src/sage/rings/padics/tutorial.py +++ b/src/sage/rings/padics/tutorial.py @@ -337,75 +337,138 @@ Note that the precision cap increased by a factor of 5, since the ramification index of this extension over `\ZZ_p` is 5. -""" -# Lazy Rings and Fields -# --------------------- - -# The model for lazy elements is quite different from any of the -# other types of `p`-adics. In addition to storing a finite -# approximation, one also stores a method for increasing the -# precision. The interface supports two ways to do this: -# ``set_precision_relative`` and ``set_precision_absolute``. - -# :: - -# #sage: R = Zp(5, prec = 10, type = 'lazy', print_mode = 'series', halt = 30) -# #sage: R -# #Lazy 5-adic Ring -# #sage: R.precision_cap() -# #10 -# #sage: R.halting_parameter() -# #30 -# #sage: K = Qp(5, type = 'lazy') -# #sage: K.precision_cap() -# #20 -# #sage: K.halting_parameter() -# #40 - -# There are two parameters that are set at the creation of a lazy -# ring or field. The first is ``prec``, which controls the precision -# to which elements are initially computed. When computing with lazy -# rings, sometimes situations arise where the unsolvability of the -# halting problem gives us problems. For example, - -# :: - -# #sage: a = R(16) -# #sage: b = a.log().exp() - a -# #sage: b -# #O(5^10) -# #sage: b.valuation() -# #Traceback (most recent call last): -# #... -# #HaltingError: Stopped computing sum: set halting parameter higher if you want computation to continue - -# Setting the halting parameter controls to what absolute precision -# one computes in such a situation. - -# The interesting feature of lazy elements is that one can perform -# computations with them, discover that the answer does not have the -# desired precision, and then ask for more precision. For example, - -# :: - -# #sage: a = R(6).log() * 15 -# #sage: b = a.exp() -# #sage: c = b / R(15).exp() -# #sage: c -# #1 + 2*5 + 4*5^2 + 3*5^3 + 2*5^4 + 3*5^5 + 5^6 + 5^10 + O(5^11) -# #sage: c.set_precision_absolute(15) -# #sage: c -# #1 + 2*5 + 4*5^2 + 3*5^3 + 2*5^4 + 3*5^5 + 5^6 + 5^10 + 4*5^11 + 2*5^12 + 4*5^13 + 3*5^14 + O(5^15) - -# There can be a performance penalty to using lazy `p`-adics -# in this way. When one does computations with them, the computer -# constructs an expression tree. As you compute, values of these -# elements are cached, and the overhead is reasonably low (though -# obviously higher than for a fixed modulus element for example). But -# when you set the precision, the computer has to reset precision -# throughout the expression tree for that element, and thus setting -# precision can take the same order of magnitude of time as doing the -# initial computation. However, lazy `p`-adics can be quite -# useful when experimenting. +Lazy Rings and Fields +--------------------- + +The model for lazy elements is quite different from any of the +other types of `p`-adics. In addition to storing a finite +approximation, one also stores a method for increasing the +precision. + +Lazy p-adic rings are created by the constructor :func:`ZpL`:: + + sage: R = ZpL(5, print_mode="digits") + sage: R + 5-adic Ring with lazy precision + +Observe that the precision is not capped on `R`:: + + sage: R.precision_cap() + +Infinity + +However, a default precision is settled. This is the precision +at which all the computations will be carried out before printing:: + + sage: R.default_prec() + 20 + +One creates elements as usual:: + + sage: a = R(17/42) + sage: a + ...00244200244200244201 + + sage: R.random_element() # random + ...21013213133412431402 + +Here we notice that 20 digits (that is the default precision) are printed. +If more digits are needed, one can increase the precision by using the +meth:`jump`:: + + sage: a.jump(30) + sage: a + ...244200244200244200244200244201 + +We can of course perform all standard operations:: + + sage: b = R(42/17) + + sage: a + b + ...03232011214322140002 + sage: a - b + ...42311334324023403400 + + sage: a * b + ...00000000000000000001 + sage: a / b + ...12442142113021233401 + +We observe again that only 20 digits are printed. +This is actually the case even if the precision on the operands is higher:: + + sage: b.jump(30) + sage: a + ...244200244200244200244200244201 + sage: b + ...104201213402432310420121340301 + sage: a / b + ...12442142113021233401 + +If more digits are needed, we need to create a new variable:: + + sage: c = a / b + sage: c.jump(40) + sage: c + ...4230030104200433321312442142113021233401 + +Note that this automatically increases the precision on `a` and `b`:: + + sage: a + ...4200244200244200244200244200244200244201 + sage: b + ...2134024323104201213402432310420121340301 + +:: + +A quite interesting feature with lazy p-adics is the possibility to +create (in somes cases) self-referent numbers. Here is an example. +We first declare a new variable as follows:: + + sage: x = R.selfref() + sage: x + ...?.0 + +We then write down an equation satisfied by `x`:: + sage: x == 1 + 5*x^2 + True + +The variable `x` now contains the unique solution of the above equation:: + + sage: x + ...04222412141121000211 + +This works because the `n`-th digit of the right hand size of the +defining equation only involves the `i`-th digits of `x` with `i < n` +(this is due to the factor `5`). + +As a comparison, the following produces an error:: + + sage: y = R.selfref() + sage: y == 1 + 3*y^2 + Traceback (most recent call last): + ... + RecursionError: definition looks circular + +Self-referent definitions also work with systems of equations:: + + sage: u = R.selfref() + sage: v = R.selfref() + sage: w = R.selfref() + + sage: u == 1 + 2*v + 3*w^2 + 5*u*v*w + True + sage: v == 2 + 4*w + sqrt(1 + 5*u + 10*v + 15*w) + True + sage: w == 3 + 25*(u*v + v*w + u*w) + True + + sage: u + ...31203130103131131433 + sage: v + ...33441043031103114240 + sage: w + ...30212422041102444403 + +""" From 6f39d6eef2959ceaa637ea9f4c12cdc7c7b4b6d5 Mon Sep 17 00:00:00 2001 From: Michael Jung Date: Thu, 21 Jan 2021 17:44:58 +0100 Subject: [PATCH 104/406] Trac #30311: apply set_immutable to all chart functions --- src/sage/manifolds/scalarfield.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/sage/manifolds/scalarfield.py b/src/sage/manifolds/scalarfield.py index 73a0843ed83..868e855fc44 100644 --- a/src/sage/manifolds/scalarfield.py +++ b/src/sage/manifolds/scalarfield.py @@ -3605,6 +3605,8 @@ def set_immutable(self): """ for rst in self._restrictions.values(): rst.set_immutable() + for func in self._express.values(): + func.set_immutable() super().set_immutable() @cached_method From 56001b479f8d40afcbcc8e16e3bc8df8b554f41f Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Fri, 22 Jan 2021 01:40:56 +0100 Subject: [PATCH 105/406] slices --- src/sage/libs/linkages/padics/lazy/API.pxi | 3 + src/sage/libs/linkages/padics/lazy/flint.pxi | 3 + src/sage/rings/padics/generic_nodes.py | 2 +- src/sage/rings/padics/lazy_template.pxi | 304 +++++++++++++----- .../rings/padics/lazy_template_header.pxi | 40 ++- src/sage/rings/padics/padic_base_leaves.py | 6 +- src/sage/rings/padics/padic_lazy_element.pxd | 9 +- src/sage/rings/padics/padic_lazy_element.pyx | 3 +- 8 files changed, 264 insertions(+), 106 deletions(-) diff --git a/src/sage/libs/linkages/padics/lazy/API.pxi b/src/sage/libs/linkages/padics/lazy/API.pxi index b2cafd63d10..dfca2980048 100644 --- a/src/sage/libs/linkages/padics/lazy/API.pxi +++ b/src/sage/libs/linkages/padics/lazy/API.pxi @@ -12,6 +12,9 @@ # Operations on digits (intended to be small elements in the exact subring) ########################################################################### +cdef cdigit digit_zero +digit_init(digit_zero) + cdef inline void digit_init(cdigit a): pass diff --git a/src/sage/libs/linkages/padics/lazy/flint.pxi b/src/sage/libs/linkages/padics/lazy/flint.pxi index 61e1aa388cb..f3eb437ee31 100644 --- a/src/sage/libs/linkages/padics/lazy/flint.pxi +++ b/src/sage/libs/linkages/padics/lazy/flint.pxi @@ -40,6 +40,9 @@ flint_randinit(flint_randstate) # Operations on digits (intended to be small elements in the exact subring) ########################################################################### +cdef cdigit digit_zero +digit_init(digit_zero) + cdef inline void digit_init(cdigit a): fmpz_init(a) diff --git a/src/sage/rings/padics/generic_nodes.py b/src/sage/rings/padics/generic_nodes.py index a94ae17fbc9..6cdf15b59c9 100644 --- a/src/sage/rings/padics/generic_nodes.py +++ b/src/sage/rings/padics/generic_nodes.py @@ -724,7 +724,7 @@ def _element_constructor_(self, x): if parent.Element is self.Element: if not self.is_field() and x.valuation() < 0: raise ValueError("negative valuation") - return self._element_classes['shift'](self, x, 0, False) + return self._element_classes['copy'](self, x) raise NotImplementedError elif isinstance(parent, pAdicGeneric): if not self.is_field() and x.valuation() < 0: diff --git a/src/sage/rings/padics/lazy_template.pxi b/src/sage/rings/padics/lazy_template.pxi index 2452adae4d5..927e35b3feb 100644 --- a/src/sage/rings/padics/lazy_template.pxi +++ b/src/sage/rings/padics/lazy_template.pxi @@ -8,6 +8,7 @@ # http://www.gnu.org/licenses/ # **************************************************************************** +from libc.stdlib cimport malloc, free from sage.libs.flint.types cimport slong from sage.libs.gmp.mpz cimport mpz_sizeinbase, mpz_tstbit @@ -15,6 +16,7 @@ from sage.structure.element import coerce_binop from sage.structure.element cimport have_same_parent from sage.structure.coerce cimport coercion_model +from sage.rings.all import ZZ from sage.rings.integer cimport Integer from sage.rings.infinity import Infinity @@ -34,12 +36,6 @@ element_init(tmp_poly) cdef class LazyElement(pAdicGenericElement): - def __cinit__(self): - element_init(self._digits) - - def __dealloc__(self): - element_clear(self._digits) - def __init__(self, parent): pAdicGenericElement.__init__(self, parent) self.prime_pow = self._parent.prime_pow @@ -52,6 +48,15 @@ cdef class LazyElement(pAdicGenericElement): def prime(self): return self._parent.prime() + cdef cdigit_ptr _getdigit_relative(self, slong i): + pass + + cdef cdigit_ptr _getdigit_absolute(self, slong i): + pass + + cdef void _getslice_relative(self, celement_ptr slice, slong start, slong length): + pass + cdef int _next_c(self): raise NotImplementedError("must be implemented in subclasses") @@ -95,6 +100,11 @@ cdef class LazyElement(pAdicGenericElement): else: raise_error(error, permissive) + cdef Integer _digit(self, slong i): + self.jump(i+1) + cdef cdigit_ptr coeff = self._getdigit_absolute(i) + return digit_get_sage(coeff) + def expansion(self, n=None): if n is None: return ExpansionIter(self, self._valuation, self._valuation + self._precrel) @@ -114,12 +124,36 @@ cdef class LazyElement(pAdicGenericElement): n = Integer(n) if n >= maxordp: raise OverflowError("beyond maximum precision (which is %s)" % maxordp) - return self._digit(long(n)) + return self._digit(n) - cdef Integer _digit(self, slong i): - self.jump(i+1) - cdef cdigit_ptr coeff = element_get_digit(self._digits, i - self._valuation) - return digit_get_sage(coeff) + def slice(self, start=None, stop=None, bounded=True): + cdef slong shift = 0 + if start is None: + start = -maxordp + elif start >= maxordp: + raise OverflowError("beyond maximum precision (which is %s)" % maxordp) + else: + shift = start + if stop is None: + stop = maxordp + elif stop >= maxordp: + raise OverflowError("beyond maximum precision (which is %s)" % maxordp) + cdef LazyElement ans = element_class_slice(self._parent, self, start, stop, shift, bounded) + if bounded and stop < maxordp: + error = ans._jump_c(stop - shift) + raise_error(error, permissive=True) + return ans + + def __getitem__(self, n): + if isinstance(n, slice): + if n.step is not None: + raise NotImplementedError("step is not allowed") + return self.slice(n.start, n.stop) + else: + n = Integer(n) + if n >= maxordp: + raise OverflowError("beyond maximum precision (which is %s)" % maxordp) + return self._digit(n) def _repr_(self): error = self.jump(quiet=True) @@ -154,7 +188,7 @@ cdef class LazyElement(pAdicGenericElement): raise_error(error, permissive) if error: # not enough precision return True - if not digit_equal(element_get_digit(self._digits, i), element_get_digit(right._digits, j)): + if not digit_equal(self._getdigit_relative(i), right._getdigit_relative(j)): return False i += 1 j += 1 @@ -238,7 +272,7 @@ cdef class LazyElement(pAdicGenericElement): if self._valuation < 0: raise ValueError("element must have non-negative valuation in order to compute residue") cdef celement digits - element_get_slice(digits, self._digits, 0, prec - self._valuation) + self._getslice_relative(digits, 0, prec - self._valuation) cdef Integer ans = element_get_sage(digits, self.prime_pow) if field and prec == 1: return self._parent.residue_class_field()(ans) @@ -249,16 +283,21 @@ cdef class LazyElement(pAdicGenericElement): if self._precrel == 0: return Integer(0) cdef celement digits - element_get_slice(digits, self._digits, 0, self._precrel) + self._getslice_relative(digits, 0, self._precrel) cdef Integer ans = element_get_sage(digits, self.prime_pow) if self._valuation: ans *= self._parent.prime() ** self._valuation return ans def __rshift__(self, s): + cdef slong start cdef slong shift = long(s) if shift: - return element_class_shift((self)._parent, self, shift, not (self)._parent.is_field()) + if (self)._parent.is_field(): + start = -maxordp + else: + start = shift + return element_class_slice((self)._parent, self, start, maxordp, shift, False) else: return self @@ -343,6 +382,22 @@ cdef class LazyElement(pAdicGenericElement): return element_class_sqrt(self._parent, self) +cdef class LazyElement_init(LazyElement): + def __cinit__(self): + element_init(self._digits) + + def __dealloc__(self): + element_clear(self._digits) + + cdef cdigit_ptr _getdigit_relative(self, slong i): + return element_get_digit(self._digits, i) + + cdef cdigit_ptr _getdigit_absolute(self, slong i): + return element_get_digit(self._digits, i - self._valuation) + + cdef void _getslice_relative(self, celement_ptr slice, slong start, slong length): + element_get_slice(slice, self._digits, start, length) + # Assignations ############## @@ -353,6 +408,15 @@ cdef class LazyElement_zero(LazyElement): LazyElement.__init__(self, parent) self._valuation = maxordp + cdef cdigit_ptr _getdigit_relative(self, slong i): + return digit_zero + + cdef cdigit_ptr _getdigit_absolute(self, slong i): + return digit_zero + + cdef void _getslice_relative(self, celement_ptr slice, slong start, slong length): + element_init(slice) + cdef int _jump_c(self, slong prec): return 0 @@ -362,7 +426,7 @@ cdef class LazyElement_zero(LazyElement): # One -cdef class LazyElement_one(LazyElement): +cdef class LazyElement_one(LazyElement_init): def __init__(self, parent): LazyElement.__init__(self, parent) element_set_digit_ui(self._digits, 1, 0) @@ -377,9 +441,42 @@ cdef class LazyElement_one(LazyElement): return 0 +# Copy + +cdef class LazyElement_copy(LazyElement): + def __init__(self, parent, LazyElement x): + LazyElement.__init__(self, parent) + self._x = x + self._valuation = x._valuation + self._precrel = x._precrel + + cdef cdigit_ptr _getdigit_relative(self, slong i): + return self._x._getdigit_relative(i) + + cdef cdigit_ptr _getdigit_absolute(self, slong i): + return self._x._getdigit_absolute(i) + + cdef void _getslice_relative(self, celement_ptr slice, slong start, slong length): + self._x._getslice_relative(slice, start, length) + + cdef int _jump_c(self, slong prec): + cdef LazyElement x = self._x + cdef int error = x._jump_c(prec) + self._valuation = x._valuation + self._precrel = x._precrel + return error + + cdef int _next_c(self): + cdef LazyElement x = self._x + cdef int error = x._next_c() + self._valuation = x._valuation + self._precrel = x._precrel + return error + + # Value -cdef class LazyElement_value(LazyElement): +cdef class LazyElement_value(LazyElement_init): def __init__(self, parent, Integer value, slong shift=0, maxprec=None): LazyElement.__init__(self, parent) element_set_digit_sage(self._digits, value, 0) @@ -407,22 +504,22 @@ cdef class LazyElement_value(LazyElement): if (self._maxprec is not None) and (self._valuation + self._precrel >= self._maxprec): return ERROR_PRECISION element_reduce_digit(self._digits, self._precrel, self.prime_pow) - if self._precrel == 0 and digit_is_zero(element_get_digit(self._digits, 0)): + if self._precrel == 0 and digit_is_zero(self._getdigit_relative(0)): self._valuation += 1 element_shift_right(self._digits) else: self._precrel += 1 - self._finished = digit_is_zero(element_get_digit(self._digits, self._precrel)) + self._finished = digit_is_zero(self._getdigit_relative(self._precrel)) return 0 # Random -cdef class LazyElement_random(LazyElement): +cdef class LazyElement_random(LazyElement_init): def __init__(self, parent, integral): LazyElement.__init__(self, parent) if not integral: - self._valuation = Integer.random_element() + self._valuation = ZZ.random_element() cdef int _next_c(self): cdef cdigit r @@ -441,40 +538,74 @@ cdef class LazyElement_random(LazyElement): # Shift -cdef class LazyElement_shift(LazyElement): - def __init__(self, parent, LazyElement x, slong s, bint truncate): - cdef slong start = 0 - cdef celement digits; +cdef class LazyElement_slice(LazyElement): + def __init__(self, parent, LazyElement x, slong start, slong stop, slong shift, bint bounded): + # self[0] = x[shift] LazyElement.__init__(self, parent) self._x = x - self._shift = s - if truncate and x._valuation < s: - start = s - x._valuation - self._valuation = 0 - self._precrel = x._precrel - start + self._start = start + self._stop = stop + self._shift = shift + self._valuation = max(x._valuation, start) - shift + self._precrel = min(x._precrel + x._valuation, stop) - self._valuation - shift + while self._precrel > 0 and digit_is_zero(self._getdigit_relative(0)): + self._precrel -= 1 + self._valuation += 1 + if bounded: + self._maxprec = stop - shift else: - self._valuation = x._valuation - s - self._precrel = x._precrel - if self._precrel < 0: - self._precrel = 0 + self._maxprec = maxordp + + cdef cdigit_ptr _getdigit_relative(self, slong i): + return self._getdigit_absolute(i + self._valuation) + + cdef cdigit_ptr _getdigit_absolute(self, slong i): + cdef slong j = i + self._shift + if j < self._start or j >= self._stop: + return digit_zero else: - element_get_slice(digits, x._digits, start, self._precrel) - element_set(self._digits, digits) + return self._x._getdigit_absolute(j) - cdef int _next_c(self): - cdef slong n = self._valuation + self._precrel + cdef void _getslice_relative(self, celement_ptr slice, slong start, slong length): cdef LazyElement x = self._x - cdef cdigit_ptr digit - cdef int error + cdef slong s = start + self._valuation + self._shift + cdef slong start_absolute = max(self._start, s) + cdef slong stop_absolute = min(self._stop, s + length) + x._getslice_relative(slice, start_absolute - x._valuation, stop_absolute - start_absolute) - error = x._jump_c(n + self._shift + 1) - if error: + cdef int _jump_c(self, slong prec): + cdef LazyElement x = self._x + cdef int error = 0 + if prec <= self._valuation + self._precrel: + return 0 + if prec > self._maxprec: + prec = self._maxprec + error = ERROR_PRECISION + cdef int errorx = x._jump_c(min(prec + self._shift, self._stop)) + if errorx: + prec = x._valuation + x._precrel - self._shift + if self._precrel == 0: + while self._valuation < prec and digit_is_zero(self._getdigit_relative(0)): + self._valuation += 1 + self._precrel = prec - self._valuation + if errorx: + return errorx + else: return error - digit = element_get_digit(x._digits, n + self._shift - x._valuation) - element_set_digit(self._digits, digit, self._precrel) - if self._precrel == 0 and digit_is_zero(element_get_digit(self._digits, 0)): + + cdef int _next_c(self): + cdef int error + cdef slong n = self._precrel + self._valuation + if n >= self._maxprec: + return ERROR_PRECISION + n += self._shift + if n <= self._stop: + print(n+1) + error = self._x._jump_c(n + 1) + if error: + return error + if self._precrel == 0 and (n > self._stop or digit_is_zero(self._getdigit_relative(0))): self._valuation += 1 - element_shift_right(self._digits) else: self._precrel += 1 return 0 @@ -482,7 +613,7 @@ cdef class LazyElement_shift(LazyElement): # Addition -cdef class LazyElement_add(LazyElement): +cdef class LazyElement_add(LazyElement_init): def __init__(self, parent, summands, signs): LazyElement.__init__(self, parent) self._valuation = min((summand)._valuation for summand in summands) @@ -502,13 +633,13 @@ cdef class LazyElement_add(LazyElement): cdef slong i for i in range(len(self._summands)): summand = self._summands[i] - coeff = element_get_digit(summand._digits, n - summand._valuation) + coeff = summand._getdigit_absolute(n) if self._signs[i]: element_iadd_digit(self._digits, coeff, n - self._valuation) else: element_isub_digit(self._digits, coeff, n - self._valuation) element_reduce_digit(self._digits, self._precrel, self.prime_pow) - if self._precrel == 0 and digit_is_zero(element_get_digit(self._digits, 0)): + if self._precrel == 0 and digit_is_zero(self._getdigit_relative(0)): self._valuation += 1 element_shift_right(self._digits) else: @@ -519,7 +650,7 @@ cdef class LazyElement_add(LazyElement): # Multiplication -cdef class LazyElement_mul(LazyElement): +cdef class LazyElement_mul(LazyElement_init): def __cinit__(self): digit_init(self._lastdigit_x) digit_init(self._lastdigit_y) @@ -553,12 +684,12 @@ cdef class LazyElement_mul(LazyElement): return error n = self._precrel - digit_set(self._lastdigit_x, element_get_digit(x._digits, n)) - digit_set(self._lastdigit_y, element_get_digit(y._digits, n)) - digit_mul(tmp_digit, element_get_digit(x._digits, 0), self._lastdigit_y) + digit_set(self._lastdigit_x, x._getdigit_relative(n)) + digit_set(self._lastdigit_y, y._getdigit_relative(n)) + digit_mul(tmp_digit, x._getdigit_relative(0), self._lastdigit_y) element_iadd_digit(self._digits, tmp_digit, n) if n: - digit_mul(tmp_digit, self._lastdigit_x, element_get_digit(y._digits, 0)) + digit_mul(tmp_digit, self._lastdigit_x, y._getdigit_relative(0)) element_iadd_digit(self._digits, tmp_digit, n) cdef slong m = n + 2 @@ -567,13 +698,13 @@ cdef class LazyElement_mul(LazyElement): while (m & 1 == 0) and (m > 3): m >>= 1 len <<= 1 - element_get_slice(slicex, x._digits, len - 1, len) - element_get_slice(slicey, y._digits, (m-1)*len - 1, len) + x._getslice_relative(slicex, len - 1, len) + y._getslice_relative(slicey, (m-1)*len - 1, len) element_mul(tmp_poly, slicex, slicey) element_iadd_slice(self._digits, tmp_poly, n) if m > 2: - element_get_slice(slicex, x._digits, (m-1)*len - 1, len) - element_get_slice(slicey, y._digits, len - 1, len) + x._getslice_relative(slicex, (m-1)*len - 1, len) + y._getslice_relative(slicey, len - 1, len) element_mul(tmp_poly, slicex, slicey) element_iadd_slice(self._digits, tmp_poly, n) @@ -595,14 +726,14 @@ cdef class LazyElement_mul(LazyElement): len <<= 1 len -= 1 - digit_sub(tmp_digit, element_get_digit(x._digits, n), self._lastdigit_x) - element_get_slice(slice, y._digits, 0, len) + digit_sub(tmp_digit, x._getdigit_relative(n), self._lastdigit_x) + y._getslice_relative(slice, 0, len) element_scalarmul(tmp_poly, slice, tmp_digit) element_iadd_slice(self._digits, tmp_poly, n) if m == 2: len -= 1 - digit_sub(tmp_digit, element_get_digit(y._digits, n), self._lastdigit_y) - element_get_slice(slice, x._digits, 0, len) + digit_sub(tmp_digit, y._getdigit_relative(n), self._lastdigit_y) + x._getslice_relative(slice, 0, len) element_scalarmul(tmp_poly, slice, tmp_digit) element_iadd_slice(self._digits, tmp_poly, n) if m == 2: @@ -610,12 +741,12 @@ cdef class LazyElement_mul(LazyElement): element_iadd_digit(self._digits, tmp_digit, 2*len) element_reduce_digit(self._digits, n, self.prime_pow) - digit_set(self._lastdigit_x, element_get_digit(x._digits, n)) - digit_set(self._lastdigit_y, element_get_digit(y._digits, n)) + digit_set(self._lastdigit_x, x._getdigit_relative(n)) + digit_set(self._lastdigit_y, y._getdigit_relative(n)) return 0 -cdef class LazyElement_muldigit(LazyElement): +cdef class LazyElement_muldigit(LazyElement_init): # compute x*y where x is assumed to be in (0,p) def __init__(self, parent, LazyElement_div x, LazyElement y): LazyElement.__init__(self, parent) @@ -628,7 +759,7 @@ cdef class LazyElement_muldigit(LazyElement): cdef int error = self._y._jump_c(n+1) if error: return error - digit_mul(tmp_digit, self._x, element_get_digit(self._y._digits, n - self._y._valuation)) + digit_mul(tmp_digit, self._x, self._y._getdigit_absolute(n)) element_iadd_digit(self._digits, tmp_digit, self._precrel) element_reduce_digit(self._digits, self._precrel, self.prime_pow) self._precrel += 1 @@ -645,7 +776,7 @@ cdef class LazyElement_muldigit(LazyElement): # Division -cdef class LazyElement_div(LazyElement): +cdef class LazyElement_div(LazyElement_init): def __cinit__(self): digit_init(self._inverse) @@ -683,14 +814,11 @@ cdef class LazyElement_div(LazyElement): if denom._precrel == 0: return ERROR_ABANDON self._valuation = num._valuation - denom._valuation - digit_inv(self._inverse, element_get_digit(denom._digits, 0), self.prime_pow) + digit_inv(self._inverse, denom._getdigit_relative(0), self.prime_pow) cdef LazyElement_muldigit a = element_class_muldigit(self._parent, self, num) cdef LazyElement_muldigit b = element_class_muldigit(self._parent, self, denom) - error = b._next_c() - if error or b._precrel != 1: - return error | ERROR_UNEXPECTED - b._erase_first_digit() - self._definition = a - b * self + cdef LazyElement_slice c = element_class_slice(self._parent, b, denom._valuation + 1, maxordp, 0, False) + self._definition = a - c * self return 0 cdef int _next_c(self): @@ -708,7 +836,7 @@ cdef class LazyElement_div(LazyElement): if definition._valuation > val: self._valuation = definition._valuation - self._denom._valuation else: - digit = element_get_digit(definition._digits, self._precrel) + digit = definition._getdigit_relative(self._precrel) element_set_digit(self._digits, digit, self._precrel) self._precrel += 1 return 0 @@ -716,7 +844,7 @@ cdef class LazyElement_div(LazyElement): # Square root -cdef class LazyElement_sqrt(LazyElement): +cdef class LazyElement_sqrt(LazyElement_init): def __init__(self, parent, LazyElement x): LazyElement.__init__(self, parent) #if parent.prime() == 2: @@ -760,30 +888,30 @@ cdef class LazyElement_sqrt(LazyElement): error = x._next_c() if error: return error - if not digit_equal_ui(element_get_digit(x._digits, 1), 0): + if not digit_equal_ui(x._getdigit_relative(1), 0): return ERROR_NOTSQUARE if x._precrel == 2: error = x._next_c() if error: return error - if not digit_equal_ui(element_get_digit(x._digits, 2), 0): + if not digit_equal_ui(x._getdigit_relative(2), 0): return ERROR_NOTSQUARE zd = Integer(1) - u = element_class_shift(parent, self, val + 2, True) - y = element_class_shift(parent, x, val + 1, False) + u = element_class_slice(parent, self, val + 2, maxordp, val + 2, False) + y = element_class_slice(parent, x, -maxordp, maxordp, val + 1, False) c = element_class_value(parent, zd, shift=val-1) - d = element_class_shift(parent, u*u, -val-3, False) + d = element_class_slice(parent, u*u, -maxordp, maxordp, -val-3, False) self._definition = y + c - d else: digit_init(digit) - if digit_sqrt(digit, element_get_digit(x._digits, 0), self.prime_pow): + if digit_sqrt(digit, x._getdigit_relative(0), self.prime_pow): digit_clear(digit) return ERROR_NOTSQUARE element_set_digit(self._digits, digit, 0) self._precrel = 1 zd = digit_get_sage(digit) - u = element_class_shift(parent, self, val + 1, True) - y = element_class_shift(parent, x, 2*val + 2, False) + u = element_class_slice(parent, self, val + 1, maxordp, val + 1, False) + y = element_class_slice(parent, x, -maxordp, maxordp, 2*val + 2, False) c = element_class_value(parent, zd*zd, shift=-2) d = element_class_value(parent, 2*zd, shift=-val-2) self._definition = (y + c - u*u) / d @@ -802,7 +930,7 @@ cdef class LazyElement_sqrt(LazyElement): error = definition._jump_c(n+1) if error: return error - element_set_digit(self._digits, element_get_digit(definition._digits, self._precrel), self._precrel) + element_set_digit(self._digits, definition._getdigit_relative(self._precrel), self._precrel) self._precrel += 1 return 0 @@ -812,7 +940,7 @@ cdef class LazyElement_sqrt(LazyElement): # Teichmüller lifts -cdef class LazyElement_teichmuller(LazyElement): +cdef class LazyElement_teichmuller(LazyElement_init): def __init__(self, parent, Integer xbar): LazyElement.__init__(self, parent) cdef cdigit digit @@ -862,7 +990,7 @@ cdef class LazyElement_teichmuller(LazyElement): cdef LazyElement_mul xn self._precrel += 1 xp._next_c() - element_set_digit(self._digits, element_get_digit(xp._digits, self._precrel - 1), self._precrel - 1) + element_set_digit(self._digits, xp._getdigit_relative(self._precrel - 1), self._precrel - 1) for xn in self._xns: error = xn._update_last_digit() if error: @@ -873,7 +1001,7 @@ cdef class LazyElement_teichmuller(LazyElement): # Self-referent definitions ########################### -cdef class LazyElement_selfref(LazyElement): +cdef class LazyElement_selfref(LazyElement_init): def __init__(self, parent, Integer valuation): LazyElement.__init__(self, parent) if valuation >= maxordp: @@ -918,7 +1046,7 @@ cdef class LazyElement_selfref(LazyElement): if diffval < 0: self._valuation = definition._valuation else: - digit = element_get_digit(definition._digits, self._precrel + diffval) + digit = definition._getdigit_relative(self._precrel + diffval) if self._precrel == 0 and digit_is_zero(digit): self._valuation += 1 else: diff --git a/src/sage/rings/padics/lazy_template_header.pxi b/src/sage/rings/padics/lazy_template_header.pxi index 4cd691436bb..be8ab7842c4 100644 --- a/src/sage/rings/padics/lazy_template_header.pxi +++ b/src/sage/rings/padics/lazy_template_header.pxi @@ -6,54 +6,70 @@ from sage.rings.padics.padic_generic_element cimport pAdicGenericElement cdef class LazyElement(pAdicGenericElement): - cdef celement _digits cdef slong _valuation cdef slong _precrel cdef PowComputer_class prime_pow + cdef cdigit_ptr _getdigit_relative(self, slong i) + cdef cdigit_ptr _getdigit_absolute(self, slong i) + cdef void _getslice_relative(self, celement_ptr slice, slong start, slong length) + cdef int _jump_c(self, slong prec) cdef int _next_c(self) cdef Integer _digit(self, slong i) cdef bint _is_equal(self, LazyElement right, slong prec, bint permissive) except -1 +cdef class LazyElement_init(LazyElement): + cdef celement _digits + cdef class LazyElement_zero(LazyElement): pass -cdef class LazyElement_one(LazyElement): +cdef class LazyElement_one(LazyElement_init): pass -cdef class LazyElement_value(LazyElement): +cdef class LazyElement_copy(LazyElement): + cdef LazyElement _x + +cdef class LazyElement_value(LazyElement_init): cdef slong _maxprec cdef slong _shift cdef bint _finished -cdef class LazyElement_random(LazyElement): +cdef class LazyElement_random(LazyElement_init): pass -cdef class LazyElement_shift(LazyElement): +cdef class LazyElement_slice(LazyElement): + cdef LazyElement _x + cdef slong _start + cdef slong _stop + cdef slong _shift + cdef slong _maxprec + +cdef class LazyElement_shift(LazyElement_init): cdef LazyElement _x cdef slong _shift -cdef class LazyElement_add(LazyElement): +cdef class LazyElement_add(LazyElement_init): cdef list _summands cdef list _signs -cdef class LazyElement_mul(LazyElement): +cdef class LazyElement_mul(LazyElement_init): cdef LazyElement _x cdef cdigit _lastdigit_x cdef LazyElement _y cdef cdigit _lastdigit_y cdef int _update_last_digit(self) -cdef class LazyElement_muldigit(LazyElement): +cdef class LazyElement_muldigit(LazyElement_init): cdef cdigit_ptr _x cdef LazyElement _y cdef void _erase_first_digit(self) -cdef class LazyElement_div(LazyElement): +cdef class LazyElement_div(LazyElement_init): cdef slong _maxprec cdef cdigit _inverse cdef LazyElement _num @@ -61,20 +77,20 @@ cdef class LazyElement_div(LazyElement): cdef LazyElement _definition cdef int _bootstrap_c(self) -cdef class LazyElement_sqrt(LazyElement): +cdef class LazyElement_sqrt(LazyElement_init): cdef slong _maxprec cdef LazyElement _x cdef LazyElement _definition cdef int _bootstrap_c(self) -cdef class LazyElement_teichmuller(LazyElement): +cdef class LazyElement_teichmuller(LazyElement_init): cdef bint _trivial cdef list _xns cdef LazyElement _xbar cdef LazyElement _xp -cdef class LazyElement_selfref(LazyElement): +cdef class LazyElement_selfref(LazyElement_init): cdef LazyElement _definition cdef bint _next cpdef set(self, LazyElement definition) diff --git a/src/sage/rings/padics/padic_base_leaves.py b/src/sage/rings/padics/padic_base_leaves.py index e2b1a112901..9f03f24c170 100644 --- a/src/sage/rings/padics/padic_base_leaves.py +++ b/src/sage/rings/padics/padic_base_leaves.py @@ -1108,9 +1108,10 @@ def __init__(self, p, prec, print_mode, names): 'generic': padic_lazy_element.pAdicLazyElement, 'zero': padic_lazy_element.pAdicLazyElement_zero, 'one': padic_lazy_element.pAdicLazyElement_one, + 'copy': padic_lazy_element.pAdicLazyElement_copy, 'value': padic_lazy_element.pAdicLazyElement_value, 'random': padic_lazy_element.pAdicLazyElement_random, - 'shift': padic_lazy_element.pAdicLazyElement_shift, + 'slice': padic_lazy_element.pAdicLazyElement_slice, 'add': padic_lazy_element.pAdicLazyElement_add, 'mul': padic_lazy_element.pAdicLazyElement_mul, 'muldigit': padic_lazy_element.pAdicLazyElement_muldigit, @@ -1128,9 +1129,10 @@ def __init__(self, p, prec, print_mode, names): 'generic': padic_lazy_element.pAdicLazyElement, 'zero': padic_lazy_element.pAdicLazyElement_zero, 'one': padic_lazy_element.pAdicLazyElement_one, + 'copy': padic_lazy_element.pAdicLazyElement_copy, 'value': padic_lazy_element.pAdicLazyElement_value, 'random': padic_lazy_element.pAdicLazyElement_random, - 'shift': padic_lazy_element.pAdicLazyElement_shift, + 'slice': padic_lazy_element.pAdicLazyElement_slice, 'add': padic_lazy_element.pAdicLazyElement_add, 'mul': padic_lazy_element.pAdicLazyElement_mul, 'muldigit': padic_lazy_element.pAdicLazyElement_muldigit, diff --git a/src/sage/rings/padics/padic_lazy_element.pxd b/src/sage/rings/padics/padic_lazy_element.pxd index fbf1a90041b..b0e1907c35a 100644 --- a/src/sage/rings/padics/padic_lazy_element.pxd +++ b/src/sage/rings/padics/padic_lazy_element.pxd @@ -1,8 +1,10 @@ -from sage.libs.flint.types cimport fmpz, fmpz_t, fmpz_poly_t +from sage.libs.flint.types cimport fmpz, fmpz_t +from sage.libs.flint.types cimport fmpz_poly_struct, fmpz_poly_t ctypedef fmpz_t cdigit ctypedef fmpz* cdigit_ptr ctypedef fmpz_poly_t celement +ctypedef fmpz_poly_struct* celement_ptr include "lazy_template_header.pxi" @@ -17,13 +19,16 @@ cdef class pAdicLazyElement_zero(LazyElement_zero): cdef class pAdicLazyElement_one(LazyElement_one): pass +cdef class pAdicLazyElement_copy(LazyElement_copy): + pass + cdef class pAdicLazyElement_value(LazyElement_value): pass cdef class pAdicLazyElement_random(LazyElement_random): pass -cdef class pAdicLazyElement_shift(LazyElement_shift): +cdef class pAdicLazyElement_slice(LazyElement_slice): pass cdef class pAdicLazyElement_add(LazyElement_add): diff --git a/src/sage/rings/padics/padic_lazy_element.pyx b/src/sage/rings/padics/padic_lazy_element.pyx index 83c8eb58f69..db817b3e5de 100644 --- a/src/sage/rings/padics/padic_lazy_element.pyx +++ b/src/sage/rings/padics/padic_lazy_element.pyx @@ -1,8 +1,9 @@ cdef inline type element_class_zero = pAdicLazyElement_zero cdef inline type element_class_one = pAdicLazyElement_one +cdef inline type element_class_copy = pAdicLazyElement_copy cdef inline type element_class_value = pAdicLazyElement_value cdef inline type element_class_random = pAdicLazyElement_random -cdef inline type element_class_shift = pAdicLazyElement_shift +cdef inline type element_class_slice = pAdicLazyElement_slice cdef inline type element_class_add = pAdicLazyElement_add cdef inline type element_class_mul = pAdicLazyElement_mul cdef inline type element_class_muldigit = pAdicLazyElement_muldigit From 4818fefbd370507e4627bdfc81de61d83ece5e2b Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Fri, 22 Jan 2021 22:11:00 +0100 Subject: [PATCH 106/406] init jump --- src/sage/rings/padics/generic_nodes.py | 10 +- src/sage/rings/padics/lazy_template.pxi | 164 +++++++++++------- .../rings/padics/lazy_template_header.pxi | 11 +- src/sage/rings/padics/padic_base_leaves.py | 3 +- src/sage/rings/padics/padic_lazy_element.pxd | 4 +- 5 files changed, 121 insertions(+), 71 deletions(-) diff --git a/src/sage/rings/padics/generic_nodes.py b/src/sage/rings/padics/generic_nodes.py index 6cdf15b59c9..f8e9d7c5743 100644 --- a/src/sage/rings/padics/generic_nodes.py +++ b/src/sage/rings/padics/generic_nodes.py @@ -700,6 +700,12 @@ def convert_multiple(self, *elts): return ans class pAdicLazyGeneric(pAdicGeneric): + def __init__(self): + if self._prec and not self.is_field(): + self._zeroprec = self.change(field=True, prec=0) + else: + self._zeroprec = self + def _prec_type(self): return "lazy" @@ -747,7 +753,7 @@ def _element_constructor_(self, x): except (TypeError, ValueError, AttributeError): pass else: - if denom % self.prime() == 0: + if not self.is_field() and denom % self.prime() == 0: raise ValueError("negative valuation") num = self._element_classes['value'](self, num) denom = self._element_classes['value'](self, denom) @@ -758,7 +764,7 @@ def selfref(self, start_val=0): if start_val not in ZZ: raise ValueError("valuation must be an integer") start_val = ZZ(start_val) - if self.is_field() and start_val < 0: + if (not self.is_field()) and start_val < 0: raise ValueError("valuation must be nonnegative") return self._element_classes['selfref'](self, start_val) diff --git a/src/sage/rings/padics/lazy_template.pxi b/src/sage/rings/padics/lazy_template.pxi index 927e35b3feb..129ccb33c0e 100644 --- a/src/sage/rings/padics/lazy_template.pxi +++ b/src/sage/rings/padics/lazy_template.pxi @@ -42,6 +42,25 @@ cdef class LazyElement(pAdicGenericElement): self._valuation = 0 self._precrel = 0 + cdef int _init_jump(self) except -1: + cdef slong default_prec = self._parent.default_prec() + if not default_prec: + return 0 + cdef int error + if self.prime_pow.in_field: + error = 0 + if self._valuation <= -maxordp: + error = self._next_c() + if not error: + prec = self._valuation + default_prec + error = self._jump_c(prec) + if not error and self._valuation < prec: + error = self._jump_c(self._valuation + default_prec) + else: + error = self._jump_c(default_prec) + raise_error(error, True) + return error + cpdef bint _is_base_elt(self, p) except -1: return True @@ -54,7 +73,7 @@ cdef class LazyElement(pAdicGenericElement): cdef cdigit_ptr _getdigit_absolute(self, slong i): pass - cdef void _getslice_relative(self, celement_ptr slice, slong start, slong length): + cdef void _getslice_relative(self, celement slice, slong start, slong length): pass cdef int _next_c(self): @@ -72,7 +91,16 @@ cdef class LazyElement(pAdicGenericElement): return 0 return ERROR_OVERFLOW - def jump(self, prec=None, quiet=False): + def jump_absolute(self, prec=None): + if prec is None: + permissive = True + prec = self._parent.default_prec() + else: + permissive = False + error = self._jump_c(prec) + raise_error(error, permissive) + + def jump_relative(self, prec=None, abort=True): if prec is None: permissive = True default_prec = self._parent.default_prec() @@ -80,28 +108,20 @@ cdef class LazyElement(pAdicGenericElement): if self._valuation <= -maxordp: error = self._next_c() if not error: - if self.prime_pow.in_field: + while self._precrel == 0: prec = self._valuation + default_prec error = self._jump_c(prec) - if not error and self._valuation < prec: - error = self._jump_c(self._valuation + default_prec) - else: - error = self._jump_c(default_prec) + if abort or error: + break + if not error and self._valuation < prec: + error = self._jump_c(self._valuation + default_prec) else: permissive = False - prec = Integer(prec) - if prec >= maxordp: - raise OverflowError("beyond maximum precision (which is %s)" % maxordp) - if prec < -maxordp: - prec = -maxordp error = self._jump_c(prec) - if quiet: - return error - else: - raise_error(error, permissive) + raise_error(error, permissive) cdef Integer _digit(self, slong i): - self.jump(i+1) + self._jump_c(i+1) cdef cdigit_ptr coeff = self._getdigit_absolute(i) return digit_get_sage(coeff) @@ -156,10 +176,6 @@ cdef class LazyElement(pAdicGenericElement): return self._digit(n) def _repr_(self): - error = self.jump(quiet=True) - s = error_to_str(error, permissive=True) - if s is not None: - return s if self._valuation <= -maxordp: return "valuation not known" return pAdicGenericElement._repr_(self) @@ -382,6 +398,16 @@ cdef class LazyElement(pAdicGenericElement): return element_class_sqrt(self._parent, self) +cdef class LazyElement_abandon(LazyElement): + def __init__(self): + self._valuation = -maxordp + + cdef int _next_c(self): + return ERROR_ABANDON + +cdef lazyelement_abandon = LazyElement_abandon() + + cdef class LazyElement_init(LazyElement): def __cinit__(self): element_init(self._digits) @@ -395,9 +421,11 @@ cdef class LazyElement_init(LazyElement): cdef cdigit_ptr _getdigit_absolute(self, slong i): return element_get_digit(self._digits, i - self._valuation) - cdef void _getslice_relative(self, celement_ptr slice, slong start, slong length): + cdef void _getslice_relative(self, celement slice, slong start, slong length): element_get_slice(slice, self._digits, start, length) + + # Assignations ############## @@ -414,7 +442,7 @@ cdef class LazyElement_zero(LazyElement): cdef cdigit_ptr _getdigit_absolute(self, slong i): return digit_zero - cdef void _getslice_relative(self, celement_ptr slice, slong start, slong length): + cdef void _getslice_relative(self, celement slice, slong start, slong length): element_init(slice) cdef int _jump_c(self, slong prec): @@ -430,6 +458,7 @@ cdef class LazyElement_one(LazyElement_init): def __init__(self, parent): LazyElement.__init__(self, parent) element_set_digit_ui(self._digits, 1, 0) + self._precrel = self._parent.default_prec() cdef int _jump_c(self, slong prec): if self._precrel < prec: @@ -456,7 +485,7 @@ cdef class LazyElement_copy(LazyElement): cdef cdigit_ptr _getdigit_absolute(self, slong i): return self._x._getdigit_absolute(i) - cdef void _getslice_relative(self, celement_ptr slice, slong start, slong length): + cdef void _getslice_relative(self, celement slice, slong start, slong length): self._x._getslice_relative(slice, start, length) cdef int _jump_c(self, slong prec): @@ -486,6 +515,7 @@ cdef class LazyElement_value(LazyElement_init): self._maxprec = maxprec self._shift = self._valuation = shift self._finished = False + self._init_jump() cdef int _jump_c(self, slong prec): if not self._finished: @@ -520,6 +550,7 @@ cdef class LazyElement_random(LazyElement_init): LazyElement.__init__(self, parent) if not integral: self._valuation = ZZ.random_element() + self._init_jump() cdef int _next_c(self): cdef cdigit r @@ -548,6 +579,8 @@ cdef class LazyElement_slice(LazyElement): self._shift = shift self._valuation = max(x._valuation, start) - shift self._precrel = min(x._precrel + x._valuation, stop) - self._valuation - shift + if self._precrel < 0: + self._precrel = 0 while self._precrel > 0 and digit_is_zero(self._getdigit_relative(0)): self._precrel -= 1 self._valuation += 1 @@ -566,7 +599,7 @@ cdef class LazyElement_slice(LazyElement): else: return self._x._getdigit_absolute(j) - cdef void _getslice_relative(self, celement_ptr slice, slong start, slong length): + cdef void _getslice_relative(self, celement slice, slong start, slong length): cdef LazyElement x = self._x cdef slong s = start + self._valuation + self._shift cdef slong start_absolute = max(self._start, s) @@ -582,8 +615,7 @@ cdef class LazyElement_slice(LazyElement): prec = self._maxprec error = ERROR_PRECISION cdef int errorx = x._jump_c(min(prec + self._shift, self._stop)) - if errorx: - prec = x._valuation + x._precrel - self._shift + prec = max(self._valuation, x._valuation + x._precrel - self._shift) if self._precrel == 0: while self._valuation < prec and digit_is_zero(self._getdigit_relative(0)): self._valuation += 1 @@ -600,8 +632,7 @@ cdef class LazyElement_slice(LazyElement): return ERROR_PRECISION n += self._shift if n <= self._stop: - print(n+1) - error = self._x._jump_c(n + 1) + error = self._x._jump_c(n+1) if error: return error if self._precrel == 0 and (n > self._stop or digit_is_zero(self._getdigit_relative(0))): @@ -619,6 +650,7 @@ cdef class LazyElement_add(LazyElement_init): self._valuation = min((summand)._valuation for summand in summands) self._summands = summands self._signs = signs + self._init_jump() cdef int _next_c(self): cdef LazyElement summand @@ -664,6 +696,7 @@ cdef class LazyElement_mul(LazyElement_init): self._x = x self._y = y self._valuation = self._x._valuation + self._y._valuation + self._init_jump() cdef int _next_c(self): global tmp_digit, tmp_poly @@ -753,6 +786,7 @@ cdef class LazyElement_muldigit(LazyElement_init): self._x = x._inverse self._y = y self._valuation = y._valuation + self._init_jump() cdef int _next_c(self): cdef slong n = self._valuation + self._precrel @@ -790,18 +824,17 @@ cdef class LazyElement_div(LazyElement_init): if denom._valuation <= -maxordp: self._maxprec = maxordp + 1 else: - self._maxprec = denom._valuation + self._parent.default_prec() + self._maxprec = denom._valuation + max(1, self._parent.default_prec()) cdef int error = self._bootstrap_c() - if error & ERROR_DIVISION: - raise ZeroDivisionError("cannot divide by something indistinguishable from zero") if error: self._valuation = -maxordp + else: + self._init_jump() cdef int _bootstrap_c(self): cdef int error cdef LazyElement num = self._num cdef LazyElement denom = self._denom - cdef cdigit gcd while denom._valuation < self._maxprec and denom._precrel == 0: error = denom._next_c() @@ -810,15 +843,19 @@ cdef class LazyElement_div(LazyElement_init): error |= ERROR_DIVISION return error if self._maxprec > maxordp and denom._valuation > -maxordp: - self._maxprec = denom._valuation + self._parent.default_prec() + self._maxprec = denom._valuation + max(1, self._parent.default_prec()) if denom._precrel == 0: return ERROR_ABANDON + self._valuation = num._valuation - denom._valuation digit_inv(self._inverse, denom._getdigit_relative(0), self.prime_pow) - cdef LazyElement_muldigit a = element_class_muldigit(self._parent, self, num) - cdef LazyElement_muldigit b = element_class_muldigit(self._parent, self, denom) - cdef LazyElement_slice c = element_class_slice(self._parent, b, denom._valuation + 1, maxordp, 0, False) - self._definition = a - c * self + self._definition = lazyelement_abandon + cdef parent = self._parent._zeroprec + cdef LazyElement a = element_class_muldigit(parent, self, num) + cdef LazyElement b = element_class_muldigit(parent, self, denom) + cdef LazyElement c = element_class_slice(parent, b, denom._valuation + 1, maxordp, 0, False) + cdef LazyElement d = element_class_mul(parent, c, self) + self._definition = element_class_add(parent, [a,d], [True,False]) return 0 cdef int _next_c(self): @@ -856,7 +893,7 @@ cdef class LazyElement_sqrt(LazyElement_init): else: self._valuation = x._valuation >> 1 self._maxprec = x._valuation + 2*self._parent.default_prec() - cdef int error = self._bootstrap_c() + cdef int error = self._init_jump() if error & ERROR_NOTSQUARE: raise ValueError("not a square") @@ -875,7 +912,7 @@ cdef class LazyElement_sqrt(LazyElement_init): if x._valuation & 1 != 0: return ERROR_NOTSQUARE - cdef parent = self._parent + cdef parent = self._parent._zeroprec cdef slong val = self._valuation cdef cdigit digit cdef Integer zd, p = self.prime_pow.prime @@ -897,10 +934,11 @@ cdef class LazyElement_sqrt(LazyElement_init): if not digit_equal_ui(x._getdigit_relative(2), 0): return ERROR_NOTSQUARE zd = Integer(1) - u = element_class_slice(parent, self, val + 2, maxordp, val + 2, False) + self._definition = lazyelement_abandon + u = element_class_slice(parent, self, val + 2, maxordp, val, False) y = element_class_slice(parent, x, -maxordp, maxordp, val + 1, False) c = element_class_value(parent, zd, shift=val-1) - d = element_class_slice(parent, u*u, -maxordp, maxordp, -val-3, False) + d = element_class_slice(parent, u*u, -maxordp, maxordp, -val + 1, False) self._definition = y + c - d else: digit_init(digit) @@ -910,10 +948,11 @@ cdef class LazyElement_sqrt(LazyElement_init): element_set_digit(self._digits, digit, 0) self._precrel = 1 zd = digit_get_sage(digit) - u = element_class_slice(parent, self, val + 1, maxordp, val + 1, False) - y = element_class_slice(parent, x, -maxordp, maxordp, 2*val + 2, False) - c = element_class_value(parent, zd*zd, shift=-2) - d = element_class_value(parent, 2*zd, shift=-val-2) + self._definition = lazyelement_abandon + u = element_class_slice(parent, self, val + 1, maxordp, val, False) + y = element_class_slice(parent, x, -maxordp, maxordp, 2*val, False) + c = element_class_value(parent, zd*zd) + d = element_class_value(parent, 2*zd, shift=-val) self._definition = (y + c - u*u) / d return 0 @@ -969,11 +1008,12 @@ cdef class LazyElement_teichmuller(LazyElement_init): self._xns.append(xn) i -= 1 self._xp = xn - error = self._xp._next_c() - if error or self._xp._precrel != 1: - raise_error(ERROR_UNEXPECTED) + self._ready = True + self._init_jump() cdef int _jump_c(self, slong prec): + if not self._ready: + return ERROR_ABANDON if self._trivial: if self._valuation == 0 and self._precrel < prec: self._precrel = prec @@ -1008,39 +1048,37 @@ cdef class LazyElement_selfref(LazyElement_init): raise OverflowError("valuation is too large (maximum is %s)" % maxordp) self._valuation = valuation self._definition = None - self._next = False + self._next = maxordp cpdef set(self, LazyElement definition): if self._definition is not None: raise ValueError("this self-referent number is already defined") self._definition = definition cdef slong valsve = self._valuation - cdef int error = self._jump_c(self._valuation + self._parent.default_prec()) - if error & ERROR_CIRCULAR: + try: + self._init_jump() + except: self._definition = None self._valuation = valsve self._precrel = 0 - self._next = False - raise_error(error, permissive=True) - - def __eq__(self, other): - if self._definition is None: - self.set(other) - return LazyElement.__eq__(self, other) + self._next = maxordp + raise cdef int _next_c(self): cdef LazyElement definition = self._definition cdef cdigit_ptr digit + cdef slong n = self._valuation + self._precrel cdef slong diffval cdef int error if definition is None: return ERROR_NOTDEFINED - if self._next: + if n >= self._next: return ERROR_CIRCULAR - self._next = True - error = definition._jump_c(self._valuation + self._precrel + 1) + cdef slong svenext = self._next + self._next = n + error = definition._jump_c(n+1) if not error: diffval = self._valuation - definition._valuation if diffval < 0: @@ -1052,7 +1090,7 @@ cdef class LazyElement_selfref(LazyElement_init): else: element_set_digit(self._digits, digit, self._precrel) self._precrel += 1 - self._next = False + self._next = svenext return error diff --git a/src/sage/rings/padics/lazy_template_header.pxi b/src/sage/rings/padics/lazy_template_header.pxi index be8ab7842c4..8bbf119ded7 100644 --- a/src/sage/rings/padics/lazy_template_header.pxi +++ b/src/sage/rings/padics/lazy_template_header.pxi @@ -12,13 +12,18 @@ cdef class LazyElement(pAdicGenericElement): cdef cdigit_ptr _getdigit_relative(self, slong i) cdef cdigit_ptr _getdigit_absolute(self, slong i) - cdef void _getslice_relative(self, celement_ptr slice, slong start, slong length) + cdef void _getslice_relative(self, celement slice, slong start, slong length) + cdef int _init_jump(self) except -1 cdef int _jump_c(self, slong prec) cdef int _next_c(self) cdef Integer _digit(self, slong i) cdef bint _is_equal(self, LazyElement right, slong prec, bint permissive) except -1 +cdef class LazyElement_abandon(LazyElement): + pass +cdef lazyelement_abandon + cdef class LazyElement_init(LazyElement): cdef celement _digits @@ -76,6 +81,7 @@ cdef class LazyElement_div(LazyElement_init): cdef LazyElement _denom cdef LazyElement _definition cdef int _bootstrap_c(self) + cdef bint _bootstraping cdef class LazyElement_sqrt(LazyElement_init): cdef slong _maxprec @@ -84,6 +90,7 @@ cdef class LazyElement_sqrt(LazyElement_init): cdef int _bootstrap_c(self) cdef class LazyElement_teichmuller(LazyElement_init): + cdef bint _ready cdef bint _trivial cdef list _xns cdef LazyElement _xbar @@ -92,5 +99,5 @@ cdef class LazyElement_teichmuller(LazyElement_init): cdef class LazyElement_selfref(LazyElement_init): cdef LazyElement _definition - cdef bint _next + cdef slong _next cpdef set(self, LazyElement definition) diff --git a/src/sage/rings/padics/padic_base_leaves.py b/src/sage/rings/padics/padic_base_leaves.py index 9f03f24c170..8a83fc1ec6f 100644 --- a/src/sage/rings/padics/padic_base_leaves.py +++ b/src/sage/rings/padics/padic_base_leaves.py @@ -1120,6 +1120,7 @@ def __init__(self, p, prec, print_mode, names): 'teichmuller': padic_lazy_element.pAdicLazyElement_teichmuller, 'selfref': padic_lazy_element.pAdicLazyElement_selfref } + pAdicLazyGeneric.__init__(self) class pAdicFieldLazy(pAdicLazyGeneric, pAdicFieldBaseGeneric): def __init__(self, p, prec, print_mode, names): @@ -1141,4 +1142,4 @@ def __init__(self, p, prec, print_mode, names): 'teichmuller': padic_lazy_element.pAdicLazyElement_teichmuller, 'selfref': padic_lazy_element.pAdicLazyElement_selfref } - + pAdicLazyGeneric.__init__(self) diff --git a/src/sage/rings/padics/padic_lazy_element.pxd b/src/sage/rings/padics/padic_lazy_element.pxd index b0e1907c35a..26769130caa 100644 --- a/src/sage/rings/padics/padic_lazy_element.pxd +++ b/src/sage/rings/padics/padic_lazy_element.pxd @@ -1,10 +1,8 @@ -from sage.libs.flint.types cimport fmpz, fmpz_t -from sage.libs.flint.types cimport fmpz_poly_struct, fmpz_poly_t +from sage.libs.flint.types cimport fmpz, fmpz_t, fmpz_poly_t ctypedef fmpz_t cdigit ctypedef fmpz* cdigit_ptr ctypedef fmpz_poly_t celement -ctypedef fmpz_poly_struct* celement_ptr include "lazy_template_header.pxi" From 30d36daf4823d70571a51d1c868c5a0c5c214142 Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Sat, 23 Jan 2021 18:58:37 +0100 Subject: [PATCH 107/406] bounded and unbounded elements --- src/sage/rings/padics/generic_nodes.py | 21 +- src/sage/rings/padics/lazy_template.pxi | 231 ++++++++++-------- .../rings/padics/lazy_template_header.pxi | 5 +- src/sage/rings/padics/padic_base_leaves.py | 4 +- src/sage/rings/padics/padic_lazy_element.pxd | 2 +- src/sage/rings/padics/padic_lazy_element.pyx | 2 +- 6 files changed, 143 insertions(+), 122 deletions(-) diff --git a/src/sage/rings/padics/generic_nodes.py b/src/sage/rings/padics/generic_nodes.py index f8e9d7c5743..719da193f2e 100644 --- a/src/sage/rings/padics/generic_nodes.py +++ b/src/sage/rings/padics/generic_nodes.py @@ -722,28 +722,29 @@ def _coerce_map_from_(self, R): if isinstance(R, pAdicLazyGeneric) and self is R.fraction_field(): return True - def _element_constructor_(self, x): + def _element_constructor_(self, x, prec=None): parent = x.parent() - if parent is self: + if parent is self and prec is None: return x elif isinstance(parent, pAdicLazyGeneric): if parent.Element is self.Element: if not self.is_field() and x.valuation() < 0: raise ValueError("negative valuation") - return self._element_classes['copy'](self, x) + return self._element_classes['bound'](self, x, prec) raise NotImplementedError elif isinstance(parent, pAdicGeneric): if not self.is_field() and x.valuation() < 0: raise ValueError("negative valuation") - return self._element_classes['value'](self, x.lift(), maxprec=x.precision_absolute()) - elif x == 0: + prec = min(prec, x.precision_absolute()) + return self._element_classes['value'](self, x.lift(), precbound=prec) + elif x == 0 and prec is None: return self._element_classes['zero'](self) - elif x == 1: + elif x == 1 and prec is None: return self._element_classes['one'](self) else: try: x = self.exact_ring()(x) - return self._element_classes['value'](self, x) + return self._element_classes['value'](self, x, precbound=prec) except (TypeError, ValueError): pass try: @@ -757,7 +758,7 @@ def _element_constructor_(self, x): raise ValueError("negative valuation") num = self._element_classes['value'](self, num) denom = self._element_classes['value'](self, denom) - return self._element_classes['div'](self, num, denom) + return self._element_classes['div'](self, num, denom, precbound=prec) raise TypeError("unable to convert '%s' to a lazy %s-adic integer" % (x, self.prime())) def selfref(self, start_val=0): @@ -768,9 +769,9 @@ def selfref(self, start_val=0): raise ValueError("valuation must be nonnegative") return self._element_classes['selfref'](self, start_val) - def random_element(self, integral=False): + def random_element(self, integral=False, prec=None): integral = integral or (not self.is_field()) - return self._element_classes['random'](self, integral) + return self._element_classes['random'](self, integral, prec) def teichmuller(self, x): return self._element_classes['teichmuller'](self, ZZ(x)) diff --git a/src/sage/rings/padics/lazy_template.pxi b/src/sage/rings/padics/lazy_template.pxi index 129ccb33c0e..1c763fe499a 100644 --- a/src/sage/rings/padics/lazy_template.pxi +++ b/src/sage/rings/padics/lazy_template.pxi @@ -41,23 +41,27 @@ cdef class LazyElement(pAdicGenericElement): self.prime_pow = self._parent.prime_pow self._valuation = 0 self._precrel = 0 + self._precbound = maxordp cdef int _init_jump(self) except -1: cdef slong default_prec = self._parent.default_prec() - if not default_prec: + if default_prec == 0: return 0 cdef int error - if self.prime_pow.in_field: - error = 0 - if self._valuation <= -maxordp: - error = self._next_c() - if not error: - prec = self._valuation + default_prec - error = self._jump_c(prec) - if not error and self._valuation < prec: - error = self._jump_c(self._valuation + default_prec) + if self._precbound < maxordp: + error = self._jump_c(self._precbound) else: - error = self._jump_c(default_prec) + if self.prime_pow.in_field: + error = 0 + if self._valuation <= -maxordp: + error = self._next_c() + if not error: + prec = self._valuation + default_prec + error = self._jump_c(prec) + if not error and self._valuation < prec: + error = self._jump_c(self._valuation + default_prec) + else: + error = self._jump_c(default_prec) raise_error(error, True) return error @@ -81,15 +85,16 @@ cdef class LazyElement(pAdicGenericElement): cdef int _jump_c(self, slong prec): cdef int error - cdef slong i, stop - prec = min(prec, maxordp) - while self._precrel + self._valuation < prec: + if prec > maxordp: + return ERROR_OVERFLOW + cdef slong pr = min(prec, self._precbound) + while self._precrel + self._valuation < pr: error = self._next_c() if error: return error - if prec < maxordp: - return 0 - return ERROR_OVERFLOW + if prec > self._precbound: + return ERROR_PRECISION + return 0 def jump_absolute(self, prec=None): if prec is None: @@ -100,24 +105,31 @@ cdef class LazyElement(pAdicGenericElement): error = self._jump_c(prec) raise_error(error, permissive) - def jump_relative(self, prec=None, abort=True): + def jump_relative(self, prec=None, halt=True): + if self._is_exact_zero(): + return + default_prec = self._parent.default_prec() if prec is None: permissive = True - default_prec = self._parent.default_prec() - error = 0 - if self._valuation <= -maxordp: - error = self._next_c() - if not error: - while self._precrel == 0: - prec = self._valuation + default_prec - error = self._jump_c(prec) - if abort or error: - break - if not error and self._valuation < prec: - error = self._jump_c(self._valuation + default_prec) + prec = default_prec else: permissive = False - error = self._jump_c(prec) + if halt is True: + halt = self._valuation + default_prec + elif halt is False: + halt = maxordp + error = 0 + if self._valuation <= -maxordp: + error = self._next_c() + if not error and self._valuation < halt: + while self._precrel == 0: + error = self._next_c() + if self._valuation >= halt: + error |= ERROR_ABANDON + if error: + break + if not error: + error = self._jump_c(self._valuation + prec) raise_error(error, permissive) cdef Integer _digit(self, slong i): @@ -146,11 +158,9 @@ cdef class LazyElement(pAdicGenericElement): raise OverflowError("beyond maximum precision (which is %s)" % maxordp) return self._digit(n) - def slice(self, start=None, stop=None, bounded=True): + def slice(self, start=None, stop=None): cdef slong shift = 0 - if start is None: - start = -maxordp - elif start >= maxordp: + if start >= maxordp: raise OverflowError("beyond maximum precision (which is %s)" % maxordp) else: shift = start @@ -158,22 +168,7 @@ cdef class LazyElement(pAdicGenericElement): stop = maxordp elif stop >= maxordp: raise OverflowError("beyond maximum precision (which is %s)" % maxordp) - cdef LazyElement ans = element_class_slice(self._parent, self, start, stop, shift, bounded) - if bounded and stop < maxordp: - error = ans._jump_c(stop - shift) - raise_error(error, permissive=True) - return ans - - def __getitem__(self, n): - if isinstance(n, slice): - if n.step is not None: - raise NotImplementedError("step is not allowed") - return self.slice(n.start, n.stop) - else: - n = Integer(n) - if n >= maxordp: - raise OverflowError("beyond maximum precision (which is %s)" % maxordp) - return self._digit(n) + return element_class_slice(self._parent, self, start, stop, shift) def _repr_(self): if self._valuation <= -maxordp: @@ -219,22 +214,13 @@ cdef class LazyElement(pAdicGenericElement): a, b = coercion_model.canonical_coercion(self, other) return a == b cdef LazyElement right = other - if self._valuation <= -maxordp: - error = self._next_c() - raise_error(error, permissive=True) - if right._valuation <= -maxordp: - error = right._next_c() - raise_error(error, permissive=True) - minprec = min(self.precision_absolute(), right.precision_absolute()) - default_prec = self._parent.default_prec() - if self.prime_pow.in_field: - prec = self._valuation + default_prec - if not self._is_equal(right, max(minprec, prec), True): - return False - if self._valuation < prec: - return self._is_equal(right, max(minprec, self._valuation + default_prec), True) - else: - return self._is_equal(right, max(minprec, default_prec), True) + prec = min(self._precbound, right._precbound) + if prec >= maxordp: + raise ValueError("comparisons between unbounded lazy padics are not allowed") + return self._is_equal(right, prec, True) + + def is_precision_bounded(self): + return self._precbound < maxordp cpdef bint _is_exact_zero(self) except -1: return self._valuation >= maxordp @@ -257,15 +243,28 @@ cdef class LazyElement(pAdicGenericElement): raise PrecisionError("no lower bound on the valuation is known") return Integer(self._precrel) + def precision_bound(self): + if self._precbound < maxordp: + return Integer(self._precbound) + else: + return Infinity + + def at_precision(self, prec): + return element_class_bound((self)._parent, self, prec) + + def __matmul__(self, prec): + return self.at_precision(prec) + cdef long valuation_c(self): return self._valuation - def valuation(self, secure=False): + def valuation(self, halt=True): if self._is_exact_zero(): return Infinity if self._valuation <= -maxordp: raise PrecisionError("no lower bound on the valuation is known") - if secure and self._precrel == 0: + self.jump_relative(1, halt) + if self._precrel == 0: raise PrecisionError("cannot determine the valuation; try to increase the precision") return Integer(self._valuation) @@ -313,7 +312,7 @@ cdef class LazyElement(pAdicGenericElement): start = -maxordp else: start = shift - return element_class_slice((self)._parent, self, start, maxordp, shift, False) + return element_class_slice((self)._parent, self, start, maxordp, shift) else: return self @@ -433,7 +432,7 @@ cdef class LazyElement_init(LazyElement): cdef class LazyElement_zero(LazyElement): def __init__(self, parent): - LazyElement.__init__(self, parent) + LazyElement.__init__(self, parent, maxordp) self._valuation = maxordp cdef cdigit_ptr _getdigit_relative(self, slong i): @@ -470,14 +469,19 @@ cdef class LazyElement_one(LazyElement_init): return 0 -# Copy +# Bound -cdef class LazyElement_copy(LazyElement): - def __init__(self, parent, LazyElement x): +cdef class LazyElement_bound(LazyElement): + def __init__(self, parent, LazyElement x, precbound=None): LazyElement.__init__(self, parent) self._x = x - self._valuation = x._valuation - self._precrel = x._precrel + if precbound is None: + self._precbound = x._precbound + else: + self._precbound = min(x._precbound, precbound) + self._valuation = min(x._valuation, self._precbound) + self._precrel = min(x._precrel, self._precbound - self._valuation) + self._init_jump() cdef cdigit_ptr _getdigit_relative(self, slong i): return self._x._getdigit_relative(i) @@ -490,38 +494,42 @@ cdef class LazyElement_copy(LazyElement): cdef int _jump_c(self, slong prec): cdef LazyElement x = self._x - cdef int error = x._jump_c(prec) - self._valuation = x._valuation - self._precrel = x._precrel + cdef int error + if prec > self._precbound: + error = ERROR_PRECISION | x._jump_c(self._precbound) + else: + error = x._jump_c(prec) + self._precbound = min(self._precbound, x._precbound) + self._valuation = min(x._valuation, self._precbound) + self._precrel = min(x._precrel, self._precbound - self._valuation) return error cdef int _next_c(self): cdef LazyElement x = self._x cdef int error = x._next_c() - self._valuation = x._valuation - self._precrel = x._precrel + self._precbound = min(self._precbound, x._precbound) + self._valuation = min(x._valuation, self._precbound) + self._precrel = min(x._precrel, self._precbound - self._valuation) return error # Value cdef class LazyElement_value(LazyElement_init): - def __init__(self, parent, Integer value, slong shift=0, maxprec=None): + def __init__(self, parent, value, slong shift=0, precbound=None): LazyElement.__init__(self, parent) element_set_digit_sage(self._digits, value, 0) - if maxprec is None: - self._maxprec = maxordp - else: - self._maxprec = maxprec self._shift = self._valuation = shift self._finished = False + if precbound is not None and precbound is not Infinity: + self._precbound = min(maxordp, precbound) self._init_jump() cdef int _jump_c(self, slong prec): if not self._finished: return LazyElement._jump_c(self, prec) - if (self._maxprec is not None) and (prec > self._maxprec): - self._precrel = self._maxprec - self._valuation + if (self._precbound is not None) and (prec > self._precbound): + self._precrel = self._precbound - self._valuation return ERROR_PRECISION cdef slong precrel = min(prec, maxordp) - self._valuation if precrel > self._precrel: @@ -531,7 +539,7 @@ cdef class LazyElement_value(LazyElement_init): return ERROR_OVERFLOW cdef int _next_c(self): - if (self._maxprec is not None) and (self._valuation + self._precrel >= self._maxprec): + if (self._precbound is not None) and (self._valuation + self._precrel >= self._precbound): return ERROR_PRECISION element_reduce_digit(self._digits, self._precrel, self.prime_pow) if self._precrel == 0 and digit_is_zero(self._getdigit_relative(0)): @@ -546,10 +554,12 @@ cdef class LazyElement_value(LazyElement_init): # Random cdef class LazyElement_random(LazyElement_init): - def __init__(self, parent, integral): + def __init__(self, parent, integral, precbound): LazyElement.__init__(self, parent) if not integral: self._valuation = ZZ.random_element() + if precbound is not None: + self._precbound = min(maxordp, precbound) self._init_jump() cdef int _next_c(self): @@ -570,24 +580,23 @@ cdef class LazyElement_random(LazyElement_init): # Shift cdef class LazyElement_slice(LazyElement): - def __init__(self, parent, LazyElement x, slong start, slong stop, slong shift, bint bounded): + def __init__(self, parent, LazyElement x, slong start, slong stop, slong shift): # self[0] = x[shift] LazyElement.__init__(self, parent) self._x = x self._start = start - self._stop = stop self._shift = shift self._valuation = max(x._valuation, start) - shift self._precrel = min(x._precrel + x._valuation, stop) - self._valuation - shift + if x._precbound < maxordp: + self._precbound = x._precbound - shift + self._stop = min(self._precbound, stop) if self._precrel < 0: self._precrel = 0 while self._precrel > 0 and digit_is_zero(self._getdigit_relative(0)): self._precrel -= 1 self._valuation += 1 - if bounded: - self._maxprec = stop - shift - else: - self._maxprec = maxordp + self._init_jump() cdef cdigit_ptr _getdigit_relative(self, slong i): return self._getdigit_absolute(i + self._valuation) @@ -611,8 +620,8 @@ cdef class LazyElement_slice(LazyElement): cdef int error = 0 if prec <= self._valuation + self._precrel: return 0 - if prec > self._maxprec: - prec = self._maxprec + if prec > self._precbound: + prec = self._precbound error = ERROR_PRECISION cdef int errorx = x._jump_c(min(prec + self._shift, self._stop)) prec = max(self._valuation, x._valuation + x._precrel - self._shift) @@ -628,8 +637,6 @@ cdef class LazyElement_slice(LazyElement): cdef int _next_c(self): cdef int error cdef slong n = self._precrel + self._valuation - if n >= self._maxprec: - return ERROR_PRECISION n += self._shift if n <= self._stop: error = self._x._jump_c(n+1) @@ -647,9 +654,10 @@ cdef class LazyElement_slice(LazyElement): cdef class LazyElement_add(LazyElement_init): def __init__(self, parent, summands, signs): LazyElement.__init__(self, parent) - self._valuation = min((summand)._valuation for summand in summands) self._summands = summands self._signs = signs + self._valuation = min((summand)._valuation for summand in summands) + self._precbound = min((summand)._precbound for summand in summands) self._init_jump() cdef int _next_c(self): @@ -696,6 +704,9 @@ cdef class LazyElement_mul(LazyElement_init): self._x = x self._y = y self._valuation = self._x._valuation + self._y._valuation + if self._x._precbound < maxordp and self._y._precbound < maxordp: + # FIXME: maybe the valuations are not known here + self._precbound = min(self._x._valuation + self._y._precbound, self._y._valuation + self._x._precbound, maxordp) self._init_jump() cdef int _next_c(self): @@ -817,7 +828,7 @@ cdef class LazyElement_div(LazyElement_init): def __dealloc__(self): digit_clear(self._inverse) - def __init__(self, parent, LazyElement num, LazyElement denom): + def __init__(self, parent, LazyElement num, LazyElement denom, precbound=None): LazyElement.__init__(self, parent) self._num = num self._denom = denom @@ -829,6 +840,11 @@ cdef class LazyElement_div(LazyElement_init): if error: self._valuation = -maxordp else: + if precbound is not None: + self._precbound = min(maxordp, precbound) + if num._precbound < maxordp or denom._precbound < maxordp: + # FIXME: maybe num._valuation is not known here + self._precbound = min(maxordp, num._precbound - denom._valuation, denom._precbound + num._valuation - 2*denom._valuation) self._init_jump() cdef int _bootstrap_c(self): @@ -884,8 +900,6 @@ cdef class LazyElement_div(LazyElement_init): cdef class LazyElement_sqrt(LazyElement_init): def __init__(self, parent, LazyElement x): LazyElement.__init__(self, parent) - #if parent.prime() == 2: - # raise NotImplementedError self._x = x if x._valuation <= -maxordp: self._valuation = -maxordp @@ -893,9 +907,16 @@ cdef class LazyElement_sqrt(LazyElement_init): else: self._valuation = x._valuation >> 1 self._maxprec = x._valuation + 2*self._parent.default_prec() - cdef int error = self._init_jump() + cdef int error = self._bootstrap_c() if error & ERROR_NOTSQUARE: raise ValueError("not a square") + if not error: + if x._valuation < maxordp: + self._precbound = x._precbound - x._valuation / 2 + if self._parent.prime() == 2: + self._precbound -= 1 + self._precbound = min(maxordp, self._precbound) + self._init_jump() cdef int _bootstrap_c(self): cdef LazyElement x = self._x diff --git a/src/sage/rings/padics/lazy_template_header.pxi b/src/sage/rings/padics/lazy_template_header.pxi index 8bbf119ded7..3bed8a61fcb 100644 --- a/src/sage/rings/padics/lazy_template_header.pxi +++ b/src/sage/rings/padics/lazy_template_header.pxi @@ -8,6 +8,7 @@ from sage.rings.padics.padic_generic_element cimport pAdicGenericElement cdef class LazyElement(pAdicGenericElement): cdef slong _valuation cdef slong _precrel + cdef slong _precbound cdef PowComputer_class prime_pow cdef cdigit_ptr _getdigit_relative(self, slong i) @@ -34,11 +35,10 @@ cdef class LazyElement_zero(LazyElement): cdef class LazyElement_one(LazyElement_init): pass -cdef class LazyElement_copy(LazyElement): +cdef class LazyElement_bound(LazyElement): cdef LazyElement _x cdef class LazyElement_value(LazyElement_init): - cdef slong _maxprec cdef slong _shift cdef bint _finished @@ -52,7 +52,6 @@ cdef class LazyElement_slice(LazyElement): cdef slong _start cdef slong _stop cdef slong _shift - cdef slong _maxprec cdef class LazyElement_shift(LazyElement_init): cdef LazyElement _x diff --git a/src/sage/rings/padics/padic_base_leaves.py b/src/sage/rings/padics/padic_base_leaves.py index 8a83fc1ec6f..8f04da5601f 100644 --- a/src/sage/rings/padics/padic_base_leaves.py +++ b/src/sage/rings/padics/padic_base_leaves.py @@ -1108,7 +1108,7 @@ def __init__(self, p, prec, print_mode, names): 'generic': padic_lazy_element.pAdicLazyElement, 'zero': padic_lazy_element.pAdicLazyElement_zero, 'one': padic_lazy_element.pAdicLazyElement_one, - 'copy': padic_lazy_element.pAdicLazyElement_copy, + 'bound': padic_lazy_element.pAdicLazyElement_bound, 'value': padic_lazy_element.pAdicLazyElement_value, 'random': padic_lazy_element.pAdicLazyElement_random, 'slice': padic_lazy_element.pAdicLazyElement_slice, @@ -1130,7 +1130,7 @@ def __init__(self, p, prec, print_mode, names): 'generic': padic_lazy_element.pAdicLazyElement, 'zero': padic_lazy_element.pAdicLazyElement_zero, 'one': padic_lazy_element.pAdicLazyElement_one, - 'copy': padic_lazy_element.pAdicLazyElement_copy, + 'bound': padic_lazy_element.pAdicLazyElement_bound, 'value': padic_lazy_element.pAdicLazyElement_value, 'random': padic_lazy_element.pAdicLazyElement_random, 'slice': padic_lazy_element.pAdicLazyElement_slice, diff --git a/src/sage/rings/padics/padic_lazy_element.pxd b/src/sage/rings/padics/padic_lazy_element.pxd index 26769130caa..758f5946792 100644 --- a/src/sage/rings/padics/padic_lazy_element.pxd +++ b/src/sage/rings/padics/padic_lazy_element.pxd @@ -17,7 +17,7 @@ cdef class pAdicLazyElement_zero(LazyElement_zero): cdef class pAdicLazyElement_one(LazyElement_one): pass -cdef class pAdicLazyElement_copy(LazyElement_copy): +cdef class pAdicLazyElement_bound(LazyElement_bound): pass cdef class pAdicLazyElement_value(LazyElement_value): diff --git a/src/sage/rings/padics/padic_lazy_element.pyx b/src/sage/rings/padics/padic_lazy_element.pyx index db817b3e5de..6a3962c72fe 100644 --- a/src/sage/rings/padics/padic_lazy_element.pyx +++ b/src/sage/rings/padics/padic_lazy_element.pyx @@ -1,6 +1,6 @@ cdef inline type element_class_zero = pAdicLazyElement_zero cdef inline type element_class_one = pAdicLazyElement_one -cdef inline type element_class_copy = pAdicLazyElement_copy +cdef inline type element_class_bound = pAdicLazyElement_bound cdef inline type element_class_value = pAdicLazyElement_value cdef inline type element_class_random = pAdicLazyElement_random cdef inline type element_class_slice = pAdicLazyElement_slice From eca2294d63f0f5be72a0fdda694afe0668c83cf9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCnter=20Rote?= Date: Sun, 24 Jan 2021 01:13:51 +0100 Subject: [PATCH 108/406] fix omitted javascript snippets --- .../ext_data/threejs/threejs_template.html | 7 +++++++ src/sage/plot/plot3d/base.pyx | 18 +++++++++--------- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/src/sage/ext_data/threejs/threejs_template.html b/src/sage/ext_data/threejs/threejs_template.html index e499f4711aa..0bdd172dc1c 100644 --- a/src/sage/ext_data/threejs/threejs_template.html +++ b/src/sage/ext_data/threejs/threejs_template.html @@ -535,6 +535,13 @@ textArea.select(); document.execCommand( 'copy' ); + var m = document.getElementById( 'menu-message' ); + m.innerHTML = 'Camera position '+ cam_position+' copied to clipboard'; + m.style.display = 'block'; + setTimeout( function() { m.style.display = 'none'; }, 2000 ); + + } + From 59e245de020b1522ee5264ae3762192555007b9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCnter=20Rote?= Date: Sat, 27 Mar 2021 11:04:32 +0100 Subject: [PATCH 236/406] fixed test output in polyhedron/plot.py example --- src/sage/geometry/polyhedron/plot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/geometry/polyhedron/plot.py b/src/sage/geometry/polyhedron/plot.py index 3ce1ba15fa0..56835ba8676 100644 --- a/src/sage/geometry/polyhedron/plot.py +++ b/src/sage/geometry/polyhedron/plot.py @@ -985,7 +985,7 @@ def render_wireframe_3d(self, **kwds): sage: cube_proj = cube.projection() sage: wire = cube_proj.render_wireframe_3d() sage: print(wire.tachyon().split('\n')[77]) # for testing - FCylinder base 1.0 1.0 -1.0 apex 1.0 1.0 1.0 rad 0.005 texture... + FCylinder base 1.0 1.0 -1.0 apex -1.0 1.0 -1.0 rad 0.005 texture... """ wireframe = [] for l in self.lines: From 3e0bef93edfdc502bafaa08cba2dbebbdcc457c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sat, 27 Mar 2021 11:06:54 +0100 Subject: [PATCH 237/406] maximal chain length for posets --- src/sage/combinat/posets/posets.py | 31 ++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/src/sage/combinat/posets/posets.py b/src/sage/combinat/posets/posets.py index 177295cff6f..2d385834ff6 100644 --- a/src/sage/combinat/posets/posets.py +++ b/src/sage/combinat/posets/posets.py @@ -136,6 +136,7 @@ :meth:`~FinitePoset.maximal_chains` | Return the maximal chains of the poset. :meth:`~FinitePoset.maximal_antichains` | Return the maximal antichains of the poset. :meth:`~FinitePoset.maximal_chains_iterator` | Return an iterator over the maximal chains of the poset. + :meth:`~FinitePoset.maximal_chain_length` | Return the maximum length of maximal chains of the poset. :meth:`~FinitePoset.antichains_iterator` | Return an iterator over the antichains of the poset. :meth:`~FinitePoset.random_maximal_chain` | Return a random maximal chain. :meth:`~FinitePoset.random_maximal_antichain` | Return a random maximal antichain. @@ -6789,6 +6790,36 @@ def maximal_chains_iterator(self, partial=None): for new in parts: yield from self.maximal_chains_iterator(partial=new) + def maximal_chain_length(self): + """ + Return the maximum length of a maximal chain in the poset. + + The length here is the number of vertices. + + EXAMPLES:: + + sage: P = posets.TamariLattice(5) + sage: P.maximal_chain_length() + 11 + + TESTS:: + + sage: Poset().maximal_chain_length() + 0 + + .. SEEALSO:: :meth:`maximal_chains`, :meth:`maximal_chains_iterator` + """ + if not self.cardinality(): + return 0 + store = {} + for x in self: + below = self.lower_covers(x) + if not below: + store[x] = 1 + else: + store[x] = 1 + max(store[y] for y in below) + return max(store.values()) + def order_complex(self, on_ints=False): r""" Return the order complex associated to this poset. From 20a7c0015598da05be5f344bc31d1489b71758fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCnter=20Rote?= Date: Sat, 27 Mar 2021 11:45:23 +0100 Subject: [PATCH 238/406] fixes suggested by reviewer --- src/sage/plot/plot3d/base.pyx | 34 +++++++++++++--------------------- 1 file changed, 13 insertions(+), 21 deletions(-) diff --git a/src/sage/plot/plot3d/base.pyx b/src/sage/plot/plot3d/base.pyx index 4b0d33790b0..1bb7db2880c 100644 --- a/src/sage/plot/plot3d/base.pyx +++ b/src/sage/plot/plot3d/base.pyx @@ -220,17 +220,12 @@ cdef class Graphics3d(SageObject): T = self._prepare_for_tachyon( opts['frame'], opts['axes'], opts['frame_aspect_ratio'], opts['aspect_ratio'], - 1 # opts['zoom'] - # let zoom be handled by tachyon. + 1 # opts['zoom']. Let zoom be handled by tachyon. # We don't want the perspective to change by zooming ) -# x, y = opts['figsize'][0]*100, opts['figsize'][1]*100 -# if DOCTEST_MODE: -# x, y = 10, 10 tachyon_args = dict((key,val) for key,val in opts.items() if key in Graphics3d.tachyon_keywords) tachyon_rt(T.tachyon(**tachyon_args), filename, opts['verbosity']) -# '-res %s %s' % (x, y)) ## handled by the tachyon method. from sage.repl.rich_output.buffer import OutputBuffer import sage.repl.rich_output.output_catalog as catalog import PIL.Image as Image @@ -953,15 +948,15 @@ cdef class Graphics3d(SageObject): """%(self.viewpoint().x3d_str(), self.x3d_str()) -################ TACHYON ################ + ################ TACHYON ################ -####### insertion of camera parameters + ####### insertion of camera parameters tachyon_keywords = ( "antialiasing", # "aspectratio", "zoom", # zoom was previously handled directly by scaling the scene. - # this has now been disabled, and zoom is handled by tachyon + # This has now been disabled, and zoom is handled by tachyon. "raydepth", "figsize", "light_position", "camera_position","updir", # "look_at", # omit look_at. viewdir is sufficient for most purposes @@ -977,9 +972,10 @@ cdef class Graphics3d(SageObject): # Instead, the tachyon aspectratio is set to match nonsquare # drawing area in "figsize". - # Parameters are taken from tachyion.py + # Parameters are mostly taken from tachyion.py, + # but camera_center is renamed camera_position. # Apparently reST strips () from default parameters in the automatic documentation. - # Thus, replaced () by []. + # Thus, I replaced () by [] as default values. def tachyon(self, zoom=1.0, @@ -988,10 +984,10 @@ cdef class Graphics3d(SageObject): raydepth=8, camera_position=[2.3, 2.4, 2.0], # old default values updir=[0, 0, 1], - # look_at=(0, 0, 0), # viewdir is good enough + # look_at=(0, 0, 0), # could be nice to have, but viewdir is good enough light_position=[4.0, 3.0, 2.0], viewdir=None, - #projection='PERSPECTIVE', + # projection='PERSPECTIVE', # future extension, allow different projection types ): """ A tachyon input file (as a string) containing the this object. @@ -1043,13 +1039,9 @@ cdef class Graphics3d(SageObject): raise ValueError('Camera center must consist of three numbers') if viewdir is None: -# viewdir = [float(look_at[i] - camera_position[i]) for i in range(3)] viewdir = [float(- camera_position[i]) for i in range(3)] if viewdir == [0.0,0.0,0.0]: - # print("Warning: camera_position and look_at coincide") - #print("Warning: camera_position at origin") - viewdir = (1,0,0) - + viewdir = (1,0,0) # issue a Warning? "camera_position at origin" # switch from LH to RH coords to be consistent with java rendition viewdir = _flip_orientation(viewdir) updir = _flip_orientation(updir) @@ -1676,8 +1668,8 @@ end_scene""".format( .. WARNING:: - By default, the jmol and tachyon viewers perform - some non-uniform scaling of the axes. + By default, the jmol and tachyon viewers perform + some non-uniform scaling of the axes. If this is not desired, one can set ``aspect_ratio=1``:: @@ -3304,7 +3296,7 @@ def optimal_extra_kwds(v): def _flip_orientation(v): """ - switch from LH to RH coords to be consistent with Java rendition + Switch from LH to RH coords to be consistent with Java rendition TESTS:: From eae2799cc42dd3e68d6a66a135cfc86222aababb Mon Sep 17 00:00:00 2001 From: dcoudert Date: Sat, 27 Mar 2021 19:24:26 +0100 Subject: [PATCH 239/406] trac #31571: more parameters for planar_graphs --- src/sage/graphs/graph_generators.py | 62 ++++++++++++++++++++++++++++- 1 file changed, 60 insertions(+), 2 deletions(-) diff --git a/src/sage/graphs/graph_generators.py b/src/sage/graphs/graph_generators.py index de16509c4b7..667e1640848 100644 --- a/src/sage/graphs/graph_generators.py +++ b/src/sage/graphs/graph_generators.py @@ -1392,7 +1392,11 @@ def fusenes(self, hexagon_count, benzenoids=False): def planar_graphs(self, order, minimum_degree=None, minimum_connectivity=None, - exact_connectivity=False, only_bipartite=False, + exact_connectivity=False, + minimum_edges=None, + maximum_edges=None, + maximum_face_size=None, + only_bipartite=False, dual=False): r""" An iterator over connected planar graphs using the plantri generator. @@ -1429,6 +1433,15 @@ def planar_graphs(self, order, minimum_degree=None, This option cannot be used with ``minimum_connectivity=3``, or if the minimum connectivity is not explicitly set. + - ``minimum_edges`` -- integer (default: ``None``); lower bound on the + number of edges + + - ``maximum_edges`` -- integer (default: ``None``); upper bound on the + number of edges + + - ``maximum_face_size`` -- integer (default: ``None``); upper bound on + the size of a face and so on the maximum degree of the dual graph + - ``only_bipartite`` - default: ``False`` - if ``True`` only bipartite graphs will be generated. This option cannot be used for graphs with a minimum degree larger than 3. @@ -1492,6 +1505,24 @@ def planar_graphs(self, order, minimum_degree=None, sage: list(graphs.planar_graphs(1, minimum_degree=1)) # optional plantri [] + Specifying lower and upper bounds on the number of edges:: + + sage: len(list(graphs.planar_graphs(4))) # optional plantri + 6 + sage: len(list(graphs.planar_graphs(4, minimum_edges=4))) # optional plantri + 4 + sage: len(list(graphs.planar_graphs(4, maximum_edges=4))) # optional plantri + 4 + sage: len(list(graphs.planar_graphs(4, minimum_edges=4, maximum_edges=4))) # optional plantri + 2 + + Specifying the maximum size of a face:: + + sage: len(list(graphs.planar_graphs(4, maximum_face_size=3))) # optional plantri + 1 + sage: len(list(graphs.planar_graphs(4, maximum_face_size=4))) # optional plantri + 3 + TESTS: The number of edges in a planar graph is equal to the number of edges in @@ -1546,6 +1577,32 @@ def planar_graphs(self, order, minimum_degree=None, if only_bipartite and minimum_degree > 3: raise NotImplementedError("Generation of bipartite planar graphs with minimum degree 4 or 5 is not implemented.") + edges = '' + if minimum_edges is None: + if maximum_edges is not None: + from math import ceil + if maximum_edges < order - 1: + raise ValueError("the number of edges cannot be less than order - 1") + edges = '-e:{}'.format(maximum_edges) + else: + if minimum_edges > 3*order - 6: + raise ValueError("the number of edges cannot be more than 3*order - 6") + if maximum_edges is None: + edges = '-e{}:'.format(minimum_edges) + elif minimum_edges > maximum_edges: + raise ValueError("the maximum number of edges must be larger " + "or equal to the minimum number of edges") + elif minimum_edges == maximum_edges: + edges = '-e{}'.format(minimum_edges) + else: + edges = '-e{}:{}'.format(minimum_edges, maximum_edges) + + faces = '' + if maximum_face_size is not None: + if maximum_face_size < 3: + raise ValueError("the upper bound on the size of a face must be at least 3") + faces = '-f{}'.format(maximum_face_size) + if order == 0: return @@ -1564,12 +1621,13 @@ def planar_graphs(self, order, minimum_degree=None, from sage.features.graph_generators import Plantri Plantri().require() - cmd = 'plantri -p{}m{}c{}{}{} {}' + cmd = 'plantri -p{}m{}c{}{}{} {} {} {}' command = cmd.format('b' if only_bipartite else '', minimum_degree, minimum_connectivity, 'x' if exact_connectivity else '', 'd' if dual else '', + edges, faces, order) sp = subprocess.Popen(command, shell=True, From 62bdf69faa9e05c884e588f24c4315b54e5122fa Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 27 Mar 2021 11:35:51 -0700 Subject: [PATCH 240/406] tox.ini: Add scientificlinux --- tox.ini | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tox.ini b/tox.ini index 1d7534148ef..e770b171c8e 100644 --- a/tox.ini +++ b/tox.ini @@ -209,6 +209,14 @@ setenv = fedora-33: BASE_TAG=33 fedora-34: BASE_TAG=34 # + # https://hub.docker.com/r/scientificlinux/sl + # + scientificlinux: SYSTEM=fedora + scientificlinux: BASE_IMAGE=scientificlinux/sl + scientificlinux: IGNORE_MISSING_SYSTEM_PACKAGES=yes + scientificlinux-6: BASE_TAG=6 + scientificlinux-7: BASE_TAG=7 + # # https://hub.docker.com/_/centos # centos-6 only has autoconf 2.63 -- too old for bootstrap; download configure tarball instead. # From ad07e9413ac76528dfec905b7be3bf551df5e422 Mon Sep 17 00:00:00 2001 From: dcoudert Date: Sat, 27 Mar 2021 23:11:35 +0100 Subject: [PATCH 241/406] trac #31571: remove useless import --- src/sage/graphs/graph_generators.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/sage/graphs/graph_generators.py b/src/sage/graphs/graph_generators.py index 667e1640848..a28054af226 100644 --- a/src/sage/graphs/graph_generators.py +++ b/src/sage/graphs/graph_generators.py @@ -1580,7 +1580,6 @@ def planar_graphs(self, order, minimum_degree=None, edges = '' if minimum_edges is None: if maximum_edges is not None: - from math import ceil if maximum_edges < order - 1: raise ValueError("the number of edges cannot be less than order - 1") edges = '-e:{}'.format(maximum_edges) From 32c069a11367dedeedb2e5079608bdf89e0b46bf Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 27 Mar 2021 15:28:31 -0700 Subject: [PATCH 242/406] .gitignore: Integrate src/{doc,sage}/.gitignore here so that Docker ignores the listed files too --- .gitignore | 14 ++++++++++++++ src/doc/.gitignore | 6 ------ src/sage/.gitignore | 1 - 3 files changed, 14 insertions(+), 7 deletions(-) delete mode 100644 src/doc/.gitignore delete mode 100644 src/sage/.gitignore diff --git a/.gitignore b/.gitignore index 8d2e8d86240..3d69b3beab4 100644 --- a/.gitignore +++ b/.gitignore @@ -93,6 +93,9 @@ __pycache__/ *.py[cod] *$py.class +# Generated by sage_setup.autogen +/src/sage/ext/interpreters + # Generated Cython files *.so src/sage/**/*.c @@ -130,6 +133,14 @@ build/bin/sage-build-env-config /build/pkgs/*/src/*.egg-info /build/pkgs/*/src/.tox +# Generated by docbuild +/src/doc/en/reference/*/sage +/src/doc/en/reference/sage +/src/doc/en/reference/spkg/*.rst +/src/doc/output +/src/doc/en/installation/*.txt +/src/doc/en/reference/repl/*.txt + # Distribution / packaging src/*.egg-info/ /src/.cython_version @@ -152,5 +163,8 @@ src/venv.bak/ # tox generated files /.tox +/build/.tox /prefix + +# git worktree worktree* diff --git a/src/doc/.gitignore b/src/doc/.gitignore deleted file mode 100644 index dd2abbd9cf1..00000000000 --- a/src/doc/.gitignore +++ /dev/null @@ -1,6 +0,0 @@ -/en/reference/*/sage -/en/reference/sage -/en/reference/spkg/*.rst -/output -/en/installation/*.txt -/en/reference/repl/*.txt diff --git a/src/sage/.gitignore b/src/sage/.gitignore deleted file mode 100644 index 78a127212f0..00000000000 --- a/src/sage/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/ext/interpreters From 92db4e40bf40a73be4570e159c4e5b36c1955451 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 27 Mar 2021 17:54:55 -0700 Subject: [PATCH 243/406] tox.ini (macos-nohomebrew): Pass compiler configuration to configure using variables other than CC, CXX. Previously, they were in the environment also at 'make' time and due to our design in 'sage-env' override the actual configured compilers from the 'configure' run, which includes additional options (-std=....) --- tox.ini | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tox.ini b/tox.ini index 162be1344ca..67dba1ce52b 100644 --- a/tox.ini +++ b/tox.ini @@ -417,9 +417,9 @@ setenv = gcc_9: CONFIG_CONFIGURE_ARGS_2=--with-system-gcc=force CC=gcc-9 CXX=g++-9 FC=gfortran-9 gcc_10: CONFIG_CONFIGURE_ARGS_2=--with-system-gcc=force CC=gcc-10 CXX=g++-10 FC=gfortran-10 gcc_11: CONFIG_CONFIGURE_ARGS_2=--with-system-gcc=force CC=gcc-11 CXX=g++-11 FC=gfortran-11 - macos-nohomebrew: CONFIG_CONFIGURE_ARGS_2=--with-system-gcc=force --with-mp=gmp --without-system-mpfr --without-system-readline - macos-nohomebrew: CXX=g++ -isysroot {env:MACOS_SDK} - macos-nohomebrew: CC=gcc -isysroot {env:MACOS_SDK} + macos-nohomebrew: CONFIG_CONFIGURE_ARGS_2=--with-system-gcc=force CC="$CONFIGURED_CC" CXX="$CONFIGURED_CXX" --with-mp=gmp --without-system-mpfr --without-system-readline --without-system-boost + macos-nohomebrew: CONFIGURED_CXX=g++ -isysroot {env:MACOS_SDK} + macos-nohomebrew: CONFIGURED_CC=gcc -isysroot {env:MACOS_SDK} # We use libgd only used in a very limited way, in {matrix,vector}_mod_2_dense. Disable search for other packages. macos-nohomebrew: LIBGD_CONFIGURE=--without-freetype --without-raqm --without-fontconfig --without-jpeg --without-liq --without-xpm --without-tiff --without-webp --without-heif --without-avif macos: MACOS_SDK=/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk From 300d054b7e4a6e3ba5a4614597f56e0f62a7e22b Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 27 Mar 2021 18:52:28 -0700 Subject: [PATCH 244/406] build/pkgs/pillow/spkg-install.in: Rename environment variable extra_build_ext to PILLOW_BUILD_EXT --- build/pkgs/pillow/spkg-install.in | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/pkgs/pillow/spkg-install.in b/build/pkgs/pillow/spkg-install.in index 86b88f1564d..57be7347493 100644 --- a/build/pkgs/pillow/spkg-install.in +++ b/build/pkgs/pillow/spkg-install.in @@ -14,7 +14,7 @@ if [ "$UNAME" = "Darwin" ] ; then fi if [ "$CONDA_PREFIX" != "" ]; then - extra_build_ext="$extra_build_ext --disable-platform-guessing -I$CONDA_PREFIX/include -L$CONDA_PREFIX/lib" + PILLOW_BUILD_EXT="$PILLOW_BUILD_EXT --disable-platform-guessing -I$CONDA_PREFIX/include -L$CONDA_PREFIX/lib" fi # Note: Avoid shared libraries inside egg files, Trac #19467 @@ -22,6 +22,6 @@ sdh_setup_bdist_wheel \ build_ext \ --debug \ --disable-jpeg \ - $extra_build_ext + $PILLOW_BUILD_EXT sdh_store_and_pip_install_wheel . From 8c8d83d70579285d10713030160501099c6533c5 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 27 Mar 2021 18:52:58 -0700 Subject: [PATCH 245/406] build/pkgs/freetype/spkg-install.in: Pass environment variable FREETYPE_CONFIGURE to configure --- build/pkgs/freetype/spkg-install.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/pkgs/freetype/spkg-install.in b/build/pkgs/freetype/spkg-install.in index 1c33c68b4ef..c1615626e72 100644 --- a/build/pkgs/freetype/spkg-install.in +++ b/build/pkgs/freetype/spkg-install.in @@ -1,7 +1,7 @@ cd src # Enable the now-deprecated `freetype-config` -GNUMAKE=${MAKE} sdh_configure --enable-freetype-config +GNUMAKE=${MAKE} sdh_configure --enable-freetype-config $FREETYPE_CONFIGURE sdh_make sdh_make_install From 12c84a3d1e9ab6fbffa90c5675cef0424609d2da Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 27 Mar 2021 18:53:56 -0700 Subject: [PATCH 246/406] tox.ini (macos-nohomebrew): Prevent /usr/local from leaking in through boost, freetype, pillow --- tox.ini | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tox.ini b/tox.ini index 67dba1ce52b..3dfbcafa090 100644 --- a/tox.ini +++ b/tox.ini @@ -417,11 +417,14 @@ setenv = gcc_9: CONFIG_CONFIGURE_ARGS_2=--with-system-gcc=force CC=gcc-9 CXX=g++-9 FC=gfortran-9 gcc_10: CONFIG_CONFIGURE_ARGS_2=--with-system-gcc=force CC=gcc-10 CXX=g++-10 FC=gfortran-10 gcc_11: CONFIG_CONFIGURE_ARGS_2=--with-system-gcc=force CC=gcc-11 CXX=g++-11 FC=gfortran-11 - macos-nohomebrew: CONFIG_CONFIGURE_ARGS_2=--with-system-gcc=force CC="$CONFIGURED_CC" CXX="$CONFIGURED_CXX" --with-mp=gmp --without-system-mpfr --without-system-readline --without-system-boost + macos-nohomebrew: CONFIG_CONFIGURE_ARGS_2=--with-system-gcc=force CC="$CONFIGURED_CC" CXX="$CONFIGURED_CXX" --with-mp=gmp --without-system-mpfr --without-system-readline --without-system-boost --without-system-boost_cropped macos-nohomebrew: CONFIGURED_CXX=g++ -isysroot {env:MACOS_SDK} macos-nohomebrew: CONFIGURED_CC=gcc -isysroot {env:MACOS_SDK} - # We use libgd only used in a very limited way, in {matrix,vector}_mod_2_dense. Disable search for other packages. + # Prevent /usr/local to leak in: + # - We use libgd only used in a very limited way, in {matrix,vector}_mod_2_dense. Disable search for other packages. macos-nohomebrew: LIBGD_CONFIGURE=--without-freetype --without-raqm --without-fontconfig --without-jpeg --without-liq --without-xpm --without-tiff --without-webp --without-heif --without-avif + macos-nohomebrew: FREETYPE_CONFIGURE=--without-harfbuzz + macos-nohomebrew: PILLOW_BUILDEXT=--disable-platform-guessing macos: MACOS_SDK=/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk # python3 from XCode 12 has MACOSX_DEPLOYMENT_TARGET=10.14.6. Selecting a lower target would cause /usr/bin/python3 to be rejected by configure. macos-10.14: MACOS_SDK=/Library/Developer/CommandLineTools/SDKs/MacOSX10.14.sdk From b5edde2e54cc58bd151880293e6454c3f92150fe Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 27 Mar 2021 22:50:13 -0700 Subject: [PATCH 247/406] tox.ini (macos-nohomebrew): Fix typo in PILLOW_... --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index 3dfbcafa090..77b814c8ef5 100644 --- a/tox.ini +++ b/tox.ini @@ -424,7 +424,7 @@ setenv = # - We use libgd only used in a very limited way, in {matrix,vector}_mod_2_dense. Disable search for other packages. macos-nohomebrew: LIBGD_CONFIGURE=--without-freetype --without-raqm --without-fontconfig --without-jpeg --without-liq --without-xpm --without-tiff --without-webp --without-heif --without-avif macos-nohomebrew: FREETYPE_CONFIGURE=--without-harfbuzz - macos-nohomebrew: PILLOW_BUILDEXT=--disable-platform-guessing + macos-nohomebrew: PILLOW_BUILD_EXT=--disable-platform-guessing macos: MACOS_SDK=/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk # python3 from XCode 12 has MACOSX_DEPLOYMENT_TARGET=10.14.6. Selecting a lower target would cause /usr/bin/python3 to be rejected by configure. macos-10.14: MACOS_SDK=/Library/Developer/CommandLineTools/SDKs/MacOSX10.14.sdk From dffcdbc75e46d08bcb1fd2ac9a5677a7a7312e2f Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 27 Mar 2021 23:43:23 -0700 Subject: [PATCH 248/406] tox.ini (macos-nohomebrew): Fix up pillow build by setting ZLIB_ROOT --- tox.ini | 1 + 1 file changed, 1 insertion(+) diff --git a/tox.ini b/tox.ini index 77b814c8ef5..f419b2fe8a2 100644 --- a/tox.ini +++ b/tox.ini @@ -425,6 +425,7 @@ setenv = macos-nohomebrew: LIBGD_CONFIGURE=--without-freetype --without-raqm --without-fontconfig --without-jpeg --without-liq --without-xpm --without-tiff --without-webp --without-heif --without-avif macos-nohomebrew: FREETYPE_CONFIGURE=--without-harfbuzz macos-nohomebrew: PILLOW_BUILD_EXT=--disable-platform-guessing + macos-nohomebrew: ZLIB_ROOT={env:MACOS_SDK}/usr macos: MACOS_SDK=/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk # python3 from XCode 12 has MACOSX_DEPLOYMENT_TARGET=10.14.6. Selecting a lower target would cause /usr/bin/python3 to be rejected by configure. macos-10.14: MACOS_SDK=/Library/Developer/CommandLineTools/SDKs/MacOSX10.14.sdk From d83c82bbeb19fecebbcdb03c709fc81821b8ea32 Mon Sep 17 00:00:00 2001 From: Christian Wuthrich Date: Sun, 28 Mar 2021 14:07:15 +0100 Subject: [PATCH 249/406] change sigma in formal groups of elliptic curves --- .../schemes/elliptic_curves/formal_group.py | 41 +++++++++++++++---- 1 file changed, 32 insertions(+), 9 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/formal_group.py b/src/sage/schemes/elliptic_curves/formal_group.py index 07e809026d0..3037bb6d605 100644 --- a/src/sage/schemes/elliptic_curves/formal_group.py +++ b/src/sage/schemes/elliptic_curves/formal_group.py @@ -733,32 +733,55 @@ def mult_by_n(self, n, prec=10): def sigma(self, prec=10): """ + Returns the Weierstrass sigma function as a formal powerseries + solution to the differential equation + + .. MATH:: + + \frac{d^2 \log \sigma}{dz^2} = - \wp(z) + + with initial conditions `\sigma(O)=0` and `\sigma'(O)=1`, + expressed in the variable `t=\log_E(z)` of the formal group. + + INPUT: + + - ``prec`` - integer (default 10) + + OUTPUT: a power series with given precision + + Other solutions can be obtained by multiplication with + an function of the form `\exp(c z^2)`. + If the curve has good ordinary reduction at a prime `p` + then there is a canonical choice of `c` that produces + the canonical `p`-adic sigma function. + To obtain that, please use ``E.padic_sigma(p)`` instead. + See :func:`~sage.schemes.elliptic_curves.padics.padic_sigma` + EXAMPLES:: sage: E = EllipticCurve('14a') sage: F = E.formal_group() sage: F.sigma(5) - t + 1/2*t^2 + (1/2*c + 1/3)*t^3 + (3/4*c + 3/4)*t^4 + O(t^5) + t + 1/2*t^2 + 1/3*t^3 + 3/4*t^4 + O(t^5) """ a1,a2,a3,a4,a6 = self.curve().ainvs() k = self.curve().base_ring() fl = self.log(prec) - R = rings.PolynomialRing(k, 'c') - c = R.gen() + #R = rings.PolynomialRing(k, 'c') + #c = R.gen() F = fl.reverse() - S = rings.LaurentSeriesRing(R,'z') - c = S(c) + S = rings.LaurentSeriesRing(k,'z') + #c = S(c) z = S.gen() F = F(z + O(z**prec)) - wp = self.x()(F) - e2 = 12*c - a1**2 - 4*a2 - g = (1/z**2 - wp + e2/12).power_series() + wp = self.x()(F) + (a1**2 + 4*a2)/12 + g = (1/z**2 - wp).power_series() h = g.integral().integral() sigma_of_z = z.power_series() * h.exp() - T = rings.PowerSeriesRing(R,'t') + T = rings.PowerSeriesRing(k,'t') fl = fl(T.gen()+O(T.gen()**prec)) sigma_of_t = sigma_of_z(fl) return sigma_of_t From fa93ec876ba881dffd2fcb2853201759b30a4179 Mon Sep 17 00:00:00 2001 From: Christian Wuthrich Date: Sun, 28 Mar 2021 17:02:30 +0100 Subject: [PATCH 250/406] docstring improvement --- src/sage/schemes/elliptic_curves/formal_group.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/formal_group.py b/src/sage/schemes/elliptic_curves/formal_group.py index 3037bb6d605..84be3dd8827 100644 --- a/src/sage/schemes/elliptic_curves/formal_group.py +++ b/src/sage/schemes/elliptic_curves/formal_group.py @@ -732,14 +732,14 @@ def mult_by_n(self, n, prec=10): return result def sigma(self, prec=10): - """ + r""" Returns the Weierstrass sigma function as a formal powerseries solution to the differential equation .. MATH:: - + \frac{d^2 \log \sigma}{dz^2} = - \wp(z) - + with initial conditions `\sigma(O)=0` and `\sigma'(O)=1`, expressed in the variable `t=\log_E(z)` of the formal group. @@ -755,7 +755,7 @@ def sigma(self, prec=10): then there is a canonical choice of `c` that produces the canonical `p`-adic sigma function. To obtain that, please use ``E.padic_sigma(p)`` instead. - See :func:`~sage.schemes.elliptic_curves.padics.padic_sigma` + See :meth:`~sage.schemes.elliptic_curves.ell_rational_field.EllipticCurve_rational_field.padic_sigma` EXAMPLES:: From d8fd8c6942d78393afaec4556c4bb4015d3e8aa4 Mon Sep 17 00:00:00 2001 From: Christian Wuthrich Date: Sun, 28 Mar 2021 17:03:04 +0100 Subject: [PATCH 251/406] small correction in docstring of elliptic_curves rank --- src/sage/schemes/elliptic_curves/ec_database.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/schemes/elliptic_curves/ec_database.py b/src/sage/schemes/elliptic_curves/ec_database.py index 0c98d286992..4c221715432 100644 --- a/src/sage/schemes/elliptic_curves/ec_database.py +++ b/src/sage/schemes/elliptic_curves/ec_database.py @@ -74,7 +74,7 @@ class EllipticCurves: def rank(self, rank, tors=0, n=10, labels=False): r""" - Return a list of at most `n` non-isogenous curves with given + Return a list of at most `n` curves with given rank and torsion order. INPUT: From 0c0caa2c63903a3a3aed83001672b8dc32c02abb Mon Sep 17 00:00:00 2001 From: "John H. Palmieri" Date: Sun, 28 Mar 2021 17:09:02 -0700 Subject: [PATCH 252/406] trac 31575: remove the full boost package --- build/pkgs/boost/SPKG.rst | 23 ----------- build/pkgs/boost/checksums.ini | 4 -- build/pkgs/boost/dependencies | 5 --- build/pkgs/boost/distros/arch.txt | 1 - build/pkgs/boost/distros/conda.txt | 1 - build/pkgs/boost/distros/cygwin.txt | 1 - build/pkgs/boost/distros/debian.txt | 1 - build/pkgs/boost/distros/fedora.txt | 1 - build/pkgs/boost/distros/freebsd.txt | 1 - build/pkgs/boost/distros/homebrew.txt | 1 - build/pkgs/boost/distros/macports.txt | 1 - build/pkgs/boost/distros/nix.txt | 1 - build/pkgs/boost/distros/opensuse.txt | 1 - build/pkgs/boost/distros/repology.txt | 1 - build/pkgs/boost/distros/slackware.txt | 1 - build/pkgs/boost/distros/void.txt | 1 - build/pkgs/boost/package-version.txt | 1 - build/pkgs/boost/spkg-configure.m4 | 11 ------ build/pkgs/boost/spkg-install.in | 53 -------------------------- build/pkgs/boost/type | 1 - 20 files changed, 111 deletions(-) delete mode 100644 build/pkgs/boost/SPKG.rst delete mode 100644 build/pkgs/boost/checksums.ini delete mode 100644 build/pkgs/boost/dependencies delete mode 100644 build/pkgs/boost/distros/arch.txt delete mode 100644 build/pkgs/boost/distros/conda.txt delete mode 100644 build/pkgs/boost/distros/cygwin.txt delete mode 100644 build/pkgs/boost/distros/debian.txt delete mode 100644 build/pkgs/boost/distros/fedora.txt delete mode 100644 build/pkgs/boost/distros/freebsd.txt delete mode 100644 build/pkgs/boost/distros/homebrew.txt delete mode 100644 build/pkgs/boost/distros/macports.txt delete mode 100644 build/pkgs/boost/distros/nix.txt delete mode 100644 build/pkgs/boost/distros/opensuse.txt delete mode 100644 build/pkgs/boost/distros/repology.txt delete mode 100644 build/pkgs/boost/distros/slackware.txt delete mode 100644 build/pkgs/boost/distros/void.txt delete mode 100644 build/pkgs/boost/package-version.txt delete mode 100644 build/pkgs/boost/spkg-configure.m4 delete mode 100644 build/pkgs/boost/spkg-install.in delete mode 100644 build/pkgs/boost/type diff --git a/build/pkgs/boost/SPKG.rst b/build/pkgs/boost/SPKG.rst deleted file mode 100644 index 29dea49c992..00000000000 --- a/build/pkgs/boost/SPKG.rst +++ /dev/null @@ -1,23 +0,0 @@ -boost: Portable C++ libraries (full set) -======================================== - -Description ------------ - -Boost provides free peer-reviewed portable C++ source libraries. - -License -------- - -Boost software license (GPL compatible) - - -Upstream Contact ----------------- - -Home page: http://boost.org - -Dependencies ------------- - -None diff --git a/build/pkgs/boost/checksums.ini b/build/pkgs/boost/checksums.ini deleted file mode 100644 index 6c4c0eda123..00000000000 --- a/build/pkgs/boost/checksums.ini +++ /dev/null @@ -1,4 +0,0 @@ -tarball=boost_VERSION.tar.bz2 -sha1=b6b284acde2ad7ed49b44e856955d7b1ea4e9459 -md5=b2dfbd6c717be4a7bb2d88018eaccf75 -cksum=2976203026 diff --git a/build/pkgs/boost/dependencies b/build/pkgs/boost/dependencies deleted file mode 100644 index 6a9b467fe73..00000000000 --- a/build/pkgs/boost/dependencies +++ /dev/null @@ -1,5 +0,0 @@ -iconv zlib - ----------- -All lines of this file are ignored except the first. -It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/boost/distros/arch.txt b/build/pkgs/boost/distros/arch.txt deleted file mode 100644 index d579dbe4edb..00000000000 --- a/build/pkgs/boost/distros/arch.txt +++ /dev/null @@ -1 +0,0 @@ -boost diff --git a/build/pkgs/boost/distros/conda.txt b/build/pkgs/boost/distros/conda.txt deleted file mode 100644 index 93de4003ce3..00000000000 --- a/build/pkgs/boost/distros/conda.txt +++ /dev/null @@ -1 +0,0 @@ -boost-cpp diff --git a/build/pkgs/boost/distros/cygwin.txt b/build/pkgs/boost/distros/cygwin.txt deleted file mode 100644 index 444ab77a410..00000000000 --- a/build/pkgs/boost/distros/cygwin.txt +++ /dev/null @@ -1 +0,0 @@ -libboost-devel diff --git a/build/pkgs/boost/distros/debian.txt b/build/pkgs/boost/distros/debian.txt deleted file mode 100644 index c65aa1f9284..00000000000 --- a/build/pkgs/boost/distros/debian.txt +++ /dev/null @@ -1 +0,0 @@ -libboost-dev diff --git a/build/pkgs/boost/distros/fedora.txt b/build/pkgs/boost/distros/fedora.txt deleted file mode 100644 index fee2552239a..00000000000 --- a/build/pkgs/boost/distros/fedora.txt +++ /dev/null @@ -1 +0,0 @@ -boost-devel diff --git a/build/pkgs/boost/distros/freebsd.txt b/build/pkgs/boost/distros/freebsd.txt deleted file mode 100644 index 1ad13b6e2f0..00000000000 --- a/build/pkgs/boost/distros/freebsd.txt +++ /dev/null @@ -1 +0,0 @@ -devel/boost-libs diff --git a/build/pkgs/boost/distros/homebrew.txt b/build/pkgs/boost/distros/homebrew.txt deleted file mode 100644 index d579dbe4edb..00000000000 --- a/build/pkgs/boost/distros/homebrew.txt +++ /dev/null @@ -1 +0,0 @@ -boost diff --git a/build/pkgs/boost/distros/macports.txt b/build/pkgs/boost/distros/macports.txt deleted file mode 100644 index d579dbe4edb..00000000000 --- a/build/pkgs/boost/distros/macports.txt +++ /dev/null @@ -1 +0,0 @@ -boost diff --git a/build/pkgs/boost/distros/nix.txt b/build/pkgs/boost/distros/nix.txt deleted file mode 100644 index d579dbe4edb..00000000000 --- a/build/pkgs/boost/distros/nix.txt +++ /dev/null @@ -1 +0,0 @@ -boost diff --git a/build/pkgs/boost/distros/opensuse.txt b/build/pkgs/boost/distros/opensuse.txt deleted file mode 100644 index fee2552239a..00000000000 --- a/build/pkgs/boost/distros/opensuse.txt +++ /dev/null @@ -1 +0,0 @@ -boost-devel diff --git a/build/pkgs/boost/distros/repology.txt b/build/pkgs/boost/distros/repology.txt deleted file mode 100644 index d579dbe4edb..00000000000 --- a/build/pkgs/boost/distros/repology.txt +++ /dev/null @@ -1 +0,0 @@ -boost diff --git a/build/pkgs/boost/distros/slackware.txt b/build/pkgs/boost/distros/slackware.txt deleted file mode 100644 index d579dbe4edb..00000000000 --- a/build/pkgs/boost/distros/slackware.txt +++ /dev/null @@ -1 +0,0 @@ -boost diff --git a/build/pkgs/boost/distros/void.txt b/build/pkgs/boost/distros/void.txt deleted file mode 100644 index fee2552239a..00000000000 --- a/build/pkgs/boost/distros/void.txt +++ /dev/null @@ -1 +0,0 @@ -boost-devel diff --git a/build/pkgs/boost/package-version.txt b/build/pkgs/boost/package-version.txt deleted file mode 100644 index 4340647cb21..00000000000 --- a/build/pkgs/boost/package-version.txt +++ /dev/null @@ -1 +0,0 @@ -1_66_0 diff --git a/build/pkgs/boost/spkg-configure.m4 b/build/pkgs/boost/spkg-configure.m4 deleted file mode 100644 index 73d231f15a6..00000000000 --- a/build/pkgs/boost/spkg-configure.m4 +++ /dev/null @@ -1,11 +0,0 @@ -SAGE_SPKG_CONFIGURE([boost], [ - SAGE_SPKG_DEPCHECK([boost_cropped], [ - AC_RUN_IFELSE([dnl an extra sanity check - AC_LANG_PROGRAM( - [[#include - ]], [[ - boost::program_options::error err("Error message"); - return 0; - ]])], [], [sage_spkg_install_boost=yes]) - ]) -]) diff --git a/build/pkgs/boost/spkg-install.in b/build/pkgs/boost/spkg-install.in deleted file mode 100644 index 348a434de5d..00000000000 --- a/build/pkgs/boost/spkg-install.in +++ /dev/null @@ -1,53 +0,0 @@ -cd src - -echo "Running boost bootstrap" -# We provide the toolset as the only toolset properly auto-detected are -# gcc (or icc) on linux -# clang on OS X -# but not clang on linux for example -# First sanitize toolset name from CC, it assume something of the form -# CC=/path/to/CC_name-version at worst. -# Please do not mix CC and CXX from different vendors. -C_COMPILER_NAME=${CC##*/} -C_COMPILER_NAME=${C_COMPILER_NAME%-*} -BOOST_TOOLSET="" -if [ "$UNAME" = "Darwin" ]; then - # On darwin provided the compiler is clang, default is fine. - # Remember that /usr/bin/gcc is clang. - if [ "$(which $C_COMPILER_NAME)" != "/usr/bin/gcc" -a "$C_COMPILER_NAME" != "clang" ]; then - BOOST_TOOLSET="--with-toolset=$C_COMPILER_NAME" - fi -else - BOOST_TOOLSET="--with-toolset=$C_COMPILER_NAME" -fi - -./bootstrap.sh $BOOST_TOOLSET -if [[ $? -ne 0 ]]; then - echo >&2 "Failed to bootstrap boost." - exit 1 -fi - -echo "Building boost" -# By default this is populated by a system value. -# If the boost build system (b2, bjam and associated files under /usr/share/boost-build) -# has been installed system wide it can cause interference. -# The build will fail purely and simply without much of an explanation. -# see http://trac.sagemath.org/ticket/20776 for details. -export BOOST_BUILD_PATH="${SAGE_LOCAL}"/share/boost-build -./b2 -if [[ $? -ne 0 ]]; then - echo >&2 "Failed to build boost." - exit 1 -fi - -echo "Clean out old boost headers and libraries" -rm -rf "$SAGE_LOCAL"/include/boost -rm -rf "$SAGE_LOCAL"/lib/libboost* - -echo "Installing boost" -./b2 install --prefix="$SAGE_LOCAL" -if [[ $? -ne 0 ]]; then - echo >&2 "Failed to install boost." - exit 1 -fi - diff --git a/build/pkgs/boost/type b/build/pkgs/boost/type deleted file mode 100644 index 134d9bc32d5..00000000000 --- a/build/pkgs/boost/type +++ /dev/null @@ -1 +0,0 @@ -optional From 3044c82996c64f9bbbe5822f34449b653f0bfba9 Mon Sep 17 00:00:00 2001 From: "John H. Palmieri" Date: Sun, 28 Mar 2021 18:18:22 -0700 Subject: [PATCH 253/406] trac 31575: keep distro files nix.txt, void.txt for boost, but move to boost_cropped/distros. --- build/pkgs/boost_cropped/distros/nix.txt | 1 + build/pkgs/boost_cropped/distros/void.txt | 1 + 2 files changed, 2 insertions(+) create mode 100644 build/pkgs/boost_cropped/distros/nix.txt create mode 100644 build/pkgs/boost_cropped/distros/void.txt diff --git a/build/pkgs/boost_cropped/distros/nix.txt b/build/pkgs/boost_cropped/distros/nix.txt new file mode 100644 index 00000000000..d579dbe4edb --- /dev/null +++ b/build/pkgs/boost_cropped/distros/nix.txt @@ -0,0 +1 @@ +boost diff --git a/build/pkgs/boost_cropped/distros/void.txt b/build/pkgs/boost_cropped/distros/void.txt new file mode 100644 index 00000000000..fee2552239a --- /dev/null +++ b/build/pkgs/boost_cropped/distros/void.txt @@ -0,0 +1 @@ +boost-devel From 3779a2525d5bbf0987a1d4ad0e42b73fb5769bc0 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 28 Mar 2021 18:58:54 -0700 Subject: [PATCH 254/406] tox.ini (macos-nohomebrew): Disable more libraries to remove dependencies on homebrew --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index f419b2fe8a2..0a4af93e754 100644 --- a/tox.ini +++ b/tox.ini @@ -424,7 +424,7 @@ setenv = # - We use libgd only used in a very limited way, in {matrix,vector}_mod_2_dense. Disable search for other packages. macos-nohomebrew: LIBGD_CONFIGURE=--without-freetype --without-raqm --without-fontconfig --without-jpeg --without-liq --without-xpm --without-tiff --without-webp --without-heif --without-avif macos-nohomebrew: FREETYPE_CONFIGURE=--without-harfbuzz - macos-nohomebrew: PILLOW_BUILD_EXT=--disable-platform-guessing + macos-nohomebrew: PILLOW_BUILD_EXT=--disable-platform-guessing --disable-jpeg2000 --disable-imagequant --disable-tiff --disable-lcms --disable-webp --disable-webpmux --disable-xcb macos-nohomebrew: ZLIB_ROOT={env:MACOS_SDK}/usr macos: MACOS_SDK=/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk # python3 from XCode 12 has MACOSX_DEPLOYMENT_TARGET=10.14.6. Selecting a lower target would cause /usr/bin/python3 to be rejected by configure. From 92e0a720c6e0a15560ebe1a92597c406edaa5ab7 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Mon, 29 Mar 2021 17:18:53 +1000 Subject: [PATCH 255/406] Refactor into single classes with classcall method and some cleanup. --- src/sage/combinat/parking_functions.py | 432 +++++++++++-------------- 1 file changed, 187 insertions(+), 245 deletions(-) diff --git a/src/sage/combinat/parking_functions.py b/src/sage/combinat/parking_functions.py index c5f0c9862a1..8a515a29c33 100644 --- a/src/sage/combinat/parking_functions.py +++ b/src/sage/combinat/parking_functions.py @@ -63,7 +63,6 @@ # https://www.gnu.org/licenses/ # **************************************************************************** from typing import NewType, Iterator, Tuple -from copy import copy from sage.rings.integer import Integer from sage.rings.rational_field import QQ @@ -73,6 +72,7 @@ from sage.combinat.dyck_word import DyckWord from sage.combinat.combinatorial_map import combinatorial_map from sage.misc.prandom import randint +from sage.misc.inherit_comparison import InheritComparisonClasscallMetaclass from sage.rings.finite_rings.integer_mod_ring import Zmod from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets from sage.categories.infinite_enumerated_sets import InfiniteEnumeratedSets @@ -81,94 +81,7 @@ from sage.structure.unique_representation import UniqueRepresentation -PF = NewType('PF', 'ParkingFunction_class') - - -def ParkingFunctions(n=None): - r""" - Return the combinatorial class of Parking Functions. - - A *parking function* of size `n` is a sequence `(a_1, \ldots,a_n)` - of positive integers such that if `b_1 \leq b_2 \leq \cdots \leq b_n` is - the increasing rearrangement of `a_1, \ldots, a_n`, then `b_i \leq i`. - - A *parking function* of size `n` is a pair `(L, D)` of two sequences - `L` and `D` where `L` is a permutation and `D` is an area sequence - of a Dyck Path of size n such that `D[i] \geq 0`, `D[i+1] \leq D[i]+1` - and if `D[i+1] = D[i]+1` then `L[i+1] > L[i]`. - - The number of parking functions of size `n` is equal to the number - of rooted forests on `n` vertices and is equal to `(n+1)^{n-1}`. - - EXAMPLES: - - Here are all parking functions of size 3:: - - sage: from sage.combinat.parking_functions import ParkingFunctions - sage: ParkingFunctions(3).list() - [[1, 1, 1], [1, 1, 2], [1, 2, 1], [2, 1, 1], [1, 1, 3], [1, 3, 1], [3, 1, 1], - [1, 2, 2], [2, 1, 2], [2, 2, 1], [1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], - [3, 1, 2], [3, 2, 1]] - - If no size is specified, then ParkingFunctions returns the - combinatorial class of all parking functions. :: - - sage: PF = ParkingFunctions(); PF - Parking functions - sage: [] in PF - True - sage: [1] in PF - True - sage: [2] in PF - False - sage: [1,3,1] in PF - True - sage: [1,4,1] in PF - False - - If the size `n` is specified, then ParkingFunctions returns - the combinatorial class of all parking functions of size `n`. - - :: - - sage: PF = ParkingFunctions(0) - sage: PF.list() - [[]] - sage: PF = ParkingFunctions(1) - sage: PF.list() - [[1]] - sage: PF = ParkingFunctions(3) - sage: PF.list() - [[1, 1, 1], [1, 1, 2], [1, 2, 1], [2, 1, 1], [1, 1, 3], - [1, 3, 1], [3, 1, 1], [1, 2, 2], [2, 1, 2], [2, 2, 1], - [1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]] - - :: - - sage: PF3 = ParkingFunctions(3); PF3 - Parking functions of size 3 - sage: [] in PF3 - False - sage: [1] in PF3 - False - sage: [1,3,1] in PF3 - True - sage: [1,4,1] in PF3 - False - - TESTS:: - - sage: PF = ParkingFunctions(5) - sage: TestSuite(PF).run() - sage: len(PF.list()) == PF.cardinality() - True - """ - if n is None: - return ParkingFunctions_all() - - if not isinstance(n, (Integer, int)) or n < 0: - raise ValueError("%s is not a non-negative integer." % n) - return ParkingFunctions_n(n) +PF = NewType('PF', 'ParkingFunction') def is_a(x, n=None) -> bool: @@ -195,11 +108,9 @@ def is_a(x, n=None) -> bool: A = sorted(x) return check_NDPF(A, n) - -def ParkingFunction(pf=None, labelling=None, area_sequence=None, - labelled_dyck_word=None): +class ParkingFunction(ClonableArray, metaclass=InheritComparisonClasscallMetaclass): r""" - Return a Parking Function. + A Parking Function. A *parking function* of size `n` is a sequence `(a_1, \ldots,a_n)` of positive integers such that if `b_1 \leq b_2 \leq \cdots \leq b_n` is @@ -215,13 +126,15 @@ def ParkingFunction(pf=None, labelling=None, area_sequence=None, INPUT: - - ``pf`` -- (default: None) a list whose increasing rearrangement satisfies `b_i \leq i` + - ``pf`` -- (default: ``None``) a list whose increasing rearrangement + satisfies `b_i \leq i` - - ``labelling`` -- (default: None) a labelling of the Dyck path + - ``labelling`` -- (default: ``None``) a labelling of the Dyck path - - ``area_sequence`` -- (default: None) an area sequence of a Dyck path + - ``area_sequence`` -- (default: ``None``) an area sequence of a Dyck path - - ``labelled_dyck_word`` -- (default: None) a Dyck word with 1's replaced by labelling + - ``labelled_dyck_word`` -- (default: ``None``) a Dyck word with 1's + replaced by labelling OUTPUT: @@ -249,36 +162,47 @@ def ParkingFunction(pf=None, labelling=None, area_sequence=None, [2, 2, 1] sage: ParkingFunction([2,2,1]).to_labelled_dyck_word() [3, 0, 1, 2, 0, 0] - sage: ParkingFunction(labelled_dyck_word = [3,0,1,2,0,0]) + sage: ParkingFunction(labelled_dyck_word=[3,0,1,2,0,0]) [2, 2, 1] sage: ParkingFunction(labelling=[3,1,2], area_sequence=[0,1,1]) Traceback (most recent call last): ... ValueError: [3, 1, 2] is not a valid labeling of area sequence [0, 1, 1] """ - if isinstance(pf, ParkingFunction_class): - return pf - if pf is not None: - return ParkingFunction_class(None, pf) - elif labelling is not None: - if (area_sequence is None): - raise ValueError("must also provide area sequence along with labelling.") - if (len(area_sequence) != len(labelling)): - raise ValueError("%s must be the same size as the labelling %s" % (area_sequence, labelling)) - if any(area_sequence[i] < area_sequence[i + 1] and labelling[i] > labelling[i + 1] for i in range(len(labelling) - 1)): - raise ValueError("%s is not a valid labeling of area sequence %s" % (labelling, area_sequence)) - return from_labelling_and_area_sequence(labelling, area_sequence) - elif labelled_dyck_word is not None: - return from_labelled_dyck_word(labelled_dyck_word) - elif area_sequence is not None: - DW = DyckWord(area_sequence) - return ParkingFunction(labelling=list(range(1, DW.size() + 1)), - area_sequence=DW) - - raise ValueError("did not manage to make this into a parking function") - - -class ParkingFunction_class(ClonableArray): + @staticmethod + def __classcall_private__(cls, pf=None, labelling=None, area_sequence=None, + labelled_dyck_word=None): + """ + Construct a parking function based on the input. + + TESTS:: + + sage: PF = ParkingFunction([1,2]) + sage: isinstance(PF, ParkingFunctions().element_class) + True + """ + if isinstance(pf, ParkingFunction): + return pf + if pf is not None: + PF = ParkingFunctions() + return PF.element_class(PF, pf) + elif labelling is not None: + if (area_sequence is None): + raise ValueError("must also provide area sequence along with labelling.") + if (len(area_sequence) != len(labelling)): + raise ValueError("%s must be the same size as the labelling %s" % (area_sequence, labelling)) + if any(area_sequence[i] < area_sequence[i + 1] and labelling[i] > labelling[i + 1] for i in range(len(labelling) - 1)): + raise ValueError("%s is not a valid labeling of area sequence %s" % (labelling, area_sequence)) + return from_labelling_and_area_sequence(labelling, area_sequence) + elif labelled_dyck_word is not None: + return from_labelled_dyck_word(labelled_dyck_word) + elif area_sequence is not None: + DW = DyckWord(area_sequence) + return ParkingFunction(labelling=list(range(1, DW.size() + 1)), + area_sequence=DW) + + raise ValueError("did not manage to make this into a parking function") + def __init__(self, parent, lst): """ TESTS:: @@ -300,7 +224,7 @@ def __init__(self, parent, lst): sage: type(b) """ - if isinstance(lst, ParkingFunction_class): + if isinstance(lst, ParkingFunction): lst = list(lst) if not isinstance(lst, list): raise TypeError('input must be a list') @@ -351,14 +275,11 @@ def diagonal_reading_word(self) -> Permutation: Return a diagonal word of the labelled Dyck path corresponding to parking function (see [Hag08]_ p. 75). - INPUT: - - - ``self`` -- parking function word - OUTPUT: - - returns a word, read diagonally from NE to SW of the pretty print of the - labelled Dyck path that corresponds to ``self`` and the same size as ``self`` + - returns a word, read diagonally from NE to SW of the pretty + print of the labelled Dyck path that corresponds to ``self`` + and the same size as ``self`` EXAMPLES:: @@ -403,10 +324,6 @@ def parking_permutation(self) -> Permutation: spot 5 by car 3, spot 2 is taken by car 4, spot 3 is taken by car 5, spot 4 is taken by car 6 and spot 7 is taken by car 7. - INPUT: - - - ``self`` -- parking function word - OUTPUT: - the permutation of parking spots that corresponds to @@ -438,12 +355,9 @@ def cars_permutation(self) -> Permutation: and corresponding to the parking function. For example, ``cars_permutation(PF) = [2, 4, 5, 6, 3, 1, 7]`` - means that car 2 takes spots 1, car 4 takes spot 2, ..., car 1 takes spot 6 and - car 7 takes spot 7. + means that car 2 takes spots 1, car 4 takes spot 2, ..., car 1 + takes spot 6 and car 7 takes spot 7. - INPUT: - - - ``self`` -- parking function word OUTPUT: @@ -479,12 +393,9 @@ def jump_list(self) -> list: # cars displacements For example, ``jump_list(PF) = [0, 0, 0, 0, 1, 3, 2]`` means that car 1 through 4 parked in their preferred spots, - car 5 had to park one spot farther (jumped or was displaced by one spot), - car 6 had to jump 3 spots, and car 7 had to jump two spots. - - INPUT: + car 5 had to park one spot farther (jumped or was displaced by one + spot), car 6 had to jump 3 spots, and car 7 had to jump two spots. - - ``self`` -- parking function word OUTPUT: @@ -516,10 +427,6 @@ def jump(self) -> Integer: # sum of all jumps, sum of all displacements See [Shin]_ p. 18. - INPUT: - - - ``self`` -- a parking function word - OUTPUT: - the sum of the differences between the parked and preferred parking @@ -545,12 +452,9 @@ def jump(self) -> Integer: # sum of all jumps, sum of all displacements def lucky_cars(self): # the set of cars that can park in their preferred spots r""" Return the cars that can park in their preferred spots. For example, - ``lucky_cars(PF) = [1, 2, 7]`` means that cars 1, 2 and 7 parked in their - preferred spots and all the other cars did not. - - INPUT: + ``lucky_cars(PF) = [1, 2, 7]`` means that cars 1, 2 and 7 parked in + their preferred spots and all the other cars did not. - - ``self`` -- parking function word OUTPUT: @@ -579,10 +483,6 @@ def luck(self) -> Integer: # the number of lucky cars Return the number of cars that parked in their preferred parking spots (see [Shin]_ p. 33). - INPUT: - - - ``self`` -- parking function word - OUTPUT: - the number of cars that parked in their preferred parking spots @@ -606,12 +506,8 @@ def luck(self) -> Integer: # the number of lucky cars def primary_dinversion_pairs(self): r""" - Return the primary descent inversion pairs of a labelled Dyck path corresponding - to the parking function. - - INPUT: - - - ``self`` -- parking function word + Return the primary descent inversion pairs of a labelled Dyck path + corresponding to the parking function. OUTPUT: @@ -643,10 +539,6 @@ def secondary_dinversion_pairs(self): Return the secondary descent inversion pairs of a labelled Dyck path corresponding to the parking function. - INPUT: - - - ``self`` -- parking function word - OUTPUT: - the pairs `(i, j)` such that `i < j`, and `i^{th}` area = `j^{th}` area +1, @@ -677,10 +569,6 @@ def dinversion_pairs(self) -> list: Return the descent inversion pairs of a labelled Dyck path corresponding to the parking function. - INPUT: - - - ``self`` -- parking function word - OUTPUT: - the primary and secondary diversion pairs @@ -709,10 +597,6 @@ def dinv(self) -> Integer: Same as the cardinality of :meth:`dinversion_pairs`. - INPUT: - - - ``self`` -- parking function word - OUTPUT: - the number of dinversion pairs @@ -739,10 +623,6 @@ def area(self) -> Integer: Return the area of the labelled Dyck path corresponding to the parking function. - INPUT: - - - ``self`` -- parking function word - OUTPUT: - the sum of squares under and over the main diagonal the Dyck Path, @@ -778,10 +658,6 @@ def ides_composition(self): :meth:`diagonal_reading_word` of the parking function with word ``PF`` are at the 4th and 6th positions. - INPUT: - - - ``self`` -- parking function word - OUTPUT: - the descents composition of the inverse of the @@ -806,8 +682,9 @@ def ides_composition(self): def ides(self): r""" - Return the :meth:`~sage.combinat.permutation.Permutation.descents` sequence - of the inverse of the :meth:`diagonal_reading_word` of ``self``. + Return the :meth:`~sage.combinat.permutation.Permutation.descents` + sequence of the inverse of the :meth:`diagonal_reading_word` + of ``self``. .. WARNING:: @@ -815,14 +692,10 @@ def ides(self): start at `1`. This behaviour has been changed in :trac:`20555`. - For example, ``ides(PF) = [2, 3, 4, 6]`` means that descents are at the 2nd, 3rd, - 4th and 6th positions in the inverse of the + For example, ``ides(PF) = [2, 3, 4, 6]`` means that descents are at + the 2nd, 3rd, 4th and 6th positions in the inverse of the :meth:`diagonal_reading_word` of the parking function (see [GXZ]_ p. 2). - INPUT: - - - ``self`` -- parking function word - OUTPUT: - the descents sequence of the inverse of the @@ -847,17 +720,13 @@ def ides(self): def touch_points(self): r""" - Return the sequence of touch points which corresponds to the labelled Dyck path - after initial step. + Return the sequence of touch points which corresponds to the + labelled Dyck path after initial step. For example, ``touch_points(PF) = [4, 7]`` means that after the initial step, the path touches the main diagonal at points `(4, 4)` and `(7, 7)`. - INPUT: - - - ``self`` -- parking function word - OUTPUT: - the sequence of touch points after the initial step of the @@ -890,10 +759,6 @@ def touch_composition(self): first touch is four diagonal units from the starting point, and the second is three units further (see [GXZ]_ p. 2). - INPUT: - - - ``self`` -- parking function word - OUTPUT: - the length between the corresponding touch points which @@ -923,10 +788,6 @@ def to_labelling_permutation(self) -> Permutation: r""" Return the labelling of the support Dyck path of the parking function. - INPUT: - - - ``self`` -- parking function word - OUTPUT: - the labelling of the Dyck path @@ -949,15 +810,11 @@ def to_labelling_permutation(self) -> Permutation: from sage.combinat.words.word import Word return Word(self).standard_permutation().inverse() - def to_area_sequence(self): + def to_area_sequence(self) -> list: r""" Return the area sequence of the support Dyck path of the parking function. - INPUT: - - - ``self`` -- parking function word - OUTPUT: - the area sequence of the Dyck path @@ -986,10 +843,6 @@ def to_labelling_area_sequence_pair(self): of a Dyck path which corresponds to the given parking function. - INPUT: - - - ``self`` -- the parking function word - OUTPUT: - returns a pair ``(L, D)`` where ``L`` is a labelling and ``D`` is the @@ -1019,10 +872,6 @@ def to_dyck_word(self) -> DyckWord: r""" Return the support Dyck word of the parking function. - INPUT: - - - ``self`` -- parking function word - OUTPUT: - the Dyck word of the corresponding parking function @@ -1054,10 +903,6 @@ def to_labelled_dyck_word(self): where the entries of 1 in the Dyck word are replaced with the corresponding label. - INPUT: - - - ``self`` -- parking function word - OUTPUT: - the labelled Dyck word of the corresponding parking function @@ -1079,7 +924,7 @@ def to_labelled_dyck_word(self): [2, 4, 0, 1, 0, 0, 3, 0] """ dw = self.to_dyck_word() - out = list(copy(self.to_labelling_permutation())) + out = list(self.to_labelling_permutation()) for i in range(2 * len(out)): if dw[i] == 0: out.insert(i, 0) @@ -1090,10 +935,6 @@ def to_labelling_dyck_word_pair(self) -> Tuple[Permutation, DyckWord]: Return the pair ``(L, D)`` where ``L`` is a labelling and ``D`` is the Dyck word of the parking function. - INPUT: - - - ``self`` -- parking function word - OUTPUT: - the pair ``(L, D)``, where ``L`` is the labelling and ``D`` is @@ -1124,10 +965,6 @@ def to_NonDecreasingParkingFunction(self) -> PF: Return the non-decreasing parking function which underlies the parking function. - INPUT: - - - ``self`` -- parking function word - OUTPUT: - a sorted parking function @@ -1338,10 +1175,17 @@ def from_labelling_and_area_sequence(L, D) -> PF: [1, 1, 2] sage: from_labelling_and_area_sequence([1, 2, 4, 3], [0, 1, 2, 1]) [1, 1, 3, 1] + + TESTS:: + + sage: PF = from_labelling_and_area_sequence([1, 2, 4, 3], [0, 1, 2, 1]) + sage: isinstance(PF, ParkingFunctions().element_class) + True """ - return ParkingFunction_class(None, - [L.index(i) + 1 - D[L.index(i)] - for i in range(1, len(L) + 1)]) + PF = ParkingFunctions_all() + return PF.element_class(PF, + [L.index(i) + 1 - D[L.index(i)] + for i in range(1, len(L) + 1)]) def from_labelled_dyck_word(LDW) -> PF: @@ -1377,8 +1221,105 @@ def from_labelled_dyck_word(LDW) -> PF: D = DyckWord([Integer(not x.is_zero()) for x in LDW]) return from_labelling_and_area_sequence(L, D.to_area_sequence()) +class ParkingFunctions(UniqueRepresentation, Parent): + r""" + Return the combinatorial class of Parking Functions. + + A *parking function* of size `n` is a sequence `(a_1, \ldots,a_n)` + of positive integers such that if `b_1 \leq b_2 \leq \cdots \leq b_n` is + the increasing rearrangement of `a_1, \ldots, a_n`, then `b_i \leq i`. + + A *parking function* of size `n` is a pair `(L, D)` of two sequences + `L` and `D` where `L` is a permutation and `D` is an area sequence + of a Dyck Path of size n such that `D[i] \geq 0`, `D[i+1] \leq D[i]+1` + and if `D[i+1] = D[i]+1` then `L[i+1] > L[i]`. -class ParkingFunctions_all(UniqueRepresentation, Parent): + The number of parking functions of size `n` is equal to the number + of rooted forests on `n` vertices and is equal to `(n+1)^{n-1}`. + + EXAMPLES: + + Here are all parking functions of size 3:: + + sage: from sage.combinat.parking_functions import ParkingFunctions + sage: ParkingFunctions(3).list() + [[1, 1, 1], [1, 1, 2], [1, 2, 1], [2, 1, 1], [1, 1, 3], [1, 3, 1], + [3, 1, 1], [1, 2, 2], [2, 1, 2], [2, 2, 1], [1, 2, 3], [1, 3, 2], + [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]] + + If no size is specified, then ParkingFunctions returns the + combinatorial class of all parking functions. :: + + sage: PF = ParkingFunctions(); PF + Parking functions + sage: [] in PF + True + sage: [1] in PF + True + sage: [2] in PF + False + sage: [1,3,1] in PF + True + sage: [1,4,1] in PF + False + + If the size `n` is specified, then ParkingFunctions returns + the combinatorial class of all parking functions of size `n`. + + :: + + sage: PF = ParkingFunctions(0) + sage: PF.list() + [[]] + sage: PF = ParkingFunctions(1) + sage: PF.list() + [[1]] + sage: PF = ParkingFunctions(3) + sage: PF.list() + [[1, 1, 1], [1, 1, 2], [1, 2, 1], [2, 1, 1], [1, 1, 3], + [1, 3, 1], [3, 1, 1], [1, 2, 2], [2, 1, 2], [2, 2, 1], + [1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]] + + :: + + sage: PF3 = ParkingFunctions(3); PF3 + Parking functions of size 3 + sage: [] in PF3 + False + sage: [1] in PF3 + False + sage: [1,3,1] in PF3 + True + sage: [1,4,1] in PF3 + False + + TESTS:: + + sage: PF = ParkingFunctions(5) + sage: TestSuite(PF).run() + sage: len(PF.list()) == PF.cardinality() + True + """ + @staticmethod + def __classcall_private__(cls, n=None): + """ + Return the correct parent based on input. + + TESTS:: + + sage: type(ParkingFunctions(5)) + + sage: type(ParkingFunctions()) + + """ + if n is None: + return ParkingFunctions_all() + + if not isinstance(n, (Integer, int)) or n < 0: + raise ValueError("%s is not a non-negative integer" % n) + return ParkingFunctions_n(n) + +class ParkingFunctions_all(ParkingFunctions): def __init__(self): """ TESTS:: @@ -1389,7 +1330,7 @@ def __init__(self): cat = InfiniteEnumeratedSets() & SetsWithGrading() Parent.__init__(self, category=cat) - Element = ParkingFunction_class + Element = ParkingFunction def _repr_(self) -> str: """ @@ -1415,7 +1356,7 @@ def __contains__(self, x) -> bool: sage: [1,4,1] in ParkingFunctions() False """ - if isinstance(x, ParkingFunction_class): + if isinstance(x, ParkingFunction): return True return is_a(x) @@ -1467,9 +1408,10 @@ def _coerce_map_from_(self, S): """ if isinstance(S, ParkingFunctions_n): return True + return False -class ParkingFunctions_n(Parent, UniqueRepresentation): +class ParkingFunctions_n(ParkingFunctions): r""" The combinatorial class of parking functions of size `n`. @@ -1523,13 +1465,12 @@ def __init__(self, n): TESTS:: sage: PF = ParkingFunctions(3) - sage: PF == loads(dumps(PF)) - True + sage: TestSuite(PF).run() """ self.n = n Parent.__init__(self, category=FiniteEnumeratedSets()) - Element = ParkingFunction_class + Element = ParkingFunction def _repr_(self) -> str: """ @@ -1556,10 +1497,12 @@ def __contains__(self, x) -> bool: True sage: [1,4,1] in PF3 False + sage: ParkingFunction([1,2]) in PF3 + False sage: all(p in PF3 for p in PF3) True """ - if isinstance(x, ParkingFunction_class): + if isinstance(x, ParkingFunction) and len(x) == self.n: return True return is_a(x, self.n) @@ -1580,7 +1523,7 @@ def __iter__(self) -> Iterator: """ Return an iterator for parking functions of size `n`. - .. warning:: + .. WARNING:: The precise order in which the parking function are generated is not fixed, and may change in the future. @@ -1627,9 +1570,7 @@ def iterator_rec(n): return for res1 in iterator_rec(n - 1): for i in range(res1[-1], n + 1): - res = copy(res1) - res.append(i) - yield res + yield res1 + [i] return for res in iterator_rec(self.n): for pi in Permutations(res): @@ -1661,7 +1602,8 @@ def random_element(self) -> PF: free = [Zm(j) for j in range(n + 1)] for car in fun: position = car - while not(position in free): + while position not in free: position += Zm.one() free.remove(position) return self.element_class(self, [(i - free[0]).lift() for i in fun]) + From a4bb7f30b9cf7aacb5869f90f91c9fc90a6f329c Mon Sep 17 00:00:00 2001 From: Christian Wuthrich Date: Mon, 29 Mar 2021 14:46:49 +0100 Subject: [PATCH 256/406] returns a return now --- src/sage/schemes/elliptic_curves/formal_group.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/formal_group.py b/src/sage/schemes/elliptic_curves/formal_group.py index 84be3dd8827..b9162eed607 100644 --- a/src/sage/schemes/elliptic_curves/formal_group.py +++ b/src/sage/schemes/elliptic_curves/formal_group.py @@ -733,30 +733,30 @@ def mult_by_n(self, n, prec=10): def sigma(self, prec=10): r""" - Returns the Weierstrass sigma function as a formal powerseries + Return the Weierstrass sigma function as a formal powerseries solution to the differential equation - + .. MATH:: \frac{d^2 \log \sigma}{dz^2} = - \wp(z) - with initial conditions `\sigma(O)=0` and `\sigma'(O)=1`, - expressed in the variable `t=\log_E(z)` of the formal group. - + with initial conditions `\sigma(O)=0` and `\sigma'(O)=1`, + expressed in the variable `t=\log_E(z)` of the formal group. + INPUT: - ``prec`` - integer (default 10) OUTPUT: a power series with given precision - + Other solutions can be obtained by multiplication with an function of the form `\exp(c z^2)`. - If the curve has good ordinary reduction at a prime `p` + If the curve has good ordinary reduction at a prime `p` then there is a canonical choice of `c` that produces the canonical `p`-adic sigma function. To obtain that, please use ``E.padic_sigma(p)`` instead. See :meth:`~sage.schemes.elliptic_curves.ell_rational_field.EllipticCurve_rational_field.padic_sigma` - + EXAMPLES:: sage: E = EllipticCurve('14a') From e9b9df48030b061b2f66bd06c8d80d8e4b9ac4f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Mon, 29 Mar 2021 18:53:05 +0200 Subject: [PATCH 257/406] fix findstat parking functions --- src/sage/databases/findstat.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/databases/findstat.py b/src/sage/databases/findstat.py index 16e2757101b..6ba94aed722 100644 --- a/src/sage/databases/findstat.py +++ b/src/sage/databases/findstat.py @@ -251,7 +251,7 @@ def mapping(sigma): from sage.combinat.composition import Composition, Compositions from sage.combinat.partition import Partition, Partitions from sage.combinat.ordered_tree import OrderedTree, OrderedTrees -from sage.combinat.parking_functions import ParkingFunction, ParkingFunction_class, ParkingFunctions +from sage.combinat.parking_functions import ParkingFunction, ParkingFunctions from sage.combinat.perfect_matching import PerfectMatching, PerfectMatchings from sage.combinat.permutation import Permutation, Permutations from sage.combinat.posets.posets import Poset, FinitePoset @@ -4452,7 +4452,7 @@ def name(self, style="singular"): str, ParkingFunctions, len, - lambda x: isinstance(x, ParkingFunction_class)), + lambda x: isinstance(x, ParkingFunction)), "PerfectMatchings": _SupportedFindStatCollection(lambda x: PerfectMatching(literal_eval(x)), str, From 8eeb047e89e1b3b2c107e945a3107be3e58509ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Tue, 30 Mar 2021 10:16:09 +0200 Subject: [PATCH 258/406] fix various lgtm suggestions --- src/sage/coding/linear_code.py | 10 +++++----- src/sage/combinat/words/paths.py | 3 +-- src/sage/graphs/graph.py | 3 +-- .../manifolds/differentiable/examples/euclidean.py | 11 ++--------- src/sage/quadratic_forms/special_values.py | 6 +++--- .../schemes/elliptic_curves/ell_rational_field.py | 6 +++--- 6 files changed, 15 insertions(+), 24 deletions(-) diff --git a/src/sage/coding/linear_code.py b/src/sage/coding/linear_code.py index 5342aad0fbc..9944228ba7b 100644 --- a/src/sage/coding/linear_code.py +++ b/src/sage/coding/linear_code.py @@ -2115,7 +2115,7 @@ def e(i): for v in C_basis: i = v.support()[0] U_basis.remove(i) # swap e_{i+1} with v - U_basis = [e(i+1) for i in U_basis] + U_basis = [e(i + 1) for i in U_basis] V = VectorSpace(F, self.length()) U = V.span(U_basis) @@ -2128,16 +2128,16 @@ def e(i): Ainv = A.inverse() Pei = [] # projection of e_i on U - for i in range(1, self.length()+1): + for i in range(1, self.length() + 1): ei = e(i) if ei in U: Pei.append(ei) else: a = Ainv * ei # get zero vector and sum a[i]u_i to it - v = vector(F, [0]*self.length()) - for i in range(len(U_basis)): - v += a[i]*U_basis[i] + v = vector(F, [0] * self.length()) + for ai, Ui in zip(a, U_basis): + v += ai * Ui if not v.is_zero(): # don't care about 0 vectors v.set_immutable() Pei.append(v) diff --git a/src/sage/combinat/words/paths.py b/src/sage/combinat/words/paths.py index 7bad614af5f..480fdf2506b 100644 --- a/src/sage/combinat/words/paths.py +++ b/src/sage/combinat/words/paths.py @@ -1203,8 +1203,7 @@ def is_simple(self): n += 1 if len(s) != n: return False - else: - return True + return True def tikz_trajectory(self): r""" diff --git a/src/sage/graphs/graph.py b/src/sage/graphs/graph.py index b4e0c2da499..7fea475c191 100644 --- a/src/sage/graphs/graph.py +++ b/src/sage/graphs/graph.py @@ -428,6 +428,7 @@ lazy_import('sage.graphs.mcqd', ['mcqd'], feature=PythonModule('sage.graphs.mcqd', spkg='mcqd')) + class Graph(GenericGraph): r""" Undirected graph. @@ -1162,9 +1163,7 @@ def __init__(self, data=None, pos=None, loops=None, format=None, from_incidence_matrix(self, data, loops=loops, multiedges=multiedges, weighted=weighted) elif format == 'seidel_adjacency_matrix': - multiedges = False weighted = False - loops = False self.allow_loops(False) self.allow_multiple_edges(False) from .graph_input import from_seidel_adjacency_matrix diff --git a/src/sage/manifolds/differentiable/examples/euclidean.py b/src/sage/manifolds/differentiable/examples/euclidean.py index de84da10c48..61acb9de3fb 100644 --- a/src/sage/manifolds/differentiable/examples/euclidean.py +++ b/src/sage/manifolds/differentiable/examples/euclidean.py @@ -675,10 +675,6 @@ def __classcall_private__(cls, n=None, name=None, latex_name=None, if names is not None and symbols is None: if n == 2: names = list(names) - symbols = '' - for x in names: - symbols += x + ' ' - symbols = symbols[:-1] if coordinates == 'polar': if names[1] in ['p', 'ph', 'phi']: names[1] += ':\\phi' @@ -698,16 +694,13 @@ def __classcall_private__(cls, n=None, name=None, latex_name=None, if names[1] in ['p', 'ph', 'phi']: names[1] = names[1] + ':\\phi' - symbols = '' - for x in names: - symbols += x + ' ' - symbols = symbols[:-1] + symbols = ' '.join(x for x in names) # Technical bit for UniqueRepresentation from sage.misc.prandom import getrandbits from time import time if unique_tag is None: - unique_tag = getrandbits(128)*time() + unique_tag = getrandbits(128) * time() if n == 2: return EuclideanPlane(name=name, latex_name=latex_name, diff --git a/src/sage/quadratic_forms/special_values.py b/src/sage/quadratic_forms/special_values.py index 828306c276e..3d761843008 100644 --- a/src/sage/quadratic_forms/special_values.py +++ b/src/sage/quadratic_forms/special_values.py @@ -67,15 +67,15 @@ def gamma__exact(n): if denominator(n) == 1: if n <= 0: return infinity - if n > 0: - return factorial(n-1) + else: + return factorial(n - 1) elif denominator(n) == 2: ans = QQ.one() while n != QQ((1, 2)): if n < 0: ans /= n n += 1 - elif n > 0: + else: n += -1 ans *= n diff --git a/src/sage/schemes/elliptic_curves/ell_rational_field.py b/src/sage/schemes/elliptic_curves/ell_rational_field.py index eea4c883303..1ce8323e1b0 100644 --- a/src/sage/schemes/elliptic_curves/ell_rational_field.py +++ b/src/sage/schemes/elliptic_curves/ell_rational_field.py @@ -4439,17 +4439,17 @@ def minimal_quadratic_twist(self): if self.conductor().is_squarefree(): return self, Integer(1) j = self.j_invariant() - if j!=0 and j!=1728: + if j != 0 and j != 1728: # the constructor from j will give the minimal twist Et = constructor.EllipticCurve_from_j(j) else: - if j==0: # divide c6 by largest cube + if j == 0: # divide c6 by largest cube c = -2*self.c6() for p in c.support(): e = c.valuation(p)//3 c /= p**(3*e) E1 = constructor.EllipticCurve([0,0,0,0,c]) - elif j==1728: # divide c4 by largest square + else: # j=1728 ; divide c4 by largest square c = -3*self.c4() for p in c.support(): e = c.valuation(p)//2 From fb7f276262096353219a7f9f27b6b7f8de289490 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Tue, 30 Mar 2021 17:46:01 +0200 Subject: [PATCH 259/406] detail in special_values --- src/sage/quadratic_forms/special_values.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/sage/quadratic_forms/special_values.py b/src/sage/quadratic_forms/special_values.py index 3d761843008..47f8ec3008f 100644 --- a/src/sage/quadratic_forms/special_values.py +++ b/src/sage/quadratic_forms/special_values.py @@ -67,9 +67,9 @@ def gamma__exact(n): if denominator(n) == 1: if n <= 0: return infinity - else: - return factorial(n - 1) + return factorial(n - 1) elif denominator(n) == 2: + # now n = 1/2 + an integer ans = QQ.one() while n != QQ((1, 2)): if n < 0: @@ -243,6 +243,7 @@ def quadratic_L_function__exact(n, d): if delta == 1: raise TypeError("n must be a critical value (i.e. odd > 0 or even <= 0)") + def quadratic_L_function__numerical(n, d, num_terms=1000): """ Evaluate the Dirichlet L-function (for quadratic character) numerically From bc2fd3e0fa59edc4569ad8d289d664bd46d3926d Mon Sep 17 00:00:00 2001 From: Peter Bruin Date: Tue, 30 Mar 2021 20:04:45 +0200 Subject: [PATCH 260/406] Trac 31581: do not require base ring of quaternion algebra to be a field --- .../algebras/quatalg/quaternion_algebra.py | 129 +++++++++++------- 1 file changed, 77 insertions(+), 52 deletions(-) diff --git a/src/sage/algebras/quatalg/quaternion_algebra.py b/src/sage/algebras/quatalg/quaternion_algebra.py index 324392f7165..b9eeceea9e1 100644 --- a/src/sage/algebras/quatalg/quaternion_algebra.py +++ b/src/sage/algebras/quatalg/quaternion_algebra.py @@ -9,6 +9,8 @@ - Julian Rueth (2014-03-02): use UniqueFactory for caching +- Peter Bruin (2021): do not require the base ring to be a field + This code is partly based on Sage code by David Kohel from 2005. TESTS: @@ -24,6 +26,7 @@ # Copyright (C) 2009 William Stein # Copyright (C) 2009 Jonathan Bober # Copyright (C) 2014 Julian Rueth +# Copyright (C) 2021 Peter Bruin # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -53,7 +56,7 @@ from sage.structure.sequence import Sequence from sage.structure.element import is_RingElement from sage.structure.factory import UniqueFactory -from sage.modules.free_module import VectorSpace, FreeModule +from sage.modules.free_module import FreeModule from sage.modules.free_module_element import vector from operator import itemgetter @@ -65,9 +68,7 @@ from sage.misc.cachefunc import cached_method -from sage.categories.fields import Fields from sage.categories.algebras import Algebras -_Fields = Fields() ######################################################## # Constructor @@ -76,24 +77,33 @@ class QuaternionAlgebraFactory(UniqueFactory): r""" + Construct a quaternion algebra. + + INPUT: + There are three input formats: - - ``QuaternionAlgebra(a, b)``: quaternion algebra generated by ``i``, ``j`` - subject to `i^2 = a`, `j^2 = b`, `j \cdot i = -i \cdot j`. + - ``QuaternionAlgebra(a, b)``, where `a` and `b` are nonzero + elements of a field `K` of characteristic different from 2 that + is deduced from `(a, b)`. - - ``QuaternionAlgebra(K, a, b)``: same as above but over a field ``K``. - Here, ``a`` and ``b`` are nonzero elements of a field (``K``) of - characteristic not 2, and we set `k = i \cdot j`. + - ``QuaternionAlgebra(K, a, b)``, where `K` is a ring in which 2 + is a unit and `a` and `b` are units of `K`. + + - ``QuaternionAlgebra(D)``, where `D \ge 1` is a squarefree + integer. This constructs a quaternion algebra of discriminant + `D` over `K = \QQ`. Suitable nonzero rational numbers `a`, `b` + as above are deduced from `D`. + + OUTPUT: - - ``QuaternionAlgebra(D)``: a rational quaternion algebra with - discriminant ``D``, where `D > 1` is a squarefree integer. + The quaternion algebra `(a, b)_K` over `K` generated by `i`, `j` + subject to `i^2 = a`, `j^2 = b`, and `ji = -ij`. EXAMPLES: - ``QuaternionAlgebra(a, b)`` - return quaternion algebra over the - *smallest* field containing the nonzero elements ``a`` and ``b`` with - generators ``i``, ``j``, ``k`` with `i^2=a`, `j^2=b` and `j \cdot i = - -i \cdot j`:: + ``QuaternionAlgebra(a, b)`` -- return the quaternion algebra + `(a, b)_K`, where the base ring `K` is deduced from `(a, b)`:: sage: QuaternionAlgebra(-2,-3) Quaternion Algebra (-2, -3) with base ring Rational Field @@ -108,10 +118,11 @@ class QuaternionAlgebraFactory(UniqueFactory): sage: QuaternionAlgebra(1r,1) Quaternion Algebra (1, 1) with base ring Rational Field - Python ints, longs and floats may be passed to the - ``QuaternionAlgebra(a, b)`` constructor, as may all pairs of nonzero - elements of a ring not of characteristic 2. The following tests address - the issues raised in :trac:`10601`:: + Python ints and floats may be passed to the + ``QuaternionAlgebra(a, b)`` constructor, as may all pairs of + nonzero elements of a domain not of characteristic 2. + + The following tests address the issues raised in :trac:`10601`:: sage: QuaternionAlgebra(1r,1) Quaternion Algebra (1, 1) with base ring Rational Field @@ -120,28 +131,27 @@ class QuaternionAlgebraFactory(UniqueFactory): sage: QuaternionAlgebra(0,0) Traceback (most recent call last): ... - ValueError: a and b must be nonzero + ValueError: defining elements of quaternion algebra (0, 0) are not invertible in Rational Field sage: QuaternionAlgebra(GF(2)(1),1) Traceback (most recent call last): ... - ValueError: a and b must be elements of a ring with characteristic not 2 + ValueError: 2 is not invertible in Finite Field of size 2 sage: a = PermutationGroupElement([1,2,3]) sage: QuaternionAlgebra(a, a) Traceback (most recent call last): ... ValueError: a and b must be elements of a ring with characteristic not 2 - ``QuaternionAlgebra(K, a, b)`` - return quaternion algebra over the - field ``K`` with generators ``i``, ``j``, ``k`` with `i^2=a`, `j^2=b` - and `i \cdot j = -j \cdot i`:: + ``QuaternionAlgebra(K, a, b)`` -- return the quaternion algebra + defined by `(a, b)` over the ring `K`:: sage: QuaternionAlgebra(QQ, -7, -21) Quaternion Algebra (-7, -21) with base ring Rational Field sage: QuaternionAlgebra(QQ[sqrt(2)], -2,-3) Quaternion Algebra (-2, -3) with base ring Number Field in sqrt2 with defining polynomial x^2 - 2 with sqrt2 = 1.414213562373095? - ``QuaternionAlgebra(D)`` - ``D`` is a squarefree integer; returns a - rational quaternion algebra of discriminant ``D``:: + ``QuaternionAlgebra(D)`` -- `D` is a squarefree integer; return a + rational quaternion algebra of discriminant `D`:: sage: QuaternionAlgebra(1) Quaternion Algebra (-1, 1) with base ring Rational Field @@ -234,16 +244,14 @@ def create_key(self, arg0, arg1=None, arg2=None, names='i,j,k'): # QuaternionAlgebra(K, a, b) else: K = arg0 - if K not in _Fields: - raise TypeError("base ring of quaternion algebra must be a field") a = K(arg1) b = K(arg2) - if K.characteristic() == 2: - # Lameness! - raise ValueError("a and b must be elements of a ring with characteristic not 2") - if a == 0 or b == 0: - raise ValueError("a and b must be nonzero") + if not K(2).is_unit(): + raise ValueError("2 is not invertible in %s" % K) + if not (a.is_unit() and b.is_unit()): + raise ValueError("defining elements of quaternion algebra (%s, %s) are not invertible in %s" + % (a, b, K)) names = normalize_names(3, names) return (K, a, b, names) @@ -310,6 +318,7 @@ def ngens(self): """ return 3 + @cached_method def basis(self): """ Return the fixed basis of ``self``, which is `1`, `i`, `j`, `k`, where @@ -330,11 +339,8 @@ def basis(self): sage: Q.basis() is Q.basis() True """ - try: - return self.__basis - except AttributeError: - self.__basis = tuple([self(1)] + list(self.gens())) - return self.__basis + i, j, k = self.gens() + return (self.one(), i, j, k) @cached_method def inner_product_matrix(self): @@ -547,10 +553,28 @@ def random_element(self, *args, **kwds): return self([K.random_element(*args, **kwds) for _ in range(4)]) @cached_method + def free_module(self): + """ + Return the free module associated to ``self`` with inner + product given by the reduced norm. + + EXAMPLES:: + + sage: A. = LaurentPolynomialRing(GF(3)) + sage: B = QuaternionAlgebra(A, -1, t) + sage: B.free_module() + Ambient free quadratic module of rank 4 over the principal ideal domain Univariate Laurent Polynomial Ring in t over Finite Field of size 3 + Inner product matrix: + [2 0 0 0] + [0 2 0 0] + [0 0 t 0] + [0 0 0 t] + """ + return FreeModule(self.base_ring(), 4, inner_product_matrix=self.inner_product_matrix()) + def vector_space(self): """ - Return the vector space associated to ``self`` with inner product given - by the reduced norm. + Alias for :meth:`free_module`. EXAMPLES:: @@ -562,23 +586,26 @@ def vector_space(self): [ 0 0 -38 0] [ 0 0 0 -114] """ - return VectorSpace(self.base_ring(), 4, inner_product_matrix=self.inner_product_matrix()) + return self.free_module() class QuaternionAlgebra_ab(QuaternionAlgebra_abstract): """ - The quaternion algebra of the form `(a, b/K)`, where `i^2=a`, `j^2 = b`, - and `j*i = -i*j`. ``K`` is a field not of characteristic 2 and ``a``, - ``b`` are nonzero elements of ``K``. + A quaternion algebra of the form `(a, b)_K`. See ``QuaternionAlgebra`` for many more examples. INPUT: - - ``base_ring`` -- commutative ring - - ``a, b`` -- elements of ``base_ring`` + - ``base_ring`` -- a commutative ring `K` in which 2 is invertible + - ``a, b`` -- units of `K` - ``names`` -- string (optional, default 'i,j,k') names of the generators + OUTPUT: + + The quaternion algebra `(a, b)` over `K` generated by `i` and `j` + subject to `i^2 = a`, `j^2 = b`, and `ji = -ij`. + EXAMPLES:: sage: QuaternionAlgebra(QQ, -7, -21) # indirect doctest @@ -587,7 +614,7 @@ class QuaternionAlgebra_ab(QuaternionAlgebra_abstract): def __init__(self, base_ring, a, b, names='i,j,k'): """ Create the quaternion algebra with `i^2 = a`, `j^2 = b`, and - `i*j = -j*i = k`. + `ij = -ji = k`. TESTS: @@ -609,12 +636,12 @@ def __init__(self, base_ring, a, b, names='i,j,k'): sage: TestSuite(Q).run() - The base ring must be a field:: + The element 2 must be a unit in the base ring:: sage: Q. = QuaternionAlgebra(ZZ,-5,-19) Traceback (most recent call last): ... - TypeError: base ring of quaternion algebra must be a field + ValueError: 2 is not invertible in Integer Ring """ ParentWithGens.__init__(self, base_ring, names=names, category=Algebras(base_ring).Division()) self._a = a @@ -631,10 +658,8 @@ def __init__(self, base_ring, a, b, names='i,j,k'): # underlying representation of quadratic fields is a bit # tricky. self.Element = quaternion_algebra_element.QuaternionAlgebraElement_number_field - elif base_ring in _Fields: - self.Element = quaternion_algebra_element.QuaternionAlgebraElement_generic else: - raise TypeError("base ring of quaternion algebra must be a field") + self.Element = quaternion_algebra_element.QuaternionAlgebraElement_generic self._populate_coercion_lists_(coerce_list=[base_ring]) self._gens = [self([0,1,0,0]), self([0,0,1,0]), self([0,0,0,1])] @@ -850,7 +875,7 @@ def invariants(self): """ Return the structural invariants `a`, `b` of this quaternion algebra: ``self`` is generated by `i`, `j` subject to - `i^2 = a`, `j^2 = b` and `j*i = -i*j`. + `i^2 = a`, `j^2 = b` and `ji = -ij`. EXAMPLES:: From eeba0134f80c4ae9a0454a691f48a2dd643d6038 Mon Sep 17 00:00:00 2001 From: Kwankyu Lee Date: Wed, 31 Mar 2021 13:47:06 +0900 Subject: [PATCH 261/406] Fix a bug in constant field extension of AG code" --- src/sage/coding/ag_code_decoders.py | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/src/sage/coding/ag_code_decoders.py b/src/sage/coding/ag_code_decoders.py index cad589e6a66..b3114d77d71 100644 --- a/src/sage/coding/ag_code_decoders.py +++ b/src/sage/coding/ag_code_decoders.py @@ -2006,6 +2006,20 @@ class _Decoder_K_extension(object): sage: code = codes.EvaluationAGCode(pls, G) sage: code.decoder('K') Unique decoder for [9, 4] evaluation AG code over GF(4) + + :: + + sage: P. = ProjectiveSpace(GF(4), 1) + sage: C = Curve(P) + sage: pls = C.places() + sage: len(pls) + 5 + sage: F = C.function_field() + sage: G = F.get_place(2).divisor() + sage: code = codes.EvaluationAGCode(pls, G) + sage: code.decoder('K') + Unique decoder for [5, 3] evaluation AG code over GF(4) + """ def __init__(self, pls, G, Q, verbose=False): """ @@ -2033,9 +2047,12 @@ def __init__(self, pls, G, Q, verbose=False): F_ext_base = FunctionField(K_ext, F_base.variable_name()) - # construct constant field extension F_ext of F - def_poly = F.polynomial().base_extend(F_ext_base) - F_ext = F_ext_base.extension(def_poly, names=def_poly.variable_name()) + if F.degree() > 1: + # construct constant field extension F_ext of F + def_poly = F.polynomial().base_extend(F_ext_base) + F_ext = F_ext_base.extension(def_poly, names=def_poly.variable_name()) + else: # rational function field + F_ext = F_ext_base O_ext = F_ext.maximal_order() Oinf_ext = F_ext.maximal_order_infinite() @@ -2043,7 +2060,11 @@ def __init__(self, pls, G, Q, verbose=False): # embedding of F into F_ext embedK = K_ext.coerce_map_from(K) embedF_base = F_base.hom(F_ext_base.gen(), embedK) - embedF = F.hom(F_ext.gen(), embedF_base) + + if F.degree() > 1: + embedF = F.hom(F_ext.gen(), embedF_base) + else: + embedF = embedF_base self._embedK = embedK self._K = K From a574c655650da092617e9d8203cf5673413ac8ac Mon Sep 17 00:00:00 2001 From: Peter Bruin Date: Wed, 31 Mar 2021 14:19:07 +0200 Subject: [PATCH 262/406] Trac 31581: test operations on quaternions with general base ring --- .../quatalg/quaternion_algebra_element.pyx | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/src/sage/algebras/quatalg/quaternion_algebra_element.pyx b/src/sage/algebras/quatalg/quaternion_algebra_element.pyx index 95c2732c78a..60697f596dd 100644 --- a/src/sage/algebras/quatalg/quaternion_algebra_element.pyx +++ b/src/sage/algebras/quatalg/quaternion_algebra_element.pyx @@ -692,6 +692,26 @@ cdef class QuaternionAlgebraElement_generic(QuaternionAlgebraElement_abstract): """ TESTS: + Test operations on quaternions over a base ring that is not a field:: + + sage: A. = LaurentPolynomialRing(GF(3)) + sage: B = QuaternionAlgebra(A, -1, t) + sage: i, j, k = B.gens() + sage: i*j + k + sage: (j + k).reduced_norm() + t + + Inverting an element is currently only possible if its reduced + norm is a unit:: + + sage: ~k + (t^-1)*k + sage: ~(i + j) + Traceback (most recent call last): + ... + TypeError: unsupported operand parent(s) for *: 'Fraction Field of Univariate Polynomial Ring in t over Finite Field of size 3' and 'Quaternion Algebra (2, t) with base ring Univariate Laurent Polynomial Ring in t over Finite Field of size 3' + We test pickling:: sage: R. = Frac(QQ['x']); Q. = QuaternionAlgebra(R,-5*x,-2) @@ -701,7 +721,7 @@ cdef class QuaternionAlgebraElement_generic(QuaternionAlgebraElement_abstract): """ def __init__(self, parent, v, bint check=True): """ - Create a quaternion over some general base field. + Create a quaternion over a general base ring. EXAMPLES:: From be9af961640d9b35b5902a0b77fbe7d3554ad01c Mon Sep 17 00:00:00 2001 From: Peter Bruin Date: Wed, 31 Mar 2021 15:05:29 +0200 Subject: [PATCH 263/406] Trac 31581: clarify documentation and add a doctest --- src/sage/algebras/quatalg/quaternion_algebra.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/sage/algebras/quatalg/quaternion_algebra.py b/src/sage/algebras/quatalg/quaternion_algebra.py index b9eeceea9e1..f6b4271edfd 100644 --- a/src/sage/algebras/quatalg/quaternion_algebra.py +++ b/src/sage/algebras/quatalg/quaternion_algebra.py @@ -83,9 +83,8 @@ class QuaternionAlgebraFactory(UniqueFactory): There are three input formats: - - ``QuaternionAlgebra(a, b)``, where `a` and `b` are nonzero - elements of a field `K` of characteristic different from 2 that - is deduced from `(a, b)`. + - ``QuaternionAlgebra(a, b)``, where `a` and `b` can be coerced to + units in a common field `K` of characteristic different from 2. - ``QuaternionAlgebra(K, a, b)``, where `K` is a ring in which 2 is a unit and `a` and `b` are units of `K`. @@ -103,7 +102,8 @@ class QuaternionAlgebraFactory(UniqueFactory): EXAMPLES: ``QuaternionAlgebra(a, b)`` -- return the quaternion algebra - `(a, b)_K`, where the base ring `K` is deduced from `(a, b)`:: + `(a, b)_K`, where the base ring `K` is a suitably chosen field + containing `a` and `b`:: sage: QuaternionAlgebra(-2,-3) Quaternion Algebra (-2, -3) with base ring Rational Field @@ -117,6 +117,9 @@ class QuaternionAlgebraFactory(UniqueFactory): Quaternion Algebra (I, sqrt(-3)) with base ring Symbolic Ring sage: QuaternionAlgebra(1r,1) Quaternion Algebra (1, 1) with base ring Rational Field + sage: A. = ZZ[] + sage: QuaternionAlgebra(-1, t) + Quaternion Algebra (-1, t) with base ring Fraction Field of Univariate Polynomial Ring in t over Integer Ring Python ints and floats may be passed to the ``QuaternionAlgebra(a, b)`` constructor, as may all pairs of From 9b0d5d1cdc90a114f6a01609db9c1c093b23a893 Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Thu, 1 Apr 2021 22:14:37 +0200 Subject: [PATCH 264/406] selfref -> unknown --- src/sage/rings/padics/factory.py | 15 ++-- src/sage/rings/padics/generic_nodes.py | 38 +++++---- src/sage/rings/padics/lazy_template.pxi | 79 ++++++++++++------- .../rings/padics/lazy_template_header.pxi | 2 +- src/sage/rings/padics/padic_lazy_element.pxd | 2 +- src/sage/rings/padics/padic_lazy_element.pyx | 2 +- 6 files changed, 87 insertions(+), 51 deletions(-) diff --git a/src/sage/rings/padics/factory.py b/src/sage/rings/padics/factory.py index 2577edc0d2e..c5c291df9eb 100644 --- a/src/sage/rings/padics/factory.py +++ b/src/sage/rings/padics/factory.py @@ -3060,7 +3060,7 @@ def ZpL(p, prec=None, *args, **kwds): create (in some cases) self-referent numbers. Here is an example. We first declare a new variable as follows:: - sage: x = R.selfref() + sage: x = R.unknown() sage: x ...?.0 @@ -3068,6 +3068,7 @@ def ZpL(p, prec=None, *args, **kwds): it satisfies:: sage: x.set(1 + 5*x^2) + True The variable `x` now contains the unique solution of the equation `x = 1 + 5 x^2`:: @@ -3081,8 +3082,9 @@ def ZpL(p, prec=None, *args, **kwds): As a comparison, the following does not work:: - sage: y = R.selfref() + sage: y = R.unknown() sage: y.set(1 + 3*y^2) + True sage: y ...?.0 sage: y[:20] @@ -3092,13 +3094,16 @@ def ZpL(p, prec=None, *args, **kwds): Self-referent definitions also work with systems of equations:: - sage: u = R.selfref() - sage: v = R.selfref() - sage: w = R.selfref() + sage: u = R.unknown() + sage: v = R.unknown() + sage: w = R.unknown() sage: u.set(1 + 2*v + 3*w^2 + 5*u*v*w) + True sage: v.set(2 + 4*w + sqrt(1 + 5*u + 10*v + 15*w)) + True sage: w.set(3 + 25*(u*v + v*w + u*w)) + True sage: u ...31203130103131131433 diff --git a/src/sage/rings/padics/generic_nodes.py b/src/sage/rings/padics/generic_nodes.py index 9c61ece8258..ef303f269d2 100644 --- a/src/sage/rings/padics/generic_nodes.py +++ b/src/sage/rings/padics/generic_nodes.py @@ -733,13 +733,13 @@ def _get_element_class(self, name=None): sage: R._get_element_class("add") - sage: R._get_element_class("selfref") - - sage: R._get_element_class("unknown") + + + sage: R._get_element_class("foobar") Traceback (most recent call last): ... - AttributeError: module 'sage.rings.padics.padic_lazy_element' has no attribute 'pAdicLazyElement_unknown' + AttributeError: module 'sage.rings.padics.padic_lazy_element' has no attribute 'pAdicLazyElement_foobar' """ if name is None: return self.Element @@ -962,13 +962,13 @@ def some_elements(self, unbounded=False): L = [ x.at_precision_absolute() for x in L ] return L - def selfref(self, valuation=0, digits=None): + def unknown(self, start_val=0, digits=None): r""" Return a self-referent number in this ring. INPUT: - - ``valuation`` -- an integer (default: 0); a lower bound on the + - ``start_val`` -- an integer (default: 0); a lower bound on the valuation of the returned element - ``digits`` -- an element, a list or ``None`` (default: ``None``); @@ -980,7 +980,7 @@ def selfref(self, valuation=0, digits=None): of the previous ones. This method is used to declare a self-referent number (and optionally, to set its first digits). The definition of the number itself will be given afterwords using - to method meth:`sage.rings.padics.lazy_template.LazyElement_selfref.set` + to method meth:`sage.rings.padics.lazy_template.LazyElement_unknown.set` of the element. EXAMPLES: @@ -989,7 +989,7 @@ def selfref(self, valuation=0, digits=None): We declare a self-referent number:: - sage: a = R.selfref() + sage: a = R.unknown() So far, we do not know anything on `a` (except that it has nonnegative valuation):: @@ -997,12 +997,13 @@ def selfref(self, valuation=0, digits=None): sage: a O(5^0) - We can now use the method meth:`sage.rings.padics.lazy_template.LazyElement_selfref.set` + We can now use the method meth:`sage.rings.padics.lazy_template.LazyElement_unknown.set` to define `a`. Below, for example, we say that the digits of `a` have to agree with the digits of `1 + 5 a`. Note that the factor `5` shifts the digits; the `n`-th digit of `a` is then defined by the previous ones:: sage: a.set(1 + 5*a) + True After this, `a` contains the solution of the equation `a = 1 + 5 a`, that is `a = -1/4`:: @@ -1012,8 +1013,9 @@ def selfref(self, valuation=0, digits=None): Here is another example with an equation of degree `2`:: - sage: b = R.selfref() + sage: b = R.unknown() sage: b.set(1 - 5*b^2) + True sage: b 1 + 4*5 + 5^2 + 3*5^4 + 4*5^6 + 4*5^8 + 2*5^9 + ... sage: (sqrt(R(21)) - 1) / 10 @@ -1021,13 +1023,16 @@ def selfref(self, valuation=0, digits=None): Cross self-referent definitions are also allowed:: - sage: u = R.selfref() - sage: v = R.selfref() - sage: w = R.selfref() + sage: u = R.unknown() + sage: v = R.unknown() + sage: w = R.unknown() sage: u.set(1 + 2*v + 3*w^2 + 5*u*v*w) + True sage: v.set(2 + 4*w + sqrt(1 + 5*u + 10*v + 15*w)) + True sage: w.set(3 + 25*(u*v + v*w + u*w)) + True sage: u 3 + 3*5 + 4*5^2 + 5^3 + 3*5^4 + 5^5 + 5^6 + 3*5^7 + 5^8 + 3*5^9 + ... @@ -1038,8 +1043,9 @@ def selfref(self, valuation=0, digits=None): TESTS:: - sage: a = R.selfref() + sage: a = R.unknown() sage: a.set(1 + 3*a) + True sage: a O(5^0) sage: a.at_precision_absolute(10) @@ -1047,12 +1053,12 @@ def selfref(self, valuation=0, digits=None): ... RecursionError: definition looks circular """ - valuation = ZZ(valuation) + valuation = ZZ(start_val) if (not self.is_field()) and valuation < 0: raise ValueError("valuation must be nonnegative") if digits is not None and not isinstance(digits, (list, tuple)): digits = [digits] - return self._get_element_class('selfref')(self, valuation, digits) + return self._get_element_class('unknown')(self, valuation, digits) def random_element(self, integral=False, prec=None): r""" diff --git a/src/sage/rings/padics/lazy_template.pxi b/src/sage/rings/padics/lazy_template.pxi index 20215a59cb0..48299acd4a8 100644 --- a/src/sage/rings/padics/lazy_template.pxi +++ b/src/sage/rings/padics/lazy_template.pxi @@ -909,7 +909,7 @@ cdef class LazyElement(pAdicGenericElement): TESTS:: - sage: s = R.selfref() + sage: s = R.unknown() sage: (1/s).precision_absolute() Traceback (most recent call last): ... @@ -960,7 +960,7 @@ cdef class LazyElement(pAdicGenericElement): TESTS:: - sage: s = R.selfref() + sage: s = R.unknown() sage: (1/s).precision_relative() Traceback (most recent call last): ... @@ -1318,7 +1318,7 @@ cdef class LazyElement(pAdicGenericElement): TESTS:: - sage: x = R.selfref() + sage: x = R.unknown() sage: (~x).valuation() Traceback (most recent call last): ... @@ -1721,7 +1721,7 @@ cdef class LazyElement(pAdicGenericElement): ... ZeroDivisionError: cannot divide by something indistinguishable from zero - sage: y = R.selfref() + sage: y = R.unknown() sage: x / y O(7^-Infinity) """ @@ -1762,7 +1762,7 @@ cdef class LazyElement(pAdicGenericElement): ... ZeroDivisionError: cannot divide by something indistinguishable from zero - sage: y = R.selfref() + sage: y = R.unknown() sage: ~y O(7^-Infinity) """ @@ -1805,24 +1805,22 @@ cdef class LazyElement(pAdicGenericElement): 3-adic Field with lazy precision This method also works for self-referent numbers - (see :meth:`sage.rings.padics.generic_nodes.pAdicLazyGeneric.selfref`):: + (see :meth:`sage.rings.padics.generic_nodes.pAdicLazyGeneric.unknown`):: - sage: x = R.selfref(); x + sage: x = R.unknown(); x O(3^0) sage: x.inverse_of_unit() O(3^0) sage: x.set(1 + 3 * x.inverse_of_unit()) + True sage: x 1 + 3 + 2*3^2 + 3^3 + 3^4 + ... Actually, in many cases, it is preferable to use it than an actual division. Indeed, compare:: - sage: y = R.selfref() + sage: y = R.unknown() sage: y.set(1 + 3/y) - sage: y - O(3^0) - sage: y[:5] Traceback (most recent call last): ... RecursionError: definition looks circular @@ -1868,13 +1866,14 @@ cdef class LazyElement(pAdicGenericElement): 1 + 2^3 + O(2^4) This method also work for self-referent numbers - (see :meth:`sage.rings.padics.generic_nodes.pAdicLazyGeneric.selfref`):: + (see :meth:`sage.rings.padics.generic_nodes.pAdicLazyGeneric.unknown`):: - sage: x = R.selfref(); x + sage: x = R.unknown(); x O(7^0) sage: x.sqrt() O(7^0) sage: x.set(1 + 7*sqrt(x)) + True sage: x 1 + 7 + 4*7^2 + 4*7^3 + 2*7^4 + 3*7^8 + 3*7^9 + ... @@ -1920,7 +1919,7 @@ cdef class LazyElement(pAdicGenericElement): sage: R = ZpL(5) sage: R(0)._test_nonzero_equal() sage: R(5^30)._test_nonzero_equal() - sage: R.selfref()._test_nonzero_equal() + sage: R.unknown()._test_nonzero_equal() """ tester = self._tester(**options) try: @@ -3233,7 +3232,7 @@ cdef class LazyElement_div(LazyElementWithDigits): sage: type(x) - sage: y = R.selfref() + sage: y = R.unknown() sage: 1/y O(5^-Infinity) sage: y.inverse_of_unit() @@ -3654,17 +3653,18 @@ cdef class LazyElement_teichmuller(LazyElementWithDigits): # Self-referent definitions ########################### -cdef class LazyElement_selfref(LazyElementWithDigits): +cdef class LazyElement_unknown(LazyElementWithDigits): r""" A class for self-referent lazy `p`-adic numbers. TESTS:: sage: R = ZpL(7) - sage: x = R.selfref() + sage: x = R.unknown() sage: TestSuite(x).run() sage: x.set(1 + 7*x^2) + True sage: TestSuite(x).run() """ def __init__(self, parent, long valuation, digits=None): @@ -3684,9 +3684,9 @@ cdef class LazyElement_selfref(LazyElementWithDigits): TESTS:: sage: R = ZpL(7) - sage: x = R.selfref() + sage: x = R.unknown() sage: type(x) - + """ LazyElement.__init__(self, parent) if valuation >= maxordp: @@ -3717,8 +3717,9 @@ cdef class LazyElement_selfref(LazyElementWithDigits): TESTS:: sage: R = ZpL(7) - sage: x = R.selfref() + sage: x = R.unknown() sage: x.set(1 + 7*x^2) + True sage: x[:20] == loads(dumps(x)) # indirect doctest True """ @@ -3729,7 +3730,7 @@ cdef class LazyElement_selfref(LazyElementWithDigits): if id(self) not in persist.already_pickled: persist.already_pickled[id(self)] = True definition = self._definition - return unpickle_selfref, (id(self), self.__class__, self._parent, self._initialvaluation, digits, definition) + return unpickle_unknown, (id(self), self.__class__, self._parent, self._initialvaluation, digits, definition) cpdef set(self, LazyElement definition): r""" @@ -3740,11 +3741,17 @@ cdef class LazyElement_selfref(LazyElementWithDigits): - ``definition`` -- a lazy `p`-adic number, to which this number is equal + OUTPUT: + + A boolean indicating if the definition is coherent with the + already known digits of this number. + EXAMPLES:: sage: R = ZpL(5, 10) - sage: x = R.selfref() + sage: x = R.unknown() sage: x.set(1 + 5*x) + True sage: x 1 + 5 + 5^2 + 5^3 + 5^4 + 5^5 + 5^6 + 5^7 + 5^8 + 5^9 + ... @@ -3754,8 +3761,9 @@ cdef class LazyElement_selfref(LazyElementWithDigits): On the contrary, the following does not work:: - sage: y = R.selfref() + sage: y = R.unknown() sage: y.set(1 + 3*y) + True sage: y O(5^0) sage: y[:20] @@ -3763,15 +3771,30 @@ cdef class LazyElement_selfref(LazyElementWithDigits): ... RecursionError: definition looks circular + In the next example, we give explicit values for the first digits + and then a recursive definition for the next digits. However, the + recursive definition does not hold for the first digits; that is the + reason why the call to :meth:`set` returns ``False``:: + + sage: z = R.unknown(digits=[2]) + sage: z + 2 + O(5) + sage: z.set(1 + 5*z) + False + sage: z + 2 + 2*5 + 2*5^2 + 2*5^3 + 2*5^4 + 2*5^5 + 2*5^6 + 2*5^7 + 2*5^8 + 2*5^9 + ... + .. SEEALSO:: - :meth:`sage.rings.padics.generic_nodes.pAdicLazyGeneric.selfref` + :meth:`sage.rings.padics.generic_nodes.pAdicLazyGeneric.unknown` """ if self._definition is not None: raise ValueError("this self-referent number is already defined") self._definition = definition self._precbound = max(self._valuation + self._precrel, definition._precbound) + eq = self._is_equal(definition, self._valuation + self._precrel, True) self._init_jump() + return eq cdef int _next_c(self): r""" @@ -3805,7 +3828,7 @@ cdef class LazyElement_selfref(LazyElementWithDigits): self._next = svenext return error -def unpickle_selfref(uid, cls, parent, valuation, digits, definition): +def unpickle_unknown(uid, cls, parent, valuation, digits, definition): r""" Unpickle a self-referent lazy `p`-adic number. @@ -3815,10 +3838,12 @@ def unpickle_selfref(uid, cls, parent, valuation, digits, definition): handled correctly:: sage: R = ZpL(7) - sage: x = R.selfref() - sage: y = R.selfref() + sage: x = R.unknown() + sage: y = R.unknown() sage: x.set(1 + 2*y + 7*x*y) + True sage: y.set(3 + 14*x^2) + True sage: x[:20] == loads(dumps(x)) # indirect doctest True diff --git a/src/sage/rings/padics/lazy_template_header.pxi b/src/sage/rings/padics/lazy_template_header.pxi index 79bf21e4f64..7e52682f9ca 100644 --- a/src/sage/rings/padics/lazy_template_header.pxi +++ b/src/sage/rings/padics/lazy_template_header.pxi @@ -125,7 +125,7 @@ cdef class LazyElement_teichmuller(LazyElementWithDigits): # Self-referent numbers -cdef class LazyElement_selfref(LazyElementWithDigits): +cdef class LazyElement_unknown(LazyElementWithDigits): cdef LazyElement _definition cdef long _next cpdef set(self, LazyElement definition) diff --git a/src/sage/rings/padics/padic_lazy_element.pxd b/src/sage/rings/padics/padic_lazy_element.pxd index 8c1b0b37069..a84ba099f1a 100644 --- a/src/sage/rings/padics/padic_lazy_element.pxd +++ b/src/sage/rings/padics/padic_lazy_element.pxd @@ -51,5 +51,5 @@ cdef class pAdicLazyElement_sqrt(LazyElement_sqrt): cdef class pAdicLazyElement_teichmuller(LazyElement_teichmuller): pass -cdef class pAdicLazyElement_selfref(LazyElement_selfref): +cdef class pAdicLazyElement_unknown(LazyElement_unknown): pass diff --git a/src/sage/rings/padics/padic_lazy_element.pyx b/src/sage/rings/padics/padic_lazy_element.pyx index 14fef259f98..784183f4164 100644 --- a/src/sage/rings/padics/padic_lazy_element.pyx +++ b/src/sage/rings/padics/padic_lazy_element.pyx @@ -11,7 +11,7 @@ cdef inline type element_class_muldigit = pAdicLazyElement_muldigit cdef inline type element_class_div = pAdicLazyElement_div cdef inline type element_class_sqrt = pAdicLazyElement_sqrt cdef inline type element_class_teichmuller = pAdicLazyElement_teichmuller -cdef inline type element_class_selfref = pAdicLazyElement_selfref +cdef inline type element_class_unknown = pAdicLazyElement_unknown include "sage/libs/linkages/padics/lazy/flint.pxi" include "lazy_template.pxi" From b520df3d37344fe3dfef0b53198cefbf195910f3 Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Fri, 2 Apr 2021 00:34:48 +0200 Subject: [PATCH 265/406] halting precision --- src/sage/rings/padics/factory.py | 74 ++++++++---- src/sage/rings/padics/generic_nodes.py | 31 ++++- src/sage/rings/padics/lazy_template.pxi | 130 ++++++++++----------- src/sage/rings/padics/padic_base_leaves.py | 6 +- 4 files changed, 145 insertions(+), 96 deletions(-) diff --git a/src/sage/rings/padics/factory.py b/src/sage/rings/padics/factory.py index c5c291df9eb..e94cc2296b2 100644 --- a/src/sage/rings/padics/factory.py +++ b/src/sage/rings/padics/factory.py @@ -216,6 +216,19 @@ def get_key_base(p, prec, type, print_mode, names, ram_name, print_pos, print_se elif absolute_cap is None: absolute_cap = 2 * relative_cap prec = (relative_cap, absolute_cap) + elif type == 'lazy': + default_prec = halting_prec = None + if prec is not None: + try: + default_prec, halting_prec = prec + except (ValueError, TypeError): + default_prec = halting_prec = prec + if default_prec is None: + default_prec = DEFAULT_PREC + if halting_prec is None: + halting_prec = 2 * default_prec + halting_prec = max(default_prec, halting_prec) + prec = (default_prec, halting_prec) else: if prec is not None: prec = Integer(prec) @@ -308,6 +321,7 @@ def get_key_base(p, prec, type, print_mode, names, ram_name, print_pos, print_se padic_field_cache = {} DEFAULT_PREC = Integer(20) + class Qp_class(UniqueFactory): r""" A creation function for `p`-adic fields. @@ -320,6 +334,9 @@ class Qp_class(UniqueFactory): In the lattice capped case, ``prec`` can either be a pair (``relative_cap``, ``absolute_cap``) or an integer (understood at relative cap). + In the lazy case, ``prec`` can be either a + pair (``default_prec``, ``halting_prec``) or an integer + (understood at default precision). Except in the floating point case, individual elements keep track of their own precision. See TYPES and PRECISION below. @@ -2939,7 +2956,7 @@ def ZpLF(p, prec=None, *args, **kwds): """ return Zp(p, prec, 'lattice-float', *args, **kwds) -def ZpL(p, prec=None, *args, **kwds): +def ZpL(p, prec=None, halt=None, *args, **kwds): r""" A shortcut function to create lazy `p`-adic rings. @@ -2969,6 +2986,22 @@ def ZpL(p, prec=None, *args, **kwds): sage: R.default_prec() 20 + A default halting precision is also set. It is the default absolute + precision at which the elements will be compared. By default, it is + twice the default precision:: + + sage: R.halting_prec() + 40 + + However, both the default precision and the halting precision can be + customized at the creation of the parent as follows: + + sage: S = ZpL(5, prec=10, halt=100) + sage: S.default_prec() + 10 + sage: S.halting_prec() + 100 + One creates elements as usual:: sage: a = R(17/42) @@ -3022,37 +3055,30 @@ def ZpL(p, prec=None, *args, **kwds): Checking equalities between lazy `p`-adics is a bit subtle and can sometimes be puzzling at first glance. - Actually, when it is obvious (from the previous computations) that - the two sides of the equality are different, everything works well:: + Basically, elements are compared at the default halting precision:: sage: a == b False - On the contrary, when the two numbers we want to compare are indeed - equal, it is not possible to conclude after a finite amount of - computations. In this case, an error is raised:: - sage: a == sqrt(a)^2 - Traceback (most recent call last): - ... - PrecisionError: unable to decide equality; try to bound precision - - and we are forced to check equality at some given finite precision - as follows:: - - sage: a[:20] == sqrt(a)^2 True - sage: a[:100] == sqrt(a)^2 + sage: a == sqrt(a)^2 + 5^50 True - Finally, note that checking equality may fail even when the two - operands are different but when the first different digit is beyond - the default precision:: + However, if both sides of the equalities have been previously + computed with more digits, those digits are taken into account. + Hence comparing two elements at different times can produce + different results:: - sage: b == b + 5^50 - Traceback (most recent call last): - ... - PrecisionError: unable to decide equality; try to bound precision + sage: aa = sqrt(a)^2 + 5^50 + sage: a == aa + True + sage: a[:60] + ...?244200244200244200244200244200244200244200244200244200244201 + sage: aa[:60] + ...?244200244300244200244200244200244200244200244200244200244201 + sage: a == aa + False .. RUBRIC:: Self-referent numbers @@ -3112,7 +3138,7 @@ def ZpL(p, prec=None, *args, **kwds): sage: w ...30212422041102444403 """ - return Zp(p, prec, 'lazy', *args, **kwds) + return Zp(p, (prec, halt), 'lazy', *args, **kwds) ####################################################################################################### diff --git a/src/sage/rings/padics/generic_nodes.py b/src/sage/rings/padics/generic_nodes.py index ef303f269d2..1b66634e544 100644 --- a/src/sage/rings/padics/generic_nodes.py +++ b/src/sage/rings/padics/generic_nodes.py @@ -795,7 +795,24 @@ def default_prec(self): sage: S(1/17) ...4201213403 """ - return self._prec + return self._default_prec + + def halting_prec(self): + r""" + Return the default halting precision of this lazy `p`-adic ring. + + The halting precision is the precision at which elements of this + parent are compared (unless more digits have been previously + computed). + By default, it is twice the default precision. + + EXAMPLES:: + + sage: R = ZpL(5, print_mode="digits") + sage: R.halting_prec() + 40 + """ + return self._halting_prec def precision_cap(self): r""" @@ -1370,7 +1387,11 @@ def construction(self, forbid_frac_field=False): extras = {'print_mode':self._printer.dict(), 'type':self._prec_type(), 'names':self._names} if hasattr(self, '_label'): extras['label'] = self._label - return (CompletionFunctor(self.prime(), self._precision_cap(), extras), ZZ) + if self._prec_type() == "lazy": + prec = (self._default_prec, self._halting_prec) + else: + prec = self._precision_cap() + return (CompletionFunctor(self.prime(), prec, extras), ZZ) def random_element(self, algorithm='default'): r""" @@ -1539,6 +1560,10 @@ def construction(self, forbid_frac_field=False): extras = {'print_mode':self._printer.dict(), 'type':self._prec_type(), 'names':self._names} if hasattr(self, '_label'): extras['label'] = self._label - return (CompletionFunctor(self.prime(), self._precision_cap(), extras), QQ) + if self._prec_type() == "lazy": + prec = (self._default_prec, self._halting_prec) + else: + prec = self._precision_cap() + return (CompletionFunctor(self.prime(), prec, extras), QQ) else: return FractionField(), self.integer_ring() diff --git a/src/sage/rings/padics/lazy_template.pxi b/src/sage/rings/padics/lazy_template.pxi index 48299acd4a8..f4af29f5c13 100644 --- a/src/sage/rings/padics/lazy_template.pxi +++ b/src/sage/rings/padics/lazy_template.pxi @@ -550,7 +550,7 @@ cdef class LazyElement(pAdicGenericElement): 7^5 + 6*7^6 + 3*7^7 + 6*7^8 + 5*7^9 + ... sage: a2 = a1.slice(3, 8); a2 7^5 + 6*7^6 + 3*7^7 + ... - sage: a2[:100] == a.slice(5, 8) + sage: a2 == a.slice(5, 8) True """ if start is None: @@ -699,8 +699,8 @@ cdef class LazyElement(pAdicGenericElement): - ``prec`` -- an integer or ``None`` (default: ``None``); if given, compare the two elements at this precision; otherwise - use the default precision of the parent and raise an error - if equality could not be decided + use the default halting precision of the parent and raise an + error if equality could not be decided EXAMPLES:: @@ -712,62 +712,51 @@ cdef class LazyElement(pAdicGenericElement): sage: a.is_equal_to(b) False - - When equality indeed holds, it is not possible to conclude by - comparing more and more accurate approximations. - In this case, the computation is abandonned:: - sage: a.is_equal_to(b + c) - Traceback (most recent call last): - ... - PrecisionError: unable to decide equality; try to bound precision + True - Bounding precision gives a + When `prec` is not given, elements are compared at the maximum + of the working precision (i.e. the precision at which the elements + to compare have been already computed) and the default halting precision + of the parent. + This may sometimes lead to unexpected behaviour:: - sage: a.is_equal_to(b + c, prec=20) - True - sage: a.is_equal_to(b + c, prec=100) + sage: s = b + c + 7^50 + sage: a.is_equal_to(s) True + sage: a[:100].is_equal_to(s) + False + sage: a.is_equal_to(s) + False - In some cases, equality tests may fail when the first different - digit is beyond the default precision of the parent:: + For this reason, explictely bounding precision is strongly recommended:: - sage: a.is_equal_to(a + 7^50) - Traceback (most recent call last): - ... - PrecisionError: unable to decide equality; try to bound precision + sage: a.is_equal_to(s, prec=20) + True + sage: a.is_equal_to(s, prec=100) + False """ - cdef long default_prec + cdef long halt if self is right: return True if self._valuation >= maxordp and right._valuation >= maxordp: return True if prec is None: - default_prec = self._parent.default_prec() - if self.prime_pow.in_field: - self._jump_relative_c(default_prec, self._valuation + default_prec) - right._jump_relative_c(default_prec, right._valuation + default_prec) - else: - self._jump_c(default_prec) - right._jump_c(default_prec) - prec = min(self._valuation + self._precrel, right._valuation + right._precrel) - if not self._is_equal(right, prec, True): - return False prec = min(self._precbound, right._precbound) else: prec = Integer(prec) if prec < maxordp: return self._is_equal(right, prec, True) - else: - raise PrecisionError("unable to decide equality; try to bound precision") + prec = min(self._valuation + self._precrel, right._valuation + right._precrel) + halt = min(self._parent.halting_prec(), maxordp) + return self._is_equal(right, max(prec, halt), True) def __eq__(self, other): r""" Return ``True`` of this element is equal to ``other`` at the - working precision. - - If equality cannot be decided, an error is raised. + maximum between the working precision and the default halting + precision of the parent. TESTS:: @@ -777,12 +766,15 @@ cdef class LazyElement(pAdicGenericElement): sage: z = R(1/6) sage: x == y + z - Traceback (most recent call last): - ... - PrecisionError: unable to decide equality; try to bound precision + True - sage: x[:20] == y + z + sage: s = y + z + 5^50 + sage: x == s True + sage: x[:100] == s + False + sage: x == s + False """ if not have_same_parent(self, other): try: @@ -804,17 +796,21 @@ cdef class LazyElement(pAdicGenericElement): sage: bool(x) True - In the next example, only `20` digits (which is the default precision) - are computed so `x` is indistinguishable from `0` at this stage:: + In the next example, only `40` digits (which is the default halting + precision) are computed so `x` is indistinguishable from `0` at this + stage:: - sage: x = R(5^21) + sage: x = R(5^41) sage: bool(x) False """ + cdef long halt if self._precrel: return True - # return self != 0 - self._jump_relative_c(1, self._valuation + self._parent.default_prec()) + prec = self._precbound + if prec >= maxordp: + halt = min(self._parent.halting_prec(), maxordp) + self._jump_c(max(self._valuation + self._precrel, halt)) return bool(self._precrel) cpdef bint _is_exact_zero(self) except -1: @@ -1086,7 +1082,7 @@ cdef class LazyElement(pAdicGenericElement): - ``halt`` -- an integer or a boolean (default: ``True``); the absolute precision after which the computation is abandonned if the first significant digit has not been found yet; - if ``True``, the default precision of the parent is used; + if ``True``, the default halting precision of the parent is used; if ``False``, the computation is never abandonned - ``permissive`` -- a boolean (default: ``False`` if ``prec`` @@ -1095,7 +1091,7 @@ cdef class LazyElement(pAdicGenericElement): EXAMPLES:: - sage: R = ZpL(5, prec=10) + sage: R = ZpL(5, prec=10, halt=10) sage: a = R(20/21); a 4*5 + 4*5^2 + 5^4 + 4*5^6 + 3*5^7 + 4*5^8 + ... sage: a.at_precision_relative(5) @@ -1160,7 +1156,7 @@ cdef class LazyElement(pAdicGenericElement): if prec > maxordp: raise OverflowError("beyond maximal precision (which is %s)" % maxordp) if halt is True: - halt = self._valuation + default_prec + halt = self._parent.halting_prec() elif halt is False: halt = maxordp else: @@ -1258,12 +1254,12 @@ cdef class LazyElement(pAdicGenericElement): - ``halt`` -- an integer or a boolean (default: ``True``); the absolute precision after which the computation is abandonned if the first significant digit has not been found yet; - if ``True``, the default precision of the parent is used; + if ``True``, the default halting precision of the parent is used; if ``False``, the computation is never abandonned EXAMPLES:: - sage: R = ZpL(5, 10) + sage: R = ZpL(5, prec=10, halt=10) sage: a = R(2001); a 1 + 5^3 + 3*5^4 + ... sage: a.valuation() @@ -1329,7 +1325,7 @@ cdef class LazyElement(pAdicGenericElement): if self._valuation <= -maxordp: raise PrecisionError("no lower bound on the valuation is known") if halt is True: - halt = self._valuation + self._parent.default_prec() + halt = self._parent.halting_prec() elif halt is False: halt = maxordp else: @@ -1348,12 +1344,12 @@ cdef class LazyElement(pAdicGenericElement): - ``halt`` -- an integer or a boolean (default: ``True``); the absolute precision after which the computation is abandonned if the first significant digit has not been found yet; - if ``True``, the default precision of the parent is used; + if ``True``, the default halting precision of the parent is used; if ``False``, the computation is never abandonned EXAMPLES:: - sage: R = ZpL(5, 10) + sage: R = ZpL(5, prec=10, halt=10) sage: a = R(20/21); a 4*5 + 4*5^2 + 5^4 + 4*5^6 + 3*5^7 + 4*5^8 + ... sage: a.unit_part() @@ -1393,7 +1389,7 @@ cdef class LazyElement(pAdicGenericElement): - ``halt`` -- an integer or a boolean (default: ``True``); the absolute precision after which the computation is abandonned if the first significant digit has not been found yet; - if ``True``, the default precision of the parent is used; + if ``True``, the default halting precision of the parent is used; if ``False``, the computation is never abandonned EXAMPLES:: @@ -1883,7 +1879,7 @@ cdef class LazyElement(pAdicGenericElement): ....: R = ZpL(p) ....: x = 1 + p * R.random_element() ....: y = x.sqrt() - ....: assert(x[:100] == y^2) + ....: assert(x == y^2) """ return element_class_sqrt(self._parent, self) @@ -2541,7 +2537,7 @@ cdef class LazyElement_random(LazyElementWithDigits): sage: b[:30] ...?343214211432220241412003314311 - sage: a[:100] == b + sage: a == b True """ return self.__class__, (self._parent, self._initialvaluation, self._precbound, self._seed) @@ -2638,7 +2634,7 @@ cdef class LazyElement_slice(LazyElement): sage: R = ZpL(5, print_mode="digits") sage: x = R(20/21) sage: y = x.slice(3, 6) - sage: y[:20] == loads(dumps(y)) # indirect doctest + sage: y == loads(dumps(y)) # indirect doctest True """ return self.__class__, (self._parent, self._x, self._start, self._stop, self._shift) @@ -2786,7 +2782,7 @@ cdef class LazyElement_add(LazyElementWithDigits): sage: R = ZpL(5) sage: x = R.random_element() + R.random_element() - sage: x[:20] == loads(dumps(x)) # indirect doctest + sage: x == loads(dumps(x)) # indirect doctest True """ return self.__class__, (self._parent, self._x, self._y) @@ -2893,7 +2889,7 @@ cdef class LazyElement_sub(LazyElementWithDigits): sage: R = ZpL(5) sage: x = R.random_element() - R.random_element() - sage: x[:20] == loads(dumps(x)) # indirect doctest + sage: x == loads(dumps(x)) # indirect doctest True """ return self.__class__, (self._parent, self._x, self._y) @@ -3028,7 +3024,7 @@ cdef class LazyElement_mul(LazyElementWithDigits): sage: R = ZpL(5) sage: x = R.random_element() * R.random_element() - sage: x[:20] == loads(dumps(x)) # indirect doctest + sage: x == loads(dumps(x)) # indirect doctest True """ return self.__class__, (self._parent, self._x, self._y) @@ -3270,7 +3266,7 @@ cdef class LazyElement_div(LazyElementWithDigits): sage: R = ZpL(5) sage: x = R(20) / R(21) - sage: x[:20] == loads(dumps(x)) # indirect doctest + sage: x == loads(dumps(x)) # indirect doctest True """ return self.__class__, (self._parent, self._num, self._denom, self._valuation, self._precbound) @@ -3414,7 +3410,7 @@ cdef class LazyElement_sqrt(LazyElementWithDigits): sage: R = ZpL(5) sage: x = R(6).sqrt() - sage: x[:20] == loads(dumps(x)) # indirect doctest + sage: x == loads(dumps(x)) # indirect doctest True """ return self.__class__, (self._parent, self._x) @@ -3599,7 +3595,7 @@ cdef class LazyElement_teichmuller(LazyElementWithDigits): sage: R = ZpL(7) sage: x = R.teichmuller(2) - sage: x[:20] == loads(dumps(x)) # indirect doctest + sage: x == loads(dumps(x)) # indirect doctest True """ xbar = digit_get_sage(element_get_digit(self._digits, 0)) @@ -3720,7 +3716,7 @@ cdef class LazyElement_unknown(LazyElementWithDigits): sage: x = R.unknown() sage: x.set(1 + 7*x^2) True - sage: x[:20] == loads(dumps(x)) # indirect doctest + sage: x == loads(dumps(x)) # indirect doctest True """ digits = [ ] @@ -3845,9 +3841,9 @@ def unpickle_unknown(uid, cls, parent, valuation, digits, definition): sage: y.set(3 + 14*x^2) True - sage: x[:20] == loads(dumps(x)) # indirect doctest + sage: x == loads(dumps(x)) # indirect doctest True - sage: y[:20] == loads(dumps(y)) # indirect doctest + sage: y == loads(dumps(y)) # indirect doctest True """ if uid in persist.already_unpickled: diff --git a/src/sage/rings/padics/padic_base_leaves.py b/src/sage/rings/padics/padic_base_leaves.py index 2a128c4b252..47d20581249 100644 --- a/src/sage/rings/padics/padic_base_leaves.py +++ b/src/sage/rings/padics/padic_base_leaves.py @@ -1130,7 +1130,8 @@ def __init__(self, p, prec, print_mode, names): sage: TestSuite(R).run(skip=['_test_log', '_test_matrix_smith']) """ from sage.rings.padics import padic_lazy_element - pAdicRingBaseGeneric.__init__(self, p, prec, print_mode, names, padic_lazy_element.pAdicLazyElement) + self._default_prec, self._halting_prec = prec + pAdicRingBaseGeneric.__init__(self, p, self._default_prec, print_mode, names, padic_lazy_element.pAdicLazyElement) self._element_class_module = padic_lazy_element self._element_class_prefix = "pAdicLazyElement_" @@ -1164,6 +1165,7 @@ def __init__(self, p, prec, print_mode, names): sage: TestSuite(K).run(skip=['_test_log', '_test_matrix_smith']) """ from sage.rings.padics import padic_lazy_element - pAdicFieldBaseGeneric.__init__(self, p, prec, print_mode, names, padic_lazy_element.pAdicLazyElement) + self._default_prec, self._halting_prec = prec + pAdicFieldBaseGeneric.__init__(self, p, self._default_prec, print_mode, names, padic_lazy_element.pAdicLazyElement) self._element_class_module = padic_lazy_element self._element_class_prefix = "pAdicLazyElement_" From da19ab399c9c2ad0ed0da302db2cbb14138deb9b Mon Sep 17 00:00:00 2001 From: Christian Wuthrich Date: Fri, 2 Apr 2021 21:24:08 +0100 Subject: [PATCH 266/406] ticket 31573 improve docstring --- src/sage/schemes/elliptic_curves/formal_group.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/formal_group.py b/src/sage/schemes/elliptic_curves/formal_group.py index b9162eed607..b7f007af459 100644 --- a/src/sage/schemes/elliptic_curves/formal_group.py +++ b/src/sage/schemes/elliptic_curves/formal_group.py @@ -733,7 +733,7 @@ def mult_by_n(self, n, prec=10): def sigma(self, prec=10): r""" - Return the Weierstrass sigma function as a formal powerseries + Return the Weierstrass sigma function as a formal power series solution to the differential equation .. MATH:: @@ -750,7 +750,7 @@ def sigma(self, prec=10): OUTPUT: a power series with given precision Other solutions can be obtained by multiplication with - an function of the form `\exp(c z^2)`. + a function of the form `\exp(c z^2)`. If the curve has good ordinary reduction at a prime `p` then there is a canonical choice of `c` that produces the canonical `p`-adic sigma function. @@ -768,12 +768,9 @@ def sigma(self, prec=10): k = self.curve().base_ring() fl = self.log(prec) - #R = rings.PolynomialRing(k, 'c') - #c = R.gen() F = fl.reverse() S = rings.LaurentSeriesRing(k,'z') - #c = S(c) z = S.gen() F = F(z + O(z**prec)) wp = self.x()(F) + (a1**2 + 4*a2)/12 From bf6e5f88af4772da93c89b41cfce7c8da0dbae01 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 4 Oct 2020 15:14:15 -0700 Subject: [PATCH 267/406] build/make/Makefile.in: Change some SAGE_LOCAL to SAGE_VENV --- build/bin/sage-spkg | 1 + build/make/Makefile.in | 50 +++++++++++++++++++------------- build/pkgs/libnauty/dependencies | 2 +- 3 files changed, 32 insertions(+), 21 deletions(-) diff --git a/build/bin/sage-spkg b/build/bin/sage-spkg index 1553f28bc0f..50b0fe21978 100755 --- a/build/bin/sage-spkg +++ b/build/bin/sage-spkg @@ -533,6 +533,7 @@ for lib in "\$SAGE_ROOT/build/bin/sage-dist-helpers" "\$SAGE_SRC/bin/sage-env-co exit 1 fi done +export PATH="$SAGE_INST_LOCAL/bin:$PATH" export SAGE_INST_LOCAL="$SAGE_INST_LOCAL" diff --git a/build/make/Makefile.in b/build/make/Makefile.in index 6273b1356a5..2699e2b4472 100644 --- a/build/make/Makefile.in +++ b/build/make/Makefile.in @@ -26,10 +26,8 @@ $(error This Makefile needs to be invoked by build/make/install) endif endif -# Directory to keep track of which packages are installed -SAGE_SPKG_INST = $(SAGE_LOCAL)/var/lib/sage/installed - -INST = $(SAGE_SPKG_INST) +# Directory to keep track of which packages are installed - relative to installation prefix +SPKG_INST_RELDIR = var/lib/sage/installed # Aliases for mutually exclusive standard packages selected at configure time TOOLCHAIN = @SAGE_TOOLCHAIN@ @@ -121,7 +119,13 @@ PIP_PACKAGES = @SAGE_PIP_PACKAGES@ # Packages that use the 'script' package build rules SCRIPT_PACKAGES = @SAGE_SCRIPT_PACKAGES@ +# Generate variables for installation locations +$(foreach pkgname,$(BUILT_PACKAGES) $(DUMMY_PACKAGES),\ + $(eval local_$(pkgname) = $(SAGE_LOCAL))) +# Override this for Python packages, which we install in the SAGE_VENV tree. +$(foreach pkgname,$(PYTHON_PACKAGES),\ + $(eval local_$(pkgname) = $(SAGE_VENV))) # Generate the actual inst_ variables; for each package that is # actually built this generates a line like: @@ -139,19 +143,21 @@ SCRIPT_PACKAGES = @SAGE_SCRIPT_PACKAGES@ # # inst_git = $(INST)/.dummy +$(foreach pkgname,$(BUILT_PACKAGES) $(DUMMY_PACKAGES),\ + $(eval real_inst_$(pkgname) = $(local_$(pkgname))/$(SPKG_INST_RELDIR)/$(pkgname)-$(vers_$(pkgname)))) $(foreach pkgname,$(BUILT_PACKAGES),\ - $(eval inst_$(pkgname) = $$(INST)/$(pkgname)-$(vers_$(pkgname)))) - + $(eval inst_$(pkgname) = $(real_inst_$(pkgname)))) $(foreach pkgname,$(DUMMY_PACKAGES),\ - $(eval inst_$(pkgname) = $$(INST)/.dummy)) + $(eval inst_$(pkgname) = $(SAGE_LOCAL)/$(SPKG_INST_RELDIR)/.dummy)) # Override this for pip packages, for which we do not keep an installation record # in addition to what pip is already doing. $(foreach pkgname,$(PIP_PACKAGES),\ + $(eval real_inst_$(pkgname) = $(pkgname))\ $(eval inst_$(pkgname) = $(pkgname))) # Dummy target for packages which are not installed -$(INST)/.dummy: +$(SAGE_LOCAL)/$(SPKG_INST_RELDIR)/.dummy: touch $@ @@ -195,6 +201,10 @@ STARTED = $(SAGE_LOCAL)/etc/sage-started.txt PYTHON_FOR_VENV = @PYTHON_FOR_VENV@ +# Where the Sage distribution installs Python packages. +# This can be overridden by 'make SAGE_VENV=/some/venv'. +SAGE_VENV = $(SAGE_LOCAL) + ifneq ($(PYTHON_FOR_VENV),) # Special rule for making the Python virtualenv from the system Python (Python # 3 only). $(PYTHON) is set in Makefile to python3_venv. @@ -206,10 +216,10 @@ ifneq ($(PYTHON_FOR_VENV),) ifeq ($(PYTHON),python3) PYTHON = python3_venv endif -inst_python3_venv = $(SAGE_LOCAL)/pyvenv.cfg +inst_python3_venv = $(SAGE_VENV)/pyvenv.cfg $(inst_python3_venv): - $(PYTHON_FOR_VENV) $(SAGE_ROOT)/build/bin/sage-venv "$(SAGE_LOCAL)" + $(PYTHON_FOR_VENV) $(SAGE_ROOT)/build/bin/sage-venv "$(SAGE_VENV)" endif # Build everything and start Sage. @@ -526,19 +536,19 @@ define NORMAL_PACKAGE_templ ########################################## $(1)-build-deps: $(3) -$$(INST)/$(1)-$(2): $(3) +$$(real_inst_$(1)): $(3) +$(MAKE_REC) $(1)-no-deps -$(1): $$(INST)/$(1)-$(2) +$(1): $$(real_inst_$(1)) $(1)-no-deps: - +$(AM_V_at)sage-logger -p 'SAGE_CHECK=$$(SAGE_CHECK_$(1)) $$(SAGE_SPKG) $$(SAGE_SPKG_OPTIONS) \ + +$(AM_V_at)sage-logger -p 'SAGE_CHECK=$$(SAGE_CHECK_$(1)) PATH=$$(local_$(1))/bin:$$$$PATH $$(SAGE_SPKG) $$(SAGE_SPKG_OPTIONS) \ $(if $(filter $(1),$(TOOLCHAIN_DEPS)),--keep-existing) \ - $(1)-$(2)' '$$(SAGE_LOGS)/$(1)-$(2).log' + $(1)-$(2) $$(local_$(1))' '$$(SAGE_LOGS)/$(1)-$(2).log' $(1)-clean: - sage-spkg-uninstall $(if $(filter $(1),$(TOOLCHAIN_DEPS)),--keep-files) \ - $(1) '$(SAGE_LOCAL)' + SAGE_INST_LOCAL=$$(local_$(1)) sage-spkg-uninstall $(if $(filter $(1),$(TOOLCHAIN_DEPS)),--keep-files) \ + $(1) '$$(local_$(1))' .PHONY: $(1) $(1)-clean $(1)-build-deps $(1)-no-deps endef ################################################################# @@ -620,10 +630,10 @@ endif define SCRIPT_PACKAGE_templ $(1)-build-deps: $(3) -$$(INST)/$(1)-$(2): $(3) +$$(real_inst_$(1)): $(3) +$(MAKE_REC) $(1)-no-deps -$(1): $$(INST)/$(1)-$(2) +$(1): $$(real_inst_$(1)) $(1)-no-deps: $(AM_V_at)cd '$$(SAGE_ROOT)/build/pkgs/$(1)' && \ @@ -634,7 +644,7 @@ $(1)-no-deps: SAGE_SPKG_WHEELS=$$(SAGE_LOCAL)/var/lib/sage/wheels \ SAGE_INST_LOCAL=$$(SAGE_LOCAL) \ sage-logger -p '$$(SAGE_ROOT)/build/pkgs/$(1)/spkg-install' '$$(SAGE_LOGS)/$(1)-$(2).log' - touch "$$(INST)/$(1)-$(2)" + touch "$$(real_inst_$(1))" $(1)-uninstall: -$(AM_V_at)cd '$$(SAGE_ROOT)/build/pkgs/$(1)' && \ @@ -643,7 +653,7 @@ $(1)-uninstall: . '$$(SAGE_ROOT)/build/bin/sage-build-env-config' && \ . '$$(SAGE_ROOT)/build/bin/sage-build-env' && \ '$$(SAGE_ROOT)/build/pkgs/$(1)/spkg-uninstall' - -rm -f "$$(INST)/$(1)-$(2)" + -rm -f "$$(real_inst_$(1))" .PHONY: $(1) $(1)-uninstall $(1)-build-deps $(1)-no-deps $(1)-clean endef diff --git a/build/pkgs/libnauty/dependencies b/build/pkgs/libnauty/dependencies index 4f7f1a035ea..88254f7b556 100644 --- a/build/pkgs/libnauty/dependencies +++ b/build/pkgs/libnauty/dependencies @@ -1,4 +1,4 @@ -$(INST)/nauty-$(vers_nauty) +$(SAGE_LOCAL)/$(SPKG_INST_RELDIR)/nauty-$(vers_nauty) ---------- All lines of this file are ignored except the first. From 832f96a1f9e57de4a7df8feabff5620ef6b2e5f9 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 25 Feb 2021 22:32:52 -0800 Subject: [PATCH 268/406] build/make/Makefile.in: Rework using tree_... variables --- build/make/Makefile.in | 70 +++++++++++++++++++++++------------------- 1 file changed, 38 insertions(+), 32 deletions(-) diff --git a/build/make/Makefile.in b/build/make/Makefile.in index 2699e2b4472..ef896a8663a 100644 --- a/build/make/Makefile.in +++ b/build/make/Makefile.in @@ -119,13 +119,7 @@ PIP_PACKAGES = @SAGE_PIP_PACKAGES@ # Packages that use the 'script' package build rules SCRIPT_PACKAGES = @SAGE_SCRIPT_PACKAGES@ -# Generate variables for installation locations -$(foreach pkgname,$(BUILT_PACKAGES) $(DUMMY_PACKAGES),\ - $(eval local_$(pkgname) = $(SAGE_LOCAL))) -# Override this for Python packages, which we install in the SAGE_VENV tree. -$(foreach pkgname,$(PYTHON_PACKAGES),\ - $(eval local_$(pkgname) = $(SAGE_VENV))) # Generate the actual inst_ variables; for each package that is # actually built this generates a line like: @@ -143,17 +137,14 @@ $(foreach pkgname,$(PYTHON_PACKAGES),\ # # inst_git = $(INST)/.dummy -$(foreach pkgname,$(BUILT_PACKAGES) $(DUMMY_PACKAGES),\ - $(eval real_inst_$(pkgname) = $(local_$(pkgname))/$(SPKG_INST_RELDIR)/$(pkgname)-$(vers_$(pkgname)))) $(foreach pkgname,$(BUILT_PACKAGES),\ - $(eval inst_$(pkgname) = $(real_inst_$(pkgname)))) + $(eval inst_$(pkgname) = $(foreach tree, $(trees_$(pkgname)), $($(tree))/$(SPKG_INST_RELDIR)/$(pkgname)-$(vers_$(pkgname))))) $(foreach pkgname,$(DUMMY_PACKAGES),\ $(eval inst_$(pkgname) = $(SAGE_LOCAL)/$(SPKG_INST_RELDIR)/.dummy)) # Override this for pip packages, for which we do not keep an installation record # in addition to what pip is already doing. $(foreach pkgname,$(PIP_PACKAGES),\ - $(eval real_inst_$(pkgname) = $(pkgname))\ $(eval inst_$(pkgname) = $(pkgname))) # Dummy target for packages which are not installed @@ -531,37 +522,44 @@ pkg_deps = \ # $(1): package name # $(2): package version # $(3): package dependencies +# $(4): package tree variable define NORMAL_PACKAGE_templ ########################################## $(1)-build-deps: $(3) -$$(real_inst_$(1)): $(3) - +$(MAKE_REC) $(1)-no-deps +$$($(4))/$(SPKG_INST_RELDIR)/$(1)-$(2): $(3) + +$(MAKE_REC) $(1)-$(4)-no-deps -$(1): $$(real_inst_$(1)) +$(1): $$($(4))/$(SPKG_INST_RELDIR)/$(1)-$(2) -$(1)-no-deps: - +$(AM_V_at)sage-logger -p 'SAGE_CHECK=$$(SAGE_CHECK_$(1)) PATH=$$(local_$(1))/bin:$$$$PATH $$(SAGE_SPKG) $$(SAGE_SPKG_OPTIONS) \ +$(1)-$(4)-no-deps: + +$(AM_V_at)sage-logger -p 'SAGE_CHECK=$$(SAGE_CHECK_$(1)) PATH=$$($(4))/bin:$$$$PATH $$(SAGE_SPKG) $$(SAGE_SPKG_OPTIONS) \ $(if $(filter $(1),$(TOOLCHAIN_DEPS)),--keep-existing) \ - $(1)-$(2) $$(local_$(1))' '$$(SAGE_LOGS)/$(1)-$(2).log' + $(1)-$(2) $$($(4))' '$$(SAGE_LOGS)/$(1)-$(2).log' -$(1)-clean: - SAGE_INST_LOCAL=$$(local_$(1)) sage-spkg-uninstall $(if $(filter $(1),$(TOOLCHAIN_DEPS)),--keep-files) \ - $(1) '$$(local_$(1))' +$(1)-no-deps: $(1)-$(4)-no-deps + +$(1)-$(4)-clean: + sage-spkg-uninstall $(if $(filter $(1),$(TOOLCHAIN_DEPS)),--keep-files) \ + $(1) '$$($(4))' + +$(1)-clean: $(1)-$(4)-clean .PHONY: $(1) $(1)-clean $(1)-build-deps $(1)-no-deps endef ################################################################# $(foreach pkgname, $(NORMAL_PACKAGES),\ + $(foreach tree, $(trees_$(pkgname)), \ $(eval $(call NORMAL_PACKAGE_templ,$(pkgname),$(vers_$(pkgname)),\ - $(call pkg_deps,$(pkgname))))) + $(call pkg_deps,$(pkgname)),$(tree))))) ifdef DEBUG_RULES $(info # Rules for standard packages) $(foreach pkgname, $(NORMAL_PACKAGES),\ + $(foreach tree, $(trees_$(pkgname)), \ $(info $(call NORMAL_PACKAGE_templ,$(pkgname),$(vers_$(pkgname)),\ - $(call pkg_deps,$(pkgname))))) + $(call pkg_deps,$(pkgname)),$(tree))))) endif # ================================ pip packages =============================== @@ -627,44 +625,52 @@ endif # $(1): package name # $(2): package version # $(3): package dependencies +# $(4): package tree variable + define SCRIPT_PACKAGE_templ $(1)-build-deps: $(3) -$$(real_inst_$(1)): $(3) - +$(MAKE_REC) $(1)-no-deps +$$($(4))/$(SPKG_INST_RELDIR)/$(1)-$(2): $(3) + +$(MAKE_REC) $(1)-$(4)-no-deps -$(1): $$(real_inst_$(1)) +$(1): $$($(4))/$(SPKG_INST_RELDIR)/$(1)-$(2) -$(1)-no-deps: +$(1)-$(4)-no-deps: $(AM_V_at)cd '$$(SAGE_ROOT)/build/pkgs/$(1)' && \ . '$$(SAGE_ROOT)/src/bin/sage-env-config' && \ . '$$(SAGE_ROOT)/src/bin/sage-env' && \ . '$$(SAGE_ROOT)/build/bin/sage-build-env-config' && \ . '$$(SAGE_ROOT)/build/bin/sage-build-env' && \ - SAGE_SPKG_WHEELS=$$(SAGE_LOCAL)/var/lib/sage/wheels \ - SAGE_INST_LOCAL=$$(SAGE_LOCAL) \ + SAGE_SPKG_WHEELS=$$($(4))/var/lib/sage/wheels \ + SAGE_INST_LOCAL=$$($(4)) \ sage-logger -p '$$(SAGE_ROOT)/build/pkgs/$(1)/spkg-install' '$$(SAGE_LOGS)/$(1)-$(2).log' - touch "$$(real_inst_$(1))" + touch "$$($(4))/$(SPKG_INST_RELDIR)/$(1)-$(2)" -$(1)-uninstall: +$(1)-no-deps: $(1)-$(4)-no-deps + +$(1)-$(4)-uninstall: -$(AM_V_at)cd '$$(SAGE_ROOT)/build/pkgs/$(1)' && \ . '$$(SAGE_ROOT)/src/bin/sage-env-config' && \ . '$$(SAGE_ROOT)/src/bin/sage-env' && \ . '$$(SAGE_ROOT)/build/bin/sage-build-env-config' && \ . '$$(SAGE_ROOT)/build/bin/sage-build-env' && \ '$$(SAGE_ROOT)/build/pkgs/$(1)/spkg-uninstall' - -rm -f "$$(real_inst_$(1))" + -rm -f "$$($(4))/$(SPKG_INST_RELDIR)/$(1)-$(2)" + +$(1)-uninstall: $(1)-$(4)-uninstall .PHONY: $(1) $(1)-uninstall $(1)-build-deps $(1)-no-deps $(1)-clean endef $(foreach pkgname,$(SCRIPT_PACKAGES),\ - $(eval $(call SCRIPT_PACKAGE_templ,$(pkgname),$(vers_$(pkgname)),$(call pkg_deps,$(pkgname))))) + $(foreach tree, $(trees_$(pkgname)), \ + $(eval $(call SCRIPT_PACKAGE_templ,$(pkgname),$(vers_$(pkgname)),$(call pkg_deps,$(pkgname)),$(tree))))) ifdef DEBUG_RULES $(info # Rules for script packages) $(foreach pkgname,$(SCRIPT_PACKAGES),\ - $(info $(call SCRIPT_PACKAGE_templ,$(pkgname),$(vers_$(pkgname)),$(call pkg_deps,$(pkgname))))) + $(foreach tree, $(trees_$(pkgname)), \ + $(info $(call SCRIPT_PACKAGE_templ,$(pkgname),$(vers_$(pkgname)),$(call pkg_deps,$(pkgname)),$(tree))))) endif # sagelib depends on this so that its install script is always executed From 3436ad68da50a3f2caa5dcb0f8e76e7f33ecca80 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 25 Feb 2021 22:45:33 -0800 Subject: [PATCH 269/406] build/make/Makefile.in: Define SAGE_VENV earlier --- build/make/Makefile.in | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/build/make/Makefile.in b/build/make/Makefile.in index ef896a8663a..390501566cf 100644 --- a/build/make/Makefile.in +++ b/build/make/Makefile.in @@ -52,6 +52,10 @@ SAGE_SPKG = sage-spkg # These are added to SAGE_SPKG in the call SAGE_SPKG_OPTIONS = @SAGE_SPKG_OPTIONS@ +# Where the Sage distribution installs Python packages. +# This can be overridden by 'make SAGE_VENV=/some/venv'. +SAGE_VENV = $(SAGE_LOCAL) + # Generate/install sage-specific .pc files. # see build/pkgs/gsl/spkg-configure.m4 $(SAGE_PKGCONFIG)/gsl.pc: @@ -192,10 +196,6 @@ STARTED = $(SAGE_LOCAL)/etc/sage-started.txt PYTHON_FOR_VENV = @PYTHON_FOR_VENV@ -# Where the Sage distribution installs Python packages. -# This can be overridden by 'make SAGE_VENV=/some/venv'. -SAGE_VENV = $(SAGE_LOCAL) - ifneq ($(PYTHON_FOR_VENV),) # Special rule for making the Python virtualenv from the system Python (Python # 3 only). $(PYTHON) is set in Makefile to python3_venv. From 8cc0b3ca43d4c9f852d86079fa6edb1d6aa4cf14 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 26 Feb 2021 09:42:50 -0800 Subject: [PATCH 270/406] WIP configure --with-sage-venv --- build/make/Makefile.in | 2 +- configure.ac | 7 +++++++ src/bin/sage-src-env-config.in | 4 ++-- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/build/make/Makefile.in b/build/make/Makefile.in index 390501566cf..73e0864bd29 100644 --- a/build/make/Makefile.in +++ b/build/make/Makefile.in @@ -54,7 +54,7 @@ SAGE_SPKG_OPTIONS = @SAGE_SPKG_OPTIONS@ # Where the Sage distribution installs Python packages. # This can be overridden by 'make SAGE_VENV=/some/venv'. -SAGE_VENV = $(SAGE_LOCAL) +SAGE_VENV = @SAGE_VENV@ # Generate/install sage-specific .pc files. # see build/pkgs/gsl/spkg-configure.m4 diff --git a/configure.ac b/configure.ac index 7274c149656..41719627e5d 100644 --- a/configure.ac +++ b/configure.ac @@ -74,6 +74,13 @@ fi SAGE_SRC="$SAGE_ROOT/src" SAGE_SPKG_INST="$SAGE_LOCAL/var/lib/sage/installed" +AC_ARG_WITH([sage-venv], + [AS_HELP_STRING([--with-sage-venv=SAGE_VENV], + [put Python packages into an installation hierarchy separate from prefix])], + [SAGE_VENV="$withval"], + [SAGE_VENV="$SAGE_LOCAL"]) ############ FIXME: need to make absolute +AC_SUBST([SAGE_VENV]) + #--------------------------------------------------------- AC_ARG_ENABLE([build-as-root], diff --git a/src/bin/sage-src-env-config.in b/src/bin/sage-src-env-config.in index b9e8692f68f..d8aeb8a9023 100644 --- a/src/bin/sage-src-env-config.in +++ b/src/bin/sage-src-env-config.in @@ -32,8 +32,8 @@ export SAGE_LOCAL="@prefix@" # SAGE_VENV is the root of the virtual environment of sagelib. -# Currently, it is identical to SAGE_LOCAL -export SAGE_VENV="@prefix@" +# By default, it is identical to SAGE_LOCAL +export SAGE_VENV="@SAGE_VENV@" # SAGE_ROOT is the location of the Sage source/build tree. export SAGE_ROOT="@SAGE_ROOT@" From 1cfbe2bf13f2f4cefc98e2ac42e99e2e83c91f24 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 3 Apr 2021 13:49:14 -0700 Subject: [PATCH 271/406] build/bin/sage-spkg: Change more SAGE_LOCAL to SAGE_INST_LOCAL --- build/bin/sage-spkg | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/build/bin/sage-spkg b/build/bin/sage-spkg index 50b0fe21978..c92f315f36f 100755 --- a/build/bin/sage-spkg +++ b/build/bin/sage-spkg @@ -702,7 +702,7 @@ rm -f "$SAGE_DESTDIR_LOCAL/lib64" # All spkgs should eventually support this, but fall back on old behavior in # case DESTDIR=$SAGE_DESTDIR installation was not used -echo "Copying package files from temporary location $SAGE_DESTDIR to $SAGE_LOCAL" +echo "Copying package files from temporary location $SAGE_DESTDIR to $SAGE_INST_LOCAL" if [ -d "$SAGE_DESTDIR" ]; then # Some `find` implementations will put superfluous slashes in the # output if we give them a directory name with a slash; so make sure @@ -718,8 +718,8 @@ if [ -d "$SAGE_DESTDIR" ]; then # Generate installed file manifest FILE_LIST="$(cd "$PREFIX" && find . -type f -o -type l | sed 's|^\./||' | sort)" - # Copy files into $SAGE_LOCAL - $SAGE_SUDO cp -Rp "$PREFIX/." "$SAGE_LOCAL" + # Copy files into $SAGE_INST_LOCAL + $SAGE_SUDO cp -Rp "$PREFIX/." "$SAGE_INST_LOCAL" if [ $? -ne 0 ]; then error_msg "Error copying files for $PKG_NAME." exit 1 @@ -785,7 +785,7 @@ fi # Note: spkg-check tests are run after the package has been copied into -# SAGE_LOCAL. It might make more sense to run the tests before, but the +# SAGE_INST_LOCAL. It might make more sense to run the tests before, but the # spkg-check scripts were written before use of DESTDIR installs, and so # fail in many cases. This might be good to change later. From 2202f842b7f7bf0a9f83d9dcdb07f57c2002dfd2 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 3 Apr 2021 13:49:40 -0700 Subject: [PATCH 272/406] configure.ac: Finish --with-sage-venv handling --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 41719627e5d..e0e6c0bb447 100644 --- a/configure.ac +++ b/configure.ac @@ -78,7 +78,7 @@ AC_ARG_WITH([sage-venv], [AS_HELP_STRING([--with-sage-venv=SAGE_VENV], [put Python packages into an installation hierarchy separate from prefix])], [SAGE_VENV="$withval"], - [SAGE_VENV="$SAGE_LOCAL"]) ############ FIXME: need to make absolute + [SAGE_VENV='${SAGE_LOCAL}'])dnl Quoted so that it is resolved at build time by shell/Makefile AC_SUBST([SAGE_VENV]) #--------------------------------------------------------- From b604a6853ccbfbce775eebeace429d0bd4bdd791 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 3 Apr 2021 17:12:50 -0700 Subject: [PATCH 273/406] build/make/Makefile.in: Also source sage-src-env-config for script packages --- build/make/Makefile.in | 2 ++ 1 file changed, 2 insertions(+) diff --git a/build/make/Makefile.in b/build/make/Makefile.in index 73e0864bd29..c494928842b 100644 --- a/build/make/Makefile.in +++ b/build/make/Makefile.in @@ -637,6 +637,7 @@ $(1): $$($(4))/$(SPKG_INST_RELDIR)/$(1)-$(2) $(1)-$(4)-no-deps: $(AM_V_at)cd '$$(SAGE_ROOT)/build/pkgs/$(1)' && \ + . '$$(SAGE_ROOT)/src/bin/sage-src-env-config' && \ . '$$(SAGE_ROOT)/src/bin/sage-env-config' && \ . '$$(SAGE_ROOT)/src/bin/sage-env' && \ . '$$(SAGE_ROOT)/build/bin/sage-build-env-config' && \ @@ -650,6 +651,7 @@ $(1)-no-deps: $(1)-$(4)-no-deps $(1)-$(4)-uninstall: -$(AM_V_at)cd '$$(SAGE_ROOT)/build/pkgs/$(1)' && \ + . '$$(SAGE_ROOT)/src/bin/sage-src-env-config' && \ . '$$(SAGE_ROOT)/src/bin/sage-env-config' && \ . '$$(SAGE_ROOT)/src/bin/sage-env' && \ . '$$(SAGE_ROOT)/build/bin/sage-build-env-config' && \ From 92f1a1bba94a5dd6a4c9458438c39b781b101ead Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 3 Apr 2021 17:13:26 -0700 Subject: [PATCH 274/406] build/pkgs/pynac/spkg-install.in: Build with PYTHON_FOR_VENV --- build/pkgs/pynac/spkg-install.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/pkgs/pynac/spkg-install.in b/build/pkgs/pynac/spkg-install.in index 9fa0d99b2ed..47ff112fd77 100644 --- a/build/pkgs/pynac/spkg-install.in +++ b/build/pkgs/pynac/spkg-install.in @@ -11,7 +11,7 @@ build_pynac() { cd ${PYNACDIR} PKG_CONFIG_PATH=${SAGE_LOCAL}/lib/pkgconfig; export PKG_CONFIG_PATH - sdh_configure --disable-static --with-giac=no PYTHON=python3 + sdh_configure --disable-static --with-giac=no PYTHON=${PYTHON_FOR_VENV} sdh_make cd ${WORKDIR} } From cfad0f223f4fecca91da1e8bd06e3af98b17218d Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Sun, 4 Apr 2021 13:27:56 +0200 Subject: [PATCH 275/406] improve equality test and computation of valuation --- src/sage/rings/padics/lazy_template.pxi | 154 ++++++++++++++---- .../rings/padics/lazy_template_header.pxi | 2 + 2 files changed, 120 insertions(+), 36 deletions(-) diff --git a/src/sage/rings/padics/lazy_template.pxi b/src/sage/rings/padics/lazy_template.pxi index f4af29f5c13..2bafff19031 100644 --- a/src/sage/rings/padics/lazy_template.pxi +++ b/src/sage/rings/padics/lazy_template.pxi @@ -689,7 +689,7 @@ cdef class LazyElement(pAdicGenericElement): return self._is_equal(right, min(prec, maxordp), False) @coerce_binop - def is_equal_to(self, LazyElement right, prec=None): + def is_equal_to(self, LazyElement right, prec=None, quiet=None): r""" Compare this element with ``right``. @@ -699,43 +699,45 @@ cdef class LazyElement(pAdicGenericElement): - ``prec`` -- an integer or ``None`` (default: ``None``); if given, compare the two elements at this precision; otherwise - use the default halting precision of the parent and raise an - error if equality could not be decided + use the default halting precision of the parent + + - ``quiet`` -- a boolean (default: ``True`` if ``prec`` is given, + ``False`` otherwise); when the elements cannot be distingiushed + at the given precision, raise an error if ``quiet`` is ``False``, + return ``True`` otherwise. EXAMPLES:: sage: R = ZpL(7) - sage: a = R(1/2) sage: b = R(1/3) sage: c = R(1/6) sage: a.is_equal_to(b) False + + When equality indeed holds, it is not possible to conclude by + comparing more and more accurate approximations. + In this case, an error is raised:: + sage: a.is_equal_to(b + c) - True + Traceback (most recent call last): + ... + PrecisionError: unable to decide equality; try to bound precision - When `prec` is not given, elements are compared at the maximum - of the working precision (i.e. the precision at which the elements - to compare have been already computed) and the default halting precision - of the parent. - This may sometimes lead to unexpected behaviour:: + You can get around this behaviour by passing in ``quiet=True``:: - sage: s = b + c + 7^50 - sage: a.is_equal_to(s) + sage: a.is_equal_to(b + c, quiet=True) True - sage: a[:100].is_equal_to(s) - False - sage: a.is_equal_to(s) - False - For this reason, explictely bounding precision is strongly recommended:: + Another option (which is actually recommended) is to provide an explicit + bound on the precision:: + sage: s = b + c + 7^50 sage: a.is_equal_to(s, prec=20) True sage: a.is_equal_to(s, prec=100) False - """ cdef long halt if self is right: @@ -743,14 +745,21 @@ cdef class LazyElement(pAdicGenericElement): if self._valuation >= maxordp and right._valuation >= maxordp: return True if prec is None: + if quiet is None: + quiet = False prec = min(self._precbound, right._precbound) else: + if quiet is None: + quiet = True prec = Integer(prec) if prec < maxordp: return self._is_equal(right, prec, True) prec = min(self._valuation + self._precrel, right._valuation + right._precrel) halt = min(self._parent.halting_prec(), maxordp) - return self._is_equal(right, max(prec, halt), True) + eq = self._is_equal(right, max(prec, halt), True) + if not quiet and eq: + raise PrecisionError("unable to decide equality; try to bound precision") + return eq def __eq__(self, other): r""" @@ -775,6 +784,10 @@ cdef class LazyElement(pAdicGenericElement): False sage: x == s False + + .. SEEALSO:: + + :meth:`is_equal_to` """ if not have_same_parent(self, other): try: @@ -782,12 +795,11 @@ cdef class LazyElement(pAdicGenericElement): except TypeError: return False return a == b - return self.is_equal_to(other) + return self.is_equal_to(other, quiet=True) def __nonzero__(self): r""" - Return ``True`` if this element is indistinguishable from zero - at the current stage of computation. + Return ``True`` if this element is indistinguishable from zero. TESTS:: @@ -797,20 +809,23 @@ cdef class LazyElement(pAdicGenericElement): True In the next example, only `40` digits (which is the default halting - precision) are computed so `x` is indistinguishable from `0` at this - stage:: + precision) are computed so `x` is considered as indistinguishable from + `0`:: sage: x = R(5^41) sage: bool(x) False """ - cdef long halt + cdef int error = 0 + cdef long prec, halt if self._precrel: return True prec = self._precbound if prec >= maxordp: halt = min(self._parent.halting_prec(), maxordp) - self._jump_c(max(self._valuation + self._precrel, halt)) + prec = max(self._valuation + self._precrel, halt) + while not error and self._precrel == 0 and self._valuation < prec: + error = self._next_c() return bool(self._precrel) cpdef bint _is_exact_zero(self) except -1: @@ -844,7 +859,8 @@ cdef class LazyElement(pAdicGenericElement): sage: R = ZpL(5, print_mode="digits") sage: a = R(20/21) - Computations have not started yet, + Computations have not started yet; hence we are not able + to distinguish `a` from zero so far:: sage: a._is_inexact_zero() True @@ -968,6 +984,56 @@ cdef class LazyElement(pAdicGenericElement): return Infinity return Integer(self._precrel) + def precision_current(self): + r""" + Return the internal absolute precision we know this lazy `p`-adic + number at the current stage of the computation. + + EXAMPLES:: + + sage: R = ZpL(5, prec=10) + sage: x = R(20/21) + sage: y = R(21/22) + sage: z = x + y + + When the elements are just defined, the computation has not started:: + + sage: x.precision_current() + 0 + sage: y.precision_current() + 0 + sage: z.precision_current() + 0 + + When elements are printed, the relevant digits are computed:: + + sage: x + 4*5 + 4*5^2 + 5^4 + 4*5^6 + 3*5^7 + 4*5^8 + ... + sage: x.precision_current() + 10 + + If we ask for more digits of `z`, the current precision of `z` + increases accordingly:: + + sage: z[:15] + 3 + 2*5 + 2*5^3 + 5^4 + 2*5^5 + 2*5^6 + 4*5^7 + 5^9 + 3*5^10 + 3*5^11 + 4*5^12 + 4*5^13 + 4*5^14 + O(5^15) + sage: z.precision_current() + 15 + + and similarly the current precision of `x` and `y` increases because + those digits are needed to carry out the computation:: + + sage: x.precision_current() + 15 + sage: y.precision_current() + 15 + """ + if self._valuation <= -maxordp: + return -Infinity + if self._valuation >= maxordp: + return Infinity + return Integer(self._valuation + self._precrel) + def at_precision_absolute(self, prec=None, permissive=None): r""" Return this element bounded at the given precision. @@ -1238,11 +1304,20 @@ cdef class LazyElement(pAdicGenericElement): ans._init_jump() return ans - cdef long valuation_c(self): + cdef long valuation_c(self, long halt=-maxordp): r""" Return the best lower bound we have on the valuation of this element at the current stage of the computation. + + INPUT: + + - ``halt`` -- an integer; if given, allow to increase the + absolute precision on this element up to ``halt`` in order + to get a better lower bound. """ + cdef int error = 0 + while not error and self._precrel == 0 and self._valuation < halt: + error = self._next_c() return self._valuation def valuation(self, halt=True): @@ -1330,10 +1405,10 @@ cdef class LazyElement(pAdicGenericElement): halt = maxordp else: halt = min(maxordp, halt) - self._jump_relative_c(1, halt) + cdef val = self.valuation_c(halt) if self._precbound >= maxordp and self._precrel == 0: raise PrecisionError("cannot determine the valuation; try to increase the halting precision") - return Integer(self._valuation) + return Integer(val) def unit_part(self, halt=True): r""" @@ -1913,9 +1988,9 @@ cdef class LazyElement(pAdicGenericElement): TESTS:: sage: R = ZpL(5) - sage: R(0)._test_nonzero_equal() - sage: R(5^30)._test_nonzero_equal() - sage: R.unknown()._test_nonzero_equal() + sage: #R(0)._test_nonzero_equal() + sage: #R(5^30)._test_nonzero_equal() + sage: #R.unknown()._test_nonzero_equal() """ tester = self._tester(**options) try: @@ -2331,6 +2406,8 @@ cdef class LazyElement_bound(LazyElement): An error code (see :meth:`LazyElement._next_c` for details). """ cdef LazyElement x = self._x + if self._valuation + self._precrel >= self._precbound: + return ERROR_PRECISION cdef int error = x._next_c() self._precbound = min(self._precbound, x._precbound) self._valuation = min(x._valuation, self._precbound) @@ -2440,6 +2517,8 @@ cdef class LazyElement_value(LazyElementWithDigits): The algorithm is not optimal (quadratic in the precision), but it sounds okay... """ + if (self._precbound is not None) and (self._valuation + self._precrel >= self._precbound): + return ERROR_PRECISION element_reduce_digit(self._digits, self._precrel, self.prime_pow) if self._precrel == 0 and digit_is_zero(self._getdigit_relative(0)): self._valuation += 1 @@ -2551,6 +2630,7 @@ cdef class LazyElement_random(LazyElementWithDigits): An error code (see :meth:`LazyElement._next_c` for details). """ cdef cdigit r + digit_init(r) digit_random(r, self.prime_pow, self._generator) if self._precrel == 0 and digit_is_zero(r): self._valuation += 1 @@ -2769,7 +2849,8 @@ cdef class LazyElement_add(LazyElementWithDigits): LazyElement.__init__(self, parent) self._x = x self._y = y - self._valuation = min(x._valuation, y._valuation) + cdef halt = self._parent.default_prec() + self._valuation = min(x.valuation_c(0), y.valuation_c(0)) self._precbound = min(x._precbound, y._precbound) self._init_jump() @@ -2876,7 +2957,7 @@ cdef class LazyElement_sub(LazyElementWithDigits): LazyElement.__init__(self, parent) self._x = x self._y = y - self._valuation = min(x._valuation, y._valuation) + self._valuation = min(x.valuation_c(0), y.valuation_c(0)) self._precbound = min(x._precbound, y._precbound) self._init_jump() @@ -3006,7 +3087,8 @@ cdef class LazyElement_mul(LazyElementWithDigits): LazyElement.__init__(self, parent) self._x = x self._y = y - self._valuation = min(maxordp, x._valuation + y._valuation) + cdef halt = self._parent.default_prec() + self._valuation = min(maxordp, x.valuation_c(0) + y.valuation_c(0)) if x._precbound < maxordp: y._jump_relative_c(1, y._valuation + self._parent.default_prec()) self._precbound = min(self._precbound, y._valuation + x._precbound) diff --git a/src/sage/rings/padics/lazy_template_header.pxi b/src/sage/rings/padics/lazy_template_header.pxi index 7e52682f9ca..b9e275bfeb4 100644 --- a/src/sage/rings/padics/lazy_template_header.pxi +++ b/src/sage/rings/padics/lazy_template_header.pxi @@ -42,6 +42,8 @@ cdef class LazyElement(pAdicGenericElement): cdef int _jump_c(self, long prec) cdef int _jump_relative_c(self, long prec, long halt) cdef int _next_c(self) + + cdef long valuation_c(self, long halt=*) cdef bint _is_equal(self, LazyElement right, long prec, bint permissive) except -1 cdef class LazyElement_abandon(LazyElement): From a4a72a532adac9d23a57af9c7115023abde73306 Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Sun, 4 Apr 2021 20:45:29 +0200 Subject: [PATCH 276/406] fix conversion and other bugs --- src/sage/rings/padics/common_conversion.pyx | 8 +++- src/sage/rings/padics/lazy_template.pxi | 45 +++++++++++++-------- 2 files changed, 34 insertions(+), 19 deletions(-) diff --git a/src/sage/rings/padics/common_conversion.pyx b/src/sage/rings/padics/common_conversion.pyx index a256e554f61..594516e9cef 100644 --- a/src/sage/rings/padics/common_conversion.pyx +++ b/src/sage/rings/padics/common_conversion.pyx @@ -129,7 +129,7 @@ cdef long get_ordp(x, PowComputer_class prime_pow) except? -10000: # We don't want to multiply by e again. return k elif isinstance(x, pAdicGenericElement): - k = (x).valuation_c() + k = (x).valuation() if not (x)._is_base_elt(prime_pow.prime): # We have to be careful with overflow ratio = e // x.parent().absolute_e() @@ -209,6 +209,8 @@ cdef long get_preccap(x, PowComputer_class prime_pow) except? -10000: if (x)._is_exact_zero(): return maxordp prec = x.precision_absolute() + if prec is infinity: + return maxordp k = mpz_get_si(prec.value) if not (x)._is_base_elt(prime_pow.prime): # since x lives in a subfield, the ramification index of x's parent will divide e. @@ -405,7 +407,9 @@ cdef inline int cconv_shared(mpz_t out, x, long prec, long valshift, PowComputer x = Integer(x) elif isinstance(x, pari_gen): x = x.sage() - if isinstance(x, pAdicGenericElement) or sage.rings.finite_rings.integer_mod.is_IntegerMod(x): + if isinstance(x, pAdicGenericElement) and x.parent().is_lazy(): + x = x.lift(valshift + prec) + elif isinstance(x, pAdicGenericElement) or sage.rings.finite_rings.integer_mod.is_IntegerMod(x): x = x.lift() if isinstance(x, Integer): if valshift > 0: diff --git a/src/sage/rings/padics/lazy_template.pxi b/src/sage/rings/padics/lazy_template.pxi index 2bafff19031..80e3b8d3938 100644 --- a/src/sage/rings/padics/lazy_template.pxi +++ b/src/sage/rings/padics/lazy_template.pxi @@ -689,7 +689,7 @@ cdef class LazyElement(pAdicGenericElement): return self._is_equal(right, min(prec, maxordp), False) @coerce_binop - def is_equal_to(self, LazyElement right, prec=None, quiet=None): + def is_equal_to(self, LazyElement right, prec=None, secure=None): r""" Compare this element with ``right``. @@ -701,9 +701,9 @@ cdef class LazyElement(pAdicGenericElement): given, compare the two elements at this precision; otherwise use the default halting precision of the parent - - ``quiet`` -- a boolean (default: ``True`` if ``prec`` is given, - ``False`` otherwise); when the elements cannot be distingiushed - at the given precision, raise an error if ``quiet`` is ``False``, + - ``secure`` -- a boolean (default: ``False`` if ``prec`` is given, + ``True`` otherwise); when the elements cannot be distingiushed + at the given precision, raise an error if ``secure`` is ``True``, return ``True`` otherwise. EXAMPLES:: @@ -725,9 +725,9 @@ cdef class LazyElement(pAdicGenericElement): ... PrecisionError: unable to decide equality; try to bound precision - You can get around this behaviour by passing in ``quiet=True``:: + You can get around this behaviour by passing ``secure=False``:: - sage: a.is_equal_to(b + c, quiet=True) + sage: a.is_equal_to(b + c, secure=False) True Another option (which is actually recommended) is to provide an explicit @@ -745,19 +745,19 @@ cdef class LazyElement(pAdicGenericElement): if self._valuation >= maxordp and right._valuation >= maxordp: return True if prec is None: - if quiet is None: - quiet = False + if secure is None: + secure = True prec = min(self._precbound, right._precbound) else: - if quiet is None: - quiet = True + if secure is None: + secure = False prec = Integer(prec) if prec < maxordp: return self._is_equal(right, prec, True) prec = min(self._valuation + self._precrel, right._valuation + right._precrel) halt = min(self._parent.halting_prec(), maxordp) eq = self._is_equal(right, max(prec, halt), True) - if not quiet and eq: + if secure and eq: raise PrecisionError("unable to decide equality; try to bound precision") return eq @@ -795,7 +795,7 @@ cdef class LazyElement(pAdicGenericElement): except TypeError: return False return a == b - return self.is_equal_to(other, quiet=True) + return self.is_equal_to(other, secure=False) def __nonzero__(self): r""" @@ -1231,7 +1231,7 @@ cdef class LazyElement(pAdicGenericElement): if permissive is None: permissive = False raise_error(error, permissive) - return element_class_bound((self)._parent, self, self._valuation + prec) + return element_class_bound((self)._parent, self, self._valuation + prec) def lift_to_precision(self, absprec=None): """ @@ -1320,7 +1320,7 @@ cdef class LazyElement(pAdicGenericElement): error = self._next_c() return self._valuation - def valuation(self, halt=True): + def valuation(self, halt=True, secure=False): r""" Return the valuation of this element. @@ -1332,6 +1332,11 @@ cdef class LazyElement(pAdicGenericElement): if ``True``, the default halting precision of the parent is used; if ``False``, the computation is never abandonned + - ``secure`` -- a boolean (default: ``False``); when the valuation + cannot be determined for sure, raise an error if ``secure`` is + ``True``, return the best known lower bound on the valuation + otherwise. + EXAMPLES:: sage: R = ZpL(5, prec=10, halt=10) @@ -1364,9 +1369,14 @@ cdef class LazyElement(pAdicGenericElement): 0 + ... Without any help, Sage does not run the computation far enough to determine - the valuation and an error is raised:: + the valuation and outputs only a lower bound:: sage: z.valuation() + 10 + + With ``secure=True``, an error is raised:: + + sage: z.valuation(secure=True) Traceback (most recent call last): ... PrecisionError: cannot determine the valuation; try to increase the halting precision @@ -1406,7 +1416,7 @@ cdef class LazyElement(pAdicGenericElement): else: halt = min(maxordp, halt) cdef val = self.valuation_c(halt) - if self._precbound >= maxordp and self._precrel == 0: + if secure and self._precbound >= maxordp and self._precrel == 0: raise PrecisionError("cannot determine the valuation; try to increase the halting precision") return Integer(val) @@ -3416,7 +3426,8 @@ cdef class LazyElement_div(LazyElementWithDigits): return error if definition._valuation > val: self._valuation = min(self._precbound, definition._valuation - self._denom._valuation) - self._precbound = min(self._precbound, definition._precbound - self._denom._valuation) + if definition._precbound < maxordp: + self._precbound = min(self._precbound, definition._precbound - self._denom._valuation) else: digit = definition._getdigit_relative(self._precrel) element_set_digit(self._digits, digit, self._precrel) From b21c36d21cac890236e1f6865a32ccf18b184168 Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Sun, 4 Apr 2021 21:49:41 +0200 Subject: [PATCH 277/406] use fmpz_sqrtmod --- src/sage/libs/linkages/padics/lazy/flint.pxi | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/src/sage/libs/linkages/padics/lazy/flint.pxi b/src/sage/libs/linkages/padics/lazy/flint.pxi index cfa224d34ff..5104b39220a 100644 --- a/src/sage/libs/linkages/padics/lazy/flint.pxi +++ b/src/sage/libs/linkages/padics/lazy/flint.pxi @@ -300,16 +300,7 @@ cdef bint digit_sqrt(fmpz_t ans, fmpz_t x, PowComputer_flint prime_pow): - ``a`` -- a ``cdigit``, the digit to square root - ``prime_pow`` -- the PowComputer for the ring """ - # Need to do something better - cdef Integer zx = digit_get_sage(x) - cdef Integer zp = digit_get_sage(prime_pow.fprime) - try: - k = GF(zp) - zans = Integer(k(zx).sqrt(extend=False)) - except ValueError: - return 1 - digit_set_sage(ans, zans) - return 0 + return not fmpz_sqrtmod(ans, x, prime_pow.fprime) # Operations on elements (represented as series of digits) From 1942363cc0462a0417463561dd2d9750a9886b5d Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 4 Apr 2021 16:17:09 -0700 Subject: [PATCH 278/406] build/pkgs/pynac/spkg-install.in: Back to PYTHON=python3 --- build/pkgs/pynac/spkg-install.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/pkgs/pynac/spkg-install.in b/build/pkgs/pynac/spkg-install.in index 47ff112fd77..9fa0d99b2ed 100644 --- a/build/pkgs/pynac/spkg-install.in +++ b/build/pkgs/pynac/spkg-install.in @@ -11,7 +11,7 @@ build_pynac() { cd ${PYNACDIR} PKG_CONFIG_PATH=${SAGE_LOCAL}/lib/pkgconfig; export PKG_CONFIG_PATH - sdh_configure --disable-static --with-giac=no PYTHON=${PYTHON_FOR_VENV} + sdh_configure --disable-static --with-giac=no PYTHON=python3 sdh_make cd ${WORKDIR} } From 9dcded5e3188eec1c4f0d1a81016783f506708fd Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 4 Apr 2021 16:18:04 -0700 Subject: [PATCH 279/406] src/bin/sage: Only do the check for an incomplete installation when run from the source tree --- src/bin/sage | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/bin/sage b/src/bin/sage index eeff9452cde..21832848901 100755 --- a/src/bin/sage +++ b/src/bin/sage @@ -67,6 +67,7 @@ if [ -x "$0-config" ]; then fi if [ -f "$0-src-env-config" ]; then # Not installed script, present only in src/bin/ + SAGE_SRC_ENV_CONFIG=1 . "$0-src-env-config" >&2 fi if [ -z "$SAGE_VENV" -a -x "$0-venv-config" ]; then @@ -229,7 +230,7 @@ fi # Prepare for running Sage, either interactively or non-interactively. sage_setup() { # Check that we're not in a source tarball which hasn't been built yet (#13561). - if [ ! -z "$SAGE_LOCAL" ] && [ ! -x "$SAGE_LOCAL/bin/sage" ]; then + if [ "$SAGE_SRC_ENV_CONFIG" = 1 ] && [ ! -z "$SAGE_VENV" ] && [ ! -x "$SAGE_VENV/bin/sage" ]; then echo >&2 '************************************************************************' echo >&2 'It seems that you are attempting to run Sage from an unpacked source' echo >&2 'tarball, but you have not compiled it yet (or maybe the build has not' @@ -259,7 +260,7 @@ sage_setup() { maybe_sage_location() { if [ -n "$SAGE_LOCAL" -a -w "$SAGE_LOCAL" ]; then - if [ -x "$SAGE_LOCAL/bin/python" ] && [ -x "$SAGE_LOCAL/bin/sage-location" ]; then + if [ -x "$SAGE_VENV/bin/python" ] && [ -x "$SAGE_VENV/bin/sage-location" ]; then sage-location || exit $? fi fi From 6e0c19ec5bf263ea75dffa0d5d7332daf37e6d6e Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 5 Apr 2021 20:11:31 -0700 Subject: [PATCH 280/406] sage.misc.package.installed_packages: After SAGE_SPKG_INST, also go through SAGE_VENV_SPKG_INST --- src/sage/env.py | 1 + src/sage/misc/package.py | 23 +++++++++++++++-------- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/src/sage/env.py b/src/sage/env.py index 2908f5d04fa..c2221ea91e9 100644 --- a/src/sage/env.py +++ b/src/sage/env.py @@ -169,6 +169,7 @@ def var(key: str, *fallbacks: Optional[str], force: bool = False) -> Optional[st SAGE_VENV = var("SAGE_VENV", os.path.abspath(sys.prefix)) SAGE_LIB = var("SAGE_LIB", os.path.dirname(os.path.dirname(sage.__file__))) SAGE_EXTCODE = var("SAGE_EXTCODE", join(SAGE_LIB, "sage", "ext_data")) +SAGE_VENV_SPKG_INST = var("SAGE_VENV_SPKG_INST", join(SAGE_VENV, "var", "lib", "sage", "installed")) # prefix hierarchy where non-Python packages are installed SAGE_LOCAL = var("SAGE_LOCAL", SAGE_VENV) diff --git a/src/sage/misc/package.py b/src/sage/misc/package.py index 9e335a09bc0..1af4f1e4470 100644 --- a/src/sage/misc/package.py +++ b/src/sage/misc/package.py @@ -47,6 +47,7 @@ import os import subprocess import sys +from pathlib import Path from urllib.request import urlopen from urllib.error import URLError @@ -344,14 +345,20 @@ def installed_packages(exclude_pip=True): if not exclude_pip: installed.update(pip_installed_packages(normalization='spkg')) # Sage packages should override pip packages (Trac #23997) - SAGE_SPKG_INST = sage.env.SAGE_SPKG_INST - if SAGE_SPKG_INST: - try: - lp = os.listdir(SAGE_SPKG_INST) - installed.update(pkgname_split(pkgname) for pkgname in lp - if not pkgname.startswith('.')) - except FileNotFoundError: - pass + + last_inst_dir = None + for inst_dir in (sage.env.SAGE_SPKG_INST, sage.env.SAGE_VENV_SPKG_INST): + if inst_dir: + inst_dir = Path(inst_dir).resolve() + if inst_dir == last_inst_dir: + continue + try: + lp = os.listdir(inst_dir) + installed.update(pkgname_split(pkgname) for pkgname in lp + if not pkgname.startswith('.')) + last_inst_dir = inst_dir + except FileNotFoundError: + pass return installed From f0fb082bce2c0d023aaccbe185f311b708c7f072 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 6 Apr 2021 17:17:53 -0700 Subject: [PATCH 281/406] sage.misc.package.installed_packages: Refactor through new generator _spkg_inst_dirs --- src/sage/misc/package.py | 42 +++++++++++++++++++++++++++------------- 1 file changed, 29 insertions(+), 13 deletions(-) diff --git a/src/sage/misc/package.py b/src/sage/misc/package.py index 1af4f1e4470..5856d5347e5 100644 --- a/src/sage/misc/package.py +++ b/src/sage/misc/package.py @@ -318,6 +318,28 @@ def list_packages(*pkg_types, **opts): return pkgs +def _spkg_inst_dirs(): + """ + Generator for the installation manifest directories as resolved paths. + + It yields first ``SAGE_SPKG_INST``, then ``SAGE_VENV_SPKG_INST``, + if defined; but it both resolve to the same directory, it only yields + one element. + + EXAMPLES:: + + sage: from sage.misc.package import _spkg_inst_dirs + sage: list(_spkg_inst_dirs()) + [...] + + """ + last_inst_dir = None + for inst_dir in (sage.env.SAGE_SPKG_INST, sage.env.SAGE_VENV_SPKG_INST): + if inst_dir: + inst_dir = Path(inst_dir).resolve() + if inst_dir.is_dir() and inst_dir != last_inst_dir: + yield inst_dir + last_inst_dir = inst_dir def installed_packages(exclude_pip=True): """ @@ -346,19 +368,13 @@ def installed_packages(exclude_pip=True): installed.update(pip_installed_packages(normalization='spkg')) # Sage packages should override pip packages (Trac #23997) - last_inst_dir = None - for inst_dir in (sage.env.SAGE_SPKG_INST, sage.env.SAGE_VENV_SPKG_INST): - if inst_dir: - inst_dir = Path(inst_dir).resolve() - if inst_dir == last_inst_dir: - continue - try: - lp = os.listdir(inst_dir) - installed.update(pkgname_split(pkgname) for pkgname in lp - if not pkgname.startswith('.')) - last_inst_dir = inst_dir - except FileNotFoundError: - pass + for inst_dir in _spkg_inst_dirs(): + try: + lp = os.listdir(inst_dir) + installed.update(pkgname_split(pkgname) for pkgname in lp + if not pkgname.startswith('.')) + except FileNotFoundError: + pass return installed From d68e86144b4a37600654899d2ab655ae55fb4378 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 6 Apr 2021 17:19:30 -0700 Subject: [PATCH 282/406] sage.misc.package.package_manifest: Go through all _spkg_inst_dirs() --- src/sage/misc/package.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/sage/misc/package.py b/src/sage/misc/package.py index 5856d5347e5..51d0ae61bf7 100644 --- a/src/sage/misc/package.py +++ b/src/sage/misc/package.py @@ -581,12 +581,15 @@ def package_manifest(package): KeyError: 'dummy-package' """ version = installed_packages()[package] - stamp_file = os.path.join(sage.env.SAGE_SPKG_INST, - '{}-{}'.format(package, version)) - with open(stamp_file) as f: - spkg_meta = json.load(f) - return spkg_meta - + for inst_dir in _spkg_inst_dirs(): + stamp_file = os.path.join(inst_dir, + '{}-{}'.format(package, version)) + try: + with open(stamp_file) as f: + return json.load(f) + except FileNotFoundError: + pass + raise RuntimeError('package manifest directory changed at runtime') class PackageNotFoundError(RuntimeError): """ From 7f49e18bbeb2113d4ce5a304af11c3e19f9515fb Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Wed, 7 Apr 2021 11:16:12 +0200 Subject: [PATCH 283/406] add keyword secure in the parent --- src/sage/rings/padics/common_conversion.pyx | 4 +- src/sage/rings/padics/factory.py | 69 +++++++++++++++++---- src/sage/rings/padics/generic_nodes.py | 31 ++++++++- src/sage/rings/padics/lazy_template.pxi | 36 ++++++++--- src/sage/rings/padics/padic_base_leaves.py | 4 +- 5 files changed, 118 insertions(+), 26 deletions(-) diff --git a/src/sage/rings/padics/common_conversion.pyx b/src/sage/rings/padics/common_conversion.pyx index 594516e9cef..a2747006e57 100644 --- a/src/sage/rings/padics/common_conversion.pyx +++ b/src/sage/rings/padics/common_conversion.pyx @@ -129,7 +129,9 @@ cdef long get_ordp(x, PowComputer_class prime_pow) except? -10000: # We don't want to multiply by e again. return k elif isinstance(x, pAdicGenericElement): - k = (x).valuation() + if x.parent().is_lazy(): + return x.valuation() + k = (x).valuation_c() if not (x)._is_base_elt(prime_pow.prime): # We have to be careful with overflow ratio = e // x.parent().absolute_e() diff --git a/src/sage/rings/padics/factory.py b/src/sage/rings/padics/factory.py index e94cc2296b2..1ea4e6d60e0 100644 --- a/src/sage/rings/padics/factory.py +++ b/src/sage/rings/padics/factory.py @@ -218,17 +218,24 @@ def get_key_base(p, prec, type, print_mode, names, ram_name, print_pos, print_se prec = (relative_cap, absolute_cap) elif type == 'lazy': default_prec = halting_prec = None - if prec is not None: - try: + secure = False + if isinstance(prec, (list, tuple)): + if len(prec) == 1: + default_prec = prec + elif len(prec) == 2: default_prec, halting_prec = prec - except (ValueError, TypeError): - default_prec = halting_prec = prec + else: + default_prec = prec[0] + halting_prec = prec[1] + secure = prec[2] + else: + default_prec = prec if default_prec is None: default_prec = DEFAULT_PREC if halting_prec is None: halting_prec = 2 * default_prec halting_prec = max(default_prec, halting_prec) - prec = (default_prec, halting_prec) + prec = (default_prec, halting_prec, secure) else: if prec is not None: prec = Integer(prec) @@ -365,7 +372,7 @@ class Qp_class(UniqueFactory): - ``print_max_terms`` -- integer (default ``None``) The maximum number of terms shown. See PRINTING below. - - ``show_prec`` -- a boolean or a string (default ``None``) Specify how + - ``show_prec`` -- a boolean or a string (default ``None``) Specify how the precision is printed. See PRINTING below. - ``check`` -- bool (default ``True``) whether to check if `p` is prime. @@ -1443,7 +1450,7 @@ def QpLF(p, prec = None, *args, **kwds): """ return Qp(p, prec, 'lattice-float', *args, **kwds) -def QpL(p, prec=None, *args, **kwds): +def QpL(p, prec=None, halt=None, secure=False, *args, **kwds): r""" A shortcut function to create lazy `p`-adic fields. @@ -1455,7 +1462,7 @@ def QpL(p, prec=None, *args, **kwds): sage: R 2-adic Field with lazy precision """ - return Qp(p, prec, 'lazy', *args, **kwds) + return Qp(p, (prec, halt, secure), 'lazy', *args, **kwds) ####################################################################################################### # @@ -1478,6 +1485,9 @@ class Zp_class(UniqueFactory): ring. In the lattice capped case, ``prec`` can either be a pair (``relative_cap``, ``absolute_cap``) or an integer (understood at relative cap). + In the lazy case, ``prec`` can be either a + pair (``default_prec``, ``halting_prec``) or an integer + (understood at default precision). Except for the fixed modulus and floating point cases, individual elements keep track of their own precision. See TYPES and PRECISION below. @@ -2956,11 +2966,24 @@ def ZpLF(p, prec=None, *args, **kwds): """ return Zp(p, prec, 'lattice-float', *args, **kwds) -def ZpL(p, prec=None, halt=None, *args, **kwds): +def ZpL(p, prec=None, halt=None, secure=False, *args, **kwds): r""" A shortcut function to create lazy `p`-adic rings. - See documentation for :func:`Zp` for a description of the input parameters. + INPUT: + + - ``prec`` -- an integer (default: ``20``), the default + precision + + - ``halt`` -- an integer (default: twice ``prec``), the + halting precision + + - ``secure`` -- a boolean (default: ``False``); if ``False``, + consider indistinguishable elements at the working precision + as equal; otherwise, raise an error. + + See documentation for :func:`Zp` for a description of the other + input parameters. A SHORT INTRODUCTION TO LAZY `p`-ADICS: @@ -3055,7 +3078,10 @@ def ZpL(p, prec=None, halt=None, *args, **kwds): Checking equalities between lazy `p`-adics is a bit subtle and can sometimes be puzzling at first glance. - Basically, elements are compared at the default halting precision:: + + When the parent is created with ``secure=False`` (which is the + default), elements are compared at the current precision, or at the + default halting precision if it is higher:: sage: a == b False @@ -3065,6 +3091,8 @@ def ZpL(p, prec=None, halt=None, *args, **kwds): sage: a == sqrt(a)^2 + 5^50 True + In the above example, the halting precision is `40`; it is the + reason why a congruence modulo `5^50` is considered as an equality. However, if both sides of the equalities have been previously computed with more digits, those digits are taken into account. Hence comparing two elements at different times can produce @@ -3080,6 +3108,23 @@ def ZpL(p, prec=None, halt=None, *args, **kwds): sage: a == aa False + This annoying situation, where the output of `a == aa` may change + depending on previous computations, cannot occur when the parent is + created with ``secure=True``. + Indeed, in this case, if the equality cannot be decided, an error + is raised:: + + sage: S = ZpL(5, secure=True) + sage: u = S.random_element() + sage: uu = u + 5^50 + sage: u == uu + Traceback (most recent call last): + ... + PrecisionError: unable to decide equality; try to bound precision + + sage: u[:60] == uu + False + .. RUBRIC:: Self-referent numbers A quite interesting feature with lazy `p`-adics is the possibility to @@ -3138,7 +3183,7 @@ def ZpL(p, prec=None, halt=None, *args, **kwds): sage: w ...30212422041102444403 """ - return Zp(p, (prec, halt), 'lazy', *args, **kwds) + return Zp(p, (prec, halt, secure), 'lazy', *args, **kwds) ####################################################################################################### diff --git a/src/sage/rings/padics/generic_nodes.py b/src/sage/rings/padics/generic_nodes.py index 1b66634e544..926a50e2613 100644 --- a/src/sage/rings/padics/generic_nodes.py +++ b/src/sage/rings/padics/generic_nodes.py @@ -756,7 +756,7 @@ def _prec_type(self): sage: ZpL(5)._prec_type() 'lazy' """ - return "lazy" + return 'lazy' def is_lazy(self): r""" @@ -773,6 +773,35 @@ def is_lazy(self): """ return True + def is_secure(self): + r""" + Return ``False`` if this `p`-adic lazy ring is not secure + (i.e. if indistinguishable elements at the working precision + are considered as equal); ``True`` otherwise (in which case, + an error is raised when equality cannot be decided). + + EXAMPLES:: + + sage: R = ZpL(5) + sage: R.is_secure() + False + sage: x = R(20/21) + sage: y = x + 5^50 + sage: x == y + True + + sage: S = ZpL(5, secure=True) + sage: S.is_secure() + True + sage: x = S(20/21) + sage: y = x + 5^50 + sage: x == y + Traceback (most recent call last): + ... + PrecisionError: unable to decide equality; try to bound precision + """ + return self._secure + def default_prec(self): r""" Return the default precision of this lazy `p`-adic ring. diff --git a/src/sage/rings/padics/lazy_template.pxi b/src/sage/rings/padics/lazy_template.pxi index 80e3b8d3938..8f361421add 100644 --- a/src/sage/rings/padics/lazy_template.pxi +++ b/src/sage/rings/padics/lazy_template.pxi @@ -763,9 +763,7 @@ cdef class LazyElement(pAdicGenericElement): def __eq__(self, other): r""" - Return ``True`` of this element is equal to ``other`` at the - maximum between the working precision and the default halting - precision of the parent. + Return ``True`` of this element is equal to ``other``. TESTS:: @@ -777,9 +775,23 @@ cdef class LazyElement(pAdicGenericElement): sage: x == y + z True + We illustrate the effect of the keyword ``secure``:: + + sage: R.is_secure() + False sage: s = y + z + 5^50 sage: x == s True + + sage: S = ZpL(5, secure=True) + sage: S(x) == S(s) + Traceback (most recent call last): + ... + PrecisionError: unable to decide equality; try to bound precision + + Note that, when ``secure=False``, once more digits have been + computed, the answer can change:: + sage: x[:100] == s False sage: x == s @@ -795,7 +807,7 @@ cdef class LazyElement(pAdicGenericElement): except TypeError: return False return a == b - return self.is_equal_to(other, secure=False) + return self.is_equal_to(other, secure=self._parent.is_secure()) def __nonzero__(self): r""" @@ -1320,7 +1332,7 @@ cdef class LazyElement(pAdicGenericElement): error = self._next_c() return self._valuation - def valuation(self, halt=True, secure=False): + def valuation(self, halt=True, secure=None): r""" Return the valuation of this element. @@ -1332,10 +1344,10 @@ cdef class LazyElement(pAdicGenericElement): if ``True``, the default halting precision of the parent is used; if ``False``, the computation is never abandonned - - ``secure`` -- a boolean (default: ``False``); when the valuation - cannot be determined for sure, raise an error if ``secure`` is - ``True``, return the best known lower bound on the valuation - otherwise. + - ``secure`` -- a boolean (default: the value given at the creation + of the parent); when the valuation cannot be determined for sure, + raise an error if ``secure`` is ``True``, return the best known + lower bound on the valuation otherwise. EXAMPLES:: @@ -1384,7 +1396,9 @@ cdef class LazyElement(pAdicGenericElement): By setting the argument ``halt``, one can force the computation to continue until a prescribed limit:: - sage: z.valuation(halt=20) # not enough to find the valuation + sage: z.valuation(halt=15) # not enough to find the correct valuation + 15 + sage: z.valuation(halt=20, secure=True) Traceback (most recent call last): ... PrecisionError: cannot determine the valuation; try to increase the halting precision @@ -1416,6 +1430,8 @@ cdef class LazyElement(pAdicGenericElement): else: halt = min(maxordp, halt) cdef val = self.valuation_c(halt) + if secure is None: + secure = self._parent.is_secure() if secure and self._precbound >= maxordp and self._precrel == 0: raise PrecisionError("cannot determine the valuation; try to increase the halting precision") return Integer(val) diff --git a/src/sage/rings/padics/padic_base_leaves.py b/src/sage/rings/padics/padic_base_leaves.py index 47d20581249..d13cc5710bc 100644 --- a/src/sage/rings/padics/padic_base_leaves.py +++ b/src/sage/rings/padics/padic_base_leaves.py @@ -1130,7 +1130,7 @@ def __init__(self, p, prec, print_mode, names): sage: TestSuite(R).run(skip=['_test_log', '_test_matrix_smith']) """ from sage.rings.padics import padic_lazy_element - self._default_prec, self._halting_prec = prec + self._default_prec, self._halting_prec, self._secure = prec pAdicRingBaseGeneric.__init__(self, p, self._default_prec, print_mode, names, padic_lazy_element.pAdicLazyElement) self._element_class_module = padic_lazy_element self._element_class_prefix = "pAdicLazyElement_" @@ -1165,7 +1165,7 @@ def __init__(self, p, prec, print_mode, names): sage: TestSuite(K).run(skip=['_test_log', '_test_matrix_smith']) """ from sage.rings.padics import padic_lazy_element - self._default_prec, self._halting_prec = prec + self._default_prec, self._halting_prec, self._secure = prec pAdicFieldBaseGeneric.__init__(self, p, self._default_prec, print_mode, names, padic_lazy_element.pAdicLazyElement) self._element_class_module = padic_lazy_element self._element_class_prefix = "pAdicLazyElement_" From 3d26006bae99898c192476bf31e799c2e9abb87e Mon Sep 17 00:00:00 2001 From: Kwankyu Lee Date: Fri, 9 Apr 2021 14:27:38 +0900 Subject: [PATCH 284/406] Sage output MathJax3 compatible htmls --- src/doc/de/tutorial/latex.rst | 14 +- src/doc/en/tutorial/latex.rst | 14 +- src/doc/fr/tutorial/latex.rst | 14 +- src/doc/ja/tutorial/latex.rst | 14 +- src/doc/pt/tutorial/latex.rst | 14 +- src/sage/interacts/test_jupyter.rst | 303 ++++++++++---------- src/sage/interfaces/mathematica.py | 4 +- src/sage/misc/html.py | 103 +++---- src/sage/misc/table.py | 91 +++--- src/sage/repl/rich_output/backend_base.py | 12 +- src/sage/repl/rich_output/output_browser.py | 14 +- src/sage/repl/rich_output/pretty_print.py | 4 +- 12 files changed, 294 insertions(+), 307 deletions(-) diff --git a/src/doc/de/tutorial/latex.rst b/src/doc/de/tutorial/latex.rst index 4596e125108..7cf8e650784 100644 --- a/src/doc/de/tutorial/latex.rst +++ b/src/doc/de/tutorial/latex.rst @@ -86,13 +86,13 @@ die dann MathJax verwendet. :: sage: var('z') z sage: mj(z^12) - + \[\newcommand{\Bold}[1]{\mathbf{#1}}z^{12}\] sage: mj(QQ) - + \[\newcommand{\Bold}[1]{\mathbf{#1}}\Bold{Q}\] sage: mj(ZZ['x']) - + \[\newcommand{\Bold}[1]{\mathbf{#1}}\Bold{Z}[x]\] sage: mj(integrate(z^4, z)) - + \[\newcommand{\Bold}[1]{\mathbf{#1}}\frac{1}{5} \, z^{5}\] Grundlegende Nutzung ==================== @@ -206,10 +206,10 @@ integriert ist. :: sage: from sage.misc.html import MathJax sage: mj=MathJax() sage: mj(QQ) - + \[\newcommand{\Bold}[1]{\mathbf{#1}}\Bold{Q}\] sage: latex.blackboard_bold(True) sage: mj(QQ) - + \[\newcommand{\Bold}[1]{\mathbb{#1}}\Bold{Q}\] sage: latex.blackboard_bold(False) Dank der Erweiterbarkeit von TeX können Sie selbst Makros und Pakete @@ -228,7 +228,7 @@ MathJax als TeX-Schnipsel interpretiert werden. :: sage: from sage.misc.html import MathJax sage: mj=MathJax() sage: mj(x+y) - + \[\newcommand{\Bold}[1]{\mathbf{#1}}\newcommand{\foo}{bar}x + y\] Zusätzliche Makros, die so hinzugefügt wurden, werden auch vom systemweiten TeX genutzt, wenn MathJax an seine Grenzen gestoßen ist. diff --git a/src/doc/en/tutorial/latex.rst b/src/doc/en/tutorial/latex.rst index eba1c6f52ad..0f2e7e47cc1 100644 --- a/src/doc/en/tutorial/latex.rst +++ b/src/doc/en/tutorial/latex.rst @@ -83,13 +83,13 @@ LaTeX representation and then wraps it in HTML that invokes the CSS sage: var('z') z sage: mj(z^12) - + \[\newcommand{\Bold}[1]{\mathbf{#1}}z^{12}\] sage: mj(QQ) - + \[\newcommand{\Bold}[1]{\mathbf{#1}}\Bold{Q}\] sage: mj(ZZ['x']) - + \[\newcommand{\Bold}[1]{\mathbf{#1}}\Bold{Z}[x]\] sage: mj(integrate(z^4, z)) - + \[\newcommand{\Bold}[1]{\mathbf{#1}}\frac{1}{5} \, z^{5}\] Basic Use ========= @@ -194,10 +194,10 @@ done in written work. This is accomplished by redefining the sage: from sage.misc.html import MathJax sage: mj=MathJax() sage: mj(QQ) - + \[\newcommand{\Bold}[1]{\mathbf{#1}}\Bold{Q}\] sage: latex.blackboard_bold(True) sage: mj(QQ) - + \[\newcommand{\Bold}[1]{\mathbb{#1}}\Bold{Q}\] sage: latex.blackboard_bold(False) It is possible to take advantage of the extensible nature of @@ -217,7 +217,7 @@ MathJax interprets a snippet of TeX in the notebook. :: sage: from sage.misc.html import MathJax sage: mj=MathJax() sage: mj(x+y) - + \[\newcommand{\Bold}[1]{\mathbf{#1}}\newcommand{\foo}{bar}x + y\] Additional macros added this way will also be used in the event that the system-wide version of TeX is called on diff --git a/src/doc/fr/tutorial/latex.rst b/src/doc/fr/tutorial/latex.rst index fdc8563a9f3..4ec9426700b 100644 --- a/src/doc/fr/tutorial/latex.rst +++ b/src/doc/fr/tutorial/latex.rst @@ -83,13 +83,13 @@ possède la classe CSS "math", laquelle indique de faire appel à MathJax. :: sage: var('z') z sage: mj(z^12) - + \[\newcommand{\Bold}[1]{\mathbf{#1}}z^{12}\] sage: mj(QQ) - + \[\newcommand{\Bold}[1]{\mathbf{#1}}\Bold{Q}\] sage: mj(ZZ['x']) - + \[\newcommand{\Bold}[1]{\mathbf{#1}}\Bold{Z}[x]\] sage: mj(integrate(z^4, z)) - + \[\newcommand{\Bold}[1]{\mathbf{#1}}\frac{1}{5} \, z^{5}\] Utilisation de base =================== @@ -197,10 +197,10 @@ mais la définition de la macro TeX ``\Bold{}`` fournie par Sage. :: sage: from sage.misc.html import MathJax sage: mj=MathJax() sage: mj(QQ) - + \[\newcommand{\Bold}[1]{\mathbf{#1}}\Bold{Q}\] sage: latex.blackboard_bold(True) sage: mj(QQ) - + \[\newcommand{\Bold}[1]{\mathbb{#1}}\Bold{Q}\] sage: latex.blackboard_bold(False) On peut aussi définir de nouvelles macros TeX ou charger des packages @@ -220,7 +220,7 @@ bloc-notes. :: sage: from sage.misc.html import MathJax sage: mj=MathJax() sage: mj(x+y) - + \[\newcommand{\Bold}[1]{\mathbf{#1}}\newcommand{\foo}{bar}x + y\] Ces macros supplémentaires sont disponibles aussi quand Sage appelle TeX pour compiler un fragment de document trop gros pour MathJax. C'est la fonction diff --git a/src/doc/ja/tutorial/latex.rst b/src/doc/ja/tutorial/latex.rst index 0d41b35e4d4..c737dbf0146 100644 --- a/src/doc/ja/tutorial/latex.rst +++ b/src/doc/ja/tutorial/latex.rst @@ -65,13 +65,13 @@ SageはLaTeXを多種多様な形で利用している. sage: var('z') z sage: mj(z^12) - + \[\newcommand{\Bold}[1]{\mathbf{#1}}z^{12}\] sage: mj(QQ) - + \[\newcommand{\Bold}[1]{\mathbf{#1}}\Bold{Q}\] sage: mj(ZZ[x]) - + \[\newcommand{\Bold}[1]{\mathbf{#1}}\Bold{Z}[x]\] sage: mj(integrate(z^4, z)) - + \[\newcommand{\Bold}[1]{\mathbf{#1}}\frac{1}{5} \, z^{5}\] @@ -169,10 +169,10 @@ LaTeXで使われるバックスラッシュには,Pythonの文字列内でエ sage: from sage.misc.html import MathJax sage: mj=MathJax() sage: mj(QQ) - + \[\newcommand{\Bold}[1]{\mathbf{#1}}\Bold{Q}\] sage: latex.blackboard_bold(True) sage: mj(QQ) - + \[\newcommand{\Bold}[1]{\mathbb{#1}}\Bold{Q}\] sage: latex.blackboard_bold(False) 新しいマクロやパッケージなどを追加して,TeXの高い拡張性を利用することができる. @@ -193,7 +193,7 @@ LaTeXで使われるバックスラッシュには,Pythonの文字列内でエ sage: from sage.misc.html import MathJax sage: mj=MathJax() sage: mj(x+y) - + \[\newcommand{\Bold}[1]{\mathbf{#1}}\newcommand{\foo}{bar}x + y\] 以上のようなやり方で追加したマクロは,MathJaxでは対応しきれない大規模な処理が発生してシステム上のTeXが呼ばれるような場合にも使われる. diff --git a/src/doc/pt/tutorial/latex.rst b/src/doc/pt/tutorial/latex.rst index f410d00e992..cba4fd4344e 100644 --- a/src/doc/pt/tutorial/latex.rst +++ b/src/doc/pt/tutorial/latex.rst @@ -91,13 +91,13 @@ evoca a classe "matemática" do CSS, a qual então emprega o MathJax. :: sage: var('z') z sage: js(z^12) - + \[\newcommand{\Bold}[1]{\mathbf{#1}}z^{12}\] sage: js(QQ) - + \[\newcommand{\Bold}[1]{\mathbf{#1}}\Bold{Q}\] sage: js(ZZ[x]) - + \[\newcommand{\Bold}[1]{\mathbf{#1}}\Bold{Z}[x]\] sage: js(integrate(z^4, z)) - + \[\newcommand{\Bold}[1]{\mathbf{#1}}\frac{1}{5} \, z^{5}\] Uso Básico ========== @@ -207,11 +207,11 @@ obtido redefinindo a macro ``\Bold{}`` que faz parte do Sage. :: sage: from sage.misc.html import MathJax sage: js = MathJax() sage: js(QQ) - + \[\newcommand{\Bold}[1]{\mathbf{#1}}\Bold{Q}\] sage: latex.blackboard_bold(True) sage: js(QQ) - + \[\newcommand{\Bold}[1]{\mathbb{#1}}\Bold{Q}\] sage: latex.blackboard_bold(False) É possível aproveitar os recursos do TeX adicionando novas funções @@ -231,7 +231,7 @@ trechos de códigos TeX no Notebook. :: sage: from sage.misc.html import MathJax sage: js = MathJax() sage: js(x+y) - + \[\newcommand{\Bold}[1]{\mathbf{#1}}\newcommand{\foo}{bar}x + y\] Macros adicionais usadas dessa forma serão também usadas eventualmente se a versão do TeX no seu sistema for usada para lidar com algo muito diff --git a/src/sage/interacts/test_jupyter.rst b/src/sage/interacts/test_jupyter.rst index 3f307d2c97c..12bda4a4615 100644 --- a/src/sage/interacts/test_jupyter.rst +++ b/src/sage/interacts/test_jupyter.rst @@ -39,107 +39,107 @@ Test all interacts from the Sage interact library:: sage: test(interacts.algebra.polar_prime_spiral) # long time Interactive function with 6 widgets - interval: IntRangeSlider(value=(1, 1000), description=u'range', max=4000, min=1, step=10) - show_factors: Checkbox(value=True, description=u'show_factors') - highlight_primes: Checkbox(value=True, description=u'highlight_primes') - show_curves: Checkbox(value=True, description=u'show_curves') - n: IntSlider(value=89, description=u'number $n$', max=200, min=1) - dpi: IntSlider(value=100, description=u'dpi', max=300, min=10, step=10) + interval: IntRangeSlider(value=(1, 1000), description='range', max=4000, min=1, step=10) + show_factors: Checkbox(value=True, description='show_factors') + highlight_primes: Checkbox(value=True, description='highlight_primes') + show_curves: Checkbox(value=True, description='show_curves') + n: IntSlider(value=89, description='number $n$', max=200, min=1) + dpi: IntSlider(value=100, description='dpi', max=300, min=10, step=10)

Polar Prime Spiral

- - Pink Curve: - Green Curve: + \(n = 89\) + Pink Curve: \(n^2 + 8\) + Green Curve: \(n^2 + n + -1\) sage: test(interacts.calculus.taylor_polynomial) Interactive function with 3 widgets - title: HTMLText(value=u'

Taylor polynomial

') - f: EvalText(value=u'e^(-x)*sin(x)', description=u'$f(x)=$', layout=Layout(max_width=u'81em')) - order: SelectionSlider(description=u'order', options=(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12), value=1) - - + title: HTMLText(value='

Taylor polynomial

') + f: EvalText(value='e^(-x)*sin(x)', description='$f(x)=$', layout=Layout(max_width='81em')) + order: SelectionSlider(description='order', options=(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12), value=1) + \(f(x)\;=\;e^{\left(-x\right)} \sin\left(x\right)\) + \(\hat{f}(x;0)\;=\;x+\mathcal{O}(x^{2})\) sage: test(interacts.calculus.definite_integral) Interactive function with 6 widgets - title: HTMLText(value=u'

Definite integral

') - f: EvalText(value=u'3*x', description=u'$f(x)=$', layout=Layout(max_width=u'81em')) - g: EvalText(value=u'x^2', description=u'$g(x)=$', layout=Layout(max_width=u'81em')) - interval: IntRangeSlider(value=(0, 3), description=u'Interval', max=10, min=-10) - x_range: IntRangeSlider(value=(0, 3), description=u'plot range (x)', max=10, min=-10) - selection: Dropdown(description=u'Select', index=2, options=('f', 'g', 'f and g', 'f - g'), value='f and g') -
+ title: HTMLText(value='

Definite integral

') + f: EvalText(value='3*x', description='$f(x)=$', layout=Layout(max_width='81em')) + g: EvalText(value='x^2', description='$g(x)=$', layout=Layout(max_width='81em')) + interval: IntRangeSlider(value=(0, 3), description='Interval', max=10, min=-10) + x_range: IntRangeSlider(value=(0, 3), description='plot range (x)', max=10, min=-10) + selection: Dropdown(description='Select', index=2, options=('f', 'g', 'f and g', 'f - g'), value='f and g') + \(\int_{0.00}^{3.00}(\color{Blue}{f(x)})\,\mathrm{d}x=\int_{0.00}^{3.00}(3 \, x)\,\mathrm{d}x=13.50\)
\(\int_{0.00}^{3.00}(\color{Green}{g(x)})\,\mathrm{d}x=\int_{0.00}^{3.00}(x^{2})\,\mathrm{d}x=9.00\) sage: test(interacts.calculus.function_derivative) Interactive function with 4 widgets - title: HTMLText(value=u'

Derivative grapher

') - function: EvalText(value=u'x^5-3*x^3+1', description=u'Function:', layout=Layout(max_width=u'81em')) - x_range: FloatRangeSlider(value=(-2.0, 2.0), description=u'Range (x)', max=15.0, min=-15.0) - y_range: FloatRangeSlider(value=(-8.0, 6.0), description=u'Range (y)', max=15.0, min=-15.0) -
-
-
+ title: HTMLText(value='

Derivative grapher

') + function: EvalText(value='x^5-3*x^3+1', description='Function:', layout=Layout(max_width='81em')) + x_range: FloatRangeSlider(value=(-2.0, 2.0), description='Range (x)', max=15.0, min=-15.0) + y_range: FloatRangeSlider(value=(-8.0, 6.0), description='Range (y)', max=15.0, min=-15.0) +
\(\color{Blue}{f(x) = x^{5} - 3 \, x^{3} + 1}\)
+
\(\color{Green}{f'(x) = 5 \, x^{4} - 9 \, x^{2}}\)
+
\(\color{Red}{f''(x) = 20 \, x^{3} - 18 \, x}\)
sage: test(interacts.calculus.difference_quotient) Interactive function with 5 widgets - title: HTMLText(value=u'

Difference quotient

') - f: EvalText(value=u'sin(x)', description=u'f(x)', layout=Layout(max_width=u'81em')) - interval: FloatRangeSlider(value=(0.0, 10.0), description=u'Range', max=10.0) - a: IntSlider(value=5, description=u'$a$', max=10) - x0: IntSlider(value=2, description=u'$x_0$ (start point)', max=10) + title: HTMLText(value='

Difference quotient

') + f: EvalText(value='sin(x)', description='f(x)', layout=Layout(max_width='81em')) + interval: FloatRangeSlider(value=(0.0, 10.0), description='Range', max=10.0) + a: IntSlider(value=5, description='$a$', max=10) + x0: IntSlider(value=2, description='$x_0$ (start point)', max=10)

Difference Quotient

Difference Quotient

-
-
- -
+
\(\text{Line's equation:}\) + \(y = 1/3*(x - 5)*(sin(5) - sin(2)) + sin(5)\)
+ \(\text{Slope:}\) + \(k = \frac{f(x_0)-f(a)}{x_0-a} = -0.62274\)
sage: test(interacts.calculus.quadratic_equation) Interactive function with 3 widgets - A: IntSlider(value=1, description=u'A', max=7, min=-7) - B: IntSlider(value=1, description=u'B', max=7, min=-7) - C: IntSlider(value=-2, description=u'C', max=7, min=-7) + A: IntSlider(value=1, description='A', max=7, min=-7) + B: IntSlider(value=1, description='B', max=7, min=-7) + C: IntSlider(value=-2, description='C', max=7, min=-7)

The Solutions of the Quadratic Equation

- - - + \(x^2 + x - 2 = 0\) + \(Ax^2 + Bx + C = 0\) + \(x = \frac{-B\pm\sqrt{B^2-4AC}}{2A} = \frac{-1\pm\sqrt{1^2-4*1*-2}}{2*1} = \frac{-1\pm\sqrt{\color{Green}{9}}}{2} = \begin{cases}1\\-2\end{cases}\) sage: test(interacts.calculus.secant_method) Interactive function with 5 widgets - title: HTMLText(value=u'

Secant method for numerical root finding

') - f: EvalText(value=u'x^2-2', description=u'f(x)', layout=Layout(max_width=u'81em')) - interval: IntRangeSlider(value=(0, 4), description=u'range', max=5, min=-5) - d: IntSlider(value=3, description=u'10^-d precision', max=16, min=1) - maxn: IntSlider(value=10, description=u'max iterations', max=15) - - - - + title: HTMLText(value='

Secant method for numerical root finding

') + f: EvalText(value='x^2-2', description='f(x)', layout=Layout(max_width='81em')) + interval: IntRangeSlider(value=(0, 4), description='range', max=5, min=-5) + d: IntSlider(value=3, description='10^-d precision', max=16, min=1) + maxn: IntSlider(value=10, description='max iterations', max=15) + \(\text{Precision }h = 10^{-d}=10^{-3}=0.00100\) + \({c = }1.4144038097709382\) + \({f(c) = }0.0005381370945443109\) + \(6 \text{ iterations}\) sage: test(interacts.calculus.newton_method) Interactive function with 7 widgets - title: HTMLText(value=u'

Newton method

') - f: EvalText(value=u'x^2 - 2', description=u'f', layout=Layout(max_width=u'81em')) - c: IntSlider(value=6, description=u'Start ($x$)', max=10, min=-10) - d: IntSlider(value=3, description=u'$10^{-d}$ precision', max=16, min=1) - maxn: IntSlider(value=10, description=u'max iterations', max=15) - interval: IntRangeSlider(value=(0, 6), description=u'Interval', max=10, min=-10) - list_steps: Checkbox(value=False, description=u'List steps') - - - - + title: HTMLText(value='

Newton method

') + f: EvalText(value='x^2 - 2', description='f', layout=Layout(max_width='81em')) + c: IntSlider(value=6, description='Start ($x$)', max=10, min=-10) + d: IntSlider(value=3, description='$10^{-d}$ precision', max=16, min=1) + maxn: IntSlider(value=10, description='max iterations', max=15) + interval: IntRangeSlider(value=(0, 6), description='Interval', max=10, min=-10) + list_steps: Checkbox(value=False, description='List steps') + \(\text{Precision } 2h = 0.001\) + \({c = }1.4142141576301823\) + \({f(c) = }1.6836416460996873 \times 10^{-06}\) + \(6 \text{ iterations}\) sage: test(interacts.calculus.trapezoid_integration) Interactive function with 7 widgets - title: HTMLText(value=u'

Trapezoid integration

') - f: EvalText(value=u'x^2-5*x + 10', description=u'$f(x)=$', layout=Layout(max_width=u'81em')) - n: IntSlider(value=5, description=u'# divisions', min=1) - interval_input: ToggleButtons(description=u'Integration interval', options=('from slider', 'from keyboard'), value='from slider') - interval_s: IntRangeSlider(value=(0, 8), description=u'slider: ', max=10, min=-10) - interval_g: Grid(value=[[0, 8]], children=(Label(value=u'keyboard: '), VBox(children=(EvalText(value=u'0', layout=Layout(max_width=u'5em')),)), VBox(children=(EvalText(value=u'8', layout=Layout(max_width=u'5em')),)))) - output_form: ToggleButtons(description=u'Computations form', options=('traditional', 'table', 'none'), value='traditional') - Function - Integral value to seven decimal places is: + title: HTMLText(value='

Trapezoid integration

') + f: EvalText(value='x^2-5*x + 10', description='$f(x)=$', layout=Layout(max_width='81em')) + n: IntSlider(value=5, description='# divisions', min=1) + interval_input: ToggleButtons(description='Integration interval', options=('from slider', 'from keyboard'), value='from slider') + interval_s: IntRangeSlider(value=(0, 8), description='slider: ', max=10, min=-10) + interval_g: Grid(value=[[0, 8]], children=(Label(value='keyboard: '), VBox(children=(EvalText(value='0', layout=Layout(max_width='5em')),)), VBox(children=(EvalText(value='8', layout=Layout(max_width='5em')),)))) + output_form: ToggleButtons(description='Computations form', options=('traditional', 'table', 'none'), value='traditional') + Function \(f(x)=x^{2} - 5 \, x + 10\) + Integral value to seven decimal places is: \(\displaystyle\int_{0.00}^{8.00} {f(x) \, \mathrm{d}x} = 90.666667\)
\begin{align*} @@ -154,15 +154,15 @@ Test all interacts from the Sage interact library:: sage: test(interacts.calculus.simpson_integration) Interactive function with 7 widgets - title: HTMLText(value=u'

Simpson integration

') - f: EvalText(value=u'x*sin(x)+x+1', description=u'$f(x)=$', layout=Layout(max_width=u'81em')) - n: IntSlider(value=6, description=u'# divisions', min=2, step=2) - interval_input: ToggleButtons(description=u'Integration interval', options=('from slider', 'from keyboard'), value='from slider') - interval_s: IntRangeSlider(value=(0, 10), description=u'slider: ', max=10, min=-10) - interval_g: Grid(value=[[0, 10]], children=(Label(value=u'keyboard: '), VBox(children=(EvalText(value=u'0', layout=Layout(max_width=u'5em')),)), VBox(children=(EvalText(value=u'10', layout=Layout(max_width=u'5em')),)))) - output_form: ToggleButtons(description=u'Computations form', options=('traditional', 'table', 'none'), value='traditional') - Function - Integral value to seven decimal places is: + title: HTMLText(value='

Simpson integration

') + f: EvalText(value='x*sin(x)+x+1', description='$f(x)=$', layout=Layout(max_width='81em')) + n: IntSlider(value=6, description='# divisions', min=2, step=2) + interval_input: ToggleButtons(description='Integration interval', options=('from slider', 'from keyboard'), value='from slider') + interval_s: IntRangeSlider(value=(0, 10), description='slider: ', max=10, min=-10) + interval_g: Grid(value=[[0, 10]], children=(Label(value='keyboard: '), VBox(children=(EvalText(value='0', layout=Layout(max_width='5em')),)), VBox(children=(EvalText(value='10', layout=Layout(max_width='5em')),)))) + output_form: ToggleButtons(description='Computations form', options=('traditional', 'table', 'none'), value='traditional') + Function \(f(x)=x \sin\left(x\right) + x + 1\) + Integral value to seven decimal places is: \(\displaystyle\int_{0.00}^{10.00} {f(x) \, \mathrm{d}x} = 67.846694\)
\begin{align*} @@ -177,110 +177,109 @@ Test all interacts from the Sage interact library:: sage: test(interacts.calculus.bisection_method) Interactive function with 5 widgets - title: HTMLText(value=u'

Bisection method

') - f: EvalText(value=u'x^2-2', description=u'f(x)', layout=Layout(max_width=u'81em')) - interval: IntRangeSlider(value=(0, 4), description=u'range', max=5, min=-5) - d: IntSlider(value=3, description=u'$10^{-d}$ precision', max=8, min=1) - maxn: IntSlider(value=10, description=u'max iterations', max=50) - - - - + title: HTMLText(value='

Bisection method

') + f: EvalText(value='x^2-2', description='f(x)', layout=Layout(max_width='81em')) + interval: IntRangeSlider(value=(0, 4), description='range', max=5, min=-5) + d: IntSlider(value=3, description='$10^{-d}$ precision', max=8, min=1) + maxn: IntSlider(value=10, description='max iterations', max=50) + \(\text{Precision }h = 10^{-d}=10^{-3}=0.00100\) + \({c = }1.4140625\) + \({f(c) = }-0.00042724609375\) + \(9 \text{ iterations}\) sage: test(interacts.calculus.riemann_sum) Manual interactive function with 9 widgets - title: HTMLText(value=u'

Riemann integral with random sampling

') - f: EvalText(value=u'x^2+1', description=u'$f(x)=$', layout=Layout(max_width=u'41em')) - n: IntSlider(value=5, description=u'# divisions', max=30, min=1) - hr1: HTMLText(value=u'
') - interval_input: ToggleButtons(description=u'Integration interval', options=('from slider', 'from keyboard'), value='from slider') - interval_s: IntRangeSlider(value=(0, 2), description=u'slider: ', max=10, min=-5) - interval_g: Grid(value=[[0, 2]], children=(Label(value=u'keyboard: '), VBox(children=(EvalText(value=u'0', layout=Layout(max_width=u'5em')),)), VBox(children=(EvalText(value=u'2', layout=Layout(max_width=u'5em')),)))) - hr2: HTMLText(value=u'
') - list_table: Checkbox(value=False, description=u'List table') + title: HTMLText(value='

Riemann integral with random sampling

') + f: EvalText(value='x^2+1', description='$f(x)=$', layout=Layout(max_width='41em')) + n: IntSlider(value=5, description='# divisions', max=30, min=1) + hr1: HTMLText(value='
') + interval_input: ToggleButtons(description='Integration interval', options=('from slider', 'from keyboard'), value='from slider') + interval_s: IntRangeSlider(value=(0, 2), description='slider: ', max=10, min=-5) + interval_g: Grid(value=[[0, 2]], children=(Label(value='keyboard: '), VBox(children=(EvalText(value='0', layout=Layout(max_width='5em')),)), VBox(children=(EvalText(value='2', layout=Layout(max_width='5em')),)))) + hr2: HTMLText(value='
') + list_table: Checkbox(value=False, description='List table') Adjust your data and click Update button. Click repeatedly for another random values. - Riemann sum: - Exact value of the integral + Riemann sum: \(\displaystyle\sum_{i=1}^{5} f(\eta_i)(x_i-x_{i-1})=...\) + Exact value of the integral \(\displaystyle\int_{0}^{2}x^{2} + + 1\,\mathrm{d}x=4.666666666666668\) sage: test(interacts.calculus.function_tool) Interactive function with 7 widgets - f: EvalText(value=u'sin(x)', description=u'f') - g: EvalText(value=u'cos(x)', description=u'g') - xrange: IntRangeSlider(value=(0, 1), description=u'x-range', max=3, min=-3) - yrange: Text(value=u'auto', description=u'yrange') - a: IntSlider(value=1, description=u'a', max=3, min=-1) - action: ToggleButtons(description=u'h = ', options=('f', 'df/dx', 'int f', 'num f', 'den f', '1/f', 'finv', 'f+a', 'f-a', 'f*a', 'f/a', 'f^a', 'f(x+a)', 'f(x*a)', 'f+g', 'f-g', 'f*g', 'f/g', 'f(g)'), value='f') - do_plot: Checkbox(value=True, description=u'Draw Plots') -
-
-
+ f: EvalText(value='sin(x)', description='f') + g: EvalText(value='cos(x)', description='g') + xrange: IntRangeSlider(value=(0, 1), description='x-range', max=3, min=-3) + yrange: Text(value='auto', description='yrange') + a: IntSlider(value=1, description='a', max=3, min=-1) + action: ToggleButtons(description='h = ', options=('f', 'df/dx', 'int f', 'num f', 'den f', '1/f', 'finv', 'f+a', 'f-a', 'f*a', 'f/a', 'f^a', 'f(x+a)', 'f(x*a)', 'f+g', 'f-g', 'f*g', 'f/g', 'f(g)'), value='f') + do_plot: Checkbox(value=True, description='Draw Plots') +
\(f = \sin\left(x\right)\)
+
\(g = \cos\left(x\right)\)
+
\(h = f = \sin\left(x\right)\)
sage: test(interacts.fractals.mandelbrot) Interactive function with 6 widgets - expo: FloatSlider(value=2.0, description=u'expo', max=10.0, min=-10.0) - iterations: IntSlider(value=20, description=u'# iterations', min=1) - zoom_x: FloatRangeSlider(value=(-2.0, 1.0), description=u'Zoom X', max=2.0, min=-2.0, step=0.01) - zoom_y: FloatRangeSlider(value=(-1.5, 1.5), description=u'Zoom Y', max=2.0, min=-2.0, step=0.01) - plot_points: IntSlider(value=150, description=u'plot points', max=400, min=20, step=20) - dpi: IntSlider(value=80, description=u'dpi', max=200, min=20, step=10) + expo: FloatSlider(value=2.0, description='expo', max=10.0, min=-10.0) + iterations: IntSlider(value=20, description='# iterations', min=1) + zoom_x: FloatRangeSlider(value=(-2.0, 1.0), description='Zoom X', max=2.0, min=-2.0, step=0.01) + zoom_y: FloatRangeSlider(value=(-1.5, 1.5), description='Zoom Y', max=2.0, min=-2.0, step=0.01) + plot_points: IntSlider(value=150, description='plot points', max=400, min=20, step=20) + dpi: IntSlider(value=80, description='dpi', max=200, min=20, step=10)

Mandelbrot Fractal

- Recursive Formula: for + Recursive Formula: \(z \leftarrow z^{2.00} + c\) for \(c \in \mathbb{C}\) sage: test(interacts.fractals.julia) Interactive function with 8 widgets - expo: FloatSlider(value=2.0, description=u'expo', max=10.0, min=-10.0) - c_real: FloatSlider(value=0.5, description=u'real part const.', max=2.0, min=-2.0, step=0.01) - c_imag: FloatSlider(value=0.5, description=u'imag part const.', max=2.0, min=-2.0, step=0.01) - iterations: IntSlider(value=20, description=u'# iterations', min=1) - zoom_x: FloatRangeSlider(value=(-1.5, 1.5), description=u'Zoom X', max=2.0, min=-2.0, step=0.01) - zoom_y: FloatRangeSlider(value=(-1.5, 1.5), description=u'Zoom Y', max=2.0, min=-2.0, step=0.01) - plot_points: IntSlider(value=150, description=u'plot points', max=400, min=20, step=20) - dpi: IntSlider(value=80, description=u'dpi', max=200, min=20, step=10) + expo: FloatSlider(value=2.0, description='expo', max=10.0, min=-10.0) + c_real: FloatSlider(value=0.5, description='real part const.', max=2.0, min=-2.0, step=0.01) + c_imag: FloatSlider(value=0.5, description='imag part const.', max=2.0, min=-2.0, step=0.01) + iterations: IntSlider(value=20, description='# iterations', min=1) + zoom_x: FloatRangeSlider(value=(-1.5, 1.5), description='Zoom X', max=2.0, min=-2.0, step=0.01) + zoom_y: FloatRangeSlider(value=(-1.5, 1.5), description='Zoom Y', max=2.0, min=-2.0, step=0.01) + plot_points: IntSlider(value=150, description='plot points', max=400, min=20, step=20) + dpi: IntSlider(value=80, description='dpi', max=200, min=20, step=10)

Julia Fractal

- Recursive Formula: + Recursive Formula: \(z \leftarrow z^{2.00} + (0.50+0.50*\mathbb{I})\) sage: test(interacts.fractals.cellular_automaton) Interactive function with 3 widgets - N: IntSlider(value=100, description=u'Number of iterations', max=500, min=1) - rule_number: IntSlider(value=110, description=u'Rule number', max=255) - size: IntSlider(value=6, description=u'size of graphic', max=11, min=1) + N: IntSlider(value=100, description='Number of iterations', max=500, min=1) + rule_number: IntSlider(value=110, description='Rule number', max=255) + size: IntSlider(value=6, description='size of graphic', max=11, min=1)

Cellular Automaton

"A cellular automaton is a collection of "colored" cells on a grid of specified shape that evolves through a number of discrete time steps according to a set of rules based on the states of neighboring cells." — Mathworld, Cellular Automaton
Rule 110 expands to 01110110
sage: test(interacts.geometry.unit_circle) Interactive function with 2 widgets - function: Dropdown(description=u'function', options=(('sin(x)', 0), ('cos(x)', 1), ('tan(x)', 2)), value=0) - x: TransformFloatSlider(value=0.0, description=u'x', max=6.283185307179586, step=0.015707963267948967) + function: Dropdown(description='function', options=(('sin(x)', 0), ('cos(x)', 1), ('tan(x)', 2)), value=0) + x: TransformFloatSlider(value=0.0, description='x', max=6.283185307179586, step=0.015707963267948967)
Lines of the same color have the same length
sage: test(interacts.geometry.trigonometric_properties_triangle) Interactive function with 3 widgets - a0: IntSlider(value=30, description=u'A', max=360) - a1: IntSlider(value=180, description=u'B', max=360) - a2: IntSlider(value=300, description=u'C', max=360) + a0: IntSlider(value=30, description='A', max=360) + a1: IntSlider(value=180, description='B', max=360) + a2: IntSlider(value=300, description='C', max=360)

Trigonometric Properties of a Triangle

- - , , - Area of triangle + \(\angle A = {60.000}^{\circ},\) \(\angle B = {45.000}^{\circ},\) \(\angle C = {75.000}^{\circ}\) + \(AB = 1.931852\), \(BC = 1.732051\), \(CA = 1.414214\) + Area of triangle \(ABC = 1.183013\) sage: test(interacts.geometry.special_points) Interactive function with 10 widgets - title: HTMLText(value=u'

Special points in triangle

') - a0: IntSlider(value=30, description=u'A', max=360) - a1: IntSlider(value=180, description=u'B', max=360) - a2: IntSlider(value=300, description=u'C', max=360) - show_median: Checkbox(value=False, description=u'Medians') - show_pb: Checkbox(value=False, description=u'Perpendicular Bisectors') - show_alt: Checkbox(value=False, description=u'Altitudes') - show_ab: Checkbox(value=False, description=u'Angle Bisectors') - show_incircle: Checkbox(value=False, description=u'Incircle') + title: HTMLText(value='

Special points in triangle

') + a0: IntSlider(value=30, description='A', max=360) + a1: IntSlider(value=180, description='B', max=360) + a2: IntSlider(value=300, description='C', max=360) + show_median: Checkbox(value=False, description='Medians') + show_pb: Checkbox(value=False, description='Perpendicular Bisectors') + show_alt: Checkbox(value=False, description='Altitudes') + show_ab: Checkbox(value=False, description='Angle Bisectors') + show_incircle: Checkbox(value=False, description='Incircle') show_euler: Checkbox(value=False, description=u"Euler's Line") sage: test(interacts.statistics.coin) Interactive function with 2 widgets - n: IntSlider(value=1000, description=u'Number of Tosses', max=10000, min=2, step=100) - interval: IntRangeSlider(value=(0, 0), description=u'Plotting range (y)', max=1) + n: IntSlider(value=1000, description='Number of Tosses', max=10000, min=2, step=100) + interval: IntRangeSlider(value=(0, 0), description='Plotting range (y)', max=1) doctest:...: UserWarning: Attempting to set identical bottom == top == 0.0 results in singular transformations; automatically expanding. Test matrix control (see :trac:`27735`):: @@ -291,7 +290,7 @@ Test matrix control (see :trac:`27735`):: ....: print(parent(A)) sage: test(matrix_test) Interactive function with 1 widget - A: Grid(value=[[0, 1], [2, 3]], children=(Label(value=u'A'), VBox(children=(EvalText(value=u'0', layout=Layout(max_width=u'5em')), EvalText(value=u'2', layout=Layout(max_width=u'5em')))), VBox(children=(EvalText(value=u'1', layout=Layout(max_width=u'5em')), EvalText(value=u'3', layout=Layout(max_width=u'5em')))))) + A: Grid(value=[[0, 1], [2, 3]], children=(Label(value='A'), VBox(children=(EvalText(value='0', layout=Layout(max_width='5em')), EvalText(value='2', layout=Layout(max_width='5em')))), VBox(children=(EvalText(value='1', layout=Layout(max_width='5em')), EvalText(value='3', layout=Layout(max_width='5em')))))) [0 1] [2 3] Full MatrixSpace of 2 by 2 dense matrices over Rational Field diff --git a/src/sage/interfaces/mathematica.py b/src/sage/interfaces/mathematica.py index 45fa98179d3..030e894355f 100644 --- a/src/sage/interfaces/mathematica.py +++ b/src/sage/interfaces/mathematica.py @@ -232,7 +232,7 @@ sage: F[4] # optional - mathematica {541, 1} -Mathematica's ECM package is no longer available. +Mathematica's ECM package is no longer available. Long Input ---------- @@ -1000,7 +1000,7 @@ def show(self, ImageSize=600): sage: Q = mathematica('Sin[x Cos[y]]/Sqrt[1-x^2]') # optional - mathematica sage: show(Q) # optional - mathematica - + \(\frac{\sin (x \cos (y))}{\sqrt{1-x^2}}\) The following example starts a Mathematica frontend to do the rendering (:trac:`28819`):: diff --git a/src/sage/misc/html.py b/src/sage/misc/html.py index b9f67c566af..c4a6a32c71a 100644 --- a/src/sage/misc/html.py +++ b/src/sage/misc/html.py @@ -64,10 +64,8 @@ def _rich_repr_(self, display_manager, **kwds): def math_parse(s): r""" - Replace TeX-``$`` with Mathjax equations. - - Turn the HTML-ish string s that can have \$\$ and \$'s in it into - pure HTML. See below for a precise definition of what this means. + Transform the string ``s`` with TeX maths to an HTML string renderable by + MathJax. INPUT: @@ -77,48 +75,25 @@ def math_parse(s): A :class:`HtmlFragment` instance. - Do the following: + Specifically this method does the following: - * Replace all ``\$ text \$``\'s by - ```` - * Replace all ``\$\$ text \$\$``\'s by - ```` - * Replace all ``\ \$``\'s by ``\$``\'s. Note that in - the above two cases nothing is done if the ``\$`` - is preceeded by a backslash. - * Replace all ``\[ text \]``\'s by - ```` + * Replace all ``\$text\$``\'s by ``\(text\)`` + * Replace all ``\$\$text\$\$``\'s by ``\[text\]`` + * Replace all ``\\\$``\'s by ``\$``\'s. Note that this has precedence over + the above two cases. EXAMPLES:: - sage: pretty_print(sage.misc.html.math_parse('This is $2+2$.')) - This is . - sage: pretty_print(sage.misc.html.math_parse('This is $$2+2$$.')) - This is . - sage: pretty_print(sage.misc.html.math_parse('This is \\[2+2\\].')) - This is . - sage: pretty_print(sage.misc.html.math_parse(r'This is \[2+2\].')) - This is . - - TESTS:: + sage: print(sage.misc.html.math_parse('This is $2+2$.')) + This is \(2+2\). + sage: print(sage.misc.html.math_parse('This is $$2+2$$.')) + This is \[2+2\]. + sage: print(sage.misc.html.math_parse('This is \\[2+2\\].')) + This is \[2+2\]. + sage: print(sage.misc.html.math_parse(r'\$2+2\$ is rendered to $2+2$.')) + $2+2$ is rendered to \(2+2\). - sage: sage.misc.html.math_parse(r'This \$\$is $2+2$.') - This $$is . """ - # first replace \\[ and \\] by , respectively. - while True: - i = s.find('\\[') - if i == -1: - break - else: - s = s[:i] + '' - else: - s = s[:j] + '' + s[j+2:] - # Below t always has the "parsed so far" version of s, and s is # just the part of the original input s that hasn't been parsed. t = '' @@ -128,18 +103,19 @@ def math_parse(s): # No dollar signs -- definitely done. return HtmlFragment(t + s) elif i > 0 and s[i-1] == '\\': - # A dollar sign with a backslash right before it, so - # we ignore it by sticking it in the parsed string t - # and skip to the next iteration. - t += s[:i-1] + '$' + # A dollar sign with a backslash right before it, so this is a + # normal dollar sign. If processEscapes is enabled in MathJax, "\$" + # will do the job. But as we do not assume that, we use the span + # tag safely. + t += s[:i-1] + '$' s = s[i+1:] continue elif i+1 < len(s) and s[i+1] == '$': # Found a math environment. Double dollar sign so display mode. - disp = '; mode=display' + disp = True else: # Found math environment. Single dollar sign so default mode. - disp = '' + disp = False # Now find the matching $ sign and form the html string. @@ -160,8 +136,10 @@ def math_parse(s): j += i + 2 txt = s[i+1:j] - t += s[:i] + '' % (disp, - ' '.join(txt.splitlines())) + if disp: + t += s[:i] + r'\[{0}\]'.format(' '.join(txt.splitlines())) + else: + t += s[:i] + r'\({0}\)'.format(' '.join(txt.splitlines())) s = s[j+1:] if disp: s = s[1:] @@ -249,9 +227,9 @@ class MathJax: sage: from sage.misc.html import MathJax sage: MathJax()(3) - + \[\newcommand{\Bold}[1]{\mathbf{#1}}3\] sage: MathJax()(ZZ) - + \[\newcommand{\Bold}[1]{\mathbf{#1}}\Bold{Z}\] """ def __call__(self, x, combine_all=False): @@ -275,7 +253,7 @@ def __call__(self, x, combine_all=False): sage: from sage.misc.html import MathJax sage: MathJax()(3) - + \[\newcommand{\Bold}[1]{\mathbf{#1}}3\] sage: str(MathJax().eval(ZZ['x'], mode='display')) == str(MathJax()(ZZ['x'])) True """ @@ -312,13 +290,11 @@ def eval(self, x, globals=None, locals=None, mode='display', combine_all=False): sage: from sage.misc.html import MathJax sage: MathJax().eval(3, mode='display') - + \[\newcommand{\Bold}[1]{\mathbf{#1}}3\] sage: MathJax().eval(3, mode='inline') - - sage: MathJax().eval(type(3), mode='inline') # py2 - - sage: MathJax().eval(type(3), mode='inline') # py3 - + \(\newcommand{\Bold}[1]{\mathbf{#1}}3\) + sage: MathJax().eval(type(3), mode='inline') + \(\newcommand{\Bold}[1]{\mathbf{#1}}\verb||\) """ # Get a regular LaTeX representation of x x = latex(x, combine_all=combine_all) @@ -376,9 +352,9 @@ def eval(self, x, globals=None, locals=None, mode='display', combine_all=False): parts ) if mode == 'display': - html = '' + html = r'\[{0}\]' elif mode == 'inline': - html = '' + html = r'\({0}\)' elif mode == 'plain': return latex_string else: @@ -427,7 +403,7 @@ def __call__(self, obj, concatenate=True): sage: html(1/2) - + \[\newcommand{\Bold}[1]{\mathbf{#1}}\frac{1}{2}\] sage: html('sagemath') sagemath @@ -470,9 +446,9 @@ def eval(self, s, locals=None): sage: a = 123 sage: html.eval('a') - + \(123\) sage: html.eval('a', locals={'a': 456}) - + \(456\) """ if locals is None: from sage.repl.user_globals import get_globals @@ -489,8 +465,7 @@ def eval(self, s, locals=None): if j == -1: t += s break - t += s[:i] + ''%\ - latex(sage_eval(s[6+i:j], locals=locals)) + t += s[:i] + r'\({}\)'.format(latex(sage_eval(s[6+i:j], locals=locals))) s = s[j+7:] return HtmlFragment(t) diff --git a/src/sage/misc/table.py b/src/sage/misc/table.py index 089ce8437ec..8610f06df0d 100644 --- a/src/sage/misc/table.py +++ b/src/sage/misc/table.py @@ -179,24 +179,24 @@ class table(SageObject): - - + + - - + + - - + + - - + + - - + +
\(x\)\(\sin(x)\)
\(0\)\(0.00\)
\(1\)\(0.84\)
\(2\)\(0.91\)
\(3\)\(0.14\)
@@ -648,22 +648,22 @@ def _html_(self): - - - + + + - - - + + + - - - + \end{array}\right)\) + +
text\(\sin(x)\)\(x\)text
\(1\)\(34342\)\(3\)
\(5\)\(6\)
@@ -690,24 +690,24 @@ def _html_(self): - - + + - - + + - - + + - - + + - - + +
\(x\)\(\sin(x)\)
\(0\)\(0.00\)
\(1\)\(0.84\)
\(2\)\(0.91\)
\(3\)\(0.14\)
@@ -776,9 +776,9 @@ def _html_table_row(self, file, row, header=False): sage: s = StringIO() sage: T._html_table_row(s, ['a', 2, '$x$']) sage: print(s.getvalue()) - a - - + a + \(2\) + \(x\) """ from sage.plot.all import Graphics from .latex import latex @@ -790,27 +790,40 @@ def _html_table_row(self, file, row, header=False): elif not isinstance(row, (list, tuple)): row = [row] - column_tag = "%s\n" if header else "%s\n" + align_char = self._options['align'][0] # 'l', 'c', 'r' + + if align_char == 'l': + style = 'text-align:left' + elif align_char == 'c': + style = 'text-align:center' + elif align_char == 'r': + style = 'text-align:right' + else: + style = '' + + style_attr = f' style="{style}"' if style else '' + + column_tag = f'%s\n' if header else f'%s\n' if self._options['header_column']: - first_column_tag = '%s\n' if header else '%s\n' + first_column_tag = '%s\n' if header else '%s\n' else: first_column_tag = column_tag - # First entry of row: + # first entry of row entry = row[0] if isinstance(entry, Graphics): file.write(first_column_tag % entry.show(linkmode = True)) elif isinstance(entry, str): file.write(first_column_tag % math_parse(entry)) else: - file.write(first_column_tag % ('' % latex(entry))) + file.write(first_column_tag % (r'\(%s\)' % latex(entry))) - # Other entries: + # other entries for column in range(1, len(row)): if isinstance(row[column], Graphics): file.write(column_tag % row[column].show(linkmode = True)) elif isinstance(row[column], str): file.write(column_tag % math_parse(row[column])) else: - file.write(column_tag % ('' % latex(row[column]))) + file.write(column_tag % (r'\(%s\)' % latex(row[column]))) diff --git a/src/sage/repl/rich_output/backend_base.py b/src/sage/repl/rich_output/backend_base.py index 152530ab383..d62ab376a19 100644 --- a/src/sage/repl/rich_output/backend_base.py +++ b/src/sage/repl/rich_output/backend_base.py @@ -454,23 +454,23 @@ def latex_formatter(self, obj, **kwds): sage: out OutputHtml container sage: out.html - buffer containing 105 bytes + buffer containing 62 bytes sage: out.html.get_str() - '' + '\\[\\newcommand{\\Bold}[1]{\\mathbf{#1}}\\frac{1}{2}\\]' sage: out = backend.latex_formatter([1/2, x, 3/4, ZZ], concatenate=False) sage: out.html.get_str() - '' + '\\[\\newcommand{\\Bold}[1]{\\mathbf{#1}}\\left[\\frac{1}{2}, x, \\frac{3}{4}, \\Bold{Z}\\right]\\]' sage: out = backend.latex_formatter([1/2, x, 3/4, ZZ], concatenate=True) sage: out.html.get_str() - '' + '\\[\\newcommand{\\Bold}[1]{\\mathbf{#1}}\\frac{1}{2} x \\frac{3}{4} \\Bold{Z}\\]' TESTS:: sage: backend.latex_formatter([], concatenate=False).html.get_str() - '' + '\\[\\newcommand{\\Bold}[1]{\\mathbf{#1}}\\left[\\right]\\]' sage: backend.latex_formatter([], concatenate=True).html.get_str() - '' + '\\[\\newcommand{\\Bold}[1]{\\mathbf{#1}}\\]' """ concatenate = kwds.get('concatenate', False) from sage.misc.html import html diff --git a/src/sage/repl/rich_output/output_browser.py b/src/sage/repl/rich_output/output_browser.py index 0c7bd7f5b7e..2746e3dff8e 100644 --- a/src/sage/repl/rich_output/output_browser.py +++ b/src/sage/repl/rich_output/output_browser.py @@ -8,8 +8,9 @@ from sage.repl.rich_output.output_basic import OutputBase from sage.repl.rich_output.buffer import OutputBuffer -latex_re = re.compile(r'', - flags=re.DOTALL) +# regex to match "\[...\]" or "\(...\)" +latex_re = re.compile(r'(?P\\\[|\\\()(?P.*)(?P\\\]|\\\))', + flags=re.DOTALL) class OutputHtml(OutputBase): @@ -20,10 +21,9 @@ def __init__(self, html): INPUT: - ``html`` -- - :class:`~sage.repl.rich_output.buffer.OutputBuffer`. Alternatively, - a string (bytes) can be passed directly which will then be - converted into an - :class:`~sage.repl.rich_output.buffer.OutputBuffer`. String + :class:`~sage.repl.rich_output.buffer.OutputBuffer`. Alternatively, a + string (bytes) can be passed directly which will then be converted + into an :class:`~sage.repl.rich_output.buffer.OutputBuffer`. String containing the html fragment code. Excludes the surrounding ```` and ```` tag. @@ -40,7 +40,7 @@ def __init__(self, html): # pdf export of a notebook m = latex_re.match(html) if m: - if m.group('mode') == 'display': + if m.group('mathstart') == r'\[' and m.group('mathend') == r'\]': self.latex = OutputBuffer('$$' + m.group('latex') + '$$') else: self.latex = OutputBuffer('$' + m.group('latex') + '$') diff --git a/src/sage/repl/rich_output/pretty_print.py b/src/sage/repl/rich_output/pretty_print.py index 64b1a281723..8c82b9c0b06 100644 --- a/src/sage/repl/rich_output/pretty_print.py +++ b/src/sage/repl/rich_output/pretty_print.py @@ -208,9 +208,9 @@ def pretty_print(*args, **kwds): sage: plt = plot(sin) sage: pretty_print(plt) # graphics output - sage: pretty_print(ZZ, 123, plt) # optional - latex - sage: pretty_print(plt, plt) # graphics output + sage: pretty_print(ZZ, 123, plt) + Integer Ring 123 Graphics object consisting of 1 graphics primitive """ dm = get_display_manager() old_preferences_text = dm.preferences.text From d25f7f3967c18063398dc4ed4bf486599f667d9e Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 12 Apr 2021 19:03:58 -0700 Subject: [PATCH 285/406] Fixup merge --- src/sage/geometry/polyhedron/base.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 11182fabd54..af75f197bd8 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -10331,9 +10331,6 @@ def affine_hull_projection(self, as_polyhedron=None, as_affine_map=False, orthog # ``convert=True`` takes care of the case, where there might be no coercion (``AA`` and quadratic field). if as_polyhedron: result['polyhedron'] = self.linear_transformation(A, new_base_ring=A.base_ring()) - A*translate_vector - [A*vector(A.base_ring(), v) - for v in self.translation(-v0).vertices()], - base_ring=A.base_ring()) if as_affine_map: result['affine_map'] = (L, - A*translate_vector) if return_all_data: From 7e0f31dedcf66d43ed4b15b5e62ab770b774f5f4 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 12 Apr 2021 19:07:37 -0700 Subject: [PATCH 286/406] affine_hull -> affine_hull_projection in doctests --- src/sage/geometry/polyhedron/base.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index af75f197bd8..dc8df89b72e 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -10174,8 +10174,8 @@ def affine_hull_projection(self, as_polyhedron=None, as_affine_map=False, orthog Return polyhedron and affine map:: sage: S = polytopes.simplex(2) - sage: S.affine_hull(orthogonal=True, - ....: as_polyhedron=True, as_affine_map=True) + sage: S.affine_hull_projection(orthogonal=True, + ....: as_polyhedron=True, as_affine_map=True) {'affine_map': (Vector space morphism represented by the matrix: [ 0 1] [ 1 -1/2] @@ -10186,7 +10186,7 @@ def affine_hull_projection(self, as_polyhedron=None, as_affine_map=False, orthog Return additional data:: - sage: S.affine_hull(orthogonal=True, return_all_data=True) + sage: S.affine_hull_projection(orthogonal=True, return_all_data=True) {'affine_map': (Vector space morphism represented by the matrix: [ 0 1] [ 1 -1/2] @@ -10206,7 +10206,7 @@ def affine_hull_projection(self, as_polyhedron=None, as_affine_map=False, orthog sage: P = P0.intersection(Polyhedron(eqns=[(-1, 1, 1, 1, 1, 1)])) sage: P.dim() 4 - sage: P.affine_hull(orthogonal=True, as_affine_map=True)[0] + sage: P.affine_hull_projection(orthogonal=True, as_affine_map=True)[0] Vector space morphism represented by the matrix: [ -1/3 -1/3 -1/6 1/12] [ 1/2 0 1/6 1/12] From c20ea90c22774fbe22caf5ec66056b311bac27f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20Leli=C3=A8vre?= Date: Tue, 13 Apr 2021 22:08:31 +0200 Subject: [PATCH 287/406] 31556: Upgrade: libgd 2.3.2 --- build/pkgs/libgd/checksums.ini | 9 +++-- build/pkgs/libgd/dependencies | 3 +- build/pkgs/libgd/package-version.txt | 2 +- build/pkgs/libgd/patches/ceill.patch | 18 --------- .../libgd/patches/gd-2.1.1-libvpx-1.4.0.patch | 37 ------------------- 5 files changed, 8 insertions(+), 61 deletions(-) delete mode 100644 build/pkgs/libgd/patches/ceill.patch delete mode 100644 build/pkgs/libgd/patches/gd-2.1.1-libvpx-1.4.0.patch diff --git a/build/pkgs/libgd/checksums.ini b/build/pkgs/libgd/checksums.ini index 365e6372d74..0db72cded2e 100644 --- a/build/pkgs/libgd/checksums.ini +++ b/build/pkgs/libgd/checksums.ini @@ -1,4 +1,5 @@ -tarball=libgd-VERSION.tar.bz2 -sha1=a36eef615a6fde81140ef4ebaae08663dfbe2b52 -md5=8770bffe87d46ff13819b7e62e1621d3 -cksum=1115143377 +tarball=libgd-VERSION.tar.xz +sha1=dddf5e9d25cb0b20b8642d5cbcfad67f8903532f +md5=0ee844caca06bb02bf4b4dabdfab4fb1 +cksum=902217083 +upstream_url=https://github.com/libgd/libgd/releases/download/gd-VERSION/libgd-VERSION.tar.xz diff --git a/build/pkgs/libgd/dependencies b/build/pkgs/libgd/dependencies index 05041105f3e..998d7d01593 100644 --- a/build/pkgs/libgd/dependencies +++ b/build/pkgs/libgd/dependencies @@ -1,5 +1,6 @@ -libpng freetype +libpng freetype xz +# xz needed to unpack tarball when sage-bootstrap-python is Python < 3.3 ---------- All lines of this file are ignored except the first. It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/libgd/package-version.txt b/build/pkgs/libgd/package-version.txt index 5c1151e094a..f90b1afc082 100644 --- a/build/pkgs/libgd/package-version.txt +++ b/build/pkgs/libgd/package-version.txt @@ -1 +1 @@ -2.1.1.1.p1 +2.3.2 diff --git a/build/pkgs/libgd/patches/ceill.patch b/build/pkgs/libgd/patches/ceill.patch deleted file mode 100644 index e8b32b00381..00000000000 --- a/build/pkgs/libgd/patches/ceill.patch +++ /dev/null @@ -1,18 +0,0 @@ -Do not use (sometimes unavailable) ceill function. - -This makes the build fail on systems where libm does not provide -long long versions of such function. -And this precision doesn't seem needed anyway, see: -* https://bitbucket.org/libgd/gd-libgd/issue/98/gd_bmpc-use-of-both-ceil-and-ceill -diff -druN gd-2.1.0.old/src/gd_bmp.c gd-2.1.0.new/src/gd_bmp.c ---- gd-2.1.0.old/src/gd_bmp.c 2013-06-25 02:58:23.000000000 -0700 -+++ gd-2.1.0.new/src/gd_bmp.c 2014-11-24 08:00:22.913537073 -0800 -@@ -792,7 +792,7 @@ - } - - /* The line must be divisible by 4, else its padded with NULLs */ -- padding = ((int)ceill(0.1 * info->width)) % 4; -+ padding = ((int)ceil(0.1 * info->width)) % 4; - if (padding) { - padding = 4 - padding; - } diff --git a/build/pkgs/libgd/patches/gd-2.1.1-libvpx-1.4.0.patch b/build/pkgs/libgd/patches/gd-2.1.1-libvpx-1.4.0.patch deleted file mode 100644 index c698972539e..00000000000 --- a/build/pkgs/libgd/patches/gd-2.1.1-libvpx-1.4.0.patch +++ /dev/null @@ -1,37 +0,0 @@ -From d41eb72cd4545c394578332e5c102dee69e02ee8 Mon Sep 17 00:00:00 2001 -From: Remi Collet -Date: Tue, 7 Apr 2015 13:11:03 +0200 -Subject: [PATCH] Fix build with latest libvpx 1.4.0 - -These new constants exist at least since 1.0.0 -Compatibility ones have been droped in 1.4.0 ---- - src/webpimg.c | 14 +++++++------- - 1 file changed, 7 insertions(+), 7 deletions(-) - -diff --git a/src/webpimg.c b/src/webpimg.c -index cf73d64..e49fcc6 100644 ---- a/src/webpimg.c -+++ b/src/webpimg.c -@@ -711,14 +711,14 @@ static WebPResult VPXEncode(const uint8* Y, - codec_ctl(&enc, VP8E_SET_STATIC_THRESHOLD, 0); - codec_ctl(&enc, VP8E_SET_TOKEN_PARTITIONS, 2); - -- vpx_img_wrap(&img, IMG_FMT_I420, -+ vpx_img_wrap(&img, VPX_IMG_FMT_I420, - y_width, y_height, 16, (uint8*)(Y)); -- img.planes[PLANE_Y] = (uint8*)(Y); -- img.planes[PLANE_U] = (uint8*)(U); -- img.planes[PLANE_V] = (uint8*)(V); -- img.stride[PLANE_Y] = y_stride; -- img.stride[PLANE_U] = uv_stride; -- img.stride[PLANE_V] = uv_stride; -+ img.planes[VPX_PLANE_Y] = (uint8*)(Y); -+ img.planes[VPX_PLANE_U] = (uint8*)(U); -+ img.planes[VPX_PLANE_V] = (uint8*)(V); -+ img.stride[VPX_PLANE_Y] = y_stride; -+ img.stride[VPX_PLANE_U] = uv_stride; -+ img.stride[VPX_PLANE_V] = uv_stride; - - res = vpx_codec_encode(&enc, &img, 0, 1, 0, VPX_DL_BEST_QUALITY); - From 84140398fe84337784834e13f5c4fc5cb73ab667 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 13 Apr 2021 14:08:36 -0700 Subject: [PATCH 288/406] Polyhedron_base.affine_hull_projection: Document a weaker guarantee of parametric_form --- src/sage/geometry/polyhedron/base.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index dc8df89b72e..dce12dfb747 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -9935,10 +9935,9 @@ def affine_hull_projection(self, as_polyhedron=None, as_affine_map=False, orthog is a linear transformation and its second component a shift; see above. - - ``parametric_form`` -- a pair `(v_0, V)` where `v_0` is a vector - and `V` a tuple of vectors. `v_0` is used as a base point - in the transformation and the vectors `V` are its neighbors - (in the already shifted polyhedron). + - ``parametric_form`` -- a pair `(v_0, V)` where `v_0` is a point + of ``self`` and `V` is a tuple of vectors spanning the linear + space parallel to the affine hull of ``self``. - ``coordinate_images`` -- a tuple of the images of the variables in the standard coordinate system of the ambient space. These From dba27635d03a6453c07a884199d2f2062aea8ad9 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 13 Apr 2021 22:13:45 -0700 Subject: [PATCH 289/406] Polyhedron_base.affine_hull_projection: Replace 'affine_map' by 'projection_map', 'parametric_form'/'coordinate_images' by 'section_map' --- src/sage/geometry/polyhedron/base.py | 157 +++++++++++++-------------- 1 file changed, 78 insertions(+), 79 deletions(-) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index dce12dfb747..fc90088e535 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -8168,7 +8168,7 @@ def volume(self, measure='ambient', engine='auto', **kwds): return self._volume_normaliz(measure='euclidean') # use an orthogonal transformation, which preserves volume up to a factor provided by the transformation matrix affine_hull = self.affine_hull_projection(orthogonal=True, as_polyhedron=True, as_affine_map=True) - A = affine_hull['affine_map'][0].matrix() + A = affine_hull['projection_map'][0].matrix() Adet = (A.transpose() * A).det() scaled_volume = self.affine_hull_projection(orthogonal=True).volume(measure='ambient', engine=engine, **kwds) if Adet.is_square(): @@ -9865,16 +9865,18 @@ def _test_is_combinatorially_isomorphic(self, tester=None, **options): @cached_method def affine_hull_projection(self, as_polyhedron=None, as_affine_map=False, orthogonal=False, orthonormal=False, extend=False, minimal=False, return_all_data=False): - """ - Return the polyhedron projected into its affine hull. + """Return the polyhedron projected into its affine hull. Each polyhedron is contained in some smallest affine subspace - (possibly the entire ambient space) -- its affine hull. - We provide a projection of the ambient - space of the polyhedron to Euclidean space of dimension of the - polyhedron. Then the image of the polyhedron under this - projection (or, depending on the parameter ``as_affine_map``, - the projection itself) is returned. + (possibly the entire ambient space) -- its affine hull. We + provide an affine linear map that projects the ambient space of + the polyhedron to the standard Euclidean space of dimension of + the polyhedron, which restricts to a bijection from the affine + hull. + + The projection map is not unique; some parameters control the + choice of the map. Other parameters control the output of the + function. INPUT: @@ -9897,6 +9899,13 @@ def affine_hull_projection(self, as_polyhedron=None, as_affine_map=False, orthog If both ``as_polyhedron`` and ``as_affine_map`` are set, then both are returned, encapsulated in a dictionary. + - ``return_all_data`` -- (boolean, default ``False``) + + If set, then ``as_polyhedron`` and ``as_affine_map`` will set + (possibly overridden) and additional (internal) data concerning + the transformation is returned. Everything is encapsulated + in a dictionary in this case. + - ``orthogonal`` -- boolean (default: ``False``); if ``True``, provide an orthogonal transformation. @@ -9913,13 +9922,6 @@ def affine_hull_projection(self, as_polyhedron=None, as_affine_map=False, orthog when doing an extension, it computes the minimal base ring of the extension, otherwise the base ring is ``AA``. - - ``return_all_data`` -- (boolean, default ``False``) - - If set, then ``as_polyhedron`` and ``as_affine_map`` will set - (possibly overridden) and additional (internal) data concerning - the transformation is returned. Everything is encapsulated - in a dictionary in this case. - OUTPUT: A full-dimensional polyhedron or an affine transformation, @@ -9929,25 +9931,18 @@ def affine_hull_projection(self, as_polyhedron=None, as_affine_map=False, orthog In case the output is a dictionary, the following entries might be included: - - ``polyhedron`` -- the affine hull of the original polyhedron + - ``polyhedron`` -- the projection of the original polyhedron - - ``affine_map`` -- the affine map as a pair whose first component + - ``projection_map`` -- the affine map as a pair whose first component is a linear transformation and its second component a shift; see above. - - ``parametric_form`` -- a pair `(v_0, V)` where `v_0` is a point - of ``self`` and `V` is a tuple of vectors spanning the linear - space parallel to the affine hull of ``self``. + - ``section_map`` -- an affine map as a pair whose first component + is a linear transformation and its second component a shift. + It maps the codomain of ``affine_map`` to the affine hull of + ``self``. It is a right inverse of ``projection_map``. - - ``coordinate_images`` -- a tuple of the images of the variables - in the standard coordinate system of the ambient space. These - images are degree one polynomials and are used for mapping a - function in the ambient space to a function in the affine hull - such that the values of this function are preserved. - - Note that all entries of this dictionary are compatible (in the - sense that the order of points/columns/etc are compatible) - with each other. + Note that all entries of this dictionary are compatible. .. TODO:: @@ -10173,28 +10168,41 @@ def affine_hull_projection(self, as_polyhedron=None, as_affine_map=False, orthog Return polyhedron and affine map:: sage: S = polytopes.simplex(2) - sage: S.affine_hull_projection(orthogonal=True, - ....: as_polyhedron=True, as_affine_map=True) - {'affine_map': (Vector space morphism represented by the matrix: - [ 0 1] - [ 1 -1/2] + sage: data = S.affine_hull_projection(orthogonal=True, + ....: as_polyhedron=True, + ....: as_affine_map=True); data + {'polyhedron': A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 3 vertices, + 'projection_map': (Vector space morphism represented by the matrix: [ -1 -1/2] + [ 1 -1/2] + [ 0 1] Domain: Vector space of dimension 3 over Rational Field - Codomain: Vector space of dimension 2 over Rational Field, (1, 1/2)), - 'polyhedron': A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 3 vertices} + Codomain: Vector space of dimension 2 over Rational Field, + (1, 1/2))} - Return additional data:: + Return all data:: - sage: S.affine_hull_projection(orthogonal=True, return_all_data=True) - {'affine_map': (Vector space morphism represented by the matrix: - [ 0 1] - [ 1 -1/2] + sage: data = S.affine_hull_projection(orthogonal=True, return_all_data=True); data + {'parametric_form': ((1, 0, 0), ((-1, 1, 0), (-1, 0, 1))), + 'polyhedron': A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 3 vertices, + 'projection_map': (Vector space morphism represented by the matrix: [ -1 -1/2] + [ 1 -1/2] + [ 0 1] Domain: Vector space of dimension 3 over Rational Field - Codomain: Vector space of dimension 2 over Rational Field, (1, 1/2)), - 'coordinate_images': (2/3*t1, 1/2*t0 - 1/3*t1, -1/2*t0 - 1/3*t1 + 1), - 'parametric_form': ((0, 0, 1), ((0, 1, -1), (1, 0, -1))), - 'polyhedron': A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 3 vertices} + Codomain: Vector space of dimension 2 over Rational Field, + (1, 1/2)), + 'section_map': (Vector space morphism represented by the matrix: + [-1/2 1/2 0] + [-1/3 -1/3 2/3] + Domain: Vector space of dimension 2 over Rational Field + Codomain: Vector space of dimension 3 over Rational Field, + (1, 0, 0))} + + The section map is a right inverse of the projection map:: + + sage: data['polyhedron'].linear_transformation(data['section_map'][0].matrix().transpose()) + data['section_map'][1] == S + True :: @@ -10207,11 +10215,11 @@ def affine_hull_projection(self, as_polyhedron=None, as_affine_map=False, orthog 4 sage: P.affine_hull_projection(orthogonal=True, as_affine_map=True)[0] Vector space morphism represented by the matrix: - [ -1/3 -1/3 -1/6 1/12] - [ 1/2 0 1/6 1/12] - [ -1/3 2/3 0 0] - [ 1/2 0 -1/6 -1/12] - [ -1/3 -1/3 1/6 -1/12] + [ 0 0 0 1/3] + [ -2/3 -1/6 0 -1/12] + [ 1/3 -1/6 1/2 -1/12] + [ 0 1/2 0 -1/12] + [ 1/3 -1/6 -1/2 -1/12] Domain: Vector space of dimension 5 over Rational Field Codomain: Vector space of dimension 4 over Rational Field @@ -10289,11 +10297,12 @@ def affine_hull_projection(self, as_polyhedron=None, as_affine_map=False, orthog if self.ambient_dim() == self.dim(): result['polyhedron'] = self if as_affine_map or return_all_data: - result['affine_map'] = (linear_transformation(matrix(self.base_ring(), - self.dim(), - self.dim(), - self.base_ring().one())), - self.ambient_space().zero()) + identity = linear_transformation(matrix(self.base_ring(), + self.dim(), + self.dim(), + self.base_ring().one())) + result['projection_map'] = (identity, self.ambient_space().zero()) + result['section_map'] = (identity, self.ambient_space().zero()) elif orthogonal or orthonormal: # see TODO if not self.is_compact(): @@ -10322,33 +10331,19 @@ def affine_hull_projection(self, as_polyhedron=None, as_affine_map=False, orthog new_ring = number_field_elements_from_algebraics(A.list(), embedded=True, minimal=True)[0] A = A.change_ring(new_ring) L = linear_transformation(A, side='right') - translate_vector = vector(A.base_ring(), affine_basis[0]) - # Note the order. We compute ``A*self`` and then subtract the translation vector. + ambient_translation = -vector(A.base_ring(), affine_basis[0]) + image_translation = A * ambient_translation + # Note the order. We compute ``A*self`` and then translate the image. # ``A*self`` uses the incidence matrix and we avoid recomputation. # Also, if the new base ring is ``AA``, we want to avoid computing the incidence matrix in that ring. - # ``convert=True`` takes care of the case, where there might be no coercion (``AA`` and quadratic field). if as_polyhedron: - result['polyhedron'] = self.linear_transformation(A, new_base_ring=A.base_ring()) - A*translate_vector + result['polyhedron'] = self.linear_transformation(A, new_base_ring=A.base_ring()) + image_translation if as_affine_map: - result['affine_map'] = (L, - A*translate_vector) + result['projection_map'] = (L, image_translation) if return_all_data: - from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing - # columns of W are equal to the vertices of affine_hull['polyhedron'] - # in an order compatible with the vectors vi - def index_neq0(C): - return next(i for i, c in enumerate(C) if c != 0) - independent_vi = tuple(vi[index_neq0(tuple(c))] - for c in G.columns()) - W = matrix([list(L(v)) for v in independent_vi]).transpose() - - # transform the coordinates - T = PolynomialRing(self.base_ring(), 't', len(independent_vi)) - t = vector(T.gens()) - beta = W.inverse() * t - coordinate_images = v0.change_ring(T) + sum(b * v for b, v in zip(beta, independent_vi)) - result['coordinate_images'] = tuple(coordinate_images) - + L_dagger = linear_transformation(A.transpose() * (A * A.transpose()).inverse(), side='right') + result['section_map'] = (L_dagger, v0) else: # translate one vertex to the origin v0 = self.vertices()[0].vector() @@ -10365,15 +10360,19 @@ def index_neq0(C): A = matrix([[1 if j == i else 0 for j in range(self.ambient_dim())] for i in pivots]) if as_affine_map: - result['affine_map'] = linear_transformation(A, side='right'), vector(self.base_ring(), self.dim()) + image_translation = vector(self.base_ring(), self.dim()) + L = linear_transformation(A, side='right') + result['projection_map'] = (L, image_translation) if as_polyhedron: result['polyhedron'] = A*self + if return_all_data: + raise NotImplementedError # assemble result if return_all_data or (as_polyhedron and as_affine_map): return result elif as_affine_map: - return result['affine_map'] + return result['projection_map'] else: return result['polyhedron'] From d49c3136036a247843d4a2b9e83c320ce01f05c5 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 14 Apr 2021 21:44:54 -0700 Subject: [PATCH 290/406] Polyhedron_base.affine_hull_projection: section map for non-orthogonal case --- src/sage/geometry/polyhedron/base.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index fc90088e535..b154c358043 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -10356,7 +10356,8 @@ def affine_hull_projection(self, as_polyhedron=None, as_affine_map=False, orthog gens.append(l.vector()) # Pick subset of coordinates to coordinatize the affine span - pivots = matrix(gens).pivots() + M = matrix(gens) + pivots = M.pivots() A = matrix([[1 if j == i else 0 for j in range(self.ambient_dim())] for i in pivots]) if as_affine_map: @@ -10366,7 +10367,9 @@ def affine_hull_projection(self, as_polyhedron=None, as_affine_map=False, orthog if as_polyhedron: result['polyhedron'] = A*self if return_all_data: - raise NotImplementedError + E = M.echelon_form() + L_section = linear_transformation(matrix([E[i] for i in pivots]).transpose(), side='right') + result['section_map'] = (L_section, v0 - L_section(L(v0) + image_translation)) # assemble result if return_all_data or (as_polyhedron and as_affine_map): From d77181b4e6da158661dea4a4fc37269d58489ada Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 14 Apr 2021 21:50:48 -0700 Subject: [PATCH 291/406] Add doctest --- src/sage/geometry/polyhedron/base.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index b154c358043..745930a8051 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -10204,6 +10204,26 @@ def affine_hull_projection(self, as_polyhedron=None, as_affine_map=False, orthog sage: data['polyhedron'].linear_transformation(data['section_map'][0].matrix().transpose()) + data['section_map'][1] == S True + Same without ``orthogonal=True``:: + + sage: data = S.affine_hull_projection(return_all_data=True); data + {'polyhedron': A 2-dimensional polyhedron in ZZ^2 defined as the convex hull of 3 vertices, + 'projection_map': (Vector space morphism represented by the matrix: + [1 0] + [0 1] + [0 0] + Domain: Vector space of dimension 3 over Rational Field + Codomain: Vector space of dimension 2 over Rational Field, + (0, 0)), + 'section_map': (Vector space morphism represented by the matrix: + [ 1 0 -1] + [ 0 1 -1] + Domain: Vector space of dimension 2 over Rational Field + Codomain: Vector space of dimension 3 over Rational Field, + (0, 0, 1))} + sage: data['polyhedron'].linear_transformation(data['section_map'][0].matrix().transpose()) + data['section_map'][1] == S + True + :: sage: P0 = Polyhedron( From ab10ce252b95caecaff09df9c1e14dbcff1f7826 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Thu, 15 Apr 2021 21:10:36 +0200 Subject: [PATCH 292/406] correct base_rings for projection and section map and test suite method --- src/sage/geometry/polyhedron/base.py | 65 +++++++++++++++++++++++++--- 1 file changed, 58 insertions(+), 7 deletions(-) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 745930a8051..2ffccf9e70b 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -1024,10 +1024,10 @@ def plot(self, sage: fcube = polytopes.hypercube(4) sage: tfcube = fcube.face_truncation(fcube.faces(0)[0]) sage: sp = tfcube.schlegel_projection() - sage: for face in tfcube.faces(2): - ....: vertices = face.ambient_Vrepresentation() - ....: indices = [sp.coord_index_of(vector(x)) for x in vertices] - ....: projected_vertices = [sp.transformed_coords[i] for i in indices] + sage: for face in tfcube.faces(2): + ....: vertices = face.ambient_Vrepresentation() + ....: indices = [sp.coord_index_of(vector(x)) for x in vertices] + ....: projected_vertices = [sp.transformed_coords[i] for i in indices] ....: assert Polyhedron(projected_vertices).dim() == 2 """ def merge_options(*opts): @@ -10300,7 +10300,6 @@ def affine_hull_projection(self, as_polyhedron=None, as_affine_map=False, orthog sage: _ = P.affine_hull() doctest:...: DeprecationWarning: affine_hull is deprecated. Please use affine_hull_projection instead. See https://trac.sagemath.org/29326 for details. - """ if as_polyhedron is None: as_polyhedron = not as_affine_map @@ -10363,7 +10362,7 @@ def affine_hull_projection(self, as_polyhedron=None, as_affine_map=False, orthog result['projection_map'] = (L, image_translation) if return_all_data: L_dagger = linear_transformation(A.transpose() * (A * A.transpose()).inverse(), side='right') - result['section_map'] = (L_dagger, v0) + result['section_map'] = (L_dagger, v0.change_ring(A.base_ring())) else: # translate one vertex to the origin v0 = self.vertices()[0].vector() @@ -10379,7 +10378,7 @@ def affine_hull_projection(self, as_polyhedron=None, as_affine_map=False, orthog M = matrix(gens) pivots = M.pivots() - A = matrix([[1 if j == i else 0 for j in range(self.ambient_dim())] for i in pivots]) + A = matrix(self.base_ring(), [[1 if j == i else 0 for j in range(self.ambient_dim())] for i in pivots]) if as_affine_map: image_translation = vector(self.base_ring(), self.dim()) L = linear_transformation(A, side='right') @@ -10401,6 +10400,58 @@ def affine_hull_projection(self, as_polyhedron=None, as_affine_map=False, orthog affine_hull = deprecated_function_alias(29326, affine_hull_projection) + def _test_affine_hull_projection(self, tester=None, **options): + """ + Run tests on the method :meth:`.affine_hull_projection`. + + TESTS:: + + sage: D = polytopes.dodecahedron() + sage: D.facets()[0].as_polyhedron()._test_affine_hull_projection() + """ + if tester is None: + tester = self._tester(**options) + + if self.n_vertices() > 30 or self.n_facets() > 30 or self.dim() > 6: + # Avoid very long doctests. + return + + data_sets = [None]*4 + data_sets[0] = self.affine_hull_projection(return_all_data=True) + data_sets[1] = self.affine_hull_projection(return_all_data=True, + orthogonal=True, + extend=True) + data_sets[2] = self.affine_hull_projection(return_all_data=True, + orthonormal=True, + extend=True) + data_sets[3] = self.affine_hull_projection(return_all_data=True, + orthonormal=True, + extend=True, + minimal=True) + + for i, data in enumerate(data_sets): + M = data['projection_map'][0].matrix().transpose() + tester.assertEqual(self.linear_transformation(M, new_base_ring=M.base_ring()) + + data['projection_map'][1], + data['polyhedron']) + + M = data['section_map'][0].matrix().transpose() + tester.assertEqual(data['polyhedron'].linear_transformation(M) + + data['section_map'][1], + self) + if i == 0: + tester.assertEqual(data['polyhedron'].base_ring(), self.base_ring()) + else: + # Test whether the map is orthogonal. + M = data['projection_map'][0].matrix() + tester.assertTrue((M.transpose() * M).is_diagonal()) + if i > 1: + # Test whether the map is orthonormal. + tester.assertTrue((M.transpose() * M).is_one()) + if i == 3: + # Test that the extension is indeed minimal. + tester.assertFalse(data['polyhedron'].base_ring() is AA) + def _polymake_init_(self): """ Return a polymake "Polytope" object corresponding to ``self``. From 69ceebcca7df21e39a4ab1bb719cdfaea9b6986d Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Thu, 15 Apr 2021 21:38:53 +0200 Subject: [PATCH 293/406] base extend in test; only run defined tests --- src/sage/geometry/polyhedron/base.py | 33 ++++++++++++++++++---------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 2ffccf9e70b..6eeebf88d2f 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -10412,22 +10412,29 @@ def _test_affine_hull_projection(self, tester=None, **options): if tester is None: tester = self._tester(**options) + if self.n_vertices() == 0: + # Does not work for the empty polyhedron. + return + if self.n_vertices() > 30 or self.n_facets() > 30 or self.dim() > 6: # Avoid very long doctests. return data_sets = [None]*4 data_sets[0] = self.affine_hull_projection(return_all_data=True) - data_sets[1] = self.affine_hull_projection(return_all_data=True, - orthogonal=True, - extend=True) - data_sets[2] = self.affine_hull_projection(return_all_data=True, - orthonormal=True, - extend=True) - data_sets[3] = self.affine_hull_projection(return_all_data=True, - orthonormal=True, - extend=True, - minimal=True) + if self.is_compact(): + data_sets[1] = self.affine_hull_projection(return_all_data=True, + orthogonal=True, + extend=True) + data_sets[2] = self.affine_hull_projection(return_all_data=True, + orthonormal=True, + extend=True) + data_sets[3] = self.affine_hull_projection(return_all_data=True, + orthonormal=True, + extend=True, + minimal=True) + else: + data_sets = data_sets[:1] for i, data in enumerate(data_sets): M = data['projection_map'][0].matrix().transpose() @@ -10436,9 +10443,13 @@ def _test_affine_hull_projection(self, tester=None, **options): data['polyhedron']) M = data['section_map'][0].matrix().transpose() + if M.base_ring() is AA: + self_extend = self.change_ring(AA) + else: + self_extend = self tester.assertEqual(data['polyhedron'].linear_transformation(M) + data['section_map'][1], - self) + self_extend) if i == 0: tester.assertEqual(data['polyhedron'].base_ring(), self.base_ring()) else: From ba62e668a1714f0629bc4a27ad7776448d58403e Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 15 Apr 2021 16:06:54 -0700 Subject: [PATCH 294/406] Polyhedron_base.affine_hull_projection: Fix for 0-dimensional case --- src/sage/geometry/polyhedron/base.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 6eeebf88d2f..d0ac941f2c1 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -10378,7 +10378,8 @@ def affine_hull_projection(self, as_polyhedron=None, as_affine_map=False, orthog M = matrix(gens) pivots = M.pivots() - A = matrix(self.base_ring(), [[1 if j == i else 0 for j in range(self.ambient_dim())] for i in pivots]) + A = matrix(self.base_ring(), len(pivots), self.ambient_dim(), + [[1 if j == i else 0 for j in range(self.ambient_dim())] for i in pivots]) if as_affine_map: image_translation = vector(self.base_ring(), self.dim()) L = linear_transformation(A, side='right') @@ -10387,7 +10388,9 @@ def affine_hull_projection(self, as_polyhedron=None, as_affine_map=False, orthog result['polyhedron'] = A*self if return_all_data: E = M.echelon_form() - L_section = linear_transformation(matrix([E[i] for i in pivots]).transpose(), side='right') + L_section = linear_transformation(matrix(len(pivots), self.ambient_dim(), + [E[i] for i in pivots]).transpose(), + side='right') result['section_map'] = (L_section, v0 - L_section(L(v0) + image_translation)) # assemble result From 934a33f9599ed8328027eca567a17f48f23e7c2c Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 15 Apr 2021 16:19:30 -0700 Subject: [PATCH 295/406] Polyhedron_base.affine_hull_projection: Error for the empty polyhedron --- src/sage/geometry/polyhedron/base.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index d0ac941f2c1..69109057b2d 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -10312,6 +10312,9 @@ def affine_hull_projection(self, as_polyhedron=None, as_affine_map=False, orthog result = {} + if self.is_empty(): + raise ValueError('affine hull projection of an empty polyhedron is undefined') + # handle trivial full-dimensional case if self.ambient_dim() == self.dim(): result['polyhedron'] = self @@ -10415,8 +10418,8 @@ def _test_affine_hull_projection(self, tester=None, **options): if tester is None: tester = self._tester(**options) - if self.n_vertices() == 0: - # Does not work for the empty polyhedron. + if self.is_empty(): + # Undefined, nothing to test return if self.n_vertices() > 30 or self.n_facets() > 30 or self.dim() > 6: From 9629620ed8e4e824adace4a7f867059961fce877 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 15 Apr 2021 16:47:28 -0700 Subject: [PATCH 296/406] Polyhedron_base.affine_hull_projection: Fix up use of echelong form --- src/sage/geometry/polyhedron/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 69109057b2d..49e6c069fb2 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -10392,7 +10392,7 @@ def affine_hull_projection(self, as_polyhedron=None, as_affine_map=False, orthog if return_all_data: E = M.echelon_form() L_section = linear_transformation(matrix(len(pivots), self.ambient_dim(), - [E[i] for i in pivots]).transpose(), + [E[i] for i in range(len(pivots))]).transpose(), side='right') result['section_map'] = (L_section, v0 - L_section(L(v0) + image_translation)) From 8c24c5c3590df6dbdf033e7a3c936941e4ff2caa Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 15 Apr 2021 18:25:18 -0700 Subject: [PATCH 297/406] Polyhedron_base.affine_hull_projection: Remove last occurrence of 'parametric_form' --- src/sage/geometry/polyhedron/base.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 49e6c069fb2..12ab9a28579 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -10183,8 +10183,7 @@ def affine_hull_projection(self, as_polyhedron=None, as_affine_map=False, orthog Return all data:: sage: data = S.affine_hull_projection(orthogonal=True, return_all_data=True); data - {'parametric_form': ((1, 0, 0), ((-1, 1, 0), (-1, 0, 1))), - 'polyhedron': A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 3 vertices, + {'polyhedron': A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 3 vertices, 'projection_map': (Vector space morphism represented by the matrix: [ -1 -1/2] [ 1 -1/2] @@ -10333,8 +10332,6 @@ def affine_hull_projection(self, as_polyhedron=None, as_affine_map=False, orthog v0 = affine_basis[0].vector() # We implicitly translate the first vertex of the affine basis to zero. vi = tuple(v.vector() - v0 for v in affine_basis[1:]) - if return_all_data: - result['parametric_form'] = (v0, vi) M = matrix(self.base_ring(), self.dim(), self.ambient_dim(), vi) # Switch base_ring to AA if necessary, From ea877d9ee384bfa2794af3c20a070e7e42bb3f7f Mon Sep 17 00:00:00 2001 From: Sebastian Oehms Date: Fri, 16 Apr 2021 08:22:34 +0200 Subject: [PATCH 298/406] 29009: initial version --- src/sage/rings/integer.pyx | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/sage/rings/integer.pyx b/src/sage/rings/integer.pyx index a2f913df7fd..a268f293551 100644 --- a/src/sage/rings/integer.pyx +++ b/src/sage/rings/integer.pyx @@ -3468,12 +3468,21 @@ cdef class Integer(sage.structure.element.EuclideanDomainElement): sage: 5.quo_rem(2/3) (15/2, 0) + Check that :trac:`29009` is fixed: + + sage: divmod(1, sys.maxsize+1r) + (0, 1) + sage: import mpmath + sage: mpmath.mp.prec = 1000 + sage: root = mpmath.findroot(lambda x: x^2 - 3, 2) + sage: len(str(root)) + 301 """ cdef Integer q = PY_NEW(Integer) cdef Integer r = PY_NEW(Integer) cdef long d, res - if type(other) is int: + if is_small_python_int(other): d = PyInt_AS_LONG(other) if d > 0: mpz_fdiv_qr_ui(q.value, r.value, self.value, d) From 9f5560ae5412fb63f442d80ad85ac1e72a5b17b8 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Fri, 16 Apr 2021 11:43:39 +0200 Subject: [PATCH 299/406] initialize empty matrix after trivial multiplication --- src/sage/matrix/matrix_double_dense.pyx | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/sage/matrix/matrix_double_dense.pyx b/src/sage/matrix/matrix_double_dense.pyx index 848d4aa6b69..37d11743955 100644 --- a/src/sage/matrix/matrix_double_dense.pyx +++ b/src/sage/matrix/matrix_double_dense.pyx @@ -371,14 +371,25 @@ cdef class Matrix_double_dense(Matrix_dense): sage: matrix.identity(QQ, 4) * matrix(RDF, 4, 0) [] + + Check that an empty matrix is initialized correctly; see :trac:`27366`: + + sage: A = matrix(RDF, 3, 0) + sage: A*A.transpose() + [0.0 0.0 0.0] + [0.0 0.0 0.0] + [0.0 0.0 0.0] """ if self._ncols != right._nrows: raise IndexError("Number of columns of self must equal number of rows of right") + cdef Matrix_double_dense M, _right, _left + if self._nrows == 0 or self._ncols == 0 or right._nrows == 0 or right._ncols == 0: - return self._new(self._nrows, right._ncols) + M = self._new(self._nrows, right._ncols) + M._matrix_numpy.fill(0) + return M - cdef Matrix_double_dense M, _right, _left M = self._new(self._nrows, right._ncols) _right = right _left = self From f9faa025d7598c07549691ef9c6c922d31caeb8d Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Fri, 16 Apr 2021 11:47:00 +0200 Subject: [PATCH 300/406] minimal extension only avoid AA if the base ring is not already AA --- src/sage/geometry/polyhedron/base.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 12ab9a28579..0cb6e9bf162 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -10403,7 +10403,7 @@ def affine_hull_projection(self, as_polyhedron=None, as_affine_map=False, orthog affine_hull = deprecated_function_alias(29326, affine_hull_projection) - def _test_affine_hull_projection(self, tester=None, **options): + def _test_affine_hull_projection(self, tester=None, verbose=False, **options): """ Run tests on the method :meth:`.affine_hull_projection`. @@ -10440,6 +10440,8 @@ def _test_affine_hull_projection(self, tester=None, **options): data_sets = data_sets[:1] for i, data in enumerate(data_sets): + if verbose: + print("Running test number {}".format(i)) M = data['projection_map'][0].matrix().transpose() tester.assertEqual(self.linear_transformation(M, new_base_ring=M.base_ring()) + data['projection_map'][1], @@ -10464,7 +10466,8 @@ def _test_affine_hull_projection(self, tester=None, **options): tester.assertTrue((M.transpose() * M).is_one()) if i == 3: # Test that the extension is indeed minimal. - tester.assertFalse(data['polyhedron'].base_ring() is AA) + if self.base_ring() is not AA: + tester.assertFalse(data['polyhedron'].base_ring() is AA) def _polymake_init_(self): """ From e7d6c32d5a7d76ef7e337978a9db971d7fd1e89a Mon Sep 17 00:00:00 2001 From: Sebastian Oehms Date: Fri, 16 Apr 2021 12:56:23 +0200 Subject: [PATCH 301/406] 29009: add comment to doctest --- src/sage/rings/integer.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/integer.pyx b/src/sage/rings/integer.pyx index a268f293551..56cf1161d0e 100644 --- a/src/sage/rings/integer.pyx +++ b/src/sage/rings/integer.pyx @@ -3470,7 +3470,7 @@ cdef class Integer(sage.structure.element.EuclideanDomainElement): Check that :trac:`29009` is fixed: - sage: divmod(1, sys.maxsize+1r) + sage: divmod(1, sys.maxsize+1r) # should not raise OverflowError: Python int too large to convert to C long (0, 1) sage: import mpmath sage: mpmath.mp.prec = 1000 From 99297d015a5f5d7e9bcd9cb6562a96998cca9336 Mon Sep 17 00:00:00 2001 From: Clemens Heuberger Date: Sun, 18 Apr 2021 19:25:26 +0200 Subject: [PATCH 302/406] Trac #21295: Fix typo in docstring --- src/sage/combinat/recognizable_series.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/combinat/recognizable_series.py b/src/sage/combinat/recognizable_series.py index 264caa02ab9..9d48dd75ac2 100644 --- a/src/sage/combinat/recognizable_series.py +++ b/src/sage/combinat/recognizable_series.py @@ -953,7 +953,7 @@ def __normalize__(cls, Normalizes the input in order to ensure a unique representation. - For more information see :class:`ReconizableSeriesSpace`. + For more information see :class:`RecognizableSeriesSpace`. TESTS:: From 3b6f64407c5a76452f652a23878cd2f973b96098 Mon Sep 17 00:00:00 2001 From: Clemens Heuberger Date: Sun, 18 Apr 2021 19:25:54 +0200 Subject: [PATCH 303/406] Trac #21295: Fix ReST reference syntax --- src/sage/combinat/recognizable_series.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/combinat/recognizable_series.py b/src/sage/combinat/recognizable_series.py index 9d48dd75ac2..c791d2886b2 100644 --- a/src/sage/combinat/recognizable_series.py +++ b/src/sage/combinat/recognizable_series.py @@ -1010,7 +1010,7 @@ def __normalize__(cls, @experimental(trac_number=21202) def __init__(self, coefficients, indices, category): r""" - See :class`RecognizableSeriesSpace` for details. + See :class:`RecognizableSeriesSpace` for details. INPUT: From 3e6410bfe7722e951b51e498d8e9b01883414db9 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Mon, 13 Jul 2020 18:12:25 +1000 Subject: [PATCH 304/406] Fixing perp bisector when coordinates out of order. --- .../hyperbolic_space/hyperbolic_geodesic.py | 40 ++++++++++++++++++- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/src/sage/geometry/hyperbolic_space/hyperbolic_geodesic.py b/src/sage/geometry/hyperbolic_space/hyperbolic_geodesic.py index 8f4e8503d8f..5d06bc7d723 100644 --- a/src/sage/geometry/hyperbolic_space/hyperbolic_geodesic.py +++ b/src/sage/geometry/hyperbolic_space/hyperbolic_geodesic.py @@ -1397,12 +1397,48 @@ def perpendicular_bisector(self): # UHP ... ValueError: the length must be finite - """ + TESTS: + + Check the result is independent of the order:: + + sage: UHP = HyperbolicPlane().UHP() + sage: g = UHP.get_geodesic(1+I,2+0.5*I) + sage: h = g.perpendicular_bisector() + sage: c = lambda x: x.coordinates() + sage: bool(c(g.intersection(h)[0]) - c(g.midpoint()) < 10**-9) + True + + sage: g = UHP.get_geodesic(2+0.5*I,1+I) + sage: h = g.perpendicular_bisector() + sage: c = lambda x: x.coordinates() + sage: bool(c(g.intersection(h)[0]) - c(g.midpoint()) < 10**-9) + True + :: + + sage: g = UHP.get_geodesic(2+I,2+0.5*I) + sage: h = g.perpendicular_bisector() + sage: c = lambda x: x.coordinates() + sage: bool(c(g.intersection(h)[0]) - c(g.midpoint()) < 10**-9) + True + + sage: g = UHP.get_geodesic(2+0.5*I,2+I) + sage: h = g.perpendicular_bisector() + sage: c = lambda x: x.coordinates() + sage: bool(c(g.intersection(h)[0]) - c(g.midpoint()) < 10**-9) + True + """ if self.length() == infinity: raise ValueError("the length must be finite") start = self._start.coordinates() - d = self._model._dist_points(start, self._end.coordinates()) / 2 + end = self._end.coordinates() + # Since the complete geodesic p1 -> p2 always returns p1 < p2, + # we need to swap start and end when that happens + if ((real(start - end) > EPSILON) or + (abs(real(start - end)) < EPSILON and + imag(start - end) > 0)): + start, end = end, start + d = self._model._dist_points(start, end) / 2 S = self.complete()._to_std_geod(start) T1 = matrix([[exp(d/2), 0], [0, exp(-d/2)]]) s2 = sqrt(2) * 0.5 From d0ee202747c7cc2afe01c40a940eef8ad7f1b396 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Wed, 15 Jul 2020 12:33:05 +1000 Subject: [PATCH 305/406] Addressing reviewer comments. --- .../hyperbolic_space/hyperbolic_geodesic.py | 49 +++++++------------ 1 file changed, 17 insertions(+), 32 deletions(-) diff --git a/src/sage/geometry/hyperbolic_space/hyperbolic_geodesic.py b/src/sage/geometry/hyperbolic_space/hyperbolic_geodesic.py index 5d06bc7d723..65cca312b9c 100644 --- a/src/sage/geometry/hyperbolic_space/hyperbolic_geodesic.py +++ b/src/sage/geometry/hyperbolic_space/hyperbolic_geodesic.py @@ -1399,49 +1399,34 @@ def perpendicular_bisector(self): # UHP TESTS: - Check the result is independent of the order:: - - sage: UHP = HyperbolicPlane().UHP() - sage: g = UHP.get_geodesic(1+I,2+0.5*I) - sage: h = g.perpendicular_bisector() - sage: c = lambda x: x.coordinates() - sage: bool(c(g.intersection(h)[0]) - c(g.midpoint()) < 10**-9) - True - - sage: g = UHP.get_geodesic(2+0.5*I,1+I) - sage: h = g.perpendicular_bisector() - sage: c = lambda x: x.coordinates() - sage: bool(c(g.intersection(h)[0]) - c(g.midpoint()) < 10**-9) - True - - :: - - sage: g = UHP.get_geodesic(2+I,2+0.5*I) - sage: h = g.perpendicular_bisector() - sage: c = lambda x: x.coordinates() - sage: bool(c(g.intersection(h)[0]) - c(g.midpoint()) < 10**-9) - True - - sage: g = UHP.get_geodesic(2+0.5*I,2+I) - sage: h = g.perpendicular_bisector() - sage: c = lambda x: x.coordinates() - sage: bool(c(g.intersection(h)[0]) - c(g.midpoint()) < 10**-9) - True + Check the result is independent of the order (:trac:`29936`):: + + sage: def works_both_ways(a, b): + ....: UHP = HyperbolicPlane().UHP() + ....: g = UHP.get_geodesic(a, b) + ....: h = UHP.get_geodesic(b, a) + ....: p = g.perpendicular_bisector() + ....: q = h.perpendicular_bisector() + ....: c = lambda x: x.coordinates() + ....: assert bool(c(g.intersection(p)[0]) - c(g.midpoint()) < 1e-9) + ....: assert bool(c(h.intersection(q)[0]) - c(g.midpoint()) < 1e-9) + sage: works_both_ways(1 + I, 2 + 0.5*I) + sage: works_both_ways(2 + I, 2 + 0.5*I) """ if self.length() == infinity: raise ValueError("the length must be finite") start = self._start.coordinates() end = self._end.coordinates() - # Since the complete geodesic p1 -> p2 always returns p1 < p2, - # we need to swap start and end when that happens + # The complete geodesic p1 -> p2 always returns p1 < p2, + # so we might need to swap start and end if ((real(start - end) > EPSILON) or (abs(real(start - end)) < EPSILON and imag(start - end) > 0)): start, end = end, start - d = self._model._dist_points(start, end) / 2 S = self.complete()._to_std_geod(start) + d = self._model._dist_points(start, end) / 2 T1 = matrix([[exp(d/2), 0], [0, exp(-d/2)]]) - s2 = sqrt(2) * 0.5 + s2 = sqrt(0.5) T2 = matrix([[s2, -s2], [s2, s2]]) isom_mtrx = S.inverse() * (T1 * T2) * S # We need to clean this matrix up. From 397ae3aedfcbb914079997997aa8829759d084b1 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 18 Apr 2021 18:09:26 -0700 Subject: [PATCH 306/406] src/sage/geometry/polyhedron/base.py: Update copyright according to git blame -w --date=format:%Y src/sage/geometry/polyhedron/base.py | sort -k2 --- src/sage/geometry/polyhedron/base.py | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 0cb6e9bf162..4b71f312ae7 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -3,10 +3,25 @@ """ # **************************************************************************** -# Copyright (C) 2008 Marshall Hampton -# Copyright (C) 2011 Volker Braun -# Copyright (C) 2015 Jean-Philippe Labbe -# Copyright (C) 2020 Jonathan Kliem +# Copyright (C) 2008-2012 Marshall Hampton +# Copyright (C) 2011-2015 Volker Braun +# Copyright (C) 2012-2018 Frederic Chapoton +# Copyright (C) 2013 Andrey Novoseltsev +# Copyright (C) 2014-2017 Moritz Firsching +# Copyright (C) 2014-2019 Thierry Monteil +# Copyright (C) 2015 Nathann Cohen +# Copyright (C) 2015-2017 Jeroen Demeyer +# Copyright (C) 2015-2017 Vincent Delecroix +# Copyright (C) 2015-2018 Dima Pasechnik +# Copyright (C) 2015-2020 Jean-Philippe Labbe +# Copyright (C) 2015-2021 Matthias Koeppe +# Copyright (C) 2016-2019 Daniel Krenn +# Copyright (C) 2017 Marcelo Forets +# Copyright (C) 2017-2018 Mark Bell +# Copyright (C) 2019 Julian Ritter +# Copyright (C) 2019-2020 Laith Rastanawi +# Copyright (C) 2019-2020 Sophia Elia +# Copyright (C) 2019-2021 Jonathan Kliem # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by From 2c1e2bd4a8fd0d632cc66a523def3b129062bd44 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 18 Apr 2021 18:40:17 -0700 Subject: [PATCH 307/406] Polyhedron_base.affine_hull_projection: Return a dataclass instance, not a dictionary --- src/sage/geometry/polyhedron/base.py | 157 +++++++++++++++------------ 1 file changed, 88 insertions(+), 69 deletions(-) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 4b71f312ae7..c572362ad67 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -31,7 +31,10 @@ # **************************************************************************** +from dataclasses import dataclass +from typing import Any import itertools + from sage.structure.element import Element, coerce_binop, is_Vector, is_Matrix from sage.structure.richcmp import rich_to_bool, op_NE from sage.cpython.string import bytes_to_str @@ -8182,10 +8185,10 @@ def volume(self, measure='ambient', engine='auto', **kwds): if engine == 'normaliz': return self._volume_normaliz(measure='euclidean') # use an orthogonal transformation, which preserves volume up to a factor provided by the transformation matrix - affine_hull = self.affine_hull_projection(orthogonal=True, as_polyhedron=True, as_affine_map=True) - A = affine_hull['projection_map'][0].matrix() + affine_hull_data = self.affine_hull_projection(orthogonal=True, as_polyhedron=True, as_affine_map=True) + A = affine_hull_data.projection_linear_map.matrix() Adet = (A.transpose() * A).det() - scaled_volume = self.affine_hull_projection(orthogonal=True).volume(measure='ambient', engine=engine, **kwds) + scaled_volume = affine_hull_data.polyhedron.volume(measure='ambient', engine=engine, **kwds) if Adet.is_square(): sqrt_Adet = Adet.sqrt() else: @@ -9877,6 +9880,14 @@ def _test_is_combinatorially_isomorphic(self, tester=None, **options): if self.n_vertices(): tester.assertTrue(self.is_combinatorially_isomorphic(self + self.center(), algorithm='face_lattice')) + @dataclass + class AffineHullProjectionData: + polyhedron: Any = None + projection_linear_map: Any = None + projection_translation: Any = None + section_linear_map: Any = None + section_translation: Any = None + @cached_method def affine_hull_projection(self, as_polyhedron=None, as_affine_map=False, orthogonal=False, orthonormal=False, extend=False, minimal=False, return_all_data=False): @@ -9912,14 +9923,14 @@ def affine_hull_projection(self, as_polyhedron=None, as_affine_map=False, orthog ``A(v)+b``. If both ``as_polyhedron`` and ``as_affine_map`` are set, then - both are returned, encapsulated in a dictionary. + both are returned, encapsulated in an instance of ``AffineHullProjectionData``. - ``return_all_data`` -- (boolean, default ``False``) If set, then ``as_polyhedron`` and ``as_affine_map`` will set (possibly overridden) and additional (internal) data concerning the transformation is returned. Everything is encapsulated - in a dictionary in this case. + in an instance of ``AffineHullProjectionData`` in this case. - ``orthogonal`` -- boolean (default: ``False``); if ``True``, provide an orthogonal transformation. @@ -9941,10 +9952,11 @@ def affine_hull_projection(self, as_polyhedron=None, as_affine_map=False, orthog A full-dimensional polyhedron or an affine transformation, depending on the parameters ``as_polyhedron`` and ``as_affine_map``, - or a dictionary containing all data (parameter ``return_all_data``). + or an instance of ``AffineHullProjectionData`` containing all data + (parameter ``return_all_data``). - In case the output is a dictionary, the following entries might - be included: + If the output is an instance of ``AffineHullProjectionData``, the + following fields may be set: - ``polyhedron`` -- the projection of the original polyhedron @@ -9957,7 +9969,7 @@ def affine_hull_projection(self, as_polyhedron=None, as_affine_map=False, orthog It maps the codomain of ``affine_map`` to the affine hull of ``self``. It is a right inverse of ``projection_map``. - Note that all entries of this dictionary are compatible. + Note that all of these data are compatible. .. TODO:: @@ -10186,56 +10198,58 @@ def affine_hull_projection(self, as_polyhedron=None, as_affine_map=False, orthog sage: data = S.affine_hull_projection(orthogonal=True, ....: as_polyhedron=True, ....: as_affine_map=True); data - {'polyhedron': A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 3 vertices, - 'projection_map': (Vector space morphism represented by the matrix: - [ -1 -1/2] - [ 1 -1/2] - [ 0 1] - Domain: Vector space of dimension 3 over Rational Field - Codomain: Vector space of dimension 2 over Rational Field, - (1, 1/2))} + Polyhedron_base.AffineHullProjectionData(... + polyhedron=A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 3 vertices, + projection_linear_map=Vector space morphism represented by the matrix: + [ -1 -1/2] + [ 1 -1/2] + [ 0 1] + Domain: Vector space of dimension 3 over Rational Field + Codomain: Vector space of dimension 2 over Rational Field, + projection_translation=(1, 1/2), + section_linear_map=None, + section_translation=None) Return all data:: sage: data = S.affine_hull_projection(orthogonal=True, return_all_data=True); data - {'polyhedron': A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 3 vertices, - 'projection_map': (Vector space morphism represented by the matrix: - [ -1 -1/2] - [ 1 -1/2] - [ 0 1] - Domain: Vector space of dimension 3 over Rational Field - Codomain: Vector space of dimension 2 over Rational Field, - (1, 1/2)), - 'section_map': (Vector space morphism represented by the matrix: - [-1/2 1/2 0] - [-1/3 -1/3 2/3] - Domain: Vector space of dimension 2 over Rational Field - Codomain: Vector space of dimension 3 over Rational Field, - (1, 0, 0))} + Polyhedron_base.AffineHullProjectionData(... + polyhedron=A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 3 vertices, + projection_linear_map=Vector space morphism represented by the matrix: + [ -1 -1/2] + [ 1 -1/2] + [ 0 1] + Domain: Vector space of dimension 3 over Rational Field + Codomain: Vector space of dimension 2 over Rational Field, + projection_translation=(1, 1/2), + section_linear_map=Vector space morphism represented by the matrix: + [-1/2 1/2 0] + [-1/3 -1/3 2/3] + Domain: Vector space of dimension 2 over Rational Field + Codomain: Vector space of dimension 3 over Rational Field, section_translation=(1, 0, 0)) The section map is a right inverse of the projection map:: - sage: data['polyhedron'].linear_transformation(data['section_map'][0].matrix().transpose()) + data['section_map'][1] == S + sage: data.polyhedron.linear_transformation(data.section_linear_map.matrix().transpose()) + data.section_translation == S True Same without ``orthogonal=True``:: sage: data = S.affine_hull_projection(return_all_data=True); data - {'polyhedron': A 2-dimensional polyhedron in ZZ^2 defined as the convex hull of 3 vertices, - 'projection_map': (Vector space morphism represented by the matrix: - [1 0] - [0 1] - [0 0] - Domain: Vector space of dimension 3 over Rational Field - Codomain: Vector space of dimension 2 over Rational Field, - (0, 0)), - 'section_map': (Vector space morphism represented by the matrix: - [ 1 0 -1] - [ 0 1 -1] - Domain: Vector space of dimension 2 over Rational Field - Codomain: Vector space of dimension 3 over Rational Field, - (0, 0, 1))} - sage: data['polyhedron'].linear_transformation(data['section_map'][0].matrix().transpose()) + data['section_map'][1] == S + Polyhedron_base.AffineHullProjectionData(... + polyhedron=A 2-dimensional polyhedron in ZZ^2 defined as the convex hull of 3 vertices, + projection_linear_map=Vector space morphism represented by the matrix: + [1 0] + [0 1] + [0 0] + Domain: Vector space of dimension 3 over Rational Field + Codomain: Vector space of dimension 2 over Rational Field, projection_translation=(0, 0), + section_linear_map=Vector space morphism represented by the matrix: + [ 1 0 -1] + [ 0 1 -1] + Domain: Vector space of dimension 2 over Rational Field + Codomain: Vector space of dimension 3 over Rational Field, section_translation=(0, 0, 1)) + sage: data.polyhedron.linear_transformation(data.section_linear_map.matrix().transpose()) + data.section_translation == S True :: @@ -10324,21 +10338,22 @@ def affine_hull_projection(self, as_polyhedron=None, as_affine_map=False, orthog as_polyhedron = True as_affine_map = True - result = {} + result = self.AffineHullProjectionData() if self.is_empty(): raise ValueError('affine hull projection of an empty polyhedron is undefined') # handle trivial full-dimensional case if self.ambient_dim() == self.dim(): - result['polyhedron'] = self - if as_affine_map or return_all_data: + if as_polyhedron: + result.polyhedron = self + if as_affine_map: identity = linear_transformation(matrix(self.base_ring(), self.dim(), self.dim(), self.base_ring().one())) - result['projection_map'] = (identity, self.ambient_space().zero()) - result['section_map'] = (identity, self.ambient_space().zero()) + result.projection_linear_map = result.section_linear_map = identity + result.projection_translation = result.section_translation = self.ambient_space().zero() elif orthogonal or orthonormal: # see TODO if not self.is_compact(): @@ -10372,12 +10387,14 @@ def affine_hull_projection(self, as_polyhedron=None, as_affine_map=False, orthog # Also, if the new base ring is ``AA``, we want to avoid computing the incidence matrix in that ring. # ``convert=True`` takes care of the case, where there might be no coercion (``AA`` and quadratic field). if as_polyhedron: - result['polyhedron'] = self.linear_transformation(A, new_base_ring=A.base_ring()) + image_translation + result.polyhedron = self.linear_transformation(A, new_base_ring=A.base_ring()) + image_translation if as_affine_map: - result['projection_map'] = (L, image_translation) + result.projection_linear_map = L + result.projection_translation = image_translation if return_all_data: L_dagger = linear_transformation(A.transpose() * (A * A.transpose()).inverse(), side='right') - result['section_map'] = (L_dagger, v0.change_ring(A.base_ring())) + result.section_linear_map = L_dagger + result.section_translation = v0.change_ring(A.base_ring()) else: # translate one vertex to the origin v0 = self.vertices()[0].vector() @@ -10398,23 +10415,25 @@ def affine_hull_projection(self, as_polyhedron=None, as_affine_map=False, orthog if as_affine_map: image_translation = vector(self.base_ring(), self.dim()) L = linear_transformation(A, side='right') - result['projection_map'] = (L, image_translation) + result.projection_linear_map = L + result.projection_translation = image_translation if as_polyhedron: - result['polyhedron'] = A*self + result.polyhedron = A*self if return_all_data: E = M.echelon_form() L_section = linear_transformation(matrix(len(pivots), self.ambient_dim(), [E[i] for i in range(len(pivots))]).transpose(), side='right') - result['section_map'] = (L_section, v0 - L_section(L(v0) + image_translation)) + result.section_linear_map = L_section + result.section_translation = v0 - L_section(L(v0) + image_translation) # assemble result if return_all_data or (as_polyhedron and as_affine_map): return result elif as_affine_map: - return result['projection_map'] + return (result.projection_linear_map, result.projection_translation) else: - return result['polyhedron'] + return result.polyhedron affine_hull = deprecated_function_alias(29326, affine_hull_projection) @@ -10457,24 +10476,24 @@ def _test_affine_hull_projection(self, tester=None, verbose=False, **options): for i, data in enumerate(data_sets): if verbose: print("Running test number {}".format(i)) - M = data['projection_map'][0].matrix().transpose() + M = data.projection_linear_map.matrix().transpose() tester.assertEqual(self.linear_transformation(M, new_base_ring=M.base_ring()) - + data['projection_map'][1], - data['polyhedron']) + + data.projection_translation, + data.polyhedron) - M = data['section_map'][0].matrix().transpose() + M = data.section_linear_map.matrix().transpose() if M.base_ring() is AA: self_extend = self.change_ring(AA) else: self_extend = self - tester.assertEqual(data['polyhedron'].linear_transformation(M) - + data['section_map'][1], + tester.assertEqual(data.polyhedron.linear_transformation(M) + + data.section_translation, self_extend) if i == 0: - tester.assertEqual(data['polyhedron'].base_ring(), self.base_ring()) + tester.assertEqual(data.polyhedron.base_ring(), self.base_ring()) else: # Test whether the map is orthogonal. - M = data['projection_map'][0].matrix() + M = data.projection_linear_map.matrix() tester.assertTrue((M.transpose() * M).is_diagonal()) if i > 1: # Test whether the map is orthonormal. @@ -10482,7 +10501,7 @@ def _test_affine_hull_projection(self, tester=None, verbose=False, **options): if i == 3: # Test that the extension is indeed minimal. if self.base_ring() is not AA: - tester.assertFalse(data['polyhedron'].base_ring() is AA) + tester.assertFalse(data.polyhedron.base_ring() is AA) def _polymake_init_(self): """ From 57fd3e185cf891c27fe93cde9fe9cabb9290468d Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 18 Apr 2021 20:07:33 -0700 Subject: [PATCH 308/406] Fixup doctest formatting --- src/sage/geometry/polyhedron/base.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index c572362ad67..af666d1e1aa 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -10198,8 +10198,8 @@ def affine_hull_projection(self, as_polyhedron=None, as_affine_map=False, orthog sage: data = S.affine_hull_projection(orthogonal=True, ....: as_polyhedron=True, ....: as_affine_map=True); data - Polyhedron_base.AffineHullProjectionData(... - polyhedron=A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 3 vertices, + Polyhedron_base.AffineHullProjectionData(polyhedron=A 2-dimensional polyhedron in QQ^2 + defined as the convex hull of 3 vertices, projection_linear_map=Vector space morphism represented by the matrix: [ -1 -1/2] [ 1 -1/2] @@ -10213,8 +10213,8 @@ def affine_hull_projection(self, as_polyhedron=None, as_affine_map=False, orthog Return all data:: sage: data = S.affine_hull_projection(orthogonal=True, return_all_data=True); data - Polyhedron_base.AffineHullProjectionData(... - polyhedron=A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 3 vertices, + Polyhedron_base.AffineHullProjectionData(polyhedron=A 2-dimensional polyhedron in QQ^2 + defined as the convex hull of 3 vertices, projection_linear_map=Vector space morphism represented by the matrix: [ -1 -1/2] [ 1 -1/2] @@ -10236,8 +10236,8 @@ def affine_hull_projection(self, as_polyhedron=None, as_affine_map=False, orthog Same without ``orthogonal=True``:: sage: data = S.affine_hull_projection(return_all_data=True); data - Polyhedron_base.AffineHullProjectionData(... - polyhedron=A 2-dimensional polyhedron in ZZ^2 defined as the convex hull of 3 vertices, + Polyhedron_base.AffineHullProjectionData(polyhedron=A 2-dimensional polyhedron in ZZ^2 + defined as the convex hull of 3 vertices, projection_linear_map=Vector space morphism represented by the matrix: [1 0] [0 1] From dc9384428e76326861deef89d070fe9d183a2db8 Mon Sep 17 00:00:00 2001 From: Tobias Diez Date: Mon, 7 Dec 2020 23:52:28 +0100 Subject: [PATCH 309/406] Partially revert "Allow Sage to work with a system Python 3.6." This reverts commit abb5607710a2a5cc5415423489fa452965cbef68. --- src/sage/combinat/subset.py | 13 ++++--------- src/sage/graphs/views.pyx | 1 - 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/src/sage/combinat/subset.py b/src/sage/combinat/subset.py index 9500636f508..057e5a36a56 100644 --- a/src/sage/combinat/subset.py +++ b/src/sage/combinat/subset.py @@ -353,17 +353,10 @@ def cardinality(self): 8 sage: Subsets(3).cardinality() 8 - """ - return Integer(1) << self._s.cardinality() - - def __len__(self): - r""" - Equivalent to ``self.cardinality()``. TESTS:: - ``__len__`` should return a Python int; in Python 3.7+ this happens - automatically, but not on Python 3.6. + ``__len__`` should return a Python int. sage: S = Subsets(Set([1,2,3])) sage: len(S) @@ -371,7 +364,9 @@ def __len__(self): sage: type(len(S)) is int True """ - return int(self.cardinality()) + return Integer(1) << self._s.cardinality() + + __len__ = cardinality def first(self): """ diff --git a/src/sage/graphs/views.pyx b/src/sage/graphs/views.pyx index 201e89455fa..33e22f3ea27 100644 --- a/src/sage/graphs/views.pyx +++ b/src/sage/graphs/views.pyx @@ -626,7 +626,6 @@ cdef class EdgesView: elif i < 0: return list(self)[i] else: - i = int(i) # For Python < 3.7 where islice doesn't support non-int try: return next(islice(self, i, i + 1, 1)) except StopIteration: From 41f1ab9ed9966a83bf7323f59e558732a5faac40 Mon Sep 17 00:00:00 2001 From: Tobias Diez Date: Tue, 8 Dec 2020 00:01:52 +0100 Subject: [PATCH 310/406] Revert #30576 --- build/bin/sage-site | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/build/bin/sage-site b/build/bin/sage-site index 400782164b3..b99923d1eff 100755 --- a/build/bin/sage-site +++ b/build/bin/sage-site @@ -149,12 +149,7 @@ if [ "$1" = "-docbuild" -o "$1" = "--docbuild" ]; then # Trac #30002: ensure an English locale so that it is possible to # scrape out warnings by pattern matching. - # Trac #30576: But we have to avoid the C locale, which disables - # proper UTF-8 operation in Python 3.6 or older. - LC_ALL=$(locale -a | grep -E -i '^(c|en_us)[-.]utf-?8$' | head -n 1) - LANG=$LC_ALL - export LC_ALL - export LANG + export LANG=C # See #30351: bugs in macOS implementations of openblas/libgopm can cause # docbuild to hang if multiple OpenMP threads are allowed. From 721ede18042cfee9ca044823599346e1ee7420f1 Mon Sep 17 00:00:00 2001 From: Tobias Diez Date: Tue, 8 Dec 2020 00:05:41 +0100 Subject: [PATCH 311/406] Partially revert #30008 --- build/bin/sage-bootstrap-python | 9 --------- 1 file changed, 9 deletions(-) diff --git a/build/bin/sage-bootstrap-python b/build/bin/sage-bootstrap-python index faee444e8dc..c3afc1261af 100755 --- a/build/bin/sage-bootstrap-python +++ b/build/bin/sage-bootstrap-python @@ -30,15 +30,6 @@ fi # is accessible by this python; this is to guard on Cygwin against Pythons # installed somewhere else in Windows. -# Trac #30008: Make it work even if the environment tries to sabotage UTF-8 -# operation in Python 3.0.x-3.6.x by setting LC_ALL=C or similar. - -if [ "$LC_ALL" = "C" -o "$LANG" = "C" -o "$LC_CTYPE" = "C" ]; then - LC_ALL=$(locale -a | grep -E -i '^(c|en_us)[-.]utf-?8$' | head -n 1) - LANG=$LC_ALL - export LC_ALL - export LANG -fi PYTHONS="python python3 python3.8 python3.7 python2.7 python3.6 python2" for PY in $PYTHONS; do From 303a747886ed40a1bca6dfce58dc026874ddb3d1 Mon Sep 17 00:00:00 2001 From: Tobias Diez Date: Tue, 8 Dec 2020 00:12:54 +0100 Subject: [PATCH 312/406] Require at least Python 3.7 --- build/pkgs/python3/spkg-configure.m4 | 2 +- tox.ini | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/build/pkgs/python3/spkg-configure.m4 b/build/pkgs/python3/spkg-configure.m4 index 637d73b74f2..df1a10e4d18 100644 --- a/build/pkgs/python3/spkg-configure.m4 +++ b/build/pkgs/python3/spkg-configure.m4 @@ -1,5 +1,5 @@ SAGE_SPKG_CONFIGURE([python3], [ - m4_pushdef([MIN_VERSION], [3.6.0]) + m4_pushdef([MIN_VERSION], [3.7.0]) m4_pushdef([MIN_NONDEPRECATED_VERSION], [3.7.0]) m4_pushdef([LT_VERSION], [3.10.0]) AC_ARG_WITH([python], diff --git a/tox.ini b/tox.ini index 1d7534148ef..fad615c7299 100644 --- a/tox.ini +++ b/tox.ini @@ -398,7 +398,6 @@ setenv = homebrew-python3.9: CONFIG_CONFIGURE_ARGS_1=--with-system-python3=force --with-python={env:HOMEBREW}/opt/python@3.9/bin/python3 # https://github.com/pypa/manylinux manylinux-standard: CONFIG_CONFIGURE_ARGS_1=--with-system-python3=force --with-python=/opt/python/cp38-cp38/bin/python3 - manylinux-python3.6: CONFIG_CONFIGURE_ARGS_1=--with-system-python3=force --with-python=/opt/python/cp36-cp36m/bin/python3 manylinux-python3.7: CONFIG_CONFIGURE_ARGS_1=--with-system-python3=force --with-python=/opt/python/cp37-cp37m/bin/python3 manylinux-python3.8: CONFIG_CONFIGURE_ARGS_1=--with-system-python3=force --with-python=/opt/python/cp38-cp38/bin/python3 manylinux-python3.9: CONFIG_CONFIGURE_ARGS_1=--with-system-python3=force --with-python=/opt/python/cp39-cp39/bin/python3 From fe6559596aea50ff7f03205900a2526a73f37625 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 19 Apr 2021 12:25:43 -0700 Subject: [PATCH 313/406] Revert "src/bin/sage (-t): Set locale to an UTF-8 locale if LC_CTYPE is C or POSIX, for Python 3.6" This reverts commit c674c57b2bf9c716cd60cd64fba6678648281143. --- src/bin/sage | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/bin/sage b/src/bin/sage index 48a65d2dd2f..241fb3fcab3 100755 --- a/src/bin/sage +++ b/src/bin/sage @@ -954,16 +954,6 @@ fi exec-runtests() { sage_setup export PYTHONIOENCODING="utf-8" # Fix encoding for doctests - # Trac #31191: Some versions of Python 3.6 disable utf-8 even if - # the locale is just "POSIX". This leads to errors in both the - # doctest framework and in various individual doctests. - if locale | grep -q -E -i '^LC_CTYPE="(C|POSIX)"$'; then - # Get an UTF-8 locale if possible. - LC_ALL=$(locale -a | grep -E -i '^(c|en_us)[-.]utf-?8$' | head -n 1) - LANG=$LC_ALL - export LC_ALL - export LANG - fi exec sage-runtests "$@" } From b06731c29501c81a35bba514ce11a957174732f0 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 19 Apr 2021 12:32:48 -0700 Subject: [PATCH 314/406] Remove Python 3.6 support from metadata and documentation --- src/doc/en/installation/source.rst | 3 +-- src/setup.cfg.m4 | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/doc/en/installation/source.rst b/src/doc/en/installation/source.rst index 13074527a07..de0493b0ef2 100644 --- a/src/doc/en/installation/source.rst +++ b/src/doc/en/installation/source.rst @@ -178,8 +178,7 @@ rather than building a Python 3 installation from scratch. Use the configure option ``--without-system-python3`` in case you want Python 3 built from scratch. -Sage will accept versions 3.6.x to 3.9.x; however, support for system python 3.6.x -is deprecated and will be removed in the next development cycle. +Sage will accept versions 3.7.x to 3.9.x. You can also use ``--with-python=/path/to/python3_binary`` to tell Sage to use ``/path/to/python3_binary`` to set up the venv. Note that setting up venv requires diff --git a/src/setup.cfg.m4 b/src/setup.cfg.m4 index 28a23b94211..12039bf1d93 100644 --- a/src/setup.cfg.m4 +++ b/src/setup.cfg.m4 @@ -19,7 +19,6 @@ classifiers = Operating System :: POSIX Operating System :: MacOS :: MacOS X Programming Language :: Python :: 3 :: Only - Programming Language :: Python :: 3.6 Programming Language :: Python :: 3.7 Programming Language :: Python :: 3.8 Programming Language :: Python :: 3.9 @@ -27,7 +26,7 @@ classifiers = Topic :: Scientific/Engineering :: Mathematics [options] -python_requires = >=3.6, <3.10 +python_requires = >=3.7, <3.10 install_requires = sage_conf esyscmd(`sage-get-system-packages install-requires \ From c7df4e6b2b547e60782e71902910c7db4cd8ec33 Mon Sep 17 00:00:00 2001 From: Kwankyu Lee Date: Tue, 20 Apr 2021 10:43:21 +0900 Subject: [PATCH 315/406] Fixes for reviewer comments --- src/sage/coding/ag_code.py | 88 ++++----- ..._code_decoders.py => ag_code_decoders.pyx} | 177 ++++++++++-------- .../coding/tests/differential_ag_code.sobj | Bin 0 -> 9112 bytes src/sage/coding/tests/evaluation_ag_code.sobj | Bin 0 -> 7681 bytes 4 files changed, 146 insertions(+), 119 deletions(-) rename src/sage/coding/{ag_code_decoders.py => ag_code_decoders.pyx} (93%) create mode 100644 src/sage/coding/tests/differential_ag_code.sobj create mode 100644 src/sage/coding/tests/evaluation_ag_code.sobj diff --git a/src/sage/coding/ag_code.py b/src/sage/coding/ag_code.py index ead3dcec588..46039dd6457 100644 --- a/src/sage/coding/ag_code.py +++ b/src/sage/coding/ag_code.py @@ -22,7 +22,7 @@ sage: codes.DifferentialAGCode(pls, G) [8, 3] differential AG code over GF(4) -As well known, the two kinds of AG codes are dual to each other. :: +As is well known, the two kinds of AG codes are dual to each other. :: sage: E = codes.EvaluationAGCode(pls, G) sage: D = codes.DifferentialAGCode(pls, G) @@ -69,8 +69,9 @@ # https://www.gnu.org/licenses/ # **************************************************************************** -from sage.modules.all import vector -from sage.matrix.all import matrix, MatrixSpace +from sage.modules.free_module_element import vector +from sage.matrix.constructor import matrix +from sage.matrix.matrix_space import MatrixSpace from .linear_code import (AbstractLinearCode, LinearCodeGeneratorMatrixEncoder, @@ -206,6 +207,9 @@ def __eq__(self, other): sage: codes.EvaluationAGCode(pls, 5*Q) == codes.EvaluationAGCode(pls, 6*Q) False """ + if self is other: + return True + if not isinstance(other, EvaluationAGCode): return False @@ -434,6 +438,9 @@ def __eq__(self, other): sage: c1 == c2 True """ + if self is other: + return True + if not isinstance(other, DifferentialAGCode): return False @@ -637,50 +644,44 @@ def __init__(self, pls, G, r=1, name=None): subfield = K.subfield(r, name=name) - def cartier_fixed(E): - """ - Compute a basis of the space of differentials in `Omega(E)` fixed - by the Cartier operator. - """ - Grp = E.parent() # group of divisors - - V, fr_V, to_V = E.differential_space() - - EE = Grp(0) - dic = E.dict() - for place in dic: - mul = dic[place] - if mul > 0: - mul = mul // p**r - EE += mul * place + # compute a basis R of the space of differentials in Omega(G - D) + # fixed by the Cartier operator + E = G - D - W, fr_W, to_W = EE.differential_space() + Grp = E.parent() # group of divisors + V, fr_V, to_V = E.differential_space() - a = K.gen() - field_basis = [a**i for i in range(K.degree())] # over prime subfield - basis = E.basis_differential_space() + EE = Grp(0) + dic = E.dict() + for place in dic: + mul = dic[place] + if mul > 0: + mul = mul // p**r + EE += mul * place - m = [] - for w in basis: - for c in field_basis: - cw = F(c) * w # c does not coerce... - carcw = cw - for i in range(r): # apply cartier r times - carcw = carcw.cartier() - m.append([f for e in to_W(carcw - cw) for f in vector(e)]) + W, fr_W, to_W = EE.differential_space() - ker = matrix(m).kernel() + a = K.gen() + field_basis = [a**i for i in range(K.degree())] # over prime subfield + basis = E.basis_differential_space() - R = [] - s = len(field_basis) - ncols = s * len(basis) - for row in ker.basis(): - v = vector([K(row[d:d+s]) for d in range(0,ncols,s)]) - R.append(fr_V(v)) - - return R - - R = cartier_fixed(G - D) + m = [] + for w in basis: + for c in field_basis: + cw = F(c) * w # c does not coerce... + carcw = cw + for i in range(r): # apply cartier r times + carcw = carcw.cartier() + m.append([f for e in to_W(carcw - cw) for f in vector(e)]) + + ker = matrix(m).kernel() + + R = [] + s = len(field_basis) + ncols = s * len(basis) + for row in ker.basis(): + v = vector([K(row[d:d+s]) for d in range(0,ncols,s)]) + R.append(fr_V(v)) # construct a generator matrix m = [] @@ -725,6 +726,9 @@ def __eq__(self, other): sage: c1 == c2 False """ + if self is other: + return True + if not isinstance(other, CartierCode): return False diff --git a/src/sage/coding/ag_code_decoders.py b/src/sage/coding/ag_code_decoders.pyx similarity index 93% rename from src/sage/coding/ag_code_decoders.py rename to src/sage/coding/ag_code_decoders.pyx index b3114d77d71..23490245b9d 100644 --- a/src/sage/coding/ag_code_decoders.py +++ b/src/sage/coding/ag_code_decoders.pyx @@ -57,12 +57,14 @@ # https://www.gnu.org/licenses/ # **************************************************************************** +cimport cython + from sage.rings.all import PolynomialRing from sage.rings.infinity import infinity from sage.rings.function_field.all import FunctionField -from sage.modules.all import vector -from sage.matrix.all import matrix +from sage.modules.free_module_element import vector +from sage.matrix.constructor import matrix from .encoder import Encoder from .decoder import Decoder, DecodingError @@ -96,11 +98,11 @@ class EvaluationAGCodeEncoder(Encoder): Encoder for [8, 5] evaluation AG code over GF(4) Constructing a decoder for an AG code takes much time. So we save the - decoder (and the connected encoder) for later examples:: + decoder (and the connected encoder) by:: - sage: save(dec, DOT_SAGE + 'temp/dec') + sage: save(dec, SAGE_SRC + '/sage/coding/tests/evaluation_ag_code') # not tested - The saved object will be removed after the final example. + and load it for later examples. """ def __init__(self, code, decoder=None): """ @@ -108,7 +110,7 @@ def __init__(self, code, decoder=None): TESTS:: - sage: dec = load(DOT_SAGE + 'temp/dec') + sage: dec = load(SAGE_SRC + '/sage/coding/tests/evaluation_ag_code') sage: enc = dec.connected_encoder() sage: TestSuite(enc).run(skip='_test_pickling') """ @@ -126,7 +128,7 @@ def __hash__(self): TESTS:: - sage: dec = load(DOT_SAGE + 'temp/dec') + sage: dec = load(SAGE_SRC + '/sage/coding/tests/evaluation_ag_code') sage: enc = dec.connected_encoder() sage: {enc: 1} {Encoder for [8, 5] evaluation AG code over GF(4): 1} @@ -156,6 +158,9 @@ def __eq__(self, other): sage: enc1 == enc2 True """ + if self is other: + return True + if not isinstance(other, EvaluationAGCodeEncoder): return False @@ -167,7 +172,7 @@ def _repr_(self): TESTS:: - sage: dec = load(DOT_SAGE + 'temp/dec') + sage: dec = load(SAGE_SRC + '/sage/coding/tests/evaluation_ag_code') sage: enc = dec.connected_encoder() sage: enc Encoder for [8, 5] evaluation AG code over GF(4) @@ -180,7 +185,7 @@ def _latex_(self): TESTS:: - sage: dec = load(DOT_SAGE + 'temp/dec') + sage: dec = load(SAGE_SRC + '/sage/coding/tests/evaluation_ag_code') sage: enc = dec.connected_encoder() sage: latex(enc) \text{Encoder for }[8, 5]\text{ evaluation AG code over }\Bold{F}_{2^{2}} @@ -197,7 +202,7 @@ def encode(self, message): EXAMPLES:: - sage: dec = load(DOT_SAGE + 'temp/dec') + sage: dec = load(SAGE_SRC + '/sage/coding/tests/evaluation_ag_code') sage: enc = dec.connected_encoder() sage: msg = enc.message_space().random_element() sage: codeword = enc.encode(msg) @@ -216,16 +221,12 @@ def unencode_nocheck(self, codeword): EXAMPLES:: - sage: dec = load(DOT_SAGE + 'temp/dec') + sage: dec = load(SAGE_SRC + '/sage/coding/tests/evaluation_ag_code') sage: enc = dec.connected_encoder() sage: msg = enc.message_space().random_element() sage: codeword = enc.encode(msg) sage: enc.unencode(codeword) in enc.message_space() # indirect doctest True - - TESTS:: - - sage: import os; os.remove(DOT_SAGE + 'temp/dec.sobj') """ return self._unencode(codeword) @@ -257,11 +258,11 @@ class DifferentialAGCodeEncoder(Encoder): Encoder for [8, 3] differential AG code over GF(4) Constructing a decoder for an AG code takes much time. So we save the - decoder (and the connected encoder) for later examples and tests:: + decoder (and the connected encoder) by:: - sage: save(dec, DOT_SAGE + 'temp/dec') + sage: save(dec, SAGE_SRC + '/sage/coding/tests/differential_ag_code') # not tested - The saved object will be removed after final example. + and load it for later examples. """ def __init__(self, code, decoder=None): """ @@ -269,7 +270,7 @@ def __init__(self, code, decoder=None): TESTS:: - sage: dec = load(DOT_SAGE + 'temp/dec') + sage: dec = load(SAGE_SRC + '/sage/coding/tests/differential_ag_code') sage: enc = dec.connected_encoder() sage: TestSuite(enc).run(skip='_test_pickling') """ @@ -287,7 +288,7 @@ def __hash__(self): TESTS:: - sage: dec = load(DOT_SAGE + 'temp/dec') + sage: dec = load(SAGE_SRC + '/sage/coding/tests/differential_ag_code') sage: enc = dec.connected_encoder() sage: {enc: 1} {Encoder for [8, 3] differential AG code over GF(4): 1} @@ -317,6 +318,9 @@ def __eq__(self, other): sage: enc1 == enc2 True """ + if self is other: + return True + if not isinstance(other, DifferentialAGCodeEncoder): return False @@ -328,7 +332,7 @@ def _repr_(self): TESTS:: - sage: dec = load(DOT_SAGE + 'temp/dec') + sage: dec = load(SAGE_SRC + '/sage/coding/tests/differential_ag_code') sage: enc = dec.connected_encoder() sage: enc Encoder for [8, 3] differential AG code over GF(4) @@ -341,7 +345,7 @@ def _latex_(self): TESTS:: - sage: dec = load(DOT_SAGE + 'temp/dec') + sage: dec = load(SAGE_SRC + '/sage/coding/tests/differential_ag_code') sage: enc = dec.connected_encoder() sage: latex(enc) \text{Encoder for }[8, 3]\text{ differential AG code over }\Bold{F}_{2^{2}} @@ -358,7 +362,7 @@ def encode(self, message): EXAMPLES:: - sage: dec = load(DOT_SAGE + 'temp/dec') + sage: dec = load(SAGE_SRC + '/sage/coding/tests/differential_ag_code') sage: enc = dec.connected_encoder() sage: msg = enc.message_space().random_element() sage: codeword = enc.encode(msg) @@ -377,16 +381,12 @@ def unencode_nocheck(self, codeword): EXAMPLES:: - sage: dec = load(DOT_SAGE + 'temp/dec') + sage: dec = load(SAGE_SRC + '/sage/coding/tests/differential_ag_code') sage: enc = dec.connected_encoder() sage: msg = enc.message_space().random_element() sage: codeword = enc.encode(msg) sage: enc.unencode(codeword) in enc.message_space() # indirect doctest True - - TESTS:: - - sage: import os; os.remove(DOT_SAGE + 'temp/dec.sobj') """ return self._unencode(codeword) @@ -443,12 +443,6 @@ class EvaluationAGCodeUniqueDecoder(Decoder): True The default ``basis`` is given by ``code.basis_functions()``. - - Saving the decoder for later examples and tests:: - - sage: save(dec, DOT_SAGE + 'temp/dec') - - The saved object will be removed after the final example. """ _decoder_type = {'always-succeed'} @@ -458,7 +452,7 @@ def __init__(self, code, Q=None, basis=None, verbose=False): TESTS:: - sage: dec = load(DOT_SAGE + 'temp/dec') + sage: dec = load(SAGE_SRC + '/sage/coding/tests/evaluation_ag_code') sage: TestSuite(dec).run() """ if not code.dimension() > 0: @@ -515,7 +509,7 @@ def __hash__(self): EXAMPLES:: - sage: dec = load(DOT_SAGE + 'temp/dec') + sage: dec = load(SAGE_SRC + '/sage/coding/tests/evaluation_ag_code') sage: {dec: 1} {Unique decoder for [8, 5] evaluation AG code over GF(4): 1} """ @@ -540,8 +534,12 @@ def __eq__(self, other): sage: c1 == c2 True """ + if self is other: + return True + if not isinstance(other, type(self)): return False + return (self.code() == other.code() and self._Q == other._Q and self._basis == other._basis) @@ -551,7 +549,7 @@ def _repr_(self): EXAMPLES:: - sage: dec = load(DOT_SAGE + 'temp/dec') + sage: dec = load(SAGE_SRC + '/sage/coding/tests/evaluation_ag_code') sage: dec Unique decoder for [8, 5] evaluation AG code over GF(4) """ @@ -563,7 +561,7 @@ def _latex_(self): EXAMPLES:: - sage: dec = load(DOT_SAGE + 'temp/dec') + sage: dec = load(SAGE_SRC + '/sage/coding/tests/evaluation_ag_code') sage: latex(dec) \text{Unique decoder for }[8, 5]\text{ evaluation AG code over }\Bold{F}_{2^{2}} """ @@ -579,7 +577,7 @@ def _encode(self, message): TESTS: - sage: dec = load(DOT_SAGE + 'temp/dec') + sage: dec = load(SAGE_SRC + '/sage/coding/tests/evaluation_ag_code') sage: enc = dec.connected_encoder() sage: msg = enc.message_space().random_element() sage: dec._decode(dec._encode(msg)) == msg @@ -605,7 +603,7 @@ def _decode(self, vector, **kwargs): TESTS: - sage: dec = load(DOT_SAGE + 'temp/dec') + sage: dec = load(SAGE_SRC + '/sage/coding/tests/evaluation_ag_code') sage: code = dec.code() sage: cw = code.random_element() sage: dec._encode(dec._decode(cw)) == cw @@ -631,7 +629,7 @@ def connected_encoder(self, *args, **kwargs): EXAMPLES:: - sage: dec = load(DOT_SAGE + 'temp/dec') + sage: dec = load(SAGE_SRC + '/sage/coding/tests/evaluation_ag_code') sage: dec.connected_encoder() Encoder for [8, 5] evaluation AG code over GF(4) """ @@ -643,7 +641,7 @@ def decoding_radius(self): EXAMPLES:: - sage: dec = load(DOT_SAGE + 'temp/dec') + sage: dec = load(SAGE_SRC + '/sage/coding/tests/evaluation_ag_code') sage: dec.decoding_radius() 1 """ @@ -662,7 +660,7 @@ def decode_to_message(self, received_vector, **kwargs): EXAMPLES:: - sage: dec = load(DOT_SAGE + 'temp/dec') + sage: dec = load(SAGE_SRC + '/sage/coding/tests/evaluation_ag_code') sage: enc = dec.connected_encoder() sage: code = dec.code() sage: chan = channels.StaticErrorRateChannel(code.ambient_space(), 1) @@ -687,17 +685,13 @@ def decode_to_code(self, received_vector, **kwargs): EXAMPLES:: - sage: dec = load(DOT_SAGE + 'temp/dec') + sage: dec = load(SAGE_SRC + '/sage/coding/tests/evaluation_ag_code') sage: code = dec.code() sage: chan = channels.StaticErrorRateChannel(code.ambient_space(), 1) sage: rv = chan.transmit(code.random_element()) sage: cw = dec.decode_to_code(rv) sage: (cw - rv).hamming_weight() == 1 True - - TESTS:: - - sage: import os; os.remove(DOT_SAGE + 'temp/dec.sobj') """ return self._encode(self._decode(received_vector, **kwargs)) @@ -769,7 +763,7 @@ def __init__(self, code, Q=None, basis=None, verbose=False): TESTS:: - sage: dec = load(DOT_SAGE + 'temp/dec') + sage: dec = load(SAGE_SRC + '/sage/coding/tests/differential_ag_code') sage: TestSuite(dec).run() """ if not code.dimension() > 0: @@ -826,7 +820,7 @@ def __hash__(self): TESTS:: - sage: dec = load(DOT_SAGE + 'temp/dec') + sage: dec = load(SAGE_SRC + '/sage/coding/tests/differential_ag_code') sage: {dec: 1} {Unique decoder for [8, 3] differential AG code over GF(4): 1} """ @@ -851,8 +845,12 @@ def __eq__(self, other): sage: c1 == c2 True """ + if self is other: + return True + if not isinstance(other, type(self)): return False + return (self.code() == other.code() and self._Q == other._Q and self._basis == other._basis) @@ -862,7 +860,7 @@ def _repr_(self): TESTS:: - sage: dec = load(DOT_SAGE + 'temp/dec') + sage: dec = load(SAGE_SRC + '/sage/coding/tests/differential_ag_code') sage: dec Unique decoder for [8, 3] differential AG code over GF(4) """ @@ -874,7 +872,7 @@ def _latex_(self): TESTS:: - sage: dec = load(DOT_SAGE + 'temp/dec') + sage: dec = load(SAGE_SRC + '/sage/coding/tests/differential_ag_code') sage: latex(dec) \text{Unique decoder for }[8, 3]\text{ differential AG code over }\Bold{F}_{2^{2}} """ @@ -888,9 +886,9 @@ def _encode(self, message): - ``message`` -- a vector to be encoded to a codeword - TESTS: + TESTS:: - sage: dec = load(DOT_SAGE + 'temp/dec') + sage: dec = load(SAGE_SRC + '/sage/coding/tests/differential_ag_code') sage: enc = dec.connected_encoder() sage: msg = enc.message_space().random_element() sage: dec._decode(dec._encode(msg)) == msg @@ -914,9 +912,9 @@ def _decode(self, vector, **kwargs): - ``vector`` -- a vector to be decoded to a message - TESTS: + TESTS:: - sage: dec = load(DOT_SAGE + 'temp/dec') + sage: dec = load(SAGE_SRC + '/sage/coding/tests/differential_ag_code') sage: code = dec.code() sage: cw = code.random_element() sage: dec._encode(dec._decode(cw)) == cw @@ -942,7 +940,7 @@ def connected_encoder(self, *args, **kwargs): EXAMPLES:: - sage: dec = load(DOT_SAGE + 'temp/dec') + sage: dec = load(SAGE_SRC + '/sage/coding/tests/differential_ag_code') sage: dec.connected_encoder() Encoder for [8, 3] differential AG code over GF(4) """ @@ -954,7 +952,7 @@ def decoding_radius(self): EXAMPLES:: - sage: dec = load(DOT_SAGE + 'temp/dec') + sage: dec = load(SAGE_SRC + '/sage/coding/tests/differential_ag_code') sage: dec.decoding_radius() 2 """ @@ -973,7 +971,7 @@ def decode_to_message(self, received_vector, **kwargs): EXAMPLES:: - sage: dec = load(DOT_SAGE + 'temp/dec') + sage: dec = load(SAGE_SRC + '/sage/coding/tests/differential_ag_code') sage: enc = dec.connected_encoder() sage: code = dec.code() sage: chan = channels.StaticErrorRateChannel(code.ambient_space(), 2) @@ -998,7 +996,7 @@ def decode_to_code(self, received_vector, **kwargs): EXAMPLES:: - sage: dec = load(DOT_SAGE + 'temp/dec') + sage: dec = load(SAGE_SRC + '/sage/coding/tests/differential_ag_code') sage: enc = dec.connected_encoder() sage: code = dec.code() sage: chan = channels.StaticErrorRateChannel(code.ambient_space(), 2) @@ -1006,18 +1004,23 @@ def decode_to_code(self, received_vector, **kwargs): sage: cw = dec.decode_to_code(rv) sage: (cw - rv).hamming_weight() == 2 True - - TESTS:: - - sage: import os; os.remove(DOT_SAGE + 'temp/dec.sobj') """ return self._encode(self._decode(received_vector, **kwargs)) -class _Decoder_K(object): +cdef class _Decoder_K(object): """ Common base class for both differential and evaluation AG code decoder K. """ + cdef bint is_differential + cdef int code_length, designed_distance, gamma, s0, tau + cdef list code_basis, message_index, hvecs, eta_vecs + cdef list dR, dRbar + cdef object mul_mat, coeff_mat + cdef object W, x + + cdef readonly dict info + def encode(self, message): """ Encode ``message`` to a codeword. @@ -1067,7 +1070,7 @@ def degree(self, f): else: return f.degree() - def exponents(self, s): + def exponents(self, int s): """ Return the exponents of the monomial with weighted degree s. @@ -1086,6 +1089,8 @@ def exponents(self, s): sage: circuit.exponents(11) (8, 1) """ + cdef int i, d + gamma = self.gamma dRbar = self.dRbar # dWbar for differential AG code @@ -1124,9 +1129,9 @@ def substitution(self, vec, w, k, i): c = (w * x**k) * sum([a[j] * mul_mat[j][i] for j in range(gamma)]) return vector(W, (c + b).list() + a.list()) - def decode(self, received_vector, verbose=False, - detect_decoding_failure=True, - detect_Q_polynomial=True): + def decode(self, received_vector, bint verbose=False, + bint detect_decoding_failure=True, + bint detect_Q_polynomial=True): """ Return the coefficients that maps to the corrected codeword from the received vector. @@ -1163,6 +1168,10 @@ def decode(self, received_vector, verbose=False, sage: circuit.decode(rv) (1, 0, a + 1, a + 1, a) """ + cdef int s, sk, si, i + cdef int k, ip, count, delta, wlt + cdef bint found_Q + code_length = self.code_length designed_distance = self.designed_distance @@ -1410,7 +1419,8 @@ def vprint_g(g, s): return vector(K, message) -class _EvaluationAGCodeDecoder_K(_Decoder_K): +@cython.auto_pickle(True) +cdef class _EvaluationAGCodeDecoder_K(_Decoder_K): """ Unique decoding algorithm K for evaluation AG codes. @@ -1459,6 +1469,8 @@ def __init__(self, pls, G, Q, verbose=False): sage: circuit = _EvaluationAGCodeDecoder_K(D, G, Q) sage: TestSuite(circuit).run(skip='_test_pickling') """ + cdef int i, s, n, r, d + D = sum(pls) F = D.parent().function_field() K = F.constant_base_field() @@ -1476,8 +1488,9 @@ def __init__(self, pls, G, Q, verbose=False): gamma += 1 # compute xR - for xR in Q.divisor(gamma).basis_function_space(): - if xR.valuation(Q) == -gamma: + for f in Q.divisor(gamma).basis_function_space(): + if f.valuation(Q) == -gamma: + xR = f break # apery R @@ -1584,6 +1597,8 @@ def next(s): # integer next to s in Rbar # compute a basis of J and h-functions via FGLM algorithm def get_eta_basis(): + cdef int num, s + basis = [None for i in range(gamma)] s = s0 mat = matrix(ev_mon(s)) @@ -1627,7 +1642,7 @@ def get_eta_basis(): # Lee-O'Sullivan bound def nu(s): - m = 0 + cdef int m = 0 for i in range(gamma): e = exponents(s + dR[i]) m += max(0, degree(eta_vecs[e[1]][e[1]]) - e[0]) @@ -1696,7 +1711,8 @@ def get_mul_mat(): self.info = info -class _DifferentialAGCodeDecoder_K(_Decoder_K): +@cython.auto_pickle(True) +cdef class _DifferentialAGCodeDecoder_K(_Decoder_K): """ Unique decoding algorithm K for differential AG codes. @@ -1982,7 +1998,7 @@ def get_mul_mat(): self.info = info -class _Decoder_K_extension(object): +cdef class _Decoder_K_extension(object): """ Unique decoding algorithm K for AG codes via constant field extension. @@ -2021,6 +2037,11 @@ class _Decoder_K_extension(object): Unique decoder for [5, 3] evaluation AG code over GF(4) """ + cdef object _embedK, _K + cdef object decoder_ext + cdef type decoder_cls + cdef readonly dict info + def __init__(self, pls, G, Q, verbose=False): """ Initialize. @@ -2192,7 +2213,8 @@ def decode(self, received_vector, **kwargs): return self.decoder_ext.decode(received_vector, **kwargs) -class _EvaluationAGCodeDecoder_K_extension(_Decoder_K_extension): +@cython.auto_pickle(True) +cdef class _EvaluationAGCodeDecoder_K_extension(_Decoder_K_extension): """ Unique decoding algorithm K for AG codes via constant field extension. @@ -2237,7 +2259,8 @@ def __init__(self, pls, G, Q, verbose=False): super().__init__(pls, G, Q, verbose=verbose) -class _DifferentialAGCodeDecoder_K_extension(_Decoder_K_extension): +@cython.auto_pickle(True) +cdef class _DifferentialAGCodeDecoder_K_extension(_Decoder_K_extension): """ Unique decoding algorithm K for AG codes via constant field extension. diff --git a/src/sage/coding/tests/differential_ag_code.sobj b/src/sage/coding/tests/differential_ag_code.sobj new file mode 100644 index 0000000000000000000000000000000000000000..0b7d7daea370c2c8e2e13e3d120d8f48884b5512 GIT binary patch literal 9112 zcmV;JBWK)roZVarbW_Ef~HNLiUl%~&E5tQa;`F-R>x4VqQb{7 zfl8}_t;4Ouja^k%mDW_4ao+^%FKLsRURf}d;O@)Km`Pc^taiYjAs=a*zc-tTs;b#F z)q(Pgs*+%ZkF+al&PqHw5{o3v(Gjzvl(erW9SqXZ-^{NSX-r2$1IYm?(#i5jS&id; zBQcgPTSJw>*=E3Qkj_Q9%wQ1dQct=Xq#Ig6a$r3qbT|Nsuvh zWNd*K_X^6cRoENF8Bom98v7YIyV|Xwg ziIYhwVi;s{cw$joxrjza*yP38b_rIHDJe45Ak(t0@;N4-uKXM{83&Pm!Y`SKpNYEf9%ndKl=wK+ws3?>o zr3Numq^zEl*O73FL<|xI6IKT~Z3q@3vy-I4AeBiHD=Z8*Pmn5uR5RWrb%f>@b>IcE zcVXHxByk5NRYYQDkP2EF4_VMrqR7h~RG6{yM3~h0^F*7`1M^4bpfxsFS;>5mxkVkh zuGB0G)>I@U*)qa3$vmu6KdE(M%vc&`KD;qHiE#7-k;Su7<8r*9cG&46gg z%{ZF%2|}`rITYK4bJ8pu3eFj*IZhYh4thdgDgs;mqFy*6j^L- zPxiNSnDNJX2B{A-=17LShYwEIu|=IS$P&Ky8q&7w((n?oJ(d~de6dk4s3R99$VCQO z&cPS+je3beE-faP<=cDda)VqUd|qjgtHR4u`GRnGl)2it#l){(msIV4^sSL8!Fpu1C%@JDGA5NO4H35?N8mR(v`uWJZ{A~8DbOZKcuT+6K1AOFx ze81*zQ%oMrFKF1aXODlPztBI%-_q~%cV++Q;J=psasILXk$%H!x3T_e{#^DyH z@k0MW{!W(C!(5H`Pw|hk08GCp?Q$!>Z0DC7`DF(;7lzIJ)hyZ!e;ln=1siRZCFT}f z&i@?cZ{_c1nY3jIj`R;>(eete=E_jPD*mUrzpP+A7fvat=TSc5mlt_QFJ{%+AM$Hx z79{!QbFS#-?_)X5VNOS}YK$yc!xh8*vkJ~-g6Su0!98S<_2JcQ^mz&Lut6RXj8Vc_ zEKr(uK#7w_i^*g8VRqyhoN?rdgB7hCw)5oE*V>&HRXy&w|J1}n{L~UC<9GGG>A`%X zEgf$7>9ORo;E8aHaLaJ3Fit4pHex5A$THh|gf#l=>j*GvUP9av$dmQtDT6#6UcB!c zyUjZ|5!h6t^ggnI(?YwVf&VTi@=QJ1Xpm>at2sA3mn6>{WD{7lmTaykFL1JW(I77s zlb7?GsH!7N=b-IF%$D$`{cw=YHb8Og%@805n+LVpzA5#*galjLKA>`IYO4DxA;d}ff(!)!Lf zTf?t}-xSa@|8ip7M5uKzP}; zp5~#y8AtJx8TCQeoVO>2C#7f$v|EbyloV}+c5B||dv|(NxM>47ZP_(N+X2#En9NMk z4rq5|*Cagv=uSX)2D%HhUD>s$CEFkl&Y|64bRZxHLGI43{QgT1MrRLp?Y)Q|!h^Nk zUxL~z|M_T7=Exe-p-8e9k~|E-4oA-sJXIG?BQ&4K-2#JuyAAnz=LdJ%oBP@jeyNxq z$)x;@#Xyfjr&^%7NqRK0>cgjssh7zeq4UE@jWXMakf*8Gp>0mSr7;9}amL{(i2v(@!9yRJwO=}km>lMv02 zLz#@Ba1h304P^?jQ-Pg^ET=;*f;kIy6$M+y3ufY^9jLB|)AJF9ov3=To{1HVMA#!?QwJz|LDMWz&3!mI0&W#UT*!ym zg7GeY*yIn@<6`30M=wIY%aQNJeAWbUUV@HGAzzkj5$EOTyh6{=n4$!77S@6H=l^7&tdU+RVczvO><0o zDoA@Nkm-Zye+d2S(f=^|A3@Kfn0tIA%X6WR zaaY_#tOuk|AO9EY;fa5@9^{SvNuG!({Zn@7pGM*vMCqSF8KeK*YRZ5cxg+*q!=6XN zc$yFo08{9*DByEEiG~Dy9yx4c*J8Rk8^ONNh+tpDC|-i$%aZlBC|cYKz$*Z}iYa;x z^6QYdL4G3(v%c9V)LZcMHY~QwQ17Tv?*i~106XCReaIg`{t)s+Z@nXm6_bB!cx#AG~Bi88dTz&}t3DBRR z{{{WO!ryP`{~i5*p#M+w|Aqd)(en>4R1|BQ1I0}t94n)HJ340l zv?T4pM|@^7VK4nO*QM5|(+589veoKL9-gU2=nPpx7YgW{eJUHRgiBj+J&Srtvs98zSfVcs0)_05BAQ6A}3&$R|TS z1@fuvTEm_&*q;({H!3j`K|0*UX?BEKFnzYtwQVWy$Fj>G&TFU^+<}8$|4yD=> z<##drL5-g@qT;r9wo9Bb7Q)v#Fjgy9Sf=LNqRdVmaiC5kEcSs7UM*SC+%irPczt3?VOvhbv@GSE`(@0^n)@u0gnK zAzugiddN3q8TyUN#Z7Q=GoUMEj9VPFSqaRoz}yDcw?ke9`3}f;x^NV^3yr&>SPgj% zS46wR#XW+c?gc^dhl}*v%UZc0`13$?EiCSXkNbt)I@sY05?Hr^K*0L9H$n0AfxRv6 z!YXkRVfTfRkq~>JdypY}8}uQ2ab`GLFLU9KiqZARsbi~i@(^Sn#mUN|tyk6oG6 z%a1dg6qRgt%;O6f#*1+IlAP<8t<|;#+O5#OBDJrorScj8uLH160^U%7HvxDHfVW*1 z>uyK$9iE^%z`U!B-hhH??AC~t&Vf+`2|CZkWQQr3ekfU)xu4V&VTINHKrg5AO zoLk~Nnsnp^AP<0Mnl-yVG@G+)6t_@b%P4MqQE^{up~;k6DqpSOt2K<<$Y5<1pdA41 z0qCIFx3-RG9{^n^&AKg#ead&4b(iW4R2R*<5vA$HeW@$--8Ad2lcpCpp#z~mi0j1- zr@JO@I2oS|9;`K9Wj(a?Rdxt6?FoN}%3=0W!#oUt!vQ!#<0~s4a&O2-LOx2vP334b z`asba@-bSr&nx?BS?(zPRmcGdIS?KO$(#l&z_9=v2fz@7J09{0kcUD(G0V_TQZ7!0 zi&FqSRmK>m0K)+o0l-N39R+zb~vhMNjfIlzUi1|79@_q3B;(8!l5wg}G7h0$Ue?L0?;>w!rElhT^v>QQ<&~*u*mjZg3gkG+t|BK2Mz+4H;RrZu$jpj9)&C$HN4vxN7rGK3x zyX)cZ2H4*yL*Ar@crySi0Juf7X?i8vw?cQD=Ah}Rb~8zD2Wpk(py{c0GfD4&{!Yz7 z(^KsxJ8D9Iwz$e;>yNcevfiahvi?NdKeGN*ZGz7-SI6f_{|n^u zr7Y1`njq`1q5THhZ>9D-$Hx2~m>+=oQDSzhBL4)y&j9>l@7!O}{7rL^_3z5)4;cLk z)L)u|tp7&mKUxM^gQW#A?ok8I(WT2=ox61D+*;G^n7W=m7&kB;V7z)$#F(ec7|m1+ zAMBgMzJ-j@(h;K-Fs*@UBQb4tMU3(vuG<0H9?%XF+EGv6NCyDZ37F2hRo*UWcGdSv zjNMfF2RgDl2=2PW{$LrhhZ^D`0Q3alP~9fRUT7Z%-Ql`}70o{bn!(X$Zasg4O4hN#2gWQ0s>q)KZP0HXmIgNS1xkAqwQ z*`K8VmbG=G-g0?I^;93asI*%o~eW1 ziuFulJX3cN<4#440XUf@41;&!0O~B= zL5y|iJX_Cxie0G7Q|vkVzE81>G7EAp0xw34^W<>qRc1*5QUEN$a%h0O6!J32=Zjgr zKv$>O3)LiCq?|2>vx{MLiHvrsqrjH|b2%_q=uMIJl`6(nD#q2YzXtZ#${5!BdNrFJt(*F*oX?jY+@yP2eq zK>w&N$@(!pD_K9Dg{z*(TzyX>*{9(0X*tpjYNXEqun~Y~vErVC{5<4MkT>ff>le^? z5sH@}zpQ5?>n(Z~vfk>LfL9RpRXBM~ruDi?Ya0M>0PrRvz6JSh$lD>mlcfOfDi`m; z#STEl70&(-wDI-^)#~nL6>Cxqn=6DyLCy{Kk55N)<3IF@Jr_E_!a5@hFpG^CHh0R zK1hW2FKGXk+J77ya}O{%ZjQ-y%g++svdEenA0h(ac3V5wgQnN*AnQD3)C@*GpqjfK zWZeRtE!`Pp4VD(f*vc&j+*-M81D9=K-A*Rj-km;}4#0E-<^Xq7#Mnv2=&WLNfqhrl zcat#=bi_CanC`$FEHORYSx&Ks0NNAKLnXAAJAES^2F&5W9ATF?AI;wGy%OV*D*dAz z*&PjcePG{LhCD_Mu^#~a0T|%6iE$v>gPIY65~nk#=LN{J7ejef@DvH%VBb)!_`Pf05B4OQEtBRM?)S1c`W2{ zZV+Pu8h$9oL!RKyMvR5-Y~Q^G@F;aIXF~;4J7!&UOBgPuF3Ff%PH)X}rn2X%!A(2{HpZRXV ziVL7U3)(uVJ=?J>7Xot*FpDJSTvg%40GtOvy}fOdXr|l_R$QWt8ep^(sAX;kE1r+e z3*6a{uot@J5%wbYzK^iW9o4uPQ7=J&OXXNDQ)yiez!d;oiG^?#*SGL3_R*LUX;_rfFVX2Teb$(tpH}-J@{#80;UH zA)in~d=h}C0C?JM)ARO>A2lY(#_Dn z;FdJ~qB|>1zm$cgUUn?KEy#2${JkQF`KlV`YXH0sz&0$mHz2-Fc72+8 z1V{UzZ4PY=-;%9>X$?#piD~PR6>bMWdjL9ktUcQi%>z6Rj_#z4I>V?7P+dI^ zj_!ue13eiW4RRKgc#s-xcjfY6xaTI1>`#&*PgX-b1%Oil80N7laX8u|pd0CNP@>CjCg~`kMtdBT=(3whItKc&9tS15 z>}HaVgTBBcDberAN{Qn=jY(mGW8D`b)6?K@q8#QVHB1A5$pB38@GUpu{<7%!Ohe!-d>1LC^8nKadWed z{GGYzq>Fh(@q3@_zxfH4mPYW6aO=lE!>qRbBcSs<_z_S&ij>6oQrzc6d`;X&r~epe z37`#tE)~#LX;AA&LCb(TAE*lis&yJ_T#R{&oAg4!E&^)PI-$b^G>AdiL*5d7C1}+db*uI9i4A z-+|ojl-uSmkGNCb4ee@Z*GTO>miAs~*Ft-r)ZVYQ+&Tar0N_E7eXd1wy~lBnd{`Mh z0;5NPdd%avM?Q|uCp;PVNPMwR+#{b<<9tfFd>Sq{!1@`P=tc#27J%mfc;3_04RVtT zuvyu^0Q(nV|B?*wvI1-YU@HKxNWiO}%rDiw2HfkwZIie+tb)G@?OV{kZP#-pM?c2F8>5ujf6k6-M zzt-D&=x&cn<|n=pz3Rnl^kZ8 zKkOK`^3SvweGyEvqoz^vc1H9FB%hDuYs2CPCX;JprL>C8MDHfzJE97_{o+J=q}STtg$!ZF z6NP4^JY2$7%2E60FvCpxJ=$x1#eZCk|I!CP@|w(Q&?nPvMCk)@Q@#x>Erip9jL?@? ztENQ!s3;Pvu1SbrB+_H}5?))>$y&5AGn7zkK&;z-9J0XLJN7$$Xn(9!947sAK-yNs zOOEJ3))VC~|A^uZ5d1v`>(x7>{x@GX)vD3UrkIDIe@MXw-l+C_8OH7J>36Yfzk=I&z-3%hv9I?^#TwY- zD0pTF%FV{DCFt?!IYCn6h)8S>9hyFtE>YI!DPGj}$BJy3rFW+)y z$NeesNhs*aX(c`dJ*RrD->C&c5gMwABQXU2RW5=w~To5O;@Y-TJ#%n#nr~haA^VoT7EDXna?E@W$ z7XU8a#YFu8jOPG;3GYaM35t@1w+R3jI=r2RTrzF5Ny5iO_?YBH+gj8vzg|p@|7=m~ z)?b%e)NB!y6@14X^!Ptm%Pq34|*Lvhjd(tTJ@`GmxUP`_8BY^1@4+v$@mO~r%+UNfW+EH)D1IBC> zslu|agnbP7D(SsCOCAJB3S?YHN}ydM+QoEEqtVS(G3QwuNcpUV&-w7NKt?~S5v~rn zvw>SEW1oZeqRdgAt0FFL9?sqN1;~qG!I+=~no=4feOo=&NM(p^mZN0rZ_f-zB5pjrQuy z=xbE;dm5GfUU*sy%ll;9`_r==>KOPs7(4)j2W9Ao&|aSz`e7CNk-e1tQTTfd_K(X1 zo=8i;s=fGH7tl`u{j`j}0qtipqi_GW z?om&-0r&>H)+{NeZ(3ApG3Hxf%(nsAp2ns9={wN83(b2Xupr(Y=z5>0TTDOT?uzM$ zjacX-RlA*D^$&a~z%F?BM3(DQiwr)4_H$^zkmdOj?XNN?=WB%gLOS_IMfnzfzJuNO z($5cQ|0wd_-6-LoRKhXC(k?T+vS295&6gW7gR)#%{eZnXKGHsaZ#vOnP9qE;>TN#hm#Nrj03t*)JZ?|R+0X7WI$n&hqc)P#K=Ii zp9LI22Gx
  • }#GSXh`OLu$!ENitL?!%U`faA8A|6zJrTVlv#3p)#a2;7C`1S?0^? zBO}aiCbK$HC!<)41u|MEW6WVGGFB&tnwE5&P7X6Mdbm!Gut&%1gFu7HxUBI!NPWiZDA>C(|_7Ix#PvH!^))lFX*N?S)9NeMH<6Er;-3bHa3N|92X7%5U#N6Kr7nId7GL{J4Q zgOu$*QGv*cdJ@%1MLmfX7Md-SB(9T6Rx?R0q4`Cfc!F#erfox#a1^9yIA#Q?C`z-@ z7Ud{e)QlU58nN=ENvgEwqR+|$vq!4Yn;ERAU^d9gqRw1aYLo@5qDfh3S+O+8D(q8@ z)HpFlEDf`|$j4-cEQ#ben4c!dMvk`%m8dxdWz;YNq9ge@osEf_WGTs+m$DUS)2S63 zrVI%Vl(96Nz&Y0eo|QH^v5uUilQn4-W;8iDMb=sjW#8RQRzgnENu9}xN4?qG?2|5I z`#PnQb)0?;X^cJ9TqhWEnodp^Ts@0~_zpUt^?j!w=kCg0ECs*s_nsf?YE6iS}lk1%n zyn#{h##(YylH9D5&06yY7U?auR#42)!wMOV!U%YM_ z)&j`d*GLqo9_}M|=4aDqsT;W$eI*K1kMxnd@-?s4u9)1N?{C<%XOA{dE7WFatu>$4 zgZ)Wg4YHq7ttqt<;<_2t~JZ5UPwoscU`hF(cllE~9|JcDl zHu8^mxvEfXp;a<(OSJ@gZTwHzBCDC}-@w0yYHhThmP$7k;8blA^VZyd5f_I1*Go@Z z`9LMw+6e#SeCT)H?yt4dPV~Reo5THo^RKy>%>-?r)?AyS_404wSOAJGM>)*VG?t;M z{#*F)WNo?sWY#dWRcsU9qmz5h%`7X;ljJ^~++Wm=FL((rZh^9@ScnreK^`b34?2%2 zmMBohkJJS=F>%zKZpm;EpJpFI9})x{&&==LPFmqAaGF__6t$c@Tt^m^tTqjSMn>nSQtS3+DWE<*V4SBkbJi|HutWKUQCeP=a z51HHcBY)Xa7oez;Jl+=^CTK9tgmbd;#O1}kYp^OBRL*?lC7ujS~U;M_$#* zYx_?0>eYctBlDc;b#r?$d1LQqB8FYMQKJ;))6M`bncPEfvYnb!Pu|kW4wL`meRn;1 zTPHhF`rfH0@9N||6RzJkcLw?32leDboqUucAM50k6!}yqpYhc&Uoc-ZUlY*JE$A2Z zt!2_2dto{Fx$u z>E!Pe`9~+aje4?2r#Wn^m`~TyT>iZ+MP2N>mb%$zl6u&uPQC2&-|iZ|Jp3e&=5g~| z&F6|~GuFz_h@Cda;7;|AXOgyHqxIB>C|Ys@z_tQ*hx{k7t$}R=Y+Lr3qV3Rbk9G&N zJ8~Nto;xYeu+tfKx&Yr*`s|j~XLn$G0NWFO4nVsX+P%^4!#<6A+83PzA?Sy8e*_cZ z*^Q{|49J$9ff(fb%vDbZL3=RQ;3K*9G#?{F_{dIkM}#*J-a!a&D5S&WR1VHMl>%T7 z0d_c|9D(*ov`3*mI!pS-aBC4BO+*BMHcXC%$wQ$!PP#uVtNX)&Jp$PAaCs!!6VRTB z_E92}lhBzA!4$NoX3gZZY?+*naEHincckZQ27;Igo3muV{;UCOz|IDC4q_=pdoJ4Z z(4LzDly? zDZ2S3TH{D>$5sN8`yJzJr|D7Lwf_-=c9drNYRDp zTm-?zXkWqwIE@cfMicZ>cxaJdG=PoFi?T~scFF0yF(nVTxR#`sKTz)jY3BlY9qnj~u3(plUjslf5Tw%~#xf7PV z!F&>>Mld=j9*c)dn@rzrO{Xu+%{bEs%XW@b8n@VK%zFDRSY8oK_~wW&dOJ@_ExiL% zx)W2ni>uGW#qOj;A|48}Q^jh7-VMk-fZQt}e8P*E?_iYP2kHHgKEM+umg7MTJcRbc z_HsOe!AHe%Y~9y#hWOfRC_5AEW&V+MlBRS(a-1c_UX}z|xn{_)5C^ zTDkfLfNuf#4(7i{`vKu6|Y~eu0T!0sT$-_+0^Z0q_R^f5PrxX#b7&KWOh3 zb$kyxIWD%}c(M~T*Ts|Va*6YQw~OylkBguGdtDrw2YEA$H+OM+EimrGcuS18!gy

    ~Lsk^3{ZYc9>z5b1!acfuq(V_ID>t*-2IT_L*^02m9<-6b{y+X(AYw1SjN{jjOfZd>3gmbEaaq3v*E?~J^4r@VJ zE0OL(%3Ub{1^{IeQ0_>S2}~H6h{UXL*)?2L@F-9fK*els#nG*l+*(mp!A{foUHl44 zc^#~uO2#OsH*YKPAtQIw}t((A3NunZEA)sn3&q|fARaU-g zFp1Rw9Ve%HyvkQC04D%&B4+7Ym!v15zXr0C`DFQ7bU0z1m9ABD6i;kJrooBgo5EYA z;;HvMt7@OZv%*giB5Pr;4yNnHHcMff@l(X~BC(FEK#!l*Mb<&D0eYtjz0;tFhwAVi z2(WUM2$xnFf~fTLy@gm97snIE?y2DrJ1swhR{))!$!FO{)V*{=XUSOj34i1)#Ihc- zoGoHGhsP4Jt_sfONO3ZM9yT;TgO6AzN*mIvOyqTSu6RBc^a9ZELfF0tMdD(NUxM*V zF@70FE>}f@10(IS5Qra?HmVJAg@XZ`FpVo=@+!G4uC_MKHIQBl>2*?iz1lQ40B|D! zH%Y+F3a}Y~TL8G#=FV;CZs7r{!_e(Y=?*B}3DjL2rH(jvWAGk6h$BmGgda#ER`tG@ zw?+B9&*dnek!)4-en%=EfY%4%;2{~-!z!#t0C*IDtyqf3(0&~4C(wRUY=x(=6-2Gv zrgHzZmDgur=~-wzCtW?ST)hCmivYaD(-zrVX?$5(f5o!C9m=ml`88?%b!Ghx0Nw=P zEeY7+$~<*_8@Qdoy(4k&Dnsu9@IC+^*sJ>?x*zc%Q3!c1BPd@HQJp?kA%CK5eF|Hj zLHTp(>5`eD&_}XO`sBh5!7P9YLR&fzL-Qbe7=6j%i5R3C87Kayxh{gGzTo$8+ z;<+wK@Smahi%|U4l}7O2T$13wyD|yBiwgxs|JYB8{;9I^mn-9z&EJUSAH=d-=4Owz z-Z^eA&2@9B%WX^DZtET*lIH=&3rwEGG;_=BHV2>u06w?1lr7P1<#rIYwNh#WrM5t| zb32II9)lg+*@@cGEs5I6y>Fs+c4s7{3mkWak8W~0-Bn~g0O$$80dCIEUTF75yARrZ z-B{HF-HNOI+;S26D`Nv-Y#@{dNpFK43C;&*2rvh^n_}!xsYJ+Fw21nN=%8$ZU}%<01SII%g`-%64+EqVJJm_THz#c6oVC+1O{^jSz>C!ab>a+ zCJD5u3^d`GOcIzXV5*xW%Sz>AmGV&o{ngMvPWm|B;iDFq6M#8UVopjUi+ozK2GElM zT`QrdICgg(F!jKs?DVcfw_)#OIaP&!nj^Z?VeSm*pDA6QrKY$ZfU^NON0Q}S^v{EA zgOe-^+>$Kk19gGe^A|QE%SBGI%y&z&TnxoagyN;?WVuX{k*{H~J#eR}ylS<5$ zZu=3TMV6~Dy{i$+H8MBXT4cEn((56;K}v6QEY?lH+ziZSiMd5(_f`OI17M52nzy5S zhm$OKDy6%ibT?4ZS-z|@J54iVzw0kf!ArHav!|?HloX(>vvaJ9- z2EgNPoQElg@kGFu?0nFPHv(ugBX!j1F?*jUsgud@!+y}sX2+T+Jl7EcuCz8OA zWD@vO75-z%CD$ zx;fmt@xFZHT zc`^tL<_fZO_Q(l$Q6{^>WH)Gcmx1V3kNyrH z1ArL_%pi#w?8$Pp%LjA_pa)6lP*3{q9tO<8z!cc&Jp|q1p1qP~gbIJ8Bf3#AHyZk5 zq|32tiiZL)4uHcvHdziw{|Ly&dmLo>Q<3FJpeA^*=O=nH$#Rs(L6%*LER&!(Stw5N zq>*K+N0MclCzCAGJ(4UlJo`tMnJO`}Jl5}tuoiyIO+z5FWn$)7OeutPE~N9MbiQMy zbYK<$vru9dsnjk8Upi~Uhu}-E0Ft|LEDb}Gb=#huI63@O5 zbsdKp23C2R{`-JBoG%+A*=7agRFARjTw8WsJgD0!m5gt;&(z zYG76Zv#Lqz)+isVm5<|~e?0VSrH>OFK28MYBw*G^%*mcC$GNqDo&so{gw{K_mI7uS zFb(#4pNj5jlDa&*4(gt+!au_i-I*|V7WCIkmuIUfo&&(S0GubOy8-?4A-ljy-RX9h zotpx6kyx>d8&UTXCv~UVU3P8?#mj`^<>}PjsHl5IBkFEa)V*>)se6@5%+-#~dJX1w zEdsetCgysJx;H?2BcwM;>CKLn+6>Gsz}zY^x2e=_0pNB3?y#5gPIT{bQul79bPts7 z1?oO0b??XE1DVtXX9ZIpRI`0ZnS2-~AA$CxGSID#$vg(k!4>tH8Vl%#fJZ^5mP>QGG(J;$`?@l zQYe1K6_fO9_QfC39upBR1b!B32S@Zk3mnBPy_<~%%vw$bT7HToIQ zUjY48K-;82t)CqI2Gs9B?GmWAX{cE-W+`FNKLGm^u)iAnof5C8s7eaIe*^jtpu72+ zh#xWS!9b1|w{5-FV`!I`54yc5R37%#!_Url+r*b4FYPg z*HJt3F*wAVQ9JR(Nz~4R)I5hOlfz*0U}zV}Ko3!X;Q)*PV5GOH!Z}Jg7_IcjKz}Us z50wtaDZpU>91g${5-{GI`LyRq;3fb!QR0rW5p+Y5TB+;WQs%_=v#A3Jz=`tck+}jiPRuZA4`^+nJl|Xay=vj7#{dvNR6mO5UGm z&QDQ-J{A3iDVjh($@|>K5^kdkHmXt0Rzki?;FqOn4f?Bj-(Y9lSgqrMuLb@D$WP?K zdac*S40;lWZx+9(ScHU1|3`&c{Zf+wss12>pD7USo=1yez1RmC`s{hYOnU@9-+{_TP=57kVvr7czvA zNERC5a3#SF?;ZmNh zsuJ-lr*N#YDrr@`%lHP~Qq=_9@OkHF`xF5%EGN z-NZ8=;jceM_?tTs{yIv8zituXFBBx{m0tVobqc+TYu}k~;$2%GyF}x<`~SMFFU#w; z)BQK{ms7tNZ^HiXzj52O(Hpl~zE-6z*T(su;?^JKA3yL9{-UgZ4gdI%<4(}r;vHPP zSDVL74HPfmdWGUk{NoKiFxr2!3URM5-mYv&(rZwfuEiR$EP0dkI*eQ|tLc<*teW1C zu?#oQ<^xb5xsN@$gsX4*3Jw3dO*x*4-G!2cuGfK6i6dMlTqOTV}BK0KgAAtUZKSGIg`HgpG$`;}M6AN2Lwhkkm|X3jyWN_EZt<< zqtfQE*8=*!JUf7vc~+olWq_kw0o@wtHqv`r^xI{6Z?C*}u)L?0N!aTMd!3-)S$gjx zy$^QGy(`e&fbK56_dvgAruPGs_g;CKY3~h7eW2M_`aLi`%E69__k%)zC=8IU2ckbH z)AeBGI)5)|9|C&^L4T+WU|3oJR_=j580Z3^50T!7qdy|k`$*+|)Lzm)8urFOf2{O= zXj-i4@5zq?`Y@mmm)?&+e?0Hk(j&Pl{t8a1kqDQ^_~lGk{E0a_fyp#Cu03!F=YIi= z21~FQ$tc1-w`VyhNEQ zf~loYJzDxYCNJ|2*)rgYfjd^Z44}U}GxVTxRia#lV4@T%23JujD8pbm?;BVOE!X Date: Tue, 20 Apr 2021 17:28:12 +0900 Subject: [PATCH 316/406] Fix a typo --- src/sage/coding/ag_code_decoders.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/coding/ag_code_decoders.pyx b/src/sage/coding/ag_code_decoders.pyx index 23490245b9d..f999a410a20 100644 --- a/src/sage/coding/ag_code_decoders.pyx +++ b/src/sage/coding/ag_code_decoders.pyx @@ -620,7 +620,7 @@ class EvaluationAGCodeUniqueDecoder(Decoder): def connected_encoder(self, *args, **kwargs): r""" - Returns the connected encoder for this decoder. + Return the connected encoder for this decoder. INPUT: From 0b40831b258ac57daa0fd96c225faea6edef835d Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Tue, 20 Apr 2021 15:40:48 +1000 Subject: [PATCH 317/406] Including some stronger typing and other refactoring. --- src/sage/coding/ag_code_decoders.pyx | 482 ++++++++++++++------------- 1 file changed, 251 insertions(+), 231 deletions(-) diff --git a/src/sage/coding/ag_code_decoders.pyx b/src/sage/coding/ag_code_decoders.pyx index 23490245b9d..daab79bb7ec 100644 --- a/src/sage/coding/ag_code_decoders.pyx +++ b/src/sage/coding/ag_code_decoders.pyx @@ -69,6 +69,9 @@ from sage.matrix.constructor import matrix from .encoder import Encoder from .decoder import Decoder, DecodingError +from sage.structure.element cimport Vector +from sage.matrix.matrix cimport Matrix + class EvaluationAGCodeEncoder(Encoder): """ @@ -620,7 +623,7 @@ class EvaluationAGCodeUniqueDecoder(Decoder): def connected_encoder(self, *args, **kwargs): r""" - Returns the connected encoder for this decoder. + Return the connected encoder for this decoder. INPUT: @@ -1013,10 +1016,11 @@ cdef class _Decoder_K(object): Common base class for both differential and evaluation AG code decoder K. """ cdef bint is_differential - cdef int code_length, designed_distance, gamma, s0, tau + cdef Py_ssize_t code_length, designed_distance, gamma, s0, tau cdef list code_basis, message_index, hvecs, eta_vecs cdef list dR, dRbar - cdef object mul_mat, coeff_mat + cdef Matrix coeff_mat + cdef list mul_mat cdef object W, x cdef readonly dict info @@ -1070,9 +1074,27 @@ cdef class _Decoder_K(object): else: return f.degree() - def exponents(self, int s): + cdef _exp(self, Py_ssize_t s, Py_ssize_t *sk, Py_ssize_t *si): + """ + Implementation of the exponents. + + This sets the result in ``sk`` and ``si``. + """ + cdef Py_ssize_t i, d, gamma + cdef list dRbar + + gamma = self.gamma + dRbar = self.dRbar # dWbar for differential AG code + + i = pos_mod(s, gamma) + d = dRbar[i] + sk[0] = (s - d) // gamma + si[0] = i + #return (s - d) // gamma, i + + def exponents(self, Py_ssize_t s): """ - Return the exponents of the monomial with weighted degree s. + Return the exponents of the monomial with weighted degree ``s``. TESTS:: @@ -1089,19 +1111,13 @@ cdef class _Decoder_K(object): sage: circuit.exponents(11) (8, 1) """ - cdef int i, d - - gamma = self.gamma - dRbar = self.dRbar # dWbar for differential AG code - - i = s % gamma - d = dRbar[i] + cdef Py_ssize_t sk, si + self._exp(s, &sk, &si) + return (sk, si) - return (s - d) // gamma, i - - def substitution(self, vec, w, k, i): - """ - Substitute z with (z + w*phi_s). + def substitution(self, vec, w, k, Py_ssize_t i): + r""" + Substitute ``z`` with ``(z + w*phi_s)``. TESTS:: @@ -1119,21 +1135,23 @@ cdef class _Decoder_K(object): sage: circuit.substitution(vector([0, a*x^2 + a*x, x + 1, 0]), a, 1, 1) (0, 0, x + 1, 0) """ - gamma = self.gamma - mul_mat = self.mul_mat + cdef Py_ssize_t j + cdef Py_ssize_t gamma = self.gamma + cdef list mul_mat = self.mul_mat W = self.W x = self.x - a = vec[gamma:] + cdef list a = vec[gamma:].list() b = vec[:gamma] - c = (w * x**k) * sum([a[j] * mul_mat[j][i] for j in range(gamma)]) - return vector(W, (c + b).list() + a.list()) + c = (w * x**k) * sum(a[j] * ( mul_mat[j])[i] for j in range(gamma)) + return vector(W, (c + b).list() + a) def decode(self, received_vector, bint verbose=False, bint detect_decoding_failure=True, bint detect_Q_polynomial=True): """ - Return the coefficients that maps to the corrected codeword from the received vector. + Return the coefficients that maps to the corrected codeword from + the received vector. INPUT: @@ -1168,34 +1186,35 @@ cdef class _Decoder_K(object): sage: circuit.decode(rv) (1, 0, a + 1, a + 1, a) """ - cdef int s, sk, si, i - cdef int k, ip, count, delta, wlt + cdef Py_ssize_t s, sk, si, i, c, cbar + cdef Py_ssize_t k, ip, count, delta, dlt, wlt, pos + cdef list mat, nu, mu, i_k, i_prime, i_value, i_count, voting_value, voting_count + cdef Matrix coeff_mat, gbmat cdef bint found_Q - code_length = self.code_length - designed_distance = self.designed_distance + cdef Py_ssize_t code_length = self.code_length + cdef Py_ssize_t designed_distance = self.designed_distance - gamma = self.gamma - dR = self.dR - dRbar = self.dRbar # dWbar for differential AG code + cdef Py_ssize_t gamma = self.gamma + cdef list dR = self.dR + cdef list dRbar = self.dRbar # dWbar for differential AG code - hvecs = self.hvecs - eta_vecs = self.eta_vecs - mul_mat = self.mul_mat + cdef list hvecs = self.hvecs + cdef list eta_vecs = self.eta_vecs + cdef list mul_mat = self.mul_mat coeff_mat = self.coeff_mat - message_index = self.message_index - code_basis = self.code_basis + cdef list message_index = self.message_index + cdef list code_basis = self.code_basis - s0 = self.s0 - tau = self.tau + cdef Py_ssize_t s0 = self.s0 + cdef Py_ssize_t tau = self.tau W = self.W x = self.x # auxiliary functions degree = self.degree - exponents = self.exponents substitution = self.substitution K = W.base_ring() @@ -1229,20 +1248,20 @@ cdef class _Decoder_K(object): print(('{:>' + str(width) + '} ').format(s1 + s2), end='') print(']') - message = [] + cdef list message = [] # construct the initial generators of the interpolation module - hvec = sum([received_vector[i] * hvecs[i] for i in range(code_length)]) + cdef Vector hvec = sum(received_vector[i] * hvecs[i] for i in range(code_length)) # weighted degree of hvec - wd_hvec = max([gamma * degree(hvec[i]) + dRbar[i] for i in range(gamma)]) + cdef Py_ssize_t wd_hvec = max(gamma * degree(hvec[i]) + dRbar[i] for i in range(gamma)) if wd_hvec <= 0: if verbose: print("no error") for s in message_index: - sk, si = exponents(s) + self._exp(s, &sk, &si) message.append(hvec[si][sk]) else: mat = [] @@ -1277,7 +1296,7 @@ cdef class _Decoder_K(object): print("G{} ".format(i), end='') vprint_g(g, s) - sk, si = exponents(s) + self._exp(s, &sk, &si) delta = 0 mu = [] i_k = [] @@ -1297,13 +1316,13 @@ cdef class _Decoder_K(object): raise DecodingError("more errors than decoding radius") # detect Q-polynomial - wlt = gamma * dlt + dR[i] + wlt = gamma * dlt + (dR[i]) if detect_Q_polynomial and wlt + s + tau < designed_distance: found_Q = True posQ = (s, i) break - k, ip = exponents(wlt + s) + self._exp(wlt + s, &k, &ip) count = degree(gbmat[ip, ip]) - k i_k.append(k) i_prime.append(ip) @@ -1314,8 +1333,8 @@ cdef class _Decoder_K(object): if s > 0 or sk < 0: # not s in message_index for i in range(gamma): - k = i_k[i] - ip = i_prime[i] + k = i_k[i] + ip = i_prime[i] if k < 0: value = K.zero() @@ -1327,8 +1346,8 @@ cdef class _Decoder_K(object): winner = 0 else: for i in range(gamma): - k = i_k[i] - ip = i_prime[i] + k = i_k[i] + ip = i_prime[i] mui = gbmat[gamma + i, gamma + i].lc() * coeff_mat[i, si] value = -gbmat[gamma + i, ip][k] / mui @@ -1347,8 +1366,8 @@ cdef class _Decoder_K(object): # voting c = -1 for i in range(len(voting_value)): - if c < voting_count[i]: - c = voting_count[i] + if c < (voting_count[i]): + c = (voting_count[i]) winner = voting_value[i] if verbose: @@ -1398,9 +1417,9 @@ cdef class _Decoder_K(object): print("# s = {}".format(s)) print("F{} ".format(i), end='') vprint_g(gbmat[gamma + i], s) - sk, si = exponents(s) + self._exp(s, &sk, &si) if s <= 0 and sk >= 0: # s in message_index - k, ip = exponents(dlt + s) + self._exp(dlt + s, &k, &ip) mui = gbmat[gamma + i,gamma + i].lc() * coeff_mat[i, si] value = -gbmat[gamma + i, ip][k] / mui if not value.is_zero(): @@ -1469,7 +1488,11 @@ cdef class _EvaluationAGCodeDecoder_K(_Decoder_K): sage: circuit = _EvaluationAGCodeDecoder_K(D, G, Q) sage: TestSuite(circuit).run(skip='_test_pickling') """ - cdef int i, s, n, r, d + cdef Py_ssize_t i, s, s0, sk, si, n, r, d, num + cdef Py_ssize_t code_length, genus, gamma + cdef list gaps, dR, yR + cdef set temp + cdef tuple e D = sum(pls) F = D.parent().function_field() @@ -1504,7 +1527,7 @@ cdef class _EvaluationAGCodeDecoder_K(_Decoder_K): if b.valuation(Q) == -s: g = b break - r = s % gamma + r = pos_mod(s, gamma) if g != 0 and not yR[r]: dR[r] = s yR[r] = g @@ -1512,10 +1535,11 @@ cdef class _EvaluationAGCodeDecoder_K(_Decoder_K): s += 1 # gaps of L - gaps = set() + temp = set() for d in dR: - gaps.update([d - gamma*(i+1) for i in range(d // gamma)]) - gaps = list(gaps) + temp.update([d - gamma*(i+1) for i in range(d // gamma)]) + gaps = list(temp) + del temp # genus of L genus = len(gaps) @@ -1532,7 +1556,7 @@ cdef class _EvaluationAGCodeDecoder_K(_Decoder_K): if b.valuation(Q) + G.multiplicity(Q) == -s: g = b break - r = s % gamma + r = pos_mod(s, gamma) if g != 0 and not yRbar[r]: dRbar[r] = s yRbar[r] = g @@ -1551,7 +1575,7 @@ cdef class _EvaluationAGCodeDecoder_K(_Decoder_K): # ev map for the monomial whose weighted degree is s evxR = vector(K, [xR.evaluate(p) for p in pls]) - evyRbar = [vector(K, [yRbar[i].evaluate(p) for p in pls]) for i in range(gamma)] + cdef list evyRbar = [vector(K, [yRbar[i].evaluate(p) for p in pls]) for i in range(gamma)] self.is_differential = False self.code_length = code_length @@ -1563,75 +1587,32 @@ cdef class _EvaluationAGCodeDecoder_K(_Decoder_K): self.x = x degree = self.degree - exponents = self.exponents - - def monomial(s): - sk, si = exponents(s) - return xR**sk * yRbar[si] - def ev_mon(s): - sk, si = exponents(s) + def ev_mon(Py_ssize_t sk, Py_ssize_t si): return vector([evxR[i]**sk * evyRbar[si][i] for i in range(code_length)]) - def next(s): # integer next to s in Rbar - while True: - s += 1 - if exponents(s)[0] >= 0: - return s - # minimum of nongaps of Rbar - s0 = next(-G.degree() - 1) + s0 = _next(self, -G.degree() - 1) # basis of the code ev(L(G)) - message_index = [] - code_basis = [] + cdef list message_index = [] + cdef list code_basis = [] s = s0 - v = ev_mon(s) + self._exp(s, &sk, &si) + v = ev_mon(sk, si) V = v.parent() while s <= 0: if not V.are_linearly_dependent(code_basis + [v]): message_index.append(s) code_basis.append(v) - s = next(s) - v = ev_mon(s) + s = _next(self, s) + self._exp(s, &sk, &si) + v = ev_mon(sk, si) # compute a basis of J and h-functions via FGLM algorithm - def get_eta_basis(): - cdef int num, s - - basis = [None for i in range(gamma)] - s = s0 - mat = matrix(ev_mon(s)) - delta = [exponents(s)] - num = 0 - while num < gamma: - s = next(s) - e = exponents(s) - if basis[e[1]] is None: - v = ev_mon(s) - try: - sol = mat.solve_left(v) - gen = [W.zero() for i in range(gamma)] - for i in range(len(delta)): - gen[delta[i][1]] += -sol[i] * x**delta[i][0] - gen[e[1]] += x**e[0] - basis[e[1]] = vector(gen) - num += 1 - except ValueError: - mat = matrix(list(mat) + [v]) - delta.append(e) - - vecs = [None for i in range(code_length)] - matinv = mat.inverse() - for i in range(code_length): - h = [W.zero() for k in range(gamma)] - for j in range(code_length): - h[delta[j][1]] += matinv[i,j]*x**delta[j][0] - vecs[i] = vector(h) - - return basis, vecs - - eta_vecs, hvecs = get_eta_basis() + eta_vecs = [None for i in range(gamma)] + hvecs = [None for i in range(code_length)] + get_eta_basis(self, eta_vecs, hvecs, s0, ev_mon) if verbose: print("message indices:", message_index) @@ -1641,14 +1622,14 @@ cdef class _EvaluationAGCodeDecoder_K(_Decoder_K): print("h{} = {}".format(i, hvecs[i])) # Lee-O'Sullivan bound - def nu(s): - cdef int m = 0 + def nu(Py_ssize_t s): + cdef Py_ssize_t i, sk, si, m = 0 for i in range(gamma): - e = exponents(s + dR[i]) - m += max(0, degree(eta_vecs[e[1]][e[1]]) - e[0]) + self._exp(s + ( dR[i]), &sk, &si) + m += max(0, degree(eta_vecs[si][si]) - sk) return m - nus = [nu(s) for s in message_index] + cdef list nus = [nu(s) for s in message_index] dLO = min(nus) tau = (dLO - 1) // 2 @@ -1662,30 +1643,26 @@ cdef class _EvaluationAGCodeDecoder_K(_Decoder_K): # the vector form corresponding to f in Rbar def vec_form(f): r = f - l = [W.zero() for i in range(gamma)] + cdef list l = [W.zero() for i in range(gamma)] while r != 0: s = -r.valuation(Q) - G.multiplicity(Q) - e = exponents(s) - mon = xR**e[0] * yRbar[e[1]] + self._exp(s, &sk, &si) + mon = xR**sk * yRbar[si] c = (r / mon).evaluate(Q) - l[e[1]] += c*x**e[0] + l[si] += c * x**sk r -= c*mon return vector(l) # the matrix of the leading coefficient of y_i*ybar_j and the product - def get_mul_mat(): - cm = matrix.zero(K, gamma, gamma) - vm = [[None for j in range(gamma)] for i in range(gamma)] - for i in range(gamma): - for j in range(gamma): - f = yR[i] * yRbar[j] - v = vec_form(f) - e = exponents(dR[i] + dRbar[j]) - cm[i,j] = v[e[1]][e[0]] - vm[i][j] = v - return vm, cm - - mul_mat, coeff_mat = get_mul_mat() + cdef Matrix coeff_mat = matrix.zero(K, gamma, gamma) + cdef list mul_mat = [[None for j in range(gamma)] for i in range(gamma)] + for i in range(gamma): + for j in range(gamma): + f = yR[i] * yRbar[j] + v = vec_form(f) + self._exp(( dR[i]) + ( dRbar[j]), &sk, &si) + coeff_mat[i,j] = v[si][sk] + ( mul_mat[i])[j] = v if verbose: print("multiplication table") @@ -1704,7 +1681,7 @@ cdef class _EvaluationAGCodeDecoder_K(_Decoder_K): self.s0 = s0 self.tau = tau - info = {} + cdef dict info = {} info['designed_distance'] = dLO info['decoding_radius'] = tau @@ -1761,6 +1738,12 @@ cdef class _DifferentialAGCodeDecoder_K(_Decoder_K): sage: circuit = _DifferentialAGCodeDecoder_K(D, G, Q) sage: TestSuite(circuit).run(skip='_test_pickling') """ + cdef Py_ssize_t gamma, code_length, genus + cdef Py_ssize_t d, i, j, n, r, s, sk, si, num + cdef list gaps, dR, yR, dWbar, wWbar + cdef set temp + cdef tuple e + D = sum(pls) F = D.parent().function_field() K = F.constant_base_field() @@ -1793,7 +1776,7 @@ cdef class _DifferentialAGCodeDecoder_K(_Decoder_K): if b.valuation(Q) == -s: g = b break - r = s % gamma + r = pos_mod(s, gamma) if g != 0 and not yR[r]: dR[r] = s yR[r] = g @@ -1801,10 +1784,11 @@ cdef class _DifferentialAGCodeDecoder_K(_Decoder_K): s += 1 # gaps of L - gaps = set() + temp = set() for d in dR: - gaps.update([d - gamma*(i + 1) for i in range(d // gamma)]) - gaps = list(gaps) + temp.update([d - gamma*(i + 1) for i in range(d // gamma)]) + gaps = list(temp) + del temp # genus of L genus = len(gaps) @@ -1821,7 +1805,7 @@ cdef class _DifferentialAGCodeDecoder_K(_Decoder_K): if b.valuation(Q) == G.multiplicity(Q) - s: g = b break - r = s % gamma + r = pos_mod(s, gamma) if g != 0 and not wWbar[r]: dWbar[r] = s wWbar[r] = g @@ -1839,8 +1823,8 @@ cdef class _DifferentialAGCodeDecoder_K(_Decoder_K): print(" {}: {}, w{} = {}".format(i, dWbar[i], i, wWbar[i])) # res map for the monomial whose weighted degree is s - evxR = vector(K, [xR.evaluate(p) for p in pls]) - reswWbar = [vector(K, [wWbar[i].residue(p) for p in pls]) for i in range(gamma)] + cdef Vector evxR = vector(K, [xR.evaluate(p) for p in pls]) + cdef list reswWbar = [vector(K, [wWbar[i].residue(p) for p in pls]) for i in range(gamma)] self.is_differential = True self.code_length = code_length @@ -1852,73 +1836,33 @@ cdef class _DifferentialAGCodeDecoder_K(_Decoder_K): self.x = x degree = self.degree - exponents = self.exponents - - def monomial(s): - sk, si = exponents(s) - return xR**sk * wWbar[si] - def res_mon(s): - sk, si = exponents(s) + def res_mon(Py_ssize_t sk, Py_ssize_t si): + cdef Py_ssize_t i return vector([evxR[i]**sk * reswWbar[si][i] for i in range(code_length)]) - def next(s): # integer next to s in Wbar - while True: - s += 1 - if exponents(s)[0] >= 0: - return s - # minimum of nongaps of Wbar - s0 = next(-code_length + G.degree() - 2*genus + 1) + s0 = _next(self, -code_length + G.degree() - 2*genus + 1) # basis of the code res(Omega(G)) - message_index = [] - code_basis = [] + cdef list message_index = [] + cdef list code_basis = [] s = s0 - v = res_mon(s) + self._exp(s, &sk, &si) + v = res_mon(sk, si) V = v.parent() while s <= 0: if not V.are_linearly_dependent(code_basis + [v]): message_index.append(s) code_basis.append(v) - s = next(s) - v = res_mon(s) + s = _next(self, s) + self._exp(s, &sk, &si) + v = res_mon(sk, si) # compute a basis of J and h-functions via FGLM algorithm - def get_eta_basis(): - basis = [None for i in range(gamma)] - s = s0 - mat = matrix(res_mon(s)) - delta = [exponents(s)] - num = 0 - while num < gamma: - s = next(s) - e = exponents(s) - if basis[e[1]] is None: - v = res_mon(s) - try: - sol = mat.solve_left(v) - gen = [W.zero() for i in range(gamma)] - for i in range(len(delta)): - gen[delta[i][1]] += -sol[i]*x**delta[i][0] - gen[e[1]] += x**e[0] - basis[e[1]] = vector(gen) - num += 1 - except ValueError: - mat = matrix(list(mat) + [v]) - delta.append(e) - - vecs = [None for i in range(code_length)] - matinv = mat.inverse() - for i in range(code_length): - h = [W.zero() for k in range(gamma)] - for j in range(code_length): - h[delta[j][1]] += matinv[i,j]*x**delta[j][0] - vecs[i] = vector(h) - - return basis, vecs - - eta_vecs, hvecs = get_eta_basis() + eta_vecs = [None for i in range(gamma)] + hvecs = [None for i in range(code_length)] + get_eta_basis(self, eta_vecs, hvecs, s0, res_mon) if verbose: print("message indices:", message_index) @@ -1928,15 +1872,15 @@ cdef class _DifferentialAGCodeDecoder_K(_Decoder_K): print("h{} = {}".format(i, hvecs[i])) # Lee-O'Sullivan bound - def nu(s): - m = 0 + def nu(Py_ssize_t s): + cdef Py_ssize_t i, sk, si, m = 0 for i in range(gamma): - e = exponents(s + dR[i]) - m += max(0, degree(eta_vecs[e[1]][e[1]]) - e[0]) + self._exp(s + ( dR[i]), &sk, &si) + m += max(0, degree(eta_vecs[si][si]) - sk) return m - nus = [nu(s) for s in message_index] - dLO = min(nus) + cdef list nus = [nu(s) for s in message_index] + cdef Py_ssize_t dLO = min(nus) tau = (dLO - 1) // 2 if verbose: @@ -1949,30 +1893,26 @@ cdef class _DifferentialAGCodeDecoder_K(_Decoder_K): # the vector form corresponding to f in Wbar def vec_form(f): r = f - l = [W.zero() for i in range(gamma)] + cdef list l = [W.zero() for i in range(gamma)] while r != 0: s = -r.valuation(Q) + G.valuation(Q) - e = exponents(s) - mon = xR**e[0] * wWbar[e[1]] + self._exp(s, &sk, &si) + mon = xR**sk * wWbar[si] c = (r / mon).evaluate(Q) - l[e[1]] += c * x**e[0] + l[si] += c * x**sk r -= c*mon return vector(l) # the matrix of the leading coefficient of y_i*w_j and the product - def get_mul_mat(): - cm = matrix.zero(K, gamma, gamma) - vm = [[None for j in range(gamma)] for i in range(gamma)] - for i in range(gamma): - for j in range(gamma): - f = yR[i] * wWbar[j] - v = vec_form(f) - e = exponents(dR[i] + dWbar[j]) - cm[i,j] = v[e[1]][e[0]] - vm[i][j] = v - return vm, cm - - mul_mat, coeff_mat = get_mul_mat() + cdef Matrix coeff_mat = matrix.zero(K, gamma, gamma) + cdef list mul_mat = [[None for j in range(gamma)] for i in range(gamma)] + for i in range(gamma): + for j in range(gamma): + f = yR[i] * wWbar[j] + v = vec_form(f) + self._exp(( dR[i]) + ( dWbar[j]), &sk, &si) + coeff_mat[i,j] = v[si][sk] + ( mul_mat[i])[j] = v if verbose: print("multiplication table") @@ -1991,7 +1931,7 @@ cdef class _DifferentialAGCodeDecoder_K(_Decoder_K): self.s0 = s0 self.tau = tau - info = {} + cdef dict info = {} info['designed_distance'] = dLO info['decoding_radius'] = tau @@ -2038,11 +1978,11 @@ cdef class _Decoder_K_extension(object): """ cdef object _embedK, _K - cdef object decoder_ext + cdef _Decoder_K decoder_ext cdef type decoder_cls cdef readonly dict info - def __init__(self, pls, G, Q, verbose=False): + def __init__(self, pls, G, Q, decoder_cls, verbose=False): """ Initialize. @@ -2121,7 +2061,7 @@ cdef class _Decoder_K_extension(object): G_ext = conorm(G) Q_ext = lift_place(Q) - self.decoder_ext = self.decoder_cls(pls_ext, G_ext, Q_ext, verbose=verbose) + self.decoder_ext = decoder_cls(pls_ext, G_ext, Q_ext, verbose=verbose) self.info = self.decoder_ext.info def lift(self, v): @@ -2148,7 +2088,8 @@ cdef class _Decoder_K_extension(object): def pull_back(self, v): """ - Pull back a vector over the extension field to a vector over the base field. + Pull back a vector over the extension field to a vector + over the base field. TESTS:: @@ -2254,9 +2195,7 @@ cdef class _EvaluationAGCodeDecoder_K_extension(_Decoder_K_extension): sage: decoder = code.decoder('K') sage: TestSuite(decoder).run(skip='_test_pickling') """ - self.decoder_cls = _EvaluationAGCodeDecoder_K - - super().__init__(pls, G, Q, verbose=verbose) + super().__init__(pls, G, Q, _EvaluationAGCodeDecoder_K, verbose=verbose) @cython.auto_pickle(True) @@ -2300,6 +2239,87 @@ cdef class _DifferentialAGCodeDecoder_K_extension(_Decoder_K_extension): sage: decoder = code.decoder('K') sage: TestSuite(decoder).run(skip='_test_pickling') """ - self.decoder_cls = _DifferentialAGCodeDecoder_K + super().__init__(pls, G, Q, _DifferentialAGCodeDecoder_K, verbose=verbose) + +############################################################################### +## Helper functions + +cdef inline Py_ssize_t pos_mod(Py_ssize_t a, Py_ssize_t b): + """ + Return ``a % b`` such that the result is positive. + + C modulus can be negative as ``a == (a / b * b) + (a % b)``. + """ + cdef Py_ssize_t ret = a % b + if ret < 0: + ret += b + return ret + +@cython.wraparound(False) +@cython.boundscheck(False) +cdef inline Py_ssize_t _next(_Decoder_K decoder, Py_ssize_t s): + """ + Helper to get the next value after ``s`` in dRbar. + """ + cdef Py_ssize_t i, d, gamma + cdef list dRbar = decoder.dRbar + gamma = decoder.gamma + i = pos_mod(s, gamma) + while True: + # We unroll (decoder.self._exp(s)[0]) >= 0 for speed + # Note decoder.self._exp(s)[0] == (s - d) // gamma, + # so we only need to compare s with d + s += 1 + i = (i + 1) % gamma + d = dRbar[i] + if s >= d: + return s + +@cython.wraparound(False) +@cython.boundscheck(False) +cdef inline get_eta_basis(_Decoder_K decoder, list basis, list vecs, Py_ssize_t s0, mon_func): + """ + Compute a basis of J and h-functions via FGLM algorithm. + + This modifies ``basis`` and ``vecs``. + """ + cdef Py_ssize_t s, sk, si, i, j, num + cdef Matrix mat, matinv + cdef list gen, delta, h + cdef tuple e, de + + cdef Py_ssize_t gamma = decoder.gamma + cdef Py_ssize_t code_length = decoder.code_length + x = decoder.x + W = decoder.W + s = s0 + decoder._exp(s, &sk, &si) + delta = [(sk, si)] + mat = matrix(mon_func(sk, si)) + num = 0 + while num < gamma: + s = _next(decoder, s) + decoder._exp(s, &sk, &si) + if basis[si] is None: + v = mon_func(sk, si) + try: + sol = mat.solve_left(v) + gen = [W.zero() for i in range(gamma)] + for i in range(len(delta)): + de = delta[i] + gen[ de[1]] += -sol[i] * x**( de[0]) + gen[si] += x**sk + basis[si] = vector(gen) + num += 1 + except ValueError: + mat = mat.stack(matrix(v)) + delta.append((sk, si)) + + matinv = mat.inverse() + for i in range(code_length): + h = [W.zero() for k in range(gamma)] + for j in range(code_length): + de = delta[j] + h[ de[1]] += matinv[i,j] * x**( de[0]) + vecs[i] = vector(h) - super().__init__(pls, G, Q, verbose=verbose) From 5c19de3b6524e1bbdcb2d65c71897ba6faca1548 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Thu, 22 Apr 2021 09:37:32 +0200 Subject: [PATCH 318/406] Trac #21295 review issue 18: move reference to references file --- src/doc/en/reference/references/index.rst | 4 ++++ src/sage/combinat/recognizable_series.py | 8 +------- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst index 86b4fb35b48..ecd1bf6c72f 100644 --- a/src/doc/en/reference/references/index.rst +++ b/src/doc/en/reference/references/index.rst @@ -1062,6 +1062,10 @@ REFERENCES: Berkovich projective line. Mathematical Surveys and Monographs, Volumne 159. 2010. +.. [BR2010a] Jean Berstel and Christophe Reutenauer, + *Noncommutative Rational Series With Applications*. + Cambridge, 2010. + .. [Brandes01] Ulrik Brandes, A faster algorithm for betweenness centrality, Journal of Mathematical Sociology 25.2 (2001): 163-177, diff --git a/src/sage/combinat/recognizable_series.py b/src/sage/combinat/recognizable_series.py index c791d2886b2..28867de0cb3 100644 --- a/src/sage/combinat/recognizable_series.py +++ b/src/sage/combinat/recognizable_series.py @@ -38,12 +38,6 @@ Various ======= -REFERENCES: - -.. [BR2010] Jean Berstel, Christophe Reutenauer, - *Noncommutative Rational Series With Applications*, - Cambridge, 2010. - AUTHORS: - Daniel Krenn (2016) @@ -710,7 +704,7 @@ def minimized(self): ALOGRITHM: This method implements the minimization algorithm presented in - Chapter 2 of [BR2010]_. + Chapter 2 of [BR2010a]_. EXAMPLES:: From b0406926c2d1bf464b86cd8d63006df5a65cf544 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Thu, 22 Apr 2021 09:48:15 +0200 Subject: [PATCH 319/406] Trac #21295 review issue 19: remove closing punctuation for INPUT and OUTPUT if applicable --- src/sage/combinat/recognizable_series.py | 56 ++++++++++++------------ 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/src/sage/combinat/recognizable_series.py b/src/sage/combinat/recognizable_series.py index 28867de0cb3..1736ba8b2ad 100644 --- a/src/sage/combinat/recognizable_series.py +++ b/src/sage/combinat/recognizable_series.py @@ -81,10 +81,10 @@ def __init__(self, alphabet=None, words=None): INPUT: - ``alphabet`` -- finite words over this ``alphabet`` - will be created. + will be created - ``words`` -- specify the finite words directly - (instead of via ``alphabet``). + (instead of via ``alphabet``) EXAMPLES:: @@ -114,7 +114,7 @@ def __repr__(self): OUTPUT: - A string. + A string EXAMPLES:: @@ -132,9 +132,9 @@ def add(self, w, check=True): INPUT: - - ``w`` -- a word. + - ``w`` -- a word - - ``check`` -- (default: ``True``) if set, then it is verified + - ``check`` -- boolean (default: ``True``). If set, then it is verified whether all proper prefixes of ``w`` are already in this prefix-closed set. @@ -172,7 +172,7 @@ def populate_interactive(self): OUTPUT: - An iterator. + An iterator EXAMPLES:: @@ -219,7 +219,7 @@ def prefix_set(self): OUTPUT: - A list. + A list EXAMPLES:: @@ -247,7 +247,7 @@ def __init__(self, parent, mu, left, right): r""" A recognizable series. - - ``parent`` -- an instance of :class:`RecognizableSeriesSpace`. + - ``parent`` -- an instance of :class:`RecognizableSeriesSpace` - ``mu`` -- a family of square matrices, all of which have the same dimension. The indices of this family are the alphabet. @@ -368,7 +368,7 @@ def _repr_(self, latex=False): OUTPUT: - A string. + A string EXAMPLES:: @@ -410,7 +410,7 @@ def _latex_(self): OUTPUT: - A string. + A string TESTS:: @@ -432,12 +432,12 @@ def __getitem__(self, w): INPUT: - ``w`` -- a word over the parent's - :meth:`~RecognizableSeriesSpace.alphabet`. + :meth:`~RecognizableSeriesSpace.alphabet` OUTPUT: An element in the parent's - :meth:`~RecognizableSeriesSpace.coefficients`. + :meth:`~RecognizableSeriesSpace.coefficients` EXAMPLES:: @@ -458,7 +458,7 @@ def _mu_of_empty_word_(self): OUTPUT: - A matrix. + A matrix TESTS:: @@ -493,11 +493,11 @@ def _mu_of_word_(self, w): INPUT: - ``w`` -- a word over the parent's - :meth:`~RecognizableSeriesSpace.alphabet`. + :meth:`~RecognizableSeriesSpace.alphabet` OUTPUT: - A matrix. + A matrix TESTS:: @@ -658,7 +658,7 @@ def transposed(self): OUTPUT: - A :class:`RecognizableSeries`. + A :class:`RecognizableSeries` Each of the matrices in :meth:`mu ` is transposed. Additionally the vectors :meth:`left ` and :meth:`right ` are switched. @@ -699,7 +699,7 @@ def minimized(self): OUTPUT: - A :class:`RecognizableSeries`. + A :class:`RecognizableSeries` ALOGRITHM: @@ -750,7 +750,7 @@ def _minimized_right_(self): OUTPUT: - A :class:`RecognizableSeries`. + A :class:`RecognizableSeries` See :meth:`minimized` for details. @@ -773,7 +773,7 @@ def _minimized_left_(self): OUTPUT: - A :class:`RecognizableSeries`. + A :class:`RecognizableSeries` See :meth:`minimized` for details. @@ -872,7 +872,7 @@ class RecognizableSeriesSpace(UniqueRepresentation, Parent): INPUT: - - ``coefficients`` -- a (semi-)ring. + - ``coefficients`` -- a (semi-)ring - ``alphabet`` -- a tuple, list or :class:`~sage.sets.totally_ordered_finite_set.TotallyOrderedFiniteSet`. @@ -886,7 +886,7 @@ class RecognizableSeriesSpace(UniqueRepresentation, Parent): at the same time. - ``category`` -- (default: ``None``) the category of this - space. + space EXAMPLES: @@ -1008,12 +1008,12 @@ def __init__(self, coefficients, indices, category): INPUT: - - ``coefficients`` -- a (semi-)ring. + - ``coefficients`` -- a (semi-)ring - - ``indices`` -- a SageMath-parent of finite words over an alphabet. + - ``indices`` -- a SageMath-parent of finite words over an alphabet - ``category`` -- (default: ``None``) the category of this - space. + space TESTS:: @@ -1031,7 +1031,7 @@ def alphabet(self): OUTPUT: - A totally ordered set. + A totally ordered set EXAMPLES:: @@ -1052,7 +1052,7 @@ def indices(self): OUTPUT: - The set of finite words over the alphabet. + The set of finite words over the alphabet EXAMPLES:: @@ -1068,7 +1068,7 @@ def coefficients(self): OUTPUT: - A (semi-)ring. + A (semi-)ring EXAMPLES:: @@ -1085,7 +1085,7 @@ def _repr_(self): OUTPUT: - A string. + A string TESTS:: From 5efa0b01d4c3759b024a85c7c5cbd191f9e7780f Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Thu, 22 Apr 2021 10:10:24 +0200 Subject: [PATCH 320/406] Trac #21295 review issue 20+21: rewrite PrefixClosedSet.__init__ --- src/sage/combinat/recognizable_series.py | 55 +++++++++++++++--------- 1 file changed, 34 insertions(+), 21 deletions(-) diff --git a/src/sage/combinat/recognizable_series.py b/src/sage/combinat/recognizable_series.py index 1736ba8b2ad..598faf54975 100644 --- a/src/sage/combinat/recognizable_series.py +++ b/src/sage/combinat/recognizable_series.py @@ -71,41 +71,54 @@ class PrefixClosedSet(object): - def __init__(self, alphabet=None, words=None): + def __init__(self, words): r""" A prefix-closed set. - Creation of this prefix-closed sets is interactive + Creation of this prefix-closed set is interactive iteratively. INPUT: - - ``alphabet`` -- finite words over this ``alphabet`` - will be created - - - ``words`` -- specify the finite words directly - (instead of via ``alphabet``) + - ``words`` -- a class of words + (instance of :class:`~sage.combinat.words.words.Words`) EXAMPLES:: sage: from sage.combinat.recognizable_series import PrefixClosedSet - sage: P = PrefixClosedSet(alphabet=[0, 1]); P + sage: P = PrefixClosedSet(Words([0, 1], infinite=False)); P + [word: ] + + sage: P = PrefixClosedSet.create_by_alphabet([0, 1]); P [word: ] See :meth:`populate_interactive` for further examples. + """ + self.words = words + self.elements = [self.words([])] - TESTS:: - sage: P = PrefixClosedSet( - ....: words=Words([0, 1], infinite=False)); P + @classmethod + def create_by_alphabet(cls, alphabet): + r""" + A prefix-closed set + + This is a convenience method for the + creation of prefix-closed sets by specifying an alphabeth. + + INPUT: + + - ``alphabet`` -- finite words over this ``alphabet`` + will used + + EXAMPLES:: + + sage: from sage.combinat.recognizable_series import PrefixClosedSet + sage: P = PrefixClosedSet.create_by_alphabet([0, 1]); P [word: ] """ - if alphabet is not None: - from sage.combinat.words.words import Words - self.words = Words(alphabet, infinite=False) - else: - self.words = words - self.elements = [self.words([])] + from sage.combinat.words.words import Words + return cls(Words(alphabet, infinite=False)) def __repr__(self): @@ -119,7 +132,7 @@ def __repr__(self): EXAMPLES:: sage: from sage.combinat.recognizable_series import PrefixClosedSet - sage: P = PrefixClosedSet(alphabet=[0, 1]) + sage: P = PrefixClosedSet.create_by_alphabet([0, 1]) sage: repr(P) # indirect doctest '[word: ]' """ @@ -147,7 +160,7 @@ def add(self, w, check=True): EXAMPLES:: sage: from sage.combinat.recognizable_series import PrefixClosedSet - sage: P = PrefixClosedSet(alphabet=[0, 1]) + sage: P = PrefixClosedSet.create_by_alphabet([0, 1]) sage: W = P.words sage: P.add(W([0])); P [word: , word: 0] @@ -177,7 +190,7 @@ def populate_interactive(self): EXAMPLES:: sage: from sage.combinat.recognizable_series import PrefixClosedSet - sage: P = PrefixClosedSet(alphabet=[0, 1]); P + sage: P = PrefixClosedSet.create_by_alphabet([0, 1]); P [word: ] sage: for n, p in enumerate(P.populate_interactive()): ....: print('{}?'.format(p)) @@ -224,7 +237,7 @@ def prefix_set(self): EXAMPLES:: sage: from sage.combinat.recognizable_series import PrefixClosedSet - sage: P = PrefixClosedSet(alphabet=[0, 1]); P + sage: P = PrefixClosedSet.create_by_alphabet([0, 1]); P [word: ] sage: for n, p in enumerate(P.populate_interactive()): ....: if n in (0, 1, 2, 4, 6): From 75697d5cd436674ffc32c1bb470a9d7ab2a7c7ad Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Thu, 22 Apr 2021 10:12:49 +0200 Subject: [PATCH 321/406] Trac #21295 review issue 22: rename to iterate_possible_additions --- src/sage/combinat/recognizable_series.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/sage/combinat/recognizable_series.py b/src/sage/combinat/recognizable_series.py index 598faf54975..fab9b094f15 100644 --- a/src/sage/combinat/recognizable_series.py +++ b/src/sage/combinat/recognizable_series.py @@ -92,7 +92,7 @@ def __init__(self, words): sage: P = PrefixClosedSet.create_by_alphabet([0, 1]); P [word: ] - See :meth:`populate_interactive` for further examples. + See :meth:`iterate_possible_additions` for further examples. """ self.words = words self.elements = [self.words([])] @@ -179,7 +179,7 @@ def add(self, w, check=True): self.elements.append(w) - def populate_interactive(self): + def iterate_possible_additions(self): r""" Return an iterator over possible new elements. @@ -192,7 +192,7 @@ def populate_interactive(self): sage: from sage.combinat.recognizable_series import PrefixClosedSet sage: P = PrefixClosedSet.create_by_alphabet([0, 1]); P [word: ] - sage: for n, p in enumerate(P.populate_interactive()): + sage: for n, p in enumerate(P.iterate_possible_additions()): ....: print('{}?'.format(p)) ....: if n in (0, 2, 3, 5): ....: P.add(p) @@ -239,7 +239,7 @@ def prefix_set(self): sage: from sage.combinat.recognizable_series import PrefixClosedSet sage: P = PrefixClosedSet.create_by_alphabet([0, 1]); P [word: ] - sage: for n, p in enumerate(P.populate_interactive()): + sage: for n, p in enumerate(P.iterate_possible_additions()): ....: if n in (0, 1, 2, 4, 6): ....: P.add(p) sage: P @@ -839,7 +839,7 @@ def _minimized_left_(self): if left.is_zero(): return self.parent().zero() Left = [left] - for p in pcs.populate_interactive(): + for p in pcs.iterate_possible_additions(): left = self.left * self._mu_of_word_(p) try: Matrix(Left).solve_left(left) From 57492a405fdf6c3bb00bd0767e26f2e80301c4d2 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Thu, 22 Apr 2021 10:20:17 +0200 Subject: [PATCH 322/406] Trac #21295 review issue 23: clearify populate_interactive recall --- src/sage/combinat/recognizable_series.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/sage/combinat/recognizable_series.py b/src/sage/combinat/recognizable_series.py index fab9b094f15..b4b6dd99dd6 100644 --- a/src/sage/combinat/recognizable_series.py +++ b/src/sage/combinat/recognizable_series.py @@ -181,7 +181,7 @@ def add(self, w, check=True): def iterate_possible_additions(self): r""" - Return an iterator over possible new elements. + Return an iterator over all elements including possible new elements. OUTPUT: @@ -213,6 +213,20 @@ def iterate_possible_additions(self): 0011? sage: P.elements [word: , word: 0, word: 00, word: 01, word: 001] + + Calling the iterator once more, returns all elements:: + + sage: list(P.iterate_possible_additions()) + [word: 0, + word: 1, + word: 00, + word: 01, + word: 000, + word: 001, + word: 010, + word: 011, + word: 0010, + word: 0011] """ n = 0 it = self.words.iterate_by_length(1) From 692e4384e936bd05e461c2ac6169f2d251d07e0d Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Thu, 22 Apr 2021 10:23:32 +0200 Subject: [PATCH 323/406] Trac #21295 review issue 24 rewrite description of .prefix_set --- src/sage/combinat/recognizable_series.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/sage/combinat/recognizable_series.py b/src/sage/combinat/recognizable_series.py index b4b6dd99dd6..afc23e4d69b 100644 --- a/src/sage/combinat/recognizable_series.py +++ b/src/sage/combinat/recognizable_series.py @@ -241,8 +241,10 @@ def iterate_possible_additions(self): def prefix_set(self): r""" - Return the prefix set corresponding to this prefix - closed set. + Return the set of minimal (with respect to prefix ordering) elements + of the complement of this prefix closed set. + + See also Proposition 2.3.1 of [BR2010a]_. OUTPUT: From 1e8b981812ae8bbcf243fd440e8219465bf3e260 Mon Sep 17 00:00:00 2001 From: Kwankyu Lee Date: Thu, 22 Apr 2021 21:25:33 +0900 Subject: [PATCH 324/406] Improve readibility --- src/sage/coding/ag_code_decoders.pyx | 806 ++++++++---------- src/sage/coding/tests/decoder_K.sobj | Bin 0 -> 1496 bytes .../coding/tests/decoder_K_extension.sobj | Bin 0 -> 10891 bytes src/sage/coding/tests/diff_decoder.sobj | Bin 0 -> 8804 bytes src/sage/coding/tests/diff_decoder_K.sobj | Bin 0 -> 1457 bytes .../tests/diff_decoder_K_extension.sobj | Bin 0 -> 2709 bytes .../coding/tests/differential_ag_code.sobj | Bin 9112 -> 0 bytes src/sage/coding/tests/eval_decoder.sobj | Bin 0 -> 7414 bytes src/sage/coding/tests/eval_decoder_K.sobj | Bin 0 -> 1496 bytes .../tests/eval_decoder_K_extension.sobj | Bin 0 -> 2675 bytes src/sage/coding/tests/evaluation_ag_code.sobj | Bin 7681 -> 0 bytes 11 files changed, 360 insertions(+), 446 deletions(-) create mode 100644 src/sage/coding/tests/decoder_K.sobj create mode 100644 src/sage/coding/tests/decoder_K_extension.sobj create mode 100644 src/sage/coding/tests/diff_decoder.sobj create mode 100644 src/sage/coding/tests/diff_decoder_K.sobj create mode 100644 src/sage/coding/tests/diff_decoder_K_extension.sobj delete mode 100644 src/sage/coding/tests/differential_ag_code.sobj create mode 100644 src/sage/coding/tests/eval_decoder.sobj create mode 100644 src/sage/coding/tests/eval_decoder_K.sobj create mode 100644 src/sage/coding/tests/eval_decoder_K_extension.sobj delete mode 100644 src/sage/coding/tests/evaluation_ag_code.sobj diff --git a/src/sage/coding/ag_code_decoders.pyx b/src/sage/coding/ag_code_decoders.pyx index daab79bb7ec..7e0c1ed31bf 100644 --- a/src/sage/coding/ag_code_decoders.pyx +++ b/src/sage/coding/ag_code_decoders.pyx @@ -103,7 +103,7 @@ class EvaluationAGCodeEncoder(Encoder): Constructing a decoder for an AG code takes much time. So we save the decoder (and the connected encoder) by:: - sage: save(dec, SAGE_SRC + '/sage/coding/tests/evaluation_ag_code') # not tested + sage: save(dec, SAGE_SRC + '/sage/coding/tests/eval_decoder') # not tested and load it for later examples. """ @@ -113,7 +113,7 @@ class EvaluationAGCodeEncoder(Encoder): TESTS:: - sage: dec = load(SAGE_SRC + '/sage/coding/tests/evaluation_ag_code') + sage: dec = load(SAGE_SRC + '/sage/coding/tests/eval_decoder') sage: enc = dec.connected_encoder() sage: TestSuite(enc).run(skip='_test_pickling') """ @@ -122,6 +122,7 @@ class EvaluationAGCodeEncoder(Encoder): if decoder is None: decoder = code.decoder('K') + self._decoder = decoder self._encode = decoder._encode self._unencode = decoder._decode @@ -131,7 +132,7 @@ class EvaluationAGCodeEncoder(Encoder): TESTS:: - sage: dec = load(SAGE_SRC + '/sage/coding/tests/evaluation_ag_code') + sage: dec = load(SAGE_SRC + '/sage/coding/tests/eval_decoder') sage: enc = dec.connected_encoder() sage: {enc: 1} {Encoder for [8, 5] evaluation AG code over GF(4): 1} @@ -144,19 +145,9 @@ class EvaluationAGCodeEncoder(Encoder): TESTS:: - sage: F. = GF(4) - sage: P. = AffineSpace(F, 2); - sage: C = Curve(y^2 + y - x^3) - sage: F = C.function_field() - sage: pls = F.places() - sage: p = C([0,0]) - sage: Q, = p.places() - sage: D = [pl for pl in pls if pl != Q] - sage: G = 5*Q - sage: code = codes.EvaluationAGCode(D, G) - sage: dec1 = code.decoder('K', Q) + sage: dec1 = load(SAGE_SRC + '/sage/coding/tests/eval_decoder') sage: enc1 = dec1.connected_encoder() - sage: dec2 = code.decoder('K', Q) + sage: dec2 = load(SAGE_SRC + '/sage/coding/tests/eval_decoder') sage: enc2 = dec2.connected_encoder() sage: enc1 == enc2 True @@ -167,7 +158,7 @@ class EvaluationAGCodeEncoder(Encoder): if not isinstance(other, EvaluationAGCodeEncoder): return False - return self.code() == other.code() and self._encode == other._encode + return self.code() == other.code() and self._decoder == other._decoder def _repr_(self): r""" @@ -175,12 +166,12 @@ class EvaluationAGCodeEncoder(Encoder): TESTS:: - sage: dec = load(SAGE_SRC + '/sage/coding/tests/evaluation_ag_code') + sage: dec = load(SAGE_SRC + '/sage/coding/tests/eval_decoder') sage: enc = dec.connected_encoder() sage: enc Encoder for [8, 5] evaluation AG code over GF(4) """ - return "Encoder for {}" .format(self.code()) + return "Encoder for {}".format(self.code()) def _latex_(self): r""" @@ -188,7 +179,7 @@ class EvaluationAGCodeEncoder(Encoder): TESTS:: - sage: dec = load(SAGE_SRC + '/sage/coding/tests/evaluation_ag_code') + sage: dec = load(SAGE_SRC + '/sage/coding/tests/eval_decoder') sage: enc = dec.connected_encoder() sage: latex(enc) \text{Encoder for }[8, 5]\text{ evaluation AG code over }\Bold{F}_{2^{2}} @@ -205,7 +196,7 @@ class EvaluationAGCodeEncoder(Encoder): EXAMPLES:: - sage: dec = load(SAGE_SRC + '/sage/coding/tests/evaluation_ag_code') + sage: dec = load(SAGE_SRC + '/sage/coding/tests/eval_decoder') sage: enc = dec.connected_encoder() sage: msg = enc.message_space().random_element() sage: codeword = enc.encode(msg) @@ -224,7 +215,7 @@ class EvaluationAGCodeEncoder(Encoder): EXAMPLES:: - sage: dec = load(SAGE_SRC + '/sage/coding/tests/evaluation_ag_code') + sage: dec = load(SAGE_SRC + '/sage/coding/tests/eval_decoder') sage: enc = dec.connected_encoder() sage: msg = enc.message_space().random_element() sage: codeword = enc.encode(msg) @@ -263,7 +254,7 @@ class DifferentialAGCodeEncoder(Encoder): Constructing a decoder for an AG code takes much time. So we save the decoder (and the connected encoder) by:: - sage: save(dec, SAGE_SRC + '/sage/coding/tests/differential_ag_code') # not tested + sage: save(dec, SAGE_SRC + '/sage/coding/tests/diff_decoder') # not tested and load it for later examples. """ @@ -273,7 +264,7 @@ class DifferentialAGCodeEncoder(Encoder): TESTS:: - sage: dec = load(SAGE_SRC + '/sage/coding/tests/differential_ag_code') + sage: dec = load(SAGE_SRC + '/sage/coding/tests/diff_decoder') sage: enc = dec.connected_encoder() sage: TestSuite(enc).run(skip='_test_pickling') """ @@ -282,6 +273,7 @@ class DifferentialAGCodeEncoder(Encoder): if decoder is None: decoder = code.decoder('K') + self._decoder = decoder self._encode = decoder._encode self._unencode = decoder._decode @@ -291,7 +283,7 @@ class DifferentialAGCodeEncoder(Encoder): TESTS:: - sage: dec = load(SAGE_SRC + '/sage/coding/tests/differential_ag_code') + sage: dec = load(SAGE_SRC + '/sage/coding/tests/diff_decoder') sage: enc = dec.connected_encoder() sage: {enc: 1} {Encoder for [8, 3] differential AG code over GF(4): 1} @@ -304,19 +296,9 @@ class DifferentialAGCodeEncoder(Encoder): TESTS:: - sage: F. = GF(4) - sage: P. = AffineSpace(F, 2); - sage: C = Curve(y^2 + y - x^3) - sage: F = C.function_field() - sage: pls = F.places() - sage: p = C([0,0]) - sage: Q, = p.places() - sage: D = [pl for pl in pls if pl != Q] - sage: G = 5*Q - sage: code = codes.DifferentialAGCode(D, G) - sage: dec1 = code.decoder('K', Q) + sage: dec1 = load(SAGE_SRC + '/sage/coding/tests/diff_decoder') sage: enc1 = dec1.connected_encoder() - sage: dec2 = code.decoder('K', Q) + sage: dec2 = load(SAGE_SRC + '/sage/coding/tests/diff_decoder') sage: enc2 = dec2.connected_encoder() sage: enc1 == enc2 True @@ -327,7 +309,7 @@ class DifferentialAGCodeEncoder(Encoder): if not isinstance(other, DifferentialAGCodeEncoder): return False - return self.code() == other.code() and self._encode == other._encode + return self.code() == other.code() and self._decoder == other._decoder def _repr_(self): r""" @@ -335,7 +317,7 @@ class DifferentialAGCodeEncoder(Encoder): TESTS:: - sage: dec = load(SAGE_SRC + '/sage/coding/tests/differential_ag_code') + sage: dec = load(SAGE_SRC + '/sage/coding/tests/diff_decoder') sage: enc = dec.connected_encoder() sage: enc Encoder for [8, 3] differential AG code over GF(4) @@ -348,7 +330,7 @@ class DifferentialAGCodeEncoder(Encoder): TESTS:: - sage: dec = load(SAGE_SRC + '/sage/coding/tests/differential_ag_code') + sage: dec = load(SAGE_SRC + '/sage/coding/tests/diff_decoder') sage: enc = dec.connected_encoder() sage: latex(enc) \text{Encoder for }[8, 3]\text{ differential AG code over }\Bold{F}_{2^{2}} @@ -365,7 +347,7 @@ class DifferentialAGCodeEncoder(Encoder): EXAMPLES:: - sage: dec = load(SAGE_SRC + '/sage/coding/tests/differential_ag_code') + sage: dec = load(SAGE_SRC + '/sage/coding/tests/diff_decoder') sage: enc = dec.connected_encoder() sage: msg = enc.message_space().random_element() sage: codeword = enc.encode(msg) @@ -384,7 +366,7 @@ class DifferentialAGCodeEncoder(Encoder): EXAMPLES:: - sage: dec = load(SAGE_SRC + '/sage/coding/tests/differential_ag_code') + sage: dec = load(SAGE_SRC + '/sage/coding/tests/diff_decoder') sage: enc = dec.connected_encoder() sage: msg = enc.message_space().random_element() sage: codeword = enc.encode(msg) @@ -455,7 +437,7 @@ class EvaluationAGCodeUniqueDecoder(Decoder): TESTS:: - sage: dec = load(SAGE_SRC + '/sage/coding/tests/evaluation_ag_code') + sage: dec = load(SAGE_SRC + '/sage/coding/tests/eval_decoder') sage: TestSuite(dec).run() """ if not code.dimension() > 0: @@ -484,10 +466,10 @@ class EvaluationAGCodeUniqueDecoder(Decoder): super().__init__(code, code.ambient_space(), connected_encoder_name='evaluation') if Q.degree() > 1: - circuit = _EvaluationAGCodeDecoder_K_extension(code._pls, code._G, Q, + circuit = EvaluationAGCodeDecoder_K_extension(code._pls, code._G, Q, verbose=verbose) else: - circuit = _EvaluationAGCodeDecoder_K(code._pls, code._G, Q, + circuit = EvaluationAGCodeDecoder_K(code._pls, code._G, Q, verbose=verbose) if basis is None: @@ -512,7 +494,7 @@ class EvaluationAGCodeUniqueDecoder(Decoder): EXAMPLES:: - sage: dec = load(SAGE_SRC + '/sage/coding/tests/evaluation_ag_code') + sage: dec = load(SAGE_SRC + '/sage/coding/tests/eval_decoder') sage: {dec: 1} {Unique decoder for [8, 5] evaluation AG code over GF(4): 1} """ @@ -524,17 +506,9 @@ class EvaluationAGCodeUniqueDecoder(Decoder): EXAMPLES:: - sage: F. = GF(4) - sage: P. = AffineSpace(F, 2); - sage: C = Curve(y^2 + y - x^3) - sage: F = C.function_field() - sage: pls = F.places() - sage: p = C([0,0]) - sage: Q, = p.places() - sage: D = [pl for pl in pls if pl != Q] - sage: c1 = codes.EvaluationAGCode(D, 5*Q) - sage: c2 = codes.EvaluationAGCode(D, 5*Q) - sage: c1 == c2 + sage: dec1 = load(SAGE_SRC + '/sage/coding/tests/eval_decoder') + sage: dec2 = load(SAGE_SRC + '/sage/coding/tests/eval_decoder') + sage: dec1 == dec2 True """ if self is other: @@ -552,7 +526,7 @@ class EvaluationAGCodeUniqueDecoder(Decoder): EXAMPLES:: - sage: dec = load(SAGE_SRC + '/sage/coding/tests/evaluation_ag_code') + sage: dec = load(SAGE_SRC + '/sage/coding/tests/eval_decoder') sage: dec Unique decoder for [8, 5] evaluation AG code over GF(4) """ @@ -564,7 +538,7 @@ class EvaluationAGCodeUniqueDecoder(Decoder): EXAMPLES:: - sage: dec = load(SAGE_SRC + '/sage/coding/tests/evaluation_ag_code') + sage: dec = load(SAGE_SRC + '/sage/coding/tests/eval_decoder') sage: latex(dec) \text{Unique decoder for }[8, 5]\text{ evaluation AG code over }\Bold{F}_{2^{2}} """ @@ -580,7 +554,7 @@ class EvaluationAGCodeUniqueDecoder(Decoder): TESTS: - sage: dec = load(SAGE_SRC + '/sage/coding/tests/evaluation_ag_code') + sage: dec = load(SAGE_SRC + '/sage/coding/tests/eval_decoder') sage: enc = dec.connected_encoder() sage: msg = enc.message_space().random_element() sage: dec._decode(dec._encode(msg)) == msg @@ -591,8 +565,8 @@ class EvaluationAGCodeUniqueDecoder(Decoder): circuit = self._circuit if self._extension: - internal_message = circuit.lift(vector(K, message)) * C - return circuit.pull_back(circuit.encode(internal_message)) + internal_message = circuit._lift(vector(K, message)) * C + return circuit._pull_back(circuit.encode(internal_message)) else: return circuit.encode(vector(K, message) * C) @@ -606,7 +580,7 @@ class EvaluationAGCodeUniqueDecoder(Decoder): TESTS: - sage: dec = load(SAGE_SRC + '/sage/coding/tests/evaluation_ag_code') + sage: dec = load(SAGE_SRC + '/sage/coding/tests/eval_decoder') sage: code = dec.code() sage: cw = code.random_element() sage: dec._encode(dec._decode(cw)) == cw @@ -616,8 +590,8 @@ class EvaluationAGCodeUniqueDecoder(Decoder): circuit = self._circuit if self._extension: - internal_message = circuit.decode(circuit.lift(vector), **kwargs) * Cinv - return circuit.pull_back(internal_message) + internal_message = circuit.decode(circuit._lift(vector), **kwargs) * Cinv + return circuit._pull_back(internal_message) else: return circuit.decode(vector, **kwargs) * Cinv @@ -632,7 +606,7 @@ class EvaluationAGCodeUniqueDecoder(Decoder): EXAMPLES:: - sage: dec = load(SAGE_SRC + '/sage/coding/tests/evaluation_ag_code') + sage: dec = load(SAGE_SRC + '/sage/coding/tests/eval_decoder') sage: dec.connected_encoder() Encoder for [8, 5] evaluation AG code over GF(4) """ @@ -644,7 +618,7 @@ class EvaluationAGCodeUniqueDecoder(Decoder): EXAMPLES:: - sage: dec = load(SAGE_SRC + '/sage/coding/tests/evaluation_ag_code') + sage: dec = load(SAGE_SRC + '/sage/coding/tests/eval_decoder') sage: dec.decoding_radius() 1 """ @@ -663,7 +637,7 @@ class EvaluationAGCodeUniqueDecoder(Decoder): EXAMPLES:: - sage: dec = load(SAGE_SRC + '/sage/coding/tests/evaluation_ag_code') + sage: dec = load(SAGE_SRC + '/sage/coding/tests/eval_decoder') sage: enc = dec.connected_encoder() sage: code = dec.code() sage: chan = channels.StaticErrorRateChannel(code.ambient_space(), 1) @@ -688,7 +662,7 @@ class EvaluationAGCodeUniqueDecoder(Decoder): EXAMPLES:: - sage: dec = load(SAGE_SRC + '/sage/coding/tests/evaluation_ag_code') + sage: dec = load(SAGE_SRC + '/sage/coding/tests/eval_decoder') sage: code = dec.code() sage: chan = channels.StaticErrorRateChannel(code.ambient_space(), 1) sage: rv = chan.transmit(code.random_element()) @@ -751,12 +725,6 @@ class DifferentialAGCodeUniqueDecoder(Decoder): True The default ``basis`` is given by ``code.basis_differentials()``. - - Saving the decoder for later examples and tests:: - - sage: save(dec, DOT_SAGE + 'temp/dec') - - The saved object will be removed after the final example. """ _decoder_type = {'always-succeed'} @@ -766,7 +734,7 @@ class DifferentialAGCodeUniqueDecoder(Decoder): TESTS:: - sage: dec = load(SAGE_SRC + '/sage/coding/tests/differential_ag_code') + sage: dec = load(SAGE_SRC + '/sage/coding/tests/diff_decoder') sage: TestSuite(dec).run() """ if not code.dimension() > 0: @@ -795,10 +763,10 @@ class DifferentialAGCodeUniqueDecoder(Decoder): super().__init__(code, code.ambient_space(), connected_encoder_name='residue') if Q.degree() > 1: - circuit = _DifferentialAGCodeDecoder_K_extension(code._pls, code._G, Q, + circuit = DifferentialAGCodeDecoder_K_extension(code._pls, code._G, Q, verbose=verbose) else: - circuit = _DifferentialAGCodeDecoder_K(code._pls, code._G, Q, + circuit = DifferentialAGCodeDecoder_K(code._pls, code._G, Q, verbose=verbose) if basis is None: @@ -823,7 +791,7 @@ class DifferentialAGCodeUniqueDecoder(Decoder): TESTS:: - sage: dec = load(SAGE_SRC + '/sage/coding/tests/differential_ag_code') + sage: dec = load(SAGE_SRC + '/sage/coding/tests/diff_decoder') sage: {dec: 1} {Unique decoder for [8, 3] differential AG code over GF(4): 1} """ @@ -835,17 +803,9 @@ class DifferentialAGCodeUniqueDecoder(Decoder): TESTS:: - sage: F. = GF(4) - sage: P. = AffineSpace(F, 2); - sage: C = Curve(y^2 + y - x^3) - sage: F = C.function_field() - sage: pls = F.places() - sage: p = C([0,0]) - sage: Q, = p.places() - sage: D = [pl for pl in pls if pl != Q] - sage: c1 = codes.DifferentialAGCode(D, 5*Q) - sage: c2 = codes.DifferentialAGCode(D, 5*Q) - sage: c1 == c2 + sage: dec1 = load(SAGE_SRC + '/sage/coding/tests/diff_decoder') + sage: dec2 = load(SAGE_SRC + '/sage/coding/tests/diff_decoder') + sage: dec1 == dec2 True """ if self is other: @@ -863,7 +823,7 @@ class DifferentialAGCodeUniqueDecoder(Decoder): TESTS:: - sage: dec = load(SAGE_SRC + '/sage/coding/tests/differential_ag_code') + sage: dec = load(SAGE_SRC + '/sage/coding/tests/diff_decoder') sage: dec Unique decoder for [8, 3] differential AG code over GF(4) """ @@ -875,7 +835,7 @@ class DifferentialAGCodeUniqueDecoder(Decoder): TESTS:: - sage: dec = load(SAGE_SRC + '/sage/coding/tests/differential_ag_code') + sage: dec = load(SAGE_SRC + '/sage/coding/tests/diff_decoder') sage: latex(dec) \text{Unique decoder for }[8, 3]\text{ differential AG code over }\Bold{F}_{2^{2}} """ @@ -891,7 +851,7 @@ class DifferentialAGCodeUniqueDecoder(Decoder): TESTS:: - sage: dec = load(SAGE_SRC + '/sage/coding/tests/differential_ag_code') + sage: dec = load(SAGE_SRC + '/sage/coding/tests/diff_decoder') sage: enc = dec.connected_encoder() sage: msg = enc.message_space().random_element() sage: dec._decode(dec._encode(msg)) == msg @@ -902,8 +862,8 @@ class DifferentialAGCodeUniqueDecoder(Decoder): circuit = self._circuit if self._extension: - internal_message = circuit.lift(vector(K, message)) * C - return circuit.pull_back(circuit.encode(internal_message)) + internal_message = circuit._lift(vector(K, message)) * C + return circuit._pull_back(circuit.encode(internal_message)) else: return circuit.encode(vector(K, message) * C) @@ -917,7 +877,7 @@ class DifferentialAGCodeUniqueDecoder(Decoder): TESTS:: - sage: dec = load(SAGE_SRC + '/sage/coding/tests/differential_ag_code') + sage: dec = load(SAGE_SRC + '/sage/coding/tests/diff_decoder') sage: code = dec.code() sage: cw = code.random_element() sage: dec._encode(dec._decode(cw)) == cw @@ -927,8 +887,8 @@ class DifferentialAGCodeUniqueDecoder(Decoder): circuit = self._circuit if self._extension: - internal_message = circuit.decode(circuit.lift(vector), **kwargs) * Cinv - return circuit.pull_back(internal_message) + internal_message = circuit.decode(circuit._lift(vector), **kwargs) * Cinv + return circuit._pull_back(internal_message) else: return circuit.decode(vector, **kwargs) * Cinv @@ -943,7 +903,7 @@ class DifferentialAGCodeUniqueDecoder(Decoder): EXAMPLES:: - sage: dec = load(SAGE_SRC + '/sage/coding/tests/differential_ag_code') + sage: dec = load(SAGE_SRC + '/sage/coding/tests/diff_decoder') sage: dec.connected_encoder() Encoder for [8, 3] differential AG code over GF(4) """ @@ -955,7 +915,7 @@ class DifferentialAGCodeUniqueDecoder(Decoder): EXAMPLES:: - sage: dec = load(SAGE_SRC + '/sage/coding/tests/differential_ag_code') + sage: dec = load(SAGE_SRC + '/sage/coding/tests/diff_decoder') sage: dec.decoding_radius() 2 """ @@ -974,7 +934,7 @@ class DifferentialAGCodeUniqueDecoder(Decoder): EXAMPLES:: - sage: dec = load(SAGE_SRC + '/sage/coding/tests/differential_ag_code') + sage: dec = load(SAGE_SRC + '/sage/coding/tests/diff_decoder') sage: enc = dec.connected_encoder() sage: code = dec.code() sage: chan = channels.StaticErrorRateChannel(code.ambient_space(), 2) @@ -999,7 +959,7 @@ class DifferentialAGCodeUniqueDecoder(Decoder): EXAMPLES:: - sage: dec = load(SAGE_SRC + '/sage/coding/tests/differential_ag_code') + sage: dec = load(SAGE_SRC + '/sage/coding/tests/diff_decoder') sage: enc = dec.connected_encoder() sage: code = dec.code() sage: chan = channels.StaticErrorRateChannel(code.ambient_space(), 2) @@ -1011,16 +971,46 @@ class DifferentialAGCodeUniqueDecoder(Decoder): return self._encode(self._decode(received_vector, **kwargs)) -cdef class _Decoder_K(object): +cdef inline int pos_mod(int a, int b): """ - Common base class for both differential and evaluation AG code decoder K. + Return ``a % b`` such that the result is positive. + + C modulus can be negative as ``a == (a / b) * b + (a % b)``. + """ + cdef int m = a % b + if m < 0: + m += b + return m + + +cdef class Decoder_K(object): + """ + Common base class for the implementation of decoding algorithm K for AG codes. + + EXAMPLES:: + + sage: F. = GF(4) + sage: P. = AffineSpace(F, 2); + sage: C = Curve(y^2 + y - x^3) + sage: pls = C.places() + sage: p = C([0,0]) + sage: Q, = p.places() + sage: D = [pl for pl in pls if pl != Q] + sage: G = 5*Q + sage: from sage.coding.ag_code_decoders import EvaluationAGCodeDecoder_K + sage: circuit = EvaluationAGCodeDecoder_K(D, G, Q) + + We save the decoder for later tests:: + + sage: save(circuit, SAGE_SRC + '/sage/coding/tests/decoder_K') # not tested + """ cdef bint is_differential - cdef Py_ssize_t code_length, designed_distance, gamma, s0, tau + cdef int code_length, designed_distance, gamma, s0, tau cdef list code_basis, message_index, hvecs, eta_vecs cdef list dR, dRbar - cdef Matrix coeff_mat cdef list mul_mat + cdef Matrix coeff_mat cdef object W, x cdef readonly dict info @@ -1031,16 +1021,8 @@ cdef class _Decoder_K(object): TESTS:: + sage: circuit = load(SAGE_SRC + '/sage/coding/tests/decoder_K') sage: F. = GF(4) - sage: P. = AffineSpace(F, 2); - sage: C = Curve(y^2 + y - x^3) - sage: pls = C.places() - sage: p = C([0,0]) - sage: Q, = p.places() - sage: D = [pl for pl in pls if pl != Q] - sage: G = 5*Q - sage: from sage.coding.ag_code_decoders import _EvaluationAGCodeDecoder_K - sage: circuit = _EvaluationAGCodeDecoder_K(D, G, Q) sage: rv = vector([0, 0, 0, a, 0, a, a + 1, 0]) sage: msg = circuit.decode(rv) sage: circuit.decode(circuit.encode(msg)) == msg @@ -1050,23 +1032,14 @@ cdef class _Decoder_K(object): message_index = self.message_index return vector(sum([message[i]*code_basis[i] for i in range(len(message_index))])) - def degree(self, f): + def _degree(self, f): """ Return the degree of polynomial ``f`` TESTS:: - sage: F. = GF(4) - sage: P. = AffineSpace(F, 2); - sage: C = Curve(y^2 + y - x^3) - sage: pls = C.places() - sage: p = C([0,0]) - sage: Q, = p.places() - sage: D = [pl for pl in pls if pl != Q] - sage: G = 5*Q - sage: from sage.coding.ag_code_decoders import _EvaluationAGCodeDecoder_K - sage: circuit = _EvaluationAGCodeDecoder_K(D, G, Q) - sage: circuit.degree(0) + sage: circuit = load(SAGE_SRC + '/sage/coding/tests/decoder_K') + sage: circuit._degree(0) -Infinity """ if f.is_zero(): @@ -1074,74 +1047,44 @@ cdef class _Decoder_K(object): else: return f.degree() - cdef _exp(self, Py_ssize_t s, Py_ssize_t *sk, Py_ssize_t *si): + cdef void _exponents(self, int s, int *sk, int *si): """ - Implementation of the exponents. + Compute the exponents of the monomial with weighted degree ``s``. This sets the result in ``sk`` and ``si``. """ - cdef Py_ssize_t i, d, gamma + cdef int i, d, gamma cdef list dRbar gamma = self.gamma dRbar = self.dRbar # dWbar for differential AG code i = pos_mod(s, gamma) - d = dRbar[i] + d = dRbar[i] sk[0] = (s - d) // gamma si[0] = i - #return (s - d) // gamma, i - def exponents(self, Py_ssize_t s): - """ - Return the exponents of the monomial with weighted degree ``s``. - - TESTS:: - - sage: F. = GF(4) - sage: P. = AffineSpace(F, 2); - sage: C = Curve(y^2 + y - x^3) - sage: pls = C.places() - sage: p = C([0,0]) - sage: Q, = p.places() - sage: D = [pl for pl in pls if pl != Q] - sage: G = 5*Q - sage: from sage.coding.ag_code_decoders import _EvaluationAGCodeDecoder_K - sage: circuit = _EvaluationAGCodeDecoder_K(D, G, Q) - sage: circuit.exponents(11) - (8, 1) - """ - cdef Py_ssize_t sk, si - self._exp(s, &sk, &si) - return (sk, si) - - def substitution(self, vec, w, k, Py_ssize_t i): + def _substitution(self, vec, w, k, int i): r""" Substitute ``z`` with ``(z + w*phi_s)``. TESTS:: + sage: circuit = load(SAGE_SRC + '/sage/coding/tests/decoder_K') sage: F. = GF(4) - sage: P. = AffineSpace(F, 2) - sage: C = Curve(y^2 + y - x^3) - sage: pls = C.places() - sage: p = C([0,0]) - sage: Q = p.place() - sage: D = [pl for pl in pls if pl != Q] - sage: G = 5*Q - sage: from sage.coding.ag_code_decoders import _EvaluationAGCodeDecoder_K - sage: circuit = _EvaluationAGCodeDecoder_K(D, G, Q) sage: W. = F[] - sage: circuit.substitution(vector([0, a*x^2 + a*x, x + 1, 0]), a, 1, 1) + sage: circuit._substitution(vector([0, a*x^2 + a*x, x + 1, 0]), a, 1, 1) (0, 0, x + 1, 0) """ - cdef Py_ssize_t j - cdef Py_ssize_t gamma = self.gamma + cdef int j + cdef int gamma = self.gamma + cdef list a cdef list mul_mat = self.mul_mat + W = self.W x = self.x - cdef list a = vec[gamma:].list() + a = vec[gamma:].list() b = vec[:gamma] c = (w * x**k) * sum(a[j] * ( mul_mat[j])[i] for j in range(gamma)) return vector(W, (c + b).list() + a) @@ -1150,8 +1093,8 @@ cdef class _Decoder_K(object): bint detect_decoding_failure=True, bint detect_Q_polynomial=True): """ - Return the coefficients that maps to the corrected codeword from - the received vector. + Return the message vector that corresponds to the corrected codeword + from the received vector. INPUT: @@ -1172,30 +1115,24 @@ cdef class _Decoder_K(object): TESTS:: + sage: circuit = load(SAGE_SRC + '/sage/coding/tests/decoder_K') sage: F. = GF(4) - sage: P. = AffineSpace(F, 2); - sage: C = Curve(y^2 + y - x^3) - sage: pls = C.places() - sage: p = C([0,0]) - sage: Q, = p.places() - sage: D = [pl for pl in pls if pl != Q] - sage: G = 5*Q - sage: from sage.coding.ag_code_decoders import _EvaluationAGCodeDecoder_K - sage: circuit = _EvaluationAGCodeDecoder_K(D, G, Q) sage: rv = vector(F, [1, a, 1, a + 1, a + 1, a + 1, 1, a + 1]) sage: circuit.decode(rv) (1, 0, a + 1, a + 1, a) """ - cdef Py_ssize_t s, sk, si, i, c, cbar - cdef Py_ssize_t k, ip, count, delta, dlt, wlt, pos - cdef list mat, nu, mu, i_k, i_prime, i_value, i_count, voting_value, voting_count + cdef int s, sk, si, i, c, cbar + cdef int k, ip, count, delta, dlt, wlt, pos, wd_hvec + cdef list mat, nu, mu, message + cdef list i_k, i_prime, i_value, i_count, voting_value, voting_count + cdef Vector hvec cdef Matrix coeff_mat, gbmat cdef bint found_Q - cdef Py_ssize_t code_length = self.code_length - cdef Py_ssize_t designed_distance = self.designed_distance + cdef int code_length = self.code_length + cdef int designed_distance = self.designed_distance - cdef Py_ssize_t gamma = self.gamma + cdef int gamma = self.gamma cdef list dR = self.dR cdef list dRbar = self.dRbar # dWbar for differential AG code @@ -1207,15 +1144,15 @@ cdef class _Decoder_K(object): cdef list message_index = self.message_index cdef list code_basis = self.code_basis - cdef Py_ssize_t s0 = self.s0 - cdef Py_ssize_t tau = self.tau + cdef int s0 = self.s0 + cdef int tau = self.tau W = self.W x = self.x # auxiliary functions - degree = self.degree - substitution = self.substitution + degree = self._degree + substitution = self._substitution K = W.base_ring() @@ -1248,20 +1185,20 @@ cdef class _Decoder_K(object): print(('{:>' + str(width) + '} ').format(s1 + s2), end='') print(']') - cdef list message = [] + message = [] # construct the initial generators of the interpolation module - cdef Vector hvec = sum(received_vector[i] * hvecs[i] for i in range(code_length)) + hvec = sum(received_vector[i] * hvecs[i] for i in range(code_length)) # weighted degree of hvec - cdef Py_ssize_t wd_hvec = max(gamma * degree(hvec[i]) + dRbar[i] for i in range(gamma)) + wd_hvec = max(gamma * degree(hvec[i]) + dRbar[i] for i in range(gamma)) if wd_hvec <= 0: if verbose: print("no error") for s in message_index: - self._exp(s, &sk, &si) + self._exponents(s, &sk, &si) message.append(hvec[si][sk]) else: mat = [] @@ -1296,7 +1233,7 @@ cdef class _Decoder_K(object): print("G{} ".format(i), end='') vprint_g(g, s) - self._exp(s, &sk, &si) + self._exponents(s, &sk, &si) delta = 0 mu = [] i_k = [] @@ -1316,13 +1253,13 @@ cdef class _Decoder_K(object): raise DecodingError("more errors than decoding radius") # detect Q-polynomial - wlt = gamma * dlt + (dR[i]) + wlt = gamma * dlt + dR[i] if detect_Q_polynomial and wlt + s + tau < designed_distance: found_Q = True posQ = (s, i) break - self._exp(wlt + s, &k, &ip) + self._exponents(wlt + s, &k, &ip) count = degree(gbmat[ip, ip]) - k i_k.append(k) i_prime.append(ip) @@ -1333,8 +1270,8 @@ cdef class _Decoder_K(object): if s > 0 or sk < 0: # not s in message_index for i in range(gamma): - k = i_k[i] - ip = i_prime[i] + k = i_k[i] + ip = i_prime[i] if k < 0: value = K.zero() @@ -1346,8 +1283,8 @@ cdef class _Decoder_K(object): winner = 0 else: for i in range(gamma): - k = i_k[i] - ip = i_prime[i] + k = i_k[i] + ip = i_prime[i] mui = gbmat[gamma + i, gamma + i].lc() * coeff_mat[i, si] value = -gbmat[gamma + i, ip][k] / mui @@ -1366,8 +1303,8 @@ cdef class _Decoder_K(object): # voting c = -1 for i in range(len(voting_value)): - if c < (voting_count[i]): - c = (voting_count[i]) + if c < voting_count[i]: + c = voting_count[i] winner = voting_value[i] if verbose: @@ -1417,9 +1354,9 @@ cdef class _Decoder_K(object): print("# s = {}".format(s)) print("F{} ".format(i), end='') vprint_g(gbmat[gamma + i], s) - self._exp(s, &sk, &si) + self._exponents(s, &sk, &si) if s <= 0 and sk >= 0: # s in message_index - self._exp(dlt + s, &k, &ip) + self._exponents(dlt + s, &k, &ip) mui = gbmat[gamma + i,gamma + i].lc() * coeff_mat[i, si] value = -gbmat[gamma + i, ip][k] / mui if not value.is_zero(): @@ -1437,11 +1374,72 @@ cdef class _Decoder_K(object): return vector(K, message) + cdef int _next(self, int s): + """ + Return the next value after ``s`` in dRbar(dWbar). + """ + cdef int i, d, gamma + cdef list dRbar = self.dRbar + gamma = self.gamma + i = pos_mod(s, gamma) + while True: + s += 1 + i = (i + 1) % gamma # equals s % gamma + d = dRbar[i] + if s >= d: + return s + + cdef void _get_eta_basis(self, list basis, list vecs, int s0, mon_func): + """ + Compute a basis of J and h-functions via FGLM algorithm. + + This sets ``basis`` with the basis of J and ``vecs`` with the h-functions. + """ + cdef int s, sk, si, i, j, num + cdef Matrix mat, matinv + cdef list gen, delta, h + cdef tuple t + + cdef int gamma = self.gamma + cdef int code_length = self.code_length + x = self.x + W = self.W + s = s0 + self._exponents(s, &sk, &si) + delta = [(sk, si)] + mat = matrix(mon_func(sk, si)) + num = 0 + while num < gamma: + s = self._next(s) + self._exponents(s, &sk, &si) + if basis[si] is None: + v = mon_func(sk, si) + try: + sol = mat.solve_left(v) + gen = [W.zero() for i in range(gamma)] + for i in range(len(delta)): + t = delta[i] + gen[ t[1]] += -sol[i] * x**( t[0]) + gen[si] += x**sk + basis[si] = vector(gen) + num += 1 + except ValueError: + mat = mat.stack(matrix(v)) + delta.append((sk, si)) + + matinv = mat.inverse() + for i in range(code_length): + h = [W.zero() for k in range(gamma)] + for j in range(code_length): + t = delta[j] + h[ t[1]] += matinv[i,j] * x**( t[0]) + vecs[i] = vector(h) + @cython.auto_pickle(True) -cdef class _EvaluationAGCodeDecoder_K(_Decoder_K): +cdef class EvaluationAGCodeDecoder_K(Decoder_K): """ - Unique decoding algorithm K for evaluation AG codes. + This class implements the decoding algorithm K for evaluation AG codes. INPUT: @@ -1453,7 +1451,7 @@ cdef class _EvaluationAGCodeDecoder_K(_Decoder_K): - ``verbose`` -- if ``True``, verbose information is printed. - TESTS:: + EXAMPLES:: sage: F. = GF(4) sage: P. = AffineSpace(F, 2); @@ -1463,12 +1461,21 @@ cdef class _EvaluationAGCodeDecoder_K(_Decoder_K): sage: Q, = p.places() sage: D = [pl for pl in pls if pl != Q] sage: G = 5*Q - sage: from sage.coding.ag_code_decoders import _EvaluationAGCodeDecoder_K - sage: circuit = _EvaluationAGCodeDecoder_K(D, G, Q) + sage: from sage.coding.ag_code_decoders import EvaluationAGCodeDecoder_K + sage: circuit = EvaluationAGCodeDecoder_K(D, G, Q) sage: rv = vector([a, 0, 0, a, 1, 1, a + 1, 0]) sage: cw = circuit.encode(circuit.decode(rv)) sage: rv - cw (a + 1, 0, 0, 0, 0, 0, 0, 0) + sage: circuit.info['designed_distance'] + 3 + sage: circuit.info['decoding_radius'] + 1 + + We save the decoder for later tests:: + + sage: save(circuit, SAGE_SRC + '/sage/coding/tests/eval_decoder_K') # not tested + """ def __init__(self, pls, G, Q, verbose=False): """ @@ -1476,23 +1483,15 @@ cdef class _EvaluationAGCodeDecoder_K(_Decoder_K): TESTS:: - sage: F. = GF(4) - sage: P. = AffineSpace(F, 2); - sage: C = Curve(y^2 + y - x^3) - sage: pls = C.places() - sage: p = C([0,0]) - sage: Q, = p.places() - sage: D = [pl for pl in pls if pl != Q] - sage: G = 5*Q - sage: from sage.coding.ag_code_decoders import _EvaluationAGCodeDecoder_K - sage: circuit = _EvaluationAGCodeDecoder_K(D, G, Q) + sage: circuit = load(SAGE_SRC + '/sage/coding/tests/eval_decoder_K') sage: TestSuite(circuit).run(skip='_test_pickling') """ - cdef Py_ssize_t i, s, s0, sk, si, n, r, d, num - cdef Py_ssize_t code_length, genus, gamma - cdef list gaps, dR, yR + cdef int i, j, s, s0, sk, si, n, r, d, num + cdef int code_length, genus, gamma, dLO, tau + cdef list gaps, dR, yR, dRbar, yRbar, evyRbar, nus, mul_mat + cdef list message_index, code_basis + cdef Vector evxR cdef set temp - cdef tuple e D = sum(pls) F = D.parent().function_field() @@ -1575,7 +1574,7 @@ cdef class _EvaluationAGCodeDecoder_K(_Decoder_K): # ev map for the monomial whose weighted degree is s evxR = vector(K, [xR.evaluate(p) for p in pls]) - cdef list evyRbar = [vector(K, [yRbar[i].evaluate(p) for p in pls]) for i in range(gamma)] + evyRbar = [vector(K, [yRbar[i].evaluate(p) for p in pls]) for i in range(gamma)] self.is_differential = False self.code_length = code_length @@ -1586,33 +1585,35 @@ cdef class _EvaluationAGCodeDecoder_K(_Decoder_K): self.W = W self.x = x - degree = self.degree + degree = self._degree - def ev_mon(Py_ssize_t sk, Py_ssize_t si): + def ev_mon(int sk, int si): + cdef int i return vector([evxR[i]**sk * evyRbar[si][i] for i in range(code_length)]) # minimum of nongaps of Rbar - s0 = _next(self, -G.degree() - 1) + s0 = self._next(-G.degree() - 1) # basis of the code ev(L(G)) - cdef list message_index = [] - cdef list code_basis = [] + message_index = [] + code_basis = [] s = s0 - self._exp(s, &sk, &si) + self._exponents(s, &sk, &si) v = ev_mon(sk, si) V = v.parent() while s <= 0: if not V.are_linearly_dependent(code_basis + [v]): message_index.append(s) code_basis.append(v) - s = _next(self, s) - self._exp(s, &sk, &si) + s = self._next(s) + self._exponents(s, &sk, &si) v = ev_mon(sk, si) # compute a basis of J and h-functions via FGLM algorithm eta_vecs = [None for i in range(gamma)] hvecs = [None for i in range(code_length)] - get_eta_basis(self, eta_vecs, hvecs, s0, ev_mon) + + self._get_eta_basis(eta_vecs, hvecs, s0, ev_mon) if verbose: print("message indices:", message_index) @@ -1622,14 +1623,14 @@ cdef class _EvaluationAGCodeDecoder_K(_Decoder_K): print("h{} = {}".format(i, hvecs[i])) # Lee-O'Sullivan bound - def nu(Py_ssize_t s): - cdef Py_ssize_t i, sk, si, m = 0 + def nu(int s): + cdef int i, sk, si, m = 0 for i in range(gamma): - self._exp(s + ( dR[i]), &sk, &si) + self._exponents(s + dR[i], &sk, &si) m += max(0, degree(eta_vecs[si][si]) - sk) return m - cdef list nus = [nu(s) for s in message_index] + nus = [nu(s) for s in message_index] dLO = min(nus) tau = (dLO - 1) // 2 @@ -1646,7 +1647,7 @@ cdef class _EvaluationAGCodeDecoder_K(_Decoder_K): cdef list l = [W.zero() for i in range(gamma)] while r != 0: s = -r.valuation(Q) - G.multiplicity(Q) - self._exp(s, &sk, &si) + self._exponents(s, &sk, &si) mon = xR**sk * yRbar[si] c = (r / mon).evaluate(Q) l[si] += c * x**sk @@ -1654,13 +1655,13 @@ cdef class _EvaluationAGCodeDecoder_K(_Decoder_K): return vector(l) # the matrix of the leading coefficient of y_i*ybar_j and the product - cdef Matrix coeff_mat = matrix.zero(K, gamma, gamma) - cdef list mul_mat = [[None for j in range(gamma)] for i in range(gamma)] + coeff_mat = matrix.zero(K, gamma, gamma) + mul_mat = [[None for j in range(gamma)] for i in range(gamma)] for i in range(gamma): for j in range(gamma): f = yR[i] * yRbar[j] v = vec_form(f) - self._exp(( dR[i]) + ( dRbar[j]), &sk, &si) + self._exponents(( dR[i]) + ( dRbar[j]), &sk, &si) coeff_mat[i,j] = v[si][sk] ( mul_mat[i])[j] = v @@ -1689,9 +1690,9 @@ cdef class _EvaluationAGCodeDecoder_K(_Decoder_K): @cython.auto_pickle(True) -cdef class _DifferentialAGCodeDecoder_K(_Decoder_K): +cdef class DifferentialAGCodeDecoder_K(Decoder_K): """ - Unique decoding algorithm K for differential AG codes. + This class implements the decoding algorithm K for differential AG codes. INPUT: @@ -1703,7 +1704,7 @@ cdef class _DifferentialAGCodeDecoder_K(_Decoder_K): - ``verbose`` -- if ``True``, verbose information is printed - TESTS:: + EXAMPLES:: sage: F. = GF(4) sage: P. = AffineSpace(F, 2); @@ -1713,12 +1714,21 @@ cdef class _DifferentialAGCodeDecoder_K(_Decoder_K): sage: Q, = p.places() sage: D = [pl for pl in pls if pl != Q] sage: G = 5*Q - sage: from sage.coding.ag_code_decoders import _DifferentialAGCodeDecoder_K - sage: circuit = _DifferentialAGCodeDecoder_K(D, G, Q) + sage: from sage.coding.ag_code_decoders import DifferentialAGCodeDecoder_K + sage: circuit = DifferentialAGCodeDecoder_K(D, G, Q) sage: rv = vector([1, a, 1, a, 1, a, a, a + 1]) sage: cw = circuit.encode(circuit.decode(rv)) sage: rv - cw (0, 0, 0, a + 1, 1, 0, 0, 0) + sage: circuit.info['designed_distance'] + 5 + sage: circuit.info['decoding_radius'] + 2 + + We save the decoder for later tests:: + + sage: save(circuit, SAGE_SRC + '/sage/coding/tests/diff_decoder_K') # not tested + """ def __init__(self, pls, G, Q, verbose=False): """ @@ -1726,23 +1736,15 @@ cdef class _DifferentialAGCodeDecoder_K(_Decoder_K): TESTS:: - sage: F. = GF(4) - sage: P. = AffineSpace(F, 2); - sage: C = Curve(y^2 + y - x^3) - sage: pls = C.places() - sage: p = C([0,0]) - sage: Q, = p.places() - sage: D = [pl for pl in pls if pl != Q] - sage: G = 5*Q - sage: from sage.coding.ag_code_decoders import _DifferentialAGCodeDecoder_K - sage: circuit = _DifferentialAGCodeDecoder_K(D, G, Q) + sage: circuit = load(SAGE_SRC + '/sage/coding/tests/diff_decoder_K') sage: TestSuite(circuit).run(skip='_test_pickling') """ - cdef Py_ssize_t gamma, code_length, genus - cdef Py_ssize_t d, i, j, n, r, s, sk, si, num - cdef list gaps, dR, yR, dWbar, wWbar + cdef int i, j, s, s0, sk, si, n, r, d, num + cdef int code_length, genus, gamma, dLO, tau + cdef list gaps, dR, yR, dWbar, wWbar, reswWbar, nus, mul_mat + cdef list message_index, code_basis + cdef Vector evxR cdef set temp - cdef tuple e D = sum(pls) F = D.parent().function_field() @@ -1823,8 +1825,8 @@ cdef class _DifferentialAGCodeDecoder_K(_Decoder_K): print(" {}: {}, w{} = {}".format(i, dWbar[i], i, wWbar[i])) # res map for the monomial whose weighted degree is s - cdef Vector evxR = vector(K, [xR.evaluate(p) for p in pls]) - cdef list reswWbar = [vector(K, [wWbar[i].residue(p) for p in pls]) for i in range(gamma)] + evxR = vector(K, [xR.evaluate(p) for p in pls]) + reswWbar = [vector(K, [wWbar[i].residue(p) for p in pls]) for i in range(gamma)] self.is_differential = True self.code_length = code_length @@ -1835,34 +1837,35 @@ cdef class _DifferentialAGCodeDecoder_K(_Decoder_K): self.W = W self.x = x - degree = self.degree + degree = self._degree - def res_mon(Py_ssize_t sk, Py_ssize_t si): - cdef Py_ssize_t i + def res_mon(int sk, int si): + cdef int i return vector([evxR[i]**sk * reswWbar[si][i] for i in range(code_length)]) # minimum of nongaps of Wbar - s0 = _next(self, -code_length + G.degree() - 2*genus + 1) + s0 = self._next(-code_length + G.degree() - 2*genus + 1) # basis of the code res(Omega(G)) - cdef list message_index = [] - cdef list code_basis = [] + message_index = [] + code_basis = [] s = s0 - self._exp(s, &sk, &si) + self._exponents(s, &sk, &si) v = res_mon(sk, si) V = v.parent() while s <= 0: if not V.are_linearly_dependent(code_basis + [v]): message_index.append(s) code_basis.append(v) - s = _next(self, s) - self._exp(s, &sk, &si) + s = self._next(s) + self._exponents(s, &sk, &si) v = res_mon(sk, si) # compute a basis of J and h-functions via FGLM algorithm eta_vecs = [None for i in range(gamma)] hvecs = [None for i in range(code_length)] - get_eta_basis(self, eta_vecs, hvecs, s0, res_mon) + + self._get_eta_basis(eta_vecs, hvecs, s0, res_mon) if verbose: print("message indices:", message_index) @@ -1872,15 +1875,16 @@ cdef class _DifferentialAGCodeDecoder_K(_Decoder_K): print("h{} = {}".format(i, hvecs[i])) # Lee-O'Sullivan bound - def nu(Py_ssize_t s): - cdef Py_ssize_t i, sk, si, m = 0 + def nu(int s): + cdef int i, sk, si, m + m = 0 for i in range(gamma): - self._exp(s + ( dR[i]), &sk, &si) + self._exponents(s + dR[i], &sk, &si) m += max(0, degree(eta_vecs[si][si]) - sk) return m - cdef list nus = [nu(s) for s in message_index] - cdef Py_ssize_t dLO = min(nus) + nus = [nu(s) for s in message_index] + dLO = min(nus) tau = (dLO - 1) // 2 if verbose: @@ -1896,7 +1900,7 @@ cdef class _DifferentialAGCodeDecoder_K(_Decoder_K): cdef list l = [W.zero() for i in range(gamma)] while r != 0: s = -r.valuation(Q) + G.valuation(Q) - self._exp(s, &sk, &si) + self._exponents(s, &sk, &si) mon = xR**sk * wWbar[si] c = (r / mon).evaluate(Q) l[si] += c * x**sk @@ -1904,13 +1908,13 @@ cdef class _DifferentialAGCodeDecoder_K(_Decoder_K): return vector(l) # the matrix of the leading coefficient of y_i*w_j and the product - cdef Matrix coeff_mat = matrix.zero(K, gamma, gamma) - cdef list mul_mat = [[None for j in range(gamma)] for i in range(gamma)] + coeff_mat = matrix.zero(K, gamma, gamma) + mul_mat = [[None for j in range(gamma)] for i in range(gamma)] for i in range(gamma): for j in range(gamma): f = yR[i] * wWbar[j] v = vec_form(f) - self._exp(( dR[i]) + ( dWbar[j]), &sk, &si) + self._exponents(( dR[i]) + ( dWbar[j]), &sk, &si) coeff_mat[i,j] = v[si][sk] ( mul_mat[i])[j] = v @@ -1938,9 +1942,9 @@ cdef class _DifferentialAGCodeDecoder_K(_Decoder_K): self.info = info -cdef class _Decoder_K_extension(object): +cdef class Decoder_K_extension(object): """ - Unique decoding algorithm K for AG codes via constant field extension. + Common base class for decoding algorithm K for AG codes via constant field extension. INPUT: @@ -1948,11 +1952,11 @@ cdef class _Decoder_K_extension(object): - ``G`` -- a divisor of the function field - - ``Q`` -- a nonrational place + - ``Q`` -- a non-rational place - ``verbose`` -- if ``True``, verbose information is printed - TESTS:: + EXAMPLES:: sage: A. = AffineSpace(GF(4), 2) sage: C = Curve(y^2 + y - x^3) @@ -1960,7 +1964,7 @@ cdef class _Decoder_K_extension(object): sage: F = C.function_field() sage: G = 1*F.get_place(4) sage: code = codes.EvaluationAGCode(pls, G) - sage: code.decoder('K') + sage: dec = code.decoder('K'); dec Unique decoder for [9, 4] evaluation AG code over GF(4) :: @@ -1976,10 +1980,14 @@ cdef class _Decoder_K_extension(object): sage: code.decoder('K') Unique decoder for [5, 3] evaluation AG code over GF(4) + We save the decoder for later tests:: + + sage: save(dec, SAGE_SRC + '/sage/coding/tests/decoder_K_extension') # not tested + """ cdef object _embedK, _K - cdef _Decoder_K decoder_ext - cdef type decoder_cls + cdef Decoder_K decoder_ext + cdef readonly dict info def __init__(self, pls, G, Q, decoder_cls, verbose=False): @@ -1988,14 +1996,8 @@ cdef class _Decoder_K_extension(object): TESTS:: - sage: A. = AffineSpace(GF(4), 2) - sage: C = Curve(y^2 + y - x^3) - sage: pls = C.places() - sage: F = C.function_field() - sage: G = 1*F.get_place(4) - sage: code = codes.EvaluationAGCode(pls, G) - sage: decoder = code.decoder('K') - sage: TestSuite(decoder).run(skip='_test_pickling') + sage: dec = load(SAGE_SRC + '/sage/coding/tests/decoder_K_extension') + sage: TestSuite(dec).run(skip='_test_pickling') """ F = G.parent().function_field() K = F.constant_base_field() @@ -2064,21 +2066,16 @@ cdef class _Decoder_K_extension(object): self.decoder_ext = decoder_cls(pls_ext, G_ext, Q_ext, verbose=verbose) self.info = self.decoder_ext.info - def lift(self, v): + def _lift(self, v): """ Lift a vector over the base field to a vector over the extension field. TESTS:: - sage: A. = AffineSpace(GF(4), 2) - sage: C = Curve(y^2 + y - x^3) - sage: pls = C.places() - sage: F = C.function_field() - sage: G = 1*F.get_place(4) - sage: code = codes.EvaluationAGCode(pls, G) - sage: decoder = code.decoder('K') - sage: lift = decoder._circuit.lift - sage: pull_back = decoder._circuit.pull_back + sage: decoder = load(SAGE_SRC + '/sage/coding/tests/decoder_K_extension') + sage: code = decoder.code() + sage: lift = decoder._circuit._lift + sage: pull_back = decoder._circuit._pull_back sage: v = code.random_element() sage: pull_back(lift(v)) == v True @@ -2086,22 +2083,17 @@ cdef class _Decoder_K_extension(object): embedK = self._embedK return vector(embedK(e) for e in v) - def pull_back(self, v): + def _pull_back(self, v): """ - Pull back a vector over the extension field to a vector - over the base field. + Pull back a vector over the extension field to a vector over the base + field. TESTS:: - sage: A. = AffineSpace(GF(4), 2) - sage: C = Curve(y^2 + y - x^3) - sage: pls = C.places() - sage: F = C.function_field() - sage: G = 1*F.get_place(4) - sage: code = codes.DifferentialAGCode(pls, G) - sage: decoder = code.decoder('K') - sage: lift = decoder._circuit.lift - sage: pull_back = decoder._circuit.pull_back + sage: decoder = load(SAGE_SRC + '/sage/coding/tests/decoder_K_extension') + sage: code = decoder.code() + sage: lift = decoder._circuit._lift + sage: pull_back = decoder._circuit._pull_back sage: v = code.random_element() sage: pull_back(lift(v)) == v True @@ -2115,13 +2107,8 @@ cdef class _Decoder_K_extension(object): TESTS:: - sage: A. = AffineSpace(GF(4), 2) - sage: C = Curve(y^2 + y - x^3) - sage: pls = C.places() - sage: F = C.function_field() - sage: G = 1*F.get_place(4) - sage: code = codes.EvaluationAGCode(pls, G) - sage: decoder = code.decoder('K') + sage: decoder = load(SAGE_SRC + '/sage/coding/tests/decoder_K_extension') + sage: code = decoder.code() sage: cw = code.random_element() sage: circuit = decoder._circuit sage: circuit.encode(circuit.decode(cw)) == cw @@ -2131,21 +2118,16 @@ cdef class _Decoder_K_extension(object): def decode(self, received_vector, **kwargs): """ - Decode received vector to a message. + Decode the received vector to a message. INPUT: - ``received_vector`` -- a vector in the ambient space of the code - EXAMPLES:: + TESTS:: - sage: A. = AffineSpace(GF(4), 2) - sage: C = Curve(y^2 + y - x^3) - sage: pls = C.places() - sage: F = C.function_field() - sage: G = 1*F.get_place(4) - sage: code = codes.DifferentialAGCode(pls, G) - sage: decoder = code.decoder('K') + sage: decoder = load(SAGE_SRC + '/sage/coding/tests/decoder_K_extension') + sage: code = decoder.code() sage: cw = code.random_element() sage: circuit = decoder._circuit sage: circuit.encode(circuit.decode(cw)) == cw @@ -2155,9 +2137,10 @@ cdef class _Decoder_K_extension(object): @cython.auto_pickle(True) -cdef class _EvaluationAGCodeDecoder_K_extension(_Decoder_K_extension): +cdef class EvaluationAGCodeDecoder_K_extension(Decoder_K_extension): """ - Unique decoding algorithm K for AG codes via constant field extension. + This class implements the decoding algorithm K for evaluation AG codes via + constant field extension. INPUT: @@ -2165,20 +2148,31 @@ cdef class _EvaluationAGCodeDecoder_K_extension(_Decoder_K_extension): - ``G`` -- a divisor of the function field - - ``Q`` -- a nonrational place + - ``Q`` -- a non-rational place - ``verbose`` -- if ``True``, verbose information is printed EXAMPLES:: - sage: A. = AffineSpace(GF(4), 2) + sage: F. = GF(4) + sage: A. = AffineSpace(F, 2) sage: C = Curve(y^2 + y - x^3) sage: pls = C.places() sage: F = C.function_field() sage: G = 1*F.get_place(4) sage: code = codes.EvaluationAGCode(pls, G) - sage: code.decoder('K') - Unique decoder for [9, 4] evaluation AG code over GF(4) + sage: Q = F.get_place(3) + sage: from sage.coding.ag_code_decoders import EvaluationAGCodeDecoder_K_extension + sage: circuit = EvaluationAGCodeDecoder_K_extension(pls, G, Q) + sage: cw = code.random_element() + sage: rv = cw + vector([0,1,1,0,0,0,0,0,0]) + sage: circuit.encode(circuit.decode(circuit._lift(rv))) == circuit._lift(cw) + True + + We save the decoder for later tests:: + + sage: save(circuit, SAGE_SRC + '/sage/coding/tests/eval_decoder_K_extension') # not tested + """ def __init__(self, pls, G, Q, verbose=False): """ @@ -2186,22 +2180,17 @@ cdef class _EvaluationAGCodeDecoder_K_extension(_Decoder_K_extension): TESTS:: - sage: A. = AffineSpace(GF(4), 2) - sage: C = Curve(y^2 + y - x^3) - sage: pls = C.places() - sage: F = C.function_field() - sage: G = 1*F.get_place(3) - sage: code = codes.EvaluationAGCode(pls, G) - sage: decoder = code.decoder('K') - sage: TestSuite(decoder).run(skip='_test_pickling') + sage: circuit = load(SAGE_SRC + '/sage/coding/tests/eval_decoder_K_extension') + sage: TestSuite(circuit).run(skip='_test_pickling') """ - super().__init__(pls, G, Q, _EvaluationAGCodeDecoder_K, verbose=verbose) + super().__init__(pls, G, Q, EvaluationAGCodeDecoder_K, verbose=verbose) @cython.auto_pickle(True) -cdef class _DifferentialAGCodeDecoder_K_extension(_Decoder_K_extension): +cdef class DifferentialAGCodeDecoder_K_extension(Decoder_K_extension): """ - Unique decoding algorithm K for AG codes via constant field extension. + This class implements the decoding algorithm K for differential AG codes via + constant field extension. INPUT: @@ -2209,20 +2198,33 @@ cdef class _DifferentialAGCodeDecoder_K_extension(_Decoder_K_extension): - ``G`` -- a divisor of the function field - - ``Q`` -- a nonrational place + - ``Q`` -- a non-rational place - ``verbose`` -- if ``True``, verbose information is printed EXAMPLES:: - sage: A. = AffineSpace(GF(4), 2) + sage: F. = GF(4) + sage: A. = AffineSpace(F, 2) sage: C = Curve(y^2 + y - x^3) sage: pls = C.places() sage: F = C.function_field() sage: G = 1*F.get_place(4) sage: code = codes.DifferentialAGCode(pls, G) - sage: code.decoder('K') - Unique decoder for [9, 5] differential AG code over GF(4) + sage: Q = F.get_place(3) + sage: from sage.coding.ag_code_decoders import DifferentialAGCodeDecoder_K_extension + sage: circuit = DifferentialAGCodeDecoder_K_extension(pls, G, Q) + sage: cw = code.random_element() + sage: rv = cw + vector([0,0,a,0,0,0,0,0,0]) + sage: circuit.encode(circuit.decode(circuit._lift(rv))) == circuit._lift(cw) + True + + TESTS:: + + We save the decoder for later tests:: + + sage: save(circuit, SAGE_SRC + '/sage/coding/tests/diff_decoder_K_extension') # not tested + """ def __init__(self, pls, G, Q, verbose=False): """ @@ -2230,96 +2232,8 @@ cdef class _DifferentialAGCodeDecoder_K_extension(_Decoder_K_extension): TESTS:: - sage: A. = AffineSpace(GF(4), 2) - sage: C = Curve(y^2 + y - x^3) - sage: pls = C.places() - sage: F = C.function_field() - sage: G = 1*F.get_place(3) - sage: code = codes.DifferentialAGCode(pls, G) - sage: decoder = code.decoder('K') - sage: TestSuite(decoder).run(skip='_test_pickling') + sage: circuit = load(SAGE_SRC + '/sage/coding/tests/diff_decoder_K_extension') + sage: TestSuite(circuit).run(skip='_test_pickling') """ - super().__init__(pls, G, Q, _DifferentialAGCodeDecoder_K, verbose=verbose) - -############################################################################### -## Helper functions - -cdef inline Py_ssize_t pos_mod(Py_ssize_t a, Py_ssize_t b): - """ - Return ``a % b`` such that the result is positive. - - C modulus can be negative as ``a == (a / b * b) + (a % b)``. - """ - cdef Py_ssize_t ret = a % b - if ret < 0: - ret += b - return ret - -@cython.wraparound(False) -@cython.boundscheck(False) -cdef inline Py_ssize_t _next(_Decoder_K decoder, Py_ssize_t s): - """ - Helper to get the next value after ``s`` in dRbar. - """ - cdef Py_ssize_t i, d, gamma - cdef list dRbar = decoder.dRbar - gamma = decoder.gamma - i = pos_mod(s, gamma) - while True: - # We unroll (decoder.self._exp(s)[0]) >= 0 for speed - # Note decoder.self._exp(s)[0] == (s - d) // gamma, - # so we only need to compare s with d - s += 1 - i = (i + 1) % gamma - d = dRbar[i] - if s >= d: - return s - -@cython.wraparound(False) -@cython.boundscheck(False) -cdef inline get_eta_basis(_Decoder_K decoder, list basis, list vecs, Py_ssize_t s0, mon_func): - """ - Compute a basis of J and h-functions via FGLM algorithm. - - This modifies ``basis`` and ``vecs``. - """ - cdef Py_ssize_t s, sk, si, i, j, num - cdef Matrix mat, matinv - cdef list gen, delta, h - cdef tuple e, de - - cdef Py_ssize_t gamma = decoder.gamma - cdef Py_ssize_t code_length = decoder.code_length - x = decoder.x - W = decoder.W - s = s0 - decoder._exp(s, &sk, &si) - delta = [(sk, si)] - mat = matrix(mon_func(sk, si)) - num = 0 - while num < gamma: - s = _next(decoder, s) - decoder._exp(s, &sk, &si) - if basis[si] is None: - v = mon_func(sk, si) - try: - sol = mat.solve_left(v) - gen = [W.zero() for i in range(gamma)] - for i in range(len(delta)): - de = delta[i] - gen[ de[1]] += -sol[i] * x**( de[0]) - gen[si] += x**sk - basis[si] = vector(gen) - num += 1 - except ValueError: - mat = mat.stack(matrix(v)) - delta.append((sk, si)) - - matinv = mat.inverse() - for i in range(code_length): - h = [W.zero() for k in range(gamma)] - for j in range(code_length): - de = delta[j] - h[ de[1]] += matinv[i,j] * x**( de[0]) - vecs[i] = vector(h) + super().__init__(pls, G, Q, DifferentialAGCodeDecoder_K, verbose=verbose) diff --git a/src/sage/coding/tests/decoder_K.sobj b/src/sage/coding/tests/decoder_K.sobj new file mode 100644 index 0000000000000000000000000000000000000000..4fb55013351ae80b7814969fadf3a87290e9aea1 GIT binary patch literal 1496 zcmV;}1tljQQ|3C*|sUtC=p3VDSA>rpdYaqR810)h03*|;lGYcNC zX^l&d*;a~YxtC5jW<5}$g&|Az$>egg3hhoRlbadOSg2?j8VZG`q_b(CPf7Dk63ca? zl2FB}SXoP%z`VpbSUnh8Qd2tX^Az{2w)iaW6c?y&2@8jD7}ky9gb`{L$MW46`QQCW zwbpiOIiBHdJnLIn!7aDlA+}rP4z*paU5gdFu@YgFT6JROZmiPSQZD9gxnvfrx}ywn zELQ8r>V(m~jg5^y*66~TKGq_v?FyH58vA^#OIR<4^-IcXRef+uYIKCM)gUfx;8wdr zbwk2NVkQ#Vm|(kW_h1vkrmjK95H@p_b#p>Z(Ygg;OKlxXsO5cqJ=iM1IKtK~6(ZYI zx+Cu8u#J}8marX*^y<1~i?DsKj~%+OqmP{k<7Eju%Mx}WOo(BkDM92}$Z%1F$GM{y z1HDv@og#n+Wv|y}r?`QVJe%iE$5;!KRL3h?EV(NN_?X;{IzqjC5aU^8pXlh6E=={Y zD`B@7qDyZgFGf-o4sP-#cAUZLi;ta&5M=>&Cp5SvKh7#e9~udJxFSsvcbe@=awx!_ z1ae7`9%qbQ1Q}~eF^&+%KnlfNfK70ro;#b z7>c7QO)AUgLCOq5T+GbViThr<)7<24=ep~IE%^uJbZlv1ru}dJ;_=ol#eWcTI%+K> zY!+eVF(mD1Uw?l;o5mX1R5pxRY;|#k)NdHujqS?n7@2jsxkp%-m6>@xYmF|H0_zw< zg^=2vH3>kiqXO$+Vz^WeZ^+?%IlLoXwPO`)7tyA(yjl&9&M!h{8jhYSZ)4bSHrlvU ziv;V~L=mkrdZq*utdh+TmjuHC3PmrJQ9hK|s;nz|!eF?N@U0g`*F~?B#z|~Obe@=S zmJuh26I$htVG$oGf*V8H79M3hh6C;BVyXGNBA3N5%XanYP{&{bi^yY;=|Y>2EFl*| z`~P_D&za-2^S{67;EH4H4Q%E-!52Hp7rzGeswf2LAROc>UZ=Fo4lsvsFc+^0ABPaS zWNIRXLv2kuOp@jXIGiv~TaK{ZQqdy=bQ1zC(L?AJvl#kpqjZ!}I@&JrIwruegyS@J zd?|K9fD;KPX~mNXrx>YIjnrwS^wR^JK{!)mXGzQ10nQ=$Ep;W~DkF8Zk-A3Hn~c=80j?uludy3+7 z2sdk~TL`xrsoRXy?UF8EA6n{;0Cy7Z(%9Wvs>x(^Pk?&~_i3s72@e>l2aVK2r3F45 zU_RjyjXkQvG?}a(3-CDM2`%*`;VC2aw2^wIw7_QrJV$t5V=ri_29wo`0bU}!tfgKd zylSLgGg7aY7WhVhHwkZP>}?&U!DRJLfOiS+X{q-KAMh@GXt#_M|0YSEPp7gx=_J#6 zKb}o+d?dawMkqvoZ#iB(nJ(nXXD|Av92OT}EG#6>=BIWHpA~oN=O)cB z+=jT!VL^Z|314aB*V6J$fQ5ubI;(F9-x<&EZFiBT{a}254Db`-XU+VD@T&;+TQSh@ zCeR;-{AYm0gnni8LgK5#R=eBmZ!at?ffo@Yd1Yc$qF%XL4-unQ^(vI3(pXMfm9|%@ yoU0Vsl4hN~H&j`wr5so7VPWk%%)}S5dwaa$%6)_)FE2*l8!5(_HGcsBCPCKq63!a{ literal 0 HcmV?d00001 diff --git a/src/sage/coding/tests/decoder_K_extension.sobj b/src/sage/coding/tests/decoder_K_extension.sobj new file mode 100644 index 0000000000000000000000000000000000000000..a13f3d3cb20582cf095d4ebc1a3b058039b3ad70 GIT binary patch literal 10891 zcmV;6DsP%<1Z@DfK(b7mHZ#1hG1 zEMJBd4xHiI;aXM1wz$q;T6Nm`blETf^m1-_R8W@B{M6IqiTIIF!Vs5vv*MB1IHN%7oBA+6SX}JoAAu6R_Zoy7 zgloDgi5HX=hZ5dt`ZsBqnqFZrKgrz}g<>Jf`bsYcjM4FuMw!R6DUQb%mz4&JisN~~ zVlQc&Q-^i$#7HcX3{8xLiVH}SO48I%n)y6FdybvyX#QZ*yo$8YJyO=giN28-OP9@` zlHlS{z$lQGIklO=VA86RwDyxWIW|^WK9IDnB<;+`rSalrv3N-&SZoplMTG;G3V)&E zPzmdFFKK^rE3RXs9TN?WQ#nHeGbG5*FkaFjGu$XM+$`K8Gc58PNIF)MPJYrErKO}x zCFz=-<6tryff(r)?yLg`kkcwjcR$Homz|xRBt0rf&m`&PC%wbWq)&ErlJxbHez~N- zrHcyDrvXcU1(;_(u3j=A+%n9(4)l{jOk#-)_LI}Yy{pI&KN%X>mZbrm^FBV+tzY>ta%TNEh|(l{9>+>ZB?2|n98v|~A$i01L`Ba_x8 z$>a($B}uaU#8*kCR*-2q8Jq(miPB&)KTNW#6IEoopUl7%5i2gZG!ic%Gjlww=4@6m zHkgPch`)-=@{`%&nK_N*WEvk~W0+veCRj}7RFS!Ua(3F;ULNAZ7GN>+*<7EC(kxLR zIboLAyb3bEik#yo3&N>!bF0WgKMADun+PQnoFKfjnBXOg*r)}oNS>eMGdH}SCI<6^ zjE)MbNXSnLt4L8L30IIv6^Z)EVz6Opkg{ngn1~c>q{L5R8i{9ThwCIssh<$WowR}^ zGIN^p4l-7wX*QCig_w#Xu~3i-a+(TR&{A2Bn>#2D#fp+)QttDJGNTFRk1RoHahbtPHjCu{kN^!Ys1 zY-ycWLC#N-NFik( z2Oh^>sRHE#z2t&SpTpNUm#ojssy=q?m~VzJ+c(Kq&*$~EX8+Z~zk0qYzRAAvKEGaW ztbaRvwS9IWXFbe}vo7HuNBGAE?n|It%;!|!Y~KVOd5r7T$5#IF5&yV>e|)M8J-$*F zcAhVRQvIyw43#d{&bpF+_43vCwb4ylvP{SO#;|A^S=Vr7e%5XLYjoCA+&Ta7m1gbb z^3U*=^(>eF;_?EYD@)_0C;7+4ysAEPIgoXP%jeEI$}0wE-Os<8=;3Oya1&UEjnCT7 ztH=5lWvyjG$hVY{=!JfA5t|4OpC?H!_LEC;8uIba<10N-SQg9Yvo%33%_Wyvc~(~i z3i}(T^ZDoU)jU$=+$<=;xP1wh`q)i&R)=xHr43?4WD#`VHkGjE6ZpW94SP<@gqiQi3X&m)ycT15Me=Gr!eOwpEhb{N(nN zcl7e*fznXwIpU7+wp?=O@ptEt!DHAu6$6-#MY>MTp}W{jt)-E>{bYNX|Knx5Mt1ng zPVC6{Xk?e4+#5#d`@%beyz+jHJm4n}R*{GN-N?I-VqH1e*WyccG(LPb&l}C;Kv;Dad=~x&A3leQ~Wej z@2}b#`OHuDhDDEku8}YN1=thTqy3VZ8tzx_iifjlgZ-tEzy0K&DssqA{;eX1 z{p3ih$D`rB=+0vrt;N=upVnrdP*EF`Ox~!Sl{M-B%E>;fs0*?iat34%`wVH+i$WbJ z>O!u^-Kw_H`fzSdhBiRu_V8ZBYlwJ_VBT0})g&dWra(0VsyPC-fZP&tE6A-ya7(N< zybf)rZQ-IFpzUR#4k>{;0@Vqq&T!rZa#zURAfG0h-yMZaD0)EdnbQ1TX)@}qx1NPt zS4I0ETwhrClS%bYNooL41A!WZc!MFI4tWUVq3koL(P1bIhhhZeGkCT*MF&cY6Lche zvy0TOtY$_rJGQdpruE`fvJ1|U^h{=Pf~8f;eOMP3+{$G|NAZT$WoKS~kmU#VDKSB2 zbTslj3we%VpE>pTNzW2}EZRN}knxZwuupzjLnoqg68r42hEC=|>YX%Cqs3l2g&XXt zq*?5{g8GojRCt_*nrz-tIClq1DZ4bF)42ibxbD0K1)*T^w0JBYDL7%@%{X!2MYtPl z-@&udL8=dD8hx1d`a4)u5@eU(;*0uuk5tfEXwqynX%07^iBs;FL?WIaVcTMPh|UG% zY(R1Zgg1B&iybVc^Prs%?K$i-D#l^~Dsmw&G{z!;%0*%GdTHLtY~qyP z{;~AcG6{M86ZG-zgGLh<;C^52OFLT`2>WaYyXr$qtNR9QVWt zZ8j}O2QJ}BR43_DCssxDwEf zGR9R3a5Vte0I&&uuZ6rB@^z4}7u(7WDBQ^B8}IA{y$PLtGk3PRif+Liy@h=y=~h5* zg?=0AZ-c+vQGW;O??nAwsJ|Qa+flQFcT^N}n*+rL5snqoopP31E;#$kL#CJB!)?=D zl$wsU;?i{5aq8(V(;T(>z2+NY%UX4paJUB0l!9KwhXgzJ$K}9|-Kd|43l+B8x{0p{!%b@?LpoakX7l6Zjii%0Wz3O+4=n>vptZpo`C|^_6 zY)u_i;g0FyYT3BA+BWXZW;4RsZ8Dq#04D%032@uY9hCu$2NHE*QKnQ1yVS zZ`0RS0~8zDjJ376tc0D>_`SOl+KAOgzgIkVf?I>)rpt-%4+hEycxhuK#Wq9S{bO8I z&8BV>8=omnZJd#sA))3rK0GaKvcFo|M1QqHBU%I0MmDvr>aTVHvJsL^V5jj*$FzSBUnsn)HN8FPQY^CJkf&Wzz>;)fZj0 zA=~~Q{RG6D2=hrYp~)(tDF9>v;6tpb zkf%Y;hCDq@yJsjDGvUGy=qwpywxwg{05cbuv*9`i@;u1%A)jNzA!Gpxxlk;G9N>!R zZg9vV8>lI0V*>%U#U&yIWg$WCwD5S- zIy)}TAZ$k%AIWDIaYYQ-!%xG;v`ldnk-6~8!)OG#M3Kv4kxMbpC8}RTmT;uF`it?N zi}FjpsD8MP+e|xC6h~uraZw5eAm}Ix#|aysx=GZRp}rjTOHi}aCU+GMjP6Xsxcn5e zOfkrE%OIYMHmrcl3OUd#^{KWB+SSmmk=nJo_B?3Mhqh8`H8qQ>09Xe=wM}Q*3s79o zvsH(d3zgADFuEA1OE^lMPcB8}WxNvSklImx?1<`n{RUnyHrvZ>md!SrZjZgf(hpZ6 z>P7^(O19-{mDV)?Yy#j~jKgNg*FnA>@(p4V+=xjacGjCz$KR}+ZGp2}V6;_6yHx?U z0dN}tw_~XsZ!f(=1-Mh$-v#@-VZU7l*r5PB0k{W%T@rAwE%kEeKH%;L?g5E=P@e%0 zLHjVYj~D~|D2k8q(ZJ5hyEBUYE-JRO$5qNt=#HL*qo-i`v<&l%%J*3So&(@{o3Wj} zfbxsby=2q3BC$f_Mp2_L1NDkc-+WAZu@}7x{cARTr!ncp#`8M#Z}1AS$?UcXdX{(Q z-n7-6UvF9F*W2*)4m`ap8~C1T;QIi40KkWsQy)Rz1NmdfpV+XAe2T(nQ0#^Lxy^c~ z@C%#VKca&5zXa~9G;v>$ndEm0@I3%O0PrLH{sj4F$onDxVgu#>io$PD z9Dw{g=J6$PaZs$3Kd@3bKSy=y{nMrss~Ck6qsWe;aPqe>{KsbE*+VwTv;W#sdG;_@ z3bH(6J2kRAs^;f0%OKXWSU=mqZRzMhG`p_Wm^@n z9RTeC=wLq)H+59rJL%p#!?+8KyGrlfl=ssB=ng=p1oW_{5>rp$dI8s4;`%5@eF5kP zK!2k%2cS66eq3%Eq*5NNe4P$oLts2q1{c`&tvNVs0QEy30SYX_d)jElKO0_S>55H=dMyHMmTkFCrFA_3Hvn)W zX2DI6Z-%@D@-1R8x7yX&@K)9L+my50;Our7-65mhsk-(q0PY50yZuD`yF&%osRG;s z`(3cVR|dFG1-Ku82LO0b0v@s}3KoBBeHge$fO}Np9^=vhIC>m_CjfZT7~`iMG7ge)f0^nr;Ua=b#{3^4>Fq{RW?5X@G}7W z5$G4lze4^E@_{ss{$05^2p4|<`lpQXmje6^z&`*Sg5Q53ABKDc@=-e|_!tVc99&V` zfdiU@FO4hU!tTK0b2w5d*y*rPFxXX4unSJy!Z5>OqF|3hQn1&NO2Ks;l7j0xPK|== zIpln=?=a7Z1`eJ`LnPA3Vf0U9hoIml&^Cp(nbbCS$ko>ZfR+HXl7Q9@*}ZK5XbV6) zhrT@9qu9Y=q2P|ns1uAj1J%W0q2R8l?B+#i z0x$}I(MFe^h2j{;aT#x{%6gniYCL>Rfbm2bY?5l$WB{fBkmWEK&xi6<=%zU=j914f zYBU?D=?)9y)iH`1odNw!hlTO#7)6cxp`YcDj5phn!gzBWH5dO}%ltkYIp)CAJlVkc zs)6SKumFHuEbWDm1CSR%4m!Yic_`#VQ2;sQurgkuBMsvfsqPLVSOm6FnayIAO)&r^ z0K^a|4!IOEflSjhI-y)7;i3%Cav5WZ0xSh!834=S_gu&;AXh+M=>X%cLSZ!&Yap-1 z(&z#g=LzCE-;v6Al@1Hzd5oe)H8`mfhU*+A#;bNn#=F3g%6RJ?lJPEdoEqa@q~`m@ z5D(}x;u7S3DH6F%_Rj`~V7$wry#m@RrFNrQeOCc+H2~L0z$Vqb*8;E^fa{Fqc|D3Z zI4q2JqcXY)MmGbs#bIH*TTr>xk^azmt3w_-w>eIJ=)BF+6}Kbm9SCrzY|C9Lt-Ar( z4!{n~f}N1>fxHXyy<#x$bEre-{i^RDP|hBNvxi{xu#EPI>e@#EcnpBY9VepTCscqZ zRe+~p|1|8MkpZ4n0iFZkc>rFJfEOL9ht8LPdl|S_B<@wUj$Z@dbpYNl#&|c1Z#oPL z<~?em;I~xPZ>yxR12*286@BUDSy0AM5l zXF5;Bc%xK+(JH`Mupa~au`<9o6<|C769AYf0h64mhtA2sO#v=T;(SiIY^DM*4S;N; zOQ)ka!+BiBo2jz)tE6VZ*K8Qik-_GwW}OW{4gm9<2II{~`5fpLI4z9V+9+x?7pR3! z3*)smiW&_-zsPA}yw*lhqe1BNoRabKohgi0kY@3REc3e%ITpcFST-=C8W;s&F#yF_ z+9i-mtVY^Ibvs`6!E&wY4s6e2VkXJ!o z4S7wPMz2*a&V!5d0j-oVGzF*vU>yL}@OuH|^^h-we3283cQFc=KyfML%dj*C!o>zb zT$ejj8Se_Gh4ES%MU7qwCmV&~RZbJ*UG0>Nca1ZZ@isXn<6Y}KHOAYl=KFOJcj`0Z zdgOis61h?K&rMFjcsE141=?GrcB@)_w*s&YfZHVCcGbOi0B|P&cNxp`ZWOmWEsVEA z8SRA8JwWYpS{UzMRNm)If9Sm5DG!|wI8T1)e9+Pr4oh2s_o#(} zKUZ0Qp_2L%zP^I-*D}~Qs#*I0_!fZgoCXDdkMa-D{phq%@Ft_E(Vu|&*=eERO-50p z`=S5EX`$dvMp2``LjRjnQt$z13I+e3hK&wdX4xOe@lSaAOE&Ot)xduMI0V4Im|2G* zAAx)n@-ZhUxRwiNJ{MQmAlqG53U;{CP_WY_yW8dB!Q8OTa2eTnTrwLk0CfPUi$L`t z*N5Byazj_mMmJI}8pA~sK%2@K%@m+H04)G$3BRo%w}#vXa$6TDxE%`Zq38g)BUeOs zq0>6Ku=qN=QYpBL%R<3dD+=xkC*6eMX)Y55cXvq&&UB?xa1WQH;GV8iqu^dHIp2G` z%rl}7n%@_R^ppM5-z6w`0JH<49VE4bUF!B707C#6Dgnb(_YMbO1OR8a^yN7c#WP(N z3Ld45M#JbVpvJf?6g(D{<6J2e4DJ<-H(oV$f^s<#E+@fyvP^V}YDpFVJ^-e=PQ-ZA zRDf(1U^?t)z<#C-;8y`=0WceYITA3}m3rtr8@L?c=1JUqwQSA-U;zNRMwc!`G2l8b z<1JEI2USve@Rbka0vRl%npFru5ddMA!FUmrqtGpOSs1T}QPgNLP$e!4TV6es$jcLW>c-Qxd4Fm09=Sb7eT%l@+FWjP1ESh zl#2~;aXFw@$QV~Dz(xSB0^n-+y$13o$k#&N>;mImhr;zx+yMDTER8X6ag!jfn_a1l zx5Z^)yzWL(qqo4xR$+K6H%!uP>1nMi7+6g;W(yvkZjVt}d$Uc|682Q$9@{5u0Qj+~1aeqLFA7zt% zQi=TxzoKU z=4`Rg@#HM~+))+bm`lI?Ab+}6%gtTYc5_!Yw-Lzh)_+C_trJ?8+t};eC}%+Daa;B} zOhK{Nd4a0qw(ND#i@mNc^!419y$*V@*VTuTg}U0M|85rrlC9 zJq?L;2R>6~+Cyd96M$X-^hOeWAoqpb4|0DucBug<41{736zSFimiBHVH9u49--2o zjnLnhL`S>TPI(rS@e(9MImUz-%5w{Xf~p?GJE6Cg9l34Rv!@W z#iG;siWM%njz5l?9}m&|P@p)%;?8j6HmeL{VDuNi0OJQ{7RNNR`VlDsG#j8f(h&_6 zvFZ>F6&P=}O3=A(PO@iXey{kSy%2GN@ZqI7#^}yk#@v@MNIsqCHcmA)U*Q#q#r0Qo znO^3lY^C4%e45rs-N=WYgVYxw_2pslNZPDru>u-rV8#-9yDH_l>x5KMI&QTjk4B;QkJ3SspiBjoYk zD$5g(Ax2`QWl5a~^7#bTa$4w9HWtcHsu>_=Z2^a@&{vM}nkE`zwd=&$)<+9X3lXoD zqDAQW{Zan5l_-DbNR+?hBT2(<<3v1;Mz~dVW*9I3>X=KT+~(BZ|J5_9Qo7iQj+`vKN?U3|x)Lg+g9M`(=#PB3`)VdOUw9(Diz*W#+jV;dS zNYZOi-o$$wd)v53tejq(vN$&Xf4n&U#*6R73-J(;*Js_tYZv+!_(o!}WPM?%efhp} zUx`>OfZxeKw(yVLT+Q1%-`AG;|BcrngO$E^cqQPetdHw39@q2ZiW;OFPrMU{9%(i5n9QaH*mZb9*l{3&>O8v18cAalmg0`eRn z&kKl4zLJ){0PTy=zQj+jN%}GhuYmJj#pi2$aK#H)3*sfgNQ}NN&I}&?71pBi4P(*i zCzXUa{PH|^v3h8(DPO`i{<|n)){RHWJ`oBg>Ui#U8ry?)i%L!=5EkAe} z0ez0tzQ8K)kSUgVmrDAjTf9KwE2zJA^UIQNI4Ej)orM?${ytl}4>Rgp?4{qq>U&uI z0N46>jnwFm(EVf@#L*i48I}9_JmVu7HLE*m^cSdqHC3`L{x?(};PCw>O=FGz4$VPR z7da=u=jgws2x~GhN|qQQ70;0rplHYb)z!FR5?PU z9#ndHr~|2;Yu9KUsOxey4mKd{++4RdM7jQ`Pr zt)c#HQwKKRVqcf6H~0}|*Z-TNTK)9zF~;$cU5+T3Uy(So4hc7S^YX{y(A8RnIl-YT z#+H94^ke4?+h3!@P&wRG*;AtQh-1 zf2f9S4s7Si^yZ^{PHM+4uq2fW@9ivur8;&Y90%ZNkvXZLd3FreXdWQ>+}O-YwKZA* zO~@coLsL(qh0qikvBHQIL1k1nVlm3asf{SHG$LlsroKkwFe;Vf*uv5%flPeD|fQtFuK)9`VQFM3ER75(s!f0JvHeamZW!P7~23ApNJ!S;~qHP1xNRq zle#ZMaw?j!Q6}}i(V+Wb`v7bolu12=^24b~Jz`1f(G26bXmnb0jXnm;$4xz1N25=m z@<}5)0zZYoPeb#JIq7HNwyu$7w23ht#0e5l(ZKe3*uEf>ei7xDQj>n!lJqO^o^Px) zoLAxVRrq`jK3+HH^M-lC4AtmvK;Fzq;gE(JeG8hm`s%zl2bg!>WReuDMS(%XKNf8pg^`fH8+f3xI&0D69nqgLGSaC;DL{xC=X z6X30KDam8NU$FQa7XQdZ4x#*SYO@Yo5;y{%)>Fk%xH|^(S{_pZwLKhs(2}jq!@+hB zE_ggfY$wVtUJi*HhBoQN?UAG-Lh`pm!ixu1UJpNA*75M)1n>!#q;);=#hCchO+8kI zO8g0ACaupa^k0ND;IjBdNJB1~e-YA%SBPJPH1?d@FG8Al_R3!iG(+0W zk#-A@@i*$09{p`S(6)xQjnuaFh~FF-zvgQPRC}O0NK{9U{5y9i06GKE#iRc$q$`Tu zcze{(LQYdg-C>joR1c0)KalK+%3i!O&Cf!5^Lp{KkUpx5j{CEazBT%!A0qchkO8ti z1668+02mCw=^lR5YY60_kcUAY?!jNrM|ji^L(WhGF;Y1@6V67#Xta!WmY$XI8^19? zjRk6)C*=nsJW0zBM8>N~6H*3iA{cuZa{Ls(h6+Nwb@e7gJ(9hu&;uj)wJt@BsIonfn1aoSP;5_6xAKuQ9 zja;A_nG3)|00J1bMUaD#^C0JY@Eef=6hcrGLN4-HeT3@DIl*7dmK$prG%M@Ta0Ota*0)8tXuY|k`@@fx$aIyx4 hwNRV~`Fu>f9&k}9X1C@MKRBu4-(`7y{|{>C>p$1s0muLV literal 0 HcmV?d00001 diff --git a/src/sage/coding/tests/diff_decoder.sobj b/src/sage/coding/tests/diff_decoder.sobj new file mode 100644 index 0000000000000000000000000000000000000000..5329bd4bb7f930696277b9297c125297046da9f8 GIT binary patch literal 8804 zcmV-qBAeZKoZWm0bW=t6Z^^wkg|Kh3i6EjBmkJ1oiXuBSP~o!GrG_-UZ4%nFGf9Dh zRZyEyS42Tj6crFe1#!iF7x!IUaNk#O-<3D>n|o(+(@^;P-g)nwcQ_>9%=i12`R>eb zCb!95jy&m6MZW+XWJ})fUl&>&i$xlwVdI3C*dnd^Is4Otuo?s!|`(iwno^=#6~*}(k88rpH5vU=I=fOCa>q28u9Xa&iE^`vLIZFN;eZLF%2U6wR4P+m4@zHnz% zSe2~iK622$t+@9Bh(4Xw4^w z)RVr2MLLt&1&EPD!+mVvKyp|;>1UF>C545B3DUog9G)OYnB>SXGdZfTAwlv@a&$2n z;Hpp=N*ZvLE5JPS%jzQo!(GG7>mZXH!z7l-V3Q09ADJRUO>%75)($htabffxZ;}(7 z-r*)WQS_csW-Esx9%(ke1qxE*7Xv3}1IF)m&=GHywNjISdT z5@e!D3hIf!j!Y_Q&ewk=UL8z?!enwoJVgpkatfw~SZl%gk*Z2ErKkn#nZ-Ir1>=!8 znVKS|Nv4IT6t$O=XiS7%yf|Af!3r`xMP`^}X4Y9g*W%YJz+#5j9G{8SG|?cl!Yr|( zI&x}?oMw{K!3$KTW6#a4Qmt4aRp{;@WI>G!0)tmiLp^OrUJvDBEccb2^q)V@$zjt{CN?U2rZFEI(nqc%exyVpm!7H&2j@ zO>&7?FC}~p2TIen=Qz2vm@Lf;vkiYp#)cmUC)nGSd*9Eu=cc_-FLSYPO)SLs>_8dc z)AG}U`Nr8gYiuw#%{D-B=;DK2>vBPZX)fHAk|&3Cd$(Xr7F5A} zWIeAW>Tn|;_6Cz|tS2{_WYfN{(t-tnYAf^5a&vfNF}Y>$M*(NjihWuUPuda8*2yB? z%GOX$l594~mN5Uv+gg&`W|FPg{%=o`J4|wC7@_Y9Zw>O!yOZP|lWa?odrfj*irjCK z2g2-Xgx7}Gg*OT4gEsV`Bzf2*kBH7k?as%NF0m?STmzOMuTceq<#7gja;wYWz1Z^AhaI3#&QNEJ<_NA~tC= z_R41l^)MkxHFWCi6-LxxQeJo~>@b6<7r0#DnzPpwZ2{Q_xh3RQ+}<6YwuUDj+mobi z0Bg%(!fQKtZIA8_GI~dD6XhvJ)3BX@?F{Szh}s2mSIFHUcjv=O(jI6W2t`lG2eH?9 zlJ;V6C1G||X>acM&pLWAf5#;VixSd4$T(vdhoF;{0~sNozR2fLxIawh-VeER5C&l9 zo(F7yU=K%HM?gLj@==iUvsCYBrv`k%&;jr=5a>ZN;xQt%MllBiJp|~Xh;uCDVUUl5 ze7vaF31|$5;zY7+x53v;^ zKmPfJe@>ei6Z;7bA>UHuYjM=>BrQX$977MYS59>_g3c&=WtVymM>fTIA|+>VK3c&I zcGlBM_FhM02wDY?)#xF-Cir9(sHW`0AEn%Y)ue?YGW+aa8LT*kYZ})pXmJjT~09*J652Hd4K|5vi`hD6WIy^^)msP*k`UfOP<@$0fQE z@&?EoA>WjRO*b_Pbu&EO0*hN^sLd+W765JoU@P3;4*3qqcS61^OQ^e*i+kW=8=&{f z822f_{Qx`wz=QDn5afp;KLYtt!Pt+X@i?Dvyx4L21d9D6ceXl3pTZp7&R!GrX+WQW z{#o=t2Y=6_{{{5Fi2j$*|1$btLC>qaP*Kcn4ip`>+&0^7 zhqO-+;^xETz4X&J(&nh!2X33$hB%7)=nh=ejed{c(-3_FLw^&4cniaN8^d~sy)G#% zraJ+~1$ft<3~VASNzwOk4c-U%19_1?WD>tZA3^&uw4X@rr?&PpXg`Pc3#t85UEr?( z_!@w3_=WVdB)>)TJ6_&a;_ekMLX6-2p}VA)-O9`N@bUx9e&lBDz(Q69xSH9QQ_G@1 zaRYwCl>Q9lFC5ts$Y5opEE2-U%RnSnYR#v=GNs=m;$ssFlvov2^XPB?fxv$Mj|3(^ z@6taMh5hMJ*k2gL--5#a`9DNqD&)UdIQ4W7W=xKU&zNQ&9@69C3qEC0NMi34w2hK?f|(XfCF;}`sffO z#Wq9S-(_6Xz^3j{%#>q2oRNkhq2q9Qj#u?M!LHYE4B|w9PLe|%q3U%q03!hyg;9Ez zBAVW^oRq-G&q_L{ERHCZ>DQf%|h@Z*qkaWeVVHD=>VJoKrvR_ znUDjJXG0FM*I<&Cpb>(i6tac*GTo@is?3w+*i&v_`Y_@}U>=nT%~1(e08j})46&*p zS3@R{X_j%vm5T&i)BrkH#+c_S%zR*Kfms083n8BcxeoH#9vni>L1PgViy@!O7119- z-RB8vIv>=;KTxD^EcJ3K@Q(q}dIU|vM@rZ&fgS$Bju{&W1nfWF2y&+ldz;pURpJc7 zc7!pJ5c>po0YmmT>4na;%y4v(%!Pk8j9!FXE=Dewh+Hn^xkT*`kV`pIeDu4FS1!sw z??vsy^)gS|&J@Mbn0>go91L&;3bh=LS9ti;y%PN^(Z34)SE1)>kK9!_Fv?F`nT*R% zF{>4WT;sZk*J2E7;PN_op|7{6+6~aIg?63Pu2=KqMgTSduu%eTQh-eW+zh}i9-C}$ zMRPMxSe;w8D5Kk8v=yk^IZ7Q-?m*|Ayc0)}W>J3fh}zrzUA$lHw|9G7`)xGaHhYh& z6x$H>UIe&Lj^%!p)&l@M2*5+Q6c0mw1oESh9~1N7am)j;xjvz4|D;{lr{HNjES{F3 zo>8Hm1>iXVp2t$z+g|#D^8TXj{UsQ`4C7a%_g9to*8q4OfHx#yhbQy1&YQr!1>D;b z_l|P36M%OCc+a`I@1ywv&lfuk-t#^5KkyE*&-~~S z3@vZ}{gh>X{p^}wzrfS4@bsG;;O}aHe*o|&0Doal{SEma$p1p#Hf)<~PBtwRzz0A}09qkXYshUNw}sqJYdq-o%0&mb=m=;h z8KbiT8~{KU0J_3&H^|)~_ket$hApHg8V5nq3vzF+h~AE(9jsy5_0jmU<9r{rsr(Sl zCTej>LCJ!*`@+eg!tgLHjko)0lDG4;Oy2I#m4bW^*Y=NmkI>|NKT>mU>Dx=}C{!RH zxg0G^G(Zz8c_6fdpgl%v2W$2nZq#rHFhhYkR$_*!A|D69@c^8l*^6{InkQ;53O-30 zjeya~K#kN~6g&!@qqXc5JVuigJXYH`1&_-t$an;vfEW|ya0*mregGx`Fd5UK5b`OI zr$C-6uBxdi7M`Xq!gS?q2As`=(JUFQ$W`D|fjJGB)3v6!_zV@JSj9LK_5s+>mN9~^ z7$v}jfGL$2OOuQom4B%!1GF5_u!Kgm^uLfqftdqLg>%U((Tr&hFZ1fUc)3cYU+v0{ zz#WBsT!u`jA=Use7l3)1L(TKiu7z%a=A!2QPBTdt0(F+=qUQciGfC^9KU;HAbAPAF z&X&+G(j+x6)-tI1+$=10Ugo?zA8FRZSyGNMrN+1ffCd0Az+Af!@G(U2_q1 zKc|_bcfiS=!tgFFjiB$=BthS!WfJr@O%n9I+WryreQM(0pE)lcK>80NmxpAD9@Ydw zKLYKe&^{)$kGmG*6TmzP%u^DxT^0Fh0G{zDn# zBUg-%f%ycOPbKCvO_8DeEA$<+ZH^6-Bl=nL{cWHYi!`&+V?_Jsb z0Czva{wEpoXEnrM0QePv-!z8|e@FWd=>F7PWVp>~Ch1>5{jIskaGTRi(tn`;S96i! zHm8}Sd!WzJB^fr;Gsw`RH)aG)&zSVO&Yc-LcjncdG3M%WjLiXP0f0}(S05m^g4`N% z8y#fW7L9gLw1?b5cavd9JqsCj($kCH8SxH)c^8>bSCvpV0J;Ov1F;T-+!OLakb7kr zdT-_8V7TZ5=piyjUj;Z6fWrXj2fulc`$Ik)@)0`7@JKX{f+8RC(OeOI8P|V+4o(}W zXOiI{-9?6XD>6I=P6i9ZA$l4a4%HT;Wy4k2ZU*fRm#c)*M zMC5XkEYS#Ekm1SDj)ZoU)Q;BE7vmUU#sV`=V#cc?PXJ&d00p|eTK#BF(p_XYSs4|= z=oFx)=q@sxicV9{ep;QT%hT#~ecz|m8JPu{iNLcEqeu?tRF&Cj0GtlM8JG^mkk5o1 zfIM4V)u66Ut0n3pgp{*VIJ00>CZmqS z6qvZgB=jt&)fzzO0yO$Q`(8HW&l3oP;#kz~2hdIq8y#)G8 zbxF`m^$dc(EDK95%ba(YBh4$|Y`Glc3N^+n0ayvZD$KR3AYTo6HRNk_5cIWZtbyV> z$k*#`g1$k|LeOhnMPG+_>tTMQOlX5jXd?hO0k8?NZiajdO3x(d?YboBr}h0K=x5Z#e>QVoJcsn3M=meO61}L~HwK}7 z8QNE*_Epznd<~e_fq6q>cBmr13BX$byzQ*kchKCay9oMSW%M45-UsRf-9^wJqVpp? zgP_68f($=a1O7z0{1h%fgZ1Y!(Jx$s`4X6~fcaW)iVVL|F}_tXzJvWP*zcAxzIVm= z0hk|w`AK4a*0Y>ee*yGYK!20a-(3so4`BWT<}atbf1~-2zE?8*SEawlm0ga(-8D0~ zy~l7uYK9!54uAoG*Ko)%7wzWIwJ=;{xLuK<52%)giww6bGHeBXYr{o`+Z7qMfxfLF z$*`S~L5A&(#*EOx$e8pUk!B}2>nz83fEr^L0J;Lu&EQMEJLDda4}{#)02v;HMlUFO zLq6DWlVKkt+xKFP^rH7gyhCArm`tdjN+=J2{s0_~SVurU67o@y^Ro>7Xysx6Tnq$s zkc@GR0t^OV2mnLj_gKioARh<$ctd>e6^-FgoCx_Ou84ksxj4cAr=4tMlHo|hMTSo* zG8_dbqlMuZBaIBl8j=jh8JT1_-jHNC!Pq}CoM_02UtowYwu(vN$M7d1k;$??g@$0l zQ=pv!?Nq5Xjr5f`4VdY`%#fIws=~7XC<5SA!(OVVp?SLDV!|_&Q8A3p1S(*-m~b{a zgGTnFYKb9_sv%?FN7YhSH7rCeLx6HQmas}I0zediIhY6)kSig_AXka&S#79uDpBR9 z%2^!F5-_Tf(dN3UI}e!oz|(l99>FFB>ji9_uud^c6UHRT#cz zr1A3WhUDcpj7(nMVMtzn)7U>=eoM{zw_TIs9pt_fiM%W8^PVAi`F&_Vfc8VF{m8Wp zKL+L#U_OXz&2EyFk~~>!QIOiUzv@)!pl&!5xYQdq97n*F}Rn6b<%-{vfZU!Cu}B z8tm_5;_)uVE`P5SjR&?0rGIjCuSMNZgAy0rj(F+GwBDxE6zt9UdJH?wxgHyaN8hl;R;8Zv< zh2b=An4r_yoA@c>cshfp*&?qf4?nS<8;MuZ@wBR@y2)=d&-6C=ZRS$pC-XPeqLa?z z5yg*jvi~3_SXvsvm$vO+=8Ux4&M$QMk4gI1(Nj^R(=fi%xz8#1Cbxr5|E10ufEELK zrhv9dgWA8?2>>-4sGvZ#O+!tLF;8)emH-w4th905(N&d|H3<>d0<;X!at_^{q+zro zm=aO1{YCCM=&bN!3##PErhkAcN|}DWd^E<*cGlA>FW>% zfzBE}aqN9@u2=4h^Sqh+;(V{%7i+!yw=XVGG`P^4{_UW%F!(y;cD7tF=Xk}wxCq+C z(4H%`=h@oxp{<8DDYYrJ*p>j$0Kf$fWnYNqMPApAc(F3N1V)zvwbbj{5idjMGH=F? zh_By?9r1EC&MTD5<#4$I)>q0zS1Q0N0ImYyYHw59;c6A&8fAYi?AO5lIvL=41-Jo# zwE(P>fc4(YufE*~+y>w_O59C$!8bvBGqkrj^}H3$&EA~7eeZ3HO8GY1(N;LR9hP^< zFn6ka?*ia%0PgWRo8LCH?}hF@uWR$0?=+M2exM!@H1}X5etgKyk8{0}A0LL{Bf{`e zZfHN?X9#`F%fF8LH~>$u*LXZhpX3Pfoox4_q)%b_36=fbT>Eh>bh}q&@-$zCx$4PU z^ck<6RhUGd!6=_a#OFBBhvn)#JcmBd`uv(JhW$7f`a)Wa7o8Z6@};yGFCoUui1CWo z`3Ac~^z>CejHvyq3I1L4*LX*P_yHe(W=x#E&RI(&VLzsT6C=;>4W3^!k+oG>VwLg- z-OwGt<0&6<8&MCTpl|X3x_C%U7JY!e#Q`K-ouY3e>vwq8oy6nAI439UM`s~Dx)ag5 zrF-MQZVXhjjVmA?;zQr%Q&PC#I{xFTP?bePR-hun;=bqQQ}}%u#=+Q6i~!~XU_RuS z&Tc;prhY-?VmjqGK3XR6k3t;a0#0!-|nBo3^VEXJFop!_lYt7TNnJyX&S4+u1vQP zr4Pg{`B9o zY5$*9e)m8Hy>KmrqM%-n1`T$SixhwQSHxV7{lMk?_sY4 z3O4Y77jjL-zMfAMYe>-FK{9`!)@=qum;Rkm+<*Q*E^c#wu;6#@ z*{*Lh)ORMUZ#R+e68^G_D?9H`iT_1G_oS6LCztm$%e8-|6$nLWs3wx2p4^PJ*@zSa zf$G}%@*#(T#vEy{&Vb)XwS9VP(p+ce1zXmA?JG=C0||R`IrH(`23!y)pm2RLZJuj? zDo+1r`J1!zObZzLa-9PchqnY=Jkf`?0-!Yq@R#t`^v8TCS$Jy$a9fwRcE}~uHk%}T zw1Lsi7MhrjXaHjr;}khQbrtQS7S7^W1t-?pD$Yb% zFXu*B0X+F(IZ4KuEO25K$`=?E!r&AbOp&3dqHSh|o~A-iPj{L2hvFIVHxu@=WCBG_ zq6!c6sX(6w^yxDC8E6-0Mn6+U57^N&A4D`8{(`VCk?4|5;;cp)7=gS0Y(-N?2 z5A*_{7Xp2jj9!QK*_qMLQPCIerR-y_WLa#juQ#oY}L_W-;tm;W07-dy?E1bh&> akNf2lL42&3O7G9LZ=uwb!eJEpwxJ)PzrTxO zqc#lq?|vleDq0jBPw@uPWCI)9MW$VB+GTcc(^lLS*rW}c5(fIa7Hrmr&1=F%LT<>! z(->qA)P^ImMH>bahIG`_)VSEP681XTmPFMu!u2wSDbwgz}trcftsz9Z9I*ot^F2!^0@T zp5YRaZ6etmbu!pX(e6#yhXp%SU9v^kx5LGLt=Qki0ff=Agac&>2NA|ZFjki!@@z_R zQG~;}r8eEY^cy=t0M)*}N~x{f0L6Ja%dO^-2FCdvF9{oRD!T14z73UxD)}Ntv(iq{ z(Fv`X=wcFKas;9EH<9Ne`4#pI`8pG)v8vo_XCg$Qhbe?=JMZGGRP>>SaIh`X6mh4T zwlofSID|kp2_v}=dN`C&>&pcm4kJtxNUc3h5gbkkYl7*f?g?+3VpCZSo5=by zgAEpUz`y#k$!rp_V3%X5$x3qtedV>s)_hG>^QBEd>FRzxceEs$W0m9iP)mSBN^ z0-;4R$|{KsVy&ThJ>devw@MUU8Coipg+X2^HVf-a(jnGwWH;ZevV zhzlXK*0(Hh7y*o0&R$NZ14_3YVJBmevNsooZ+FV#JR4TQ89 zrnI=K{S%(?&`5yo+c{E`_23ekxH#P|nhD3s)IE4=?NZA zB+OA1ClTffDT0$t&2)-pYBTe^JP+-J4uy5*W2bsJjc~eRJcDqiraDVg&C54G+rv48 za}{=;)XevAKH&mIa3NuVCb&owT&!xS)&!S$xRh|2!WK$wn!J4Mau16LS177035zw= z5>0iLGOyKCS9`dIaIM0wQ(*+^W7m7QfpDXuT1r@^sczC#H%oJQ>J`;39&RPvrm)); zRjtnI4i9${?ow2D6YkMe_iC#9@(aA*!vll|750z{Q>(Lj*ux`)M-|m_!eg51aZU9^ zet}PVc#80}!k$r7)jF$ZJv>KvUQxY3cu`Zmq^VxcFYpx)uM%EU*y}1xwa)4d4{s7y zD5|#zZ}V2XV}=KaAAvZ}CKG8Mx8ljH8%@VJREu4NtD`dVM;<;Ve4><}O3h~;J}0bJxqLzRQhR=7+N+h> z*V=cDhi?eqD$DN(--}>Bn&GZL4H`2d*_=B)k5&cQ%QXEb| z{F6{LPJs{=lM@u8=M;*SoFb`KxjMzZrbMiq`z>HW<=e}r@q=}=JEgvRnQv@})pdG{ Lb!PcrpI9De>Bqt+ literal 0 HcmV?d00001 diff --git a/src/sage/coding/tests/diff_decoder_K_extension.sobj b/src/sage/coding/tests/diff_decoder_K_extension.sobj new file mode 100644 index 0000000000000000000000000000000000000000..36a9b0995dd3517b7425f7d193f2eaf56db56b6f GIT binary patch literal 2709 zcmV;G3TpLuoUK>~cobD0&n6*|0Ro6vK*8?XA|PT#jfA!%5g(SdtmCpf$(yhxzqhk7 zpg9%IY+~=dVei;cV@JhaP!S8Kcb8OOC}I(90Fx*iN>7-H6&HPIUB z6NxFPFTn5-g&bC{(aE@Wn=OoVe4%X>6%37rWWp2->%nk_2GZgf(SwndxgFWmqD(d| z8&DxyZLO2LG@PBX)3QMZM*Vb_^2rWo^fujH)Z%}&yT&CtsIDQRQiW?e5)Sqz7(32E=fLKi89wC+#Z$t7fyQYk|{thDWN zwQ4f_dq3-M^L)lZUe1r*A?%#oMR8Z1C;5!9llg=^7_vVw<8A49&WdM}Ry>(>T&Gh_ zys{Z{>D81;Hu(;1Fwn76H&B3J$kF*>o#?pSig!8LG~DPgX=!#OCuc!kWg%ZcC&P){ zjgFSCB3M%{mrXcvis&j}5yRs3BBp24>Ab5WPAcGJhHe?rP!Vy2TyOCdeo??F45vz` zVp9oxS^-NKPM4k$6=`!ao$@fYaYg|>44(9=t>c3g3g~6%lg1%5kWNRP$vCrsvl!0y zYgAG@r+{-A&ht_99>DnpT)=RlJRL4#xVRgabmP)+bg*hxGMkP&8C=GvMeCI=&wp*X zoTsUImXni4TO?H7D|%_fG^LiisKd5oXK`gqbda*9oK_cCwP+yAp{4%k&5SXxcTRwHS=}iPaZk9YQZYKDzY=MGdtuOYykB2M;hjsE*mRS>GpbwTHU#u#4pkkHqk3n5#KF z(8FU4{UmSW;cuqN#5DR3k)ks-oe9* z3@?#f=iz0BS8TIho`A11yv8M4S^jlhzRJTJ3~%z4TvOQ5!&?liNFL_lZH9OFo4#)& z5AQOpCV8rd_ZZe_*!LMe@L@j;%WxI2kB5&K){?xPhhH*$O!B@SK4JJ3$x}T1n&DHD z$9wnbnZ$u%DS#ITO!?LGXN z;V*v1{vQ6y@HJn@ztFIMWB9ud`;Q{*C=dT+_!rl#Hu3OphX0V<;NcsFZ%N+G!*>h= zT#sppC#vK{!$e3v$uigz)nuuWPbwyANgt$jfAPiT>D}eO)VGjw$(|rL(Id?8Cs7FD1Z5JvwFO*9@nYI+HzvO z*&z!aZRSX8h&)$x`C=$?iF)l~Hkjl5lm^MM*=&;4hD5GV&P3!kg}O}1ZRW_xawXKs z$lA!~gkc_L?h*M!iM1y8QK}@$4B3I^$UVw&uz5mci4<%z)8~qMnm$8Kuv~j)wJU~_ zWte=%#Bge7m04rQL<9Bn8&&^XO0SKH5z5B(IY9@)GP7D92x6qPcZ*T->57dAZ#3a; zT%O?{`!HX;*y^y?`=A$N6vt&Tu?f}Gn_O&4;F~Ge^K#XGCZGN4yGd*=O`aG_?s4+j zUQFMD(pUH!a%)d)NipMhCw-#UV`7p& z9X`Fu0eX9ov!4iRmi>GcVNW6KJqc@SC7r!WBdU0eFOFhwO52Ch_AOD`kM#WmeVP^^ zazMFH`WzkYi32I?AZ7cQuO#>;f4W?ul7snXN2K`BMZn zhoBoCn!&kZB^`Q$jflIan|R9^~1J#i#O9Hp5l z+g_fClI`eXPmdv}W65}2iD``V;{!~s0Mir7Ow~m&;LmKF;1*DNqLNv%)WZWjVUxdA zxjt=IulAWmK5YcRDZnZ5X(xSQfKMvGCtY4SYEo(GOIGg?Xof(u1kzE-3dCw1UrH?a zYKj&Voug>CL?KUlXMnHNv2P4RXWbrQ0SQydKQJA zT`GPK>E{NDpBE^8ez^&&WMkLyuL- zMA0`>^erVyw~~IF(!0g&Dla-pe#<89oYR)ElU7pxof(&Z&xkvu&#a}t^5{!k{bNYP zlTJP-?o_T&zBeZB3I#5}$alp{->j0?^AEO$}D?$3pg&rrICn)d963$bkKdtnbcqYjC*#PGi zNLSs<>4s>}?8rhr-@1Ra{N_drG%y{>)G*e@(fTJj{Cw;(dbnfKomTsSlWsNa^rsi22fGO_8d(! literal 0 HcmV?d00001 diff --git a/src/sage/coding/tests/differential_ag_code.sobj b/src/sage/coding/tests/differential_ag_code.sobj deleted file mode 100644 index 0b7d7daea370c2c8e2e13e3d120d8f48884b5512..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9112 zcmV;JBWK)roZVarbW_Ef~HNLiUl%~&E5tQa;`F-R>x4VqQb{7 zfl8}_t;4Ouja^k%mDW_4ao+^%FKLsRURf}d;O@)Km`Pc^taiYjAs=a*zc-tTs;b#F z)q(Pgs*+%ZkF+al&PqHw5{o3v(Gjzvl(erW9SqXZ-^{NSX-r2$1IYm?(#i5jS&id; zBQcgPTSJw>*=E3Qkj_Q9%wQ1dQct=Xq#Ig6a$r3qbT|Nsuvh zWNd*K_X^6cRoENF8Bom98v7YIyV|Xwg ziIYhwVi;s{cw$joxrjza*yP38b_rIHDJe45Ak(t0@;N4-uKXM{83&Pm!Y`SKpNYEf9%ndKl=wK+ws3?>o zr3Numq^zEl*O73FL<|xI6IKT~Z3q@3vy-I4AeBiHD=Z8*Pmn5uR5RWrb%f>@b>IcE zcVXHxByk5NRYYQDkP2EF4_VMrqR7h~RG6{yM3~h0^F*7`1M^4bpfxsFS;>5mxkVkh zuGB0G)>I@U*)qa3$vmu6KdE(M%vc&`KD;qHiE#7-k;Su7<8r*9cG&46gg z%{ZF%2|}`rITYK4bJ8pu3eFj*IZhYh4thdgDgs;mqFy*6j^L- zPxiNSnDNJX2B{A-=17LShYwEIu|=IS$P&Ky8q&7w((n?oJ(d~de6dk4s3R99$VCQO z&cPS+je3beE-faP<=cDda)VqUd|qjgtHR4u`GRnGl)2it#l){(msIV4^sSL8!Fpu1C%@JDGA5NO4H35?N8mR(v`uWJZ{A~8DbOZKcuT+6K1AOFx ze81*zQ%oMrFKF1aXODlPztBI%-_q~%cV++Q;J=psasILXk$%H!x3T_e{#^DyH z@k0MW{!W(C!(5H`Pw|hk08GCp?Q$!>Z0DC7`DF(;7lzIJ)hyZ!e;ln=1siRZCFT}f z&i@?cZ{_c1nY3jIj`R;>(eete=E_jPD*mUrzpP+A7fvat=TSc5mlt_QFJ{%+AM$Hx z79{!QbFS#-?_)X5VNOS}YK$yc!xh8*vkJ~-g6Su0!98S<_2JcQ^mz&Lut6RXj8Vc_ zEKr(uK#7w_i^*g8VRqyhoN?rdgB7hCw)5oE*V>&HRXy&w|J1}n{L~UC<9GGG>A`%X zEgf$7>9ORo;E8aHaLaJ3Fit4pHex5A$THh|gf#l=>j*GvUP9av$dmQtDT6#6UcB!c zyUjZ|5!h6t^ggnI(?YwVf&VTi@=QJ1Xpm>at2sA3mn6>{WD{7lmTaykFL1JW(I77s zlb7?GsH!7N=b-IF%$D$`{cw=YHb8Og%@805n+LVpzA5#*galjLKA>`IYO4DxA;d}ff(!)!Lf zTf?t}-xSa@|8ip7M5uKzP}; zp5~#y8AtJx8TCQeoVO>2C#7f$v|EbyloV}+c5B||dv|(NxM>47ZP_(N+X2#En9NMk z4rq5|*Cagv=uSX)2D%HhUD>s$CEFkl&Y|64bRZxHLGI43{QgT1MrRLp?Y)Q|!h^Nk zUxL~z|M_T7=Exe-p-8e9k~|E-4oA-sJXIG?BQ&4K-2#JuyAAnz=LdJ%oBP@jeyNxq z$)x;@#Xyfjr&^%7NqRK0>cgjssh7zeq4UE@jWXMakf*8Gp>0mSr7;9}amL{(i2v(@!9yRJwO=}km>lMv02 zLz#@Ba1h304P^?jQ-Pg^ET=;*f;kIy6$M+y3ufY^9jLB|)AJF9ov3=To{1HVMA#!?QwJz|LDMWz&3!mI0&W#UT*!ym zg7GeY*yIn@<6`30M=wIY%aQNJeAWbUUV@HGAzzkj5$EOTyh6{=n4$!77S@6H=l^7&tdU+RVczvO><0o zDoA@Nkm-Zye+d2S(f=^|A3@Kfn0tIA%X6WR zaaY_#tOuk|AO9EY;fa5@9^{SvNuG!({Zn@7pGM*vMCqSF8KeK*YRZ5cxg+*q!=6XN zc$yFo08{9*DByEEiG~Dy9yx4c*J8Rk8^ONNh+tpDC|-i$%aZlBC|cYKz$*Z}iYa;x z^6QYdL4G3(v%c9V)LZcMHY~QwQ17Tv?*i~106XCReaIg`{t)s+Z@nXm6_bB!cx#AG~Bi88dTz&}t3DBRR z{{{WO!ryP`{~i5*p#M+w|Aqd)(en>4R1|BQ1I0}t94n)HJ340l zv?T4pM|@^7VK4nO*QM5|(+589veoKL9-gU2=nPpx7YgW{eJUHRgiBj+J&Srtvs98zSfVcs0)_05BAQ6A}3&$R|TS z1@fuvTEm_&*q;({H!3j`K|0*UX?BEKFnzYtwQVWy$Fj>G&TFU^+<}8$|4yD=> z<##drL5-g@qT;r9wo9Bb7Q)v#Fjgy9Sf=LNqRdVmaiC5kEcSs7UM*SC+%irPczt3?VOvhbv@GSE`(@0^n)@u0gnK zAzugiddN3q8TyUN#Z7Q=GoUMEj9VPFSqaRoz}yDcw?ke9`3}f;x^NV^3yr&>SPgj% zS46wR#XW+c?gc^dhl}*v%UZc0`13$?EiCSXkNbt)I@sY05?Hr^K*0L9H$n0AfxRv6 z!YXkRVfTfRkq~>JdypY}8}uQ2ab`GLFLU9KiqZARsbi~i@(^Sn#mUN|tyk6oG6 z%a1dg6qRgt%;O6f#*1+IlAP<8t<|;#+O5#OBDJrorScj8uLH160^U%7HvxDHfVW*1 z>uyK$9iE^%z`U!B-hhH??AC~t&Vf+`2|CZkWQQr3ekfU)xu4V&VTINHKrg5AO zoLk~Nnsnp^AP<0Mnl-yVG@G+)6t_@b%P4MqQE^{up~;k6DqpSOt2K<<$Y5<1pdA41 z0qCIFx3-RG9{^n^&AKg#ead&4b(iW4R2R*<5vA$HeW@$--8Ad2lcpCpp#z~mi0j1- zr@JO@I2oS|9;`K9Wj(a?Rdxt6?FoN}%3=0W!#oUt!vQ!#<0~s4a&O2-LOx2vP334b z`asba@-bSr&nx?BS?(zPRmcGdIS?KO$(#l&z_9=v2fz@7J09{0kcUD(G0V_TQZ7!0 zi&FqSRmK>m0K)+o0l-N39R+zb~vhMNjfIlzUi1|79@_q3B;(8!l5wg}G7h0$Ue?L0?;>w!rElhT^v>QQ<&~*u*mjZg3gkG+t|BK2Mz+4H;RrZu$jpj9)&C$HN4vxN7rGK3x zyX)cZ2H4*yL*Ar@crySi0Juf7X?i8vw?cQD=Ah}Rb~8zD2Wpk(py{c0GfD4&{!Yz7 z(^KsxJ8D9Iwz$e;>yNcevfiahvi?NdKeGN*ZGz7-SI6f_{|n^u zr7Y1`njq`1q5THhZ>9D-$Hx2~m>+=oQDSzhBL4)y&j9>l@7!O}{7rL^_3z5)4;cLk z)L)u|tp7&mKUxM^gQW#A?ok8I(WT2=ox61D+*;G^n7W=m7&kB;V7z)$#F(ec7|m1+ zAMBgMzJ-j@(h;K-Fs*@UBQb4tMU3(vuG<0H9?%XF+EGv6NCyDZ37F2hRo*UWcGdSv zjNMfF2RgDl2=2PW{$LrhhZ^D`0Q3alP~9fRUT7Z%-Ql`}70o{bn!(X$Zasg4O4hN#2gWQ0s>q)KZP0HXmIgNS1xkAqwQ z*`K8VmbG=G-g0?I^;93asI*%o~eW1 ziuFulJX3cN<4#440XUf@41;&!0O~B= zL5y|iJX_Cxie0G7Q|vkVzE81>G7EAp0xw34^W<>qRc1*5QUEN$a%h0O6!J32=Zjgr zKv$>O3)LiCq?|2>vx{MLiHvrsqrjH|b2%_q=uMIJl`6(nD#q2YzXtZ#${5!BdNrFJt(*F*oX?jY+@yP2eq zK>w&N$@(!pD_K9Dg{z*(TzyX>*{9(0X*tpjYNXEqun~Y~vErVC{5<4MkT>ff>le^? z5sH@}zpQ5?>n(Z~vfk>LfL9RpRXBM~ruDi?Ya0M>0PrRvz6JSh$lD>mlcfOfDi`m; z#STEl70&(-wDI-^)#~nL6>Cxqn=6DyLCy{Kk55N)<3IF@Jr_E_!a5@hFpG^CHh0R zK1hW2FKGXk+J77ya}O{%ZjQ-y%g++svdEenA0h(ac3V5wgQnN*AnQD3)C@*GpqjfK zWZeRtE!`Pp4VD(f*vc&j+*-M81D9=K-A*Rj-km;}4#0E-<^Xq7#Mnv2=&WLNfqhrl zcat#=bi_CanC`$FEHORYSx&Ks0NNAKLnXAAJAES^2F&5W9ATF?AI;wGy%OV*D*dAz z*&PjcePG{LhCD_Mu^#~a0T|%6iE$v>gPIY65~nk#=LN{J7ejef@DvH%VBb)!_`Pf05B4OQEtBRM?)S1c`W2{ zZV+Pu8h$9oL!RKyMvR5-Y~Q^G@F;aIXF~;4J7!&UOBgPuF3Ff%PH)X}rn2X%!A(2{HpZRXV ziVL7U3)(uVJ=?J>7Xot*FpDJSTvg%40GtOvy}fOdXr|l_R$QWt8ep^(sAX;kE1r+e z3*6a{uot@J5%wbYzK^iW9o4uPQ7=J&OXXNDQ)yiez!d;oiG^?#*SGL3_R*LUX;_rfFVX2Teb$(tpH}-J@{#80;UH zA)in~d=h}C0C?JM)ARO>A2lY(#_Dn z;FdJ~qB|>1zm$cgUUn?KEy#2${JkQF`KlV`YXH0sz&0$mHz2-Fc72+8 z1V{UzZ4PY=-;%9>X$?#piD~PR6>bMWdjL9ktUcQi%>z6Rj_#z4I>V?7P+dI^ zj_!ue13eiW4RRKgc#s-xcjfY6xaTI1>`#&*PgX-b1%Oil80N7laX8u|pd0CNP@>CjCg~`kMtdBT=(3whItKc&9tS15 z>}HaVgTBBcDberAN{Qn=jY(mGW8D`b)6?K@q8#QVHB1A5$pB38@GUpu{<7%!Ohe!-d>1LC^8nKadWed z{GGYzq>Fh(@q3@_zxfH4mPYW6aO=lE!>qRbBcSs<_z_S&ij>6oQrzc6d`;X&r~epe z37`#tE)~#LX;AA&LCb(TAE*lis&yJ_T#R{&oAg4!E&^)PI-$b^G>AdiL*5d7C1}+db*uI9i4A z-+|ojl-uSmkGNCb4ee@Z*GTO>miAs~*Ft-r)ZVYQ+&Tar0N_E7eXd1wy~lBnd{`Mh z0;5NPdd%avM?Q|uCp;PVNPMwR+#{b<<9tfFd>Sq{!1@`P=tc#27J%mfc;3_04RVtT zuvyu^0Q(nV|B?*wvI1-YU@HKxNWiO}%rDiw2HfkwZIie+tb)G@?OV{kZP#-pM?c2F8>5ujf6k6-M zzt-D&=x&cn<|n=pz3Rnl^kZ8 zKkOK`^3SvweGyEvqoz^vc1H9FB%hDuYs2CPCX;JprL>C8MDHfzJE97_{o+J=q}STtg$!ZF z6NP4^JY2$7%2E60FvCpxJ=$x1#eZCk|I!CP@|w(Q&?nPvMCk)@Q@#x>Erip9jL?@? ztENQ!s3;Pvu1SbrB+_H}5?))>$y&5AGn7zkK&;z-9J0XLJN7$$Xn(9!947sAK-yNs zOOEJ3))VC~|A^uZ5d1v`>(x7>{x@GX)vD3UrkIDIe@MXw-l+C_8OH7J>36Yfzk=I&z-3%hv9I?^#TwY- zD0pTF%FV{DCFt?!IYCn6h)8S>9hyFtE>YI!DPGj}$BJy3rFW+)y z$NeesNhs*aX(c`dJ*RrD->C&c5gMwABQXU2RW5=w~To5O;@Y-TJ#%n#nr~haA^VoT7EDXna?E@W$ z7XU8a#YFu8jOPG;3GYaM35t@1w+R3jI=r2RTrzF5Ny5iO_?YBH+gj8vzg|p@|7=m~ z)?b%e)NB!y6@14X^!Ptm%Pq34|*Lvhjd(tTJ@`GmxUP`_8BY^1@4+v$@mO~r%+UNfW+EH)D1IBC> zslu|agnbP7D(SsCOCAJB3S?YHN}ydM+QoEEqtVS(G3QwuNcpUV&-w7NKt?~S5v~rn zvw>SEW1oZeqRdgAt0FFL9?sqN1;~qG!I+=~no=4feOo=&NM(p^mZN0rZ_f-zB5pjrQuy z=xbE;dm5GfUU*sy%ll;9`_r==>KOPs7(4)j2W9Ao&|aSz`e7CNk-e1tQTTfd_K(X1 zo=8i;s=fGH7tl`u{j`j}0qtipqi_GW z?om&-0r&>H)+{NeZ(3ApG3Hxf%(nsAp2ns9={wN83(b2Xupr(Y=z5>0TTDOT?uzM$ zjacX-RlA*D^$&a~z%F?BM3(DQiwr)4_H$^zkmdOj?XNN?=WB%gLOS_IMfnzfzJuNO z($5cQ|0wd_-6-LoRKhuX;)mfJ5$Y_ zMPP9_X$C^Uq){EG4aC<{4)Uh%Bkeu%(uv07i&C|L>S(+o81<2EW$jsd zr-ozUq%k#YL@P=6CelMEJ+*e4yUfjGG&G*{%8=fcManXqC<(_{xNQH_1Q!_ryF>bv zwP6b5N#7>YPbdA+3zFTM$bf9!+IX}f7Ox2hqgl;Bb=A0fVa|veH7w^ovir`pxQs1! zOw2P*ann-mcF*78rlL)F{ zZIH74Cn^wGlqOM~)TBwQwA5^$BypY8vYJU62`w(`!4qV&Fl!r0wJzo4v|@kr_U+rX zIa;YUP3x%nw0`Wr0{rW!&CsT6lQrGycd`C@wKkgDYS(GQ{oA+!Td9zlnyJmxrii|u z$+l-5uH%P|{BR{de85$OVmqytc{^H5px4R&q%E?VZTu_wpHW&Tt-qzxhXpuUo4~vk z`7hzZkbkZ8^cWwgMOz!|e}WHf=ItR`2W^@EBi@*(gRQ(*D@u|xb#hi&7rx*XoZ5k^R4l}| zXM&tvPR?;wR7(`7;^(Ondt2h1G1Zdc6gI^^ft)M0-yzKWj_qX8w!~?sJ}D|0Ij@PF zuagVR-a9`bIij-pq2xZY(j3TR+UMWJL@sP17wP0;)6Y*nm!!$1I#~twH;~Jk$mRTO zbcIf?EGJhLo9CLVb|HT)r7l2GBY3>49VTcn%Y<{X^0a;Rjx|^f2`Xnkat%)grgANh z{5qYiX(HF_WbMuqy<|zC*2ukLxWQagPHx=sdDyTE7fzCj@>$nEmQ3!Uo7he*NRyj& zvd-lHc;B5Sx9DU&&b_y$$!$8h-Gu8q%=JM&cxRg2rIWieen<4k<DlZSNjuo!&A8hkWO9@EL=8S;cqp3IP^bnY1 z+i~v|=f?PcvAJITL%KdllMi+Bk&u5}Y_604gnUz)e4>+2GvqU!e4Zg+=;X^>t6vpo z+50+8zR}6ILh@a{z3{x{HBxNGvp7QY-Ywcn$`sV%$vYp z#aM4wn*6PkEoiq%lYexwl?AZQ+zJ=l(qy|%3m6HZMBA`dULe%PM$^>Ijymo8R zuom(%Jj<`}Is%r8p)pc=-LplnqktU^>|XFxg7)5Mk3oB^$jdl%_JLqL+WY3@Wxtm4 zvcDA{_bJxx0Qfu*CJvJE9o%AkhX8vhu!q6*;bQ82PVMp)Kj zghvB=46w(-`Eh8MqkTNuffn+z(Dn-}L0G8(IwU<-w&>9SwhGv4crnoqqa8tek;q9D zof-&YXvet#_g;b8XoA+lL%ZU#p=@MMl-=yGn-u4x40(#e-3KL1qm}wx%WXKTGtA1` zOevpU2X?2#Z-4l0-i(+yBhm!oOd`${JC=3i_XiH|b(s5NK*3b6z$OlbwC)XdfA z;NECLA`uUT*|k-jK~DqZbU@A!5I*7g%y%$K&xG_WNY7@+h**nrFmNu~=h2+|oQz(}0}*+@&d&Q91b)59 z`M^50FRba(?d#BN{MU5AN#2634`kKTv`-o%4wPSTqZ!#Z{>r?<3Jao1;6HBr%S z#Vl@v;_b5f-N9S9dZc#(a2Eh~V~OrT`(Cu~L;L;~itGbXg`Ja(=E7qMwxgPCY}TIdFkT?1=s+0~A#y`UN#~9y)@lP=FDNj@c zdz%Br3Brt3(a+>Abv&jRBkxpv^mDG;(xZ&5dn55cW7ZDF=@(gh)M*1x<19ryjPubi zv8Zb_m*#4Ye1WNdg-LvkX?=rfeant3O3UeY0Am5Zw>AUY2rDx52du%50RJQx>1WpB zEA$Jbze4((l>Tl>|A2Heq<>23UuuE>24D*S|L}#>Sdd%M-Nw_~K|B=WNr>?$N_4xl zQs9zS+PJtCmy4^p*-<=|phgtsn(0?l!=N6nz@NiXFOY>C*&WDWO}Hu?!i{qv9IG_y zXp6iYv=lR1x((_Q=dzgu=Xyigmu6qI=WE8I=TL5N|