def _define(self): """Calculate a subcircuit that implements this unitary.""" if self.num_qubits == 1: q = QuantumRegister(1, "q") angles = euler_angles_1q(self.to_matrix()) self.definition = [(U3Gate(*angles), [q[0]], [])] elif self.num_qubits == 2: self.definition = two_qubit_cnot_decompose(self.to_matrix()) else: raise NotImplementedError("Not able to generate a subcircuit for " "a {}-qubit unitary".format( self.num_qubits))
def _dec_ucg(self): """ Call to create a circuit that implements the uniformly controlled gate. If up_to_diagonal=True, the circuit implements the gate up to a diagonal gate and the diagonal gate is also returned. """ diag = np.ones(2 ** self.num_qubits).tolist() q = QuantumRegister(self.num_qubits) q_controls = q[1:] q_target = q[0] circuit = QuantumCircuit(q) # If there is no control, we use the ZYZ decomposition if not q_controls: theta, phi, lamb = euler_angles_1q(self.params[0]) circuit.u3(theta, phi, lamb, q) return circuit, diag # if self.up_to_diagonal: # squ = SingleQubitUnitary(self.params[0], mode="ZYZ", up_to_diagonal=True) # circuit.append(squ, [q_target]) # return circuit, squ.get_diag() # else: # squ = SingleQubitUnitary(self.params[0], mode="ZYZ") # circuit.append(squ, [q_target]) # return circuit, diag # If there is at least one control, first, # we find the single qubit gates of the decomposition. (single_qubit_gates, diag) = self._dec_ucg_help() # Now, it is easy to place the C-NOT gates and some Hadamards and Rz(pi/2) gates # (which are absorbed into the single-qubit unitaries) to get back the full decomposition. for i, gate in enumerate(single_qubit_gates): # Absorb Hadamards and Rz(pi/2) gates if i == 0: squ = _h().dot(gate) elif i == len(single_qubit_gates) - 1: squ = gate.dot(_rz(np.pi / 2)).dot(_h()) else: squ = _h().dot(gate.dot(_rz(np.pi / 2))).dot(_h()) # Add single-qubit gate circuit.squ(squ, q_target) # The number of the control qubit is given by the number of zeros at the end # of the binary representation of (i+1) binary_rep = np.binary_repr(i + 1) num_trailing_zeros = len(binary_rep) - len(binary_rep.rstrip('0')) q_contr_index = num_trailing_zeros # Add C-NOT gate if not i == len(single_qubit_gates) - 1: circuit.cx(q_controls[q_contr_index], q_target) if not self.up_to_diagonal: # Important: the diagonal gate is given in the computational basis of the qubits # q[k-1],...,q[0],q_target (ordered with decreasing significance), # where q[i] are the control qubits and t denotes the target qubit. circuit.diag_gate(diag.tolist(), q) return circuit, diag
def check_one_qubit_euler_angles(self, operator, tolerance=1e-14): """Check euler_angles_1q works for the given unitary """ with self.subTest(operator=operator): target_unitary = operator.data angles = euler_angles_1q(target_unitary) decomp_unitary = U3Gate(*angles).to_matrix() target_unitary *= la.det(target_unitary)**(-0.5) decomp_unitary *= la.det(decomp_unitary)**(-0.5) maxdist = np.max(np.abs(target_unitary - decomp_unitary)) if maxdist > 0.1: maxdist = np.max(np.abs(target_unitary + decomp_unitary)) self.assertTrue(np.abs(maxdist) < tolerance, "Worst distance {}".format(maxdist))
def check_one_qubit_euler_angles(self, operator, tolerance=1e-14): """Check euler_angles_1q works for the given unitary """ with self.subTest(operator=operator): target_unitary = operator.data angles = euler_angles_1q(target_unitary) decomp_circuit = QuantumCircuit(1) decomp_circuit.u3(*angles, 0) result = execute(decomp_circuit, UnitarySimulatorPy()).result() decomp_unitary = result.get_unitary() target_unitary *= la.det(target_unitary)**(-0.5) decomp_unitary *= la.det(decomp_unitary)**(-0.5) maxdist = np.max(np.abs(target_unitary - decomp_unitary)) if maxdist > 0.1: maxdist = np.max(np.abs(target_unitary + decomp_unitary)) self.assertTrue(np.abs(maxdist) < tolerance, "Worst distance {}".format(maxdist))
def test_one_qubit_euler_angles(self): """Verify euler_angles_1q produces correct Euler angles for a single-qubit unitary. """ for _ in range(100): unitary = random_unitary(2) with self.subTest(unitary=unitary): angles = euler_angles_1q(unitary.data) decomp_circuit = QuantumCircuit(1) decomp_circuit.u3(*angles, 0) result = execute(decomp_circuit, UnitarySimulatorPy()).result() decomp_unitary = Operator(result.get_unitary()) equal_up_to_phase = matrix_equal(unitary.data, decomp_unitary.data, ignore_phase=True, atol=1e-7) self.assertTrue(equal_up_to_phase)
def accreditation_parser(target_circuit): """ Converts an input quantum circuit to lists representing the input Args: target_circuit (QuantumCircuit): Quantum circuit consisting of cZ gates and single qubit gates, followed by Pauli-Z measure- ments on all qubits Returns: gates_target (list): A 2D list of all 1-qubit gates in the target circuit cz_gate (list): list of all cz gate in target circuit """ # Initialize empty lists gates_target and cz_gate gates_target = [] cz_gate = [] # Qubits in the circuit circuit_qubits = target_circuit.qubits # Initialize empty list single_qubit_gates # This list will be used to store 1-qubit gates in the circuit single_qubit_gates = [[] for _ in circuit_qubits] # Initialize empty list # This is used to check if in a band, a qubit can still be entangled with # other qubits (qubits can be entanged one time per band) unavailiable_qubits = [] # Keep track of current band current_band_no = 0 # Loops over all gates in the circuit. An extra element is added so the # last band is closed at the end for gate in target_circuit.data + ['END STRING']: # Checks for special cases that need to be handled differently gate_qubits = gate[1] last_element = (gate == 'END STRING') is_measure = ((len(gate_qubits) == len(gate[2])) and not gate == 'END STRING') # Records the position of the last cz gate if cz_gate: last_cz = cz_gate[-1][0] else: last_cz = -1 # Makes sure measurements are ignored if not is_measure: circuit_end_band = last_element\ and ((last_cz == current_band_no) or (single_qubit_gates != [[] for _ in circuit_qubits])) # If a new band is required, converts the current band's single # qubit gates to Euler angles and prepares for the next band if((not set(gate_qubits).isdisjoint(set(unavailiable_qubits))) or circuit_end_band): band_gates_angles = [] u3_gates_temp = [] for qubit in single_qubit_gates: matrix = np.array([[1, 0], [0, 1]]) for gate_index in qubit[::-1]: matrix = np.matmul(matrix, gate_index[0].to_matrix()) band_gates_angles.append(euler_angles_1q(matrix)) u3_gates_temp.append(band_gates_angles[0]) band_gates_angles = [] gates_target.append(u3_gates_temp) current_band_no += 1 unavailiable_qubits = [] single_qubit_gates = [[] for _ in circuit_qubits] if last_element: break # Adds single gates' gate object to the array corresponding to # their qubit in single_qubit_gates if len(gate_qubits) == 1: single_qubit_gates[gate_qubits[0].index].append(gate) # Records the location of 2 qubit gates else: cz_gate.append([current_band_no, gate_qubits[0].index, gate_qubits[1].index]) unavailiable_qubits += gate_qubits # Adds a band of identity gates if circuit ends with cz gates if last_cz == current_band_no - 1: last_band = [] for qubit in circuit_qubits: last_band.append((0.0, 0.0, 0.0)) gates_target.append(last_band) # Transposes the u3_gates matrix gates_target = [list(i) for i in zip(*gates_target)] return gates_target, cz_gate
def set_state(self, target_variables, qubit_label, q_if=None): def basis_change(pole, basis, qubit, dagger=False): ''' Returns the circuit required to change from the Z basis to the eigenbasis of a particular Pauli. The opposite is done when `dagger=True`. ''' if pole == '+' and dagger == True: self.qc.x(qubit) if basis == 'X': self.qc.h(qubit) elif basis == 'Y': if dagger: self.qc.rx(-pi / 2, qubit) else: self.qc.rx(pi / 2, qubit) if pole == '+' and dagger == False: self.qc.x(qubit) def normalize(expect): for pauli in ['X', 'Y', 'Z']: if pauli not in expect: expect[pauli] = self.expect[qubit][pauli] R = sqrt(expect['X']**2 + expect['Y']**2 + expect['Z']**2) return {pauli: expect[pauli] / R for pauli in expect} def get_basis(expect): normalized_expect = normalize(expect) theta = arccos(normalized_expect['Z']) phi = arctan2(normalized_expect['Y'], normalized_expect['X']) state0 = [cos(theta / 2), exp(1j * phi) * sin(theta / 2)] state1 = [conj(state0[1]), -conj(state0[0])] return [state0, state1] target_expect = {} for label in target_variables: pauli = list(self.variables.keys())[list( self.variables.values()).index(label)] target_expect[pauli] = target_variables[label] qubit = self.labels.index(qubit_label) current_basis = get_basis(self.get_state(self.labels[qubit])) target_basis = get_basis(target_expect) U = array([[0 for _ in range(2)] for _ in range(2)], dtype=complex) for i in range(2): for j in range(2): for k in range(2): U[j][k] += target_basis[i][j] * conj(current_basis[i][k]) the, phi, lam = euler_angles_1q(U) if q_if: control_variable, pole, control_qubit_label = q_if[0], q_if[ 1], q_if[2] control_qubit = self.labels.index(control_qubit_label) control_pauli = list(self.variables.keys())[list( self.variables.values()).index(control_variable)] basis_change(pole, control_pauli, control_qubit, dagger=False) self.qc.cu3(the, phi, lam, control_qubit, qubit) basis_change(pole, control_pauli, control_qubit, dagger=True) else: self.qc.u3(the, phi, lam, qubit) self._update_expect()
def euler_angles_1q(unitary_matrix): """Deprecated after 0.8 """ warnings.warn("euler_angles_1q function is now accessible under " "qiskit.quantum_info.synthesis", DeprecationWarning) return synthesis.euler_angles_1q(unitary_matrix)