Source code for QHyper.solvers.gate_based.pennylane.wf_qaoa

import pennylane as qml
from pennylane import numpy as np

from typing import Callable

from dataclasses import dataclass, field

from QHyper.problems.base import Problem
from QHyper.optimizers import OptimizationResult, Optimizer, Dummy, OptimizationParameter

from QHyper.util import weighted_avg_evaluation

from QHyper.solvers.gate_based.pennylane.qaoa import QAOA


[docs] @dataclass class WF_QAOA(QAOA): """ Different implementation of QAOA. This implementation uses different function to evaluate the hamiltonian - this function doesn't return expectation value but the score of the solution. Attributes ---------- problem : Problem The problem to be solved. layers : int Number of layers. gamma : OptimizationParameter Vector of gamma angles used in cost Hamiltonian. Size of the vector should be equal to the number of layers. beta : OptimizationParameter Vector of beta angles used in mixing Hamiltonian. Size of the vector should be equal to the number of layers. optimizer : Optimizer Optimizer used in the classical part of the algorithm. penalty_weights : list[float] | None Penalty weights used for converting Problem to QUBO. They connect cost function with constraints. If not specified, all penalty weights are set to 1. limit_results : int | None, default None Specifies how many results should be considered in the evaluation of the objective function. If None, all results are considered. penalty : float, default 0 When calculating the score of the solution, the penalty is the score for the solution that doesn't satisfy the constraints. backend : str, default 'default.qubit' Backend for PennyLane. mixer : str, default 'pl_x_mixer' Mixer name. Currently only 'pl_x_mixer' is supported. qubo_cache : dict[tuple[float, ...], qml.Hamiltonian] Cache for QUBO. dev : qml.devices.LegacyDevice PennyLane device instance. """ problem: Problem layers: int gamma: OptimizationParameter beta: OptimizationParameter optimizer: Optimizer penalty_weights: list[float] | None penalty: float = 0 backend: str = "default.qubit" mixer: str = "pl_x_mixer" limit_results: int | None = None qubo_cache: dict[tuple[float, ...], qml.Hamiltonian] = field( default_factory=dict, init=False) dev: qml.devices.LegacyDevice | None = field(default=None, init=False) def __init__( self, problem: Problem, layers: int, gamma: OptimizationParameter, beta: OptimizationParameter, penalty_weights: list[float] | None = None, penalty: float = 0, backend: str = "default.qubit", mixer: str = "pl_x_mixer", limit_results: int | None = None, optimizer: Optimizer = Dummy(), ) -> None: self.problem = problem self.optimizer = optimizer self.gamma = gamma self.beta = beta self.penalty = penalty self.penalty_weights = penalty_weights self.limit_results = limit_results self.layers = layers self.backend = backend self.mixer = mixer self.qubo_cache = {} def get_expval_circuit(self, penalty_weights: list[float] ) -> Callable[[list[float]], float]: cost_operator = self.create_cost_operator(self.problem, penalty_weights) self.dev = qml.device(self.backend, wires=cost_operator.wires) probs_func = self.get_probs_func(self.problem, penalty_weights) def wrapper(angles: list[float]) -> float: probs = probs_func(angles) if isinstance(probs, np.numpy_boxes.ArrayBox): probs = probs._value dtype = [ (wire, 'i4') for wire in self.dev.wires]+[('probability', 'f8')] recarray = np.recarray((len(probs),), dtype=dtype) for i, probability in enumerate(probs): solution = format(i, "b").zfill(self._get_num_of_wires()) recarray[i] = *solution, probability result = weighted_avg_evaluation( recarray, self.problem.get_score, self.penalty, limit_results=self.limit_results ) return OptimizationResult(result, angles) return wrapper