mirror of
https://github.com/K-Dense-AI/claude-scientific-skills.git
synced 2026-03-27 07:09:27 +08:00
Add support for Qiskit from IBM: software stack for quantum computing and algorithms research. Build, optimize, and execute quantum workloads at scale.
This commit is contained in:
607
scientific-skills/qiskit/references/algorithms.md
Normal file
607
scientific-skills/qiskit/references/algorithms.md
Normal file
@@ -0,0 +1,607 @@
|
||||
# Quantum Algorithms and Applications
|
||||
|
||||
Qiskit supports a wide range of quantum algorithms for optimization, chemistry, machine learning, and physics simulations.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
1. [Optimization Algorithms](#optimization-algorithms)
|
||||
2. [Chemistry and Materials Science](#chemistry-and-materials-science)
|
||||
3. [Machine Learning](#machine-learning)
|
||||
4. [Algorithm Libraries](#algorithm-libraries)
|
||||
|
||||
## Optimization Algorithms
|
||||
|
||||
### Variational Quantum Eigensolver (VQE)
|
||||
|
||||
VQE finds the minimum eigenvalue of a Hamiltonian using a hybrid quantum-classical approach.
|
||||
|
||||
**Use Cases:**
|
||||
- Molecular ground state energy
|
||||
- Combinatorial optimization
|
||||
- Materials simulation
|
||||
|
||||
**Implementation:**
|
||||
```python
|
||||
from qiskit import QuantumCircuit, transpile
|
||||
from qiskit_ibm_runtime import QiskitRuntimeService, EstimatorV2 as Estimator, Session
|
||||
from qiskit.quantum_info import SparsePauliOp
|
||||
from scipy.optimize import minimize
|
||||
import numpy as np
|
||||
|
||||
def vqe_algorithm(hamiltonian, ansatz, backend, initial_params):
|
||||
"""
|
||||
Run VQE algorithm
|
||||
|
||||
Args:
|
||||
hamiltonian: Observable (SparsePauliOp)
|
||||
ansatz: Parameterized quantum circuit
|
||||
backend: Quantum backend
|
||||
initial_params: Initial parameter values
|
||||
"""
|
||||
|
||||
with Session(backend=backend) as session:
|
||||
estimator = Estimator(session=session)
|
||||
|
||||
def cost_function(params):
|
||||
# Bind parameters to circuit
|
||||
bound_circuit = ansatz.assign_parameters(params)
|
||||
|
||||
# Transpile for hardware
|
||||
qc_isa = transpile(bound_circuit, backend=backend, optimization_level=3)
|
||||
|
||||
# Compute expectation value
|
||||
job = estimator.run([(qc_isa, hamiltonian)])
|
||||
result = job.result()
|
||||
energy = result[0].data.evs
|
||||
|
||||
return energy
|
||||
|
||||
# Classical optimization
|
||||
result = minimize(
|
||||
cost_function,
|
||||
initial_params,
|
||||
method='COBYLA',
|
||||
options={'maxiter': 100}
|
||||
)
|
||||
|
||||
return result.fun, result.x
|
||||
|
||||
# Example: H2 molecule Hamiltonian
|
||||
hamiltonian = SparsePauliOp(
|
||||
["IIII", "ZZII", "IIZZ", "ZZZI", "IZZI"],
|
||||
coeffs=[-0.8, 0.17, 0.17, -0.24, 0.17]
|
||||
)
|
||||
|
||||
# Create ansatz
|
||||
qc = QuantumCircuit(4)
|
||||
# ... define ansatz structure ...
|
||||
|
||||
service = QiskitRuntimeService()
|
||||
backend = service.backend("ibm_brisbane")
|
||||
|
||||
energy, params = vqe_algorithm(hamiltonian, qc, backend, np.random.rand(10))
|
||||
print(f"Ground state energy: {energy}")
|
||||
```
|
||||
|
||||
### Quantum Approximate Optimization Algorithm (QAOA)
|
||||
|
||||
QAOA solves combinatorial optimization problems like MaxCut, TSP, and graph coloring.
|
||||
|
||||
**Use Cases:**
|
||||
- MaxCut problems
|
||||
- Portfolio optimization
|
||||
- Vehicle routing
|
||||
- Scheduling problems
|
||||
|
||||
**Implementation:**
|
||||
```python
|
||||
from qiskit import QuantumCircuit
|
||||
from qiskit.circuit import Parameter
|
||||
import networkx as nx
|
||||
|
||||
def qaoa_maxcut(graph, p, backend):
|
||||
"""
|
||||
QAOA for MaxCut problem
|
||||
|
||||
Args:
|
||||
graph: NetworkX graph
|
||||
p: Number of QAOA layers
|
||||
backend: Quantum backend
|
||||
"""
|
||||
num_qubits = len(graph.nodes())
|
||||
qc = QuantumCircuit(num_qubits)
|
||||
|
||||
# Initial superposition
|
||||
qc.h(range(num_qubits))
|
||||
|
||||
# Alternating layers
|
||||
betas = [Parameter(f'β_{i}') for i in range(p)]
|
||||
gammas = [Parameter(f'γ_{i}') for i in range(p)]
|
||||
|
||||
for i in range(p):
|
||||
# Problem Hamiltonian (MaxCut)
|
||||
for edge in graph.edges():
|
||||
u, v = edge
|
||||
qc.cx(u, v)
|
||||
qc.rz(2 * gammas[i], v)
|
||||
qc.cx(u, v)
|
||||
|
||||
# Mixer Hamiltonian
|
||||
for qubit in range(num_qubits):
|
||||
qc.rx(2 * betas[i], qubit)
|
||||
|
||||
qc.measure_all()
|
||||
return qc, betas + gammas
|
||||
|
||||
# Example: MaxCut on 4-node graph
|
||||
G = nx.Graph()
|
||||
G.add_edges_from([(0, 1), (1, 2), (2, 3), (3, 0)])
|
||||
|
||||
qaoa_circuit, params = qaoa_maxcut(G, p=2, backend=backend)
|
||||
|
||||
# Run with Sampler and optimize parameters
|
||||
# ... (similar to VQE optimization loop)
|
||||
```
|
||||
|
||||
### Grover's Algorithm
|
||||
|
||||
Quantum search algorithm providing quadratic speedup for unstructured search.
|
||||
|
||||
**Use Cases:**
|
||||
- Database search
|
||||
- SAT solving
|
||||
- Finding marked items
|
||||
|
||||
**Implementation:**
|
||||
```python
|
||||
from qiskit import QuantumCircuit
|
||||
|
||||
def grover_oracle(marked_states):
|
||||
"""Create oracle that marks target states"""
|
||||
num_qubits = len(marked_states[0])
|
||||
qc = QuantumCircuit(num_qubits)
|
||||
|
||||
for target in marked_states:
|
||||
# Flip phase of target state
|
||||
for i, bit in enumerate(target):
|
||||
if bit == '0':
|
||||
qc.x(i)
|
||||
|
||||
# Multi-controlled Z
|
||||
qc.h(num_qubits - 1)
|
||||
qc.mcx(list(range(num_qubits - 1)), num_qubits - 1)
|
||||
qc.h(num_qubits - 1)
|
||||
|
||||
for i, bit in enumerate(target):
|
||||
if bit == '0':
|
||||
qc.x(i)
|
||||
|
||||
return qc
|
||||
|
||||
def grover_diffusion(num_qubits):
|
||||
"""Create Grover diffusion operator"""
|
||||
qc = QuantumCircuit(num_qubits)
|
||||
|
||||
qc.h(range(num_qubits))
|
||||
qc.x(range(num_qubits))
|
||||
|
||||
qc.h(num_qubits - 1)
|
||||
qc.mcx(list(range(num_qubits - 1)), num_qubits - 1)
|
||||
qc.h(num_qubits - 1)
|
||||
|
||||
qc.x(range(num_qubits))
|
||||
qc.h(range(num_qubits))
|
||||
|
||||
return qc
|
||||
|
||||
def grover_algorithm(marked_states, num_iterations):
|
||||
"""Complete Grover's algorithm"""
|
||||
num_qubits = len(marked_states[0])
|
||||
qc = QuantumCircuit(num_qubits)
|
||||
|
||||
# Initialize superposition
|
||||
qc.h(range(num_qubits))
|
||||
|
||||
# Grover iterations
|
||||
oracle = grover_oracle(marked_states)
|
||||
diffusion = grover_diffusion(num_qubits)
|
||||
|
||||
for _ in range(num_iterations):
|
||||
qc.compose(oracle, inplace=True)
|
||||
qc.compose(diffusion, inplace=True)
|
||||
|
||||
qc.measure_all()
|
||||
return qc
|
||||
|
||||
# Search for state |101⟩ in 3-qubit space
|
||||
marked = ['101']
|
||||
iterations = int(np.pi/4 * np.sqrt(2**3)) # Optimal iterations
|
||||
qc_grover = grover_algorithm(marked, iterations)
|
||||
```
|
||||
|
||||
## Chemistry and Materials Science
|
||||
|
||||
### Molecular Ground State Energy
|
||||
|
||||
**Install Qiskit Nature:**
|
||||
```bash
|
||||
uv pip install qiskit-nature qiskit-nature-pyscf
|
||||
```
|
||||
|
||||
**Example: H2 Molecule**
|
||||
```python
|
||||
from qiskit_nature.second_q.drivers import PySCFDriver
|
||||
from qiskit_nature.second_q.mappers import JordanWignerMapper, ParityMapper
|
||||
from qiskit_nature.second_q.circuit.library import UCCSD, HartreeFock
|
||||
|
||||
# Define molecule
|
||||
driver = PySCFDriver(
|
||||
atom="H 0 0 0; H 0 0 0.735",
|
||||
basis="sto3g",
|
||||
charge=0,
|
||||
spin=0
|
||||
)
|
||||
|
||||
# Get electronic structure problem
|
||||
problem = driver.run()
|
||||
|
||||
# Map fermionic operators to qubits
|
||||
mapper = JordanWignerMapper()
|
||||
hamiltonian = mapper.map(problem.hamiltonian.second_q_op())
|
||||
|
||||
# Create initial state
|
||||
num_particles = problem.num_particles
|
||||
num_spatial_orbitals = problem.num_spatial_orbitals
|
||||
|
||||
init_state = HartreeFock(
|
||||
num_spatial_orbitals,
|
||||
num_particles,
|
||||
mapper
|
||||
)
|
||||
|
||||
# Create ansatz
|
||||
ansatz = UCCSD(
|
||||
num_spatial_orbitals,
|
||||
num_particles,
|
||||
mapper,
|
||||
initial_state=init_state
|
||||
)
|
||||
|
||||
# Run VQE
|
||||
energy, params = vqe_algorithm(
|
||||
hamiltonian,
|
||||
ansatz,
|
||||
backend,
|
||||
np.zeros(ansatz.num_parameters)
|
||||
)
|
||||
|
||||
# Add nuclear repulsion energy
|
||||
total_energy = energy + problem.nuclear_repulsion_energy
|
||||
print(f"Ground state energy: {total_energy} Ha")
|
||||
```
|
||||
|
||||
### Different Molecular Mappers
|
||||
|
||||
```python
|
||||
# Jordan-Wigner mapping
|
||||
jw_mapper = JordanWignerMapper()
|
||||
ham_jw = jw_mapper.map(problem.hamiltonian.second_q_op())
|
||||
|
||||
# Parity mapping (often more efficient)
|
||||
parity_mapper = ParityMapper()
|
||||
ham_parity = parity_mapper.map(problem.hamiltonian.second_q_op())
|
||||
|
||||
# Bravyi-Kitaev mapping
|
||||
from qiskit_nature.second_q.mappers import BravyiKitaevMapper
|
||||
bk_mapper = BravyiKitaevMapper()
|
||||
ham_bk = bk_mapper.map(problem.hamiltonian.second_q_op())
|
||||
```
|
||||
|
||||
### Excited States
|
||||
|
||||
```python
|
||||
from qiskit_nature.second_q.algorithms import QEOM
|
||||
|
||||
# Quantum Equation of Motion for excited states
|
||||
qeom = QEOM(estimator, ansatz, 'sd') # Singles and doubles excitations
|
||||
excited_states = qeom.solve(problem)
|
||||
```
|
||||
|
||||
## Machine Learning
|
||||
|
||||
### Quantum Kernels
|
||||
|
||||
Quantum computers can compute kernel functions for machine learning.
|
||||
|
||||
**Install Qiskit Machine Learning:**
|
||||
```bash
|
||||
uv pip install qiskit-machine-learning
|
||||
```
|
||||
|
||||
**Example: Classification with Quantum Kernel**
|
||||
```python
|
||||
from qiskit_machine_learning.kernels import FidelityQuantumKernel
|
||||
from qiskit_algorithms.state_fidelities import ComputeUncompute
|
||||
from qiskit.circuit.library import ZZFeatureMap
|
||||
from sklearn.svm import SVC
|
||||
import numpy as np
|
||||
|
||||
# Create feature map
|
||||
num_features = 2
|
||||
feature_map = ZZFeatureMap(feature_dimension=num_features, reps=2)
|
||||
|
||||
# Create quantum kernel
|
||||
fidelity = ComputeUncompute(sampler=sampler)
|
||||
qkernel = FidelityQuantumKernel(fidelity=fidelity, feature_map=feature_map)
|
||||
|
||||
# Use with scikit-learn
|
||||
X_train = np.random.rand(50, 2)
|
||||
y_train = np.random.choice([0, 1], 50)
|
||||
|
||||
# Compute kernel matrix
|
||||
kernel_matrix = qkernel.evaluate(X_train)
|
||||
|
||||
# Train SVM with quantum kernel
|
||||
svc = SVC(kernel='precomputed')
|
||||
svc.fit(kernel_matrix, y_train)
|
||||
|
||||
# Predict
|
||||
X_test = np.random.rand(10, 2)
|
||||
kernel_test = qkernel.evaluate(X_test, X_train)
|
||||
predictions = svc.predict(kernel_test)
|
||||
```
|
||||
|
||||
### Variational Quantum Classifier (VQC)
|
||||
|
||||
```python
|
||||
from qiskit_machine_learning.algorithms import VQC
|
||||
from qiskit.circuit.library import RealAmplitudes
|
||||
|
||||
# Create feature map and ansatz
|
||||
feature_map = ZZFeatureMap(2)
|
||||
ansatz = RealAmplitudes(2, reps=1)
|
||||
|
||||
# Create VQC
|
||||
vqc = VQC(
|
||||
sampler=sampler,
|
||||
feature_map=feature_map,
|
||||
ansatz=ansatz,
|
||||
optimizer='COBYLA'
|
||||
)
|
||||
|
||||
# Train
|
||||
vqc.fit(X_train, y_train)
|
||||
|
||||
# Predict
|
||||
predictions = vqc.predict(X_test)
|
||||
accuracy = vqc.score(X_test, y_test)
|
||||
```
|
||||
|
||||
### Quantum Neural Networks (QNN)
|
||||
|
||||
```python
|
||||
from qiskit_machine_learning.neural_networks import SamplerQNN
|
||||
from qiskit.circuit import QuantumCircuit, Parameter
|
||||
|
||||
# Create parameterized circuit
|
||||
qc = QuantumCircuit(2)
|
||||
params = [Parameter(f'θ_{i}') for i in range(4)]
|
||||
|
||||
# Network structure
|
||||
for i, param in enumerate(params[:2]):
|
||||
qc.ry(param, i)
|
||||
|
||||
qc.cx(0, 1)
|
||||
|
||||
for i, param in enumerate(params[2:]):
|
||||
qc.ry(param, i)
|
||||
|
||||
qc.measure_all()
|
||||
|
||||
# Create QNN
|
||||
qnn = SamplerQNN(
|
||||
circuit=qc,
|
||||
sampler=sampler,
|
||||
input_params=[], # No input parameters for this example
|
||||
weight_params=params
|
||||
)
|
||||
|
||||
# Use with PyTorch or TensorFlow for training
|
||||
```
|
||||
|
||||
## Algorithm Libraries
|
||||
|
||||
### Qiskit Algorithms
|
||||
|
||||
Standard implementations of quantum algorithms:
|
||||
|
||||
```bash
|
||||
uv pip install qiskit-algorithms
|
||||
```
|
||||
|
||||
**Available Algorithms:**
|
||||
- Amplitude Estimation
|
||||
- Phase Estimation
|
||||
- Shor's Algorithm
|
||||
- Quantum Fourier Transform
|
||||
- HHL (Linear systems)
|
||||
|
||||
**Example: Quantum Phase Estimation**
|
||||
```python
|
||||
from qiskit.circuit.library import QFT
|
||||
from qiskit_algorithms import PhaseEstimation
|
||||
|
||||
# Create unitary operator
|
||||
num_qubits = 3
|
||||
unitary = QuantumCircuit(num_qubits)
|
||||
# ... define unitary ...
|
||||
|
||||
# Phase estimation
|
||||
pe = PhaseEstimation(num_evaluation_qubits=3, quantum_instance=backend)
|
||||
result = pe.estimate(unitary=unitary, state_preparation=initial_state)
|
||||
```
|
||||
|
||||
### Qiskit Optimization
|
||||
|
||||
Optimization problem solvers:
|
||||
|
||||
```bash
|
||||
uv pip install qiskit-optimization
|
||||
```
|
||||
|
||||
**Supported Problems:**
|
||||
- Quadratic programs
|
||||
- Integer programming
|
||||
- Linear programming
|
||||
- Constraint satisfaction
|
||||
|
||||
**Example: Portfolio Optimization**
|
||||
```python
|
||||
from qiskit_optimization.applications import PortfolioOptimization
|
||||
from qiskit_optimization.algorithms import MinimumEigenOptimizer
|
||||
from qiskit_algorithms import QAOA
|
||||
|
||||
# Define portfolio problem
|
||||
returns = [0.1, 0.15, 0.12] # Expected returns
|
||||
covariances = [[1, 0.5, 0.3], [0.5, 1, 0.4], [0.3, 0.4, 1]]
|
||||
budget = 2 # Number of assets to select
|
||||
|
||||
portfolio = PortfolioOptimization(
|
||||
expected_returns=returns,
|
||||
covariances=covariances,
|
||||
budget=budget
|
||||
)
|
||||
|
||||
# Convert to quadratic program
|
||||
qp = portfolio.to_quadratic_program()
|
||||
|
||||
# Solve with QAOA
|
||||
qaoa = QAOA(sampler=sampler, optimizer='COBYLA', reps=2)
|
||||
optimizer = MinimumEigenOptimizer(qaoa)
|
||||
|
||||
result = optimizer.solve(qp)
|
||||
print(f"Optimal portfolio: {result.x}")
|
||||
```
|
||||
|
||||
## Physics Simulations
|
||||
|
||||
### Time Evolution
|
||||
|
||||
```python
|
||||
from qiskit.synthesis import SuzukiTrotter
|
||||
from qiskit.quantum_info import SparsePauliOp
|
||||
|
||||
# Define Hamiltonian
|
||||
hamiltonian = SparsePauliOp(["XX", "YY", "ZZ"], coeffs=[1.0, 1.0, 1.0])
|
||||
|
||||
# Time evolution
|
||||
time = 1.0
|
||||
evolution_gate = SuzukiTrotter(order=2, reps=10).synthesize(
|
||||
hamiltonian,
|
||||
time
|
||||
)
|
||||
|
||||
qc = QuantumCircuit(2)
|
||||
qc.append(evolution_gate, range(2))
|
||||
```
|
||||
|
||||
### Partial Differential Equations
|
||||
|
||||
**Use Case:** Quantum algorithms for solving PDEs with potential exponential speedup.
|
||||
|
||||
```python
|
||||
# Quantum PDE solver implementation
|
||||
# Requires advanced techniques like HHL algorithm
|
||||
# and amplitude encoding of solution vectors
|
||||
```
|
||||
|
||||
## Benchmarking
|
||||
|
||||
### Benchpress Toolkit
|
||||
|
||||
Benchmark quantum algorithms:
|
||||
|
||||
```python
|
||||
# Benchpress provides standardized benchmarks
|
||||
# for comparing quantum algorithm performance
|
||||
|
||||
# Examples:
|
||||
# - Quantum volume circuits
|
||||
# - Random circuit sampling
|
||||
# - Application-specific benchmarks
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
### 1. Start Simple
|
||||
Begin with small problem instances to validate your approach:
|
||||
```python
|
||||
# Test with 2-3 qubits first
|
||||
# Scale up after confirming correctness
|
||||
```
|
||||
|
||||
### 2. Use Simulators for Development
|
||||
```python
|
||||
from qiskit.primitives import StatevectorSampler
|
||||
|
||||
# Develop with local simulator
|
||||
sampler_sim = StatevectorSampler()
|
||||
|
||||
# Switch to hardware for production
|
||||
# sampler_hw = Sampler(backend)
|
||||
```
|
||||
|
||||
### 3. Monitor Convergence
|
||||
```python
|
||||
convergence_data = []
|
||||
|
||||
def tracked_cost_function(params):
|
||||
energy = cost_function(params)
|
||||
convergence_data.append(energy)
|
||||
return energy
|
||||
|
||||
# Plot convergence after optimization
|
||||
import matplotlib.pyplot as plt
|
||||
plt.plot(convergence_data)
|
||||
plt.xlabel('Iteration')
|
||||
plt.ylabel('Energy')
|
||||
plt.show()
|
||||
```
|
||||
|
||||
### 4. Parameter Initialization
|
||||
```python
|
||||
# Use problem-specific initialization when possible
|
||||
# Random initialization
|
||||
initial_params = np.random.uniform(0, 2*np.pi, num_params)
|
||||
|
||||
# Or use classical preprocessing
|
||||
# initial_params = classical_solution_to_params(classical_result)
|
||||
```
|
||||
|
||||
### 5. Save Intermediate Results
|
||||
```python
|
||||
import json
|
||||
|
||||
checkpoint = {
|
||||
'iteration': iteration,
|
||||
'params': params.tolist(),
|
||||
'energy': energy,
|
||||
'timestamp': time.time()
|
||||
}
|
||||
|
||||
with open(f'checkpoint_{iteration}.json', 'w') as f:
|
||||
json.dump(checkpoint, f)
|
||||
```
|
||||
|
||||
## Resources and Further Reading
|
||||
|
||||
**Official Documentation:**
|
||||
- [Qiskit Textbook](https://qiskit.org/learn)
|
||||
- [Qiskit Nature Documentation](https://qiskit.org/ecosystem/nature)
|
||||
- [Qiskit Machine Learning Documentation](https://qiskit.org/ecosystem/machine-learning)
|
||||
- [Qiskit Optimization Documentation](https://qiskit.org/ecosystem/optimization)
|
||||
|
||||
**Research Papers:**
|
||||
- VQE: Peruzzo et al., Nature Communications (2014)
|
||||
- QAOA: Farhi et al., arXiv:1411.4028
|
||||
- Quantum Kernels: Havlíček et al., Nature (2019)
|
||||
433
scientific-skills/qiskit/references/backends.md
Normal file
433
scientific-skills/qiskit/references/backends.md
Normal file
@@ -0,0 +1,433 @@
|
||||
# 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
|
||||
|
||||
```python
|
||||
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
|
||||
|
||||
```python
|
||||
# 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
|
||||
|
||||
```python
|
||||
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
|
||||
|
||||
```python
|
||||
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
|
||||
|
||||
```python
|
||||
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
|
||||
|
||||
```python
|
||||
# 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
|
||||
|
||||
```python
|
||||
# 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:
|
||||
|
||||
```python
|
||||
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:
|
||||
|
||||
```python
|
||||
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)
|
||||
|
||||
```python
|
||||
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)
|
||||
|
||||
```python
|
||||
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
|
||||
|
||||
```python
|
||||
# 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:
|
||||
|
||||
```python
|
||||
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
|
||||
|
||||
```python
|
||||
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
|
||||
|
||||
```python
|
||||
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)
|
||||
|
||||
```python
|
||||
options = Options()
|
||||
options.resilience_level = 2
|
||||
options.resilience.zne_mitigation = True
|
||||
|
||||
sampler = Sampler(backend, options=options)
|
||||
```
|
||||
|
||||
## Monitoring Usage and Costs
|
||||
|
||||
### Check Account Usage
|
||||
|
||||
```python
|
||||
# For IBM Quantum
|
||||
service = QiskitRuntimeService()
|
||||
|
||||
# Check remaining credits
|
||||
print(service.usage())
|
||||
```
|
||||
|
||||
### Estimate Job Cost
|
||||
|
||||
```python
|
||||
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
|
||||
|
||||
```python
|
||||
# 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
|
||||
|
||||
```python
|
||||
# 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
|
||||
|
||||
```python
|
||||
# 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
|
||||
|
||||
```python
|
||||
# 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
|
||||
|
||||
```python
|
||||
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"
|
||||
```python
|
||||
# List available backends
|
||||
print([b.name for b in service.backends()])
|
||||
```
|
||||
|
||||
### Issue: "Invalid credentials"
|
||||
```python
|
||||
# Re-save credentials
|
||||
QiskitRuntimeService.save_account(
|
||||
channel="ibm_quantum",
|
||||
token="YOUR_TOKEN",
|
||||
overwrite=True
|
||||
)
|
||||
```
|
||||
|
||||
### Issue: Long queue times
|
||||
```python
|
||||
# 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"
|
||||
```python
|
||||
# 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 |
|
||||
197
scientific-skills/qiskit/references/circuits.md
Normal file
197
scientific-skills/qiskit/references/circuits.md
Normal file
@@ -0,0 +1,197 @@
|
||||
# Quantum Circuit Building
|
||||
|
||||
## Creating a Quantum Circuit
|
||||
|
||||
Create circuits using the `QuantumCircuit` class:
|
||||
|
||||
```python
|
||||
from qiskit import QuantumCircuit
|
||||
|
||||
# Create a circuit with 3 qubits
|
||||
qc = QuantumCircuit(3)
|
||||
|
||||
# Create a circuit with 3 qubits and 3 classical bits
|
||||
qc = QuantumCircuit(3, 3)
|
||||
```
|
||||
|
||||
## Single-Qubit Gates
|
||||
|
||||
### Pauli Gates
|
||||
|
||||
```python
|
||||
qc.x(0) # NOT/Pauli-X gate on qubit 0
|
||||
qc.y(1) # Pauli-Y gate on qubit 1
|
||||
qc.z(2) # Pauli-Z gate on qubit 2
|
||||
```
|
||||
|
||||
### Hadamard Gate
|
||||
|
||||
Creates superposition:
|
||||
|
||||
```python
|
||||
qc.h(0) # Hadamard gate on qubit 0
|
||||
```
|
||||
|
||||
### Phase Gates
|
||||
|
||||
```python
|
||||
qc.s(0) # S gate (√Z)
|
||||
qc.t(0) # T gate (√S)
|
||||
qc.p(π/4, 0) # Phase gate with custom angle
|
||||
```
|
||||
|
||||
### Rotation Gates
|
||||
|
||||
```python
|
||||
from math import pi
|
||||
|
||||
qc.rx(pi/2, 0) # Rotation around X-axis
|
||||
qc.ry(pi/4, 1) # Rotation around Y-axis
|
||||
qc.rz(pi/3, 2) # Rotation around Z-axis
|
||||
```
|
||||
|
||||
## Multi-Qubit Gates
|
||||
|
||||
### CNOT (Controlled-NOT)
|
||||
|
||||
```python
|
||||
qc.cx(0, 1) # CNOT with control=0, target=1
|
||||
```
|
||||
|
||||
### Controlled Gates
|
||||
|
||||
```python
|
||||
qc.cy(0, 1) # Controlled-Y
|
||||
qc.cz(0, 1) # Controlled-Z
|
||||
qc.ch(0, 1) # Controlled-Hadamard
|
||||
```
|
||||
|
||||
### SWAP Gate
|
||||
|
||||
```python
|
||||
qc.swap(0, 1) # Swap qubits 0 and 1
|
||||
```
|
||||
|
||||
### Toffoli (CCX) Gate
|
||||
|
||||
```python
|
||||
qc.ccx(0, 1, 2) # Toffoli with controls=0,1 and target=2
|
||||
```
|
||||
|
||||
## Measurements
|
||||
|
||||
Add measurements to read qubit states:
|
||||
|
||||
```python
|
||||
# Measure all qubits
|
||||
qc.measure_all()
|
||||
|
||||
# Measure specific qubits to specific classical bits
|
||||
qc.measure(0, 0) # Measure qubit 0 to classical bit 0
|
||||
qc.measure([0, 1], [0, 1]) # Measure qubits 0,1 to bits 0,1
|
||||
```
|
||||
|
||||
## Circuit Composition
|
||||
|
||||
### Combining Circuits
|
||||
|
||||
```python
|
||||
qc1 = QuantumCircuit(2)
|
||||
qc1.h(0)
|
||||
|
||||
qc2 = QuantumCircuit(2)
|
||||
qc2.cx(0, 1)
|
||||
|
||||
# Compose circuits
|
||||
qc_combined = qc1.compose(qc2)
|
||||
```
|
||||
|
||||
### Tensor Product
|
||||
|
||||
```python
|
||||
qc1 = QuantumCircuit(1)
|
||||
qc1.h(0)
|
||||
|
||||
qc2 = QuantumCircuit(1)
|
||||
qc2.x(0)
|
||||
|
||||
# Create larger circuit from smaller ones
|
||||
qc_tensor = qc1.tensor(qc2) # Results in 2-qubit circuit
|
||||
```
|
||||
|
||||
## Barriers and Labels
|
||||
|
||||
```python
|
||||
qc.barrier() # Add visual barrier in circuit
|
||||
qc.barrier([0, 1]) # Barrier on specific qubits
|
||||
|
||||
# Add labels for clarity
|
||||
qc.barrier(label="Initialization")
|
||||
```
|
||||
|
||||
## Circuit Properties
|
||||
|
||||
```python
|
||||
print(qc.num_qubits) # Number of qubits
|
||||
print(qc.num_clbits) # Number of classical bits
|
||||
print(qc.depth()) # Circuit depth
|
||||
print(qc.size()) # Total gate count
|
||||
print(qc.count_ops()) # Dictionary of gate counts
|
||||
```
|
||||
|
||||
## Example: Bell State
|
||||
|
||||
Create entanglement between two qubits:
|
||||
|
||||
```python
|
||||
qc = QuantumCircuit(2)
|
||||
qc.h(0) # Superposition on qubit 0
|
||||
qc.cx(0, 1) # Entangle qubit 0 and 1
|
||||
qc.measure_all() # Measure both qubits
|
||||
```
|
||||
|
||||
## Example: Quantum Fourier Transform (QFT)
|
||||
|
||||
```python
|
||||
from math import pi
|
||||
|
||||
def qft(n):
|
||||
qc = QuantumCircuit(n)
|
||||
for j in range(n):
|
||||
qc.h(j)
|
||||
for k in range(j+1, n):
|
||||
qc.cp(pi/2**(k-j), k, j)
|
||||
return qc
|
||||
|
||||
# Create 3-qubit QFT
|
||||
qc_qft = qft(3)
|
||||
```
|
||||
|
||||
## Parameterized Circuits
|
||||
|
||||
Create circuits with parameters for variational algorithms:
|
||||
|
||||
```python
|
||||
from qiskit.circuit import Parameter
|
||||
|
||||
theta = Parameter('θ')
|
||||
qc = QuantumCircuit(1)
|
||||
qc.ry(theta, 0)
|
||||
|
||||
# Bind parameter value
|
||||
qc_bound = qc.assign_parameters({theta: pi/4})
|
||||
```
|
||||
|
||||
## Circuit Operations
|
||||
|
||||
```python
|
||||
# Inverse of a circuit
|
||||
qc_inverse = qc.inverse()
|
||||
|
||||
# Decompose gates to basis gates
|
||||
qc_decomposed = qc.decompose()
|
||||
|
||||
# Draw circuit (returns string or diagram)
|
||||
print(qc.draw())
|
||||
print(qc.draw('mpl')) # Matplotlib figure
|
||||
```
|
||||
533
scientific-skills/qiskit/references/patterns.md
Normal file
533
scientific-skills/qiskit/references/patterns.md
Normal file
@@ -0,0 +1,533 @@
|
||||
# Qiskit Patterns: The Four-Step Workflow
|
||||
|
||||
Qiskit Patterns provide a general framework for solving domain-specific quantum computing problems in four stages: Map, Optimize, Execute, and Post-process.
|
||||
|
||||
## Overview
|
||||
|
||||
The patterns framework enables seamless composition of quantum capabilities and supports heterogeneous computing infrastructure (CPU/GPU/QPU). Execute locally, through cloud services, or via Qiskit Serverless.
|
||||
|
||||
## The Four Steps
|
||||
|
||||
```
|
||||
Problem → [Map] → [Optimize] → [Execute] → [Post-process] → Solution
|
||||
```
|
||||
|
||||
### 1. Map
|
||||
Translate classical problems into quantum circuits and operators
|
||||
|
||||
### 2. Optimize
|
||||
Prepare circuits for target hardware through transpilation
|
||||
|
||||
### 3. Execute
|
||||
Run circuits on quantum hardware using primitives
|
||||
|
||||
### 4. Post-process
|
||||
Extract and refine results with classical computation
|
||||
|
||||
## Step 1: Map
|
||||
|
||||
### Goal
|
||||
Transform domain-specific problems into quantum representations (circuits, operators, Hamiltonians).
|
||||
|
||||
### Key Decisions
|
||||
|
||||
**Choose Output Type:**
|
||||
- **Sampler**: For bitstring outputs (optimization, search)
|
||||
- **Estimator**: For expectation values (chemistry, physics)
|
||||
|
||||
**Design Circuit Structure:**
|
||||
```python
|
||||
from qiskit import QuantumCircuit
|
||||
from qiskit.circuit import Parameter
|
||||
import numpy as np
|
||||
|
||||
# Example: Parameterized circuit for VQE
|
||||
def create_ansatz(num_qubits, depth):
|
||||
qc = QuantumCircuit(num_qubits)
|
||||
params = []
|
||||
|
||||
for d in range(depth):
|
||||
# Rotation layer
|
||||
for i in range(num_qubits):
|
||||
theta = Parameter(f'θ_{d}_{i}')
|
||||
params.append(theta)
|
||||
qc.ry(theta, i)
|
||||
|
||||
# Entanglement layer
|
||||
for i in range(num_qubits - 1):
|
||||
qc.cx(i, i + 1)
|
||||
|
||||
return qc, params
|
||||
|
||||
ansatz, params = create_ansatz(num_qubits=4, depth=2)
|
||||
```
|
||||
|
||||
### Considerations
|
||||
|
||||
- **Hardware topology**: Design with backend coupling map in mind
|
||||
- **Gate efficiency**: Minimize two-qubit gates
|
||||
- **Measurement basis**: Determine required measurements
|
||||
|
||||
### Domain-Specific Examples
|
||||
|
||||
**Chemistry: Molecular Hamiltonian**
|
||||
```python
|
||||
from qiskit_nature.second_q.drivers import PySCFDriver
|
||||
from qiskit_nature.second_q.mappers import JordanWignerMapper
|
||||
|
||||
# Define molecule
|
||||
driver = PySCFDriver(atom='H 0 0 0; H 0 0 0.735', basis='sto3g')
|
||||
problem = driver.run()
|
||||
|
||||
# Map to qubit Hamiltonian
|
||||
mapper = JordanWignerMapper()
|
||||
hamiltonian = mapper.map(problem.hamiltonian)
|
||||
```
|
||||
|
||||
**Optimization: QAOA Circuit**
|
||||
```python
|
||||
from qiskit.circuit import QuantumCircuit, Parameter
|
||||
|
||||
def qaoa_circuit(graph, p):
|
||||
"""Create QAOA circuit for MaxCut problem"""
|
||||
num_qubits = len(graph.nodes())
|
||||
qc = QuantumCircuit(num_qubits)
|
||||
|
||||
# Initial superposition
|
||||
qc.h(range(num_qubits))
|
||||
|
||||
# Alternating layers
|
||||
betas = [Parameter(f'β_{i}') for i in range(p)]
|
||||
gammas = [Parameter(f'γ_{i}') for i in range(p)]
|
||||
|
||||
for i in range(p):
|
||||
# Problem Hamiltonian
|
||||
for edge in graph.edges():
|
||||
qc.cx(edge[0], edge[1])
|
||||
qc.rz(2 * gammas[i], edge[1])
|
||||
qc.cx(edge[0], edge[1])
|
||||
|
||||
# Mixer Hamiltonian
|
||||
qc.rx(2 * betas[i], range(num_qubits))
|
||||
|
||||
return qc
|
||||
```
|
||||
|
||||
## Step 2: Optimize
|
||||
|
||||
### Goal
|
||||
Transform abstract circuits to hardware-compatible ISA (Instruction Set Architecture) circuits.
|
||||
|
||||
### Transpilation
|
||||
|
||||
```python
|
||||
from qiskit import transpile
|
||||
|
||||
# Basic transpilation
|
||||
qc_isa = transpile(qc, backend=backend, optimization_level=3)
|
||||
|
||||
# With specific initial layout
|
||||
qc_isa = transpile(
|
||||
qc,
|
||||
backend=backend,
|
||||
optimization_level=3,
|
||||
initial_layout=[0, 2, 4, 6], # Map to specific physical qubits
|
||||
seed_transpiler=42 # Reproducibility
|
||||
)
|
||||
```
|
||||
|
||||
### Pre-optimization Tips
|
||||
|
||||
1. **Test with simulators first**:
|
||||
```python
|
||||
from qiskit_aer import AerSimulator
|
||||
|
||||
sim = AerSimulator.from_backend(backend)
|
||||
qc_test = transpile(qc, sim, optimization_level=3)
|
||||
print(f"Estimated depth: {qc_test.depth()}")
|
||||
```
|
||||
|
||||
2. **Analyze transpilation results**:
|
||||
```python
|
||||
print(f"Original gates: {qc.size()}")
|
||||
print(f"Transpiled gates: {qc_isa.size()}")
|
||||
print(f"Two-qubit gates: {qc_isa.count_ops().get('cx', 0)}")
|
||||
```
|
||||
|
||||
3. **Consider circuit cutting** for large circuits:
|
||||
```python
|
||||
# For circuits too large for available hardware
|
||||
# Use circuit cutting techniques to split into smaller subcircuits
|
||||
```
|
||||
|
||||
## Step 3: Execute
|
||||
|
||||
### Goal
|
||||
Run ISA circuits on quantum hardware using primitives.
|
||||
|
||||
### Using Sampler
|
||||
|
||||
```python
|
||||
from qiskit_ibm_runtime import QiskitRuntimeService, SamplerV2 as Sampler
|
||||
|
||||
service = QiskitRuntimeService()
|
||||
backend = service.backend("ibm_brisbane")
|
||||
|
||||
# Transpile first
|
||||
qc_isa = transpile(qc, backend=backend, optimization_level=3)
|
||||
|
||||
# Execute
|
||||
sampler = Sampler(backend)
|
||||
job = sampler.run([qc_isa], shots=10000)
|
||||
result = job.result()
|
||||
counts = result[0].data.meas.get_counts()
|
||||
```
|
||||
|
||||
### Using Estimator
|
||||
|
||||
```python
|
||||
from qiskit_ibm_runtime import EstimatorV2 as Estimator
|
||||
from qiskit.quantum_info import SparsePauliOp
|
||||
|
||||
service = QiskitRuntimeService()
|
||||
backend = service.backend("ibm_brisbane")
|
||||
|
||||
# Transpile
|
||||
qc_isa = transpile(qc, backend=backend, optimization_level=3)
|
||||
|
||||
# Define observable
|
||||
observable = SparsePauliOp(["ZZZZ", "XXXX"])
|
||||
|
||||
# Execute
|
||||
estimator = Estimator(backend)
|
||||
job = estimator.run([(qc_isa, observable)])
|
||||
result = job.result()
|
||||
expectation_value = result[0].data.evs
|
||||
```
|
||||
|
||||
### Execution Modes
|
||||
|
||||
**Session Mode (Iterative):**
|
||||
```python
|
||||
from qiskit_ibm_runtime import Session
|
||||
|
||||
with Session(backend=backend) as session:
|
||||
sampler = Sampler(session=session)
|
||||
|
||||
# Multiple iterations
|
||||
for iteration in range(max_iterations):
|
||||
qc_iteration = update_circuit(params[iteration])
|
||||
qc_isa = transpile(qc_iteration, backend=backend)
|
||||
|
||||
job = sampler.run([qc_isa], shots=1000)
|
||||
result = job.result()
|
||||
|
||||
# Update parameters
|
||||
params[iteration + 1] = optimize_params(result)
|
||||
```
|
||||
|
||||
**Batch Mode (Parallel):**
|
||||
```python
|
||||
from qiskit_ibm_runtime import Batch
|
||||
|
||||
with Batch(backend=backend) as batch:
|
||||
sampler = Sampler(session=batch)
|
||||
|
||||
# Submit all jobs at once
|
||||
jobs = []
|
||||
for qc in circuit_list:
|
||||
qc_isa = transpile(qc, backend=backend)
|
||||
job = sampler.run([qc_isa], shots=1000)
|
||||
jobs.append(job)
|
||||
|
||||
# Collect results
|
||||
results = [job.result() for job in jobs]
|
||||
```
|
||||
|
||||
### Error Mitigation
|
||||
|
||||
```python
|
||||
from qiskit_ibm_runtime import Options
|
||||
|
||||
options = Options()
|
||||
options.resilience_level = 2 # 0=none, 1=light, 2=moderate, 3=heavy
|
||||
options.optimization_level = 3
|
||||
|
||||
sampler = Sampler(backend, options=options)
|
||||
```
|
||||
|
||||
## Step 4: Post-process
|
||||
|
||||
### Goal
|
||||
Extract meaningful results from quantum measurements using classical computation.
|
||||
|
||||
### Result Processing
|
||||
|
||||
**For Sampler (Bitstrings):**
|
||||
```python
|
||||
counts = result[0].data.meas.get_counts()
|
||||
|
||||
# Convert to probabilities
|
||||
total_shots = sum(counts.values())
|
||||
probabilities = {state: count/total_shots for state, count in counts.items()}
|
||||
|
||||
# Find most probable state
|
||||
max_state = max(counts, key=counts.get)
|
||||
print(f"Most probable state: {max_state} ({counts[max_state]}/{total_shots})")
|
||||
```
|
||||
|
||||
**For Estimator (Expectation Values):**
|
||||
```python
|
||||
expectation_value = result[0].data.evs
|
||||
std_dev = result[0].data.stds # Standard deviation
|
||||
|
||||
print(f"Energy: {expectation_value} ± {std_dev}")
|
||||
```
|
||||
|
||||
### Domain-Specific Post-Processing
|
||||
|
||||
**Chemistry: Ground State Energy**
|
||||
```python
|
||||
def post_process_chemistry(result, nuclear_repulsion):
|
||||
"""Extract ground state energy"""
|
||||
electronic_energy = result[0].data.evs
|
||||
total_energy = electronic_energy + nuclear_repulsion
|
||||
return total_energy
|
||||
```
|
||||
|
||||
**Optimization: MaxCut Solution**
|
||||
```python
|
||||
def post_process_maxcut(counts, graph):
|
||||
"""Find best cut from measurement results"""
|
||||
def compute_cut_value(bitstring, graph):
|
||||
cut_value = 0
|
||||
for edge in graph.edges():
|
||||
if bitstring[edge[0]] != bitstring[edge[1]]:
|
||||
cut_value += 1
|
||||
return cut_value
|
||||
|
||||
# Find bitstring with maximum cut
|
||||
best_cut = 0
|
||||
best_string = None
|
||||
|
||||
for bitstring, count in counts.items():
|
||||
cut = compute_cut_value(bitstring, graph)
|
||||
if cut > best_cut:
|
||||
best_cut = cut
|
||||
best_string = bitstring
|
||||
|
||||
return best_string, best_cut
|
||||
```
|
||||
|
||||
### Advanced Post-Processing
|
||||
|
||||
**Error Mitigation Post-Processing:**
|
||||
```python
|
||||
# Apply additional classical error mitigation
|
||||
from qiskit.result import marginal_counts
|
||||
|
||||
# Marginalize to relevant qubits
|
||||
relevant_qubits = [0, 1, 2]
|
||||
marginal = marginal_counts(counts, indices=relevant_qubits)
|
||||
```
|
||||
|
||||
**Statistical Analysis:**
|
||||
```python
|
||||
import numpy as np
|
||||
|
||||
def analyze_results(results_list):
|
||||
"""Analyze multiple runs for statistics"""
|
||||
energies = [r[0].data.evs for r in results_list]
|
||||
|
||||
mean_energy = np.mean(energies)
|
||||
std_energy = np.std(energies)
|
||||
confidence_interval = 1.96 * std_energy / np.sqrt(len(energies))
|
||||
|
||||
return {
|
||||
'mean': mean_energy,
|
||||
'std': std_energy,
|
||||
'95% CI': (mean_energy - confidence_interval, mean_energy + confidence_interval)
|
||||
}
|
||||
```
|
||||
|
||||
**Visualization:**
|
||||
```python
|
||||
from qiskit.visualization import plot_histogram
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
# Visualize results
|
||||
plot_histogram(counts, figsize=(12, 6))
|
||||
plt.title("Measurement Results")
|
||||
plt.show()
|
||||
```
|
||||
|
||||
## Complete Example: VQE for Chemistry
|
||||
|
||||
```python
|
||||
from qiskit import QuantumCircuit, transpile
|
||||
from qiskit_ibm_runtime import QiskitRuntimeService, EstimatorV2 as Estimator, Session
|
||||
from qiskit.quantum_info import SparsePauliOp
|
||||
from scipy.optimize import minimize
|
||||
import numpy as np
|
||||
|
||||
# 1. MAP: Create parameterized circuit
|
||||
def create_ansatz(num_qubits):
|
||||
qc = QuantumCircuit(num_qubits)
|
||||
params = []
|
||||
|
||||
for i in range(num_qubits):
|
||||
theta = f'θ_{i}'
|
||||
params.append(theta)
|
||||
qc.ry(theta, i)
|
||||
|
||||
for i in range(num_qubits - 1):
|
||||
qc.cx(i, i + 1)
|
||||
|
||||
return qc, params
|
||||
|
||||
# Define Hamiltonian (example: H2 molecule)
|
||||
hamiltonian = SparsePauliOp(["IIZZ", "ZZII", "XXII", "IIXX"], coeffs=[0.3, 0.3, 0.1, 0.1])
|
||||
|
||||
# 2. OPTIMIZE: Connect and prepare
|
||||
service = QiskitRuntimeService()
|
||||
backend = service.backend("ibm_brisbane")
|
||||
|
||||
ansatz, param_names = create_ansatz(num_qubits=4)
|
||||
|
||||
# 3. EXECUTE: Run VQE
|
||||
def cost_function(params):
|
||||
# Bind parameters
|
||||
bound_circuit = ansatz.assign_parameters({param_names[i]: params[i] for i in range(len(params))})
|
||||
|
||||
# Transpile
|
||||
qc_isa = transpile(bound_circuit, backend=backend, optimization_level=3)
|
||||
|
||||
# Execute
|
||||
job = estimator.run([(qc_isa, hamiltonian)])
|
||||
result = job.result()
|
||||
energy = result[0].data.evs
|
||||
|
||||
return energy
|
||||
|
||||
with Session(backend=backend) as session:
|
||||
estimator = Estimator(session=session)
|
||||
|
||||
# Classical optimization loop
|
||||
initial_params = np.random.random(len(param_names)) * 2 * np.pi
|
||||
result = minimize(cost_function, initial_params, method='COBYLA')
|
||||
|
||||
# 4. POST-PROCESS: Extract ground state energy
|
||||
ground_state_energy = result.fun
|
||||
optimized_params = result.x
|
||||
|
||||
print(f"Ground state energy: {ground_state_energy}")
|
||||
print(f"Optimized parameters: {optimized_params}")
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
### 1. Iterate Locally First
|
||||
Test the full workflow with simulators before using hardware:
|
||||
```python
|
||||
from qiskit.primitives import StatevectorEstimator
|
||||
|
||||
estimator = StatevectorEstimator()
|
||||
# Test workflow locally
|
||||
```
|
||||
|
||||
### 2. Use Sessions for Iterative Algorithms
|
||||
VQE, QAOA, and other variational algorithms benefit from sessions.
|
||||
|
||||
### 3. Choose Appropriate Shots
|
||||
- Development/testing: 100-1000 shots
|
||||
- Production: 10,000+ shots
|
||||
|
||||
### 4. Monitor Convergence
|
||||
```python
|
||||
energies = []
|
||||
|
||||
def cost_function_with_tracking(params):
|
||||
energy = cost_function(params)
|
||||
energies.append(energy)
|
||||
print(f"Iteration {len(energies)}: E = {energy}")
|
||||
return energy
|
||||
```
|
||||
|
||||
### 5. Save Results
|
||||
```python
|
||||
import json
|
||||
|
||||
results_data = {
|
||||
'energy': float(ground_state_energy),
|
||||
'parameters': optimized_params.tolist(),
|
||||
'iterations': len(energies),
|
||||
'backend': backend.name
|
||||
}
|
||||
|
||||
with open('vqe_results.json', 'w') as f:
|
||||
json.dump(results_data, f, indent=2)
|
||||
```
|
||||
|
||||
## Qiskit Serverless
|
||||
|
||||
For large-scale workflows, use Qiskit Serverless for distributed computation:
|
||||
|
||||
```python
|
||||
from qiskit_serverless import ServerlessClient, QiskitFunction
|
||||
|
||||
client = ServerlessClient()
|
||||
|
||||
# Define serverless function
|
||||
@QiskitFunction()
|
||||
def run_vqe_serverless(hamiltonian, ansatz):
|
||||
# Your VQE implementation
|
||||
pass
|
||||
|
||||
# Execute remotely
|
||||
job = run_vqe_serverless(hamiltonian, ansatz)
|
||||
result = job.result()
|
||||
```
|
||||
|
||||
## Common Workflow Patterns
|
||||
|
||||
### Pattern 1: Parameter Sweep
|
||||
```python
|
||||
# Map → Optimize once → Execute many → Post-process
|
||||
qc_isa = transpile(parameterized_circuit, backend=backend)
|
||||
|
||||
with Batch(backend=backend) as batch:
|
||||
sampler = Sampler(session=batch)
|
||||
results = []
|
||||
|
||||
for param_set in parameter_sweep:
|
||||
bound_qc = qc_isa.assign_parameters(param_set)
|
||||
job = sampler.run([bound_qc], shots=1000)
|
||||
results.append(job.result())
|
||||
```
|
||||
|
||||
### Pattern 2: Iterative Refinement
|
||||
```python
|
||||
# Map → (Optimize → Execute → Post-process) repeated
|
||||
with Session(backend=backend) as session:
|
||||
estimator = Estimator(session=session)
|
||||
|
||||
for iteration in range(max_iter):
|
||||
qc = update_circuit(params)
|
||||
qc_isa = transpile(qc, backend=backend)
|
||||
|
||||
result = estimator.run([(qc_isa, observable)]).result()
|
||||
params = update_params(result)
|
||||
```
|
||||
|
||||
### Pattern 3: Ensemble Measurement
|
||||
```python
|
||||
# Map → Optimize → Execute many observables → Post-process
|
||||
qc_isa = transpile(qc, backend=backend)
|
||||
|
||||
observables = [obs1, obs2, obs3, obs4]
|
||||
jobs = [(qc_isa, obs) for obs in observables]
|
||||
|
||||
estimator = Estimator(backend)
|
||||
result = estimator.run(jobs).result()
|
||||
expectation_values = [r.data.evs for r in result]
|
||||
```
|
||||
277
scientific-skills/qiskit/references/primitives.md
Normal file
277
scientific-skills/qiskit/references/primitives.md
Normal file
@@ -0,0 +1,277 @@
|
||||
# Qiskit Primitives
|
||||
|
||||
Primitives are the fundamental building blocks for executing quantum circuits. Qiskit provides two main primitives: **Sampler** (for measuring bitstrings) and **Estimator** (for computing expectation values).
|
||||
|
||||
## Primitive Types
|
||||
|
||||
### Sampler
|
||||
Calculates probabilities or quasi-probabilities of bitstrings from quantum circuits. Use when you need:
|
||||
- Measurement outcomes
|
||||
- Output probability distributions
|
||||
- Sampling from quantum states
|
||||
|
||||
### Estimator
|
||||
Computes expectation values of observables for quantum circuits. Use when you need:
|
||||
- Energy calculations
|
||||
- Observable measurements
|
||||
- Variational algorithm optimization
|
||||
|
||||
## V2 Interface (Current Standard)
|
||||
|
||||
Qiskit uses V2 primitives (BaseSamplerV2, BaseEstimatorV2) as the current standard. V1 primitives are legacy.
|
||||
|
||||
## Sampler Primitive
|
||||
|
||||
### StatevectorSampler (Local Simulation)
|
||||
|
||||
```python
|
||||
from qiskit import QuantumCircuit
|
||||
from qiskit.primitives import StatevectorSampler
|
||||
|
||||
# Create circuit
|
||||
qc = QuantumCircuit(2)
|
||||
qc.h(0)
|
||||
qc.cx(0, 1)
|
||||
qc.measure_all()
|
||||
|
||||
# Run with Sampler
|
||||
sampler = StatevectorSampler()
|
||||
result = sampler.run([qc], shots=1024).result()
|
||||
|
||||
# Access results
|
||||
counts = result[0].data.meas.get_counts()
|
||||
print(counts) # e.g., {'00': 523, '11': 501}
|
||||
```
|
||||
|
||||
### Multiple Circuits
|
||||
|
||||
```python
|
||||
qc1 = QuantumCircuit(2)
|
||||
qc1.h(0)
|
||||
qc1.measure_all()
|
||||
|
||||
qc2 = QuantumCircuit(2)
|
||||
qc2.x(0)
|
||||
qc2.measure_all()
|
||||
|
||||
# Run multiple circuits
|
||||
sampler = StatevectorSampler()
|
||||
job = sampler.run([qc1, qc2], shots=1000)
|
||||
results = job.result()
|
||||
|
||||
# Access individual results
|
||||
counts1 = results[0].data.meas.get_counts()
|
||||
counts2 = results[1].data.meas.get_counts()
|
||||
```
|
||||
|
||||
### Using Parameters
|
||||
|
||||
```python
|
||||
from qiskit.circuit import Parameter
|
||||
|
||||
theta = Parameter('θ')
|
||||
qc = QuantumCircuit(1)
|
||||
qc.ry(theta, 0)
|
||||
qc.measure_all()
|
||||
|
||||
# Run with parameter values
|
||||
sampler = StatevectorSampler()
|
||||
param_values = [[0], [np.pi/4], [np.pi/2]]
|
||||
result = sampler.run([(qc, param_values)], shots=1024).result()
|
||||
```
|
||||
|
||||
## Estimator Primitive
|
||||
|
||||
### StatevectorEstimator (Local Simulation)
|
||||
|
||||
```python
|
||||
from qiskit import QuantumCircuit
|
||||
from qiskit.primitives import StatevectorEstimator
|
||||
from qiskit.quantum_info import SparsePauliOp
|
||||
|
||||
# Create circuit WITHOUT measurements
|
||||
qc = QuantumCircuit(2)
|
||||
qc.h(0)
|
||||
qc.cx(0, 1)
|
||||
|
||||
# Define observable
|
||||
observable = SparsePauliOp(["ZZ", "XX"])
|
||||
|
||||
# Run Estimator
|
||||
estimator = StatevectorEstimator()
|
||||
result = estimator.run([(qc, observable)]).result()
|
||||
|
||||
# Access expectation values
|
||||
exp_value = result[0].data.evs
|
||||
print(f"Expectation value: {exp_value}")
|
||||
```
|
||||
|
||||
### Multiple Observables
|
||||
|
||||
```python
|
||||
from qiskit.quantum_info import SparsePauliOp
|
||||
|
||||
qc = QuantumCircuit(2)
|
||||
qc.h(0)
|
||||
|
||||
obs1 = SparsePauliOp(["ZZ"])
|
||||
obs2 = SparsePauliOp(["XX"])
|
||||
|
||||
estimator = StatevectorEstimator()
|
||||
result = estimator.run([(qc, obs1), (qc, obs2)]).result()
|
||||
|
||||
ev1 = result[0].data.evs
|
||||
ev2 = result[1].data.evs
|
||||
```
|
||||
|
||||
### Parameterized Estimator
|
||||
|
||||
```python
|
||||
from qiskit.circuit import Parameter
|
||||
import numpy as np
|
||||
|
||||
theta = Parameter('θ')
|
||||
qc = QuantumCircuit(1)
|
||||
qc.ry(theta, 0)
|
||||
|
||||
observable = SparsePauliOp(["Z"])
|
||||
|
||||
# Run with multiple parameter values
|
||||
estimator = StatevectorEstimator()
|
||||
param_values = [[0], [np.pi/4], [np.pi/2], [np.pi]]
|
||||
result = estimator.run([(qc, observable, param_values)]).result()
|
||||
```
|
||||
|
||||
## IBM Quantum Runtime Primitives
|
||||
|
||||
For running on real hardware, use runtime primitives:
|
||||
|
||||
### Runtime Sampler
|
||||
|
||||
```python
|
||||
from qiskit_ibm_runtime import QiskitRuntimeService, SamplerV2 as Sampler
|
||||
|
||||
service = QiskitRuntimeService()
|
||||
backend = service.backend("ibm_brisbane")
|
||||
|
||||
qc = QuantumCircuit(2)
|
||||
qc.h(0)
|
||||
qc.cx(0, 1)
|
||||
qc.measure_all()
|
||||
|
||||
# Run on real hardware
|
||||
sampler = Sampler(backend)
|
||||
job = sampler.run([qc], shots=1024)
|
||||
result = job.result()
|
||||
counts = result[0].data.meas.get_counts()
|
||||
```
|
||||
|
||||
### Runtime Estimator
|
||||
|
||||
```python
|
||||
from qiskit_ibm_runtime import QiskitRuntimeService, EstimatorV2 as Estimator
|
||||
from qiskit.quantum_info import SparsePauliOp
|
||||
|
||||
service = QiskitRuntimeService()
|
||||
backend = service.backend("ibm_brisbane")
|
||||
|
||||
qc = QuantumCircuit(2)
|
||||
qc.h(0)
|
||||
qc.cx(0, 1)
|
||||
|
||||
observable = SparsePauliOp(["ZZ"])
|
||||
|
||||
# Run on real hardware
|
||||
estimator = Estimator(backend)
|
||||
job = estimator.run([(qc, observable)])
|
||||
result = job.result()
|
||||
exp_value = result[0].data.evs
|
||||
```
|
||||
|
||||
## Sessions for Iterative Workloads
|
||||
|
||||
Sessions group multiple jobs to reduce queue wait times:
|
||||
|
||||
```python
|
||||
from qiskit_ibm_runtime import Session
|
||||
|
||||
service = QiskitRuntimeService()
|
||||
backend = service.backend("ibm_brisbane")
|
||||
|
||||
with Session(backend=backend) as session:
|
||||
sampler = Sampler(session=session)
|
||||
|
||||
# Run multiple jobs in session
|
||||
job1 = sampler.run([qc1], shots=1024)
|
||||
result1 = job1.result()
|
||||
|
||||
job2 = sampler.run([qc2], shots=1024)
|
||||
result2 = job2.result()
|
||||
```
|
||||
|
||||
## Batch Mode for Parallel Jobs
|
||||
|
||||
Batch mode runs independent jobs in parallel:
|
||||
|
||||
```python
|
||||
from qiskit_ibm_runtime import Batch
|
||||
|
||||
service = QiskitRuntimeService()
|
||||
backend = service.backend("ibm_brisbane")
|
||||
|
||||
with Batch(backend=backend) as batch:
|
||||
sampler = Sampler(session=batch)
|
||||
|
||||
# Submit multiple independent jobs
|
||||
job1 = sampler.run([qc1], shots=1024)
|
||||
job2 = sampler.run([qc2], shots=1024)
|
||||
|
||||
# Retrieve results when ready
|
||||
result1 = job1.result()
|
||||
result2 = job2.result()
|
||||
```
|
||||
|
||||
## Result Processing
|
||||
|
||||
### Sampler Results
|
||||
|
||||
```python
|
||||
result = sampler.run([qc], shots=1024).result()
|
||||
|
||||
# Get counts
|
||||
counts = result[0].data.meas.get_counts()
|
||||
|
||||
# Get probabilities
|
||||
probs = {k: v/1024 for k, v in counts.items()}
|
||||
|
||||
# Get metadata
|
||||
metadata = result[0].metadata
|
||||
```
|
||||
|
||||
### Estimator Results
|
||||
|
||||
```python
|
||||
result = estimator.run([(qc, observable)]).result()
|
||||
|
||||
# Expectation value
|
||||
exp_val = result[0].data.evs
|
||||
|
||||
# Standard deviation (if available)
|
||||
std_dev = result[0].data.stds
|
||||
|
||||
# Metadata
|
||||
metadata = result[0].metadata
|
||||
```
|
||||
|
||||
## Differences from V1 Primitives
|
||||
|
||||
**V2 Improvements:**
|
||||
- More flexible parameter binding
|
||||
- Better result structure
|
||||
- Improved performance
|
||||
- Cleaner API design
|
||||
|
||||
**Migration from V1:**
|
||||
- Use `StatevectorSampler` instead of `Sampler`
|
||||
- Use `StatevectorEstimator` instead of `Estimator`
|
||||
- Result access changed from `.result().quasi_dists[0]` to `.result()[0].data.meas.get_counts()`
|
||||
99
scientific-skills/qiskit/references/setup.md
Normal file
99
scientific-skills/qiskit/references/setup.md
Normal file
@@ -0,0 +1,99 @@
|
||||
# Qiskit Setup and Installation
|
||||
|
||||
## Installation
|
||||
|
||||
Install Qiskit using uv:
|
||||
|
||||
```bash
|
||||
uv pip install qiskit
|
||||
```
|
||||
|
||||
For visualization capabilities:
|
||||
|
||||
```bash
|
||||
uv pip install "qiskit[visualization]" matplotlib
|
||||
```
|
||||
|
||||
## Python Environment Setup
|
||||
|
||||
Create and activate a virtual environment to isolate dependencies:
|
||||
|
||||
```bash
|
||||
# macOS/Linux
|
||||
python3 -m venv .venv
|
||||
source .venv/bin/activate
|
||||
|
||||
# Windows
|
||||
python -m venv .venv
|
||||
.venv\Scripts\activate
|
||||
```
|
||||
|
||||
## Supported Python Versions
|
||||
|
||||
Check the [Qiskit PyPI page](https://pypi.org/project/qiskit/) for currently supported Python versions. As of 2025, Qiskit typically supports Python 3.8+.
|
||||
|
||||
## IBM Quantum Account Setup
|
||||
|
||||
To run circuits on real IBM Quantum hardware, you need an IBM Quantum account and API token.
|
||||
|
||||
### Creating an Account
|
||||
|
||||
1. Visit [IBM Quantum Platform](https://quantum.ibm.com/)
|
||||
2. Sign up for a free account
|
||||
3. Navigate to your account settings to retrieve your API token
|
||||
|
||||
### Configuring Authentication
|
||||
|
||||
Save your IBM Quantum credentials:
|
||||
|
||||
```python
|
||||
from qiskit_ibm_runtime import QiskitRuntimeService
|
||||
|
||||
# Save credentials (first time only)
|
||||
QiskitRuntimeService.save_account(
|
||||
channel="ibm_quantum",
|
||||
token="YOUR_IBM_QUANTUM_TOKEN"
|
||||
)
|
||||
|
||||
# Later sessions - load saved credentials
|
||||
service = QiskitRuntimeService()
|
||||
```
|
||||
|
||||
### Environment Variable Method
|
||||
|
||||
Alternatively, set the API token as an environment variable:
|
||||
|
||||
```bash
|
||||
export QISKIT_IBM_TOKEN="YOUR_IBM_QUANTUM_TOKEN"
|
||||
```
|
||||
|
||||
## Local Development (No Account Required)
|
||||
|
||||
You can build and test quantum circuits locally without an IBM Quantum account using simulators:
|
||||
|
||||
```python
|
||||
from qiskit import QuantumCircuit
|
||||
from qiskit.primitives import StatevectorSampler
|
||||
|
||||
qc = QuantumCircuit(2)
|
||||
qc.h(0)
|
||||
qc.cx(0, 1)
|
||||
qc.measure_all()
|
||||
|
||||
# Run locally with simulator
|
||||
sampler = StatevectorSampler()
|
||||
result = sampler.run([qc], shots=1024).result()
|
||||
```
|
||||
|
||||
## Verifying Installation
|
||||
|
||||
Test your installation:
|
||||
|
||||
```python
|
||||
import qiskit
|
||||
print(qiskit.__version__)
|
||||
|
||||
from qiskit import QuantumCircuit
|
||||
qc = QuantumCircuit(2)
|
||||
print("Qiskit installed successfully!")
|
||||
```
|
||||
286
scientific-skills/qiskit/references/transpilation.md
Normal file
286
scientific-skills/qiskit/references/transpilation.md
Normal file
@@ -0,0 +1,286 @@
|
||||
# Circuit Transpilation and Optimization
|
||||
|
||||
Transpilation is the process of rewriting a quantum circuit to match the topology and gate set of a specific quantum device, while optimizing for execution on noisy quantum computers.
|
||||
|
||||
## Why Transpilation?
|
||||
|
||||
**Problem**: Abstract quantum circuits may use gates not available on hardware and assume all-to-all qubit connectivity.
|
||||
|
||||
**Solution**: Transpilation transforms circuits to:
|
||||
1. Use only hardware-native gates (basis gates)
|
||||
2. Respect physical qubit connectivity
|
||||
3. Minimize circuit depth and gate count
|
||||
4. Optimize for reduced errors on noisy devices
|
||||
|
||||
## Basic Transpilation
|
||||
|
||||
### Simple Transpile
|
||||
|
||||
```python
|
||||
from qiskit import QuantumCircuit, transpile
|
||||
|
||||
qc = QuantumCircuit(3)
|
||||
qc.h(0)
|
||||
qc.cx(0, 1)
|
||||
qc.cx(1, 2)
|
||||
|
||||
# Transpile for a specific backend
|
||||
transpiled_qc = transpile(qc, backend=backend)
|
||||
```
|
||||
|
||||
### Optimization Levels
|
||||
|
||||
Choose optimization level 0-3:
|
||||
|
||||
```python
|
||||
# Level 0: No optimization (fastest)
|
||||
qc_0 = transpile(qc, backend=backend, optimization_level=0)
|
||||
|
||||
# Level 1: Light optimization
|
||||
qc_1 = transpile(qc, backend=backend, optimization_level=1)
|
||||
|
||||
# Level 2: Moderate optimization (default)
|
||||
qc_2 = transpile(qc, backend=backend, optimization_level=2)
|
||||
|
||||
# Level 3: Heavy optimization (slowest, best results)
|
||||
qc_3 = transpile(qc, backend=backend, optimization_level=3)
|
||||
```
|
||||
|
||||
**Qiskit SDK v2.2** provides **83x faster transpilation** compared to competitors.
|
||||
|
||||
## Transpilation Stages
|
||||
|
||||
The transpiler pipeline consists of six stages:
|
||||
|
||||
### 1. Init Stage
|
||||
- Validates circuit instructions
|
||||
- Translates multi-qubit gates to standard form
|
||||
|
||||
### 2. Layout Stage
|
||||
- Maps virtual qubits to physical qubits
|
||||
- Considers qubit connectivity and error rates
|
||||
|
||||
```python
|
||||
from qiskit.transpiler import CouplingMap
|
||||
|
||||
# Define custom coupling
|
||||
coupling = CouplingMap([(0, 1), (1, 2), (2, 3)])
|
||||
qc_transpiled = transpile(qc, coupling_map=coupling)
|
||||
```
|
||||
|
||||
### 3. Routing Stage
|
||||
- Inserts SWAP gates to satisfy connectivity constraints
|
||||
- Minimizes additional SWAP overhead
|
||||
|
||||
### 4. Translation Stage
|
||||
- Converts gates to hardware basis gates
|
||||
- Typical basis: {RZ, SX, X, CX}
|
||||
|
||||
```python
|
||||
# Specify basis gates
|
||||
basis_gates = ['cx', 'id', 'rz', 'sx', 'x']
|
||||
qc_transpiled = transpile(qc, basis_gates=basis_gates)
|
||||
```
|
||||
|
||||
### 5. Optimization Stage
|
||||
- Reduces gate count and circuit depth
|
||||
- Applies gate cancellation and commutation rules
|
||||
- Uses **virtual permutation elision** (levels 2-3)
|
||||
- Finds separable operations to decompose
|
||||
|
||||
### 6. Scheduling Stage
|
||||
- Adds timing information for pulse-level control
|
||||
|
||||
## Advanced Optimization Features
|
||||
|
||||
### Virtual Permutation Elision
|
||||
|
||||
At optimization levels 2-3, Qiskit analyzes commutation structure to eliminate unnecessary SWAP gates by tracking virtual qubit permutations.
|
||||
|
||||
### Gate Cancellation
|
||||
|
||||
Identifies and removes pairs of gates that cancel:
|
||||
- X-X → I
|
||||
- H-H → I
|
||||
- CNOT-CNOT → I
|
||||
|
||||
### Numerical Decomposition
|
||||
|
||||
Splits two-qubit gates that can be expressed as separable one-qubit operations.
|
||||
|
||||
## Common Transpilation Parameters
|
||||
|
||||
### Initial Layout
|
||||
|
||||
Specify which physical qubits to use:
|
||||
|
||||
```python
|
||||
# Use specific physical qubits
|
||||
initial_layout = [0, 2, 4] # Maps circuit qubits 0,1,2 to physical qubits 0,2,4
|
||||
qc_transpiled = transpile(qc, backend=backend, initial_layout=initial_layout)
|
||||
```
|
||||
|
||||
### Approximation Degree
|
||||
|
||||
Trade accuracy for fewer gates (0.0 = max approximation, 1.0 = no approximation):
|
||||
|
||||
```python
|
||||
# Allow 5% approximation error for fewer gates
|
||||
qc_transpiled = transpile(qc, backend=backend, approximation_degree=0.95)
|
||||
```
|
||||
|
||||
### Seed for Reproducibility
|
||||
|
||||
```python
|
||||
qc_transpiled = transpile(qc, backend=backend, seed_transpiler=42)
|
||||
```
|
||||
|
||||
### Scheduling Method
|
||||
|
||||
```python
|
||||
# Add timing constraints
|
||||
qc_transpiled = transpile(
|
||||
qc,
|
||||
backend=backend,
|
||||
scheduling_method='alap' # As Late As Possible
|
||||
)
|
||||
```
|
||||
|
||||
## Transpiling for Simulators
|
||||
|
||||
Even for simulators, transpilation can optimize circuits:
|
||||
|
||||
```python
|
||||
from qiskit_aer import AerSimulator
|
||||
|
||||
simulator = AerSimulator()
|
||||
qc_optimized = transpile(qc, backend=simulator, optimization_level=3)
|
||||
|
||||
# Compare gate counts
|
||||
print(f"Original: {qc.size()} gates")
|
||||
print(f"Optimized: {qc_optimized.size()} gates")
|
||||
```
|
||||
|
||||
## Target-Aware Transpilation
|
||||
|
||||
Use `Target` objects for detailed backend specifications:
|
||||
|
||||
```python
|
||||
from qiskit.transpiler import Target
|
||||
|
||||
# Transpile with target specification
|
||||
qc_transpiled = transpile(qc, target=backend.target)
|
||||
```
|
||||
|
||||
## Circuit Analysis After Transpilation
|
||||
|
||||
```python
|
||||
qc_transpiled = transpile(qc, backend=backend, optimization_level=3)
|
||||
|
||||
# Analyze results
|
||||
print(f"Depth: {qc_transpiled.depth()}")
|
||||
print(f"Gate count: {qc_transpiled.size()}")
|
||||
print(f"Operations: {qc_transpiled.count_ops()}")
|
||||
|
||||
# Check two-qubit gate count (major error source)
|
||||
two_qubit_gates = qc_transpiled.count_ops().get('cx', 0)
|
||||
print(f"Two-qubit gates: {two_qubit_gates}")
|
||||
```
|
||||
|
||||
**Qiskit produces circuits with 29% fewer two-qubit gates** than leading alternatives, significantly reducing errors.
|
||||
|
||||
## Multiple Circuit Transpilation
|
||||
|
||||
Transpile multiple circuits efficiently:
|
||||
|
||||
```python
|
||||
circuits = [qc1, qc2, qc3]
|
||||
transpiled_circuits = transpile(
|
||||
circuits,
|
||||
backend=backend,
|
||||
optimization_level=3
|
||||
)
|
||||
```
|
||||
|
||||
## Pre-transpilation Best Practices
|
||||
|
||||
### 1. Design with Hardware Topology in Mind
|
||||
|
||||
Consider backend coupling map when designing circuits:
|
||||
|
||||
```python
|
||||
# Check backend coupling
|
||||
print(backend.coupling_map)
|
||||
|
||||
# Design circuits that align with coupling
|
||||
```
|
||||
|
||||
### 2. Use Native Gates When Possible
|
||||
|
||||
Some backends support gates beyond {CX, RZ, SX, X}:
|
||||
|
||||
```python
|
||||
# Check available basis gates
|
||||
print(backend.configuration().basis_gates)
|
||||
```
|
||||
|
||||
### 3. Minimize Two-Qubit Gates
|
||||
|
||||
Two-qubit gates have significantly higher error rates:
|
||||
- Design algorithms to minimize CNOT gates
|
||||
- Use gate identities to reduce counts
|
||||
|
||||
### 4. Test with Simulators First
|
||||
|
||||
```python
|
||||
from qiskit_aer import AerSimulator
|
||||
|
||||
# Test transpilation locally
|
||||
sim_backend = AerSimulator.from_backend(backend)
|
||||
qc_test = transpile(qc, backend=sim_backend, optimization_level=3)
|
||||
```
|
||||
|
||||
## Transpilation for Different Providers
|
||||
|
||||
### IBM Quantum
|
||||
|
||||
```python
|
||||
from qiskit_ibm_runtime import QiskitRuntimeService
|
||||
|
||||
service = QiskitRuntimeService()
|
||||
backend = service.backend("ibm_brisbane")
|
||||
qc_transpiled = transpile(qc, backend=backend)
|
||||
```
|
||||
|
||||
### IonQ
|
||||
|
||||
```python
|
||||
# IonQ has all-to-all connectivity, different basis gates
|
||||
basis_gates = ['gpi', 'gpi2', 'ms']
|
||||
qc_transpiled = transpile(qc, basis_gates=basis_gates)
|
||||
```
|
||||
|
||||
### Amazon Braket
|
||||
|
||||
Transpilation depends on specific device (Rigetti, IonQ, etc.)
|
||||
|
||||
## Performance Tips
|
||||
|
||||
1. **Cache transpiled circuits** - Transpilation is expensive, reuse when possible
|
||||
2. **Use appropriate optimization level** - Level 3 is slow but best for production
|
||||
3. **Leverage v2.2 speed improvements** - Update to latest Qiskit for 83x speedup
|
||||
4. **Parallelize transpilation** - Qiskit automatically parallelizes when transpiling multiple circuits
|
||||
|
||||
## Common Issues and Solutions
|
||||
|
||||
### Issue: Circuit too deep after transpilation
|
||||
**Solution**: Use higher optimization level or redesign circuit with fewer layers
|
||||
|
||||
### Issue: Too many SWAP gates inserted
|
||||
**Solution**: Adjust initial_layout to better match qubit topology
|
||||
|
||||
### Issue: Transpilation takes too long
|
||||
**Solution**: Reduce optimization level or update to Qiskit v2.2+ for speed improvements
|
||||
|
||||
### Issue: Unexpected gate decompositions
|
||||
**Solution**: Check basis_gates and consider specifying custom decomposition rules
|
||||
415
scientific-skills/qiskit/references/visualization.md
Normal file
415
scientific-skills/qiskit/references/visualization.md
Normal file
@@ -0,0 +1,415 @@
|
||||
# Visualization in Qiskit
|
||||
|
||||
Qiskit provides comprehensive visualization tools for quantum circuits, measurement results, and quantum states.
|
||||
|
||||
## Installation
|
||||
|
||||
Install visualization dependencies:
|
||||
|
||||
```bash
|
||||
uv pip install "qiskit[visualization]" matplotlib
|
||||
```
|
||||
|
||||
## Circuit Visualization
|
||||
|
||||
### Text-Based Drawings
|
||||
|
||||
```python
|
||||
from qiskit import QuantumCircuit
|
||||
|
||||
qc = QuantumCircuit(3)
|
||||
qc.h(0)
|
||||
qc.cx(0, 1)
|
||||
qc.cx(1, 2)
|
||||
|
||||
# Simple text output
|
||||
print(qc.draw())
|
||||
|
||||
# Text with more detail
|
||||
print(qc.draw('text', fold=-1)) # Don't fold long circuits
|
||||
```
|
||||
|
||||
### Matplotlib Drawings
|
||||
|
||||
```python
|
||||
# High-quality matplotlib figure
|
||||
qc.draw('mpl')
|
||||
|
||||
# Save to file
|
||||
fig = qc.draw('mpl')
|
||||
fig.savefig('circuit.png', dpi=300, bbox_inches='tight')
|
||||
```
|
||||
|
||||
### LaTeX Drawings
|
||||
|
||||
```python
|
||||
# Generate LaTeX circuit diagram
|
||||
qc.draw('latex')
|
||||
|
||||
# Save LaTeX source
|
||||
latex_source = qc.draw('latex_source')
|
||||
with open('circuit.tex', 'w') as f:
|
||||
f.write(latex_source)
|
||||
```
|
||||
|
||||
## Customizing Circuit Drawings
|
||||
|
||||
### Styling Options
|
||||
|
||||
```python
|
||||
from qiskit.visualization import circuit_drawer
|
||||
|
||||
# Reverse qubit order
|
||||
qc.draw('mpl', reverse_bits=True)
|
||||
|
||||
# Fold long circuits
|
||||
qc.draw('mpl', fold=20) # Fold at 20 columns
|
||||
|
||||
# Show idle wires
|
||||
qc.draw('mpl', idle_wires=False)
|
||||
|
||||
# Add initial state
|
||||
qc.draw('mpl', initial_state=True)
|
||||
```
|
||||
|
||||
### Color Customization
|
||||
|
||||
```python
|
||||
style = {
|
||||
'displaycolor': {
|
||||
'h': ('#FA74A6', '#000000'), # Hadamard: pink
|
||||
'cx': ('#A8D0DB', '#000000'), # CNOT: light blue
|
||||
'measure': ('#F7E7B4', '#000000') # Measure: yellow
|
||||
}
|
||||
}
|
||||
|
||||
qc.draw('mpl', style=style)
|
||||
```
|
||||
|
||||
## Result Visualization
|
||||
|
||||
### Histogram of Counts
|
||||
|
||||
```python
|
||||
from qiskit.visualization import plot_histogram
|
||||
from qiskit.primitives import StatevectorSampler
|
||||
|
||||
qc = QuantumCircuit(2)
|
||||
qc.h(0)
|
||||
qc.cx(0, 1)
|
||||
qc.measure_all()
|
||||
|
||||
sampler = StatevectorSampler()
|
||||
result = sampler.run([qc], shots=1024).result()
|
||||
counts = result[0].data.meas.get_counts()
|
||||
|
||||
# Plot histogram
|
||||
plot_histogram(counts)
|
||||
|
||||
# Compare multiple experiments
|
||||
counts1 = {'00': 500, '11': 524}
|
||||
counts2 = {'00': 480, '11': 544}
|
||||
plot_histogram([counts1, counts2], legend=['Run 1', 'Run 2'])
|
||||
|
||||
# Save figure
|
||||
fig = plot_histogram(counts)
|
||||
fig.savefig('histogram.png', dpi=300, bbox_inches='tight')
|
||||
```
|
||||
|
||||
### Histogram Options
|
||||
|
||||
```python
|
||||
# Customize colors
|
||||
plot_histogram(counts, color=['#1f77b4', '#ff7f0e'])
|
||||
|
||||
# Sort by value
|
||||
plot_histogram(counts, sort='value')
|
||||
|
||||
# Set bar labels
|
||||
plot_histogram(counts, bar_labels=True)
|
||||
|
||||
# Set target distribution (for comparison)
|
||||
target = {'00': 0.5, '11': 0.5}
|
||||
plot_histogram(counts, target=target)
|
||||
```
|
||||
|
||||
## State Visualization
|
||||
|
||||
### Bloch Sphere
|
||||
|
||||
Visualize single-qubit states on the Bloch sphere:
|
||||
|
||||
```python
|
||||
from qiskit.visualization import plot_bloch_vector
|
||||
from qiskit.quantum_info import Statevector
|
||||
import numpy as np
|
||||
|
||||
# Visualize a specific state vector
|
||||
# State |+⟩: equal superposition of |0⟩ and |1⟩
|
||||
state = Statevector.from_label('+')
|
||||
plot_bloch_vector(state.to_bloch())
|
||||
|
||||
# Custom vector
|
||||
plot_bloch_vector([0, 1, 0]) # |+⟩ state on X-axis
|
||||
```
|
||||
|
||||
### Multi-Qubit Bloch Sphere
|
||||
|
||||
```python
|
||||
from qiskit.visualization import plot_bloch_multivector
|
||||
|
||||
qc = QuantumCircuit(2)
|
||||
qc.h(0)
|
||||
qc.cx(0, 1)
|
||||
|
||||
state = Statevector.from_instruction(qc)
|
||||
plot_bloch_multivector(state)
|
||||
```
|
||||
|
||||
### State City Plot
|
||||
|
||||
Visualize state amplitudes as a 3D city:
|
||||
|
||||
```python
|
||||
from qiskit.visualization import plot_state_city
|
||||
from qiskit.quantum_info import Statevector
|
||||
|
||||
qc = QuantumCircuit(3)
|
||||
qc.h(range(3))
|
||||
state = Statevector.from_instruction(qc)
|
||||
|
||||
plot_state_city(state)
|
||||
|
||||
# Customize
|
||||
plot_state_city(state, color=['#FF6B6B', '#4ECDC4'])
|
||||
```
|
||||
|
||||
### QSphere
|
||||
|
||||
Visualize quantum states on a sphere:
|
||||
|
||||
```python
|
||||
from qiskit.visualization import plot_state_qsphere
|
||||
|
||||
state = Statevector.from_instruction(qc)
|
||||
plot_state_qsphere(state)
|
||||
```
|
||||
|
||||
### Hinton Diagram
|
||||
|
||||
Display state amplitudes:
|
||||
|
||||
```python
|
||||
from qiskit.visualization import plot_state_hinton
|
||||
|
||||
state = Statevector.from_instruction(qc)
|
||||
plot_state_hinton(state)
|
||||
```
|
||||
|
||||
## Density Matrix Visualization
|
||||
|
||||
```python
|
||||
from qiskit.visualization import plot_state_density
|
||||
from qiskit.quantum_info import DensityMatrix
|
||||
|
||||
qc = QuantumCircuit(2)
|
||||
qc.h(0)
|
||||
qc.cx(0, 1)
|
||||
|
||||
state = DensityMatrix.from_instruction(qc)
|
||||
plot_state_density(state)
|
||||
```
|
||||
|
||||
## Gate Map Visualization
|
||||
|
||||
Visualize backend coupling map:
|
||||
|
||||
```python
|
||||
from qiskit.visualization import plot_gate_map
|
||||
from qiskit_ibm_runtime import QiskitRuntimeService
|
||||
|
||||
service = QiskitRuntimeService()
|
||||
backend = service.backend("ibm_brisbane")
|
||||
|
||||
# Show qubit connectivity
|
||||
plot_gate_map(backend)
|
||||
|
||||
# Show with error rates
|
||||
plot_gate_map(backend, plot_error_rates=True)
|
||||
```
|
||||
|
||||
## Error Map Visualization
|
||||
|
||||
Display backend error rates:
|
||||
|
||||
```python
|
||||
from qiskit.visualization import plot_error_map
|
||||
|
||||
plot_error_map(backend)
|
||||
```
|
||||
|
||||
## Circuit Properties Display
|
||||
|
||||
```python
|
||||
from qiskit.visualization import plot_circuit_layout
|
||||
|
||||
# Show how circuit maps to physical qubits
|
||||
transpiled_qc = transpile(qc, backend=backend)
|
||||
plot_circuit_layout(transpiled_qc, backend)
|
||||
```
|
||||
|
||||
## Pulse Visualization
|
||||
|
||||
For pulse-level control:
|
||||
|
||||
```python
|
||||
from qiskit import pulse
|
||||
from qiskit.visualization import pulse_drawer
|
||||
|
||||
# Create pulse schedule
|
||||
with pulse.build(backend) as schedule:
|
||||
pulse.play(pulse.Gaussian(duration=160, amp=0.1, sigma=40), pulse.drive_channel(0))
|
||||
|
||||
# Visualize
|
||||
schedule.draw()
|
||||
```
|
||||
|
||||
## Interactive Widgets (Jupyter)
|
||||
|
||||
### Circuit Composer Widget
|
||||
|
||||
```python
|
||||
from qiskit.tools.jupyter import QuantumCircuitComposer
|
||||
|
||||
composer = QuantumCircuitComposer()
|
||||
composer.show()
|
||||
```
|
||||
|
||||
### Interactive State Visualization
|
||||
|
||||
```python
|
||||
from qiskit.visualization import plot_histogram
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
# Enable interactive mode
|
||||
plt.ion()
|
||||
plot_histogram(counts)
|
||||
plt.show()
|
||||
```
|
||||
|
||||
## Comparison Plots
|
||||
|
||||
### Multiple Histograms
|
||||
|
||||
```python
|
||||
# Compare results from different backends
|
||||
counts_sim = {'00': 500, '11': 524}
|
||||
counts_hw = {'00': 480, '01': 20, '10': 24, '11': 500}
|
||||
|
||||
plot_histogram(
|
||||
[counts_sim, counts_hw],
|
||||
legend=['Simulator', 'Hardware'],
|
||||
figsize=(12, 6)
|
||||
)
|
||||
```
|
||||
|
||||
### Before/After Transpilation
|
||||
|
||||
```python
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(16, 4))
|
||||
|
||||
# Original circuit
|
||||
qc.draw('mpl', ax=ax1)
|
||||
ax1.set_title('Original Circuit')
|
||||
|
||||
# Transpiled circuit
|
||||
qc_transpiled = transpile(qc, backend=backend, optimization_level=3)
|
||||
qc_transpiled.draw('mpl', ax=ax2)
|
||||
ax2.set_title('Transpiled Circuit')
|
||||
|
||||
plt.tight_layout()
|
||||
plt.show()
|
||||
```
|
||||
|
||||
## Saving Visualizations
|
||||
|
||||
### Save to Various Formats
|
||||
|
||||
```python
|
||||
# PNG
|
||||
fig = qc.draw('mpl')
|
||||
fig.savefig('circuit.png', dpi=300, bbox_inches='tight')
|
||||
|
||||
# PDF
|
||||
fig.savefig('circuit.pdf', bbox_inches='tight')
|
||||
|
||||
# SVG (vector graphics)
|
||||
fig.savefig('circuit.svg', bbox_inches='tight')
|
||||
|
||||
# Histogram
|
||||
hist_fig = plot_histogram(counts)
|
||||
hist_fig.savefig('results.png', dpi=300, bbox_inches='tight')
|
||||
```
|
||||
|
||||
## Styling Best Practices
|
||||
|
||||
### Publication-Quality Figures
|
||||
|
||||
```python
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
# Set matplotlib style
|
||||
plt.rcParams['figure.dpi'] = 300
|
||||
plt.rcParams['font.size'] = 12
|
||||
plt.rcParams['font.family'] = 'sans-serif'
|
||||
|
||||
# Create high-quality visualization
|
||||
fig = qc.draw('mpl', style='iqp')
|
||||
fig.savefig('publication_circuit.png', dpi=600, bbox_inches='tight')
|
||||
```
|
||||
|
||||
### Available Styles
|
||||
|
||||
```python
|
||||
# Default style
|
||||
qc.draw('mpl')
|
||||
|
||||
# IQP style (IBM Quantum)
|
||||
qc.draw('mpl', style='iqp')
|
||||
|
||||
# Colorblind-friendly
|
||||
qc.draw('mpl', style='bw') # Black and white
|
||||
```
|
||||
|
||||
## Troubleshooting Visualization
|
||||
|
||||
### Common Issues
|
||||
|
||||
**Issue**: "No module named 'matplotlib'"
|
||||
```bash
|
||||
uv pip install matplotlib
|
||||
```
|
||||
|
||||
**Issue**: Circuit too large to display
|
||||
```python
|
||||
# Use folding
|
||||
qc.draw('mpl', fold=50)
|
||||
|
||||
# Or export to file instead of displaying
|
||||
fig = qc.draw('mpl')
|
||||
fig.savefig('large_circuit.png', dpi=150, bbox_inches='tight')
|
||||
```
|
||||
|
||||
**Issue**: Jupyter notebook not displaying plots
|
||||
```python
|
||||
# Add magic command at notebook start
|
||||
%matplotlib inline
|
||||
```
|
||||
|
||||
**Issue**: LaTeX visualization not working
|
||||
```bash
|
||||
# Install LaTeX support
|
||||
uv pip install pylatexenc
|
||||
```
|
||||
Reference in New Issue
Block a user