Skip to content
Snippets Groups Projects
Commit 22a3bf43 authored by Valentin Bruch's avatar Valentin Bruch
Browse files

steering scripts: first version as available on Moodle

parent c29be98a
No related branches found
No related tags found
No related merge requests found
#!/usr/bin/env python3
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.widgets import Slider
from matplotlib.colors import LinearSegmentedColormap
### Vectors
def M1non(c,l):
s0 = np.sqrt(l); s1 = np.sqrt(1-s0**2)
v0=np.cos(c); v1=np.sin(c)
return np.transpose(np.array([[s0*v0,s1*v1],[-s0*v1,s1*v0]]))
def M1(c,l):
s0 = np.sqrt(l); s1 = np.sqrt(1-s0**2)
x = M1non(c,l)
return x/np.linalg.norm(x,ord=2,axis=0)
def M2(c):
v0=np.cos(c); v1=np.sin(c);
return np.transpose(np.array([[v0,v1],[-v1,v0]]))
class Steering:
# define red-green colormap
colorlist = ["red","red","limegreen"]
segmentlist = [0.,0.4, 1.]
cmap=LinearSegmentedColormap.from_list('rg',
list(zip(segmentlist,colorlist)), N=20)
arrow_params = dict(
head_width=0.05,
head_length=0.05,
length_includes_head=True
)
alice_arrow_params = dict(width=0.01, color='black', **arrow_params)
bob_arrow_params = dict(width=0.01, color=(0.,0.,1.), **arrow_params)
bob_mirror_arrow_params = dict(width=0.01, color=(0.2,0.2,1.,0.3), **arrow_params)
marginal_arrow_params = dict(width=0.001, color='lightgray',linestyle='--', **arrow_params)
def __init__(self, resolution=200):
self.fig = plt.figure(figsize=plt.figaspect(0.5))
self.fig.subplots_adjust(left=0.15, bottom=0.25)
self.ax1 = self.fig.add_subplot(1, 2, 1)
self.ax2 = self.fig.add_subplot(1, 2, 2)
self.ax1.axis('equal') # set the axes to the same scale
self.ax2.axis('equal')
self.ax1.plot(0, 0, 'ok') # plot a black point at the origin
self.ax2.plot(0, 0, 'ok')
self.arrows_alice = []
self.arrows_bob = []
m = 0.15 # margin
self.ax1.set_xlim([-1.0-m,1.0+m])
self.ax1.set_ylim([-1.0-m,1.0+m])
self.ax2.set_xlim([-1.0-m,1.0+m])
self.ax2.set_ylim([-1.0-m,1.0+m])
self.drag_pointer = 0
# Hide axes
self.ax1.xaxis.set_visible(False)
self.ax1.yaxis.set_visible(False)
self.ax2.xaxis.set_visible(False)
self.ax2.yaxis.set_visible(False)
self.ax1.set_title('Alice: Ensemble state vectors\n'
r'$\vert \psi_{Ab} \rangle = \langle b_B \vert \psi_{AB} \rangle / |\langle b_B \vert \psi_{AB} \rangle|$')
self.ax2.set_title('Bob: Observation vectors\n'
r'$\vert b_{B} \rangle$',)
self.ax1.arrow(-1,0,2,0,**self.marginal_arrow_params,zorder=-2)
self.ax1.arrow(0,-1,0,2,**self.marginal_arrow_params,zorder=-2)
self.ax2.arrow(-1,0,2,0,**self.marginal_arrow_params,zorder=-2)
self.ax2.arrow(0,-1,0,2,**self.marginal_arrow_params,zorder=-2)
self.ax1.text(0.85,-0.2, 'eigen-\n' r'vectors $\rho_A$',
horizontalalignment='center', verticalalignment='center',
color = 'grey')
self.ax2.text(0.85,-0.2, 'eigen-\n' r'vectors $\rho_B$',
horizontalalignment='center', verticalalignment='center',
color = 'grey')
self.ax2.text(-0.75,0.925, 'Grab vectors\n to rotate',
horizontalalignment='center', verticalalignment='center',
color = 'grey')
self.angles = np.linspace(0, 2*np.pi, resolution)
#### Slider ###
# Define initial values.
l_min = 1e-5 # avoid ugly drawings for l=0 ...
c = 0*np.pi
l = 0.5
ax_slider_c = plt.axes([0.3, 0.1, 0.55, 0.03])
self.slider_c = Slider(
ax_slider_c,
'Eigenbasis $\\rho_B$: angle $\\theta$ $\\in$ [-$\\pi$, 3$\\pi$]',
-1*np.pi,
3*np.pi,
valinit=c
)
ax_slider_l = plt.axes([0.3, 0.05, 0.55, 0.03])
self.slider_l = Slider(
ax_slider_l,
'Eigenvalue $\\rho_B$: $\lambda_0$ $\\in$ [0,1]',
l_min,
1-l_min,
valinit=l
)
self.slider_c.on_changed(self.update)
self.slider_l.on_changed(self.update)
# Dragging the arrows requires handling mouse input events:
self.fig.canvas.mpl_connect('button_press_event', self.on_press)
self.fig.canvas.mpl_connect('button_release_event', self.on_release)
self.fig.canvas.mpl_connect('motion_notify_event', self.drag)
def on_press(self, event):
'Mouse button press event: check if this should start dragging an arrow'
if event.inaxes != self.ax2:
return
if self.arrows_bob[0].contains(event, 500)[0] or self.arrows_bob[1].contains(event, 500)[0]:
vec1 = (event.xdata - self.arrows_bob[0].xy[0,0], event.ydata - self.arrows_bob[0].xy[0,1])
vec2 = (event.xdata - self.arrows_bob[1].xy[0,0], event.ydata - self.arrows_bob[1].xy[0,1])
if (vec1[0]**2 + vec1[1]**2 < vec2[0]**2 + vec2[1]**2):
self.drag_pointer = 1
else:
self.drag_pointer = 2
def on_release(self, event):
'Stop dragging arrow'
self.drag_pointer = 0
def drag(self, event):
'''Drag one of Bob's arrows'''
if event.inaxes != self.ax2:
return
if self.drag_pointer == 1:
self.slider_c.set_val(np.arctan2(event.ydata, event.xdata))
elif self.drag_pointer == 2:
self.slider_c.set_val(np.arctan2(-event.xdata, event.ydata))
def run(self):
'Start the animation.'
self.update()
plt.show()
def steer(self, c, l):
# Alice (left panel)
for arr in self.arrows_alice:
arr.remove()
for arr in self.arrows_bob:
arr.remove()
self.arrows_alice.clear()
self.arrows_bob.clear()
x = np.sqrt(l)*np.cos(self.angles)
y = np.sqrt(1-l)*np.sin(self.angles)
try:
self.ellipse.set_data(x, y)
except AttributeError:
self.ellipse, = self.ax1.plot(x, y, color='lightgray', zorder=-1)
M = M2(c)
for i in range(0, 2):
self.arrows_alice.append(
self.ax1.arrow(0, 0, M[0,i], M[1,i], **self.bob_mirror_arrow_params)
)
M = M1(c,l)
for i in range(0, 2):
self.arrows_alice.append(
self.ax1.arrow(0, 0, M[0,i], M[1,i], **self.alice_arrow_params)
)
M = M1non(c,l)
for i in range(0, 2):
self.arrows_alice.append(
self.ax1.arrow(0, 0, M[0,i], M[1,i], **self.arrow_params, width=0.05,
color = self.cmap( np.sqrt(M[0,i]**2+M[1,i]**2) ) )
)
# Bob (right panel)
M = M2(c)
for i in range(0, 2):
self.arrows_bob.append(
self.ax2.arrow(0, 0, M[0,i], M[1,i], **self.bob_arrow_params)
)
def update(self, *args):
'Read values from the sliders and update the figure'
c = self.slider_c.val
l = self.slider_l.val
self.steer(c, l)
self.fig.canvas.draw_idle()
if __name__ == '__main__':
Steering().run()
#!/usr/bin/env python3
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.widgets import Slider
from matplotlib.colors import LinearSegmentedColormap
from mpl_toolkits.mplot3d import Axes3D
from matplotlib.patches import FancyArrowPatch
from mpl_toolkits.mplot3d import proj3d
class Arrow3D(FancyArrowPatch):
# https://stackoverflow.com/questions/42281966/how-to-plot-vectors-in-python-using-matplotlib
def __init__(self, xs, ys, zs, *args, **kwargs):
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 = proj3d.proj_transform(xs3d, ys3d, zs3d, renderer.M)
self.set_positions((xs[0],ys[0]),(xs[1],ys[1]))
FancyArrowPatch.draw(self, renderer)
# Bloch vectors
def M1non(c,t,l):
'Probability-normalized Bloch vectors of A-ensemble'
s0 = np.sqrt(l); s1 = np.sqrt(1-s0**2)
xB=np.cos(c)*np.sin(t); yB=np.sin(c)*np.sin(t); zB=np.cos(t)
xA1= s0*s1*xB; xA2= s0*s1*(-xB)
yA1= - s0*s1*yB; yA2= -s0*s1*(-yB)
zA1= ((s0**2-s1**2)+zB)/2; zA2= ((s0**2-s1**2)-zB)/2
return np.transpose(np.array([[xA1,yA1,zA1],[xA2,yA2,zA2]]))
def M1(c,t,l):
'Normalized Bloch vectors of A-ensemble'
x = M1non(c,t,l)
return x/np.linalg.norm(x,ord=2,axis=0)
def M2(c,t):
'Normalized Bloch vectors of B-measurement'
xB=np.cos(c)*np.sin(t); yB=np.sin(c)*np.sin(t); zB=np.cos(t)
return np.transpose(np.array([[ xB, yB, zB],
[-xB,-yB,-zB]]))
# Probabilities
def prob(c,t,l):
'Probabilities of A-ensemble'
s0 = np.sqrt(l); s1 = np.sqrt(1-s0**2)
zB=np.cos(t)
return np.transpose(np.array([ (1 + (s0**2-s1**2)* zB )/2,
(1 + (s0**2-s1**2)*(-zB) )/2 ]))
#########################
class Steering:
# Red-Green colormap
colorlist = ["red","red","limegreen"]
segmentlist = [0.,0.2, 1.]
cmap=LinearSegmentedColormap.from_list('rg', list(zip(segmentlist,colorlist)), N=20)
# Bloch sphere/circle parameters
# stride = key performance parameter: < 6 is slow, > 6 gives ugly spheres
stride=6
sphere_params1 = dict(color='blue',linewidth=0.3,alpha=0.01,rstride=stride,cstride=stride)
sphere_params2 = sphere_params1
ellipsoid_params1 = dict(color='yellow',linewidth=0.1, alpha=0.05,rstride=stride,cstride=stride)
circle_params1 = dict(color='gray',linewidth=0.2, alpha=1.0)
circle_params2 = circle_params1
def __init__(self, resolution=200):
# Initialize figures
self.fig = plt.figure(figsize=plt.figaspect(0.5))
self.ax1 = self.fig.add_subplot(1, 2, 1,projection='3d') #, adjustable='box')
self.ax2 = self.fig.add_subplot(1, 2, 2,projection='3d')
# Initialize what needs NO update
self.fig.subplots_adjust(left=0.03,right=1-0.05,bottom=0.15,top=1-0.03,
wspace=0.01)
self.ax1.xaxis.set_ticks([]); self.ax1.yaxis.set_ticks([]); self.ax1.zaxis.set_ticks([]);
self.ax2.xaxis.set_ticks([]); self.ax2.yaxis.set_ticks([]); self.ax2.zaxis.set_ticks([]);
self.ax1.set_xlim([-1,1]);self.ax1.set_ylim([-1,1]);self.ax1.set_zlim([-1,1]);
self.ax2.set_xlim([-1,1]);self.ax2.set_ylim([-1,1]);self.ax2.set_zlim([-1,1]);
# Bloch spheres
self.u = np.linspace(0, 2 * np.pi, 100)
self.v = np.linspace(0, np.pi, 100)
self.xsphere = 1 * np.outer(np.cos(self.u), np.sin(self.v))
self.ysphere = 1 * np.outer(np.sin(self.u), np.sin(self.v))
self.zsphere = 1 * np.outer(np.ones(np.size(self.u)), np.cos(self.v))
self.ax1.plot_surface(self.xsphere, self.ysphere, self.zsphere,**self.sphere_params1, zorder=-1)
self.ax2.plot_surface(self.xsphere, self.ysphere, self.zsphere,**self.sphere_params2, zorder=-1)
# Circles
self.tline = np.linspace(0, 2*np.pi, 100)
self.cline = np.linspace(0, 2*np.pi, 100)
# Z-axis of Bob
self.ax2.add_artist(Arrow3D([0,0],[0,0],[-1,1],
mutation_scale=20,lw=2,arrowstyle="-",color='lightgrey',zorder=-1))
# Text
self.ax1.text2D(0.99, 0.99,
'Alice: Ensemble state Bloch-vectors\n'
r'for $\vert \psi_{Ab} \rangle = \langle b_B \vert \psi_{AB} \rangle / |\langle b_B \vert \psi_{AB} \rangle|$',
horizontalalignment = 'right', verticalalignment = 'top',
transform=self.ax1.transAxes)
self.ax2.text2D( 0.99, 0.99,
'Bob: Observation Bloch-vectors\n'
r'for $\vert b_{B} \rangle$',
horizontalalignment = 'right', verticalalignment = 'top',
transform=self.ax2.transAxes)
self.ax2.text2D(0.99, 0.01,
r'Grab figure to rotate: mouse (dx,dy) = ($d\phi$,$d\theta$)',
color = 'grey',
horizontalalignment = 'right', verticalalignment = 'bottom',
transform=self.ax2.transAxes)
# Initialize what needs to be updated
self.arrows1 = []; self.arrows2 = [];
self.drag_pointer = 0
# Initialize interactive stuff
# Initial values of l = \lambda_0, c = phi, t=theta
l_min = 1e-5 # avoid ugly drawings for l=0 ...
self.l = 0.
l = 0.5
c = 0*np.pi
t = 0.0*np.pi
# Sliders: positions adjusted relative to left and right panel
xoffset=0.3; yoffset=0.05; yheight=0.03
xslider=self.ax1.get_position().x0 + xoffset;
xwidth =self.ax2.get_position().x1 - xslider
yslider=self.ax1.get_position().y0 - yoffset
ax_slider_c = plt.axes([xslider, yslider, xwidth, yheight])
ax_slider_t = plt.axes([xslider, yslider-1.5*yheight, xwidth, yheight])
ax_slider_l = plt.axes([xslider, yslider-3.0*yheight, xwidth, yheight])
self.slider_c = Slider(ax_slider_c,
'Eigenbasis $\\rho_B$: angle $\\phi$ $\\in$ [-$\\pi$, 3$\\pi$]',
-1*np.pi, 3*np.pi, valinit=c)
self.slider_t = Slider(ax_slider_t,
'Eigenbasis $\\rho_B$: angle $\\theta$ $\\in$ [-$\\pi$, 3$\\pi$]',
-0.5*np.pi, 1.5*np.pi, valinit=t)
self.slider_l = Slider(ax_slider_l,
'Eigenvalue $\\rho_B$: $\lambda_0$ $\\in$ [0,1]',
l_min, 1-l_min,valinit=l)
self.slider_c.on_changed(self.update)
self.slider_t.on_changed(self.update)
self.slider_l.on_changed(self.update)
# Dragging: requires handling mouse input events:
self.fig.canvas.mpl_connect('button_press_event', self.on_press)
self.fig.canvas.mpl_connect('button_release_event', self.on_release)
self.fig.canvas.mpl_connect('motion_notify_event', self.drag)
self.ax1.disable_mouse_rotation(); self.ax2.disable_mouse_rotation()
def update_plots(self, c,t, l):
for arr in self.arrows1:
arr.remove()
for arr in self.arrows2:
arr.remove()
self.arrows1.clear(); self.arrows2.clear();
# Circles: \theta
xline=np.cos(c)*np.sin(self.tline); yline=np.sin(c)*np.sin(self.tline); zline=np.cos(self.tline)
try:
self.tcircle1.set_data(xline,-yline); self.tcircle1.set_3d_properties(zline) # update x,y,z data
self.tcircle2.set_data(xline, yline); self.tcircle2.set_3d_properties(zline) # update x,y,z data
except AttributeError:
self.tcircle1, = self.ax1.plot3D(xline, -yline, zline, **self.circle_params1,zorder=-1)
self.tcircle2, = self.ax2.plot3D(xline, yline, zline, **self.circle_params2,zorder=-1)
# Circles: \phi (perhaps not helpful)
xline=np.cos(self.cline)*np.sin(t); yline=np.sin(self.cline)*np.sin(t); zline=np.cos(t)
try:
self.ccircle2.set_data(xline, yline); self.ccircle2.set_3d_properties(zline) # update x,y,z data
except AttributeError:
#self.ccircle1, = self.ax1.plot3D(xline, -yline, zline, **self.circle_params1,zorder=-1)
self.ccircle2, = self.ax2.plot3D(xline, yline, zline, **self.circle_params2,zorder=-1)
# Ellipsoid of probability-normalized ensemble vectors
# (update only if l=\lambda changed which is not mouse-driven)
if self.l != l:
self.l = l
x = np.sqrt(l*(1-l))* self.xsphere; y = np.sqrt(l*(1-l))* self.ysphere; z = (l-(1-l))/2+ (1/2)* self.zsphere
try:
self.ellipsoid1.remove()
self.ellipsoid1 = self.ax1.plot_surface(x, y, z,**self.ellipsoid_params1,zorder=-1)
except (AttributeError,IndexError):
self.ellipsoid1 = self.ax1.plot_surface(x, y, z,**self.ellipsoid_params1,zorder=-1)
# Right panel (B)
# B-measurement Bloch vectors (normalized)
M=M2(c,t)
arrows= [ "-|>", "-" ]
for i in range(0,1+1):
self.arrows2.append(
self.ax2.add_artist(Arrow3D([0,M[0,i]],[0,M[1,i]],[0,M[2,i]],
mutation_scale=10,lw=2, arrowstyle=arrows[i] )) #"-|>"))
)
# Left panel (A)
# A-ensemble Bloch vectors (normalized)
M=M1(c,t,l)
for i in range(0,1+1):
self.arrows1.append(
self.ax1.add_artist(Arrow3D([0,M[0,i]],[0,M[1,i]],[0,M[2,i]],
mutation_scale=10,lw=2, arrowstyle="-|>",color='black'))
)
#
self.arrows1.append(
self.ax1.add_artist(Arrow3D([M[0,0],M[0,1]],[M[1,0],M[1,1]],[M[2,0],M[2,1]],
mutation_scale=10,lw=1, arrowstyle="-", color = 'blue' ))
)
# A-ensemble Bloch vectors (probability normalized)
M=M1non(c,t,l)
for i in range(0,1+1):
self.arrows1.append(
self.ax1.add_artist(Arrow3D([0,M[0,i]],[0,M[1,i]],[0,M[2,i]],
mutation_scale=10,lw=2, arrowstyle="-|>"
,color = self.cmap( prob(c,t,l)[i] )
))
)
# A-mixed state Bloch vector
self.arrows1.append(
self.ax1.add_artist(Arrow3D([0,M[0,0]+M[0,1]],[0,M[1,0]+M[1,1]],[0,M[2,0]+M[2,1]],
mutation_scale=10,lw=2, arrowstyle="-|>", color = 'blue' ))
)
# Parallelogram of mixture (works, but is perhaps confusing)
#self.arrows1.append(
# self.ax1.add_artist(Arrow3D([M[0,0],M[0,0]+M[0,1]],[M[1,0],M[1,0]+M[1,1]],[M[2,0],M[2,0]+M[2,1]],
# mutation_scale=10,lw=1, arrowstyle="-"
# ,color = self.cmap( prob(c,t,l)[1] ) ))
#)
#self.arrows1.append(
# self.ax1.add_artist(Arrow3D([M[0,1],M[0,0]+M[0,1]],[M[1,1],M[1,0]+M[1,1]],[M[2,1],M[2,0]+M[2,1]],
# mutation_scale=10,lw=1, arrowstyle="-"
# ,color = self.cmap( prob(c,t,l)[0] ) ))
#)
# Interactive stuff
def on_press(self, event):
'Mouse button press event: check if this should start dragging'
if event.inaxes != self.ax2:
return
self.drag_pointer = 1
def on_release(self, event):
'Stop dragging'
self.drag_pointer = 0
def drag(self, event):
'Drag right panel (B)'
if self.drag_pointer == 1:
# Map mouse positions (x,y) in [0,1]x[0,1] to (\phi,\theta) angles
x,y = self.ax2.transLimits.transform((event.xdata,event.ydata))
# Make-shift parameterization ...
self.slider_c.set_val((x-0.5)*1.25*np.pi)
self.slider_t.set_val((0.75-y)*2*np.pi)
def run(self):
'Start the animation.'
self.update()
plt.show()
def update(self, *args):
'Read values from the sliders and update the plots'
c = self.slider_c.val
t = self.slider_t.val
l = self.slider_l.val
self.update_plots(c,t, l)
self.fig.canvas.draw_idle()
if __name__ == '__main__':
Steering().run()
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment