Skip to content

Commit

Permalink
sagemathgh-36934: details fixed in cfinite_sequence.py
Browse files Browse the repository at this point in the history
fixing a few details in C-finite sequences

main change : use `Parent` (new coercion framework) instead of `Ring`

### 📝 Checklist

- [x] The title is concise, informative, and self-explanatory.
- [x] The description explains in detail what this PR is about.

URL: sagemath#36934
Reported by: Frédéric Chapoton
Reviewer(s): Matthias Köppe
  • Loading branch information
Release Manager committed Dec 21, 2023
2 parents 8c14983 + 48457d2 commit 83a72d1
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 69 deletions.
6 changes: 3 additions & 3 deletions build/pkgs/configure/checksums.ini
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
tarball=configure-VERSION.tar.gz
sha1=2389d2b093493c568deda190ffc326ff2b835169
md5=545e80b50deb4efa46f14d0a543ba98f
cksum=169905223
sha1=d12d97e2c8e20bd1ab7f100bfb37d561f21850f9
md5=2fba693187ab11552a3ddc83b4faf700
cksum=3223474542
2 changes: 1 addition & 1 deletion build/pkgs/configure/package-version.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
73e52a419812253c3c3ce72bab7f1a5ddf4c0461
d85731e67da133fc0254acd5d70e951b7215af8f
142 changes: 77 additions & 65 deletions src/sage/rings/cfinite_sequence.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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)
Expand Down Expand Up @@ -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.
Expand All @@ -174,7 +174,7 @@ class CFiniteSequence(FieldElement,
OUTPUT:
- A CFiniteSequence object
A CFiniteSequence object
EXAMPLES::
Expand Down Expand Up @@ -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).
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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:
Expand All @@ -400,7 +398,7 @@ def __init__(self, parent, ogf):

self._ogf = ogf

def _repr_(self):
def _repr_(self) -> str:
"""
Return textual definition of sequence.
Expand All @@ -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"""
Expand Down Expand Up @@ -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
Expand All @@ -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):
"""
Expand Down Expand Up @@ -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::
Expand Down Expand Up @@ -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
Expand All @@ -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::
Expand All @@ -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::
Expand All @@ -956,7 +956,7 @@ def _repr_(self):

def _element_constructor_(self, ogf):
r"""
Construct a C-Finite Sequence
Construct a C-Finite Sequence.
INPUT:
Expand Down Expand Up @@ -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::
Expand Down Expand Up @@ -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.<x> = CFiniteSequences(QQ)
sage: C.gens()
(x,)
"""
return (self.gen(0),)

def an_element(self):
r"""
Return an element of C-Finite Sequences.
Expand All @@ -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.
Expand Down Expand Up @@ -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::
Expand All @@ -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()
Expand All @@ -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);\
Expand All @@ -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"""
Expand Down

0 comments on commit 83a72d1

Please sign in to comment.