Skip to content

Conversation

videlec
Copy link
Contributor

@videlec videlec commented Feb 22, 2023

📚 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

  • I have made sure that the title is self-explanatory and the description concisely explains the PR.
  • I have linked an issue or discussion.
  • I have created tests covering the changes.
  • I have updated the documentation accordingly.

@roed314
Copy link
Contributor

roed314 commented Feb 22, 2023

@videlec I'm currently in Chamonix with bad internet; I'll try to take a look at this over the weekend.

@codecov-commenter
Copy link

codecov-commenter commented Feb 23, 2023

Codecov Report

Patch coverage: 97.13% and project coverage change: +0.03 🎉

Comparison is base (e28bd1a) 88.58% compared to head (db9aed3) 88.61%.

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     
Impacted Files Coverage Δ
src/sage/schemes/elliptic_curves/ell_generic.py 93.25% <66.66%> (+0.01%) ⬆️
src/sage/interfaces/tachyon.py 87.93% <90.00%> (+0.43%) ⬆️
src/sage/schemes/elliptic_curves/gal_reps.py 82.23% <90.00%> (+0.04%) ⬆️
src/sage/quadratic_forms/quadratic_form.py 90.30% <90.90%> (+0.20%) ⬆️
.../sage/rings/polynomial/multi_polynomial_element.py 90.71% <94.00%> (-0.39%) ⬇️
src/sage/modular/quasimodform/element.py 99.20% <100.00%> (+0.06%) ⬆️
src/sage/rings/polynomial/multi_polynomial_ring.py 86.25% <100.00%> (+0.35%) ⬆️
src/sage/rings/qqbar.py 95.30% <100.00%> (+<0.01%) ⬆️
src/sage/schemes/affine/affine_morphism.py 90.33% <100.00%> (ø)
src/sage/schemes/elliptic_curves/BSD.py 43.75% <100.00%> (+0.21%) ⬆️
... and 42 more

... 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.
📢 Do you have feedback about the report comment? Let us know in this issue.

@videlec videlec force-pushed the 34000 branch 2 times, most recently from 70393bd to bcec6b3 Compare February 26, 2023 10:14
D[e] -= c
else:
D[e] = -c
return self._new(D)
Copy link
Collaborator

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.

Copy link
Contributor Author

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).

Copy link
Collaborator

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..

Copy link
Collaborator

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.

Copy link
Contributor Author

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.

Copy link
Collaborator

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:

  1. ETuple: represents a single monomial. This is good, unique, and clearly necessary.
  2. PolyDict: represents a linear combination of terms by a dict mapping monomials to their coefficients. Implemented in Cython.
  3. MPolynomial_element: partially wraps a PolyDict 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?

Copy link
Contributor Author

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 (eg int or float) 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)

Copy link
Contributor Author

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.

Copy link
Collaborator

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.

Copy link
Contributor Author

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.

@videlec
Copy link
Contributor Author

videlec commented Mar 19, 2023

The situation with trailing zeros evaluating to False is extremely annoying. For example equality of polynomials is not equality of coefficients

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, bool(p) is probably wrong. Of course the same question holds for matrices, etc.

@tscrim
Copy link
Collaborator

tscrim commented Mar 19, 2023

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.

@videlec videlec mentioned this pull request Mar 20, 2023
@github-actions
Copy link

Documentation preview for this PR is ready! 🎉
Built with commit: 1a7eeac

@videlec
Copy link
Contributor Author

videlec commented Mar 20, 2023

Actually changing bool(x) to mean x is not an exact zero would solve all issues here! But Julian does not seems convinced about it yet at #35319.

Copy link
Collaborator

@tscrim tscrim left a 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

cleaning and enhancement to PolyDict
8 participants