From 48457d2a9d82bf0f6f5db0a97f258008cfe51778 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Wed, 20 Dec 2023 14:08:59 +0100 Subject: [PATCH] details fixed in cfinite_sequence.py --- src/sage/rings/cfinite_sequence.py | 142 ++++++++++++++++------------- 1 file changed, 77 insertions(+), 65 deletions(-) diff --git a/src/sage/rings/cfinite_sequence.py b/src/sage/rings/cfinite_sequence.py index 4d9353e57b8..48d585384f6 100644 --- a/src/sage/rings/cfinite_sequence.py +++ b/src/sage/rings/cfinite_sequence.py @@ -89,9 +89,8 @@ from numbers import Integral -from sage.categories.fields import Fields +from sage.categories.rings import Rings from sage.misc.inherit_comparison import InheritComparisonClasscallMetaclass -from sage.rings.ring import CommutativeRing from sage.rings.integer_ring import ZZ from sage.rings.rational_field import QQ from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing @@ -100,6 +99,7 @@ from sage.rings.power_series_ring import PowerSeriesRing from sage.rings.fraction_field import FractionField from sage.structure.element import FieldElement, parent +from sage.structure.parent import Parent from sage.structure.unique_representation import UniqueRepresentation from sage.interfaces.gp import Gp @@ -110,7 +110,7 @@ def CFiniteSequences(base_ring, names=None, category=None): r""" - Return the ring of C-Finite sequences. + Return the commutative ring of C-Finite sequences. The ring is defined over a base ring (`\ZZ` or `\QQ` ) and each element is represented by its ordinary generating function (ogf) @@ -154,15 +154,15 @@ def CFiniteSequences(base_ring, names=None, category=None): elif len(names) > 1: raise NotImplementedError("Multidimensional o.g.f. not implemented.") if category is None: - category = Fields() - if not (base_ring in (QQ, ZZ)): + category = Rings().Commutative() + if base_ring not in [QQ, ZZ]: raise ValueError("O.g.f. base not rational.") polynomial_ring = PolynomialRing(base_ring, names) return CFiniteSequences_generic(polynomial_ring, category) class CFiniteSequence(FieldElement, - metaclass=InheritComparisonClasscallMetaclass): + metaclass=InheritComparisonClasscallMetaclass): r""" Create a C-finite sequence given its ordinary generating function. @@ -174,7 +174,7 @@ class CFiniteSequence(FieldElement, OUTPUT: - - A CFiniteSequence object + A CFiniteSequence object EXAMPLES:: @@ -247,7 +247,7 @@ class CFiniteSequence(FieldElement, @staticmethod def __classcall_private__(cls, ogf): r""" - Ensures that elements created by :class:`CFiniteSequence` have the same + Ensure that elements created by :class:`CFiniteSequence` have the same parent than the ones created by the parent itself and follow the category framework (they should be instance of :class:`CFiniteSequences` automatic element class). @@ -299,9 +299,8 @@ def __classcall_private__(cls, ogf): sage: f4.parent() The ring of C-Finite sequences in y over Rational Field """ - br = ogf.base_ring() - if not (br in (QQ, ZZ)): + if br not in [QQ, ZZ]: br = QQ # if the base ring of the o.g.f is not QQ, we force it to QQ and see if the o.g.f converts nicely # trying to figure out the ogf variables @@ -388,7 +387,6 @@ def __init__(self, parent, ogf): # determine start values (may be different from _get_item_ values) alen = max(self._deg, num.degree() + 1) R = LaurentSeriesRing(br, parent.variable_name(), default_prec=alen) - rem = num % den if den != 1: self._a = R(num / den).list() else: @@ -400,7 +398,7 @@ def __init__(self, parent, ogf): self._ogf = ogf - def _repr_(self): + def _repr_(self) -> str: """ Return textual definition of sequence. @@ -414,10 +412,8 @@ def _repr_(self): if self._deg == 0: if self.ogf() == 0: return 'Constant infinite sequence 0.' - else: - return 'Finite sequence ' + str(self._a) + ', offset = ' + str(self._off) - else: - return 'C-finite sequence, generated by ' + str(self.ogf()) + return 'Finite sequence ' + str(self._a) + ', offset = ' + str(self._off) + return 'C-finite sequence, generated by ' + str(self.ogf()) def __hash__(self): r""" @@ -656,7 +652,8 @@ def __getitem__(self, key): if isinstance(key, slice): m = max(key.start, key.stop) return [self[ii] for ii in range(*key.indices(m + 1))] - elif isinstance(key, Integral): + + if isinstance(key, Integral): n = key - self._off if n < 0: return 0 @@ -679,8 +676,8 @@ def __getitem__(self, key): den = P((den * nden).list()[::2]) n //= 2 return wp + num[0] / den[0] - else: - raise TypeError("invalid argument type") + + raise TypeError("invalid argument type") def ogf(self): """ @@ -726,14 +723,14 @@ def denominator(self): """ return self.ogf().denominator() - def recurrence_repr(self): + def recurrence_repr(self) -> str: """ Return a string with the recurrence representation of the C-finite sequence. OUTPUT: - - A string + A string EXAMPLES:: @@ -892,7 +889,7 @@ def closed_form(self, n='n'): return expr -class CFiniteSequences_generic(CommutativeRing, UniqueRepresentation): +class CFiniteSequences_generic(Parent, UniqueRepresentation): r""" The class representing the ring of C-Finite Sequences @@ -912,13 +909,13 @@ class CFiniteSequences_generic(CommutativeRing, UniqueRepresentation): def __init__(self, polynomial_ring, category): r""" - Create the ring of CFiniteSequences over ``base_ring`` + Create the ring of CFiniteSequences over ``base_ring``. INPUT: - ``base_ring`` -- the base ring for the o.g.f (either ``QQ`` or ``ZZ``) - ``names`` -- an iterable of variables (should contain only one variable) - - ``category`` -- the category of the ring (default: ``Fields()``) + - ``category`` -- the category of the ring (default: ``Rings().Commutative()``) TESTS:: @@ -940,11 +937,14 @@ def __init__(self, polynomial_ring, category): base_ring = polynomial_ring.base_ring() self._polynomial_ring = polynomial_ring self._fraction_field = FractionField(self._polynomial_ring) - CommutativeRing.__init__(self, base_ring, self._polynomial_ring.gens(), category) + if category is None: + category = Rings().Commutative() + Parent.__init__(self, base_ring, names=self._polynomial_ring.gens(), + category=category) def _repr_(self): r""" - Return the string representation of ``self`` + Return the string representation of ``self``. EXAMPLES:: @@ -956,7 +956,7 @@ def _repr_(self): def _element_constructor_(self, ogf): r""" - Construct a C-Finite Sequence + Construct a C-Finite Sequence. INPUT: @@ -986,9 +986,9 @@ def _element_constructor_(self, ogf): ogf = self.fraction_field()(ogf) return self.element_class(self, ogf) - def ngens(self): + def ngens(self) -> int: r""" - Return the number of generators of ``self`` + Return the number of generators of ``self``. EXAMPLES:: @@ -1026,6 +1026,18 @@ def gen(self, i=0): raise ValueError("{} has only one generator (i=0)".format(self)) return self.polynomial_ring().gen() + def gens(self) -> tuple: + """ + Return the generators of ``self``. + + EXAMPLES:: + + sage: C. = CFiniteSequences(QQ) + sage: C.gens() + (x,) + """ + return (self.gen(0),) + def an_element(self): r""" Return an element of C-Finite Sequences. @@ -1043,7 +1055,7 @@ def an_element(self): x = self.gen() return self((2 - x) / (1 - x - x**2)) - def __contains__(self, x): + def __contains__(self, x) -> bool: """ Return ``True`` if x is an element of ``CFiniteSequences`` or canonically coerces to this ring. @@ -1194,7 +1206,7 @@ def guess(self, sequence, algorithm='sage'): sage: r = C.guess([1,2,3,4,5]) Traceback (most recent call last): ... - ValueError: Sequence too short for guessing. + ValueError: sequence too short for guessing With Berlekamp-Massey, if an odd number of values is given, the last one is dropped. So with an odd number of values the result may not generate the last value:: @@ -1205,10 +1217,11 @@ def guess(self, sequence, algorithm='sage'): [1, 2, 4, 8, 16] """ S = self.polynomial_ring() + if algorithm == 'bm': from sage.matrix.berlekamp_massey import berlekamp_massey if len(sequence) < 2: - raise ValueError('Sequence too short for guessing.') + raise ValueError('sequence too short for guessing') R = PowerSeriesRing(QQ, 'x') if len(sequence) % 2: sequence.pop() @@ -1217,10 +1230,11 @@ def guess(self, sequence, algorithm='sage'): numerator = R(S(sequence) * denominator, prec=l).truncate() return CFiniteSequence(numerator / denominator) - elif algorithm == 'pari': + + if algorithm == 'pari': global _gp if len(sequence) < 6: - raise ValueError('Sequence too short for guessing.') + raise ValueError('sequence too short for guessing') if _gp is None: _gp = Gp() _gp("ggf(v)=local(l,m,p,q,B);l=length(v);B=floor(l/2);\ @@ -1236,37 +1250,35 @@ def guess(self, sequence, algorithm='sage'): den = S(sage_eval(_gp.eval("Vec(denominator(gf))"))[::-1]) if num == 0: return 0 - else: - return CFiniteSequence(num / den) - else: - from sage.matrix.constructor import matrix - from sage.arith.misc import integer_ceil as ceil - from numpy import trim_zeros - seq = sequence[:] - while seq and sequence[-1] == 0: - seq.pop() - l = len(seq) - if l == 0: - return 0 - if l < 6: - raise ValueError('Sequence too short for guessing.') - - hl = ceil(ZZ(l) / 2) - A = matrix([sequence[k: k + hl] for k in range(hl)]) - K = A.kernel() - if K.dimension() == 0: - return 0 - R = PolynomialRing(QQ, 'x') - den = R(trim_zeros(K.basis()[-1].list()[::-1])) - if den == 1: - return 0 - offset = next((i for i, x in enumerate(sequence) if x), None) - S = PowerSeriesRing(QQ, 'x', default_prec=l - offset) - num = S(R(sequence) * den).truncate(ZZ(l) // 2 + 1) - if num == 0 or sequence != S(num / den).list(): - return 0 - else: - return CFiniteSequence(num / den) + return CFiniteSequence(num / den) + + from sage.matrix.constructor import matrix + from sage.arith.misc import integer_ceil as ceil + from numpy import trim_zeros + seq = sequence[:] + while seq and sequence[-1] == 0: + seq.pop() + l = len(seq) + if l == 0: + return 0 + if l < 6: + raise ValueError('sequence too short for guessing') + + hl = ceil(ZZ(l) / 2) + A = matrix([sequence[k: k + hl] for k in range(hl)]) + K = A.kernel() + if K.dimension() == 0: + return 0 + R = PolynomialRing(QQ, 'x') + den = R(trim_zeros(K.basis()[-1].list()[::-1])) + if den == 1: + return 0 + offset = next((i for i, x in enumerate(sequence) if x), None) + S = PowerSeriesRing(QQ, 'x', default_prec=l - offset) + num = S(R(sequence) * den).truncate(ZZ(l) // 2 + 1) + if num == 0 or sequence != S(num / den).list(): + return 0 + return CFiniteSequence(num / den) r"""