1 min read
Quantum Simulators: Testing Without Quantum Hardware
I wrote “Quantum Simulators: Testing Without Quantum Hardware” to share practical, production-minded guidance on this topic.
Types of Quantum Simulators
Azure Quantum provides several simulator options:
from azure.quantum import Workspace
from azure.quantum.cirq import AzureQuantumService
# Connect to workspace
workspace = Workspace(
subscription_id="...",
resource_group="quantum-rg",
name="quantum-workspace"
)
# Available simulators
simulators = [
"microsoft.simulator.fullstate", # Full state simulator
"microsoft.simulator.sparsesimulator", # Sparse simulator
"microsoft.simulator.resources", # Resource estimator
"ionq.simulator", # IonQ simulator
"quantinuum.sim.h1-1sc", # Quantinuum simulator
]
Full State Simulator
Simulates all 2^n amplitudes:
namespace FullStateSimulation {
open Microsoft.Quantum.Canon;
open Microsoft.Quantum.Intrinsic;
open Microsoft.Quantum.Diagnostics;
@EntryPoint()
operation SimulateQuantumState() : Unit {
use qubits = Qubit[3];
// Create complex superposition
H(qubits[0]);
CNOT(qubits[0], qubits[1]);
T(qubits[2]);
CNOT(qubits[1], qubits[2]);
// Dump the full state vector
DumpMachine();
// Show state in different bases
Message("\nState in computational basis:");
DumpRegister((), qubits);
ResetAll(qubits);
}
}
Run with the full state simulator:
dotnet run --simulator FullStateSimulator
Sparse Simulator
Efficient for states with few non-zero amplitudes:
namespace SparseSimulation {
open Microsoft.Quantum.Canon;
open Microsoft.Quantum.Intrinsic;
// Good for Grover's algorithm - only one marked state
operation SparseGroverIteration(qubits : Qubit[], markedState : Int) : Unit {
// Oracle marks only one state - sparse!
let n = Length(qubits);
// Convert marked state to bit pattern
for i in 0..n-1 {
if ((markedState >>> i) &&& 1) == 0 {
X(qubits[i]);
}
}
Controlled Z(qubits[0..n-2], qubits[n-1]);
for i in 0..n-1 {
if ((markedState >>> i) &&& 1) == 0 {
X(qubits[i]);
}
}
}
@EntryPoint()
operation RunSparseSimulation() : Unit {
use qubits = Qubit[10]; // 1024 states but only one marked
ApplyToEach(H, qubits);
SparseGroverIteration(qubits, 42);
ResetAll(qubits);
Message("Sparse simulation completed efficiently!");
}
}
Resource Estimator
Estimate resources without full simulation:
namespace ResourceEstimation {
open Microsoft.Quantum.Canon;
open Microsoft.Quantum.Intrinsic;
operation EstimateShorsResources(N : Int) : Unit {
// Simplified Shor's algorithm structure
let nQubits = 2 * BitSizeI(N) + 3;
use register = Qubit[nQubits];
// QFT-like operations
for i in 0..nQubits-1 {
H(register[i]);
for j in i+1..nQubits-1 {
Controlled R1([register[j]], (PI() / PowD(2.0, IntAsDouble(j - i)), register[i]));
}
}
// Modular exponentiation (simplified)
for i in 0..nQubits/2-1 {
CNOT(register[i], register[i + nQubits/2]);
}
// Inverse QFT
for i in 0..nQubits-1 {
for j in 0..i-1 {
Controlled R1([register[j]], (-PI() / PowD(2.0, IntAsDouble(i - j)), register[i]));
}
H(register[i]);
}
ResetAll(register);
}
@EntryPoint()
operation Main() : Unit {
EstimateShorsResources(15);
}
}
Run resource estimation:
dotnet run --simulator ResourcesEstimator
Output example:
Metric Sum
CNOT 512
QubitClifford 1024
R 2048
T 4096
Measure 32
QubitCount 35
Depth 8192
Python Simulation with Qiskit
from qiskit import QuantumCircuit, transpile
from qiskit_aer import AerSimulator
from qiskit.visualization import plot_histogram
import matplotlib.pyplot as plt
# Create quantum circuit
qc = QuantumCircuit(3, 3)
# Add gates
qc.h(0)
qc.cx(0, 1)
qc.cx(1, 2)
qc.measure([0, 1, 2], [0, 1, 2])
# Different simulator backends
simulators = {
'statevector': AerSimulator(method='statevector'),
'matrix_product_state': AerSimulator(method='matrix_product_state'),
'stabilizer': AerSimulator(method='stabilizer'), # For Clifford circuits
'extended_stabilizer': AerSimulator(method='extended_stabilizer'),
}
# Run on statevector simulator
backend = simulators['statevector']
job = backend.run(transpile(qc, backend), shots=1000)
result = job.result()
counts = result.get_counts(qc)
print("Measurement results:", counts)
plot_histogram(counts)
plt.savefig('simulation_results.png')
# Noise simulation
from qiskit_aer.noise import NoiseModel, depolarizing_error
# Create noise model
noise_model = NoiseModel()
error_1q = depolarizing_error(0.01, 1) # 1% error on single-qubit gates
error_2q = depolarizing_error(0.05, 2) # 5% error on two-qubit gates
noise_model.add_all_qubit_quantum_error(error_1q, ['h', 'x', 'y', 'z'])
noise_model.add_all_qubit_quantum_error(error_2q, ['cx'])
# Run noisy simulation
noisy_backend = AerSimulator(noise_model=noise_model)
noisy_job = noisy_backend.run(transpile(qc, noisy_backend), shots=1000)
noisy_result = noisy_job.result()
noisy_counts = noisy_result.get_counts(qc)
print("Noisy measurement results:", noisy_counts)
Cirq Simulation
import cirq
import numpy as np
# Create qubits
qubits = cirq.LineQubit.range(3)
# Build circuit
circuit = cirq.Circuit([
cirq.H(qubits[0]),
cirq.CNOT(qubits[0], qubits[1]),
cirq.CNOT(qubits[1], qubits[2]),
cirq.measure(*qubits, key='result')
])
print("Circuit:")
print(circuit)
# Simulate with different simulators
# 1. State vector simulator
sv_simulator = cirq.Simulator()
sv_result = sv_simulator.simulate(circuit[:-1]) # Without measurement
print("\nState vector:")
print(sv_result.final_state_vector)
# 2. Sampling simulator
sample_result = sv_simulator.run(circuit, repetitions=1000)
print("\nMeasurement histogram:")
print(sample_result.histogram(key='result'))
# 3. Density matrix simulator (for mixed states)
dm_simulator = cirq.DensityMatrixSimulator()
dm_result = dm_simulator.simulate(circuit[:-1])
print("\nDensity matrix:")
print(dm_result.final_density_matrix)
# 4. Noisy simulation
noise_model = cirq.ConstantQubitNoiseModel(
cirq.depolarize(p=0.01)
)
noisy_simulator = cirq.DensityMatrixSimulator(noise=noise_model)
noisy_result = noisy_simulator.run(circuit, repetitions=1000)
print("\nNoisy measurement histogram:")
print(noisy_result.histogram(key='result'))
Performance Comparison
import time
import numpy as np
def benchmark_simulation(num_qubits, shots=1000):
"""Benchmark different simulation methods."""
from qiskit import QuantumCircuit
from qiskit_aer import AerSimulator
# Create random circuit
qc = QuantumCircuit(num_qubits)
for i in range(num_qubits):
qc.h(i)
for i in range(num_qubits - 1):
qc.cx(i, i + 1)
qc.measure_all()
methods = ['statevector', 'matrix_product_state']
results = {}
for method in methods:
try:
backend = AerSimulator(method=method)
start = time.time()
job = backend.run(qc, shots=shots)
job.result()
elapsed = time.time() - start
results[method] = elapsed
print(f"{method}: {elapsed:.3f}s for {num_qubits} qubits")
except Exception as e:
print(f"{method}: Failed - {e}")
return results
# Run benchmarks
for n in [10, 15, 20, 25]:
print(f"\n=== {n} qubits ===")
benchmark_simulation(n)
Summary
Quantum simulators enable:
- Algorithm development without hardware access
- Full state inspection for debugging
- Resource estimation for planning
- Noise modeling for realistic testing
- Performance benchmarking
Essential tools for quantum software development.