matplotlib/bloch_sphere.py
import math
import numpy as np
import matplotlib
import mpl_toolkits
from matplotlib.patches import FancyArrowPatch
import common
class Arrow3D(matplotlib.patches.FancyArrowPatch):
def __init__(self, xs, ys, zs, *args, **kwargs):
matplotlib.patches.FancyArrowPatch.__init__(self, (0,0), (0,0), *args, **kwargs)
self._verts3d = xs, ys, zs
def draw(self, renderer):
xs3d, ys3d, zs3d = self._verts3d
xs, ys, zs = mpl_toolkits.mplot3d.proj3d.proj_transform(xs3d, ys3d, zs3d, renderer.M)
self.set_positions((xs[0],ys[0]),(xs[1],ys[1]))
matplotlib.patches.FancyArrowPatch.draw(self, renderer)
def draw_arrow_3d(ax, x, y, z):
"""
https://stackoverflow.com/questions/22867620/putting-arrowheads-on-vectors-in-matplotlibs-3d-plot
https://stackoverflow.com/questions/11140163/plotting-a-3d-cube-a-sphere-and-a-vector-in-matplotlib
"""
ax.add_artist(Arrow3D(
x,
y,
z,
mutation_scale=20,
lw=1,
arrowstyle="-|>",
color="black"
))
def plot(plt, params):
matplotlib.rcParams['font.weight'] = 'bold'
matplotlib.rcParams['font.size'] = 15
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
# Working labels
ax.azim = 60
ax.elev = 30
ax.grid(False)
ax.set_axis_off()
fig.subplots_adjust(left=0, right=1, bottom=0, top=1, wspace=0)
# Params.
r = 1
lim = r*1.5
# Reduce actual "visible limit" to reduce excessive whitespace around figure.
# https://stackoverflow.com/questions/41225293/remove-white-spaces-in-axes3d-matplotlib
lim_vis = lim * 0.55
ax.set_xlim(-lim_vis, lim_vis)
ax.set_ylim(-lim_vis, lim_vis)
ax.set_zlim(-lim_vis, lim_vis)
# Axes.
draw_arrow_3d(ax, [-lim, lim], [0, 0], [0, 0])
draw_arrow_3d(ax, [0, 0], [-lim, lim], [0, 0])
draw_arrow_3d(ax, [0, 0], [0, 0], [-lim, lim])
ax.text(lim, 0, 0.1*lim, 'x')
ax.text(0, lim, -0.11*lim, 'y')
ax.text(-0.05*lim, 0, lim, 'z')
# Circles.
# https://stackoverflow.com/questions/56870675/how-to-do-a-3d-circle-in-matplotlib/64843339#64843339
p = matplotlib.patches.Circle((0, 0), 1, fill=False)
ax.add_patch(p)
mpl_toolkits.mplot3d.art3d.pathpatch_2d_to_3d(p, z=0, zdir="x")
# p = matplotlib.patches.Circle((0, 0), 1, fill=False)
# ax.add_patch(p)
# mpl_toolkits.mplot3d.art3d.pathpatch_2d_to_3d(p, z=0, zdir="y")
p = matplotlib.patches.Circle((0, 0), 1, fill=False, ls='--')
ax.add_patch(p)
mpl_toolkits.mplot3d.art3d.pathpatch_2d_to_3d(p, z=0, zdir="z")
p = matplotlib.patches.Circle((0, 0), 1, fill=False, ls='--')
ax.add_patch(p)
mpl_toolkits.mplot3d.art3d.pathpatch_2d_to_3d(p, z=0, zdir="z")
# Markers.
def marker(x, y, z):
# Some of the points appear below the rest of the plot, which is undesired,
# but maybe there is no way to get it to work:
# - https://github.com/matplotlib/matplotlib/issues/14148
# - https://github.com/matplotlib/matplotlib/issues/14175
# - https://github.com/matplotlib/matplotlib/pull/14508
# - https://stackoverflow.com/questions/56010933/matplotlib-plot3d-surface-line-scatter-plot-how-to-define-z-order
# - https://stackoverflow.com/questions/37611023/3d-parametric-curve-in-matplotlib-does-not-respect-zorder-workaround
# - https://stackoverflow.com/questions/52923540/matplotlib-3d-workaround-for-plot-order
# - https://stackoverflow.com/questions/23188561/matplotlib-3d-plot-zorder-issue
ax.scatter(x, y, z, s=100, color='white', edgecolors='black')
marker(-r, 0, 0)
marker( r, 0, 0)
marker( 0, r, 0)
marker( 0, -r, 0)
marker( 0, 0, r)
marker( 0, 0, -r)
# Marker text.
ha = 'center'
ax.text( r, 0, -0.3*r, common.braket( '+'), ha=ha)
ax.text(-1.1*r, -0.1*r, 0.2*r, common.braket( '-'), ha=ha)
ax.text(-0.1*r, 1.1*r, 0.3*r, common.braket('-i'), ha=ha)
ax.text(-0.2*r, -1.5*r, 0.1*r, common.braket( 'i'), ha=ha)
ax.text(-0.2*r, 0, r, common.braket( '0'), ha=ha)
ax.text( 0.2*r, 0, 1.1*-r, common.braket( '1'), ha=ha)
# Sample vector. with theta and phi.
if False:
# It is getting too hard to cram everything in a single image,
# bail out for now, just point people to https://en.wikipedia.org/wiki/File:Bloch_sphere.svg
# which shows that pretty well.
theta = np.pi/4
phi = np.pi/4
sin_theta = np.sin(theta)
x = sin_theta*np.cos(phi)
y = sin_theta*np.sin(phi)
z = np.cos(theta)
draw_arrow_3d(ax, [0, x], [0, y], [0, z])
ax.plot([x, x], [y, y], [0, z], color='black', ls='--')
ax.plot([0, x], [0, y], [0, 0], color='black', ls='--')
# return {'bbox_inches': fig.bbox_inches.from_bounds(1, 1, 8, 6)}