Repository: qutip/qutip

Issue:
Set point transparency on Bloch sphere
**Is your feature request related to a problem? Please describe.**
In our simulations we frequently run Monte-Carlo samples that would be great to visualize on the Bloch sphere. However, an alpha value would be very helpful.

**Describe the solution you'd like**
Bloch().add_points(pnt, alpha = 0.3)

**Describe alternatives you've considered**
Absolutely not sure.

**Additional context**
In a similiar fashion I wonder if colormaps etc would become possible.



Discussion:
Hi! I'm working on this issue and was wondering if the alpha value should be an array-like that would apply to each point or vector individually, similarly to the color option I've seen in dev.major, or make it more simple. Where a single value would apply to all the rendered vectors or points in their add method.
I personally would be actually a bit confused if the alpha values were array-like and the simple solution would certainly be enough for me. But then again I do not have the very big picture of `qutip`.

Resolving patch:
diff --git a/qutip/bloch.py b/qutip/bloch.py
--- a/qutip/bloch.py
+++ b/qutip/bloch.py
@@ -180,12 +180,16 @@ def __init__(self, fig=None, axes=None, view=None, figsize=None,
         self.points = []
         # Data for Bloch vectors
         self.vectors = []
+        # Transparency of vectors, alpha value from 0 to 1
+        self.vector_alpha = []
         # Data for annotations
         self.annotations = []
         # Number of times sphere has been saved
         self.savenum = 0
         # Style of points, 'm' for multiple colors, 's' for single color
         self.point_style = []
+        # Transparency of points, alpha value from 0 to 1
+        self.point_alpha = []
         # Data for line segment
         self._lines = []
         # Data for arcs and arc style
@@ -299,11 +303,13 @@ def clear(self):
         self.points = []
         self.vectors = []
         self.point_style = []
+        self.point_alpha = []
+        self.vector_alpha = []
         self.annotations = []
         self._lines = []
         self._arcs = []
 
-    def add_points(self, points, meth='s'):
+    def add_points(self, points, meth='s', alpha=1.0):
         """Add a list of data points to bloch sphere.
 
         Parameters
@@ -314,8 +320,18 @@ def add_points(self, points, meth='s'):
         meth : {'s', 'm', 'l'}
             Type of points to plot, use 'm' for multicolored, 'l' for points
             connected with a line.
+
+        alpha : float, default=1.
+            Transparency value for the vectors. Values between 0 and 1.
+
+        .. note::
+
+           When using ``meth=l`` in QuTiP 4.6, the line transparency defaulted
+           to ``0.75`` and there was no way to alter it.
+           When the ``alpha`` parameter was added in QuTiP 4.7, the default
+           became ``alpha=1.0`` for values of ``meth``.
         """
-        if not isinstance(points[0], (list, ndarray)):
+        if not isinstance(points[0], (list, tuple, ndarray)):
             points = [[points[0]], [points[1]], [points[2]]]
         points = array(points)
         if meth == 's':
@@ -326,14 +342,17 @@ def add_points(self, points, meth='s'):
                 pnts = points
             self.points.append(pnts)
             self.point_style.append('s')
+            self.point_alpha.append(alpha)
         elif meth == 'l':
             self.points.append(points)
             self.point_style.append('l')
+            self.point_alpha.append(alpha)
         else:
             self.points.append(points)
             self.point_style.append('m')
+            self.point_alpha.append(alpha)
 
-    def add_states(self, state, kind='vector'):
+    def add_states(self, state, kind='vector', alpha=1.0):
         """Add a state vector Qobj to Bloch sphere.
 
         Parameters
@@ -343,6 +362,9 @@ def add_states(self, state, kind='vector'):
 
         kind : {'vector', 'point'}
             Type of object to plot.
+
+        alpha : float, default=1.
+            Transparency value for the vectors. Values between 0 and 1.
         """
         if isinstance(state, Qobj):
             state = [state]
@@ -353,23 +375,28 @@ def add_states(self, state, kind='vector'):
                    expect(sigmaz(), st)]
 
             if kind == 'vector':
-                self.add_vectors(vec)
+                self.add_vectors(vec, alpha=alpha)
             elif kind == 'point':
-                self.add_points(vec)
+                self.add_points(vec, alpha=alpha)
 
