def plot_state_qsphere( state, figsize=None, ax=None, show_state_labels=True, show_state_phases=False, use_degrees=False, *, rho=None, ): """Plot the qsphere representation of a quantum state. Here, the size of the points is proportional to the probability of the corresponding term in the state and the color represents the phase. Args: state (Statevector or DensityMatrix or ndarray): an N-qubit quantum state. figsize (tuple): Figure size in inches. ax (matplotlib.axes.Axes): An optional Axes object to be used for the visualization output. If none is specified a new matplotlib Figure will be created and used. Additionally, if specified there will be no returned Figure since it is redundant. show_state_labels (bool): An optional boolean indicating whether to show labels for each basis state. show_state_phases (bool): An optional boolean indicating whether to show the phase for each basis state. use_degrees (bool): An optional boolean indicating whether to use radians or degrees for the phase values in the plot. Returns: Figure: A matplotlib figure instance if the ``ax`` kwarg is not set Raises: MissingOptionalLibraryError: Requires matplotlib. VisualizationError: if input is not a valid N-qubit state. QiskitError: Input statevector does not have valid dimensions. Example: .. jupyter-execute:: from qiskit import QuantumCircuit from qiskit.quantum_info import Statevector from qiskit.visualization import plot_state_qsphere %matplotlib inline qc = QuantumCircuit(2) qc.h(0) qc.cx(0, 1) state = Statevector.from_instruction(qc) plot_state_qsphere(state) """ if not HAS_MATPLOTLIB: raise MissingOptionalLibraryError( libname="Matplotlib", name="plot_state_qsphere", pip_install="pip install matplotlib", ) import matplotlib.gridspec as gridspec from matplotlib import pyplot as plt from matplotlib.patches import Circle from matplotlib import get_backend from qiskit.visualization.bloch import Arrow3D try: import seaborn as sns except ImportError as ex: raise MissingOptionalLibraryError( libname="seaborn", name="plot_state_qsphere", pip_install="pip install seaborn", ) from ex rho = DensityMatrix(state) num = rho.num_qubits if num is None: raise VisualizationError("Input is not a multi-qubit quantum state.") # get the eigenvectors and eigenvalues eigvals, eigvecs = linalg.eigh(rho.data) if figsize is None: figsize = (7, 7) if ax is None: return_fig = True fig = plt.figure(figsize=figsize) else: return_fig = False fig = ax.get_figure() gs = gridspec.GridSpec(nrows=3, ncols=3) ax = fig.add_subplot(gs[0:3, 0:3], projection="3d") ax.axes.set_xlim3d(-1.0, 1.0) ax.axes.set_ylim3d(-1.0, 1.0) ax.axes.set_zlim3d(-1.0, 1.0) ax.axes.grid(False) ax.view_init(elev=5, azim=275) # Force aspect ratio # MPL 3.2 or previous do not have set_box_aspect if hasattr(ax.axes, "set_box_aspect"): ax.axes.set_box_aspect((1, 1, 1)) # start the plotting # Plot semi-transparent sphere u = np.linspace(0, 2 * np.pi, 25) v = np.linspace(0, np.pi, 25) x = np.outer(np.cos(u), np.sin(v)) y = np.outer(np.sin(u), np.sin(v)) z = np.outer(np.ones(np.size(u)), np.cos(v)) ax.plot_surface(x, y, z, rstride=1, cstride=1, color=plt.rcParams["grid.color"], alpha=0.2, linewidth=0) # Get rid of the panes ax.w_xaxis.set_pane_color((1.0, 1.0, 1.0, 0.0)) ax.w_yaxis.set_pane_color((1.0, 1.0, 1.0, 0.0)) ax.w_zaxis.set_pane_color((1.0, 1.0, 1.0, 0.0)) # Get rid of the spines ax.w_xaxis.line.set_color((1.0, 1.0, 1.0, 0.0)) ax.w_yaxis.line.set_color((1.0, 1.0, 1.0, 0.0)) ax.w_zaxis.line.set_color((1.0, 1.0, 1.0, 0.0)) # Get rid of the ticks ax.set_xticks([]) ax.set_yticks([]) ax.set_zticks([]) # traversing the eigvals/vecs backward as sorted low->high for idx in range(eigvals.shape[0] - 1, -1, -1): if eigvals[idx] > 0.001: # get the max eigenvalue state = eigvecs[:, idx] loc = np.absolute(state).argmax() # remove the global phase from max element angles = (np.angle(state[loc]) + 2 * np.pi) % (2 * np.pi) angleset = np.exp(-1j * angles) state = angleset * state d = num for i in range(2**num): # get x,y,z points element = bin(i)[2:].zfill(num) weight = element.count("1") zvalue = -2 * weight / d + 1 number_of_divisions = n_choose_k(d, weight) weight_order = bit_string_index(element) angle = (float(weight) / d) * (np.pi * 2) + ( weight_order * 2 * (np.pi / number_of_divisions)) if (weight > d / 2) or ( (weight == d / 2) and (weight_order >= number_of_divisions / 2)): angle = np.pi - angle - (2 * np.pi / number_of_divisions) xvalue = np.sqrt(1 - zvalue**2) * np.cos(angle) yvalue = np.sqrt(1 - zvalue**2) * np.sin(angle) # get prob and angle - prob will be shade and angle color prob = np.real(np.dot(state[i], state[i].conj())) if prob > 1: # See https://github.com/Qiskit/qiskit-terra/issues/4666 prob = 1 colorstate = phase_to_rgb(state[i]) alfa = 1 if yvalue >= 0.1: alfa = 1.0 - yvalue if not np.isclose(prob, 0) and show_state_labels: rprime = 1.3 angle_theta = np.arctan2(np.sqrt(1 - zvalue**2), zvalue) xvalue_text = rprime * np.sin(angle_theta) * np.cos(angle) yvalue_text = rprime * np.sin(angle_theta) * np.sin(angle) zvalue_text = rprime * np.cos(angle_theta) element_text = "$\\vert" + element + "\\rangle$" if show_state_phases: element_angle = (np.angle(state[i]) + (np.pi * 4)) % (np.pi * 2) if use_degrees: element_text += "\n$%.1f^\\circ$" % ( element_angle * 180 / np.pi) else: element_angle = pi_check(element_angle, ndigits=3).replace( "pi", "\\pi") element_text += "\n$%s$" % (element_angle) ax.text( xvalue_text, yvalue_text, zvalue_text, element_text, ha="center", va="center", size=12, ) ax.plot( [xvalue], [yvalue], [zvalue], markerfacecolor=colorstate, markeredgecolor=colorstate, marker="o", markersize=np.sqrt(prob) * 30, alpha=alfa, ) a = Arrow3D( [0, xvalue], [0, yvalue], [0, zvalue], mutation_scale=20, alpha=prob, arrowstyle="-", color=colorstate, lw=2, ) ax.add_artist(a) # add weight lines for weight in range(d + 1): theta = np.linspace(-2 * np.pi, 2 * np.pi, 100) z = -2 * weight / d + 1 r = np.sqrt(1 - z**2) x = r * np.cos(theta) y = r * np.sin(theta) ax.plot(x, y, z, color=(0.5, 0.5, 0.5), lw=1, ls=":", alpha=0.5) # add center point ax.plot( [0], [0], [0], markerfacecolor=(0.5, 0.5, 0.5), markeredgecolor=(0.5, 0.5, 0.5), marker="o", markersize=3, alpha=1, ) else: break n = 64 theta = np.ones(n) colors = sns.hls_palette(n) ax2 = fig.add_subplot(gs[2:, 2:]) ax2.pie(theta, colors=colors[5 * n // 8:] + colors[:5 * n // 8], radius=0.75) ax2.add_artist(Circle((0, 0), 0.5, color="white", zorder=1)) offset = 0.95 # since radius of sphere is one. if use_degrees: labels = ["Phase\n(Deg)", "0", "90", "180 ", "270"] else: labels = ["Phase", "$0$", "$\\pi/2$", "$\\pi$", "$3\\pi/2$"] ax2.text(0, 0, labels[0], horizontalalignment="center", verticalalignment="center", fontsize=14) ax2.text(offset, 0, labels[1], horizontalalignment="center", verticalalignment="center", fontsize=14) ax2.text(0, offset, labels[2], horizontalalignment="center", verticalalignment="center", fontsize=14) ax2.text(-offset, 0, labels[3], horizontalalignment="center", verticalalignment="center", fontsize=14) ax2.text(0, -offset, labels[4], horizontalalignment="center", verticalalignment="center", fontsize=14) if return_fig: if get_backend() in [ "module://ipykernel.pylab.backend_inline", "nbAgg" ]: plt.close(fig) return fig
def plot_state_city(state, title="", figsize=None, color=None, alpha=1, ax_real=None, ax_imag=None, *, rho=None): """Plot the cityscape of quantum state. Plot two 3d bar graphs (two dimensional) of the real and imaginary part of the density matrix rho. Args: state (Statevector or DensityMatrix or ndarray): an N-qubit quantum state. title (str): a string that represents the plot title figsize (tuple): Figure size in inches. color (list): A list of len=2 giving colors for real and imaginary components of matrix elements. alpha (float): Transparency value for bars ax_real (matplotlib.axes.Axes): An optional Axes object to be used for the visualization output. If none is specified a new matplotlib Figure will be created and used. If this is specified without an ax_imag only the real component plot will be generated. Additionally, if specified there will be no returned Figure since it is redundant. ax_imag (matplotlib.axes.Axes): An optional Axes object to be used for the visualization output. If none is specified a new matplotlib Figure will be created and used. If this is specified without an ax_real only the imaginary component plot will be generated. Additionally, if specified there will be no returned Figure since it is redundant. Returns: matplotlib.Figure: The matplotlib.Figure of the visualization if the ``ax_real`` and ``ax_imag`` kwargs are not set Raises: MissingOptionalLibraryError: Requires matplotlib. ValueError: When 'color' is not a list of len=2. VisualizationError: if input is not a valid N-qubit state. Example: .. jupyter-execute:: from qiskit import QuantumCircuit from qiskit.quantum_info import DensityMatrix from qiskit.visualization import plot_state_city %matplotlib inline qc = QuantumCircuit(2) qc.h(0) qc.cx(0, 1) state = DensityMatrix.from_instruction(qc) plot_state_city(state, color=['midnightblue', 'midnightblue'], title="New State City") """ if not HAS_MATPLOTLIB: raise MissingOptionalLibraryError( libname="Matplotlib", name="plot_state_city", pip_install="pip install matplotlib", ) from matplotlib import get_backend from matplotlib import pyplot as plt from mpl_toolkits.mplot3d.art3d import Poly3DCollection rho = DensityMatrix(state) num = rho.num_qubits if num is None: raise VisualizationError("Input is not a multi-qubit quantum state.") # get the real and imag parts of rho datareal = np.real(rho.data) dataimag = np.imag(rho.data) # get the labels column_names = [bin(i)[2:].zfill(num) for i in range(2**num)] row_names = [bin(i)[2:].zfill(num) for i in range(2**num)] lx = len(datareal[0]) # Work out matrix dimensions ly = len(datareal[:, 0]) xpos = np.arange(0, lx, 1) # Set up a mesh of positions ypos = np.arange(0, ly, 1) xpos, ypos = np.meshgrid(xpos + 0.25, ypos + 0.25) xpos = xpos.flatten() ypos = ypos.flatten() zpos = np.zeros(lx * ly) dx = 0.5 * np.ones_like(zpos) # width of bars dy = dx.copy() dzr = datareal.flatten() dzi = dataimag.flatten() if color is None: color = ["#648fff", "#648fff"] else: if len(color) != 2: raise ValueError("'color' must be a list of len=2.") if color[0] is None: color[0] = "#648fff" if color[1] is None: color[1] = "#648fff" if ax_real is None and ax_imag is None: # set default figure size if figsize is None: figsize = (15, 5) fig = plt.figure(figsize=figsize) ax1 = fig.add_subplot(1, 2, 1, projection="3d") ax2 = fig.add_subplot(1, 2, 2, projection="3d") elif ax_real is not None: fig = ax_real.get_figure() ax1 = ax_real ax2 = ax_imag else: fig = ax_imag.get_figure() ax1 = None ax2 = ax_imag max_dzr = max(dzr) min_dzr = min(dzr) min_dzi = np.min(dzi) max_dzi = np.max(dzi) if ax1 is not None: fc1 = generate_facecolors(xpos, ypos, zpos, dx, dy, dzr, color[0]) for idx, cur_zpos in enumerate(zpos): if dzr[idx] > 0: zorder = 2 else: zorder = 0 b1 = ax1.bar3d( xpos[idx], ypos[idx], cur_zpos, dx[idx], dy[idx], dzr[idx], alpha=alpha, zorder=zorder, ) b1.set_facecolors(fc1[6 * idx:6 * idx + 6]) xlim, ylim = ax1.get_xlim(), ax1.get_ylim() x = [xlim[0], xlim[1], xlim[1], xlim[0]] y = [ylim[0], ylim[0], ylim[1], ylim[1]] z = [0, 0, 0, 0] verts = [list(zip(x, y, z))] pc1 = Poly3DCollection(verts, alpha=0.15, facecolor="k", linewidths=1, zorder=1) if min(dzr) < 0 < max(dzr): ax1.add_collection3d(pc1) ax1.set_xticks(np.arange(0.5, lx + 0.5, 1)) ax1.set_yticks(np.arange(0.5, ly + 0.5, 1)) if max_dzr != min_dzr: ax1.axes.set_zlim3d(np.min(dzr), max(np.max(dzr) + 1e-9, max_dzi)) else: if min_dzr == 0: ax1.axes.set_zlim3d(np.min(dzr), max(np.max(dzr) + 1e-9, np.max(dzi))) else: ax1.axes.set_zlim3d(auto=True) ax1.get_autoscalez_on() ax1.w_xaxis.set_ticklabels(row_names, fontsize=14, rotation=45, ha="right", va="top") ax1.w_yaxis.set_ticklabels(column_names, fontsize=14, rotation=-22.5, ha="left", va="center") ax1.set_zlabel("Re[$\\rho$]", fontsize=14) for tick in ax1.zaxis.get_major_ticks(): tick.label.set_fontsize(14) if ax2 is not None: fc2 = generate_facecolors(xpos, ypos, zpos, dx, dy, dzi, color[1]) for idx, cur_zpos in enumerate(zpos): if dzi[idx] > 0: zorder = 2 else: zorder = 0 b2 = ax2.bar3d( xpos[idx], ypos[idx], cur_zpos, dx[idx], dy[idx], dzi[idx], alpha=alpha, zorder=zorder, ) b2.set_facecolors(fc2[6 * idx:6 * idx + 6]) xlim, ylim = ax2.get_xlim(), ax2.get_ylim() x = [xlim[0], xlim[1], xlim[1], xlim[0]] y = [ylim[0], ylim[0], ylim[1], ylim[1]] z = [0, 0, 0, 0] verts = [list(zip(x, y, z))] pc2 = Poly3DCollection(verts, alpha=0.2, facecolor="k", linewidths=1, zorder=1) if min(dzi) < 0 < max(dzi): ax2.add_collection3d(pc2) ax2.set_xticks(np.arange(0.5, lx + 0.5, 1)) ax2.set_yticks(np.arange(0.5, ly + 0.5, 1)) if min_dzi != max_dzi: eps = 0 ax2.axes.set_zlim3d(np.min(dzi), max(np.max(dzr) + 1e-9, np.max(dzi) + eps)) else: if min_dzi == 0: ax2.set_zticks([0]) eps = 1e-9 ax2.axes.set_zlim3d(np.min(dzi), max(np.max(dzr) + 1e-9, np.max(dzi) + eps)) else: ax2.axes.set_zlim3d(auto=True) ax2.w_xaxis.set_ticklabels(row_names, fontsize=14, rotation=45, ha="right", va="top") ax2.w_yaxis.set_ticklabels(column_names, fontsize=14, rotation=-22.5, ha="left", va="center") ax2.set_zlabel("Im[$\\rho$]", fontsize=14) for tick in ax2.zaxis.get_major_ticks(): tick.label.set_fontsize(14) ax2.get_autoscalez_on() fig.suptitle(title, fontsize=16) if ax_real is None and ax_imag is None: if get_backend() in [ "module://ipykernel.pylab.backend_inline", "nbAgg" ]: plt.close(fig) return fig
def plot_state_hinton(state, title="", figsize=None, ax_real=None, ax_imag=None, *, rho=None): """Plot a hinton diagram for the density matrix of a quantum state. Args: state (Statevector or DensityMatrix or ndarray): An N-qubit quantum state. title (str): a string that represents the plot title figsize (tuple): Figure size in inches. ax_real (matplotlib.axes.Axes): An optional Axes object to be used for the visualization output. If none is specified a new matplotlib Figure will be created and used. If this is specified without an ax_imag only the real component plot will be generated. Additionally, if specified there will be no returned Figure since it is redundant. ax_imag (matplotlib.axes.Axes): An optional Axes object to be used for the visualization output. If none is specified a new matplotlib Figure will be created and used. If this is specified without an ax_imag only the real component plot will be generated. Additionally, if specified there will be no returned Figure since it is redundant. Returns: matplotlib.Figure: The matplotlib.Figure of the visualization if neither ax_real or ax_imag is set. Raises: MissingOptionalLibraryError: Requires matplotlib. VisualizationError: if input is not a valid N-qubit state. Example: .. jupyter-execute:: from qiskit import QuantumCircuit from qiskit.quantum_info import DensityMatrix from qiskit.visualization import plot_state_hinton %matplotlib inline qc = QuantumCircuit(2) qc.h(0) qc.cx(0, 1) state = DensityMatrix.from_instruction(qc) plot_state_hinton(state, title="New Hinton Plot") """ if not HAS_MATPLOTLIB: raise MissingOptionalLibraryError( libname="Matplotlib", name="plot_state_hinton", pip_install="pip install matplotlib", ) from matplotlib import pyplot as plt from matplotlib import get_backend # Figure data rho = DensityMatrix(state) num = rho.num_qubits if num is None: raise VisualizationError("Input is not a multi-qubit quantum state.") max_weight = 2**np.ceil(np.log(np.abs(rho.data).max()) / np.log(2)) datareal = np.real(rho.data) dataimag = np.imag(rho.data) if figsize is None: figsize = (8, 5) if not ax_real and not ax_imag: fig, (ax1, ax2) = plt.subplots(1, 2, figsize=figsize) else: if ax_real: fig = ax_real.get_figure() else: fig = ax_imag.get_figure() ax1 = ax_real ax2 = ax_imag column_names = [bin(i)[2:].zfill(num) for i in range(2**num)] row_names = [bin(i)[2:].zfill(num) for i in range(2**num)] ly, lx = datareal.shape # Real if ax1: ax1.patch.set_facecolor("gray") ax1.set_aspect("equal", "box") ax1.xaxis.set_major_locator(plt.NullLocator()) ax1.yaxis.set_major_locator(plt.NullLocator()) for (x, y), w in np.ndenumerate(datareal): color = "white" if w > 0 else "black" size = np.sqrt(np.abs(w) / max_weight) rect = plt.Rectangle( [0.5 + x - size / 2, 0.5 + y - size / 2], size, size, facecolor=color, edgecolor=color, ) ax1.add_patch(rect) ax1.set_xticks(0.5 + np.arange(lx)) ax1.set_yticks(0.5 + np.arange(ly)) ax1.set_xlim([0, lx]) ax1.set_ylim([ly, 0]) ax1.set_yticklabels(row_names, fontsize=14) ax1.set_xticklabels(column_names, fontsize=14, rotation=90) ax1.invert_yaxis() ax1.set_title("Re[$\\rho$]", fontsize=14) # Imaginary if ax2: ax2.patch.set_facecolor("gray") ax2.set_aspect("equal", "box") ax2.xaxis.set_major_locator(plt.NullLocator()) ax2.yaxis.set_major_locator(plt.NullLocator()) for (x, y), w in np.ndenumerate(dataimag): color = "white" if w > 0 else "black" size = np.sqrt(np.abs(w) / max_weight) rect = plt.Rectangle( [0.5 + x - size / 2, 0.5 + y - size / 2], size, size, facecolor=color, edgecolor=color, ) ax2.add_patch(rect) ax2.set_xticks(0.5 + np.arange(lx)) ax2.set_yticks(0.5 + np.arange(ly)) ax1.set_xlim([0, lx]) ax1.set_ylim([ly, 0]) ax2.set_yticklabels(row_names, fontsize=14) ax2.set_xticklabels(column_names, fontsize=14, rotation=90) ax2.invert_yaxis() ax2.set_title("Im[$\\rho$]", fontsize=14) if title: fig.suptitle(title, fontsize=16) if ax_real is None and ax_imag is None: if get_backend() in [ "module://ipykernel.pylab.backend_inline", "nbAgg" ]: plt.close(fig) return fig
def process_fidelity(channel, target=None, require_cp=True, require_tp=False): r"""Return the process fidelity of a noisy quantum channel. The process fidelity :math:`F_{\text{pro}}(\mathcal{E}, \methcal{F})` between two quantum channels :math:`\mathcal{E}, \mathcal{F}` is given by .. math: F_{\text{pro}}(\mathcal{E}, \mathcal{F}) = F(\rho_{\mathcal{E}}, \rho_{\mathcal{F}}) where :math:`F` is the :func:`~qiskit.quantum_info.state_fidelity`, :math:`\rho_{\mathcal{E}} = \Lambda_{\mathcal{E}} / d` is the normalized :class:`~qiskit.quantum_info.Choi` matrix for the channel :math:`\mathcal{E}`, and :math:`d` is the input dimension of :math:`\mathcal{E}`. When the target channel is unitary this is equivalent to .. math:: F_{\text{pro}}(\mathcal{E}, U) = \frac{Tr[S_U^\dagger S_{\mathcal{E}}]}{d^2} where :math:`S_{\mathcal{E}}, S_{U}` are the :class:`~qiskit.quantum_info.SuperOp` matrices for the *input* quantum channel :math:`\mathcal{E}` and *target* unitary :math:`U` respectively, and :math:`d` is the input dimension of the channel. Args: channel (Operator or QuantumChannel): input quantum channel. target (Operator or QuantumChannel or None): target quantum channel. If `None` target is the identity operator [Default: None]. require_cp (bool): require channel to be completely-positive [Default: True]. require_tp (bool): require channel to be trace-preserving [Default: False]. Returns: float: The process fidelity :math:`F_{\text{pro}}`. Raises: QiskitError: if the channel and target do not have the same dimensions. QiskitError: if the channel and target are not completely-positive (with ``require_cp=True``) or not trace-preserving (with ``require_tp=True``). """ # Format inputs channel = _input_formatter(channel, SuperOp, 'process_fidelity', 'channel') target = _input_formatter(target, Operator, 'process_fidelity', 'target') if target: # Validate dimensions if channel.dim != target.dim: raise QiskitError( 'Input quantum channel and target unitary must have the same ' 'dimensions ({} != {}).'.format(channel.dim, target.dim)) # Validate complete-positivity and trace-preserving for label, chan in [('Input', channel), ('Target', target)]: if isinstance(chan, Operator) and (require_cp or require_tp): is_unitary = chan.is_unitary() # Validate as unitary if require_cp and not is_unitary: raise QiskitError( '{} channel is not completely-positive'.format(label)) if require_tp and not is_unitary: raise QiskitError( '{} channel is not trace-preserving'.format(label)) elif chan is not None: # Validate as QuantumChannel if require_cp and not chan.is_cp(): raise QiskitError( '{} channel is not completely-positive'.format(label)) if require_tp and not chan.is_tp(): raise QiskitError( '{} channel is not trace-preserving'.format(label)) if isinstance(target, Operator): # Compute fidelity with unitary target by applying the inverse # to channel and computing fidelity with the identity channel = channel @ target.adjoint() target = None input_dim, _ = channel.dim if target is None: # Compute process fidelity with identity channel if isinstance(channel, Operator): # |Tr[U]/dim| ** 2 fid = np.abs(np.trace(channel.data) / input_dim)**2 else: # Tr[S] / (dim ** 2) fid = np.trace(SuperOp(channel).data) / (input_dim**2) return float(np.real(fid)) # For comparing two non-unitary channels we compute the state fidelity of # the normalized Choi-matrices. This is equivalent to the previous definition # when the target is a unitary channel. state1 = DensityMatrix(Choi(channel).data / input_dim) state2 = DensityMatrix(Choi(target).data / input_dim) return state_fidelity(state1, state2, validate=False)