Files
claude-scientific-skills/scientific-skills/pennylane/references/quantum_circuits.md

9.2 KiB

Quantum Circuits in PennyLane

Table of Contents

  1. Basic Gates and Operations
  2. Multi-Qubit Gates
  3. Controlled Operations
  4. Measurements
  5. Circuit Construction Patterns
  6. Dynamic Circuits
  7. Circuit Inspection

Basic Gates and Operations

Single-Qubit Gates

import pennylane as qml

# Pauli gates
qml.PauliX(wires=0)  # X gate (bit flip)
qml.PauliY(wires=0)  # Y gate
qml.PauliZ(wires=0)  # Z gate (phase flip)

# Hadamard gate (superposition)
qml.Hadamard(wires=0)

# Phase gates
qml.S(wires=0)       # S gate (π/2 phase)
qml.T(wires=0)       # T gate (π/4 phase)
qml.PhaseShift(phi, wires=0)  # Arbitrary phase

# Rotation gates (parameterized)
qml.RX(theta, wires=0)  # Rotation around X-axis
qml.RY(theta, wires=0)  # Rotation around Y-axis
qml.RZ(theta, wires=0)  # Rotation around Z-axis

# General single-qubit rotation
qml.Rot(phi, theta, omega, wires=0)

# Universal gate (any single-qubit unitary)
qml.U3(theta, phi, delta, wires=0)

Basis State Preparation

# Computational basis state
qml.BasisState([1, 0, 1], wires=[0, 1, 2])  # |101⟩

# Amplitude encoding
amplitudes = [0.5, 0.5, 0.5, 0.5]  # Must be normalized
qml.MottonenStatePreparation(amplitudes, wires=[0, 1])

Multi-Qubit Gates

Two-Qubit Gates

# CNOT (Controlled-NOT)
qml.CNOT(wires=[0, 1])  # control=0, target=1

# CZ (Controlled-Z)
qml.CZ(wires=[0, 1])

# SWAP gate
qml.SWAP(wires=[0, 1])

# Controlled rotations
qml.CRX(theta, wires=[0, 1])
qml.CRY(theta, wires=[0, 1])
qml.CRZ(theta, wires=[0, 1])

# Ising coupling gates
qml.IsingXX(phi, wires=[0, 1])
qml.IsingYY(phi, wires=[0, 1])
qml.IsingZZ(phi, wires=[0, 1])

Multi-Qubit Gates

# Toffoli gate (CCNOT)
qml.Toffoli(wires=[0, 1, 2])  # control=0,1, target=2

# Multi-controlled X
qml.MultiControlledX(control_wires=[0, 1, 2], wires=3)

# Multi-qubit Pauli rotations
qml.MultiRZ(theta, wires=[0, 1, 2])

Controlled Operations

General Controlled Operations

# Apply controlled version of any operation
qml.ctrl(qml.RX(0.5, wires=1), control=0)

# Multiple control qubits
qml.ctrl(qml.RY(0.3, wires=2), control=[0, 1])

# Negative controls (activate when control is |0⟩)
qml.ctrl(qml.Hadamard(wires=2), control=0, control_values=[0])

Conditional Operations

@qml.qnode(dev)
def conditional_circuit():
    qml.Hadamard(wires=0)

    # Mid-circuit measurement
    m = qml.measure(0)

    # Apply gate conditionally
    qml.cond(m, qml.PauliX)(wires=1)

    return qml.expval(qml.PauliZ(1))

Measurements

Expectation Values

@qml.qnode(dev)
def measure_expectation():
    qml.Hadamard(wires=0)

    # Single observable
    return qml.expval(qml.PauliZ(0))

@qml.qnode(dev)
def measure_tensor():
    qml.Hadamard(wires=0)
    qml.Hadamard(wires=1)

    # Tensor product of observables
    return qml.expval(qml.PauliZ(0) @ qml.PauliZ(1))

Probability Distributions

@qml.qnode(dev)
def measure_probabilities():
    qml.Hadamard(wires=0)
    qml.CNOT(wires=[0, 1])

    # Probabilities of all basis states
    return qml.probs(wires=[0, 1])  # Returns [p(|00⟩), p(|01⟩), p(|10⟩), p(|11⟩)]

Samples and Counts

@qml.qnode(dev)
def measure_samples(shots=1000):
    qml.Hadamard(wires=0)

    # Raw samples
    return qml.sample(qml.PauliZ(0))

@qml.qnode(dev)
def measure_counts(shots=1000):
    qml.Hadamard(wires=0)
    qml.CNOT(wires=[0, 1])

    # Count occurrences
    return qml.counts(wires=[0, 1])

Variance

@qml.qnode(dev)
def measure_variance():
    qml.RX(0.5, wires=0)

    # Variance of observable
    return qml.var(qml.PauliZ(0))

Mid-Circuit Measurements

@qml.qnode(dev)
def mid_circuit_measure():
    qml.Hadamard(wires=0)

    # Measure qubit 0 during circuit
    m0 = qml.measure(0)

    # Use measurement result
    qml.cond(m0, qml.PauliX)(wires=1)

    # Final measurement
    return qml.expval(qml.PauliZ(1))

Circuit Construction Patterns

Layer-Based Construction

def layer(weights, wires):
    """Single layer of parameterized gates."""
    for i, wire in enumerate(wires):
        qml.RY(weights[i], wires=wire)

    for wire in wires[:-1]:
        qml.CNOT(wires=[wire, wire+1])

