Skip to main content

Overview

The QuantumCircuit class is a pure data structure that stores an ordered sequence of quantum gate instructions. It does not execute gates itself — execution is performed by QuantumComputer.run(). This separation follows the Open/Closed Principle and allows the same circuit to be executed on different physics backends. Key Features:
  • Fluent API with method chaining
  • Supports all standard gates (H, X, Y, Z, S, T, Rx, Ry, Rz, CNOT, CZ, SWAP, Toffoli)
  • Multi-controlled gates (MCZ for Grover oracles)
  • Custom physics evolution gates
  • Automatic qubit index validation
Location: quantum_computer.py:1191-1274

Constructor

QuantumCircuit(n_qubits: int)
Create an empty quantum circuit for n qubits.
n_qubits
int
required
Number of qubits in the circuit. Must be ≥ 1.

Example

from quantum_computer import QuantumCircuit

# Create a 3-qubit circuit
circuit = QuantumCircuit(3)
print(circuit)  # QuantumCircuit(3 qubits, depth=0)

Single-Qubit Gates

All single-qubit gate methods return self for method chaining.

h (Hadamard)

h(q: int) -> QuantumCircuit
Apply Hadamard gate: H = [[1, 1], [1, -1]] / √2
q
int
required
Target qubit index (0-indexed)
circuit.h(0)  # H on qubit 0

x (Pauli-X)

x(q: int) -> QuantumCircuit
Apply Pauli-X gate (bit flip): X = [[0, 1], [1, 0]]
q
int
required
Target qubit index
circuit.x(1)  # X on qubit 1

y (Pauli-Y)

y(q: int) -> QuantumCircuit
Apply Pauli-Y gate: Y = [[0, -i], [i, 0]]
q
int
required
Target qubit index
circuit.y(2)  # Y on qubit 2

z (Pauli-Z)

z(q: int) -> QuantumCircuit
Apply Pauli-Z gate (phase flip): Z = [[1, 0], [0, -1]]
q
int
required
Target qubit index
circuit.z(0)  # Z on qubit 0

s (S gate)

s(q: int) -> QuantumCircuit
Apply S gate (phase): S = [[1, 0], [0, i]]
q
int
required
Target qubit index
circuit.s(1)  # S on qubit 1

t (T gate)

t(q: int) -> QuantumCircuit
Apply T gate (π/8 phase): T = [[1, 0], [0, e^(iπ/4)]]
q
int
required
Target qubit index
circuit.t(0)  # T on qubit 0

Rotation Gates

rx (Rotation around X)

rx(q: int, theta: float) -> QuantumCircuit
Apply Rx rotation: Rx(θ) = exp(-i θ/2 X)
q
int
required
Target qubit index
theta
float
required
Rotation angle in radians
import math
circuit.rx(0, math.pi / 2)  # π/2 rotation around X

ry (Rotation around Y)

ry(q: int, theta: float) -> QuantumCircuit
Apply Ry rotation: Ry(θ) = exp(-i θ/2 Y)
q
int
required
Target qubit index
theta
float
required
Rotation angle in radians
import math
circuit.ry(1, math.pi / 3)  # π/3 rotation around Y

rz (Rotation around Z)

rz(q: int, theta: float) -> QuantumCircuit
Apply Rz rotation: Rz(θ) = exp(-i θ/2 Z)
q
int
required
Target qubit index
theta
float
required
Rotation angle in radians
import math
circuit.rz(2, math.pi / 4)  # π/4 rotation around Z

Two-Qubit Gates

cnot / cx (Controlled-NOT)

cnot(ctrl: int, tgt: int) -> QuantumCircuit
cx(ctrl: int, tgt: int) -> QuantumCircuit  # Alias
Apply CNOT gate: |ctrl, tgt⟩ → |ctrl, ctrl ⊕ tgt⟩
ctrl
int
required
Control qubit index
tgt
int
required
Target qubit index
circuit.cnot(0, 1)  # CNOT with control=0, target=1
circuit.cx(0, 1)    # Same as above

cz (Controlled-Z)

cz(ctrl: int, tgt: int) -> QuantumCircuit
Apply CZ gate: applies phase -1 to |11⟩
ctrl
int
required
Control qubit index
tgt
int
required
Target qubit index
circuit.cz(0, 1)  # CZ between qubits 0 and 1

swap (SWAP)

swap(a: int, b: int) -> QuantumCircuit
Swap two qubits: |ab⟩ → |ba⟩
a
int
required
First qubit index
b
int
required
Second qubit index
circuit.swap(0, 2)  # Swap qubits 0 and 2

Multi-Qubit Gates

toffoli / ccx (Toffoli/CCX)

toffoli(c0: int, c1: int, tgt: int) -> QuantumCircuit
ccx(c0: int, c1: int, tgt: int) -> QuantumCircuit  # Alias
Apply Toffoli (controlled-controlled-X): flip target iff both controls are |1⟩
c0
int
required
First control qubit index
c1
int
required
Second control qubit index
tgt
int
required
Target qubit index
circuit.toffoli(0, 1, 2)  # Toffoli with controls 0,1 and target 2
circuit.ccx(0, 1, 2)      # Same as above

_append (Advanced: Custom Gates)

_append(
    gate_name: str,
    targets: List[int],
    params: Optional[Dict[str, float]] = None
) -> QuantumCircuit
Low-level method to append any gate from the registry. Used internally by all gate methods.
gate_name
str
required
Gate name from the global gate registry (e.g., “H”, “CNOT”, “MCZ”, “Evolve”)
targets
List[int]
required
List of target qubit indices
params
Dict[str, float]
default:"None"
Optional dictionary of gate parameters
# Multi-controlled Z (for Grover oracle)
circuit._append("MCZ", [0, 1, 2])  # Phase flip on |111⟩

# Custom evolution
circuit._append("Evolve", [0, 1], {"dt": 0.01, "steps": 10})

Physics Evolution

evolve

evolve(
    qubits: List[int],
    dt: float = 0.01,
    steps: int = 1
) -> QuantumCircuit
Apply free Hamiltonian evolution to specified qubits using the active physics backend.
qubits
List[int]
required
List of qubit indices to evolve
dt
float
default:"0.01"
Time step for evolution
steps
int
default:"1"
Number of evolution steps
# Evolve qubits 0 and 1 for 10 time steps
circuit.evolve([0, 1], dt=0.01, steps=10)

Utility Methods

barrier

barrier() -> QuantumCircuit
Logical barrier (no-op for visualization/organization).
circuit.h(0).barrier().cnot(0, 1)

depth

depth() -> int
Return the circuit depth (number of instructions).
depth
int
Number of gate instructions in the circuit
print(circuit.depth())  # 5

len

__len__() -> int
Return the circuit length (same as depth).
print(len(circuit))  # 5

repr

__repr__() -> str
Return a human-readable string representation.
print(circuit)
# Output:
# QuantumCircuit(3 qubits, depth=5)
#   [000] H q[0]
#   [001] CNOT q[0, 1]
#   [002] Rz q[2]
#   ...

Complete Examples

Bell State

from quantum_computer import QuantumCircuit

circuit = QuantumCircuit(2)
circuit.h(0).cnot(0, 1)
print(circuit.depth())  # 2

GHZ State

circuit = QuantumCircuit(3)
circuit.h(0)
for i in range(2):
    circuit.cnot(i, i + 1)

Quantum Fourier Transform

import math

def qft(n_qubits: int) -> QuantumCircuit:
    circuit = QuantumCircuit(n_qubits)
    for i in range(n_qubits):
        circuit.h(i)
        for j in range(i + 1, n_qubits):
            circuit.rz(j, math.pi / (2 ** (j - i)))
    return circuit

qft_circuit = qft(3)

Variational Ansatz

import math

def variational(n_qubits: int, thetas: list) -> QuantumCircuit:
    circuit = QuantumCircuit(n_qubits)
    n_layers = len(thetas) // n_qubits
    for layer in range(n_layers):
        # Ry layer
        for q in range(n_qubits):
            circuit.ry(q, thetas[layer * n_qubits + q])
        # Entangling layer
        for q in range(n_qubits - 1):
            circuit.cnot(q, q + 1)
    return circuit

thetas = [math.pi/4, math.pi/3, math.pi/6, math.pi/2]
circuit = variational(2, thetas)

Grover Oracle (3-qubit)

def grover_oracle(n_qubits: int, target: str) -> QuantumCircuit:
    """Phase oracle that marks target bitstring."""
    circuit = QuantumCircuit(n_qubits)
    
    # Apply X where target bit is '0'
    for i, bit in enumerate(target):
        if bit == "0":
            circuit.x(i)
    
    # Multi-controlled Z
    circuit._append("MCZ", list(range(n_qubits)))
    
    # Undo X gates
    for i, bit in enumerate(target):
        if bit == "0":
            circuit.x(i)
    
    return circuit

oracle = grover_oracle(3, "101")

Method Chaining

# Build complex circuits with fluent API
circuit = (QuantumCircuit(4)
    .h(0).h(1).h(2).h(3)          # Initial superposition
    .cnot(0, 1).cnot(2, 3)        # Entangle pairs
    .rz(1, math.pi/4)             # Phase rotation
    .cz(1, 2)                     # Controlled-Z
    .barrier()                     # Logical separation
    .h(0).h(1).h(2).h(3))         # Final Hadamards

print(f"Circuit depth: {circuit.depth()}")

Data Structure

CircuitInstruction

Internal dataclass for gate instructions:
@dataclass
class CircuitInstruction:
    gate_name: str                          # Gate identifier
    targets: List[int]                      # Qubit indices
    params: Optional[Dict[str, float]]      # Gate parameters
Accessed via circuit._instructions (list of instructions).

Gate Registry

All gates are registered in the global _GATE_REGISTRY dictionary. To add custom gates:
from quantum_computer import register_gate, IQuantumGate

class CustomGate(IQuantumGate):
    @property
    def name(self) -> str:
        return "CUSTOM"
    
    def apply(self, state, backend, targets, params):
        # Implementation
        return state

register_gate("CUSTOM", CustomGate())

# Use in circuit
circuit._append("CUSTOM", [0, 1], {"param": 1.0})

See Also

Build docs developers (and LLMs) love