Construct circuits
This page takes a closer look at the QuantumCircuit
class in the Qiskit SDK, including some more advanced methods you can use to create quantum circuits.
What is a quantum circuit?
A simple quantum circuit is a collection of qubits and a list of instructions that act on those qubits. To demonstrate, the following cell creates a new circuit with two new qubits, then displays the circuit's qubits
attribute.
from qiskit import QuantumCircuit
qc = QuantumCircuit(2)
qc.qubits
Output:
[Qubit(QuantumRegister(2, 'q'), 0), Qubit(QuantumRegister(2, 'q'), 1)]
Adding an instruction to the circuit appends the instruction to the circuit's data
attribute. The following cell output shows data
is a list of CircuitInstruction
objects, each of which has an operation
attribute, and a qubits
attribute.
qc.x(0) # Add X-gate to qubit 0
qc.data
Output:
[CircuitInstruction(operation=Instruction(name='x', num_qubits=1, num_clbits=0, params=[]), qubits=(Qubit(QuantumRegister(2, 'q'), 0),), clbits=())]
The easiest way to view this information is through the draw
method, which returns a visualization of a circuit. See Visualize circuits for different ways of displaying quantum circuits.
qc.draw("mpl")
Output:
Circuit instruction objects can contain "definition" circuits that describe the instruction in terms of more fundamental instructions. For example, the X-gate is defined as a specific case of the U3-gate, a more general single-qubit gate.
[4] :# Draw definition circuit of 0th instruction in `qc`
qc.data[0].operation.definition.draw("mpl")
Output:
Instructions and circuits are similar in that they both describe operations on bits and qubits, but they have different purposes:
- Instructions are treated as fixed, and their methods will usually return new instructions (without mutating the original object).
- Circuits are designed to be built over many lines of code, and
QuantumCircuit
methods often mutate the existing object.
The rest of this page illustrates how to manipulate quantum circuits.
Build circuits
Methods such as QuantumCircuit.h
and QuantumCircuit.cx
add specific instructions to circuits. To add instructions to a circuit more generally, use the append
method. This takes an instruction and a list of qubits to apply the instruction to. See the Circuit Library API documentation for a list of supported instructions.
from qiskit.circuit.library import HGate
qc = QuantumCircuit(1)
qc.append(
HGate(), # New HGate instruction
[0] # Apply to qubit 0
)
qc.draw("mpl")
Output:
To combine two circuits, use the compose
method. This accepts another QuantumCircuit
and an optional list of qubit mappings.
qc_a = QuantumCircuit(4)
qc_a.x(0)
qc_b = QuantumCircuit(2, name="qc_b")
qc_b.y(0)
qc_b.z(1)
# compose qubits (0, 1) of qc_a to qubits (1, 3) of qc_b respectively
combined = qc_a.compose(qc_b, qubits=[1, 3])
combined.draw("mpl")
Output:
You might also want to compile circuits into instructions to keep your circuits organized. You can convert a circuit to an instruction by using the to_instruction
method, then append this to another circuit as you would any other instruction. The circuit drawn in the following cell is functionally equivalent to the circuit drawn in the previous cell.
inst = qc_b.to_instruction()
qc_a.append(inst, [1, 3])
qc_a.draw("mpl")
Output:
If your circuit is unitary, you can convert it to a Gate
by using the to_gate
method. Gate
objects are specific types of instructions that have some extra features, such as the control
method, which adds a quantum control.
gate = qc_b.to_gate().control()
qc_a.append(gate, [0, 1, 3])
qc_a.draw("mpl")
Output:
To see what's going on, you can use the decompose
method to expand each instruction into its definition.
The decompose
method returns a new circuit and does not mutate the circuit it acts on.
qc_a.decompose().draw("mpl")
Output:
Parameterized circuits
Many near-term quantum algorithms involve executing many variations of a quantum circuit. Since constructing and optimizing large circuits can be computationally expensive, Qiskit supports parameterized circuits. These circuits have undefined parameters, and their values do not need to be defined until just before executing the circuit. This lets you move circuit construction and optimization out of the main program loop. The following cell creates and displays a parameterized circuit.
[10] :from qiskit.circuit import Parameter
angle = Parameter("angle") # undefined number
# Create and optimize circuit once
qc = QuantumCircuit(1)
qc.rx(angle, 0)
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
qc = generate_preset_pass_manager(optimization_level=3, basis_gates=['u', 'cx']).run(qc)
qc.draw("mpl")
Output:
The following cell creates many variations of this circuit and displays one of the variations.
[11] :circuits = []
for value in range(100):
circuits.append(
qc.assign_parameters({ angle: value })
)
circuits[0].draw("mpl")
Output:
You can find a list of a circuit's undefined parameters in its parameters
attribute.
qc.parameters
Output:
ParameterView([Parameter(angle)])
Forgotten the method name? Try asking Qiskit Code Assistant.
# Assign all parameters in qc to 0
New to the code assistant? See Qiskit Code Assistant for installation and usage. Note this is an experimental feature and only available to IBM Quantum™ Premium Plan users.
Next steps
- To learn about near-term quantum algorithms, take our Variational algorithm design course.
- See an example of circuits being used in the Grover's Algorithm tutorial.
- Work with simple circuits in the Explore gates and circuits with the Quantum Composer tutorial.