Skip to content
This repository has been archived by the owner on Jan 30, 2023. It is now read-only.

Commit

Permalink
Merge branch 'u/tscrim/categories_lazy_series-34470' of trac.sagemath…
Browse files Browse the repository at this point in the history
….org:sage into t/34552/more_testsuites_for_lazy_series_rings
  • Loading branch information
mantepse committed Sep 18, 2022
2 parents 7cbde49 + b930f58 commit 6a46183
Show file tree
Hide file tree
Showing 6 changed files with 247 additions and 69 deletions.
7 changes: 5 additions & 2 deletions src/sage/combinat/species/all.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,5 +44,8 @@
from sage.misc.namespace_package import install_doc
install_doc(__package__, __doc__)

from .recursive_species import CombinatorialSpecies
from . import library as species
from sage.misc.lazy_import import lazy_import
lazy_import("sage.combinat.species.recursive_species", "CombinatorialSpecies")
lazy_import("sage.combinat.species", "library", as_="species")
del lazy_import

17 changes: 10 additions & 7 deletions src/sage/combinat/species/library.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
@cached_function
def SimpleGraphSpecies():
"""
Returns the species of simple graphs.
Return the species of simple graphs.
EXAMPLES::
Expand Down Expand Up @@ -73,11 +73,10 @@ def SimpleGraphSpecies():
@cached_function
def BinaryTreeSpecies():
r"""
Return the species of binary trees on n leaves.
Return the species of binary trees on `n` leaves.
The species of
binary trees B is defined by B = X + B\*B where X is the singleton
species.
The species of binary trees `B` is defined by `B = X + B \cdot B`,
where `X` is the singleton species.
EXAMPLES::
Expand Down Expand Up @@ -111,8 +110,9 @@ def BinaryTreeSpecies():
@cached_function
def BinaryForestSpecies():
"""
Returns the species of binary forests. Binary forests are defined
as sets of binary trees.
Return the species of binary forests.
Binary forests are defined as sets of binary trees.
EXAMPLES::
Expand Down Expand Up @@ -140,3 +140,6 @@ def BinaryForestSpecies():
S = SetSpecies()
F = S(B)
return F

del cached_function # so it doesn't get picked up by tab completion

20 changes: 16 additions & 4 deletions src/sage/data_structures/stream.py
Original file line number Diff line number Diff line change
Expand Up @@ -1653,7 +1653,10 @@ def get_coefficient(self, n):
[0, 1/3, -2/9, -2/9, -2/27, -2/9, 2/27, -2/9]
"""
if self._ainv is None:
self._ainv = ~self._series[1]
try:
self._ainv = ~self._series[1]
except TypeError:
self._ainv = self._series[1].inverse_of_unit()
if n == 1:
return self._ainv
c = self._zero
Expand Down Expand Up @@ -2290,7 +2293,10 @@ def __init__(self, series, approximate_order=None):
"""
if approximate_order is None:
v = series.order()
self._ainv = ~series[v]
try:
self._ainv = ~series[v]
except TypeError:
self._ainv = series[v].inverse_of_unit()
else:
v = approximate_order
self._ainv = None
Expand Down Expand Up @@ -2318,7 +2324,10 @@ def get_coefficient(self, n):
"""
if self._ainv is None:
self._approximate_order = -self._series.order()
self._ainv = ~self._series[self._approximate_order]
try:
self._ainv = ~self._series[self._approximate_order]
except TypeError:
self._ainv = self._series[self._approximate_order].inverse_of_unit()

