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

13 KiB

Devices and Backends in PennyLane

Table of Contents

  1. Built-in Simulators
  2. Hardware Plugins
  3. Device Selection
  4. Device Configuration
  5. Custom Devices
  6. Performance Optimization

Built-in Simulators

default.qubit

General-purpose state vector simulator:

import pennylane as qml

# Basic initialization
dev = qml.device('default.qubit', wires=4)

# With shots (sampling mode)
dev = qml.device('default.qubit', wires=4, shots=1000)

# Specify wire labels
dev = qml.device('default.qubit', wires=['a', 'b', 'c', 'd'])

default.mixed

Mixed-state simulator for noisy quantum systems:

# Supports density matrix simulation
dev = qml.device('default.mixed', wires=2)

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

    # Apply noise
    qml.DepolarizingChannel(0.1, wires=0)

    qml.CNOT(wires=[0, 1])

    # Amplitude damping
    qml.AmplitudeDamping(0.05, wires=1)

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

default.qubit.torch, default.qubit.tf, default.qubit.jax

Framework-specific simulators with better integration:

# PyTorch
dev = qml.device('default.qubit.torch', wires=4)

# TensorFlow
dev = qml.device('default.qubit.tf', wires=4)

# JAX
dev = qml.device('default.qubit.jax', wires=4)

lightning.qubit

High-performance C++ simulator:

# Faster than default.qubit
dev = qml.device('lightning.qubit', wires=20)

# Supports larger systems efficiently
@qml.qnode(dev)
def large_circuit():
    for i in range(20):
        qml.Hadamard(wires=i)

    for i in range(19):
        qml.CNOT(wires=[i, i+1])

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

default.clifford

Efficient simulator for Clifford circuits:

# Only supports Clifford gates (H, S, CNOT, etc.)
dev = qml.device('default.clifford', wires=100)

@qml.qnode(dev)
def clifford_circuit():
    qml.Hadamard(wires=0)
    qml.CNOT(wires=[0, 1])
    qml.S(wires=1)
    # Cannot use RX, RY, RZ, etc.

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

Hardware Plugins

IBM Quantum (Qiskit)

# Install plugin
uv pip install pennylane-qiskit
import pennylane as qml

# Use IBM simulator
dev = qml.device('qiskit.aer', wires=2)

# Use IBM quantum hardware
dev = qml.device(
    'qiskit.ibmq',
    wires=2,
    backend='ibmq_manila',  # Specify backend
    shots=1024
)

# With API token
dev = qml.device(
    'qiskit.ibmq',
    wires=2,
    backend='ibmq_manila',
    ibmqx_token='YOUR_API_TOKEN'
)

@qml.qnode(dev)
def circuit():
    qml.Hadamard(wires=0)
    qml.CNOT(wires=[0, 1])
    return qml.expval(qml.PauliZ(0))

Amazon Braket

# Install plugin
uv pip install amazon-braket-pennylane-plugin
# Use Braket simulators
dev = qml.device(
    'braket.local.qubit',
    wires=2
)

# Use AWS simulators
dev = qml.device(
    'braket.aws.qubit',
    device_arn='arn:aws:braket:::device/quantum-simulator/amazon/sv1',
    wires=4,
    s3_destination_folder=('amazon-braket-outputs', 'outputs')
)

# Use quantum hardware (IonQ, Rigetti, etc.)
dev = qml.device(
    'braket.aws.qubit',
    device_arn='arn:aws:braket:us-east-1::device/qpu/ionq/Harmony',
    wires=11,
    shots=1000,
    s3_destination_folder=('amazon-braket-outputs', 'outputs')
)

Google Cirq

# Install plugin
uv pip install pennylane-cirq
# Use Cirq simulator
dev = qml.device('cirq.simulator', wires=2)

# Use Cirq with qsim (faster)
dev = qml.device('cirq.qsim', wires=20)

# Use Google quantum hardware (if you have access)
dev = qml.device(
    'cirq.pasqal',
    wires=2,
    device='rainbow',
    shots=1000
)

Rigetti Forest

# Install plugin
uv pip install pennylane-rigetti
# Use QVM (Quantum Virtual Machine)
dev = qml.device('rigetti.qvm', device='4q-qvm', shots=1000)

# Use Rigetti QPU
dev = qml.device('rigetti.qpu', device='Aspen-M-3', shots=1000)

Microsoft Azure Quantum

# Install plugin
uv pip install pennylane-azure
# Use Azure simulators
dev = qml.device(
    'azure.simulator',
    wires=4,
    workspace='your-workspace',
    resource_group='your-resource-group',
    subscription_id='your-subscription-id'
)

