1010
1111from time import time
1212from typing import Any , Dict , Optional
13+
1314import matplotlib .pyplot as plt
1415import numpy as np
15-
1616from qiskit import QuantumCircuit
1717from qiskit .quantum_info import SparsePauliOp
1818from scipy .optimize import minimize
1919
2020from qaoa_training_pipeline .evaluation import EVALUATORS
2121from qaoa_training_pipeline .evaluation .base_evaluator import BaseEvaluator
22- from qaoa_training_pipeline .training .history_mixin import HistoryMixin
2322from qaoa_training_pipeline .training .base_trainer import BaseTrainer
23+ from qaoa_training_pipeline .training .functions import BaseAnglesFunction
24+ from qaoa_training_pipeline .training .history_mixin import HistoryMixin
2425from qaoa_training_pipeline .training .param_result import ParamResult
2526
2627
28+ class TQATrainerFunction (BaseAnglesFunction ):
29+ """Wrapper function around TQATrainer.tqa_schedule.
30+
31+ This function allows for a default ``rep`` value to be set, which is passed
32+ to ``tqa_schedule``. If no value has been set yet, and no value is provided
33+ to :meth:`__call__`, then an error is raised.
34+ """
35+
36+ def __init__ (self , tqa_schedule_method : callable , reps : int | None = None ) -> None :
37+ """Create an instance of a TQATrainer QAOA angles function.
38+
39+ Args:
40+ tqa_schedule_method: The :meth:`TQATrainer.tqa_schedule` method for
41+ an instance of :class:`TQATrainer`.
42+ reps: The default reps to use if not overridden by
43+ :meth:`TQATrainer.train`. If None and ``reps`` is not provided
44+ in :meth:`__call__`, an error is raised. Defaults to None.
45+ """
46+ super ().__init__ ()
47+ self ._tqa_schedule = tqa_schedule_method
48+ self .reps = reps
49+
50+ # pylint: disable=unused-argument
51+ def __call__ (self , x : list , reps : int | None = None ) -> list :
52+ if reps is None :
53+ reps = self .reps
54+ if reps is None :
55+ raise ValueError (
56+ f"reps must be provided to { self .__class__ .__name__ } (reps=...) or "
57+ + "set with trainer.train(..., reps=...)"
58+ )
59+ return self ._tqa_schedule (reps = reps , dt = x [0 ])
60+
61+ # pylint: disable=unused-argument
62+ @classmethod
63+ def from_config (cls , config : dict ) -> None :
64+ """Create a TQATrainer from a config dictionary."""
65+ raise RuntimeError (f"{ cls .__name__ } cannot be constructed from a config." )
66+
67+
2768class TQATrainer (BaseTrainer , HistoryMixin ):
2869 """Trotterized Quantum Annealing parameter generation.
2970
@@ -33,13 +74,24 @@ class TQATrainer(BaseTrainer, HistoryMixin):
3374 be used as an initial point generator which is independent of problem instance and
3475 does not do any optimization. However, we can also use this class to perform a
3576 SciPy optimization of the end point of the TQA schedule.
77+
78+
79+ .. note::
80+
81+ :attr:`qaoa_angles_function` for :class:`TQATrainer` requires knowledge
82+ of the number of repetitions. ``reps`` can be provided by calling
83+ ``qaoa_angles_function(params, reps=reps)``. If no value for ``reps`` is
84+ provided, the value for the most recent call to :meth:`train` will be
85+ used. If :meth:`train` has not been called yet for the instance of
86+ :class:`TQATrainer`, then an error is raised.
3687 """
3788
3889 def __init__ (
3990 self ,
4091 evaluator : Optional [BaseEvaluator ] = None ,
4192 minimize_args : Optional [Dict [str , Any ]] = None ,
4293 energy_minimization : bool = False ,
94+ initial_dt : float = 0.75 ,
4395 ) -> None :
4496 """Initialize an instance.
4597
@@ -51,11 +103,18 @@ def __init__(
51103 energy_minimization: Allows us to switch between minimizing the energy or maximizing
52104 the energy. The default and assumed convention in this repository is to
53105 maximize the energy.
106+ initial_dt: Initial dt if not provided to :meth:`train`. Defaults to
107+ ``0.75``.
54108 """
55- BaseTrainer .__init__ (self , evaluator )
109+ BaseTrainer .__init__ (
110+ self ,
111+ evaluator ,
112+ qaoa_angles_function = TQATrainerFunction (self .tqa_schedule , reps = None ),
113+ )
56114 HistoryMixin .__init__ (self )
57115
58116 self ._minimize_args = {"method" : "COBYLA" , "options" : {"maxiter" : 20 , "rhobeg" : 0.1 }}
117+ self .initial_dt = initial_dt
59118
60119 minimize_args = minimize_args or {}
61120 self ._minimize_args .update (minimize_args )
@@ -76,9 +135,17 @@ def train(
76135 mixer : Optional [QuantumCircuit ] = None ,
77136 initial_state : Optional [QuantumCircuit ] = None ,
78137 ansatz_circuit : Optional [QuantumCircuit ] = None ,
138+ initial_dt : float | None = None ,
79139 ) -> ParamResult :
80140 """Train the QAOA parameters."""
81141 self .reset_history ()
142+ # Set the reps attribute on the angles function, if it supports one.
143+ # This allow us to override it later with a function that doesn't
144+ # require reps. We set reps here so that ParamResult.from_scipy_result
145+ # correctly populates "optimized_qaoa_angles" and so we can call
146+ # `trainer.qaoa_angles_function()` in scripts.
147+ if hasattr (self ._qaoa_angles_function , "reps" ):
148+ self ._qaoa_angles_function .reps = reps
82149
83150 def _energy (x ):
84151 """Optimize the energy by minimizing the negative energy.
@@ -105,21 +172,15 @@ def _energy(x):
105172
106173 start = time ()
107174
175+ initial_dt = initial_dt or self .initial_dt
108176 if self .evaluator is None :
109- tqa_dt = 0.75
110- param_result = ParamResult (
111- self .tqa_schedule (reps , dt = tqa_dt ), time () - start , self , None
112- )
177+ param_result = ParamResult ([initial_dt ], time () - start , self , None )
113178 else :
114- params0 = [0.75 ]
179+ params0 = [initial_dt ]
115180 result = minimize (_energy , params0 , ** self ._minimize_args )
116181 param_result = ParamResult .from_scipy_result (
117182 result , params0 , time () - start , self ._sign , self
118183 )
119- param_result ["optimized_params" ] = self .tqa_schedule (
120- reps , dt = param_result ["optimized_params" ]
121- )
122-
123184 param_result .add_history (self )
124185
125186 return param_result
0 commit comments