Program Batches
The divi.qprog.batch module provides powerful program batch capabilities for running multiple quantum programs in parallel, managing large-scale hyperparameter sweeps, and handling complex workflows.
Overview
Program batches in Divi enable you to:
Parallel Execution: Run multiple quantum programs simultaneously
Hyperparameter Sweeps: Systematically explore parameter spaces
Large Problem Decomposition: Break down complex problems into manageable subproblems
Progress Monitoring: Track execution across multiple programs with rich progress bars
Core Architecture
- class ProgramBatch(backend)[source]
Bases:
ABCThis abstract class provides the basic scaffolding for higher-order computations that require more than one quantum program to achieve its goal.
- Each implementation of this class has to have an implementation of two functions:
- create_programs: This function generates the independent programs that
are needed to achieve the objective of the job. The creation of those programs can utilize the instance variables of the class to initialize their parameters. The programs should be stored in a key-value store where the keys represent the identifier of the program, whether random or identificatory.
- aggregate_results: This function aggregates the results of the programs
after they are done executing. This function should be aware of the different formats the programs might have (counts dictionary, expectation value, etc) and handle such cases accordingly.
- property total_circuit_count
Get the total number of circuits executed across all programs in the batch.
- Returns:
Cumulative count of circuits submitted by all programs.
- Return type:
- property total_run_time
Get the total runtime across all programs in the batch.
- Returns:
Cumulative execution time in seconds across all programs.
- Return type:
- property programs: dict
Get a copy of the programs dictionary.
- Returns:
- Copy of the programs dictionary mapping program IDs to
QuantumProgram instances. Modifications to this dict will not affect the internal state.
- Return type:
- reset()[source]
Reset the batch to its initial state.
Clears all programs, stops any running executors, terminates listener threads, and stops progress bars. This allows the batch to be reused for a new set of programs.
Note
Any running programs will be forcefully stopped. Results from incomplete programs will be lost.
- run(blocking=False)[source]
Execute all programs in the batch.
Starts all quantum programs in parallel using a thread pool. Can run in blocking or non-blocking mode.
- Parameters:
blocking (bool, optional) – If True, waits for all programs to complete before returning. If False, returns immediately and programs run in the background. Defaults to False.
- Returns:
Returns self for method chaining.
- Return type:
- Raises:
RuntimeError – If a batch is already running or if no programs have been created.
Note
In non-blocking mode, call join() later to wait for completion and collect results.
- check_all_done()[source]
Check if all programs in the batch have completed execution.
- Returns:
- True if all programs are finished (successfully or with errors),
False if any are still running.
- Return type:
- join()[source]
Wait for all programs in the batch to complete and collect results.
Blocks until all programs finish execution, aggregating their circuit counts and run times. Handles keyboard interrupts gracefully by attempting to cancel remaining programs.
- Returns:
Returns False if interrupted by KeyboardInterrupt, None otherwise.
- Return type:
bool or None
- Raises:
RuntimeError – If any program fails with an exception, after cancelling remaining programs.
Note
This method should be called after run(blocking=False) to wait for completion. It’s automatically called when using run(blocking=True).
Usage:
from divi.qprog.batch import ProgramBatch
from divi.backends import ParallelSimulator
class MyBatch(ProgramBatch):
def create_programs(self):
# Create multiple quantum programs
self.programs = {
"program_1": VQE(...),
"program_2": QAOA(...),
"program_3": VQE(...)
}
def run(self):
# Execute all programs in parallel
return super().run()
# Run program batch
batch = MyBatch(backend=ParallelSimulator())
results = batch.run()
Workflows
VQE Hyperparameter Sweeps
- class VQEHyperparameterSweep(ansatze, molecule_transformer, optimizer=None, max_iterations=10, **kwargs)[source]
Bases:
ProgramBatchAllows user to carry out a grid search across different values for the ansatz and the bond length used in a VQE program.
Initialize a VQE hyperparameter sweep.
- Parameters:
ansatze (Sequence[Ansatz]) – A sequence of ansatz circuits to test.
molecule_transformer (MoleculeTransformer) – A MoleculeTransformer object defining the configuration for generating the molecule variants.
optimizer (Optimizer) – The optimization algorithm for the VQE runs.
max_iterations (int) – The maximum number of optimizer iterations for each VQE run.
**kwargs (Forwarded to parent class.)
- __init__(ansatze, molecule_transformer, optimizer=None, max_iterations=10, **kwargs)[source]
Initialize a VQE hyperparameter sweep.
- Parameters:
ansatze (Sequence[Ansatz]) – A sequence of ansatz circuits to test.
molecule_transformer (MoleculeTransformer) – A MoleculeTransformer object defining the configuration for generating the molecule variants.
optimizer (Optimizer) – The optimization algorithm for the VQE runs.
max_iterations (int) – The maximum number of optimizer iterations for each VQE run.
**kwargs (Forwarded to parent class.)
- create_programs()[source]
Create VQE programs for all combinations of ansatze and molecule variants.
Generates molecule variants using the configured MoleculeTransformer, then creates a VQE program for each (ansatz, molecule_variant) pair.
Note
Program IDs are tuples of (ansatz_name, bond_modifier_value).
- aggregate_results()[source]
Find the best ansatz and bond configuration from all VQE runs.
Compares the final energies across all ansatz/molecule combinations and returns the configuration that achieved the lowest ground state energy.
- Returns:
- A tuple containing:
best_config (tuple): (ansatz_name, bond_modifier) of the best result.
best_energy (float): The lowest energy achieved.
- Return type:
- Raises:
RuntimeError – If programs haven’t been run or have empty losses.
Molecule Transformer
- class MoleculeTransformer(base_molecule, bond_modifiers, atom_connectivity=None, bonds_to_transform=None, alignment_atoms=None)[source]
Bases:
objectA class for transforming molecular structures by modifying bond lengths.
This class generates variants of a base molecule by adjusting bond lengths according to specified modifiers. The modification mode is detected automatically.
- base_molecule
The reference molecule used as a template for generating variants.
- Type:
qml.qchem.Molecule
- bond_modifiers
A list of values used to adjust bond lengths. The class will generate one new molecule for each modifier in this list. The modification mode is detected automatically: - Scale mode: If all values are positive, they are used as scaling factors (e.g., 1.1 for a 10% increase). - Delta mode: If any value is zero or negative, all values are treated as additive changes to the bond length, in Ångstroms.
- Type:
Sequence[float]
- atom_connectivity
A sequence of atom index pairs specifying the bonds in the molecule. If not provided, a chain structure will be assumed e.g.: [(0, 1), (1, 2), (2, 3), …].
- bonds_to_transform
A subset of atom_connectivity that specifies the bonds to modify. If None, all bonds will be transformed.
- alignment_atoms
Indices of atoms onto which to align the orientation of the resulting variants of the molecule. Only useful for visualization and debugging. If None, no alignment is carried out.
- Type:
Sequence[int] | None
-
base_molecule:
Molecule
- __init__(base_molecule, bond_modifiers, atom_connectivity=None, bonds_to_transform=None, alignment_atoms=None)
Graph Partitioning QAOA
- class GraphPartitioningQAOA(graph, graph_problem, n_layers, backend, partitioning_config, initial_state='Recommended', aggregate_fn=<function linear_aggregation>, optimizer=None, max_iterations=10, **kwargs)[source]
Bases:
ProgramBatchInitializes the graph partitioning class.
- Parameters:
graph (nx.Graph | rx.PyGraph) – The input graph to be partitioned.
graph_problem (GraphProblem) – The type of graph partitioning problem (e.g., EDGE_PARTITIONING).
n_layers (int) – Number of layers for the QAOA circuit.
backend (CircuitRunner) – Backend used to run quantum/classical circuits.
partitioning_config (PartitioningConfig) – the configuration of the partitioning as to the algorithm and
output. (expected)
initial_state ("Zeros", "Ones", "Superposition", "Recommended", optional) – Initial state for the QAOA algorithm. Defaults to “Recommended”.
aggregate_fn (optional) – Aggregation function to combine results. Defaults to linear_aggregation.
optimizer (optional) – Optimizer to use for QAOA. Defaults to Optimizers.MONTE_CARLO.
max_iterations (int, optional) – Maximum number of optimization iterations. Defaults to 10.
**kwargs – Additional keyword arguments passed to the QAOA constructor.
- __init__(graph, graph_problem, n_layers, backend, partitioning_config, initial_state='Recommended', aggregate_fn=<function linear_aggregation>, optimizer=None, max_iterations=10, **kwargs)[source]
Initializes the graph partitioning class.
- Parameters:
graph (nx.Graph | rx.PyGraph) – The input graph to be partitioned.
graph_problem (GraphProblem) – The type of graph partitioning problem (e.g., EDGE_PARTITIONING).
n_layers (int) – Number of layers for the QAOA circuit.
backend (CircuitRunner) – Backend used to run quantum/classical circuits.
partitioning_config (PartitioningConfig) – the configuration of the partitioning as to the algorithm and
output. (expected)
initial_state ("Zeros", "Ones", "Superposition", "Recommended", optional) – Initial state for the QAOA algorithm. Defaults to “Recommended”.
aggregate_fn (optional) – Aggregation function to combine results. Defaults to linear_aggregation.
optimizer (optional) – Optimizer to use for QAOA. Defaults to Optimizers.MONTE_CARLO.
max_iterations (int, optional) – Maximum number of optimization iterations. Defaults to 10.
**kwargs – Additional keyword arguments passed to the QAOA constructor.
- create_programs()[source]
Creates and initializes QAOA programs for each partitioned subgraph.
The main graph is partitioned into node-based subgraphs according to the specified partitioning configuration. Each subgraph is relabeled with integer node labels for QAOA compatibility, and a reverse index map is stored for later result aggregation.
- Each program is assigned a unique program ID, which is a tuple of:
An uppercase letter (A, B, C, …) corresponding to the partition index.
The number of nodes in the subgraph.
Example program ID: (‘A’, 5) for the first partition with 5 nodes.
The created QAOA programs are stored in the self.programs dictionary, keyed by their program IDs.
- aggregate_results()[source]
Aggregates the results from all QAOA subprograms to form a global solution.
This method collects the final bitstring solutions from each partitioned subgraph’s QAOA program, using the aggregation function specified at initialization (e.g., linear or dominance aggregation). It reconstructs the global solution by mapping each subgraph’s solution back to the original node indices using the stored reverse index maps.
The final solution is stored in self.solution as a list of node indices assigned to the selected partition.
- Raises:
RuntimeError – If no programs exist, if programs have not been run, or if results are incomplete.
- Returns:
The list of node indices in the final aggregated solution.
- Return type:
Partitioning Configuration
- class PartitioningConfig(max_n_nodes_per_cluster=None, minimum_n_clusters=None, partitioning_algorithm='spectral')[source]
Bases:
objectConfiguration for graph partitioning algorithms.
This class defines the parameters and constraints for partitioning large graphs into smaller subgraphs for quantum algorithm execution. It supports multiple partitioning algorithms and allows specification of size constraints.
- max_n_nodes_per_cluster
Maximum number of nodes allowed in each cluster. If None, no upper limit is enforced. Must be a positive integer.
- minimum_n_clusters
Minimum number of clusters to create. If None, no lower limit is enforced. Must be a positive integer.
- partitioning_algorithm
Algorithm to use for partitioning. Options are: - “spectral”: Spectral partitioning using Fiedler vector (default) - “metis”: METIS graph partitioning library - “kernighan_lin”: Kernighan-Lin algorithm
Note
At least one of max_n_nodes_per_cluster or minimum_n_clusters must be specified. Both constraints cannot be None.
Examples
>>> # Partition into clusters of at most 10 nodes >>> config = PartitioningConfig(max_n_nodes_per_cluster=10)
>>> # Create at least 5 clusters using METIS >>> config = PartitioningConfig( ... minimum_n_clusters=5, ... partitioning_algorithm="metis" ... )
>>> # Both constraints: clusters of max 8 nodes, min 3 clusters >>> config = PartitioningConfig( ... max_n_nodes_per_cluster=8, ... minimum_n_clusters=3 ... )
-
partitioning_algorithm:
Literal['spectral','metis','kernighan_lin'] = 'spectral'
- __init__(max_n_nodes_per_cluster=None, minimum_n_clusters=None, partitioning_algorithm='spectral')
QUBO Partitioning QAOA
- class QUBOPartitioningQAOA(qubo, decomposer, n_layers, backend, composer=SplatComposer(), optimizer=None, max_iterations=10, **kwargs)[source]
Bases:
ProgramBatchInitialize a QUBOPartitioningQAOA instance for solving QUBO problems using partitioning and QAOA.
- Parameters:
qubo (QUBOProblemTypes | BinaryQuadraticModel) – The QUBO problem to solve, provided as a supported type or a BinaryQuadraticModel. Note: Variable types are assumed to be binary (not Spin).
decomposer (hybrid.traits.ProblemDecomposer) – The decomposer used to partition the QUBO problem into subproblems.
n_layers (int) – Number of QAOA layers to use for each subproblem.
backend (CircuitRunner) – Backend responsible for running quantum circuits.
composer (hybrid.traits.SubsamplesComposer, optional) – Composer to aggregate subsamples from subproblems. Defaults to hybrid.SplatComposer().
optimizer (Optimizer, optional) – Optimizer to use for QAOA. Defaults to Optimizer.MONTE_CARLO.
max_iterations (int, optional) – Maximum number of optimization iterations. Defaults to 10.
**kwargs – Additional keyword arguments passed to the QAOA constructor.
- __init__(qubo, decomposer, n_layers, backend, composer=SplatComposer(), optimizer=None, max_iterations=10, **kwargs)[source]
Initialize a QUBOPartitioningQAOA instance for solving QUBO problems using partitioning and QAOA.
- Parameters:
qubo (QUBOProblemTypes | BinaryQuadraticModel) – The QUBO problem to solve, provided as a supported type or a BinaryQuadraticModel. Note: Variable types are assumed to be binary (not Spin).
decomposer (hybrid.traits.ProblemDecomposer) – The decomposer used to partition the QUBO problem into subproblems.
n_layers (int) – Number of QAOA layers to use for each subproblem.
backend (CircuitRunner) – Backend responsible for running quantum circuits.
composer (hybrid.traits.SubsamplesComposer, optional) – Composer to aggregate subsamples from subproblems. Defaults to hybrid.SplatComposer().
optimizer (Optimizer, optional) – Optimizer to use for QAOA. Defaults to Optimizer.MONTE_CARLO.
max_iterations (int, optional) – Maximum number of optimization iterations. Defaults to 10.
**kwargs – Additional keyword arguments passed to the QAOA constructor.
- create_programs()[source]
Partition the main QUBO problem and instantiate QAOA programs for each subproblem.
This implementation: - Uses the configured decomposer to split the main QUBO into subproblems. - For each subproblem, creates a QAOA program with the specified parameters. - Stores each program in self.programs with a unique identifier.
- Unique Identifier Format:
- Each key in self.programs is a tuple of the form (letter, size), where:
letter: An uppercase letter (‘A’, ‘B’, ‘C’, …) indicating the partition index.
size: The number of variables in the subproblem.
Example: (‘A’, 5) refers to the first partition with 5 variables.
- aggregate_results()[source]
Aggregate results from all QUBO subproblems into a global solution.
Collects solutions from each partitioned subproblem (both QAOA-optimized and trivial ones) and uses the hybrid framework composer to combine them into a final solution for the original QUBO problem.
- Returns:
- A tuple containing:
solution (np.ndarray): Binary solution vector for the QUBO problem.
solution_energy (float): Energy/cost of the solution.
- Return type:
- Raises:
RuntimeError – If programs haven’t been run or if final probabilities haven’t been computed.