# Use IonQ on Azure
dev = qml.device(
    'azure.ionq',
    wires=11,
    workspace='your-workspace',
    resource_group='your-resource-group',
    subscription_id='your-subscription-id',
    shots=500
)

IonQ

# Install plugin
uv pip install pennylane-ionq
# Use IonQ hardware
dev = qml.device(
    'ionq.simulator',  # or 'ionq.qpu'
    wires=11,
    shots=1024,
    api_key='your_api_key'
)

Xanadu Hardware (Borealis)

# Photonic quantum computer
dev = qml.device(
    'strawberryfields.remote',
    backend='borealis',
    shots=10000
)

Device Selection

Choosing the Right Device

def select_device(n_qubits, use_hardware=False, noise_model=None):
    """Select appropriate device based on requirements."""

    if use_hardware:
        # Use real quantum hardware
        if n_qubits <= 11:
            return qml.device('ionq.qpu', wires=n_qubits, shots=1000)
        elif n_qubits <= 127:
            return qml.device('qiskit.ibmq', wires=n_qubits, shots=1024)
        else:
            raise ValueError(f"No hardware available for {n_qubits} qubits")

    elif noise_model:
        # Use noisy simulator
        return qml.device('default.mixed', wires=n_qubits)

    else:
        # Use ideal simulator
        if n_qubits <= 20:
            return qml.device('lightning.qubit', wires=n_qubits)
        else:
            return qml.device('default.qubit', wires=n_qubits)

# Usage
dev = select_device(n_qubits=10, use_hardware=False)

Device Capabilities

# Check device capabilities
dev = qml.device('default.qubit', wires=4)

print("Device name:", dev.name)
print("Number of wires:", dev.num_wires)
print("Supports shots:", dev.shots is not None)

# Check supported operations
print("Supported gates:", dev.operations)

# Check supported observables
print("Supported observables:", dev.observables)

Device Configuration

Setting Shots

# Exact simulation (no shots)
dev = qml.device('default.qubit', wires=2)

@qml.qnode(dev)
def exact_circuit():
    qml.Hadamard(wires=0)
    return qml.expval(qml.PauliZ(0))

result = exact_circuit()  # Returns exact expectation

# Sampling mode (with shots)
dev_sampled = qml.device('default.qubit', wires=2, shots=1000)

@qml.qnode(dev_sampled)
def sampled_circuit():
    qml.Hadamard(wires=0)
    return qml.expval(qml.PauliZ(0))

result = sampled_circuit()  # Estimated from samples

Dynamic Shots

# Change shots per execution
dev = qml.device('default.qubit', wires=2)

@qml.qnode(dev)
def circuit():
    qml.Hadamard(wires=0)
    return qml.expval(qml.PauliZ(0))

# Different shot numbers
result_100 = circuit(shots=100)
result_1000 = circuit(shots=1000)
result_exact = circuit(shots=None)  # Exact

Analytic Mode vs Finite Shots

# Compare analytic vs sampled
dev_analytic = qml.device('default.qubit', wires=2)
dev_sampled = qml.device('default.qubit', wires=2, shots=1000)

@qml.qnode(dev_analytic)
def circuit_analytic(x):
    qml.RX(x, wires=0)
    return qml.expval(qml.PauliZ(0))

@qml.qnode(dev_sampled)
def circuit_sampled(x):
    qml.RX(x, wires=0)
    return qml.expval(qml.PauliZ(0))

import numpy as np
x = np.pi / 4

print(f"Analytic: {circuit_analytic(x)}")
print(f"Sampled: {circuit_sampled(x)}")
print(f"Exact value: {np.cos(x)}")

Seed for Reproducibility

# Set random seed
dev = qml.device('default.qubit', wires=2, shots=1000, seed=42)

@qml.qnode(dev)
def circuit():
    qml.Hadamard(wires=0)
    return qml.sample(qml.PauliZ(0))

# Reproducible results
samples1 = circuit()
samples2 = circuit()  # Same as samples1 if seed is set

Custom Devices

Creating a Custom Device

from pennylane.devices import DefaultQubit

class CustomDevice(DefaultQubit):
    """Custom quantum device with additional features."""

    name = 'Custom device'
    short_name = 'custom'
    pennylane_requires = '>=0.30.0'
    version = '0.1.0'
    author = 'Your Name'

    def __init__(self, wires, shots=None, **kwargs):
        super().__init__(wires=wires, shots=shots)
        # Custom initialization

    def apply(self, operations, **kwargs):
        """Apply operations with custom logic."""
        # Custom operation handling
        for op in operations:
            # Log or modify operations
            print(f"Applying: {op.name}")

        # Call parent implementation
        super().apply(operations, **kwargs)

