diff --git a/qiskit/circuit/quantumcircuit.py b/qiskit/circuit/quantumcircuit.py index 78b2273ab74b..8d959a244593 100644 --- a/qiskit/circuit/quantumcircuit.py +++ b/qiskit/circuit/quantumcircuit.py @@ -14,6 +14,7 @@ """Quantum circuit object.""" +from __future__ import annotations import copy import itertools import functools @@ -285,6 +286,45 @@ def __init__( raise TypeError("Only a dictionary or None is accepted for circuit metadata") self._metadata = metadata + @staticmethod + def from_instructions( + instructions: Iterable[ + CircuitInstruction + | tuple[qiskit.circuit.Instruction] + | tuple[qiskit.circuit.Instruction, Iterable[Qubit]] + | tuple[qiskit.circuit.Instruction, Iterable[Qubit], Iterable[Clbit]] + ], + *, + name: Optional[str] = None, + global_phase: ParameterValueType = 0, + metadata: Optional[dict] = None, + ) -> "QuantumCircuit": + """Construct a circuit from an iterable of CircuitInstructions. + + Args: + instructions: The instructions to add to the circuit. + name: The name of the circuit. + global_phase: The global phase of the circuit in radians. + metadata: Arbitrary key value metadata to associate with the circuit. + + Returns: + The quantum circuit. + """ + circuit = QuantumCircuit(name=name, global_phase=global_phase, metadata=metadata) + added_qubits = set() + added_clbits = set() + for instruction in instructions: + if not isinstance(instruction, CircuitInstruction): + instruction = CircuitInstruction(*instruction) + qubits = [qubit for qubit in instruction.qubits if qubit not in added_qubits] + clbits = [clbit for clbit in instruction.clbits if clbit not in added_clbits] + circuit.add_bits(qubits) + circuit.add_bits(clbits) + added_qubits.update(qubits) + added_clbits.update(clbits) + circuit._append(instruction) + return circuit + @property def data(self) -> QuantumCircuitData: """Return the circuit data (instructions and context). diff --git a/releasenotes/notes/circuit-from-instructions-832b43bfd2bfd921.yaml b/releasenotes/notes/circuit-from-instructions-832b43bfd2bfd921.yaml new file mode 100644 index 000000000000..36ae1ca6953a --- /dev/null +++ b/releasenotes/notes/circuit-from-instructions-832b43bfd2bfd921.yaml @@ -0,0 +1,5 @@ +--- +features: + - | + Added the method :meth:`.QuantumCircuit.from_instructions` + for constructing a quantum circuit from an iterable of instructions. diff --git a/test/python/circuit/test_circuit_operations.py b/test/python/circuit/test_circuit_operations.py index a0e87a980606..f028984c94c5 100644 --- a/test/python/circuit/test_circuit_operations.py +++ b/test/python/circuit/test_circuit_operations.py @@ -13,21 +13,23 @@ """Test Qiskit's QuantumCircuit class.""" -from ddt import ddt, data import numpy as np -from qiskit import BasicAer -from qiskit import QuantumRegister, ClassicalRegister, QuantumCircuit -from qiskit import execute -from qiskit.circuit import Gate, Instruction, Parameter, Measure +from ddt import data, ddt + +from qiskit import BasicAer, ClassicalRegister, QuantumCircuit, QuantumRegister, execute +from qiskit.circuit import Gate, Instruction, Measure, Parameter from qiskit.circuit.bit import Bit from qiskit.circuit.classicalregister import Clbit from qiskit.circuit.exceptions import CircuitError +from qiskit.circuit.controlflow import IfElseOp +from qiskit.circuit.library import CXGate, HGate +from qiskit.circuit.library.standard_gates import SGate from qiskit.circuit.quantumcircuit import BitLocations +from qiskit.circuit.quantumcircuitdata import CircuitInstruction from qiskit.circuit.quantumregister import AncillaQubit, AncillaRegister, Qubit -from qiskit.test import QiskitTestCase -from qiskit.circuit.library.standard_gates import SGate +from qiskit.pulse import DriveChannel, Gaussian, Play, Schedule from qiskit.quantum_info import Operator -from qiskit.pulse import Schedule, Play, Gaussian, DriveChannel +from qiskit.test import QiskitTestCase @ddt @@ -1262,6 +1264,71 @@ def test_deprecated_reset_function(self): self.assertEqual(test, expected) + def test_from_instructions(self): + """Test from_instructions method.""" + + qreg = QuantumRegister(4) + creg = ClassicalRegister(3) + + a, b, c, d = qreg + x, y, z = creg + + circuit_1 = QuantumCircuit(2) + circuit_1.x(0) + circuit_2 = QuantumCircuit(2) + circuit_2.y(0) + + def instructions(): + yield CircuitInstruction(HGate(), [a], []) + yield CircuitInstruction(CXGate(), [a, b], []) + yield CircuitInstruction(Measure(), [a], [x]) + yield CircuitInstruction(Measure(), [b], [y]) + yield CircuitInstruction(IfElseOp((z, 1), circuit_1, circuit_2), [c, d], [z]) + + def instruction_tuples(): + yield HGate(), [a], [] + yield CXGate(), [a, b], [] + yield CircuitInstruction(Measure(), [a], [x]) + yield Measure(), [b], [y] + yield IfElseOp((z, 1), circuit_1, circuit_2), [c, d], [z] + + def instruction_tuples_partial(): + yield HGate(), [a] + yield CXGate(), [a, b], [] + yield CircuitInstruction(Measure(), [a], [x]) + yield Measure(), [b], [y] + yield IfElseOp((z, 1), circuit_1, circuit_2), [c, d], [z] + + circuit = QuantumCircuit.from_instructions(instructions()) + circuit_tuples = QuantumCircuit.from_instructions(instruction_tuples()) + circuit_tuples_partial = QuantumCircuit.from_instructions(instruction_tuples_partial()) + + expected = QuantumCircuit([a, b, c, d], [x, y, z]) + for instruction in instructions(): + expected.append(*instruction) + + self.assertEqual(circuit, expected) + self.assertEqual(circuit_tuples, expected) + self.assertEqual(circuit_tuples_partial, expected) + + def test_from_instructions_metadata(self): + """Test from_instructions method passes metadata.""" + qreg = QuantumRegister(2) + a, b = qreg + + def instructions(): + yield CircuitInstruction(HGate(), [a], []) + yield CircuitInstruction(CXGate(), [a, b], []) + + circuit = QuantumCircuit.from_instructions(instructions(), name="test", global_phase=0.1) + + expected = QuantumCircuit([a, b], global_phase=0.1) + for instruction in instructions(): + expected.append(*instruction) + + self.assertEqual(circuit, expected) + self.assertEqual(circuit.name, "test") + class TestCircuitPrivateOperations(QiskitTestCase): """Direct tests of some of the private methods of QuantumCircuit. These do not represent