Files

9.3 KiB

Hardware Backends and Execution

Qiskit is backend-agnostic and supports execution on simulators and real quantum hardware from multiple providers.

Backend Types

Local Simulators

  • Run on your machine
  • No account required
  • Perfect for development and testing

Cloud-Based Hardware

  • IBM Quantum (100+ qubit systems)
  • IonQ (trapped ion)
  • Amazon Braket (Rigetti, IonQ, Oxford Quantum Circuits)
  • Other providers via plugins

IBM Quantum Backends

Connecting to IBM Quantum

from qiskit_ibm_runtime import QiskitRuntimeService

# First time: save credentials
QiskitRuntimeService.save_account(
    channel="ibm_quantum",
    token="YOUR_IBM_QUANTUM_TOKEN"
)

# Subsequent sessions: load credentials
service = QiskitRuntimeService()

Listing Available Backends

# List all available backends
backends = service.backends()
for backend in backends:
    print(f"{backend.name}: {backend.num_qubits} qubits")

# Filter by minimum qubits
backends_127q = service.backends(min_num_qubits=127)

# Get specific backend
backend = service.backend("ibm_brisbane")
backend = service.least_busy()  # Get least busy backend

Backend Properties

backend = service.backend("ibm_brisbane")

# Basic info
print(f"Name: {backend.name}")
print(f"Qubits: {backend.num_qubits}")
print(f"Version: {backend.version}")
print(f"Status: {backend.status()}")

# Coupling map (qubit connectivity)
print(backend.coupling_map)

# Basis gates
print(backend.configuration().basis_gates)

# Qubit properties
print(backend.qubit_properties(0))  # Properties of qubit 0

Checking Backend Status

status = backend.status()
print(f"Operational: {status.operational}")
print(f"Pending jobs: {status.pending_jobs}")
print(f"Status message: {status.status_msg}")

Running on IBM Quantum Hardware

Using Runtime Primitives

from qiskit import QuantumCircuit, transpile
from qiskit_ibm_runtime import QiskitRuntimeService, SamplerV2 as Sampler

service = QiskitRuntimeService()
backend = service.backend("ibm_brisbane")

# Create and transpile circuit
qc = QuantumCircuit(2)
qc.h(0)
qc.cx(0, 1)
qc.measure_all()

# Transpile for backend
transpiled_qc = transpile(qc, backend=backend, optimization_level=3)

# Run with Sampler
sampler = Sampler(backend)
job = sampler.run([transpiled_qc], shots=1024)

# Retrieve results
result = job.result()
counts = result[0].data.meas.get_counts()
print(counts)

Job Management

# Submit job
job = sampler.run([qc], shots=1024)

# Get job ID (save for later retrieval)
job_id = job.job_id()
print(f"Job ID: {job_id}")

# Check job status
print(job.status())

# Wait for completion
result = job.result()

# Retrieve job later
service = QiskitRuntimeService()
retrieved_job = service.job(job_id)
result = retrieved_job.result()

Job Queuing

# Check queue position
job_status = job.status()
print(f"Queue position: {job.queue_position()}")

# Cancel job if needed
job.cancel()

Session Mode

Use sessions for iterative algorithms (VQE, QAOA) to reduce queue time:

from qiskit_ibm_runtime import Session, SamplerV2 as Sampler

service = QiskitRuntimeService()
backend = service.backend("ibm_brisbane")

with Session(backend=backend) as session:
    sampler = Sampler(session=session)

    # Multiple iterations in same session
    for iteration in range(10):
        # Parameterized circuit
        qc = create_parameterized_circuit(params[iteration])
        job = sampler.run([qc], shots=1024)
        result = job.result()

        # Update parameters based on results
        params[iteration + 1] = optimize(result)

Session benefits:

  • Reduced queue waiting between iterations
  • Guaranteed backend availability during session
  • Better for variational algorithms

Batch Mode

Use batch mode for independent parallel jobs:

from qiskit_ibm_runtime import Batch, SamplerV2 as Sampler

service = QiskitRuntimeService()
backend = service.backend("ibm_brisbane")

with Batch(backend=backend) as batch:
    sampler = Sampler(session=batch)

    # Submit multiple independent jobs
    jobs = []
    for qc in circuit_list:
        job = sampler.run([qc], shots=1024)
        jobs.append(job)

    # Collect all results
    results = [job.result() for job in jobs]

Local Simulators

StatevectorSampler (Ideal Simulation)

from qiskit.primitives import StatevectorSampler

sampler = StatevectorSampler()
result = sampler.run([qc], shots=1024).result()
counts = result[0].data.meas.get_counts()

Aer Simulator (Realistic Noise)

from qiskit_aer import AerSimulator
from qiskit_ibm_runtime import SamplerV2 as Sampler

