Source code for QHyper.solvers.gate_based.pennylane.qml_qaoa
# This work was supported by the EuroHPC PL infrastructure funded at the
# Smart Growth Operational Programme (2014-2020), Measure 4.2
# under the grant agreement no. POIR.04.02.00-00-D014/20-00
from dataclasses import dataclass, field
from typing import Any, cast, Callable
import pennylane as qml
from QHyper.problems.base import Problem
from QHyper.optimizers.qml_gradient_descent import QmlGradientDescent
from QHyper.optimizers import (
OptimizationResult, Optimizer, OptimizationParameter)
from QHyper.solvers.gate_based.pennylane.qaoa import QAOA
[docs]
@dataclass
class QML_QAOA(QAOA):
"""
QAOA implementation with additonal support for PennyLane optimizers.
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.
backend : str
Backend for PennyLane.
mixer : str
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 = None
mixer: str = "pl_x_mixer"
backend: str = "default.qubit"
qubo_cache: dict[tuple[float, ...], qml.Hamiltonian] = field(
default_factory=dict, init=False)
dev: qml.devices.LegacyDevice | None = field(default=None, init=False)
def __post_init__(self) -> None:
if not isinstance(self.optimizer, QmlGradientDescent):
raise ValueError(f"Optimizer {self.optimizer} not supported")
def get_expval_circuit(
self, penalty_weights: list[float]
) -> Callable[[list[float]], OptimizationResult]:
cost_operator = self.create_cost_operator(
self.problem, penalty_weights)
self.dev = qml.device(self.backend, wires=cost_operator.wires)
@qml.qnode(self.dev)
def expval_circuit(angles: list[float]) -> Any:
self._circuit(angles, cost_operator)
return qml.expval(cost_operator)
return expval_circuit
def _run_optimizer(
self,
penalty_weights: list[float],
angles: OptimizationParameter
) -> OptimizationResult:
return self.optimizer.minimize_expval_func(
cast(qml.QNode, self.get_expval_circuit(penalty_weights)), angles)