Skip to content
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

Support matplotlib 3.8 #364

Closed
bmcfee opened this issue Sep 21, 2023 · 7 comments · Fixed by #380
Closed

Support matplotlib 3.8 #364

bmcfee opened this issue Sep 21, 2023 · 7 comments · Fixed by #380
Labels
Milestone

Comments

@bmcfee
Copy link
Collaborator

bmcfee commented Sep 21, 2023

Matplotlib 3.8 removes some (private) functionality that we've been relying on in the display module:

https://github.com/craffel/mir_eval/blob/9be1c5bf9b02b842b22f5b248189fbf88a7e30ac/mir_eval/display.py#L150

https://github.com/craffel/mir_eval/blob/9be1c5bf9b02b842b22f5b248189fbf88a7e30ac/mir_eval/display.py#L273

https://github.com/craffel/mir_eval/blob/9be1c5bf9b02b842b22f5b248189fbf88a7e30ac/mir_eval/display.py#L464

https://github.com/craffel/mir_eval/blob/9be1c5bf9b02b842b22f5b248189fbf88a7e30ac/mir_eval/display.py#L555

https://github.com/craffel/mir_eval/blob/9be1c5bf9b02b842b22f5b248189fbf88a7e30ac/mir_eval/display.py#L623

https://github.com/craffel/mir_eval/blob/9be1c5bf9b02b842b22f5b248189fbf88a7e30ac/mir_eval/display.py#L782-L786

We probably should never have been doing this — my bad! 😅 — but now we have to remove / rewrite this functionality to maintain compatibility.

This is going to take a bit of work, and I probably can't commit to doing this in the short term. But I'd be happy to help out if anyone wants to take this on.

@bmcfee bmcfee added the bug label Sep 21, 2023
@craffel
Copy link
Collaborator

craffel commented Sep 21, 2023

Thanks for noting this. In the short-term, should we at least enforce a < 3.8 requirement for matplotlib in setup.py?

@bmcfee
Copy link
Collaborator Author

bmcfee commented Sep 21, 2023

Thanks for noting this. In the short-term, should we at least enforce a < 3.8 requirement for matplotlib in setup.py?

Yeah, I think that would make sense - both for the display and testing options. I'm not sure if it's worth cutting a post-release for, or pushing ahead with a proper next version?

@craffel
Copy link
Collaborator

craffel commented Sep 21, 2023

I know this is the wrong answer but I think few enough people are using both mir_eval.display and matplotlib >= 3.8.0 that we can probably get away with just leaving it at HEAD until the next proper release.

@bmcfee
Copy link
Collaborator Author

bmcfee commented Sep 21, 2023

That's almost certainly true. Quick github search has 51 code hits: https://github.com/search?q=mir_eval.display&type=code and most of them are either our own (for appropriately defined "ours") or one-off code from a few years back.

That said, it would be good to set a milestone for the next release so we have a sense of how long it will take.

@craffel
Copy link
Collaborator

craffel commented Sep 21, 2023

I don't have a sense of what we'd include in a next milestone... maybe once #355 is in?

@bmcfee bmcfee added this to the 0.8 milestone Mar 13, 2024
@bmcfee bmcfee mentioned this issue Mar 21, 2024
4 tasks
@bmcfee
Copy link
Collaborator Author

bmcfee commented Mar 25, 2024

Jotting down some thoughts here...

The basic problem is that we want something that works essentially like ax.plot - in that subsequent calls automatically increment the style cycler, so that we can draw multiple annotations overtop each other and have it work the way one would expect. To make this happen, we previously hooked into the axes object's line style cycler
https://github.com/craffel/mir_eval/blob/7997fdf3972f992209eaf144f37c18926fa3c960/mir_eval/display.py#L553
or patch cycler
https://github.com/craffel/mir_eval/blob/7997fdf3972f992209eaf144f37c18926fa3c960/mir_eval/display.py#L148

This is no longer allowed in mpl 3.8.

We bumped into this issue in librosa's waveshow function a little while ago, and circumvented it by calling ax.plot first, then hijacking the style from the resulting object to propagate to other linked objects (fill_between for envelope display). This approach won't work in mir_eval because the underlying artists are not usually line plots.

After a bit of hacking and prodding ye olde chatbot, I think the most reliable solution here might be for us to add a custom cycler object to the axes object in question, and only interface with that. Something like:

ax = plt.gca()

if not hasattr(ax, "mir_eval_cycler"):
    ax.mir_eval_cycler = ... # instantiate a custom cycler object from matplotlib.rcParams

style = next(ax.mir_eval_cycler)  # or something

This, I think, would work forward and backward across matplotlib versions, and should keep us entirely independent of other artists on the axes. That last point is a double-edged sword, as ax.plot and mir_eval.display.pitch_contour (or whatever) could potentially be independently cycling through the same styles and end up indistinguishable.

We may therefore want to provide some API for a user-provided cycler object to use or initialize the axes custom cycler with.

There may be downsides to this approach that I'm not yet seeing. I'll prototype it some time and then see if some mpl devs can comment on how bad of an idea it is.

@bmcfee
Copy link
Collaborator Author

bmcfee commented Apr 1, 2024

Gave this a bit more of a think, and decided we should avoid stashing custom stylers in the axes object.

Instead, I think we can do a slightly less direct version of what we had already been doing, where rather than poll the appropriate style cycler directly (verboten), we create a temporary line or patch object that does this for us, yank its style for the new artists we're creating, and then purge the object from the axes.

This also is a little clumsy, but it's at least adhering to the API in a way that shouldn't break any time soon.

Aside: since we're bumping the minimum matplotlib version, we can significantly simplify the pitch contour (and probably multipitch) viz by using nans for unvoiced regions. This lets us use a single plot object rather than plotting each segment separately.

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

Successfully merging a pull request may close this issue.

2 participants