Skip to content

Commit

Permalink
small refactor of quantum_initializer (#3241)
Browse files Browse the repository at this point in the history
* _ct is unused

* loose functions as static methods

* loose functions as static methods

* remove _h()

* old code
  • Loading branch information
Luciano authored Oct 27, 2019
1 parent 52b4dc1 commit 516607b
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 58 deletions.
4 changes: 0 additions & 4 deletions qiskit/extensions/quantum_initializer/squ.py
Original file line number Diff line number Diff line change
Expand Up @@ -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`.
Expand Down
48 changes: 19 additions & 29 deletions qiskit/extensions/quantum_initializer/ucg.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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()
Expand All @@ -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
Expand Down Expand Up @@ -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):
Expand All @@ -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)
Expand All @@ -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):
Expand Down
51 changes: 26 additions & 25 deletions qiskit/extensions/quantum_initializer/ucrot.py
Original file line number Diff line number Diff line change
Expand Up @@ -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":
Expand Down Expand Up @@ -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

0 comments on commit 516607b

Please sign in to comment.