-    def add_vectors(self, vectors):
+    def add_vectors(self, vectors, alpha=1.0):
         """Add a list of vectors to Bloch sphere.
 
         Parameters
         ----------
         vectors : array_like
             Array with vectors of unit length or smaller.
+
+        alpha : float, default=1.
+            Transparency value for the vectors. Values between 0 and 1.
         """
         if isinstance(vectors[0], (list, tuple, ndarray)):
             for vec in vectors:
                 self.vectors.append(vec)
+                self.vector_alpha.append(alpha)
         else:
             self.vectors.append(vectors)
+            self.vector_alpha.append(alpha)
 
     def add_annotation(self, state_or_vector, text, **kwargs):
         """
@@ -603,11 +630,11 @@ def render(self):
         self.plot_back()
         self.plot_points()
         self.plot_vectors()
+        self.plot_lines()
+        self.plot_arcs()
         self.plot_front()
         self.plot_axes_labels()
         self.plot_annotations()
-        self.plot_lines()
-        self.plot_arcs()
         # Trigger an update of the Bloch sphere if it is already shown:
         self.fig.canvas.draw()
 
@@ -697,19 +724,21 @@ def plot_vectors(self):
             zs3d = self.vectors[k][2] * array([0, 1])
 
             color = self.vector_color[mod(k, len(self.vector_color))]
+            alpha = self.vector_alpha[k]
 
             if self.vector_style == '':
                 # simple line style
                 self.axes.plot(xs3d, ys3d, zs3d,
                                zs=0, zdir='z', label='Z',
-                               lw=self.vector_width, color=color)
+                               lw=self.vector_width, color=color,
+                               alpha=alpha)
             else:
                 # decorated style, with arrow heads
                 a = Arrow3D(xs3d, ys3d, zs3d,
                             mutation_scale=self.vector_mutation,
                             lw=self.vector_width,
                             arrowstyle=self.vector_style,
-                            color=color)
+                            color=color, alpha=alpha)
 
                 self.axes.add_artist(a)
 
@@ -734,7 +763,7 @@ def plot_points(self):
                     - real(self.points[k][0][indperm]),
                     real(self.points[k][2][indperm]),
                     s=self.point_size[mod(k, len(self.point_size))],
-                    alpha=1,
+                    alpha=self.point_alpha[k],
                     edgecolor=None,
                     zdir='z',
                     color=self.point_color[mod(k, len(self.point_color))],
@@ -742,7 +771,8 @@ def plot_points(self):
 
             elif self.point_style[k] == 'm':
                 pnt_colors = array(self.point_color *
-                                   int(ceil(num / float(len(self.point_color)))))
+                                   int(ceil(num / float(
+                                       len(self.point_color)))))
 
                 pnt_colors = pnt_colors[0:num]
                 pnt_colors = list(pnt_colors[indperm])
@@ -751,17 +781,17 @@ def plot_points(self):
                 self.axes.scatter(real(self.points[k][1][indperm]),
                                   -real(self.points[k][0][indperm]),
                                   real(self.points[k][2][indperm]),
-                                  s=s, alpha=1, edgecolor=None,
-                                  zdir='z', color=pnt_colors,
-                                  marker=marker)
+                                  s=s, alpha=self.point_alpha[k],
+                                  edgecolor=None, zdir='z',
+                                  color=pnt_colors, marker=marker)
 
             elif self.point_style[k] == 'l':
                 color = self.point_color[mod(k, len(self.point_color))]
                 self.axes.plot(real(self.points[k][1]),
                                -real(self.points[k][0]),
                                real(self.points[k][2]),
-                               alpha=0.75, zdir='z',
-                               color=color)
+                               alpha=self.point_alpha[k],
+                               zdir='z', color=color)
 
     def plot_annotations(self):
         # -X and Y data are switched for plotting purposes
diff --git a/qutip/bloch3d.py b/qutip/bloch3d.py
--- a/qutip/bloch3d.py
+++ b/qutip/bloch3d.py
@@ -6,7 +6,7 @@
 from qutip.operators import sigmax, sigmay, sigmaz
 
 
-class Bloch3d():
+class Bloch3d:
     """Class for plotting data on a 3D Bloch sphere using mayavi.
     Valid data can be either points, vectors, or qobj objects
     corresponding to state vectors or density matrices. for
@@ -50,15 +50,15 @@ class Bloch3d():
         Width of displayed vectors.
     view : list {[45,65]}
         Azimuthal and Elevation viewing angles.