# Use custom device
dev = CustomDevice(wires=4)

Plugin Development

# Define custom plugin operations
class CustomGate(qml.operation.Operation):
    """Custom quantum gate."""

    num_wires = 1
    num_params = 1
    par_domain = 'R'

    def decomposition(self):
        """Decompose into standard gates."""
        theta = self.parameters[0]
        wires = self.wires

        return [
            qml.RY(theta / 2, wires=wires),
            qml.RZ(theta, wires=wires),
            qml.RY(-theta / 2, wires=wires)
        ]

# Register with device
qml.ops.CustomGate = CustomGate

Performance Optimization

Batch Execution

# Execute multiple parameter sets efficiently
dev = qml.device('default.qubit', wires=2)

@qml.qnode(dev)
def circuit(params):
    qml.RX(params[0], wires=0)
    qml.RY(params[1], wires=1)
    qml.CNOT(wires=[0, 1])
    return qml.expval(qml.PauliZ(0))

# Batch parameters
params_batch = np.random.random((100, 2))

# Vectorized execution (faster)
results = [circuit(p) for p in params_batch]

Device Caching

# Cache device for reuse
_device_cache = {}

def get_device(n_qubits, device_type='default.qubit'):
    """Get or create cached device."""
    key = (device_type, n_qubits)

    if key not in _device_cache:
        _device_cache[key] = qml.device(device_type, wires=n_qubits)

    return _device_cache[key]

# Reuse devices
dev1 = get_device(4)
dev2 = get_device(4)  # Returns same device

JIT Compilation with Catalyst

# Install Catalyst
# uv pip install pennylane-catalyst

import pennylane as qml
from catalyst import qjit

dev = qml.device('lightning.qubit', wires=4)

@qjit  # Just-in-time compilation
@qml.qnode(dev)
def compiled_circuit(x):
    qml.RX(x, wires=0)
    qml.Hadamard(wires=1)
    qml.CNOT(wires=[0, 1])
    return qml.expval(qml.PauliZ(0))

# First call compiles, subsequent calls are fast
result = compiled_circuit(0.5)

Parallel Execution

from multiprocessing import Pool

def run_circuit(params):
    """Run circuit with given parameters."""
    dev = qml.device('default.qubit', wires=4)

    @qml.qnode(dev)
    def circuit(p):
        # Circuit definition
        return qml.expval(qml.PauliZ(0))

    return circuit(params)

# Parallel execution
param_list = [np.random.random(10) for _ in range(100)]

with Pool(processes=4) as pool:
    results = pool.map(run_circuit, param_list)

GPU Acceleration

# Use GPU-accelerated devices if available
try:
    dev = qml.device('lightning.gpu', wires=20)
except:
    dev = qml.device('lightning.qubit', wires=20)

@qml.qnode(dev)
def gpu_circuit():
    # Large circuit benefits from GPU
    for i in range(20):
        qml.Hadamard(wires=i)

    for i in range(19):
        qml.CNOT(wires=[i, i+1])

    return [qml.expval(qml.PauliZ(i)) for i in range(20)]

Best Practices

  1. Start with simulators - Test on default.qubit before hardware
  2. Use lightning for speed - Switch to lightning.qubit for larger circuits
  3. Match device to task - Use default.mixed for noise studies
  4. Cache devices - Reuse device objects to avoid initialization overhead
  5. Set appropriate shots - Balance accuracy vs speed
  6. Check capabilities - Verify device supports required operations
  7. Handle hardware errors - Implement retries and error mitigation
  8. Monitor costs - Track hardware usage and costs
  9. Use JIT when possible - Compile circuits with Catalyst for speedup
  10. Test locally first - Validate on simulators before submitting to hardware

Device Comparison

Device Type Max Qubits Speed Noise Use Case
default.qubit Simulator ~25 Medium No General purpose
lightning.qubit Simulator ~30 Fast No Large circuits
default.mixed Simulator ~15 Slow Yes Noise studies
default.clifford Simulator 100+ Very fast No Clifford circuits
IBM Quantum Hardware 127 Slow Yes Real experiments
IonQ Hardware 11 Slow Low High fidelity
Rigetti Hardware 80 Slow Yes Research
Borealis Hardware 216 Slow Yes Photonic QC