-
-
Notifications
You must be signed in to change notification settings - Fork 657
cleaning and enhancement to PolyDict #35174
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
@videlec I'm currently in Chamonix with bad internet; I'll try to take a look at this over the weekend. |
Codecov ReportPatch coverage:
Additional details and impacted files@@ Coverage Diff @@
## develop #35174 +/- ##
===========================================
+ Coverage 88.58% 88.61% +0.03%
===========================================
Files 2141 2148 +7
Lines 397475 398873 +1398
===========================================
+ Hits 352106 353472 +1366
- Misses 45369 45401 +32
... and 149 files with indirect coverage changes Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here. ☔ View full report in Codecov by Sentry. |
70393bd
to
bcec6b3
Compare
D[e] -= c | ||
else: | ||
D[e] = -c | ||
return self._new(D) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You need to also need to remove the zeros from here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How do you test zero
? This is the issue @roed314 complained about: for some base rings (such as power series or p-adics) we do not want to simplify trailing zeros such as O(t^5)
. Removing zeros is now handled by polynomials in multi_polynomial_element.py
rather than PolyDict
. Though I am looking for a better way to consistently handle all base rings (which at the moment is not taken care of at all).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that this is a change in the behavior compared to before. Extra zeros in the polynomial pollute it and make the code slower. Simply do what you do in other places and call the method that you replaced the remove_zeros
argument.
In fact, I really don't see the utility of pulling that out as a separate method. It leads to easy-to-overlook issues like this and requires more direct manual intervention (with now a seemingly separate method) from the coder. I think you need to have some justification for separating this..
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In summary, the here is not how to test for zeros (which is a separate issue), but they fact you are not calling remove_zeros()
whereas before it was doing the equivalent with remove_zero=True
for the constructor.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do you call polynomial in this context: a Polydict
or a MPolynomial_element
?
A PolyDict
never removes zero coefficients simply because it does not know how to test zero. This test depends on the base ring and PolyDict
does not store the base ring. However, MPolynomial_element
does know about the base ring and can implement a remove_zero
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That would be a bit of an improvement, although I am not sure the extra work and maintenance is worth it from your current proposal.
Could we step back from this local picture and look at what each class is supposed to do? My initial comment might have been looking too locally at the change. I am hoping this will clarify things, possibly leading to your current proposal.
Right now, we have three classes:
ETuple
: represents a single monomial. This is good, unique, and clearly necessary.PolyDict
: represents a linear combination of terms by adict
mapping monomials to their coefficients. Implemented in Cython.MPolynomial_element
: partially wraps aPolyDict
but also carries more information about the base ring. Implemented in Python.
Now it is clear what stuff should be handled by the ETuple
, but it is not clear what separates 2 and 3. Why shouldn't a PolyDict
know about the coefficient ring? Do we have some reason why it is kept as a separate backend class? Part (most?) of this, I imagine, is to be able to split it off as its own separate entity and not have it fully integrated into Sage. So why doesn't MPolynomial_element
inherit from PolyDict
? If it was a Cython class, then we run into multiple inheritance issues. If MPolynomial_element
inherited from PolyDict
, then it could implement, e.g.,
def _add_(self, other):
ret = PolyDict.__add__(self, other)
ret.remove_zeros()
return ret
which should be comparative in terms of speed (not tested). Although I guess from this separation of the backend with eye towards Cythonization, it would make sense to keep the current version, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree with your analysis of the situation. Some additional points
- making
PolyDict
agnostic to coefficients nature makes it possible to have plain Python type for coefficients (egint
orfloat
) or mixed types (eg exact and floating point values) - one extra delicate point with inheritance, is that
PolyDict.__add__
would have to be careful about its return type (which do have a cost)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I cleaned the current version to make it clear that we want to use remove_zeros
more carefully. If you are happy with the current merge request I will open an issue on what has to be done next to handle properly polynomials over rings with trailing zeros.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I am modulo the last few things I commented below about with your more recent changes. It is nice to see a little more clarification in the docs about this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I opened a general discussion about inexact zeros at #35319. I will mention the latter in the documentations of PolyDict
and MPolynomial_element
.
- make all PolyDict methods but two cpdef and doctested - implement binary search in ETuple
The situation with trailing zeros evaluating to sage: from sage.rings.polynomial.polydict import PolyDict
sage: R.<t> = PowerSeriesRing(QQ)
sage: PolyDict({(1, 0): O(t)}) == PolyDict({})
False
sage: PolyDict({(1, 0): O(t)}) == PolyDict({(1, 0): O(t^2)})
True Similarly, |
I will take a look at this tonight. Indeed, the removal of zeros in general (not just trailing) definitely is useful at keeping this consistent and fast an should be done as much as possible. |
Documentation preview for this PR is ready! 🎉 |
Actually changing |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you. LGTM. We can always do more changes later should we find other improvements.
📚 Description
Fixes #34000 (and more complete version of the proposition at #35020).
A discussion about the general problem of inexact zeros has been opened at #35319
📝 Checklist