-    xlabel : list {['|x>', '']}
+    xlabel : list {``['|x>', '']``}
         List of strings corresponding to +x and -x axes labels, respectively.
     xlpos : list {[1.07,-1.07]}
         Positions of +x and -x labels respectively.
-    ylabel : list {['|y>', '']}
+    ylabel : list {``['|y>', '']``}
         List of strings corresponding to +y and -y axes labels, respectively.
     ylpos : list {[1.07,-1.07]}
         Positions of +y and -y labels respectively.
-    zlabel : list {['|0>', '|1>']}
+    zlabel : list {``['|0>', '|1>']``}
         List of strings corresponding to +z and -z axes labels, respectively.
     zlpos : list {[1.07,-1.07]}
         Positions of +z and -z labels respectively.
@@ -146,8 +146,6 @@ def __init__(self, fig=None):
         # Object used for representing vectors on Bloch sphere.
         # List of colors for Bloch vectors, default = ['b','g','r','y']
         self.vector_color = ['r', 'g', 'b', 'y']
-        # Transparency of vectors
-        self.vector_alpha = 1.0
         # Width of Bloch vectors, default = 2
         self.vector_width = 2.0
         # Height of vector head
@@ -174,6 +172,10 @@ def __init__(self, fig=None):
         self.savenum = 0
         # Style of points, 'm' for multiple colors, 's' for single color
         self.point_style = []
+        # Transparency of points
+        self.point_alpha = []
+        # Transparency of vectors
+        self.vector_alpha = []
 
     def __str__(self):
         s = ""
@@ -202,7 +204,6 @@ def __str__(self):
         s += "sphere_alpha:       " + str(self.sphere_alpha) + "\n"
         s += "sphere_color:       " + str(self.sphere_color) + "\n"
         s += "size:               " + str(self.size) + "\n"
-        s += "vector_alpha:       " + str(self.vector_alpha) + "\n"
         s += "vector_color:       " + str(self.vector_color) + "\n"
         s += "vector_width:       " + str(self.vector_width) + "\n"
         s += "vector_head_height: " + str(self.vector_head_height) + "\n"
@@ -223,7 +224,7 @@ def clear(self):
         self.vectors = []
         self.point_style = []
 
-    def add_points(self, points, meth='s'):
+    def add_points(self, points, meth='s', alpha=1.0):
         """Add a list of data points to bloch sphere.
 
         Parameters
@@ -234,6 +235,9 @@ def add_points(self, points, meth='s'):
         meth : str {'s','m'}
             Type of points to plot, use 'm' for multicolored.
 
+        alpha : float, default=1.
+            Transparency value for the vectors. Values between 0 and 1.
+
         """
         if not isinstance(points[0], (list, np.ndarray)):
             points = [[points[0]], [points[1]], [points[2]]]
@@ -250,8 +254,9 @@ def add_points(self, points, meth='s'):
         else:
             self.points.append(points)
             self.point_style.append('m')
+        self.point_alpha.append(alpha)
 
-    def add_states(self, state, kind='vector'):
+    def add_states(self, state, kind='vector', alpha=1.0):
         """Add a state vector Qobj to Bloch sphere.
 
         Parameters
@@ -262,6 +267,8 @@ def add_states(self, state, kind='vector'):
         kind : str {'vector','point'}
             Type of object to plot.
 
+        alpha : float, default=1.
+            Transparency value for the vectors. Values between 0 and 1.
         """
         if isinstance(state, Qobj):
             state = [state]
@@ -269,13 +276,13 @@ def add_states(self, state, kind='vector'):
             if kind == 'vector':
                 vec = [expect(sigmax(), st), expect(sigmay(), st),
                        expect(sigmaz(), st)]
-                self.add_vectors(vec)
+                self.add_vectors(vec, alpha=alpha)
             elif kind == 'point':
                 pnt = [expect(sigmax(), st), expect(sigmay(), st),
                        expect(sigmaz(), st)]
-                self.add_points(pnt)
+                self.add_points(pnt, alpha=alpha)
 
-    def add_vectors(self, vectors):
+    def add_vectors(self, vectors, alpha=1.0):
         """Add a list of vectors to Bloch sphere.
 
         Parameters
@@ -283,12 +290,17 @@ def add_vectors(self, vectors):
         vectors : array/list
             Array with vectors of unit length or smaller.
 
+        alpha : float, default=1.
+            Transparency value for the vectors. Values between 0 and 1.
+
         """
         if isinstance(vectors[0], (list, np.ndarray)):
             for vec in vectors:
                 self.vectors.append(vec)