@qml.qnode(dev)
def layered_circuit(weights):
    n_layers = len(weights)
    wires = range(4)

    for i in range(n_layers):
        layer(weights[i], wires)

    return qml.expval(qml.PauliZ(0))

Data Encoding

def angle_encoding(x, wires):
    """Encode classical data as rotation angles."""
    for i, wire in enumerate(wires):
        qml.RX(x[i], wires=wire)

def amplitude_encoding(x, wires):
    """Encode data as quantum state amplitudes."""
    qml.MottonenStatePreparation(x, wires=wires)

def basis_encoding(x, wires):
    """Encode binary data in computational basis."""
    for i, val in enumerate(x):
        if val:
            qml.PauliX(wires=i)

Ansatz Patterns

# Hardware-efficient ansatz
def hardware_efficient_ansatz(weights, wires):
    n_layers = len(weights) // len(wires)

    for layer in range(n_layers):
        # Rotation layer
        for i, wire in enumerate(wires):
            qml.RY(weights[layer * len(wires) + i], wires=wire)

        # Entanglement layer
        for wire in wires[:-1]:
            qml.CNOT(wires=[wire, wire+1])

# Alternating layered ansatz
def alternating_ansatz(weights, wires):
    for w in weights:
        for wire in wires:
            qml.RX(w[wire], wires=wire)
        for wire in wires[:-1]:
            qml.CNOT(wires=[wire, wire+1])

Dynamic Circuits

For Loops

@qml.qnode(dev)
def dynamic_for_loop(n_iterations):
    qml.Hadamard(wires=0)

    # Dynamic for loop
    for i in range(n_iterations):
        qml.RX(0.1 * i, wires=0)

    return qml.expval(qml.PauliZ(0))

While Loops (with Catalyst)

@qml.qjit  # Just-in-time compilation
@qml.qnode(dev)
def dynamic_while_loop():
    qml.Hadamard(wires=0)

    # Dynamic while loop
    @qml.while_loop(lambda i: i < 5)
    def loop(i):
        qml.RX(0.1, wires=0)
        return i + 1

    loop(0)
    return qml.expval(qml.PauliZ(0))

Adaptive Circuits

@qml.qnode(dev)
def adaptive_circuit():
    qml.Hadamard(wires=0)

    # Measure and adapt
    m = qml.measure(0)

    # Different paths based on measurement
    if m:
        qml.RX(0.5, wires=1)
    else:
        qml.RY(0.5, wires=1)

    return qml.expval(qml.PauliZ(1))

Circuit Inspection

Drawing Circuits

# Text representation
print(qml.draw(circuit)(params))

# ASCII art
print(qml.draw(circuit, wire_order=[0,1,2])(params))

# Matplotlib visualization
fig, ax = qml.draw_mpl(circuit)(params)

Analyzing Circuit Structure

# Get circuit specs
specs = qml.specs(circuit)(params)
print(f"Gates: {specs['gate_sizes']}")
print(f"Depth: {specs['depth']}")
print(f"Parameters: {specs['num_trainable_params']}")

# Resource estimation
resources = qml.resource.resource_estimation(circuit)(params)
print(f"Total gates: {resources['num_gates']}")

Tape Inspection

# Record operations
with qml.tape.QuantumTape() as tape:
    qml.Hadamard(wires=0)
    qml.CNOT(wires=[0, 1])
    qml.expval(qml.PauliZ(0))

# Inspect tape contents
print("Operations:", tape.operations)
print("Measurements:", tape.measurements)
print("Wires used:", tape.wires)

Circuit Transformations

# Expand composite operations
expanded = qml.transforms.expand_tape(tape)

# Cancel adjacent operations
optimized = qml.transforms.cancel_inverses(tape)

# Commute measurements to end
commuted = qml.transforms.commute_controlled(tape)

Best Practices

  1. Use native gates - Prefer gates supported by target device
  2. Minimize circuit depth - Reduce decoherence effects
  3. Encode efficiently - Choose encoding matching data structure
  4. Reuse circuits - Cache compiled circuits when possible
  5. Validate measurements - Ensure observables are Hermitian
  6. Check qubit count - Verify device has sufficient wires
  7. Profile circuits - Use qml.specs() to analyze complexity

Common Patterns

Bell State Preparation

@qml.qnode(dev)
def bell_state():
    qml.Hadamard(wires=0)
    qml.CNOT(wires=[0, 1])
    return qml.state()  # Returns |Φ+⟩ = (|00⟩ + |11⟩)/√2

GHZ State

@qml.qnode(dev)
def ghz_state(n_qubits):
    qml.Hadamard(wires=0)
    for i in range(n_qubits-1):
        qml.CNOT(wires=[0, i+1])
    return qml.state()

Quantum Fourier Transform

def qft(wires):
    """Quantum Fourier Transform."""
    n_wires = len(wires)
    for i in range(n_wires):
        qml.Hadamard(wires=wires[i])
        for j in range(i+1, n_wires):
            qml.CRZ(np.pi / (2**(j-i)), wires=[wires[j], wires[i]])

Inverse QFT

def inverse_qft(wires):
    """Inverse Quantum Fourier Transform."""
    n_wires = len(wires)
    for i in range(n_wires-1, -1, -1):
        for j in range(n_wires-1, i, -1):
            qml.CRZ(-np.pi / (2**(j-i)), wires=[wires[j], wires[i]])
        qml.Hadamard(wires=wires[i])