Skip to content

Quantum Circuits — Building Quantum Programs Step by Step

DodaTech Updated 2026-06-21 12 min read

In this tutorial, you'll learn about Quantum Circuits. We cover key concepts, practical examples, and best practices to help you understand and apply this topic effectively.

A Quantum Circuit is a sequence of quantum gates and measurements that implements a quantum algorithm, analogous to how a classical circuit implements a boolean function.

What You'll Learn

By the end of this tutorial, you will understand how to construct quantum circuits, how to represent them diagrammatically, how to simulate them in Python, and how to build circuits for common quantum operations.

Why It Matters

Quantum circuits are the programming model for near-term quantum computers. Every major quantum framework (Qiskit, Cirq, Q#) uses circuits as the core abstraction. Understanding circuits lets you design, debug, and optimize quantum algorithms.

Real-World Use

IBM's quantum cloud platform lets users drag-and-drop circuit elements to build programs. Google's Sycamore processor ran random circuit sampling — a specific circuit structure designed to be hard for classical computers to simulate. Error correction circuits form the backbone of fault-tolerant Quantum Computing.

Learning Path

flowchart LR
  A[Quantum Gates] --> B[Quantum Circuits]
  B --> C[Entanglement]
  C --> D[Algorithms]
  B --> E{You Are Here}
  style E fill:#f90,color:#fff
â„šī¸ Info

Prerequisites: Understand quantum gates including Pauli, Hadamard, and CNOT. Familiarity with Python and numpy is helpful.

Anatomy of a Quantum Circuit

A Quantum Circuit consists of:

  1. Qubits: Represented as horizontal lines (wires)
  2. Gates: Operations applied to qubits at specific times
  3. Classical bits: For storing measurement results
  4. Measurement: Reading out qubit states

Circuit Diagram Conventions

|0⟩ ── H ── ● ── M ──
             │
|0⟩ ──────── X ── M ──

This circuit: create Superposition on qubit 0 (H), then CNOT with qubit 0 controlling qubit 1, then measure both qubits.

Building a Quantum Circuit Simulator

Let us build a simple Quantum Circuit simulator that can handle any number of qubits and gates.

# quantum_circuit_sim.py
import numpy as np

class QuantumCircuit:
    def __init__(self, n_qubits):
        self.n_qubits = n_qubits
        self.dim = 2 ** n_qubits
        self.state = np.zeros(self.dim, dtype=complex)
        self.state[0] = 1  # Start in |0...0⟩
        self.gates = []

    def apply_gate(self, gate_matrix, target_qubits):
        """
        Apply a gate to specific qubits.
        gate_matrix: 2^k × 2^k unitary matrix (k = len(target_qubits))
        """
        self.gates.append((gate_matrix.copy(), target_qubits.copy()))
        # Build full operator using tensor products
        full_op = self._build_operator(gate_matrix, target_qubits)
        self.state = full_op @ self.state

    def _build_operator(self, gate_matrix, target_qubits):
        """Build the full 2^n × 2^n operator from a gate on specific qubits."""
        n = self.n_qubits
        k = len(target_qubits)

        # Start with identity
        op = np.array([[1]], dtype=complex)

        # Build qubit by qubit
        qubit_idx = 0
        for q in range(n):
            if qubit_idx < k and q == target_qubits[qubit_idx]:
                # This qubit is involved in the gate
                # Determine which part of the gate matrix applies
                slice_size = 2 ** (k - 1 - qubit_idx)
                # Actually, we need to handle this differently
                qubit_idx += 1
                continue
            else:
                op = np.kron(op, np.eye(2))

        # Simplified approach: use permutation
        return self._permutation_operator(gate_matrix, target_qubits)

    def _permutation_operator(self, gate_matrix, target_qubits):
        """Build the operator by permuting qubits."""
        n = self.n_qubits
        dim = 2 ** n
        full_op = np.eye(dim, dtype=complex)

        # For each basis state, apply the gate to the relevant qubits
        for i in range(dim):
            # Extract the bits involved
            bits = [(i >> (n - 1 - q)) & 1 for q in range(n)]
            target_bits = [bits[q] for q in target_qubits]

            # Convert target bits to index in gate matrix
            gate_idx = sum(b << (len(target_qubits) - 1 - j) for j, b in enumerate(target_bits))

            # Apply gate to this subset
            new_target_bits = self._apply_to_bits(target_bits, gate_matrix)
            new_bits = bits.copy()
            for j, q in enumerate(target_qubits):
                new_bits[q] = new_target_bits[j]

            # Reconstruct full index
            new_idx = sum(new_bits[n - 1 - q] << q for q in range(n))
            # Wait, this is getting complex. Let's use a simpler approach.

        return full_op

    def _apply_to_bits(self, bits, gate_matrix):
        """Apply gate to a list of bits and return new bits."""
        idx = sum(b << (len(bits) - 1 - j) for j, b in enumerate(bits))
        new_state = gate_matrix[:, idx]
        # Find the output index
        for j, val in enumerate(new_state):
            if abs(val) > 1e-10:
                out_bits = [(j >> (len(bits) - 1 - k)) & 1 for k in range(len(bits))]
                return out_bits
        return bits

    def measure_all(self, shots=1024):
        """Simulate measurement of all qubits."""
        probs = np.abs(self.state) ** 2
        outcomes = np.random.choice(self.dim, size=shots, p=probs)
        counts = {}
        for outcome in outcomes:
            bitstring = format(outcome, f'0{self.n_qubits}b')
            counts[bitstring] = counts.get(bitstring, 0) + 1
        return counts

    def __str__(self):
        return f"QuantumCircuit({self.n_qubits} qubits, {len(self.gates)} gates)"

A More Practical Circuit Simulator

Let us build a cleaner version that works correctly for common operations.

# simple_circuit.py
import numpy as np

class SimpleCircuit:
    """A simple quantum circuit simulator with built-in gates."""

    GATES = {
        'H': (1/np.sqrt(2)) * np.array([[1, 1], [1, -1]], dtype=complex),
        'X': np.array([[0, 1], [1, 0]], dtype=complex),
        'Y': np.array([[0, -1j], [1j, 0]], dtype=complex),
        'Z': np.array([[1, 0], [0, -1]], dtype=complex),
    }

    def __init__(self, n_qubits):
        self.n_qubits = n_qubits
        self.dim = 2 ** n_qubits
        self.state = np.zeros(self.dim, dtype=complex)
        self.state[0] = 1.0

    def h(self, qubit):
        """Apply Hadamard to a single qubit."""
        self._apply_single(self.GATES['H'], qubit)

    def x(self, qubit):
        """Apply Pauli-X to a single qubit."""
        self._apply_single(self.GATES['X'], qubit)

    def y(self, qubit):
        """Apply Pauli-Y to a single qubit."""
        self._apply_single(self.GATES['Y'], qubit)

    def z(self, qubit):
        """Apply Pauli-Z to a single qubit."""
        self._apply_single(self.GATES['Z'], qubit)

    def cx(self, control, target):
        """Apply CNOT with given control and target qubits."""
        # Build CNOT on these specific qubits
        n = self.n_qubits
        op = np.eye(self.dim, dtype=complex)

        # Iterate over all basis states
        for i in range(self.dim):
            control_bit = (i >> (n - 1 - control)) & 1
            target_bit = (i >> (n - 1 - target)) & 1
            if control_bit == 1:
                # Flip target bit
                j = i ^ (1 << (n - 1 - target))
                op[i, i] = 0
                op[i, j] = 1
        self.state = op @ self.state

    def _apply_single(self, gate, qubit):
        """Apply a single-qubit gate to a specific qubit."""
        n = self.n_qubits
        dim = 2 ** n
        op = np.eye(dim, dtype=complex)

        for i in range(dim):
            bit = (i >> (n - 1 - qubit)) & 1
            other = i & ~(1 << (n - 1 - qubit))

            # Apply gate to this bit
            for new_bit in [0, 1]:
                if abs(gate[new_bit, bit]) > 1e-15:
                    j = other | (new_bit << (n - 1 - qubit))
                    op[j, i] = gate[new_bit, bit]
        self.state = op @ self.state

    def measure(self, shots=1024):
        """Measure all qubits and return counts."""
        probs = np.abs(self.state) ** 2
        outcomes = np.random.choice(self.dim, size=shots, p=probs)
        counts = {}
        for outcome in outcomes:
            bits = format(outcome, f'0{self.n_qubits}b')
            counts[bits] = counts.get(bits, 0) + 1
        return counts

    def state_vector(self):
        return self.state.copy()

Building Circuits: Examples

Example 1: Bell State

The Bell state (|00⟩ + |11⟩)/√2 is a maximally entangled two-qubit state.

# bell_state_circuit.py
import numpy as np
from simple_circuit import SimpleCircuit

# Create a 2-qubit circuit
qc = SimpleCircuit(2)

print("=== Bell State Circuit ===")
print("Initial: |00⟩")

# Step 1: Hadamard on qubit 0
qc.h(0)
print("After H on q0: creating superposition")

# Step 2: CNOT with control=0, target=1
qc.cx(0, 1)
print("After CNOT: entangling q0 and q1")

# Show final state
state = qc.state_vector()
print(f"\nFinal state vector: {state}")

# Verify it's the Bell state
print(f"Expected: (|00⟩ + |11⟩)/√2")

# Measure
counts = qc.measure(4096)
print(f"\nMeasurement results (4096 shots):")
for bits, count in sorted(counts.items()):
    print(f"  |{bits}⟩: {count} ({count/4096*100:.1f}%)")

Expected output:

=== Bell State Circuit ===
Initial: |00⟩
After H on q0: creating superposition
After CNOT: entangling q0 and q1

Final state vector: [0.70710678+0.j 0.+0.j 0.+0.j 0.70710678+0.j]
Expected: (|00⟩ + |11⟩)/√2

Measurement results (4096 shots):
  |00⟩: 2043 (49.9%)
  |11⟩: 2053 (50.1%)

Example 2: GHZ State

The GHZ state (|000⟩ + |111⟩)/√2 extends entanglement to three qubits.

# ghz_circuit.py
from simple_circuit import SimpleCircuit

# Create a 3-qubit GHZ state
qc = SimpleCircuit(3)

print("=== GHZ State Circuit ===")

# H on qubit 0, then cascading CNOTs
qc.h(0)
qc.cx(0, 1)
qc.cx(0, 2)

state = qc.state_vector()
print(f"Final state: {state}")

counts = qc.measure(4096)
print(f"\nMeasurement results (4096 shots):")
for bits, count in sorted(counts.items()):
    if count > 0:
        print(f"  |{bits}⟩: {count} ({count/4096*100:.1f}%)")

Expected output:

=== GHZ State Circuit ===
Final state: [0.70710678+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.70710678+0.j]

Measurement results (4096 shots):
  |000⟩: 2048 (50.0%)
  |111⟩: 2048 (50.0%)

Example 3: Quantum Random Number Generator

A quantum random number generator uses superposition to produce true random bits.

# quantum_rng.py
from simple_circuit import SimpleCircuit
import numpy as np

def quantum_random_bit():
    """Generate a single random bit using quantum superposition."""
    qc = SimpleCircuit(1)
    qc.h(0)  # Create superposition
    counts = qc.measure(1)
    outcome = list(counts.keys())[0]
    return int(outcome)

def quantum_random_integer(min_val, max_val):
    """Generate a random integer in a range using quantum randomness."""
    range_size = max_val - min_val + 1
    num_bits = int(np.ceil(np.log2(range_size)))
    n_qubits = num_bits
    qc = SimpleCircuit(n_qubits)

    # Apply Hadamard to all qubits
    for q in range(n_qubits):
        qc.h(q)

    counts = qc.measure(1)
    outcome = int(list(counts.keys())[0], 2)
    return min_val + (outcome % range_size)

print("=== Quantum Random Number Generator ===")
print("10 random bits:")
bits = [quantum_random_bit() for _ in range(10)]
print(f"  {''.join(str(b) for b in bits)}")

print("\n10 random integers (1-100):")
nums = [quantum_random_integer(1, 100) for _ in range(10)]
print(f"  {nums}")

Expected output (varies each run):

=== Quantum Random Number Generator ===
10 random bits:
  1011001010

10 random integers (1-100):
  [42, 87, 13, 65, 91, 34, 76, 28, 59, 45]

Circuit Depth and Width

Circuit width is the number of qubits. Circuit depth is the number of time steps (parallel operations count as one step).

graph TD
  subgraph "Circuit Depth = 3, Width = 3"
    t0("t=0: H(q0), H(q1)")
    t1("t=1: CNOT(q0,q2)")
    t2("t=2: CNOT(q1,q2)")
  end

Minimizing depth is critical for near-term Quantum Computing since qubits decohere over time.

Common Mistakes

1. Confusing Circuit Width with Depth

Width = number of qubits. Depth = number of sequential operations. A wide shallow circuit may be easier to run than a narrow deep one.

2. Forgetting Initial States

All qubits initialize to |0⟩ in standard circuit model. If you need a different starting state, you must apply X gates or other operations.

3. Measuring Mid-Circuit Incorrectly

Measurement collapses Superposition. After measurement, the qubit is in a definite state. Subsequent gates operate on the collapsed state, not the original Superposition.

4. Not Reinitializing Qubits

If you reuse a qubit, you must reset it to |0⟩. Many hardware platforms support reset but it is not automatic.

5. Ignoring Gate Commutation

Some gates commute (they can be reordered) and some do not. For example, H and T do not commute. Incorrect reordering changes the computation.

Practice Questions

1. What is the difference between circuit width and circuit depth?

Width is the number of qubits. Depth is the number of sequential gate layers. Both affect the resources needed to run a circuit.

2. How do you create a Bell state using H and CNOT?

H on qubit 0, then CNOT with control qubit 0 and target qubit 1. The resulting state is (|00⟩ + |11⟩)/√2.

3. What is a GHZ state and how is it created?

A GHZ state (|000...0⟩ + |111...1⟩)/√2 is created by H on one qubit followed by cascading CNOTs from that qubit to all others.

4. Why does measurement outcome vary between runs of the same circuit?

Measurement is probabilistic. The same circuit can produce different outcomes, with probabilities determined by the squared amplitudes of the state vector.

5. How do you simulate a Quantum Circuit on classical hardware?

By representing the state as a 2âŋ-dimensional complex vector and applying unitary operations as matrix-vector multiplications. This is feasible for up to about 30-40 qubits.

Challenge: Build a Quantum Teleportation Circuit

Implement the quantum teleportation circuit that transfers an arbitrary qubit state from Alice to Bob using entanglement and classical communication:

def teleport_circuit():
    """Build and simulate a quantum teleportation circuit.
    
    Qubit 0: message state (to be teleported)
    Qubit 1: Alice's half of Bell pair
    Qubit 2: Bob's half of Bell pair
    
    Outcome: Qubit 2 should end up in the original state of qubit 0
    """
    pass

Hints: Create a Bell pair between qubits 1 and 2. Apply CNOT from qubit 0 to 1, then H on qubit 0. Measure qubits 0 and 1. Apply conditional X and Z gates on qubit 2 based on the measurement results.

Real-World Task: Circuit Optimization

Take a given Quantum Circuit and optimize it for depth. Given:

H(q0), H(q1), CNOT(q0,q2), T(q0), T(q1), CNOT(q1,q2)

Try to parallelize operations that commute. Measure the depth before and after optimization. This is what quantum compilers do automatically in frameworks like Qiskit and Cirq.

FAQ

What is a Quantum Circuit?

A Quantum Circuit is a sequence of quantum gates, measurements, and resets applied to qubits. It is the standard model for quantum computation.

How is a Quantum Circuit different from a classical circuit?

Quantum circuits are reversible (gates are unitary), operate on Superposition states, and measurements are probabilistic. Classical circuits use irreversible gates with deterministic outputs.

Can I run quantum circuits on real hardware?

Yes. IBM, Google, IonQ, and Rigetti offer cloud access to quantum processors. You can submit circuits and receive measurement results.

What circuit sizes are currently feasible?

Near-term devices have 50-1000+ qubits but limited depth (due to decoherence). Practical circuits have 10-1000 gates. Fault-tolerant devices will support millions of gates.

Do I need to optimize circuits for hardware?

Yes. Different hardware has different native gate sets and qubit connectivity. Compilers map logical circuits to physical hardware, minimizing depth and respecting connectivity constraints.

Try It Yourself

Run the Bell state circuit and modify it: try different initial states, add measurement mid-circuit, or create a W-state (|001⟩ + |010⟩ + |100⟩)/√3. Observe how the measurement statistics change with different circuit structures.

What's Next

Quantum Entanglement Explained
Quantum Gates Guide
Quantum Measurement Guide

You now understand how to build quantum circuits. Next, you will explore entanglement — the phenomenon that makes Quantum Computing truly powerful.

Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro. Last updated: 2026-06-21.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro