diff --git a/qiskit/extensions/quantum_initializer/squ.py b/qiskit/extensions/quantum_initializer/squ.py index bfda0f76bd81..820e34192d4e 100644 --- a/qiskit/extensions/quantum_initializer/squ.py +++ b/qiskit/extensions/quantum_initializer/squ.py @@ -139,10 +139,6 @@ def _zyz_dec(self): return -a, -b, -c, d -def _ct(m): - return np.transpose(np.conjugate(m)) - - def squ(self, u, qubit, mode="ZYZ", up_to_diagonal=False): """ Decompose an arbitrary 2*2 unitary into three rotation gates :math:`U=R_zR_yR_z`. diff --git a/qiskit/extensions/quantum_initializer/ucg.py b/qiskit/extensions/quantum_initializer/ucg.py index ac3dd16e7b38..0019d7230743 100644 --- a/qiskit/extensions/quantum_initializer/ucg.py +++ b/qiskit/extensions/quantum_initializer/ucg.py @@ -42,6 +42,7 @@ import numpy as np from qiskit.circuit.gate import Gate +from qiskit.extensions.standard.h import HGate from qiskit.quantum_info.operators.predicates import is_unitary_matrix from qiskit.circuit.quantumregister import QuantumRegister from qiskit.circuit.quantumcircuit import QuantumCircuit @@ -123,14 +124,6 @@ def _dec_ucg(self): 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() @@ -139,11 +132,11 @@ def _dec_ucg(self): for i, gate in enumerate(single_qubit_gates): # Absorb Hadamards and Rz(pi/2) gates if i == 0: - squ = _h().dot(gate) + squ = HGate().to_matrix().dot(gate) elif i == len(single_qubit_gates) - 1: - squ = gate.dot(_rz(np.pi / 2)).dot(_h()) + squ = gate.dot(UCG._rz(np.pi / 2)).dot(HGate().to_matrix()) else: - squ = _h().dot(gate.dot(_rz(np.pi / 2))).dot(_h()) + squ = HGate().to_matrix().dot(gate.dot(UCG._rz(np.pi / 2))).dot(HGate().to_matrix()) # 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 @@ -202,22 +195,23 @@ def _dec_ucg_help(self): # which hasn't been decomposed yet. k = shift + len_ucg + i single_qubit_gates[k] = \ - single_qubit_gates[k].dot(_ct(r)) * _rz(np.pi / 2).item((0, 0)) + single_qubit_gates[k].dot(UCG._ct(r)) * UCG._rz(np.pi / 2).item((0, 0)) k = k + len_ucg // 2 single_qubit_gates[k] = \ - single_qubit_gates[k].dot(r) * _rz(np.pi / 2).item((1, 1)) + single_qubit_gates[k].dot(r) * UCG._rz(np.pi / 2).item((1, 1)) else: # Absorb the Rz(pi/2) rotation on the control into the UC-Rz gate and merge # the trailing UC-Rz rotation into a diagonal gate at the end of the circuit for ucg_index_2 in range(num_ucgs): shift_2 = ucg_index_2 * len_ucg k = 2 * (i + shift_2) - diag[k] = diag[k] * _ct(r).item((0, 0)) * _rz(np.pi / 2).item((0, 0)) - diag[k + 1] = diag[k + 1] * _ct(r).item((1, 1)) * _rz(np.pi / 2).item( + diag[k] = diag[k] * UCG._ct(r).item((0, 0)) * UCG._rz(np.pi / 2).item( (0, 0)) + diag[k + 1] = diag[k + 1] * UCG._ct(r).item((1, 1)) * UCG._rz( + np.pi / 2).item((0, 0)) k = len_ucg + k - diag[k] *= r.item((0, 0)) * _rz(np.pi / 2).item((1, 1)) - diag[k + 1] *= r.item((1, 1)) * _rz(np.pi / 2).item((1, 1)) + diag[k] *= r.item((0, 0)) * UCG._rz(np.pi / 2).item((1, 1)) + diag[k + 1] *= r.item((1, 1)) * UCG._rz(np.pi / 2).item((1, 1)) return single_qubit_gates, diag def _demultiplex_single_uc(self, a, b): @@ -230,7 +224,7 @@ def _demultiplex_single_uc(self, a, b): (see there for the details). """ # The notation is chosen as in https://arxiv.org/pdf/quant-ph/0410066.pdf. - x = a.dot(_ct(b)) + x = a.dot(UCG._ct(b)) det_x = np.linalg.det(x) x11 = x.item((0, 0)) / cmath.sqrt(det_x) phi = cmath.phase(det_x) @@ -245,20 +239,16 @@ def _demultiplex_single_uc(self, a, b): d = np.flip(d, 0) u = np.flip(u, 1) d = np.diag(np.sqrt(d)) - v = d.dot(_ct(u)).dot(_ct(r)).dot(b) + v = d.dot(UCG._ct(u)).dot(UCG._ct(r)).dot(b) return v, u, r + @staticmethod + def _ct(m): + return np.transpose(np.conjugate(m)) -def _ct(m): - return np.transpose(np.conjugate(m)) - - -def _h(): - return 1 / np.sqrt(2) * np.array([[1, 1], [1, -1]]) - - -def _rz(alpha): - return np.array([[np.exp(1j * alpha / 2), 0], [0, np.exp(-1j * alpha / 2)]]) + @staticmethod + def _rz(alpha): + return np.array([[np.exp(1j * alpha / 2), 0], [0, np.exp(-1j * alpha / 2)]]) def ucg(self, gate_list, q_controls, q_target, up_to_diagonal=False): diff --git a/qiskit/extensions/quantum_initializer/ucrot.py b/qiskit/extensions/quantum_initializer/ucrot.py index f1d0fab723bf..63524766f1d6 100644 --- a/qiskit/extensions/quantum_initializer/ucrot.py +++ b/qiskit/extensions/quantum_initializer/ucrot.py @@ -107,7 +107,7 @@ def _dec_ucrot(self): # First, we find the rotation angles of the single-qubit rotations acting # on the target qubit angles = self.params.copy() - _dec_uc_rotations(angles, 0, len(angles), False) + UCRot._dec_uc_rotations(angles, 0, len(angles), False) # Now, it is easy to place the C-NOT gates to get back the full decomposition.s for (i, angle) in enumerate(angles): if self.rot_axes == "X": @@ -139,29 +139,30 @@ def _dec_ucrot(self): circuit.ry(-np.pi / 2, q_target) return circuit - -# Calculates rotation angles for a uniformly controlled R_t gate with a C-NOT gate at the end of -# the circuit. The rotation angles of the gate R_t are stored in angles[start_index:end_index]. -# If reversed == True, it decomposes the gate such that there is a C-NOT gate at the start -# of the circuit (in fact, the circuit topology for the reversed decomposition is the reversed -# one of the original decomposition) -def _dec_uc_rotations(angles, start_index, end_index, reversedDec): - interval_len_half = (end_index - start_index) // 2 - for i in range(start_index, start_index + interval_len_half): - if not reversedDec: - angles[i], angles[i + interval_len_half] = _update_angles(angles[i], - angles[i + interval_len_half]) + @staticmethod + def _dec_uc_rotations(angles, start_index, end_index, reversedDec): + """ + Calculates rotation angles for a uniformly controlled R_t gate with a C-NOT gate at + the end of the circuit. The rotation angles of the gate R_t are stored in + angles[start_index:end_index]. If reversed == True, it decomposes the gate such that + there is a C-NOT gate at the start of the circuit (in fact, the circuit topology for + the reversed decomposition is the reversed one of the original decomposition) + """ + interval_len_half = (end_index - start_index) // 2 + for i in range(start_index, start_index + interval_len_half): + if not reversedDec: + angles[i], angles[i + interval_len_half] = UCRot._update_angles(angles[i], angles[ + i + interval_len_half]) + else: + angles[i + interval_len_half], angles[i] = UCRot._update_angles(angles[i], angles[ + i + interval_len_half]) + if interval_len_half <= 1: + return else: - angles[i + interval_len_half], angles[i] = _update_angles(angles[i], - angles[i + interval_len_half]) - if interval_len_half <= 1: - return - else: - _dec_uc_rotations(angles, start_index, start_index + interval_len_half, False) - _dec_uc_rotations(angles, start_index + interval_len_half, end_index, True) - - -# Calculate the new rotation angles according to Shende's decomposition + UCRot._dec_uc_rotations(angles, start_index, start_index + interval_len_half, False) + UCRot._dec_uc_rotations(angles, start_index + interval_len_half, end_index, True) -def _update_angles(angle1, angle2): - return (angle1 + angle2) / 2.0, (angle1 - angle2) / 2.0 + @staticmethod + def _update_angles(angle1, angle2): + """Calculate the new rotation angles according to Shende's decomposition""" + return (angle1 + angle2) / 2.0, (angle1 - angle2) / 2.0