# if self._ainv is not None, self._approximate_order is the
# true order
Expand Down Expand Up @@ -2348,7 +2357,10 @@ def iterate_coefficients(self):
"""
if self._ainv is None:
self._approximate_order = -self._series.order()
self._ainv = ~self._series[self._approximate_order]
try:
self._ainv = ~self._series[self._approximate_order]
except TypeError:
self._ainv = self._series[self._approximate_order].inverse_of_unit()

# if self._ainv is not None, self._approximate_order is the
# true order
Expand Down
157 changes: 138 additions & 19 deletions src/sage/rings/lazy_series.py
Original file line number Diff line number Diff line change
Expand Up @@ -3054,11 +3054,14 @@ def is_unit(self):
sage: (2*z).is_unit()
False
sage: (1+2*z).is_unit()
sage: (1 + 2*z).is_unit()
True
sage: (1+2*z^-1).is_unit()
sage: (1 + 2*z^-1).is_unit()
False
sage: (z^3 + 4 - z^-2).is_unit()
True
"""
if self.is_zero(): # now 0 != 1
return False
Expand All @@ -3080,12 +3083,13 @@ def _im_gens_(self, codomain, im_gens, base_map=None):
sage: f
-t^-2 - i*t^-1 + 1 + i*t - t^2 - i*t^3 + t^4 + O(t^5)
sage: f._im_gens_(R, [t + t^2])
-t^-2 + (-i + 2)*t^-1 + (i - 2) + 4*t + (2*i - 6)*t^2 + (-2*i + 4)*t^3 + (-2*i - 7)*t^4 + O(t^5)
-t^-2 + (-i + 2)*t^-1 + (i - 2) + 4*t + (2*i - 6)*t^2
+ (-2*i + 4)*t^3 + (-2*i - 7)*t^4 + O(t^5)
sage: cc = K.hom([-i])
sage: f._im_gens_(R, [t + t^2], base_map=cc)
-t^-2 + (i + 2)*t^-1 + (-i - 2) + 4*t + (-2*i - 6)*t^2 + (2*i + 4)*t^3 + (2*i - 7)*t^4 + O(t^5)
-t^-2 + (i + 2)*t^-1 + (-i - 2) + 4*t + (-2*i - 6)*t^2
+ (2*i + 4)*t^3 + (2*i - 7)*t^4 + O(t^5)
"""
if base_map is None:
return codomain(self(im_gens[0]))
Expand Down Expand Up @@ -3944,7 +3948,6 @@ def _format_series(self, formatter, format_strings=False):
return strformat("O({})".format(formatter(z**m)))
return formatter(poly) + strformat(" + O({})".format(formatter(z**m)))


class LazyPowerSeries(LazyCauchyProductSeries):
r"""
A Taylor series where the coefficients are computed lazily.
Expand Down Expand Up @@ -3976,14 +3979,14 @@ def is_unit(self):
sage: (2*z).is_unit()
False
sage: (1+2*z).is_unit()
sage: (1 + 2*z).is_unit()
True
sage: (3+2*z).is_unit()
sage: (3 + 2*z).is_unit()
False
sage: L.<x,y> = LazyPowerSeriesRing(ZZ)
sage: (1+2*x+3*x*y).is_unit()
sage: (-1 + 2*x + 3*x*y).is_unit()
True
"""
if self.is_zero(): # now 0 != 1
Expand Down Expand Up @@ -4713,6 +4716,123 @@ def polynomial(self, degree=None, names=None):
return R(self[0:m])
return R.sum(self[0:m])

class LazyPowerSeries_gcd(LazyPowerSeries):
"""
A lazy power series that also implements the GCD algorithm.
"""
def gcd(self, other):
r"""
Return the greatest common divisor of ``self`` and ``other``.
EXAMPLES::
sage: L.<x> = LazyPowerSeriesRing(QQ)
sage: a = 16*x^5 / (1 - 5*x)
sage: b = (22*x^2 + x^8) / (1 - 4*x^2)
sage: a.gcd(b)
x^2
"""
P = self.parent()
if P._arity != 1:
raise NotImplementedError("only implemented for arity one")
if not self or not other:
return P.zero()
sv = self.valuation()
ov = other.valuation()
val = min(sv, ov)
assert val is not infinity
# This assumes the base ring is a field
return P.gen(0) ** val

def xgcd(self, f):
r"""
Return the extended gcd of ``self`` and ``f``.
OUTPUT:
A triple ``(g, s, t)`` such that ``g`` is the gcd of ``self``
and ``f``, and ``s`` and ``t`` are cofactors satisfying the
Bezout identity
.. MATH::
g = s \cdot \mathrm{self} + t \cdot f.
EXAMPLES::
sage: L.<x> = LazyPowerSeriesRing(QQ)
sage: a = 16*x^5 / (1 - 2*x)
sage: b = (22*x^3 + x^8) / (1 - 3*x^2)
sage: g, s, t = a.xgcd(b)
sage: g
x^3
sage: s
1/22 - 41/242*x^2 - 8/121*x^3 + 120/1331*x^4 + 1205/5324*x^5 + 316/14641*x^6 + O(x^7)
sage: t
1/22 - 41/242*x^2 - 8/121*x^3 + 120/1331*x^4 + 1205/5324*x^5 + 316/14641*x^6 + O(x^7)
sage: LazyPowerSeriesRing.options.halting_precision(20) # verify up to degree 20
sage: g == s * a + t * b
True
sage: a = 16*x^5 / (1 - 2*x)
sage: b = (-16*x^5 + x^8) / (1 - 3*x^2)
sage: g, s, t = a.xgcd(b)
sage: g
x^5
sage: s
1/16 - 1/16*x - 3/16*x^2 + 1/8*x^3 - 17/256*x^4 + 9/128*x^5 + 1/128*x^6 + O(x^7)
sage: t
1/16*x - 1/16*x^2 - 3/16*x^3 + 1/8*x^4 - 17/256*x^5 + 9/128*x^6 + 1/128*x^7 + O(x^8)
sage: g == s * a + t * b
True
sage: L.<x> = LazyPowerSeriesRing(GF(2))
sage: a = L(lambda n: n % 2, valuation=3); a
x^3 + x^5 + x^7 + x^9 + O(x^10)
sage: b = L(lambda n: binomial(n,2) % 2, valuation=3); b
x^3 + x^6 + x^7 + O(x^10)
sage: g, s, t = a.xgcd(b)
sage: g
x^3
sage: s
1 + x + x^3 + x^4 + x^5 + O(x^7)
sage: t
x + x^2 + x^4 + x^5 + x^6 + O(x^8)
sage: g == s * a + t * b
True
sage: LazyPowerSeriesRing.options._reset() # reset the options
"""
P = self.parent()
if P._arity != 1:
raise NotImplementedError("only implemented for arity one")
# one of the elements is zero
if not self:
return (P.zero(), P.zero(), P.one())
if not f:
return (P.zero(), P.one(), P.zero())
# get the valuations
sv = self.valuation()
sc = self[sv]
fv = f.valuation()
fc = f[fv]
val = min(sv, fv)
assert val is not infinity
# This assumes the base ring is a field
x = P.gen(0)
unit = (self + f).shift(-val)
if not unit[0]:
# this only happens if they have the same valuation
# we multiply f by the generator to avoid any cancellations
unit = (self + f.shift(1)).shift(-val)
unit = ~unit
return (x**val,
unit,
unit * x)
unit = ~unit
return (x**val, unit, unit)

class LazyCompletionGradedAlgebraElement(LazyCauchyProductSeries):
"""
Expand Down Expand Up @@ -4814,16 +4934,16 @@ def is_unit(self):
sage: L(2*m[1]).is_unit()
False
sage: L(1+2*m[1]).is_unit()
sage: L(-1 + 2*m[1]).is_unit()
True
sage: L(2+3*m[1]).is_unit()
sage: L(2 + m[1]).is_unit()
False
sage: m = SymmetricFunctions(QQ).m()
sage: L = LazySymmetricFunctions(m)
sage: L(2+3*m[1]).is_unit()
sage: L(2 + 3*m[1]).is_unit()
True
"""
if self.is_zero(): # now 0 != 1
Expand Down Expand Up @@ -5704,17 +5824,17 @@ def is_unit(self):
EXAMPLES::
sage: D = LazyDirichletSeriesRing(ZZ, "s")
sage: D([0,2]).is_unit()
sage: D([0, 2]).is_unit()
False
sage: D([1,2]).is_unit()
sage: D([-1, 2]).is_unit()
True
sage: D([3,2]).is_unit()
sage: D([3, 2]).is_unit()
False
sage: D = LazyDirichletSeriesRing(QQ, "s")
sage: D([3,2]).is_unit()
sage: D([3, 2]).is_unit()
True
"""
if self.is_zero(): # now 0 != 1
Expand Down Expand Up @@ -5841,22 +5961,21 @@ def __invert__(self):
O(1/(8^z))
Trying to invert a non-invertible 'exact' series raises a
``ZeroDivisionError``:
``ZeroDivisionError``::
sage: _ = ~L([0,1], constant=1)
Traceback (most recent call last):
...
ZeroDivisionError: the Dirichlet inverse only exists if the coefficient with index 1 is non-zero
If the series is not 'exact', we cannot do this without
actually computing a term.
actually computing a term::
sage: f = ~L(lambda n: n-1)
sage: f[1]
Traceback (most recent call last):
...
ZeroDivisionError: rational division by zero
"""
P = self.parent()
return P.element_class(P, Stream_dirichlet_invert(self._coeff_stream))
Expand Down
Loading

0 comments on commit 6a46183

Please sign in to comment.