Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Limit full-invalidation of CompositeGenericTransforms. #25291

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

Merged
merged 1 commit into from
Mar 26, 2023

Conversation

anntzer
Copy link
Contributor

@anntzer anntzer commented Feb 22, 2023

The extension of AFFINE_ONLY invalidations into FULL invalidations in CompositeGenericTransform._invalidate_internal was too generous; it is only needed in case (b) , not in case (a). This is actually the argument made by Phil Elson (in
https://discourse.matplotlib.org/t/16538/2, starting at "If the invalid part is a1...") when he first introduced the _invalidate_internal API, but for some reason his implementation further added case (a). Remove that case.

PR Summary

PR Checklist

Documentation and Tests

  • Has pytest style unit tests (and pytest passes)
  • Documentation is sphinx and numpydoc compliant (the docs should build without error).
  • New plotting related features are documented with examples.

Release Notes

  • New features are marked with a .. versionadded:: directive in the docstring and documented in doc/users/next_whats_new/
  • API changes are marked with a .. versionchanged:: directive in the docstring and documented in doc/api/next_api_changes/
  • Release notes conform with instructions in next_whats_new/README.rst or next_api_changes/README.rst

The extension of AFFINE_ONLY invalidations into FULL invalidations in
CompositeGenericTransform._invalidate_internal was too generous; it is
only needed in case (b) , not in case (a).  This is actually the
argument made by Phil Elson (in
https://discourse.matplotlib.org/t/16538/2, starting at "If the invalid
part is a1...") when he first introduced the _invalidate_internal API,
but for some reason his implementation further added case (a).  Remove
that case.
@QuLogic
Copy link
Member

QuLogic commented Feb 25, 2023

I suppose we could confirm with @pelson, though I don't know if he'd remember a post from 10 years ago.

Copy link
Member

@pelson pelson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Logically, I agree. I don't remember the details though, naturally.

Seems strange that I didn't try to avoid unnecessarily invalidating the left-hand non-affine component. I suspect it is because in cartopy at least, there is only one non-affine path transformation in the stack, and it doesn't have an affine part to it, so never really came up.

(not self._a.is_affine or invalidating_node is self._a)):
# When the left child is invalidated at AFFINE_ONLY level and the right child is
# non-affine, the composite transform is FULLY invalidated.
if invalidating_node is self._a and not self._b.is_affine:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can just be invalidating_node is self._a then, no? If _b is non affine, it is a full invalidation needed. If _b is affine, then an affine_only invalidation is the same as full anyway.

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 don't think so? If _a is non affine but invalidated at affine level, and _b is affine, then we need to invalidate the composite only at affine level and that's not equivalent to a full invalidation.
(Can you confirm?)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I agree. Your example demonstrates an optimisation that I would have lost.

The other optimisation that we lose with the current approach is that we over-invalidate the first composite in that case:

(c1, a1, c2, a2)

Suppose we were effectively invalidating a1 (lhs @ AFFINE_ONLY), then we ideally really only want to invalidate (a1, c2, a2) - c1 is fine, but our current implementation is upgraded to INVALID_FULL. I wonder if we would be better changing the code to:

if invalidating_node is self._a:
    super()._invalidate_internal(Transform._INVALID_FULL, self._b)
super()._invalidate_internal(level, invalidating_node)

I don't know the implications, and whether this is something that would be worse than the current approach, but perhaps interesting to explore?

It would be great if we could test all of this - unfortunately though I didn't introduce unit-tests for these changes when I first did them.

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 don't think the code is currently really architected to support this kind of partial invalidations? (With the patch you propose, I think(?) the super()._invalidate_internal(FULL, self._b) will recurse to the current transform anyways as it's in self._b._parents.)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, agreed. Your implementation looks good IMO.

@greglucas greglucas merged commit b7324b2 into matplotlib:main Mar 26, 2023
@anntzer anntzer deleted the cgtii branch March 26, 2023 17:30
@QuLogic QuLogic added this to the v3.8.0 milestone Apr 4, 2023
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.

5 participants