add QuantumCircuit.from_instructions (#9006)

* add QuantumCircuit.from_instructions

* simplify loop

* handle both cases in just one method

* delete stray import

* fix type annotation

* address comments

Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
This commit is contained in:
Kevin J. Sung 2022-10-28 17:29:33 -04:00 committed by GitHub
parent 351da447f7
commit 8b58a8f41e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 121 additions and 9 deletions

View File

@ -14,6 +14,7 @@
"""Quantum circuit object."""
from __future__ import annotations
import copy
import itertools
import functools
@ -285,6 +286,45 @@ class QuantumCircuit:
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).

View File

@ -0,0 +1,5 @@
---
features:
- |
Added the method :meth:`.QuantumCircuit.from_instructions`
for constructing a quantum circuit from an iterable of instructions.

View File

@ -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.quantumcircuit import BitLocations
from qiskit.circuit.quantumregister import AncillaQubit, AncillaRegister, Qubit
from qiskit.test import QiskitTestCase
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.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 @@ class TestCircuitOperations(QiskitTestCase):
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