Backends Guide
Divi provides a flexible backend system that allows you to run quantum programs on different execution environments, from local simulators to cloud-based quantum hardware. This guide will walk you through the available backends and how to choose the right one for your needs.
Backend Architecture
All backends in Divi implement the CircuitRunner interface, providing a consistent API regardless of the underlying execution environment. This powerful abstraction allows you to develop your quantum programs locally and then switch to a different backend—like cloud hardware—with a single line of code.
Understanding ExecutionResult
All backend submit_circuits() methods return an ExecutionResult object, which provides a unified interface for handling both synchronous and asynchronous execution.
- For Synchronous Backends (like
ParallelSimulator): Results are available immediately after submission:
from divi.backends import ParallelSimulator backend = ParallelSimulator() result = backend.submit_circuits({"circuit_0": qasm_string}) # Access results directly for circuit_result in result.results: label = circuit_result["label"] counts = circuit_result["results"] print(f"{label}: {counts}")
- For Asynchronous Backends (like
QoroService): For cloud-based backends, you need to wait for the job to complete and then fetch the results:
from divi.backends import QoroService service = QoroService() result = service.submit_circuits({"circuit_0": qasm_string}) # Wait for the job to complete service.poll_job_status(result, loop_until_complete=True) # Fetch the results completed_result = service.get_job_results(result) # Access the results for circuit_result in completed_result.results: label = circuit_result["label"] counts = circuit_result["results"] print(f"{label}: {counts}")
Note: For most use cases, you don’t need to interact with ExecutionResult directly. The backends handle the workflow automatically. The examples above show the typical patterns for accessing results from both synchronous and asynchronous backends.
- Result Format:
The
resultsattribute is a list of dictionaries, each containing:label(str): The circuit label from your input dictionaryresults(dict): The execution results (bitstring counts for sampling mode, or expectation values for expectation mode)
Example:
[ {"label": "circuit_0", "results": {"00": 500, "11": 500}}, {"label": "circuit_1", "results": {"01": 1000}} ]
Available Backends
Divi comes with two primary backends out of the box:
ParallelSimulator: A high-performance local simulator with parallel execution capabilities, perfect for development and testing.
QoroService: A cloud-based quantum computing service for accessing powerful simulators and real quantum hardware.
Let’s dive into each one.
ParallelSimulator
The ParallelSimulator is your go-to backend for local development, testing, and research. It’s designed for speed and flexibility, allowing you to iterate quickly without needing an internet connection.
Key Features:
Fast Execution: Optimized simulation with parallel processing to take full advantage of your local machine.
Noise Modeling: Simulate realistic noise conditions by integrating with Qiskit’s fake backends.
Flexible Configuration: Easily customize the number of shots, parallel processes, and noise models.
Local Execution: Runs entirely on your machine, no cloud access required.
Getting Started with ParallelSimulator
Using the simulator is straightforward. You can create a default instance or configure it to your specific needs.
from divi.backends import ParallelSimulator
# A basic simulator with default settings
backend = ParallelSimulator()
# A configured simulator for better accuracy and speed
backend = ParallelSimulator(
shots=1000,
n_processes=4
)
Advanced Configuration
You can tune the ParallelSimulator for different scenarios, like maximizing performance or simulating a noisy environment.
# High-performance configuration for production-level simulations
backend = ParallelSimulator(
shots=10000, # Increase measurement shots for higher precision
n_processes=8, # Use more parallel processes
qiskit_backend="auto", # Let Divi auto-select the best available simulator
seed=42 # Set a random seed for reproducible results
)
# Noisy simulation to mimic real hardware
from qiskit_ibm_runtime.fake_provider import FakeManilaV2
backend = ParallelSimulator(
shots=5000,
qiskit_backend=FakeManilaV2(), # Use a fake backend with a realistic noise model
n_processes=2
)
QoroService
The QoroService provides access to cloud-based quantum computing resources, including advanced simulation services with greater bandwidth and a wider variety of simulation types (such as tensor networks), as well as real quantum hardware. While ParallelSimulator is ideal for local prototyping, QoroService offers production-grade simulation capabilities and hardware access. The service supports two execution modes: sampling mode for measurement histograms (available on both simulation and hardware) and expectation mode for expectation values (currently simulation-only).
Key Features:
Advanced Simulation: Access production-grade simulation services with greater bandwidth and a variety of simulation types, including tensor networks, beyond what’s available in local prototyping.
Real Hardware: Run your algorithms on actual quantum computers.
Scalable Execution: The service is designed to handle large queues of jobs efficiently.
Circuit Packing: Enable circuit packing optimization via
JobConfig.use_circuit_packingto improve execution efficiency by combining multiple circuits into optimized batches.Job Configuration: Use
JobConfigto configure job settings including shots, QPU system selection, circuit packing, and tags. Set default configurations at service initialization or override them per job.Job Management: Track job status (PENDING, RUNNING, COMPLETED, FAILED, CANCELLED), poll for completion with configurable intervals, retrieve results, and delete jobs.
Getting Started with QoroService
To use the service, you’ll first need to initialize it and test your connection.
from divi.backends import QoroService, JobType
# Initialize the service (API keys are loaded from your environment)
service = QoroService()
# Test your connection to the service
service.test_connection()
Execution Modes
The QoroService supports two distinct execution modes:
Sampling Mode (circuit-only input): Submit circuits without Hamiltonian operators. The service executes the circuits with a specified number of shots and returns measurement histograms (bitstring counts). This mode works with both simulation (
JobType.SIMULATE) and real hardware (JobType.EXECUTE).Expectation Mode (circuit with Pauli terms): Submit circuits along with Hamiltonian operators specified as semicolon-separated Pauli terms (e.g.,
"XYZ;XXZ;ZIZ"). The service automatically usesJobType.EXPECTATIONand returns expectation values for each Pauli term. Note: Expectation mode is currently only available on simulation backends, not on real hardware.
Submitting and Monitoring Jobs
The workflow for submitting circuits depends on which execution mode you’re using.
Sampling Mode Example:
# Prepare your circuits as a dictionary
circuits = {
"circuit_1": qasm_string_1,
"circuit_2": qasm_string_2
}
# Submit the job in sampling mode (no ham_ops parameter)
execution_result = service.submit_circuits(
circuits,
job_type=JobType.SIMULATE # Can also use JobType.EXECUTE for real hardware
)
# Monitor the execution until completion
service.poll_job_status(execution_result, loop_until_complete=True)
# Retrieve your results (returns ExecutionResult with results populated)
completed_result = service.get_job_results(execution_result)
results = completed_result.results
# Example output shape:
# [{'label': 'circuit_0', 'results': {'0011': 2000}},
# {'label': 'circuit_1', 'results': {'0011': 2000}}, ...]
Expectation Mode Example:
# Prepare your circuits and Pauli operators
circuits = {
"circuit_1": qasm_string_1
}
# Define Hamiltonian operators as semicolon-separated Pauli terms
# Each term must have the same length and contain only I, X, Y, Z
ham_ops = "XYZ;XXZ;ZIZ"
# Submit the job in expectation mode (ham_ops automatically sets JobType.EXPECTATION)
# Note: This mode is only available on simulation backends, not real hardware
# The QPU system in your JobConfig should be a simulation system (e.g., "qoro_maestro")
execution_result = service.submit_circuits(
circuits,
ham_ops=ham_ops
# job_type is automatically set to JobType.EXPECTATION when ham_ops is provided
)
# Monitor the execution until completion
service.poll_job_status(execution_result, loop_until_complete=True)
# Retrieve your results (returns ExecutionResult with results populated)
completed_result = service.get_job_results(execution_result)
results = completed_result.results
# Example output shape:
# [{'label': 'circuit_0', 'results': {'IIII': 1.0, 'XXXX': 0.0, 'YYYY': 0.0, 'ZZZZ': 1.0}}]
Note
Bitstring Ordering: QoroService returns bitstrings in Little Endian ordering (least significant bit first, rightmost bit is qubit 0), but Hamiltonian operators passed via the ham_ops parameter should follow Big Endian ordering (most significant bit first, leftmost bit is qubit 0). For example, a 4-qubit system with qubits labeled 0-3: the bitstring "0011" in results represents qubit 0=1, qubit 1=1, qubit 2=0, qubit 3=0 (reading right to left), while the Hamiltonian operator "ZIZI" applies Z to qubit 0, I to qubit 1, Z to qubit 2, and I to qubit 3 (reading left to right).
Configuring Jobs with JobConfig
The QoroService uses a JobConfig object to manage settings for job submissions. You can configure it in two ways:
Default Configuration: Set a default
JobConfigwhen you initialize the service. This configuration will apply to all jobs unless you override it.Override Configuration: For a specific job, you can provide an
override_configto thesubmit_circuitsmethod.
from divi.backends import QoroService, JobConfig
# 1. Set a custom default configuration for the service
default_config = JobConfig(
shots=500,
qpu_system="qoro_maestro",
use_circuit_packing=True,
tag="default_run"
)
service = QoroService(config=default_config)
# 2. Override the default configuration for a single job
override = JobConfig(shots=2000, tag="high_shot_run")
execution_result = service.submit_circuits(circuits, override_config=override)
# This job will run with 2000 shots and the tag 'high_shot_run',
# but will still use 'qoro_maestro' and circuit packing from the default config.
Backend Selection Guide
Choosing the right backend depends on what stage of development you’re in.
For Development and Testing, use
ParallelSimulator. It offers fast iteration cycles, easy debugging, and is completely free.For Production Runs, use
QoroService. It provides access to real quantum hardware, scalable execution, and advanced features.For Research, it’s often best to use both. Start with
ParallelSimulatorfor rapid prototyping and then useQoroServicefor final validation and to compare simulated results against real hardware.
Backend Comparison
The best choice of backend depends on your specific needs. Here’s a summary of the key differences:
Feature |
ParallelSimulator |
QoroService |
|---|---|---|
Use Case |
Development & Prototyping |
Production (Simulation & Real Hardware) |
Speed |
Fast (Local CPU) |
High-throughput (Cloud) |
Accuracy |
Ideal |
Ideal (simulation) / Real-world (hardware) |
Cost |
Free |
Pay-per-use |
Scalability |
Limited by local hardware |
High (Cloud infrastructure) |
Availability |
Always (Local) |
Queue-dependent |
Best Practices
Start Local: Always begin your development and testing with the
ParallelSimulator.Monitor Resources: Keep an eye on your circuit counts and execution times to avoid unexpected costs.
Choose the Right Backend: Select your backend based on your specific problem requirements.
Handle Errors Gracefully: Implement proper error handling and fallbacks in your code.
Optimize Your Configuration: Tune your backend parameters to get the best performance for your use case.
Common Issues and Solutions
Slow Simulation: Increase
n_processes, reduceshotsfor testing.High Memory Usage: Reduce
n_processes, process circuits in smaller batches.Job Queue Delays: Submit jobs during off-peak hours or use local simulation for development.
Connection Problems: Check your internet connection, verify your API credentials, and implement retry logic.
Next Steps
Try the runnable examples in the tutorials/ directory.
Learn about Error Mitigation for improving your results on noisy hardware.