sagemath / sage

Main repository of SageMath
https://www.sagemath.org
Other
1.34k stars 452 forks source link

Bugs and missing features for lazy power series #34969

Open mantepse opened 1 year ago

mantepse commented 1 year ago
sage: S.<z> = LazyLaurentSeriesRing(QQ)
sage: matrix([[z^(i+j) for i in range(4)] for j in range(4)]).det()
Traceback:
...
AttributeError: 'LazyLaurentSeriesRing_with_category.element_class' object has no attribute 'precision_relative'
sage: S.<z> = LazyPowerSeriesRing(QQ)
sage: matrix([[z^(i+j) for i in range(4)] for j in range(4)]).det()
Traceback:
...
TypeError: Cannot convert LazyPowerSeriesRing_with_category.element_class to sage.structure.element.RingElement
sage: S.<z> = LazyLaurentSeriesRing(QQ)
sage: S(lambda n: QQ(1), valuation=0).approximate_series(10)
1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + z^7 + z^8 + z^9 + O(z^10)

but

sage: S.<z> = LazyPowerSeriesRing(QQ)
sage: S(lambda n: QQ(1), valuation=0).approximate_series(10)
Traceback:
...
AttributeError: 'LazyPowerSeriesRing_with_category.element_class' object has no attribute 'approximate_series'
sage: S.<z> = LazyLaurentSeriesRing(QQ)
sage: S(lambda n: QQ(1/(n+1)), valuation=0).approximate_series(10).stieltjes_continued_fraction()
(1/2, 1/6, 1/3, 1/5, 3/10, 3/14, 2/7, 2/9, 5/18)
sage: S(lambda n: QQ(1/(n+1)), valuation=0).approximate_series(10).jacobi_continued_fraction()
((-1/2, -1/12), (-1/2, -1/15), (-1/2, -9/140), (-1/2, -4/63))
DavidAyotte commented 1 year ago

Hello Martin, Before opening an issue about this, I wanted to check if this is an expected behavior:

sage: L = LazyPowerSeriesRing(QQ, 'z')
sage: f = lambda n: 1 if ZZ(n).is_power_of(2) else 0
sage: lazy_f = L(f); lazy_f
z + z^2 + z^4 + O(z^7)
sage: lazy_f.compositional_inverse()
<repr(<sage.rings.lazy_series_ring.LazyPowerSeriesRing_with_category.element_class at 0x7f3209489720>) failed: ValueError: inverse does not exist>
sage: lazy_f[:2**4]
[1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0]
sage: lazy_f.compositional_inverse()
z - z^2 + 2*z^3 - 6*z^4 + 20*z^5 - 70*z^6 + 256*z^7 + O(z^8)

It seems that, in this particular case, the compositional inverse can be computed only after the code knows about some coefficients of f.

mantepse commented 1 year ago

This is extremely strange, and almost certainly a bug.

DavidAyotte commented 1 year ago

Hello Martin,

I noticed something with the representation of a lazy power series and I wanted to check with you if that's a a normal behavior. Essentially, if you create a Lazy series over a polynomial ring, then their elements seems to be displayed with an extra set of parenthesis (compared to the nonlazy power series):

sage: R.<T> = QQ[]
sage: L.<z> = LazyPowerSeriesRing(R)
sage: T*z
T*z
sage: (T+1)*z  # _repr_ gives an extra pair of parenthesis:
((T+1)*z)
sage: P.<w> = PowerSeriesRing(R)
sage: T*w
T*w
sage: (T+1)*w  # no extra parenthesis:
(T + 1)*w

I wondered if this is wanted or not, because I find that sometimes it clutters the representation a bit.

By the way, I've been experimenting with lazy series lately and it is very useful! Thank you very much for maintaining this.

mantepse commented 1 year ago

Thank you for the thank you :-)

@tscrim, do you remember why the parentheses are done the way they are done?

tscrim commented 1 year ago

It is a side effect of the somewhat lazily done implementation of printing (IIRC, homogeneous components always have parentheses). Something smarter could definitely be done, but it would take a little bit of work and string manipulations that I didn’t want to do at the time.

tscrim commented 1 year ago

I am wondering if we fix the first bug (some missing attribute that I am not sure it should have) if the second bug will take its place. I would have to dig into the matrix code. Somewhat annoyingly, the matrix code is quite strict when it comes to the subclasses of Element, but the good news is the fix is fairly easy: just inherit from RingElement instead of Element.

tscrim commented 1 year ago

Ah, I know why the behavior in the first bug is different. The Laurent series is a field, which results in a different code path. Although it seems to be trying to treat it as an inexact field, which is bad. Although IMO it should do a division-free algorithm by default, but we probably would need to have an additional special case for lazy series.

DavidAyotte commented 1 year ago

Here's something I found:

sage: L.<z> = LazyPowerSeriesRing(QQ)
sage: f = z^2 + z^3 + z^4
sage: f
z^2 + z^3 + z^4
sage: list(f)
# infinite loop
tscrim commented 1 year ago

Ouch, that’s a pretty bad one. Although I believe it is simply doing the iterator, which is what is leading to the infinite loop. I think the most consistent solution would be to simply raise an error though __len__ (or however other infinite enumerated sets behave). That way an exact versus and inexact element behaves in the same way.

mantepse commented 1 year ago

I don't think that this is a bug, actually. The slice should return all coefficients, not just the non-zero ones, so what is list supposed to do?

tscrim commented 1 year ago

@mantepse I say it should raise an error like list(ZZ) does. Infinite slices are one thing, but we can at least do a basic guard around list(f).

mantepse commented 1 year ago

Ah! So if the proposal is to always raise an error, I agree! (though -> through, right?)

tscrim commented 1 year ago

Yes, that’s right (including about my typo).

mantepse commented 1 year ago

More bugs and missing features:

mantepse commented 1 year ago