Source code for qplex.workflows.ibm_session_workflow

import qiskit_ibm_runtime
import scipy.optimize
import qiskit.transpiler.preset_passmanagers
import docplex.mp.model

import qplex.commons.algorithm_factory
from qplex.model.execution_config import ExecutionConfig
import qplex.utils.workflow_utils


[docs] def run_ibm_session_workflow(model: docplex.mp.model.Model, ibmq_solver, options: ExecutionConfig): """ Executes a quantum optimization workflow using IBM Quantum Runtime. Parameters ---------- model : Model The optimization model to be solved, typically formulated as a QUBO. ibmq_solver : Solver The IBM Quantum solver instance responsible for managing the quantum backend, parsing input, and processing the results. options: Options A dictionary containing configuration options for the workflow. Returns ------- dict A dictionary representing the optimal measurement results (bitstring counts) obtained after optimizing the quantum circuit's parameters. """ service = ibmq_solver.service verbose = options.verbose optimizer = options.optimizer callback = options.callback max_iter = options.max_iter tolerance = options.tolerance algorithm_config = qplex.commons.algorithm_factory.AlgorithmConfig( algorithm=qplex.commons.algorithm_factory.AlgorithmType( options.algorithm), penalty=options.penalty, seed=options.seed, p=options.p, mixer=options.mixer, layers=options.layers, ansatz=options.ansatz ) algorithm_instance = qplex.commons.algorithm_factory.AlgorithmFactory.get_algorithm( model, algorithm_config) vqc = ibmq_solver.parse_input(algorithm_instance.circuit) backend = ibmq_solver.select_backend(vqc.num_qubits) pass_manager = qiskit.transpiler.preset_passmanagers.generate_preset_pass_manager( backend=backend, optimization_level= ibmq_solver.optimization_level) starting_point = algorithm_instance.get_starting_point() isa_circuit = pass_manager.run(vqc) def cost_function(params) -> float: # pragma: no cover """ Computes the cost (objective function value) for a given set of parameters. Parameters ---------- params : np.ndarray An array of parameters for the quantum circuit. Returns ------- float The computed cost (energy) for the given parameters. """ counts = compute_counts(params, ibmq_solver, isa_circuit, sampler) cost = qplex.utils.workflow_utils.calculate_energy(counts, ibmq_solver.shots, algorithm_instance) if verbose: print(f'\nCost = {cost}') return cost with qiskit_ibm_runtime.Session(service=service, backend=backend) as session: sampler = qiskit_ibm_runtime.SamplerV2(mode=session) optimization_result = scipy.optimize.minimize(fun=cost_function, x0=starting_point, method=optimizer, tol=tolerance, callback=callback, options={ 'maxiter': max_iter}) optimal_params = optimization_result.x return compute_counts(optimal_params, ibmq_solver, isa_circuit, sampler)
[docs] def compute_counts(params, solver, qc, sampler): """ Computes the measurement results (bitstring counts) for a given set of parameters. This function binds the parameters to the quantum circuit, runs the circuit using the provided sampler, and parses the raw measurement results into meaningful counts. Parameters ---------- params : np.ndarray An array of parameters to assign to the quantum circuit. solver : Solver The solver instance responsible for running the quantum circuit and processing the results. qc : QuantumCircuit The transpiled quantum circuit. sampler : Sampler The sampler instance responsible for running the quantum circuit on the quantum backend. Returns ------- dict A dictionary of bitstring counts representing the measurement results from the quantum execution. """ params_dict = {f'theta{i}': params[i] for i in range(len(params))} bound_vqc = qc.assign_parameters(params_dict) raw_counts = solver.run(bound_vqc, sampler) counts = solver.parse_response(raw_counts) return counts