+                self.vector_alpha.append(alpha)
         else:
             self.vectors.append(vectors)
+            self.vector_alpha.append(alpha)
 
     def plot_vectors(self):
         """
@@ -313,7 +325,7 @@ def plot_vectors(self):
             mlab.plot3d([0, vec[0]], [0, vec[1]], [0, vec[2]],
                         name='vector' + str(ii), tube_sides=100,
                         line_width=self.vector_width,
-                        opacity=self.vector_alpha,
+                        opacity=self.vector_alpha[k],
                         color=color)
 
             cone = tvtk.ConeSource(height=self.vector_head_height,
@@ -321,7 +333,7 @@ def plot_vectors(self):
                                    resolution=100)
             cone_mapper = tvtk.PolyDataMapper(
                 input_connection=cone.output_port)
-            prop = tvtk.Property(opacity=self.vector_alpha, color=color)
+            prop = tvtk.Property(opacity=self.vector_alpha[k], color=color)
             cc = tvtk.Actor(mapper=cone_mapper, property=prop)
             cc.rotate_z(np.degrees(phi))
             cc.rotate_y(-90 + np.degrees(theta))
@@ -354,7 +366,8 @@ def plot_points(self):
                     self.points[k][0][indperm], self.points[k][1][indperm],
                     self.points[k][2][indperm], figure=self.fig,
                     resolution=100, scale_factor=self.point_size,
-                    mode=self.point_mode, color=color)
+                    mode=self.point_mode, color=color,
+                    opacity=self.point_alpha[k])
 
             elif self.point_style[k] == 'm':
                 pnt_colors = np.array(self.point_color * np.ceil(
@@ -368,7 +381,8 @@ def plot_points(self):
                         self.points[k][2][
                             indperm[kk]], figure=self.fig, resolution=100,
                         scale_factor=self.point_size, mode=self.point_mode,
-                        color=colors.colorConverter.to_rgb(pnt_colors[kk]))
+                        color=colors.colorConverter.to_rgb(pnt_colors[kk]),
+                        opacity=self.point_alpha[k])
 
     def make_sphere(self):
         """


Test patch:
diff --git a/qutip/tests/test_bloch.py b/qutip/tests/test_bloch.py
--- a/qutip/tests/test_bloch.py
+++ b/qutip/tests/test_bloch.py
@@ -1,3 +1,5 @@
+import copy
+
 import numpy as np
 import pytest
 
@@ -17,6 +19,23 @@ def _error(*args, **kw):
 check_pngs_equal = check_figures_equal(extensions=["png"])
 
 
+class RefBloch(Bloch):
+    """ A helper for rendering reference Bloch spheres. """
+    def render(self):
+        raise NotImplementedError("RefBloch disables .render()")
+
+    def render_back(self):
+        old_plot_front = self.plot_front
+        self.plot_front = lambda: None
+        try:
+            Bloch.render(self)
+        finally:
+            self.plot_front = old_plot_front
+
+    def render_front(self):
+        self.plot_front()
+
+
 class TestBloch:
     def plot_arc_test(self, fig, *args, **kw):
         b = Bloch(fig=fig)
@@ -36,9 +55,10 @@ def plot_arc_ref(self, fig, start, end, **kw):
         line = start[:, np.newaxis] * t + end[:, np.newaxis] * (1 - t)
         arc = line * np.linalg.norm(start) / np.linalg.norm(line, axis=0)
 
-        b = Bloch(fig=fig)
-        b.render()
+        b = RefBloch(fig=fig)
+        b.render_back()
         b.axes.plot(arc[1, :], -arc[0, :], arc[2, :], fmt, **kw)
