def test_bloch_components(): """Tests the Bloch components function""" # |0> state state = np.array([1, 0], dtype=complex) comp = bloch_components(state)[0] assert np.allclose(comp, [0, 0, 1]) # |1> state state = np.array([0, 1], dtype=complex) comp = bloch_components(state)[0] assert np.allclose(comp, [0, 0, -1]) # (|00> + |11>)/sqrt(2) state = np.array([1/np.sqrt(2), 0, 0, 1/np.sqrt(2)], dtype=complex) comp = bloch_components(state) assert np.allclose(comp[0], [0.0, 0.0, 0.0]) assert np.allclose(comp[1], [0.0, 0.0, 0.0])
def bloch_disc(rho, figsize=None, title=None, as_widget=False): """Plot a Bloch disc for a single qubit. Parameters: rho (list or ndarray or Statevector or DensityMatrix): Input statevector, density matrix, or Bloch components. figsize (tuple): Figure size in pixels, default=(200,275). title (str): Plot title. as_widget (bool): Return plot as a widget. Returns: PlotlyFigure: A Plotly figure instance PlotlyWidget : A Plotly widget if `as_widget=True`. Example: .. jupyter-execute:: import numpy as np from qiskit import * from qiskit.quantum_info import Statevector from kaleidoscope.interactive import bloch_disc qc = QuantumCircuit(1) qc.ry(np.pi*np.random.random(), 0) qc.rz(np.pi*np.random.random(), 0) state = Statevector.from_instruction(qc) bloch_disc(state) """ # A hack so I do not have to import the actual instances from Qiskit. if rho.__class__.__name__ in ['Statevector', 'DensityMatrix'] \ and 'qiskit' in rho.__class__.__module__: rho = rho.data if len(rho) != 3: rho = np.asarray(rho, dtype=complex) comp = bloch_components(rho) else: comp = [rho] if title: title = [title] + ["\u2329Z\u232A"] else: title = [""] + ["\u2329Z\u232A"] if figsize is None: figsize = (200, 275) fig = make_subplots(rows=1, cols=2, specs=[[{'type': 'domain'}]+[{'type': 'xy'}]], subplot_titles=title, column_widths=[0.93]+[0.07]) fig.add_trace(bloch_sunburst(comp[0]), row=1, col=1) zval = comp[0][2] zrange = [k*np.ones(1) for k in np.linspace(-1, 1, 100)] idx = (np.abs(np.linspace(-1, 1, 100) - zval)).argmin() tickvals = np.array([0, 49, 99, idx]) idx_sort = np.argsort(tickvals) tickvals = tickvals[idx_sort] ticktext = [-1, 0, 1, "\u25C0"+str(np.round(zval, 3))] if zval <= -0.95: ticktext[0] = '' elif abs(zval) <= 0.05: ticktext[1] = '' elif zval >= 0.95: ticktext[2] = '' ticktext = [ticktext[kk] for kk in idx_sort] fig.append_trace(go.Heatmap(z=zrange, colorscale=BMY_PLOTLY, showscale=False, hoverinfo='none', ), row=1, col=2 ) fig.update_yaxes(row=1, col=2, tickvals=tickvals, ticktext=ticktext) fig.update_yaxes(row=1, col=2, side="right") fig.update_xaxes(row=1, col=2, visible=False) fig.update_layout(margin=dict(t=30, l=10, r=0, b=0), height=figsize[0], width=figsize[1], hoverlabel=dict(font_size=16, font_family="courier,monospace", align='left' ) ) for ann in fig['layout']['annotations']: ann['font'] = dict(size=14) if as_widget: return PlotlyWidget(fig) return PlotlyFigure(fig)
def bloch_sphere(vectors=None, vectors_color=None, vectors_alpha=None, vectors_annotation=False, points=None, points_color=None, points_alpha=None, figsize=(350, 350), label_fontsize=16, annotation_fontsize=10, as_widget=False): """Generates a Bloch sphere from a given collection of vector and/or points data expressed in cartesian coordinates, [x, y, z]. Parameters: vectors (list, ndarray): Collection of one or more vectors to display. vectors_color (str or list): List of colors to use when plotting vectors. vectors_alpha (float or list): List of alphas to use when plotting vectors. vectors_annotation (bool or list): Boolean values to determine if a annotation should be displayed. points (list, ndarray): Collection of one or more points to display. points_color (str or list): List of colors to use when plotting points. points_alpha (float or list): List of alphas to use when plotting points. figsize (tuple): Figure size in pixels. label_fontsize (int): Font size for axes labels. annotation_fontsize (int): Font size for annotations. as_widget (bool): Return plot as a widget. Returns: PlotlyFigure or PlotlyWidget: A Plotly figure or widget instance Raises: ValueError: Input lengths do not match. KaleidoscopeError: Invalid vector input. Example: .. jupyter-execute:: import numpy as np from matplotlib.colors import LinearSegmentedColormap, rgb2hex from kaleidoscope.interactive import bloch_sphere cm = LinearSegmentedColormap.from_list('graypurple', ["#999999", "#AA00FF"]) pointsx = [[0, -np.sin(th), np.cos(th)] for th in np.linspace(0, np.pi/2, 20)] pointsz = [[np.sin(th), -np.cos(th), 0] for th in np.linspace(0, 3*np.pi/4, 30)] points = pointsx + pointsz points_alpha = [np.linspace(0.8, 1, len(points))] points_color = [[rgb2hex(cm(kk)) for kk in np.linspace(-1,1,len(points))]] vectors_color = ["#777777", "#AA00FF"] bloch_sphere(points=points, vectors=[[0, 0, 1], [1/np.sqrt(2), 1/np.sqrt(2), 0]], vectors_color=vectors_color, points_alpha=points_alpha, points_color=points_color) """ # Output figure instance fig = go.Figure() # List for vector annotations, if any fig_annotations = [] idx = 0 if points is not None: nest_depth = nest_level(points) # Take care of single point passed if nest_depth == 1: points = [[points]] # A single list of points passes elif nest_depth == 2: points = [points] # nest_depth = 3 means multiple lists passed if points_color is None: # passed a single point if nest_depth == 1: points_color = [DARK2[0]] elif nest_depth == 2: points_color = [[ DARK2[kk % 8] for kk in range(len(points[0])) ]] elif nest_depth == 3: points_color = [] for kk, pnts in enumerate(points): points_color.append(DARK2[kk % 8] * len(pnts)) if nest_depth == 2 and nest_level(points_color) == 1: points_color = [points_color] if isinstance(points_color, str): points_color = [points_color] if points_alpha is None: points_alpha = [[1.0] * len(p) for p in points] if nest_depth == 2 and nest_level(points_alpha) == 1: points_alpha = [points_alpha] if isinstance(points_alpha, (int, float)): points_alpha = [[points_alpha]] for idx, point_collection in enumerate(points): x_pnts = [] y_pnts = [] z_pnts = [] if isinstance(points_color[idx], str): _colors = [points_color[idx]] * len(point_collection) else: _colors = points_color[idx] if len(points_alpha[idx]) != len(point_collection): err_str = 'number of alpha values ({}) does not equal number of points ({})' raise ValueError( err_str.format(len(points_alpha[idx]), len(x_pnts))) mcolors = [] for kk, point in enumerate(point_collection): x_pnts.append(point[0]) y_pnts.append(point[1]) z_pnts.append(point[2]) mcolors.append("rgba({},{},{},{})".format( *hex_to_rgb(_colors[kk]), points_alpha[idx][kk])) fig.add_trace( go.Scatter3d( x=x_pnts, y=y_pnts, z=z_pnts, mode='markers', marker=dict(size=7, color=mcolors), )) idx += 1 if vectors is not None: if vectors.__class__.__name__ in ['Statevector'] \ and 'qiskit' in vectors.__class__.__module__: vectors = bloch_components(vectors.data) elif not isinstance(vectors[0], (list, np.ndarray)): if vectors[0].__class__.__name__ not in ['Statevector']: vectors = [[vectors[0], vectors[1], vectors[2]]] new_vecs = [] for vec in vectors: if vec.__class__.__name__ in [ 'Statevector' ] and 'qiskit' in vec.__class__.__module__: # pylint: disable=no-member new_vecs.append(bloch_components(vec.data)[0]) else: nst_lvl = nest_level(vec) if nst_lvl == 1: new_vecs.append(vec) elif nst_lvl == 2: new_vecs.append(vec[0]) else: raise KaleidoscopeError("Invalid vector input.") if vectors_color is None: vectors_color = [ DARK2[kk + idx % 8] for kk in range(len(new_vecs)) ] if isinstance(vectors_color, str): vectors_color = [vectors_color] if vectors_alpha is None: vectors_alpha = [1.0] * len(new_vecs) if isinstance(vectors_alpha, (int, float)): vectors_alpha = [vectors_alpha] if vectors_annotation is True: vectors_annotation = [True] * len(new_vecs) elif not vectors_annotation: vectors_annotation = [False] * len(new_vecs) eps = 1e-12 for idx, vec in enumerate(new_vecs): vec = np.asarray(vec) if np.linalg.norm(vec) > 1.0 + eps: raise ValueError('Vector norm must be <= 1.') # So that line does not go out of arrow head vec_line = vec / 1.05 color_str = "rgba({},{},{},{})".format( *hex_to_rgb(vectors_color[idx]), vectors_alpha[idx]) fig.add_trace( go.Scatter3d(x=[0, vec_line[0]], y=[0, vec_line[1]], z=[0, vec_line[2]], mode="lines", hoverinfo=None, line=dict(color=color_str, width=10))) fig.add_trace( go.Cone(x=[vec[0]], y=[vec[1]], z=[vec[2]], u=[vec[0]], v=[vec[1]], w=[vec[2]], sizemode="absolute", showscale=False, opacity=vectors_alpha[idx], colorscale=[vectors_color[idx], vectors_color[idx]], sizeref=0.25, anchor="tip")) if vectors_annotation[idx]: fig_annotations.append( dict( showarrow=False, x=vec[0] * 1.05, y=vec[1] * 1.05, z=vec[2] * 1.05, text="[{},<br> {},<br> {}]".format( round(vec[0], 3), round(vec[1], 3), round(vec[2], 3)), align='left', borderpad=3, xanchor='right' if vec[1] <= 0 else "left", xshift=10, bgcolor="#53565F", font=dict( size=annotation_fontsize, color="#ffffff", family="Courier New, monospace", ), )) # Start construction of sphere # Sphere fig.add_trace(BSPHERE()) # latitudes for kk in LATS: fig.add_trace(kk) # longitudes for kk in LONGS: fig.add_trace(kk) # z-axis fig.add_trace(ZAXIS) # x-axis fig.add_trace(XAXIS) # y-axis fig.add_trace(YAXIS) # zaxis label fig.add_trace(Z0LABEL(fontsize=label_fontsize)) fig.add_trace(Z1LABEL(fontsize=label_fontsize)) # xaxis label fig.add_trace(XLABEL(fontsize=label_fontsize)) # yaxis label fig.add_trace(YLABEL(fontsize=label_fontsize)) fig.update_layout(width=figsize[0], height=figsize[1], autosize=False, hoverdistance=50, showlegend=False, scene_aspectmode='cube', margin=dict(r=0, b=0, l=0, t=0), scene=dict(annotations=fig_annotations, xaxis=dict(showbackground=False, range=[-1.2, 1.2], showspikes=False, visible=False), yaxis=dict(showbackground=False, range=[-1.2, 1.2], showspikes=False, visible=False), zaxis=dict(showbackground=False, range=[-1.2, 1.2], showspikes=False, visible=False)), scene_camera=dict(eye=dict(x=1.5, y=0.4, z=0.4))) if as_widget: return PlotlyWidget(fig) return PlotlyFigure(fig, modebar=True)
def bloch_multi_disc(rho, figsize=None, titles=True, as_widget=False): """Plot Bloch discs for a multi-qubit state. Parameters: rho (list or ndarray or Statevector or DensityMatrix): Input statevector, density matrix. figsize (tuple): Figure size in pixels, default=(125*num_qubits, 150). titles (bool): Display titles. as_widget (bool): Return plot as a widget. Returns: PlotlyFigure: A Plotly figure instance PlotlyWidget : A Plotly widget if `as_widget=True`. Example: .. jupyter-execute:: import numpy as np from qiskit import * from qiskit.quantum_info import Statevector from kaleidoscope.interactive import bloch_multi_disc N = 4 qc = QuantumCircuit(N) qc.h(range(N)) for kk in range(N): qc.ry(2*np.pi*np.random.random(), kk) for kk in range(N-1): qc.cx(kk,kk+1) for kk in range(N): qc.rz(2*np.pi*np.random.random(), kk) state = Statevector.from_instruction(qc) bloch_multi_disc(state) """ # A hack so I do not have to import the actual instances from Qiskit. if rho.__class__.__name__ in ['Statevector', 'DensityMatrix'] \ and 'qiskit' in rho.__class__.__module__: rho = rho.data rho = np.asarray(rho, dtype=complex) comp = bloch_components(rho) num = int(np.log2(rho.shape[0])) nrows = 1 ncols = num if figsize is None: figsize = (ncols*125, 150) if titles: titles = ["Qubit {}".format(k) for k in range(num)] + ["\u2329Z\u232A"] else: titles = ["" for k in range(num)] + ["\u2329Z\u232A"] fig = make_subplots(rows=nrows, cols=ncols+1, specs=[[{'type': 'domain'}]*ncols+[{'type': 'xy'}]], subplot_titles=titles, column_widths=[0.95/num]*num+[0.05]) for jj in range(num): fig.add_trace(bloch_sunburst(comp[jj]), row=1, col=jj+1) zrange = [k*np.ones(1) for k in np.linspace(-1, 1, 100)] fig.append_trace(go.Heatmap(z=zrange, colorscale=BMY_PLOTLY, showscale=False, hoverinfo='none', ), row=1, col=num+1) fig.update_yaxes(row=1, col=num+1, tickvals=[0, 49, 99], ticktext=[-1, 0, 1]) fig.update_yaxes(row=1, col=num+1, side="right") fig.update_xaxes(row=1, col=num+1, visible=False) fig.update_layout(margin=dict(t=50, l=0, r=15, b=30), width=figsize[0], height=figsize[1], hoverlabel=dict(font_size=14, font_family="monospace", align='left' ) ) # Makes the subplot titles smaller than the 16pt default for ann in fig['layout']['annotations']: ann['font'] = dict(size=16) if as_widget: return PlotlyWidget(fig) return PlotlyFigure(fig)