Skip to content

Commit

Permalink
Fix compatibility with Matplotlib 3.5 (Qiskit#7301)
Browse files Browse the repository at this point in the history
* Fix compatibility with Matplotlib 3.5

The problem was `qiskit.visualization.bloch.Arrow3D`, which subclassed
`matplotlib.patches.FancyArrowPatch` (a 2D patch), but not the 3D
machinery.  It is now made an unholy multiple-inherited abomination of
both the 2D patch and `mpl_toolkits.mplot3d.art3d.Patch3D`; the latter
is a relatively thin wrapper (in terms of attributes) around the 2D
patch, so this is not too terrible.

Matplotlib 3.5 calls the `Patch3D.do_3d_projection` method using a
deprecated parameter, triggering two warnings, unless the artist's
module appears to have come from `mpl_toolkits.mplot3d.art3d`, even if
the new calling convention is respected.  The warning is only triggered
when the figure is drawn, which may well be outside of our control, so
we cannot suppress the warnings.  Instead, we just lie about the module
the arrow patch was defined in, to trick it into not warning, because we
use the new calling convention.  This is supported at least as far back
as Matplotlib 3.3, which is the current minimum supported version.  The
nasty hack should be removable once Matplotlib 3.6 is the minimum
version because the deprecation period will expire.

* Remove useless word

Co-authored-by: Matthew Treinish <[email protected]>

Co-authored-by: Matthew Treinish <[email protected]>
Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
  • Loading branch information
3 people authored Nov 23, 2021
1 parent f6b6395 commit ba8e3f3
Show file tree
Hide file tree
Showing 5 changed files with 29 additions and 8 deletions.
2 changes: 1 addition & 1 deletion azure-pipelines.yml
Original file line number Diff line number Diff line change
Expand Up @@ -778,7 +778,7 @@ stages:
python -m pip install --upgrade pip
pip install -U -r requirements.txt -r requirements-dev.txt -c constraints.txt
pip install -c constraints.txt -e .
pip install "qiskit-ibmq-provider" "qiskit-aer" "z3-solver" "qiskit-ignis" "matplotlib>=3.3.0,<3.5" sphinx nbsphinx sphinx_rtd_theme cvxpy -c constraints.txt
pip install "qiskit-ibmq-provider" "qiskit-aer" "z3-solver" "qiskit-ignis" "matplotlib>=3.3.0" sphinx nbsphinx sphinx_rtd_theme cvxpy -c constraints.txt
python setup.py build_ext --inplace
sudo apt install -y graphviz pandoc
pip check
Expand Down
27 changes: 22 additions & 5 deletions qiskit/visualization/bloch.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,19 +54,36 @@
import matplotlib.pyplot as plt
from matplotlib.patches import FancyArrowPatch
from mpl_toolkits.mplot3d import Axes3D, proj3d
from mpl_toolkits.mplot3d.art3d import Patch3D

from .utils import matplotlib_close_if_inline


class Arrow3D(FancyArrowPatch):
class Arrow3D(Patch3D, FancyArrowPatch):
"""Makes a fancy arrow"""

def __init__(self, xs, ys, zs, *args, **kwargs):
FancyArrowPatch.__init__(self, (0, 0), (0, 0), *args, **kwargs)
self._verts3d = xs, ys, zs
# Nasty hack around a poorly implemented deprecation warning in Matplotlib 3.5 that issues two
# deprecation warnings if an artist's module does not claim to be part of the below module.
# This revolves around the method `Patch3D.do_3d_projection(self, renderer=None)`. The
# `renderer` argument has been deprecated since Matplotlib 3.4, but in 3.5 some internal calls
# during `Axes3D` display started calling the method. If an artist does not have this module,
# then it issues a deprecation warning, and calls it by passing the `renderer` parameter as
# well, which consequently triggers another deprecation warning. We should be able to remove
# this once 3.6 is the minimum supported version, because the deprecation period ends then.
__module__ = "mpl_toolkits.mplot3d.art3d"

def __init__(self, xs, ys, zs, zdir="z", **kwargs):
# The Patch3D.__init__() method just calls its own super() method and then
# self.set_3d_properties, but its __init__ signature is actually pretty incompatible with
# how it goes on to call set_3d_properties, so we just have to do things ourselves. The
# parent of Patch3D is Patch, which is also a parent of FancyArrowPatch, so its __init__ is
# still getting suitably called.
# pylint: disable=super-init-not-called
FancyArrowPatch.__init__(self, (0, 0), (0, 0), **kwargs)
self.set_3d_properties(tuple(zip(xs, ys)), zs, zdir)

def draw(self, renderer):
xs3d, ys3d, zs3d = self._verts3d
xs3d, ys3d, zs3d = zip(*self._segment3d)
x_s, y_s, _ = proj3d.proj_transform(xs3d, ys3d, zs3d, self.axes.M)
self.set_positions((x_s[0], y_s[0]), (x_s[1], y_s[1]))
FancyArrowPatch.draw(self, renderer)
Expand Down
4 changes: 4 additions & 0 deletions releasenotes/notes/fix-matplotlib-3.5-40f6d1a109ae06fe.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
fixes:
- |
Fixed a compatibility issue with Matplotlib 3.5, where the Bloch sphere would fail to render if it had any vectors attached, such as by using :obj:`~qiskit.visualization.plot_bloch_vector`.
See `#7272 <https://github.com/Qiskit/qiskit-terra/issues/7272>`__ for more detail.
2 changes: 1 addition & 1 deletion requirements-dev.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ ipython<7.22.0
ipykernel<5.5.2
ipywidgets>=7.3.0
jupyter
matplotlib>=3.3,<3.5
matplotlib>=3.3
pillow>=4.2.1
black==21.4b2
pydot
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@


visualization_extras = [
"matplotlib>=3.3,<3.5",
"matplotlib>=3.3",
"ipywidgets>=7.3.0",
"pydot",
"pillow>=4.2.1",
Expand Down

0 comments on commit ba8e3f3

Please sign in to comment.