+        b.render_front()
 
     @pytest.mark.parametrize([
         "start_test", "start_ref", "end_test", "end_ref", "kwargs",
@@ -129,9 +149,10 @@ def plot_line_ref(self, fig, start, end, **kw):
         y = [-start[0], -end[0]]
         z = [start[2], end[2]]
 
-        b = Bloch(fig=fig)
-        b.render()
+        b = RefBloch(fig=fig)
+        b.render_back()
         b.axes.plot(x, y, z, fmt, **kw)
+        b.render_front()
 
     @pytest.mark.parametrize([
         "start_test", "start_ref", "end_test", "end_ref", "kwargs",
@@ -166,47 +187,177 @@ def test_line(
         self.plot_line_test(fig_test, start_test, end_test, **kwargs)
         self.plot_line_ref(fig_ref, start_ref, end_ref, **kwargs)
 
-    def plot_vector_test(self, fig, vectors):
+    def plot_point_test(self, fig, point_kws):
         b = Bloch(fig=fig)
-        b.add_vectors(vectors)
+        for kw in point_kws:
+            points = kw.pop("points")
+            b.add_points(points, **kw)
         b.render()
 
-    def plot_vector_ref(self, fig, vectors):
-        from qutip.bloch import Arrow3D
+    def plot_point_ref(self, fig, point_kws):
+        b = RefBloch(fig=fig)
+        b.render_back()
+        point_colors = ['b', 'r', 'g', '#CC6600']
+        point_sizes = [25, 32, 35, 45]
+        point_markers = ["o", "s", "d", "^"]
+        idx = 0
+
+        for kw in point_kws:
+            points = kw.pop("points")
+            if not isinstance(points[0], (list, tuple, np.ndarray)):
+                points = [[points[0]], [points[1]], [points[2]]]
+            points = np.array(points)
+            if len(points[0]) == 1:
+                points = np.append(points, points, axis=1)
+
+            point_style = kw.pop("meth", "s")
+            point_color = point_colors[idx % len(point_colors)]
+            point_size = point_sizes[idx % len(point_sizes)]
+            point_marker = point_markers[idx % len(point_markers)]
+            point_alpha = kw.get("alpha", 1.0)
+            idx += 1
+
+            if point_style == 's':
+                b.axes.scatter(
+                    np.real(points[1]),
+                    -np.real(points[0]),
+                    np.real(points[2]),
+                    s=point_size,
+                    alpha=point_alpha,
+                    edgecolor=None,
+                    zdir='z',
+                    color=point_color,
+                    marker=point_marker)
+            elif point_style == 'l':
+                b.axes.plot(
+                    np.real(points[1]),
+                    -np.real(points[0]),
+                    np.real(points[2]),
+                    alpha=point_alpha,
+                    zdir='z',
+                    color=point_color,
+                )
+            else:
+                raise ValueError(
+                    "Tests currently only support point method 's'"
+                )
+        b.render_front()
+
+    @pytest.mark.parametrize([
+        "point_kws"
+    ], [
+        pytest.param(
+            dict(points=np.array([
+                [
+                    np.cos(np.pi / 4) * np.cos(t),
+                    np.sin(np.pi / 4) * np.cos(t),
+                    np.sin(t),
+                ]
+                for t in np.linspace(0, 2 * np.pi, 20)
+            ]).T, alpha=0.5), id="circle-of-points-scatter"),
+        pytest.param(
+            dict(points=np.array([
+                [
+                    np.cos(np.pi / 4) * np.cos(t),
+                    np.sin(np.pi / 4) * np.cos(t),
+                    np.sin(t),
+                ]
+                for t in np.linspace(0, 2 * np.pi, 20)
+            ]).T, meth="l"), id="circle-of-points-line"),
+        pytest.param(
+            dict(points=(0, 0, 1), alpha=1), id="alpha-opaque"),
+        pytest.param(
+            dict(points=(0, 0, 1), alpha=0.3), id="alpha-transparent"),
+        pytest.param(
+            dict(points=(0, 0, 1), alpha=0), id="alpha-invisible"),
+        pytest.param(
+            dict(points=(0, 0, 1)), id="alpha-default"),
+        pytest.param([
+            dict(points=[(0, 0), (0, 1), (1, 0)], alpha=1.0),
+            dict(points=[(1, 1), (0, 1), (1, 0)], alpha=0.5),
+        ], id="alpha-multiple-point-sets"),
+    ])
+    @check_pngs_equal
+    def test_point(self, point_kws, fig_test, fig_ref):
+        if isinstance(point_kws, dict):
+            point_kws = [point_kws]
+        self.plot_point_test(fig_test, copy.deepcopy(point_kws))
+        self.plot_point_ref(fig_ref, copy.deepcopy(point_kws))
+
+    def plot_vector_test(self, fig, vector_kws):
         b = Bloch(fig=fig)
+        for kw in vector_kws:
+            vectors = kw.pop("vectors")
+            b.add_vectors(vectors, **kw)
         b.render()
-        colors = ['g', '#CC6600', 'b', 'r']
 
-        if not isinstance(vectors[0], (list, tuple, np.ndarray)):
-            vectors = [vectors]
+    def plot_vector_ref(self, fig, vector_kws):
+        from qutip.bloch import Arrow3D
+        b = RefBloch(fig=fig)
+        b.render_back()
+        vector_colors = ['g', '#CC6600', 'b', 'r']
+        idx = 0
 
-        for i, v in enumerate(vectors):
-            color = colors[i % len(colors)]
-            xs3d = v[1] * np.array([0, 1])
-            ys3d = -v[0] * np.array([0, 1])
-            zs3d = v[2] * np.array([0, 1])
-            a = Arrow3D(
-                xs3d, ys3d, zs3d,
-                mutation_scale=20, lw=3, arrowstyle="-|>", color=color)
-            b.axes.add_artist(a)
+        for kw in vector_kws:
+            vectors = kw.pop("vectors")
+
+            if not isinstance(vectors[0], (list, tuple, np.ndarray)):
+                vectors = [vectors]
+
+            for v in vectors:
+                color = vector_colors[idx % len(vector_colors)]
+                alpha = kw.get("alpha", 1.0)
+                idx += 1
+                xs3d = v[1] * np.array([0, 1])
+                ys3d = -v[0] * np.array([0, 1])
+                zs3d = v[2] * np.array([0, 1])
+                a = Arrow3D(
+                    xs3d, ys3d, zs3d,
+                    mutation_scale=20, lw=3, arrowstyle="-|>",
+                    color=color, alpha=alpha)
+                b.axes.add_artist(a)
+        b.render_front()
 
     @pytest.mark.parametrize([
-        "vectors",
+        "vector_kws"
     ], [
         pytest.param(
-            (0, 0, 1), id="single-vector-tuple"),
+            dict(vectors=(0, 0, 1)), id="single-vector-tuple"),
+        pytest.param(
+            dict(vectors=[0, 0, 1]), id="single-vector-list"),
+        pytest.param(
+            dict(vectors=np.array([0, 0, 1])), id="single-vector-numpy"),
+        pytest.param(
+            dict(vectors=[(0, 0, 1), (0, 1, 0)]), id="list-vectors-tuple"),
+        pytest.param(
+            dict(vectors=[[0, 0, 1]]), id="list-vectors-list"),
+        pytest.param(
+            dict(vectors=[np.array([0, 0, 1])]), id="list-vectors-numpy"),
+        pytest.param(
+            dict(vectors=[
+                [
+                    np.cos(np.pi / 4) * np.cos(t),
+                    np.sin(np.pi / 4) * np.cos(t),
+                    np.sin(t),
+                ]
+                for t in np.linspace(0, 2 * np.pi, 20)
+            ], alpha=0.5), id="circle-of-vectors"),
         pytest.param(
-            [0, 0, 1], id="single-vector-list"),
-       pytest.param(
-            np.array([0, 0, 1]), id="single-vector-numpy"),
+            dict(vectors=(0, 0, 1), alpha=1), id="alpha-opaque"),
         pytest.param(
-            [(0, 0, 1), (0, 1, 0)], id="list-vectors-tuple"),
+            dict(vectors=(0, 0, 1), alpha=0.3), id="alpha-transparent"),
         pytest.param(
-            [[0, 0, 1]], id="list-vectors-list"),
+            dict(vectors=(0, 0, 1), alpha=0), id="alpha-invisible"),
         pytest.param(
-            [np.array([0, 0, 1])], id="list-vectors-numpy"),
+            dict(vectors=(0, 0, 1)), id="alpha-default"),
+        pytest.param([
+            dict(vectors=[(0, 0, 1), (0, 1, 0)], alpha=1.0),
+            dict(vectors=[(1, 0, 1), (1, 1, 0)], alpha=0.5),
+        ], id="alpha-multiple-vector-sets"),
     ])
     @check_pngs_equal
-    def test_vector(self, vectors, fig_test, fig_ref):
-        self.plot_vector_test(fig_test, vectors)
-        self.plot_vector_ref(fig_ref, vectors)
+    def test_vector(self, vector_kws, fig_test, fig_ref):
+        if isinstance(vector_kws, dict):
+            vector_kws = [vector_kws]
+        self.plot_vector_test(fig_test, copy.deepcopy(vector_kws))
+        self.plot_vector_ref(fig_ref, copy.deepcopy(vector_kws))