Repository: Qiskit/qiskit

Issue:
AttributeError: 'Arrow3D' object has no attribute '_path2d' while plotting bloch vector
I wrote the simple code 

from qiskit.visualization import plot_bloch_vector
%matplotlib inline

plot_bloch_vector([0,1,0], title="New Bloch Sphere") taken from 
https://qiskit.org/documentation/stubs/qiskit.visualization.plot_bloch_vector.html

I keep getting  AttributeError: 'Arrow3D' object has no attribute '_path2d' in my jupyter notebook.

I have {'qiskit-terra': '0.19.1', 'qiskit-aer': '0.10.1', 'qiskit-ignis': '0.7.0', 'qiskit-ibmq-provider': '0.18.3', 'qiskit-aqua': '0.9.4', 'qiskit': '0.34.0', 'qiskit-nature': None, 'qiskit-finance': None, 'qiskit-optimization': None, 'qiskit-machine-learning': None}

My matplotlib version is 3.4.3

Can anyone provide a solution for this and let me know where I am going wrong.


Discussion:
I'm not surprised there's some sort of bug with slightly older Matplotlib versions - it's quite hard to write 3D code that works with several concurrent MPL versions, and MPL 3.5 caused us quite a few issues, so this bug probably crept in there.  As a temporary workaround, upgrading to Matplotlib 3.5 _should_ make everything work.

That said, I can't seem to reproduce this failure with the Qiskit and Matplotlib versions here - could you post a full traceback of the error (if available), and perhaps just check that the environment within Jupyter definitely has Matplotlib 3.4.3 and Qiskit Terra 0.19.1?  (It's often hard to tell in Jupyter if you've got the same environment loaded as you expect, but `import matplotlib; print(matplotlib.__version__)` and similar for `qiskit` is reliable.)
Im also seeing this error just following the basic tutorials (https://github.com/Qiskit/qiskit-tutorials/blob/master/tutorials/circuits/2_plotting_data_in_qiskit.ipynb).  I can confirm that I am on matplotlib 3.4.3 and qiskit.terra 0.19.1.    Just running the code in the tutorial mentioned above returns the errors below when trying plot the bloch sphere or use the q sphere function (both provide similar errors):

---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
C:\ProgramData\Anaconda3\lib\site-packages\IPython\core\formatters.py in __call__(self, obj)
    339                 pass
    340             else:
--> 341                 return printer(obj)
    342             # Finally look for special method names
    343             method = get_real_method(obj, self.print_method)

C:\ProgramData\Anaconda3\lib\site-packages\IPython\core\pylabtools.py in <lambda>(fig)
    246 
    247     if 'png' in formats:
--> 248         png_formatter.for_type(Figure, lambda fig: print_figure(fig, 'png', **kwargs))
    249     if 'retina' in formats or 'png2x' in formats:
    250         png_formatter.for_type(Figure, lambda fig: retina_figure(fig, **kwargs))

C:\ProgramData\Anaconda3\lib\site-packages\IPython\core\pylabtools.py in print_figure(fig, fmt, bbox_inches, **kwargs)
    130         FigureCanvasBase(fig)
    131 
--> 132     fig.canvas.print_figure(bytes_io, **kw)
    133     data = bytes_io.getvalue()
    134     if fmt == 'svg':

C:\ProgramData\Anaconda3\lib\site-packages\matplotlib\backend_bases.py in print_figure(self, filename, dpi, facecolor, edgecolor, orientation, format, bbox_inches, pad_inches, bbox_extra_artists, backend, **kwargs)
   2193                         self.figure.draw(renderer)
   2194 
-> 2195                     bbox_inches = self.figure.get_tightbbox(
   2196                         renderer, bbox_extra_artists=bbox_extra_artists)
   2197                     if pad_inches is None:

C:\ProgramData\Anaconda3\lib\site-packages\matplotlib\figure.py in get_tightbbox(self, renderer, bbox_extra_artists)
   2504 
   2505         for a in artists:
-> 2506             bbox = a.get_tightbbox(renderer)
   2507             if bbox is not None and (bbox.width != 0 or bbox.height != 0):
   2508                 bb.append(bbox)

C:\ProgramData\Anaconda3\lib\site-packages\matplotlib\artist.py in get_tightbbox(self, renderer)
    276             The enclosing bounding box (in figure pixel coordinates).
    277         """
--> 278         bbox = self.get_window_extent(renderer)
    279         if self.get_clip_on():
    280             clip_box = self.get_clip_box()

C:\ProgramData\Anaconda3\lib\site-packages\matplotlib\patches.py in get_window_extent(self, renderer)
    596 
    597     def get_window_extent(self, renderer=None):
--> 598         return self.get_path().get_extents(self.get_transform())
    599 
    600     def _convert_xy_units(self, xy):

C:\ProgramData\Anaconda3\lib\site-packages\mpl_toolkits\mplot3d\art3d.py in get_path(self)
    309 
    310     def get_path(self):
--> 311         return self._path2d
    312 
    313     def get_facecolor(self):

AttributeError: 'Arrow3D' object has no attribute '_path2d'

<Figure size 504x504 with 2 Axes>
Just upgraded to matplotlib 3.5 and it now works!
Using Python 3.8 and Matplotlib 3.5.1 on MBP M1 Max. I still get the error. 


Thanks @lfenster: with the extra output, I can see why I was never able to reproduce the failures locally - I only develop in terminal IPython, but the issue actually comes in when Jupyter tries to cut out the whitespace around the issues.  A minimal reproducer of the bug:
```python
from qiskit.visualization import plot_bloch_vector
plot_bloch_vector([0, 1, 0]).savefig("tmp.png", bbox_inches="tight")
```

@sodoherty-ai: what version of Qiskit Terra are you using?

Resolving patch:
diff --git a/qiskit/visualization/bloch.py b/qiskit/visualization/bloch.py
--- a/qiskit/visualization/bloch.py
+++ b/qiskit/visualization/bloch.py
@@ -81,10 +81,12 @@ def __init__(self, xs, ys, zs, zdir="z", **kwargs):
         # pylint: disable=super-init-not-called
         FancyArrowPatch.__init__(self, (0, 0), (0, 0), **kwargs)
         self.set_3d_properties(tuple(zip(xs, ys)), zs, zdir)
+        self._path2d = None
 
     def draw(self, renderer):
         xs3d, ys3d, zs3d = zip(*self._segment3d)
         x_s, y_s, _ = proj3d.proj_transform(xs3d, ys3d, zs3d, self.axes.M)
+        self._path2d = matplotlib.path.Path(np.column_stack([x_s, y_s]))
         self.set_positions((x_s[0], y_s[0]), (x_s[1], y_s[1]))
         FancyArrowPatch.draw(self, renderer)
 


Test patch: