Source code for qplex.model.qmodel

import time
from dataclasses import dataclass
from enum import Enum
from typing import Dict, Any, Optional

import docplex.mp.model as dpmodel
import docplex.mp.solution as dpsolution
import qiskit_optimization
import qiskit_optimization.translators
import qiskit_optimization.converters
import qplex.commons
import qplex.commons.solver_factory
import qplex.utils.workflow_utils
import qplex.workflows
import qplex.model.execution_config as ec
import os


[docs] @dataclass class ModelSolution: solution: Dict[str, Any] objective: float execution_time: float method: str provider: Optional[str] = None backend: Optional[str] = None algorithm: str = "N/A"
[docs] class QModel(dpmodel.Model): """ Creates an instance of a QModel. Parameters ---------- name: str The name of the model. Returns ---------- An instance of a QModel. """ def __init__(self, name): super(QModel, self).__init__(name) self.quantum_api_tokens: Dict[str, Optional[str]] = { 'd-wave_token': os.environ.get('D-WAVE_API_TOKEN'), 'ibmq_token': os.environ.get('IBMQ_API_TOKEN'), } self._qmodel_solution: Optional[ModelSolution] = None
[docs] def get_qubo(self, penalty: float | None = None) -> ( qiskit_optimization.QuadraticProgram): """ Returns the QUBO encoding of this problem. Parameters ---------- penalty: float, optional The penalty factor for the QUBO conversion. Returns ------- QuadraticProgram The QUBO encoding of this problem. """ mod = qiskit_optimization.translators.from_docplex_mp(self) converter = qiskit_optimization.converters.QuadraticProgramToQubo( penalty=penalty) return converter.convert(mod)
[docs] def solve(self, method: str = 'classical', config=ec.ExecutionConfig()): """ Solves the model using the specified method and Options. Parameters ---------- method: str, optional The method to solve the model, either 'classical' or 'quantum'. config: Options, optional The options for solving the model. Raises ------ ValueError If the method argument is not 'classical' or 'quantum'. """ start_time = time.time() if method == 'classical': super().solve() solution = self._create_solution( execution_time=time.time() - start_time, method=method ) elif method == 'quantum': provider_config = qplex.commons.solver_factory.ProviderConfig( shots=config.shots, backend=config.backend, provider_options=config.provider_options ) solver = qplex.commons.SolverFactory.get_solver( provider=qplex.commons.solver_factory.ProviderType( config.provider), quantum_api_tokens= self.quantum_api_tokens, config=provider_config) if config.provider == 'd-wave': result = solver.solve(self) elif (config.provider == "ibmq" and config.workflow == 'ibm_session'): optimal_counts = qplex.workflows.run_ibm_session_workflow( model=self, ibmq_solver= solver, options=config) result = ( qplex.utils.workflow_utils.get_solution_from_counts( model=self, optimal_counts=optimal_counts)) else: optimal_counts = qplex.workflows.ggae_workflow(model=self, solver=solver, options=config) result = qplex.utils.workflow_utils.get_solution_from_counts( model=self, optimal_counts= optimal_counts) solution = self._create_solution( execution_time=time.time() - start_time, method=method, provider=config.provider, backend=config.backend, algorithm=config.algorithm, result=result ) else: raise ValueError("Invalid value for argument 'method'. Must be " "'classical' or 'quantum'") self._set_solution(solution)
def _create_solution( self, execution_time: float, method: str, provider: Optional[str] = None, backend: Optional[str] = None, algorithm: str = "N/A", result: Optional[Dict] = None ) -> ModelSolution: """Creates a ModelSolution object from the solve results.""" if method == 'classical': solution = {var.name: var.solution_value for var in self.iter_variables()} objective = self.objective_value else: solution = result['solution'] objective = result['objective'] return ModelSolution( solution=solution, objective=objective, execution_time=execution_time, method=method, provider=provider, backend=backend, algorithm=algorithm ) def _set_solution(self, solution: ModelSolution): """ Sets the solution for the model. Used internally by the QModel's solve method. Parameters ---------- solution: ModelSolution The ModelSolution containing 'solution' and 'objective'. """ self._qmodel_solution = solution solve_solution = dpsolution.SolveSolution(model=self, var_value_map=solution.solution, obj=solution.objective, name=self.name) super()._set_solution(new_solution=solve_solution)
[docs] def print_solution(self, print_zeros: bool = False, solution_header_fmt: Optional[str] = None, var_value_fmt: Optional[str] = None, **kwargs): """ Prints the solution of the model. Must be called after solve(). Parameters ---------- print_zeros: bool, optional Whether to print variables with zero values. solution_header_fmt: str, optional Format string for the solution header. var_value_fmt: str, optional Format string for variable values. kwargs: dict Additional keyword arguments. """ if not self._qmodel_solution: print("No solution available. Please run solve() first.") return print("\nResults") print("----------") print(f"Method: {self._qmodel_solution.method}") print(f"Algorithm: {self._qmodel_solution.algorithm}") print(f"Provider: {self._qmodel_solution.provider or 'N/A'}") print(f"Backend: {self._qmodel_solution.backend or 'N/A'}") print( f"Execution time: {round(self._qmodel_solution.execution_time, 2)} " f"seconds") super().print_solution( print_zeros=print_zeros, solution_header_fmt=solution_header_fmt, var_value_fmt=var_value_fmt, **kwargs ) print("----------")