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

Commit

Permalink
Implementing _floordiv_, Stream._true_order to boolean, first fractio…
Browse files Browse the repository at this point in the history
…n field, more tests, marking long tests.
  • Loading branch information
tscrim committed Sep 19, 2022
1 parent b930f58 commit b47407b
Show file tree
Hide file tree
Showing 3 changed files with 376 additions and 78 deletions.
81 changes: 50 additions & 31 deletions src/sage/data_structures/stream.py
Expand Up @@ -112,8 +112,9 @@ class Stream():
- ``sparse`` -- boolean; whether the implementation of the stream is sparse
- ``approximate_order`` -- integer; a lower bound for the order
of the stream
- ``true_order`` -- boolean; if the approximate order is the actual order
"""
def __init__(self, sparse, approximate_order, true_order=None):
def __init__(self, sparse, approximate_order, true_order=False):
"""
Initialize ``self``.
Expand Down Expand Up @@ -376,20 +377,22 @@ def order(self):
sage: f.order()
1
"""
if self._true_order is not None:
return self._true_order
if self._true_order:
return self._approximate_order
if self._is_sparse:
n = self._approximate_order
cache = self._cache
while True:
if n in cache:
if cache[n]:
self._approximate_order = self._true_order = n
self._approximate_order = n
self._true_order = True
return n
n += 1
else:
if self[n]:
self._approximate_order = self._true_order = n
self._approximate_order = n
self._true_order = True
return n
n += 1
else:
Expand All @@ -398,12 +401,14 @@ def order(self):
while True:
if n - self._offset < len(cache):
if cache[n - self._offset]:
self._approximate_order = self._true_order = n
self._approximate_order = n
self._true_order = True
return n
n += 1
else:
if self[n]:
self._approximate_order = self._true_order = n
self._approximate_order = n
self._true_order = True
return n
n += 1

Expand Down Expand Up @@ -542,28 +547,40 @@ def __init__(self, initial_coefficients, is_sparse, constant=None, degree=None,
AssertionError: Stream_exact should only be used for non-zero streams
sage: s = Stream_exact([0, 0, 1, 0, 0], False)
sage: s._initial_coefficients, s._true_order, s._degree
((1,), 2, 3)
sage: s._initial_coefficients, s._approximate_order, s._degree, s._true_order
((1,), 2, 3, True)
sage: s = Stream_exact([0, 0, 1, 0, 0], False, constant=0)
sage: s._initial_coefficients, s._true_order, s._degree
((1,), 2, 3)
sage: s._initial_coefficients, s._approximate_order, s._degree, s._true_order
((1,), 2, 3, True)
sage: s = Stream_exact([0, 0, 1, 0, 0], False, constant=0, degree=10)
sage: s._initial_coefficients, s._true_order, s._degree
((1,), 2, 3)
sage: s._initial_coefficients, s._approximate_order, s._degree, s._true_order
((1,), 2, 3, True)
sage: s = Stream_exact([0, 0, 1, 0, 0], False, constant=1)
sage: s._initial_coefficients, s._true_order, s._degree
((1,), 2, 5)
sage: s._initial_coefficients, s._approximate_order, s._degree, s._true_order
((1,), 2, 5, True)
sage: s = Stream_exact([0, 0, 1, 0, 1], False, constant=1, degree=10)
sage: s._initial_coefficients, s._approximate_order, s._degree, s._true_order
((1, 0, 1), 2, 10, True)
sage: s = Stream_exact([0, 0, 1, 0, 1], False, constant=1, degree=5)
sage: s._initial_coefficients, s._approximate_order, s._degree, s._true_order
((1,), 2, 4, True)
sage: s = Stream_exact([0, 0, 1, 2, 0, 1], False, constant=1)
sage: s._initial_coefficients, s._approximate_order, s._degree, s._true_order
((1, 2), 2, 5, True)
sage: s = Stream_exact([0, 0, 1, 2, 1, 1], False, constant=1)
sage: s._initial_coefficients, s._true_order, s._degree
((1, 2), 2, 4)
sage: s._initial_coefficients, s._approximate_order, s._degree, s._true_order
((1, 2), 2, 4, True)
sage: s = Stream_exact([0, 0, 1, 2, 1, 1], False, constant=1, order=-2)
sage: s._initial_coefficients, s._true_order, s._degree
((1, 2), 0, 2)
sage: s._initial_coefficients, s._approximate_order, s._degree, s._true_order
((1, 2), 0, 2, True)
"""
if constant is None:
self._constant = ZZ.zero()
Expand All @@ -588,18 +605,20 @@ def __init__(self, initial_coefficients, is_sparse, constant=None, degree=None,
# different from constant, because __eq__ below would become
# complicated otherwise

# TODO: simplify
for i, v in enumerate(initial_coefficients):
if v:
# We have found the first nonzero coefficient
order += i
initial_coefficients = initial_coefficients[i:]
if order + len(initial_coefficients) == self._degree:
for j, w in enumerate(reversed(initial_coefficients)):
# Strip off the constant values at the end
for w in reversed(initial_coefficients):
if w != self._constant:
break
initial_coefficients.pop()
self._degree -= 1
for j, w in enumerate(reversed(initial_coefficients)):
# Strip off all remaining zeros at the end
for w in reversed(initial_coefficients):
if w:
break
initial_coefficients.pop()
Expand All @@ -611,7 +630,7 @@ def __init__(self, initial_coefficients, is_sparse, constant=None, degree=None,

assert self._initial_coefficients or self._constant, "Stream_exact should only be used for non-zero streams"

super().__init__(is_sparse, order, true_order=order)
super().__init__(is_sparse, order, true_order=True)

def __getitem__(self, n):
"""
Expand Down Expand Up @@ -676,7 +695,7 @@ def order(self):
0
"""
return self._true_order
return self._approximate_order

def __hash__(self):
"""
Expand Down Expand Up @@ -780,7 +799,7 @@ def __ne__(self, other):
if self[i] != other._cache[i]:
return True
else:
if other._offset > self._true_order:
if other._offset > self._approximate_order:
return False
return any(self[i] != c for i, c in enumerate(other._cache, other._offset))

Expand Down Expand Up @@ -1267,7 +1286,7 @@ def order(self):
sage: s.order()
+Infinity
"""
return self._true_order
return self._approximate_order # == infinity

def __eq__(self, other):
"""
Expand Down Expand Up @@ -1702,7 +1721,7 @@ def __init__(self, f, g):
sage: g = Stream_function(lambda n: n^2, True, 1)
sage: h = Stream_cauchy_compose(f, g)
"""
if g._true_order is not None and g._true_order <= 0:
if g._true_order and g._approximate_order <= 0:
raise ValueError("can only compose with a series of positive valuation")
if f._approximate_order < 0:
ginv = Stream_cauchy_invert(g)
Expand Down Expand Up @@ -1853,7 +1872,7 @@ def __init__(self, f, g, p, ring=None, include=None, exclude=None):
self._degree_f = f._degree
else:
self._degree_f = None
if g._true_order is not None and g._true_order == 0 and self._degree_f is None:
if g._true_order and g._approximate_order == 0 and self._degree_f is None:
raise ValueError("can only compute plethysm with a series of valuation 0 for symmetric functions of finite support")

val = f._approximate_order * g._approximate_order
Expand Down Expand Up @@ -2000,9 +2019,9 @@ def stretched_power_restrict_degree(self, i, m, d):
sage: g = Stream_function(lambda n: sum(tensor([p[k], p[n-k]]) for k in range(n+1)), True, 1)
sage: h = Stream_plethysm(f, g, p2)
sage: A = h.stretched_power_restrict_degree(2, 3, 6)
sage: B = p[2,2,2](sum(g[n] for n in range(7)))
sage: B = p2.element_class(p2, {m: c for m, c in B if sum(mu.size() for mu in m) == 12})
sage: A == B
sage: B = p[2,2,2](sum(g[n] for n in range(7))) # long time
sage: B = p2.element_class(p2, {m: c for m, c in B if sum(mu.size() for mu in m) == 12}) # long time
sage: A == B # long time
True
"""
while len(self._powers) < m:
Expand Down
102 changes: 92 additions & 10 deletions src/sage/rings/lazy_series.py
Expand Up @@ -218,6 +218,7 @@
from sage.combinat.partition import Partition, Partitions
from sage.misc.misc_c import prod
from sage.misc.derivative import derivative_parse
from sage.categories.integral_domains import IntegralDomains
from sage.rings.infinity import infinity
from sage.rings.integer_ring import ZZ
from sage.rings.polynomial.laurent_polynomial_ring import LaurentPolynomialRing
Expand Down Expand Up @@ -2882,17 +2883,14 @@ def _div_(self, other):
sage: t / L(1)
t
sage: t^3*(1+2*t+3*t^2+4*t^3)/(t-t^2)
sage: t^3 * (1+2*t+3*t^2+4*t^3) / (t-t^2)
t^2 + 3*t^3 + 6*t^4 + 10*t^5 + 10*t^6 + 10*t^7 + O(t^8)
sage: t^3*((1+2*t+3*t^2+4*t^3)/(t-t^2))
Traceback (most recent call last):
...
ZeroDivisionError: cannot divide by a series of larger valuation
sage: t^3 * ((1+2*t+3*t^2+4*t^3) / (t-t^2))
t^2 + 3*t^3 + 6*t^4 + 10*t^5 + 10*t^6 + 10*t^7 + O(t^8)
sage: L(lambda n: n) / (t + t^2)
1 + t + 2*t^2 + 2*t^3 + 3*t^4 + 3*t^5 + O(t^6)
"""
if isinstance(other._coeff_stream, Stream_zero):
raise ZeroDivisionError("cannot divide by 0")
Expand All @@ -2903,9 +2901,12 @@ def _div_(self, other):
return P.zero()
right = other._coeff_stream
if (P._minimal_valuation is not None
and left._true_order is not None
and left._true_order < right._approximate_order):
raise ZeroDivisionError("cannot divide by a series of larger valuation")
and left._true_order
and left._approximate_order < right._approximate_order):
F = P.fraction_field()
num = F.element_class(F, left)
den = F.element_class(F, right)
return num / den

R = P._internal_poly_ring
if (isinstance(left, Stream_exact)
Expand Down Expand Up @@ -2973,6 +2974,37 @@ def _div_(self, other):
return P.element_class(P, Stream_cauchy_mul(left, right_inverse))


def _floordiv_(self, other):
r"""
Return ``self`` floor divided by ``other``.
INPUT:
- ``other`` -- nonzero series
EXAMPLES::
sage: L.<x> = LazyLaurentSeriesRing(QQ)
sage: g = (x + 2*x^2) / (1 - x - x^2)
sage: x // g
1 - 3*x + 5*x^2 - 10*x^3 + 20*x^4 - 40*x^5 + 80*x^6 + O(x^7)
sage: 1 // g
x^-1 - 3 + 5*x - 10*x^2 + 20*x^3 - 40*x^4 + 80*x^5 + O(x^6)
sage: x^-3 // g
x^-4 - 3*x^-3 + 5*x^-2 - 10*x^-1 + 20 - 40*x + 80*x^2 + O(x^3)
sage: f = (x + x^2) / (1 - x)
sage: f // g
1 - x + x^2 - 4*x^3 + 6*x^4 - 14*x^5 + 26*x^6 + O(x^7)
sage: g // f
1 + x + 3*x^3 + x^4 + 6*x^5 + 5*x^6 + O(x^7)
"""
if isinstance(other._coeff_stream, Stream_zero):
raise ZeroDivisionError("cannot divide by 0")
P = self.parent()
if P not in IntegralDomains():
raise TypeError("must be an integral domain")
return P(self / other)

class LazyLaurentSeries(LazyCauchyProductSeries):
r"""
A Laurent series where the coefficients are computed lazily.
Expand Down Expand Up @@ -4689,7 +4721,6 @@ def polynomial(self, degree=None, names=None):
sage: f = z-z^2
sage: f.polynomial()
-z^2 + z
"""
from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
S = self.parent()
Expand All @@ -4712,6 +4743,57 @@ def polynomial(self, degree=None, names=None):
return R(self[0:m])
return R.sum(self[0:m])

def _floordiv_(self, other):
r"""
Return ``self`` floor divided by ``other``.
INPUT:
- ``other`` -- nonzero series
EXAMPLES::
sage: L.<x,y> = LazyPowerSeriesRing(ZZ)
sage: g = x^2 + y*x
sage: x // g
0
sage: g = (x^2 + y*x) / (1 - x + x*y)
sage: x // g
0
sage: f = (x + y) / (1 - x - y + x*y)
sage: f // g
0
sage: L.<x> = LazyPowerSeriesRing(QQ)
sage: g = (x + 2*x^2) / (1 - x - x^2)
sage: 3 // g
0
sage: x // g
1 - 3*x + 5*x^2 - 10*x^3 + 20*x^4 - 40*x^5 + 80*x^6 + O(x^7)
sage: x^2 // g
x - 3*x^2 + 5*x^3 - 10*x^4 + 20*x^5 - 40*x^6 + 80*x^7 + O(x^8)
sage: f = (x + x^2) / (1 - x)
sage: f // g
1 - x + x^2 - 4*x^3 + 6*x^4 - 14*x^5 + 26*x^6 + O(x^7)
"""
if isinstance(other._coeff_stream, Stream_zero):
raise ZeroDivisionError("cannot divide by 0")
P = self.parent()
if P not in IntegralDomains():
raise TypeError("must be an integral domain")
left = self._coeff_stream
right_order = other._coeff_stream._approximate_order
if left._approximate_order < right_order:
if left._true_order:
return P.zero()
while left._approximate_order < right_order:
# TODO: Implement a bound on computing the order of a Stream
if left[left._approximate_order]:
left._true_order = True
return P.zero()
left._approximate_order += 1
return super()._floordiv_(other)

class LazyPowerSeries_gcd(LazyPowerSeries):
"""
A lazy power series that also implements the GCD algorithm.
Expand Down

0 comments on commit b47407b

Please sign in to comment.