pyqpanda.Visualization.bloch_plot 源代码

try:
    from matplotlib import animation
    import matplotlib.pyplot as plt
    from matplotlib import get_backend
    from mpl_toolkits.mplot3d import Axes3D
    plt.switch_backend('TKAgg')
    HAS_MATPLOTLIB = True
except:
    HAS_MATPLOTLIB = False
    pass

import numpy as np
from numpy import pi
from scipy import sparse
from .quantum_state_plot import state_to_density_matrix
from pyqpanda import circuit_layer
from math import sin, cos, acos, sqrt
from .bloch import Bloch
import matplotlib

[文档] def count_pauli(i): i = i - ((i >> 1) & 0x55555555) i = (i & 0x33333333) + ((i >> 2) & 0x33333333) return (((i + (i >> 4) & 0xF0F0F0F) * 0x1010101) & 0xffffffff) >> 24
[文档] class pauli(object): def __init__(self, z, x): self.z = z self.x = x
[文档] def to_matrix(self): _x, _z = self.x, self.z n = 2**len(_x) twos_array = 1 << np.arange(len(_x)) xs = np.array(_x).dot(twos_array) zs = np.array(_z).dot(twos_array) rows = np.arange(n+1, dtype=np.uint) columns = rows ^ xs global_factor = (-1j)**np.dot(np.array(_x, dtype=np.uint), _z) data = global_factor*(-1)**np.mod(count_pauli(zs & rows), 2) matrix = sparse.csr_matrix((data, columns, rows), shape=(n, n)) return matrix.toarray()
[文档] def get_single_paulis(num_qubits, index): labels = ['X', 'Y', 'Z'] single_pauli = [] for label in labels: z = np.zeros(len(label), dtype=np.bool) x = np.zeros(len(label), dtype=np.bool) for i, char in enumerate(label): if char == 'X': x[-i - 1] = True elif char == 'Z': z[-i - 1] = True elif char == 'Y': z[-i - 1] = True x[-i - 1] = True elif char != 'I': raise RuntimeError("Pauli error") tmp = pauli(z, x) pauli_z = np.zeros(num_qubits, dtype=np.bool) pauli_x = np.zeros(num_qubits, dtype=np.bool) pauli_z[index] = tmp.z[0] pauli_x[index] = tmp.x[0] single_pauli.append(pauli(pauli_z, pauli_x)) return single_pauli
[文档] def plot_bloch_vector(bloch, title="bloch", axis_obj=None, fig_size=None): """Draw a quantum state bloch view Args: state : the quantum state title : the figure title fig_size : the figure size Returns: bloch figure shows quantum state """ if not HAS_MATPLOTLIB: raise ImportError('Must have Matplotlib installed') if fig_size is None: fig_size = (5, 5) bloch_obj = Bloch(axes=axis_obj) bloch_obj.add_vectors(bloch) bloch_obj.render(title=title) if axis_obj is None: fig = bloch_obj.fig fig.set_size_inches(fig_size[0], fig_size[1]) if get_backend() in ['module://ipykernel.pylab.backend_inline', 'nbAgg']: plt .close(fig) plt.show() return fig return None
[文档] def plot_bloch_multivector(state, title='', fig_size=None): """Draw a quantum state bloch view Args: state : the quantum state title : the figure title fig_size : the figure size Returns: bloch figure shows quantum state """ if not HAS_MATPLOTLIB: raise ImportError('Must have Matplotlib installed') # get density matrix from quantum state vector state = state_to_density_matrix(state) print(state) num = int(np.log2(len(state))) width, height = plt.figaspect(1/num) fig = plt.figure(figsize=(width, height)) for qubit in range(num): axis_obj = fig.add_subplot(1, num, qubit + 1, projection='3d') pauli_singles = get_single_paulis(num, qubit) bloch_state = list( map(lambda x: np.real(np.trace(np.dot(x.to_matrix(), state))), pauli_singles)) plot_bloch_vector(bloch_state, "qubit " + str(qubit), axis_obj=axis_obj, fig_size=fig_size) fig.suptitle(title, fontsize=16) if get_backend() in ['module://ipykernel.pylab.backend_inline', 'nbAgg']: plt.close(fig) plt.show() return fig
[文档] def normalize(v, tolerance=0.00001): mag2 = sum(n * n for n in v) if abs(mag2 - 1.0) > tolerance: mag = sqrt(mag2) v = tuple(n / mag for n in v) return np.array(v)
[文档] class gate_node(object): def __init__(self, name, theta): self.name = name self.theta = theta
[文档] class PlotVector: def __init__(self): self._val = None @staticmethod
[文档] def from_axisangle(theta, v): v = normalize(v) new_quaternion = PlotVector() new_quaternion._axisangle_to_q(theta, v) return new_quaternion
@staticmethod
[文档] def from_value(value): new_quaternion = PlotVector() new_quaternion._val = value return new_quaternion
def _axisangle_to_q(self, theta, v): x = v[0] y = v[1] z = v[2] w = cos(theta/2.) x = x * sin(theta/2.) y = y * sin(theta/2.) z = z * sin(theta/2.) self._val = np.array([w, x, y, z]) def __mul__(self, b): if isinstance(b, PlotVector): return self._multiply_with_quaternion(b) elif isinstance(b, (list, tuple, np.ndarray)): if len(b) != 3: raise RuntimeError( "Input vector has invalid length {0}".format(len(b))) return self._multiply_with_vector(b) else: raise RuntimeError( "Multiplication with unknown type {0}".format(type(b))) def _multiply_with_quaternion(self, q_2): w_1, x_1, y_1, z_1 = self._val w_2, x_2, y_2, z_2 = q_2._val w = w_1 * w_2 - x_1 * x_2 - y_1 * y_2 - z_1 * z_2 x = w_1 * x_2 + x_1 * w_2 + y_1 * z_2 - z_1 * y_2 y = w_1 * y_2 + y_1 * w_2 + z_1 * x_2 - x_1 * z_2 z = w_1 * z_2 + z_1 * w_2 + x_1 * y_2 - y_1 * x_2 result = PlotVector.from_value(np.array((w, x, y, z))) return result def _multiply_with_vector(self, v): q_2 = PlotVector.from_value(np.append((0.0), v)) return (self * q_2 * self.get_conjugate())._val[1:]
[文档] def get_conjugate(self): w, x, y, z = self._val result = PlotVector.from_value(np.array((w, -x, -y, -z))) return result
def __repr__(self): theta, v = self.get_axisangle() return "(({0}; {1}, {2}, {3}))".format(theta, v[0], v[1], v[2])
[文档] def get_axisangle(self): w, v = self._val[0], self._val[1:] theta = acos(w) * 2.0 return theta, normalize(v)
[文档] def tolist(self): return self._val.tolist()
[文档] def vector_norm(self): _, v = self.get_axisangle() return np.linalg.norm(v)
[文档] def bloch_plot_dict(frames_per_gate): bloch_dict = dict() bloch_dict['x'] = ('x', PlotVector.from_axisangle( np.pi / frames_per_gate, [1, 0, 0]), '#1a8bbc') bloch_dict['y'] = ('y', PlotVector.from_axisangle( np.pi / frames_per_gate, [0, 1, 0]), '#c02ecc') bloch_dict['z'] = ('z', PlotVector.from_axisangle( np.pi / frames_per_gate, [0, 0, 1]), '#db7734') bloch_dict['s'] = ('s', PlotVector.from_axisangle(np.pi / 2 / frames_per_gate, [0, 0, 1]), '#9b59b6') bloch_dict['sdg'] = ('sdg', PlotVector.from_axisangle(-np.pi / 2 / frames_per_gate, [0, 0, 1]), '#8e44ad') bloch_dict['h'] = ('h', PlotVector.from_axisangle(np.pi / frames_per_gate, normalize([1, 0, 1])), '#a96386') bloch_dict['t'] = ('t', PlotVector.from_axisangle(np.pi / 4 / frames_per_gate, [0, 0, 1]), '#e74c3c') bloch_dict['tdg'] = ('tdg', PlotVector.from_axisangle(-np.pi / 4 / frames_per_gate, [0, 0, 1]), '#c0392b') return bloch_dict
[文档] def traversal_circuit(circuit): origin_gates = ['H', 'X', 'Y', 'Z', 'RX', 'RY', 'RZ', 'S', 'T', 'U1'] layers = circuit_layer(circuit)[0] if 0 == len(layers): raise RuntimeError("empty circuit") def get_layer_qubit_addr(node): return node.m_target_qubits[0].get_phy_addr() trans = 180. / pi cir = [] tar_qubit = layers[0][0].m_target_qubits[0].get_phy_addr() for layer in layers: if get_layer_qubit_addr(layer[0]) != tar_qubit: raise RuntimeError("only one qubit circuits are supported") if layer[0].m_name not in origin_gates: raise RuntimeError("un supported gate".format(gate[0].name)) if layer[0].m_name == 'H': cir.append(gate_node('h', 0)) if layer[0].m_name == 'X': cir.append(gate_node('x', 0)) if layer[0].m_name == 'Y': cir.append(gate_node('y', 0)) if layer[0].m_name == 'Z': cir.append(gate_node('z', 0)) if layer[0].m_name == 'U1': cir.append(gate_node('u1', np.round(layer[0].m_params[0] * trans))) if layer[0].m_name == 'RX': cir.append(gate_node('rx', np.round(layer[0].m_params[0] * trans))) if layer[0].m_name == 'RY': cir.append(gate_node('ry', np.round(layer[0].m_params[0] * trans))) if layer[0].m_name == 'RZ': cir.append(gate_node('rz', np.round(layer[0].m_params[0] * trans))) if layer[0].m_name == 'T' and (1 - layer[0].m_is_dagger): cir.append(gate_node('t', 0)) if layer[0].m_name == 'T' and layer[0].m_is_dagger: cir.append(gate_node('tdg', 0)) if layer[0].m_name == 'S' and (1 - layer[0].m_is_dagger): cir.append(gate_node('s', 0)) if layer[0].m_name == 'S' and layer[0].m_is_dagger: cir.append(gate_node('sdg', 0)) return cir
[文档] def plot_bloch_circuit(circuit, trace=True, saveas=None, fps=20, secs_per_gate=1): """Draw a quantum circuit bloch view , only support one qubit Args: circuit : the quantum circuit trace : whether shows the trace fps : flash fps Returns: bloch figure shows quantum circuit """ if not HAS_MATPLOTLIB: raise ImportError("Must have Matplotlib installed.") frames_per_gate = fps time_between_frames = (secs_per_gate * 1000) / fps plot_cir = traversal_circuit(circuit) plot_dict = bloch_plot_dict(frames_per_gate) simple_gates = ['h', 'x', 'y', 'z', 's', 'sdg', 't', 'tdg'] list_of_circuit_gates = [] for gate in plot_cir: if gate.name in simple_gates: list_of_circuit_gates.append(plot_dict[gate.name]) else: theta = gate.theta rad = np.deg2rad(theta) if gate.name == 'rx': quaternion = PlotVector.from_axisangle( rad / frames_per_gate, [1, 0, 0]) list_of_circuit_gates.append( ('rx:'+str(theta), quaternion, '#f39c12')) elif gate.name == 'ry': quaternion = PlotVector.from_axisangle( rad / frames_per_gate, [0, 1, 0]) list_of_circuit_gates.append( ('ry:'+str(theta), quaternion, '#0c93bf')) elif gate.name == 'rz': quaternion = PlotVector.from_axisangle( rad / frames_per_gate, [0, 0, 1]) list_of_circuit_gates.append( ('rz:'+str(theta), quaternion, '#a06aad')) elif gate.name == 'u1': quaternion = PlotVector.from_axisangle( rad / frames_per_gate, [0, 0, 1]) list_of_circuit_gates.append( ('u1:'+str(theta), quaternion, '#69a45a')) if len(list_of_circuit_gates) == 0: raise RuntimeError("Nothing to visualize.") starting_pos = normalize(np.array([0, 0, 1])) view=[-60,30] fig = plt.figure(figsize=(6, 6)) if tuple(int(x) for x in matplotlib.__version__.split(".")) >= (3, 4, 0): _ax = Axes3D( fig, azim=view[0], elev=view[1], auto_add_to_figure=False ) fig.add_axes(_ax) else: _ax = Axes3D( fig, azim=view[0], elev=view[1], ) sphere = Bloch(axes=_ax) class PlotParams: def __init__(self): self.new_vec = [] self.last_gate = -2 self.colors = [] self.pnts = [] plot_parms = PlotParams() plot_parms.new_vec = starting_pos def plot_flash(i): sphere.clear() gate_counter = (i-1) // frames_per_gate if gate_counter != plot_parms.last_gate: plot_parms.pnts.append([[], [], []]) plot_parms.colors.append(list_of_circuit_gates[gate_counter][2]) if i == 0: sphere.add_vectors(plot_parms.new_vec) plot_parms.pnts[0][0].append(plot_parms.new_vec[0]) plot_parms.pnts[0][1].append(plot_parms.new_vec[1]) plot_parms.pnts[0][2].append(plot_parms.new_vec[2]) plot_parms.colors[0] = 'r' sphere.make_sphere() return _ax plot_parms.new_vec = list_of_circuit_gates[gate_counter][1] * \ plot_parms.new_vec plot_parms.pnts[gate_counter+1][0].append(plot_parms.new_vec[0]) plot_parms.pnts[gate_counter+1][1].append(plot_parms.new_vec[1]) plot_parms.pnts[gate_counter+1][2].append(plot_parms.new_vec[2]) sphere.add_vectors(plot_parms.new_vec) if trace: for point_set in plot_parms.pnts: sphere.add_points([point_set[0], point_set[1], point_set[2]]) sphere.vector_color = [list_of_circuit_gates[gate_counter][2]] sphere.point_color = plot_parms.colors sphere.point_marker = 'o' annotation_text = list_of_circuit_gates[gate_counter][0] annotationvector = [1.4, -0.45, 1.7] sphere.add_annotation(annotationvector, annotation_text, color=list_of_circuit_gates[gate_counter][2], fontsize=30, horizontalalignment='left') sphere.make_sphere() plot_parms.last_gate = gate_counter return _ax def init(): sphere.vector_color = ['r'] return _ax ani = animation.FuncAnimation(fig, plot_flash, range(frames_per_gate * len(list_of_circuit_gates)+1), init_func=init, blit=False, repeat=False, interval=time_between_frames) if saveas: ani.save(saveas, fps=30) plt.show() plt.close(fig) return None