Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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