From 4d7ba12987cd47fc6a681c7d268419032e648dbc Mon Sep 17 00:00:00 2001 From: Adenilton Silva Date: Sat, 18 Apr 2020 21:22:39 -0300 Subject: [PATCH 1/7] reverse multiplexed Ry --- qiskit/extensions/quantum_initializer/initializer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiskit/extensions/quantum_initializer/initializer.py b/qiskit/extensions/quantum_initializer/initializer.py index d0ae22dab026..4771dd8abe9b 100644 --- a/qiskit/extensions/quantum_initializer/initializer.py +++ b/qiskit/extensions/quantum_initializer/initializer.py @@ -110,7 +110,7 @@ def gates_to_uncompute(self): 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]) + circuit.append(ry_mult.to_instruction().mirror(), q[i:self.num_qubits]) return circuit @staticmethod From 4092be95e1aca7105b42913f4a31456234009a96 Mon Sep 17 00:00:00 2001 From: Adenilton Silva Date: Sun, 19 Apr 2020 08:22:13 -0300 Subject: [PATCH 2/7] simplifying cnots --- qiskit/extensions/quantum_initializer/initializer.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/qiskit/extensions/quantum_initializer/initializer.py b/qiskit/extensions/quantum_initializer/initializer.py index 4771dd8abe9b..9d3ecf38c639 100644 --- a/qiskit/extensions/quantum_initializer/initializer.py +++ b/qiskit/extensions/quantum_initializer/initializer.py @@ -178,7 +178,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, first_call = True): """ Return a recursive implementation of a multiplexor circuit, where each instruction itself has a decomposition based on @@ -217,7 +217,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 @@ -226,14 +226,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 first_call: + circuit.append(CXGate(), [msb, lsb]) return circuit From f0143a7f89f32abfeb6c668428bbb8f0d8d866cc Mon Sep 17 00:00:00 2001 From: Adenilton Silva Date: Sun, 19 Apr 2020 08:32:45 -0300 Subject: [PATCH 3/7] simplifying cnots --- qiskit/extensions/quantum_initializer/initializer.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/qiskit/extensions/quantum_initializer/initializer.py b/qiskit/extensions/quantum_initializer/initializer.py index 9d3ecf38c639..1a5ab4ec7389 100644 --- a/qiskit/extensions/quantum_initializer/initializer.py +++ b/qiskit/extensions/quantum_initializer/initializer.py @@ -107,10 +107,13 @@ 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().mirror(), q[i:self.num_qubits]) + if np.linalg.norm(phis) != 0: + rz_mult = self._multiplex(RZGate, phis) + circuit.append(rz_mult.to_instruction(), q[i:self.num_qubits]) + if np.linalg.norm(thetas) != 0: + ry_mult = self._multiplex(RYGate, thetas) + circuit.append(ry_mult.to_instruction().mirror(), q[i:self.num_qubits]) + return circuit @staticmethod From a991f2c68cc866dea5f5f5277f98f164aa973e0a Mon Sep 17 00:00:00 2001 From: Adenilton Silva Date: Sun, 19 Apr 2020 11:00:05 -0300 Subject: [PATCH 4/7] simplify last cnot --- qiskit/extensions/quantum_initializer/initializer.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/qiskit/extensions/quantum_initializer/initializer.py b/qiskit/extensions/quantum_initializer/initializer.py index 1a5ab4ec7389..7e8c153b5700 100644 --- a/qiskit/extensions/quantum_initializer/initializer.py +++ b/qiskit/extensions/quantum_initializer/initializer.py @@ -107,11 +107,17 @@ 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) + + 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) + rz_mult = self._multiplex(RZGate, phis, first_call=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) + ry_mult = self._multiplex(RYGate, thetas, first_call=add_last_cnot) circuit.append(ry_mult.to_instruction().mirror(), q[i:self.num_qubits]) return circuit From 5bd34acb2d8dae8261ec627a08007897bd13a9cd Mon Sep 17 00:00:00 2001 From: Adenilton Silva Date: Sun, 19 Apr 2020 11:51:00 -0300 Subject: [PATCH 5/7] test initializer number of cnots --- test/python/circuit/test_initializer.py | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/test/python/circuit/test_initializer.py b/test/python/circuit/test_initializer.py index cb3bf4d01a17..ca7768117dc3 100644 --- a/test/python/circuit/test_initializer.py +++ b/test/python/circuit/test_initializer.py @@ -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 @@ -314,6 +315,27 @@ 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 = 3 + _optimization_level = 0 + + vector = -1 * np.random.rand(2 ** num_qubits) + vector[0] = 0 + 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.""" @@ -354,6 +376,5 @@ def test_init(self): all(isinstance(p, complex) and not isinstance(p, np.number) for p in params)) - if __name__ == '__main__': unittest.main() From 965368f58129f7b7c04dac84e46d898be399fa35 Mon Sep 17 00:00:00 2001 From: Adenilton Silva Date: Sun, 19 Apr 2020 12:48:26 -0300 Subject: [PATCH 6/7] fix style --- qiskit/extensions/quantum_initializer/initializer.py | 9 +++++---- test/python/circuit/test_initializer.py | 7 +++++-- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/qiskit/extensions/quantum_initializer/initializer.py b/qiskit/extensions/quantum_initializer/initializer.py index 7e8c153b5700..11d1fdb98339 100644 --- a/qiskit/extensions/quantum_initializer/initializer.py +++ b/qiskit/extensions/quantum_initializer/initializer.py @@ -113,11 +113,11 @@ def gates_to_uncompute(self): add_last_cnot = False if np.linalg.norm(phis) != 0: - rz_mult = self._multiplex(RZGate, phis, first_call=add_last_cnot) + 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, first_call=add_last_cnot) + 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 @@ -187,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, first_call = True): + 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 @@ -199,6 +199,7 @@ def _multiplex(self, target_gate, list_of_angles, first_call = True): 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 @@ -242,7 +243,7 @@ def _multiplex(self, target_gate, list_of_angles, first_call = True): circuit.append(multiplex_2.to_instruction(), q[0:-1]) # attach a final CNOT - if first_call: + if last_cnot: circuit.append(CXGate(), [msb, lsb]) return circuit diff --git a/test/python/circuit/test_initializer.py b/test/python/circuit/test_initializer.py index ca7768117dc3..cd568cb15190 100644 --- a/test/python/circuit/test_initializer.py +++ b/test/python/circuit/test_initializer.py @@ -330,9 +330,11 @@ def test_max_number_cnots(self): circuit = QuantumCircuit(qr) circuit.initialize(vector, qr) - b = transpile(circuit, basis_gates=['u1', 'u2', 'u3', 'cx'], optimization_level=_optimization_level) + 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 + max_cnots = 2 ** (num_qubits + 1) - 2 * num_qubits self.assertLessEqual(number_cnots, max_cnots) @@ -376,5 +378,6 @@ def test_init(self): all(isinstance(p, complex) and not isinstance(p, np.number) for p in params)) + if __name__ == '__main__': unittest.main() From 37d3c973c1281f42d84638fb968a6f36a101a8e4 Mon Sep 17 00:00:00 2001 From: Adenilton Silva Date: Mon, 4 May 2020 17:18:00 -0300 Subject: [PATCH 7/7] numbers which test failed for the old implementation --- test/python/circuit/test_initializer.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/test/python/circuit/test_initializer.py b/test/python/circuit/test_initializer.py index cd568cb15190..44cc0d4b68d4 100644 --- a/test/python/circuit/test_initializer.py +++ b/test/python/circuit/test_initializer.py @@ -319,11 +319,17 @@ def test_max_number_cnots(self): """ Check if the number of cnots <= 2^(n+1) - 2n (arXiv:quant-ph/0406176) """ - num_qubits = 3 + num_qubits = 4 _optimization_level = 0 - vector = -1 * np.random.rand(2 ** num_qubits) - vector[0] = 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')