diff --git a/qiskit/circuit/library/n_local/n_local.py b/qiskit/circuit/library/n_local/n_local.py index b02f3962799..e14f3d14daa 100644 --- a/qiskit/circuit/library/n_local/n_local.py +++ b/qiskit/circuit/library/n_local/n_local.py @@ -730,7 +730,7 @@ def add_layer(self, parameterized_block = self._parameterize_block(block, params=params) layer.compose(parameterized_block, i) - self += layer + self.compose(layer, inplace=True) else: # cannot prepend a block currently, just rebuild self._invalidate() @@ -832,7 +832,7 @@ def _build_rotation_layer(self, param_iter, i): layer.compose(parameterized_block, indices, inplace=True) # add the layer to the circuit - self += layer + self.compose(layer, inplace=True) def _build_entanglement_layer(self, param_iter, i): """Build an entanglement layer.""" @@ -848,7 +848,7 @@ def _build_entanglement_layer(self, param_iter, i): layer.compose(parameterized_block, indices, inplace=True) # add the layer to the circuit - self += layer + self.compose(layer, inplace=True) def _build_additional_layers(self, which): if which == 'appended': @@ -867,7 +867,7 @@ def _build_additional_layers(self, which): for indices in ent: layer.compose(block, indices, inplace=True) - self += layer + self.compose(layer, inplace=True) def _build(self) -> None: """Build the circuit.""" @@ -884,7 +884,7 @@ def _build(self) -> None: # use the initial state circuit if it is not None if self._initial_state: circuit = self._initial_state.construct_circuit('circuit', register=self.qregs[0]) - self += circuit + self.compose(circuit, inplace=True) param_iter = iter(self.ordered_parameters) diff --git a/qiskit/circuit/quantumcircuit.py b/qiskit/circuit/quantumcircuit.py index df79a028ba0..bd010a59e2c 100644 --- a/qiskit/circuit/quantumcircuit.py +++ b/qiskit/circuit/quantumcircuit.py @@ -539,8 +539,11 @@ def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None): return controlled_circ + @deprecate_function('The QuantumCircuit.combine() method is being deprecated. ' + 'Use the compose() method which is more flexible w.r.t ' + 'circuit register compatibility.') def combine(self, rhs): - """Append rhs to self if self contains compatible registers. + """DEPRECATED - Returns rhs appended to self if self contains compatible registers. Two circuits are compatible if they contain the same registers or if they contain different registers with unique names. The @@ -586,8 +589,11 @@ def combine(self, rhs): return circuit + @deprecate_function('The QuantumCircuit.extend() method is being deprecated. Use the ' + 'compose() (potentially with the inplace=True argument) and tensor() ' + 'methods which are more flexible w.r.t circuit register compatibility.') def extend(self, rhs): - """Append QuantumCircuit to the right hand side if it contains compatible registers. + """DEPRECATED - Append QuantumCircuit to the RHS if it contains compatible registers. Two circuits are compatible if they contain the same registers or if they contain different registers with unique names. The @@ -847,10 +853,16 @@ def ancillas(self): """ return self._ancillas + @deprecate_function('The QuantumCircuit.__add__() method is being deprecated.' + 'Use the compose() method which is more flexible w.r.t ' + 'circuit register compatibility.') def __add__(self, rhs): """Overload + to implement self.combine.""" return self.combine(rhs) + @deprecate_function('The QuantumCircuit.__iadd__() method is being deprecated. Use the ' + 'compose() (potentially with the inplace=True argument) and tensor() ' + 'methods which are more flexible w.r.t circuit register compatibility.') def __iadd__(self, rhs): """Overload += to implement self.extend.""" return self.extend(rhs) diff --git a/releasenotes/notes/deprecate-circuit-combine-extend-ffc6a073ed114890.yaml b/releasenotes/notes/deprecate-circuit-combine-extend-ffc6a073ed114890.yaml new file mode 100644 index 00000000000..e054c8786b6 --- /dev/null +++ b/releasenotes/notes/deprecate-circuit-combine-extend-ffc6a073ed114890.yaml @@ -0,0 +1,32 @@ +--- +deprecations: + - | + Two ``QuantumCircuit`` methods ``+``, ``+=``, ``combine`` and ``extend`` are deprecated, + in favor of ``QuantumCircuit.compose()``. The latter allows more flexibility + in composing two circuits that do not have matching registers. It does not, + however, automatically add qubits/clbits unlike the deprecated methods. + To add a circuit on new qubits/clbits, ``QuantumCircuit.tensor`` can be used. + + .. code:: + + from qiskit.circuit import QuantumRegister, QuantumCircuit + + a = QuantumRegister(2, 'a') + circuit_a = QuantumCircuit(a) + circuit_a.cx(0, 1) + + b = QuantumRegister(2, 'b') + circuit_b = QuantumCircuit(b) + circuit_b.cz(0, 1) + + # same as circuit_a + circuit_b (or combine) + added_with_different_regs = circuit_b.tensor(circuit_a) + + # same as circuit_a + circuit_a (or combine) + added_with_same_regs = circuit_a.compose(circuit_a) + + # same as circuit_a += circuit_b (or extend) + circuit_a = circuit_b.tensor(circuit_a) + + # same as circuit_a += circuit_a (or extend) + circuit_a.compose(circuit_a, inplace=True) diff --git a/test/python/basicaer/test_multi_registers_convention.py b/test/python/basicaer/test_multi_registers_convention.py index 1d125b20cec..1da4cc4cca7 100644 --- a/test/python/basicaer/test_multi_registers_convention.py +++ b/test/python/basicaer/test_multi_registers_convention.py @@ -27,7 +27,7 @@ def test_circuit_multi(self): creg0 = ClassicalRegister(2, 'c0') qreg1 = QuantumRegister(2, 'q1') creg1 = ClassicalRegister(2, 'c1') - circ = QuantumCircuit(qreg0, qreg1) + circ = QuantumCircuit(qreg0, qreg1, creg0, creg1) circ.x(qreg0[1]) circ.x(qreg1[0]) @@ -35,7 +35,7 @@ def test_circuit_multi(self): meas.measure(qreg0, creg0) meas.measure(qreg1, creg1) - qc = circ + meas + qc = circ.compose(meas) backend_sim = BasicAer.get_backend('qasm_simulator') diff --git a/test/python/circuit/library/test_nlocal.py b/test/python/circuit/library/test_nlocal.py index ca6027601e6..775490c988b 100644 --- a/test/python/circuit/library/test_nlocal.py +++ b/test/python/circuit/library/test_nlocal.py @@ -463,11 +463,11 @@ def test_parameters_settable_is_constant(self): with self.subTest(msg='num_parameters_settable remained constant'): self.assertEqual(two.num_parameters_settable, len(ordered_params)) - def test_iadd_to_circuit(self): + def test_compose_inplace_to_circuit(self): """Test adding a two-local to an existing circuit.""" two = TwoLocal(3, ['ry', 'rz'], 'cz', 'full', reps=1, insert_barriers=True) circuit = QuantumCircuit(3) - circuit += two + circuit.compose(two, inplace=True) reference = QuantumCircuit(3) param_iter = iter(two.ordered_parameters) @@ -487,11 +487,11 @@ def test_iadd_to_circuit(self): self.assertCircuitEqual(circuit, reference) - def test_adding_two(self): + def test_composing_two(self): """Test adding two two-local circuits.""" entangler_map = [[0, 3], [0, 2]] two = TwoLocal(4, [], 'cry', entangler_map, reps=1) - circuit = two + two + circuit = two.compose(two) reference = QuantumCircuit(4) params = two.ordered_parameters diff --git a/test/python/circuit/test_circuit_multi_registers.py b/test/python/circuit/test_circuit_multi_registers.py index b943b8b967d..59c96300a74 100644 --- a/test/python/circuit/test_circuit_multi_registers.py +++ b/test/python/circuit/test_circuit_multi_registers.py @@ -28,7 +28,7 @@ def test_circuit_multi(self): creg0 = ClassicalRegister(2, 'c0') qreg1 = QuantumRegister(2, 'q1') creg1 = ClassicalRegister(2, 'c1') - circ = QuantumCircuit(qreg0, qreg1) + circ = QuantumCircuit(qreg0, qreg1, creg0, creg1) circ.x(qreg0[1]) circ.x(qreg1[0]) @@ -41,6 +41,8 @@ def test_circuit_multi(self): circ2 = QuantumCircuit() circ2.add_register(qreg0) circ2.add_register(qreg1) + circ2.add_register(creg0) + circ2.add_register(creg1) circ2.x(qreg0[1]) circ2.x(qreg1[0]) diff --git a/test/python/circuit/test_circuit_operations.py b/test/python/circuit/test_circuit_operations.py index 4929d2d6795..9126004091b 100644 --- a/test/python/circuit/test_circuit_operations.py +++ b/test/python/circuit/test_circuit_operations.py @@ -46,7 +46,7 @@ def test_adding_self(self): self.assertEqual(['x', 'x'], [x[0].name for x in qc.data]) def test_combine_circuit_common(self): - """Test combining two circuits with same registers. + """Test combining two circuits with same registers (inplace=False). """ qr = QuantumRegister(2) cr = ClassicalRegister(2) @@ -55,32 +55,39 @@ def test_combine_circuit_common(self): qc1.h(qr[0]) qc1.measure(qr[0], cr[0]) qc2.measure(qr[1], cr[1]) - new_circuit = qc1 + qc2 + + new_circuit = qc1.combine(qc2) + backend = BasicAer.get_backend('qasm_simulator') shots = 1024 result = execute(new_circuit, backend=backend, shots=shots, seed_simulator=78).result() counts = result.get_counts() target = {'00': shots / 2, '01': shots / 2} threshold = 0.04 * shots + self.assertDictEqual(qc1.count_ops(), {'h': 1, 'measure': 1}) # no changes "in-place" + self.assertDictEqual(qc2.count_ops(), {'measure': 1}) # no changes "in-place" self.assertDictAlmostEqual(counts, target, threshold) - def test_combine_circuit_different(self): - """Test combining two circuits with different registers. + def test_combine_circuit_common_plus(self): + """Test combining two circuits with same registers (as plus). """ qr = QuantumRegister(2) cr = ClassicalRegister(2) - qc1 = QuantumCircuit(qr) - qc1.x(qr) + qc1 = QuantumCircuit(qr, cr) qc2 = QuantumCircuit(qr, cr) - qc2.measure(qr, cr) + qc1.h(qr[0]) + qc1.measure(qr[0], cr[0]) + qc2.measure(qr[1], cr[1]) new_circuit = qc1 + qc2 backend = BasicAer.get_backend('qasm_simulator') shots = 1024 - result = execute(new_circuit, backend=backend, shots=shots, - seed_simulator=78).result() + result = execute(new_circuit, backend=backend, shots=shots, seed_simulator=78).result() counts = result.get_counts() - target = {'11': shots} - self.assertEqual(counts, target) + target = {'00': shots / 2, '01': shots / 2} + threshold = 0.04 * shots + self.assertDictEqual(qc1.count_ops(), {'h': 1, 'measure': 1}) # no changes "in-place" + self.assertDictEqual(qc2.count_ops(), {'measure': 1}) # no changes "in-place" + self.assertDictAlmostEqual(counts, target, threshold) def test_combine_circuit_fail(self): """Test combining two circuits fails if registers incompatible. @@ -99,7 +106,7 @@ def test_combine_circuit_fail(self): self.assertRaises(CircuitError, qc1.__add__, qcr3) def test_extend_circuit(self): - """Test extending a circuit with same registers. + """Test extending a circuit with same registers (in place add). """ qr = QuantumRegister(2) cr = ClassicalRegister(2) @@ -108,7 +115,8 @@ def test_extend_circuit(self): qc1.h(qr[0]) qc1.measure(qr[0], cr[0]) qc2.measure(qr[1], cr[1]) - qc1 += qc2 + + qc1.extend(qc2) backend = BasicAer.get_backend('qasm_simulator') shots = 1024 result = execute(qc1, backend=backend, shots=shots, @@ -116,25 +124,31 @@ def test_extend_circuit(self): counts = result.get_counts() target = {'00': shots / 2, '01': shots / 2} threshold = 0.04 * shots + self.assertDictEqual(qc1.count_ops(), {'h': 1, 'measure': 2}) # changes "in-place" + self.assertDictEqual(qc2.count_ops(), {'measure': 1}) # no changes "in-place" self.assertDictAlmostEqual(counts, target, threshold) - def test_extend_circuit_different_registers(self): - """Test extending a circuit with different registers. + def test_extend_circuit_iadd(self): + """Test extending a circuit with same registers (in place add). """ qr = QuantumRegister(2) cr = ClassicalRegister(2) - qc1 = QuantumCircuit(qr) - qc1.x(qr) + qc1 = QuantumCircuit(qr, cr) qc2 = QuantumCircuit(qr, cr) - qc2.measure(qr, cr) + qc1.h(qr[0]) + qc1.measure(qr[0], cr[0]) + qc2.measure(qr[1], cr[1]) qc1 += qc2 backend = BasicAer.get_backend('qasm_simulator') shots = 1024 result = execute(qc1, backend=backend, shots=shots, seed_simulator=78).result() counts = result.get_counts() - target = {'11': shots} - self.assertEqual(counts, target) + target = {'00': shots / 2, '01': shots / 2} + threshold = 0.04 * shots + self.assertDictEqual(qc1.count_ops(), {'h': 1, 'measure': 2}) # changes "in-place" + self.assertDictEqual(qc2.count_ops(), {'measure': 1}) # no changes "in-place" + self.assertDictAlmostEqual(counts, target, threshold) def test_extend_circuit_fail(self): """Test extending a circuit fails if registers incompatible. diff --git a/test/python/circuit/test_parameters.py b/test/python/circuit/test_parameters.py index 12defdf69a0..4ef8425f0ca 100644 --- a/test/python/circuit/test_parameters.py +++ b/test/python/circuit/test_parameters.py @@ -597,7 +597,7 @@ def test_circuit_composition(self): theta = Parameter('θ') qr = QuantumRegister(1) cr = ClassicalRegister(1) - qc1 = QuantumCircuit(qr) + qc1 = QuantumCircuit(qr, cr) qc1.rx(theta, qr) phi = Parameter('phi') @@ -751,7 +751,7 @@ def test_binding_parameterized_circuits_built_in_multiproc(self): num_processes=num_processes) for qc in results: - circuit += qc + circuit.compose(qc, inplace=True) parameter_values = [{x: 1 for x in parameters}]