# Ideal simulation
simulator = AerSimulator()

# Simulate with backend noise model
backend = service.backend("ibm_brisbane")
noisy_simulator = AerSimulator.from_backend(backend)

# Run simulation
transpiled_qc = transpile(qc, simulator)
sampler = Sampler(simulator)
job = sampler.run([transpiled_qc], shots=1024)
result = job.result()

Aer GPU Acceleration

# Use GPU for faster simulation
simulator = AerSimulator(method='statevector', device='GPU')

Third-Party Providers

IonQ

IonQ offers trapped-ion quantum computers with all-to-all connectivity:

from qiskit_ionq import IonQProvider

provider = IonQProvider("YOUR_IONQ_API_TOKEN")

# List IonQ backends
backends = provider.backends()
backend = provider.get_backend("ionq_qpu")

# Run circuit
job = backend.run(qc, shots=1024)
result = job.result()

Amazon Braket

from qiskit_braket_provider import BraketProvider

provider = BraketProvider()

# List available devices
backends = provider.backends()

# Use specific device
backend = provider.get_backend("Rigetti")
job = backend.run(qc, shots=1024)
result = job.result()

Error Mitigation

Measurement Error Mitigation

from qiskit_ibm_runtime import SamplerV2 as Sampler, Options

# Configure error mitigation
options = Options()
options.resilience_level = 1  # 0=none, 1=minimal, 2=moderate, 3=heavy

sampler = Sampler(backend, options=options)
job = sampler.run([qc], shots=1024)
result = job.result()

Error Mitigation Levels

  • Level 0: No mitigation
  • Level 1: Readout error mitigation
  • Level 2: Level 1 + gate error mitigation
  • Level 3: Level 2 + advanced techniques

Qiskit's Samplomatic package can reduce sampling overhead by up to 100x with probabilistic error cancellation.

Zero Noise Extrapolation (ZNE)

options = Options()
options.resilience_level = 2
options.resilience.zne_mitigation = True

sampler = Sampler(backend, options=options)

Monitoring Usage and Costs

Check Account Usage

# For IBM Quantum
service = QiskitRuntimeService()

# Check remaining credits
print(service.usage())

Estimate Job Cost

from qiskit_ibm_runtime import EstimatorV2 as Estimator

backend = service.backend("ibm_brisbane")

# Estimate job cost
estimator = Estimator(backend)
# Cost depends on circuit complexity and shots

Best Practices

1. Always Transpile Before Running

# Bad: Run without transpilation
job = sampler.run([qc], shots=1024)

# Good: Transpile first
qc_transpiled = transpile(qc, backend=backend, optimization_level=3)
job = sampler.run([qc_transpiled], shots=1024)

2. Test with Simulators First

# Test with noisy simulator before hardware
noisy_sim = AerSimulator.from_backend(backend)
qc_test = transpile(qc, noisy_sim, optimization_level=3)

# Verify results look reasonable
# Then run on hardware

3. Use Appropriate Shot Counts

# For optimization algorithms: fewer shots (100-1000)
# For final measurements: more shots (10000+)

# Adaptive shots based on stage
shots_optimization = 500
shots_final = 10000

4. Choose Backend Strategically

# For testing: Use least busy backend
backend = service.least_busy(min_num_qubits=5)

# For production: Use backend matching requirements
backend = service.backend("ibm_brisbane")  # 127 qubits

5. Use Sessions for Variational Algorithms

Sessions are ideal for VQE, QAOA, and other iterative algorithms.

6. Monitor Job Status

import time

job = sampler.run([qc], shots=1024)

while job.status().name not in ['DONE', 'ERROR', 'CANCELLED']:
    print(f"Status: {job.status().name}")
    time.sleep(10)

result = job.result()

Troubleshooting

Issue: "Backend not found"

# List available backends
print([b.name for b in service.backends()])

Issue: "Invalid credentials"

# Re-save credentials
QiskitRuntimeService.save_account(
    channel="ibm_quantum",
    token="YOUR_TOKEN",
    overwrite=True
)

Issue: Long queue times

# Use least busy backend
backend = service.least_busy(min_num_qubits=5)

# Or use batch mode for multiple independent jobs

Issue: Job fails with "Circuit too large"

# Reduce circuit complexity
# Use higher transpilation optimization
qc_opt = transpile(qc, backend=backend, optimization_level=3)

Backend Comparison

Provider Connectivity Gate Set Notes
IBM Quantum Limited CX, RZ, SX, X 100+ qubit systems, high quality
IonQ All-to-all GPI, GPI2, MS Trapped ion, low error rates
Rigetti Limited CZ, RZ, RX Superconducting qubits
Oxford Quantum Circuits Limited ECR, RZ, SX Coaxmon technology