Source code for QHyper.optimizers.qml_gradient_descent

# 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 typing import Callable, Any, Type

import pennylane as qml
from pennylane import numpy as np

from QHyper.optimizers.base import (
    Optimizer, OptimizationResult, OptimizerError, OptimizationParameter)


QML_GRADIENT_DESCENT_OPTIMIZERS: dict[
        str, Type[qml.GradientDescentOptimizer]] = {
    'adam': qml.AdamOptimizer,
    'adagrad': qml.AdagradOptimizer,
    'rmsprop': qml.RMSPropOptimizer,
    'momentum': qml.MomentumOptimizer,
    'nesterov_momentum': qml.NesterovMomentumOptimizer,
    'sgd': qml.GradientDescentOptimizer,
    'qng': qml.QNGOptimizer,
}


[docs] class QmlGradientDescent(Optimizer): """Gradient Descent Optimizer This minimizer is a wrapper for gradient descent optimizers provided by PennyLane. This alogrithm requries the following parameters to be set: - `init` initial values for each parameter Attributes ---------- optimizer : qml.GradientDescentOptimizer object of class GradientDescentOptimizer or inheriting from this class steps : int, default 200 number of optimization steps stepsize : float, default 0.005 stepsize for the optimizer verbose : bool, default False if set to True, additional information will be printed """ optimizer: qml.GradientDescentOptimizer steps: int stepsize: float verbose: bool def __init__( self, name: str = 'adam', steps: int = 200, stepsize: float = 0.005, verbose: bool = False, **kwargs: Any ) -> None: """ Parameters ---------- name : str, default 'adam' name of the gradient descent optimizer provided by PennyLane steps : int, default 200 number of optimization steps stepsize : float, default 0.005 stepsize for the optimizer verbose : bool, default False if set to True, additional information will be printed **kwargs : Any Additional arguments that will be passed to the PennyLane optimizer. More infomation can be found in the PennyLane documentation. """ if name not in QML_GRADIENT_DESCENT_OPTIMIZERS: raise ValueError( f'Optimizer {name} not found. ' 'Available optimizers: ' f'{list(QML_GRADIENT_DESCENT_OPTIMIZERS.keys())}' ) self.optimizer = QML_GRADIENT_DESCENT_OPTIMIZERS[name]( stepsize=stepsize, **kwargs ) self.steps = steps self.verbose = verbose
[docs] def minimize( self, func: Callable[[list[float]], OptimizationResult], init: OptimizationParameter ) -> OptimizationResult: init.assert_init() if isinstance(self.optimizer, qml.QNGOptimizer): raise OptimizerError( 'QNG is not supported via optimizer, use qml_qaoa instead') def wrapper(params: list[float]) -> float: return func(list(params)).value cost_history = [] best_result = float('inf') best_params = np.array(init.init, requires_grad=True) params = np.array(init.init, requires_grad=True) if hasattr(self.optimizer, 'reset'): self.optimizer.reset() # type: ignore for i in range(self.steps): params, cost = self.optimizer.step_and_cost(wrapper, params) params = np.array(params, requires_grad=True) if cost < best_result: best_params = params best_result = cost cost_history.append(OptimizationResult(float(cost), params)) if self.verbose: print(f'Step {i+1}/{self.steps}: {float(cost)}') return OptimizationResult(best_result, best_params, [cost_history])
[docs] def minimize_expval_func( self, func: qml.QNode, init: OptimizationParameter ) -> OptimizationResult: """ Used in :py:class:`.QML_QAOA` to minimize the expectation value function. """ cost_history = [] cost = float('inf') params = np.array(init.init, requires_grad=True) if hasattr(self.optimizer, 'reset'): self.optimizer.reset() # type: ignore for i in range(self.steps): params, cost = self.optimizer.step_and_cost(func, params) params = np.array(params, requires_grad=True) cost_history.append(OptimizationResult(float(cost), params)) if self.verbose: print(f'Step {i+1}/{self.steps}: {float(cost)}') return OptimizationResult(cost, params, [cost_history])