Skip to content

Commit

Permalink
Issue Qiskit#4182 Initializer should use at most 2^(n+1)-2n cnots (Qi…
Browse files Browse the repository at this point in the history
…skit#4183)

* reverse multiplexed Ry

* simplifying cnots

* simplifying cnots

* simplify last cnot

* test initializer number of  cnots

* fix style

* numbers which test failed for the old implementation

Co-authored-by: Julien Gacon <[email protected]>
Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
  • Loading branch information
3 people authored Jun 18, 2020
1 parent 7b1f7af commit 1ad9cac
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 8 deletions.
27 changes: 19 additions & 8 deletions qiskit/extensions/quantum_initializer/initializer.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,10 +107,19 @@ def gates_to_uncompute(self):

# perform the required rotations to decouple the LSB qubit (so that
# it can be "factored" out, leaving a shorter amplitude vector to peel away)
rz_mult = self._multiplex(RZGate, phis)
ry_mult = self._multiplex(RYGate, thetas)
circuit.append(rz_mult.to_instruction(), q[i:self.num_qubits])
circuit.append(ry_mult.to_instruction(), q[i:self.num_qubits])

add_last_cnot = True
if np.linalg.norm(phis) != 0 and np.linalg.norm(thetas) != 0:
add_last_cnot = False

if np.linalg.norm(phis) != 0:
rz_mult = self._multiplex(RZGate, phis, last_cnot=add_last_cnot)
circuit.append(rz_mult.to_instruction(), q[i:self.num_qubits])

if np.linalg.norm(thetas) != 0:
ry_mult = self._multiplex(RYGate, thetas, last_cnot=add_last_cnot)
circuit.append(ry_mult.to_instruction().mirror(), q[i:self.num_qubits])

return circuit

@staticmethod
Expand Down Expand Up @@ -178,7 +187,7 @@ def _bloch_angles(pair_of_complex):

return final_r * np.exp(1.J * final_t / 2), theta, phi

def _multiplex(self, target_gate, list_of_angles):
def _multiplex(self, target_gate, list_of_angles, last_cnot=True):
"""
Return a recursive implementation of a multiplexor circuit,
where each instruction itself has a decomposition based on
Expand All @@ -190,6 +199,7 @@ def _multiplex(self, target_gate, list_of_angles):
target_gate (Gate): Ry or Rz gate to apply to target qubit, multiplexed
over all other "select" qubits
list_of_angles (list[float]): list of rotation angles to apply Ry and Rz
last_cnot (bool): add the last cnot if last_cnot = True
Returns:
DAGCircuit: the circuit implementing the multiplexor's action
Expand Down Expand Up @@ -217,7 +227,7 @@ def _multiplex(self, target_gate, list_of_angles):
list_of_angles = angle_weight.dot(np.array(list_of_angles)).tolist()

# recursive step on half the angles fulfilling the above assumption
multiplex_1 = self._multiplex(target_gate, list_of_angles[0:(list_len // 2)])
multiplex_1 = self._multiplex(target_gate, list_of_angles[0:(list_len // 2)], False)
circuit.append(multiplex_1.to_instruction(), q[0:-1])

# attach CNOT as follows, thereby flipping the LSB qubit
Expand All @@ -226,14 +236,15 @@ def _multiplex(self, target_gate, list_of_angles):
# implement extra efficiency from the paper of cancelling adjacent
# CNOTs (by leaving out last CNOT and reversing (NOT inverting) the
# second lower-level multiplex)
multiplex_2 = self._multiplex(target_gate, list_of_angles[(list_len // 2):])
multiplex_2 = self._multiplex(target_gate, list_of_angles[(list_len // 2):], False)
if list_len > 1:
circuit.append(multiplex_2.to_instruction().mirror(), q[0:-1])
else:
circuit.append(multiplex_2.to_instruction(), q[0:-1])

# attach a final CNOT
circuit.append(CXGate(), [msb, lsb])
if last_cnot:
circuit.append(CXGate(), [msb, lsb])

return circuit

Expand Down
30 changes: 30 additions & 0 deletions test/python/circuit/test_initializer.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
from qiskit import QuantumCircuit
from qiskit import QuantumRegister
from qiskit import ClassicalRegister
from qiskit import transpile
from qiskit import execute, assemble, BasicAer
from qiskit.quantum_info import state_fidelity
from qiskit.exceptions import QiskitError
Expand Down Expand Up @@ -314,6 +315,35 @@ def test_equivalence(self):

self.assertEqual(qc1, qc2)

def test_max_number_cnots(self):
"""
Check if the number of cnots <= 2^(n+1) - 2n (arXiv:quant-ph/0406176)
"""
num_qubits = 4
_optimization_level = 0

vector = np.array(
[0.1314346 + 0.j, 0.32078572 - 0.01542775j, 0.13146466 + 0.0945312j,
0.21090852 + 0.07935982j, 0.1700122 - 0.07905648j, 0.15570757 - 0.12309154j,
0.18039667 + 0.04904504j, 0.22227187 - 0.05055569j, 0.23573255 - 0.09894111j,
0.27307292 - 0.10372994j, 0.24162792 + 0.1090791j, 0.3115577 + 0.1211683j,
0.1851788 + 0.08679141j, 0.36226463 - 0.09940202j, 0.13863395 + 0.10558225j,
0.30767986 + 0.02073838j])

vector = vector / np.linalg.norm(vector)

qr = QuantumRegister(num_qubits, 'qr')
circuit = QuantumCircuit(qr)
circuit.initialize(vector, qr)

b = transpile(circuit, basis_gates=['u1', 'u2', 'u3', 'cx'],
optimization_level=_optimization_level)

number_cnots = b.count_ops()['cx']
max_cnots = 2 ** (num_qubits + 1) - 2 * num_qubits

self.assertLessEqual(number_cnots, max_cnots)


class TestInstructionParam(QiskitTestCase):
"""Test conversion of numpy type parameters."""
Expand Down

0 comments on commit 1ad9cac

Please sign in to comment.