Skip to content

Commit 73bc246

Browse files
committed
Adding excitationsolve optimizer
1 parent d99699b commit 73bc246

File tree

3 files changed

+148
-2
lines changed

3 files changed

+148
-2
lines changed

requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ pytest
1010
openfermion ~= 1.0 # can not be smaller than 1.0
1111
#cmake # needed by qulacs, can be removed otherwise, now in qulacs requirements
1212
qulacs # default simulator (best integration), remove if the installation gives you trouble and just install one of the other supported backend. Version restriction only for noise models, otherwise the new version is fine
13+
excitationsolve
1314

1415
#optional quantum backends
1516
#cirq >= 0.9.2 #

src/tequila/optimizers/__init__.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
from tequila.optimizers.optimizer_base import OptimizerHistory, Optimizer, TequilaOptimizerException, OptimizerResults
22
from tequila.optimizers.optimizer_scipy import OptimizerSciPy
33
from tequila.optimizers.optimizer_gd import OptimizerGD
4+
from tequila.optimizers.optimizer_excsolve import OptimizerExcitationSolve
45
from tequila.optimizers.optimizer_scipy import minimize as minimize_scipy
56
from tequila.optimizers.optimizer_gd import minimize as minimize_gd
7+
from tequila.optimizers.optimizer_excsolve import minimize as minimize_excsolve
68
from tequila.simulators.simulator_api import simulate
79
from dataclasses import dataclass
810

@@ -18,12 +20,16 @@ class _Optimizers:
1820
methods: list = None
1921

2022

21-
SUPPORTED_OPTIMIZERS = ["scipy", "gpyopt", "gd"]
23+
SUPPORTED_OPTIMIZERS = ["scipy", "gpyopt", "gd", "excitationsolve"]
2224
INSTALLED_OPTIMIZERS = {}
2325
INSTALLED_OPTIMIZERS["scipy"] = _Optimizers(
2426
cls=OptimizerSciPy, minimize=minimize_scipy, methods=OptimizerSciPy.available_methods()
2527
)
2628
INSTALLED_OPTIMIZERS["gd"] = _Optimizers(cls=OptimizerGD, minimize=minimize_gd, methods=OptimizerGD.available_methods())
29+
INSTALLED_OPTIMIZERS["excitationsolve"] = _Optimizers(
30+
cls=OptimizerExcitationSolve, minimize=minimize_excsolve, methods=OptimizerExcitationSolve.available_methods()
31+
)
32+
2733

2834
has_gpyopt = False
2935
try:
@@ -138,5 +144,5 @@ def minimize(
138144
)
139145

140146
raise TequilaOptimizerException(
141-
"Could not find optimization method {} in tequila optimizers. You might miss dependencies"
147+
f"Could not find optimization method {method} in tequila optimizers. You might miss dependencies"
142148
)
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
import scipy
2+
import numpy
3+
import typing
4+
import numbers
5+
from excitationsolve import ExcitationSolveScipy
6+
from tequila.objective import Objective
7+
from tequila.objective.objective import assign_variable, Variable, format_variable_dictionary, format_variable_list
8+
from .optimizer_base import Optimizer, OptimizerResults
9+
from ._containers import _EvalContainer, _GradContainer, _HessContainer, _QngContainer
10+
from .optimizer_scipy import SciPyResults
11+
from tequila.utils.exceptions import TequilaException
12+
from tequila.circuit.noise import NoiseModel
13+
from tequila.tools.qng import get_qng_combos
14+
15+
from dataclasses import dataclass
16+
17+
18+
class OptimizerExcitationSolve(Optimizer):
19+
r"""The ExcitationSolve optimizer as a SciPy optimizer that can be given to the scipy.optimize.minimize function.
20+
21+
Usage:
22+
```python
23+
excsolve_obj = ExcitationSolveScipy(maxiter=100, tol=1e-10, save_parameters=True)
24+
optimizer = excsolve_obj.minimize
25+
res = scipy.optimize.minimize(cost, params, method=optimizer)
26+
energies = excsolve_obj.energies
27+
counts = excsolve_obj.nfevs
28+
```
29+
30+
Note that this optimizer never needs to evaluate the ansatz circuit
31+
at the (current) optimal parameters, unless the optimal parameters fall onto
32+
the sample points used to reconstruct the energy function.
33+
Therefore, when used with a qiskit VQE object, the energies transmitted
34+
to a VQE callback function, do not seem to improve or converge. Nevertheless,
35+
the determined optimal energy and parameters are still returned.
36+
37+
Args:
38+
maxiter (int): Maximum number of VQE iterations (maximum number of times to optimize all parameters)
39+
tol: Threshold of energy difference after subsequent VQE iterations defining convergence
40+
num_samples (int, optional): Number of different parameter values at which to sample
41+
the energy to reconstruct the energy function in one parameter.
42+
Must be greater or equal to 5. Defaults to 5.
43+
hf_energy (float | None, optional): The Hartree-Fock energy, i.e. the energy of the
44+
system where all parameters in the circuit are zero. If none, this will be
45+
calculated by evaluating the energy of the ansatz with all parameters set to zero.
46+
If this energy is known from a prior classical calculation, e.g. a Hartree-Fock
47+
calculation, one energy evaluation is saved. Defaults to None.
48+
save_parameters (bool, optional): If True, params member variable contains
49+
all optimal parameter values after each optimization step,
50+
i.e. after optimizing each single parameter. Defaults to False.
51+
param_scaling (float, optional): Factor used for rescaling the parameters. This ExcitationSolve optimizer
52+
expects the parameters to be 2\pi periodic. For example, in Qiskit
53+
the excitation parameters result in excitation operators being \pi periodic.
54+
Therefore, we use a factor of 0.5 for qiskit, resulting in a Period of 2\pi.
55+
"""
56+
57+
@classmethod
58+
def available_methods(cls):
59+
""":return: All tested available methods"""
60+
return ["excitationsolve"]
61+
62+
def __init__(
63+
self, maxiter, tol=1e-12, num_samples=5, hf_energy=None, save_parameters=False, param_scaling=0.5, **kwargs
64+
):
65+
if maxiter is None:
66+
maxiter = 10
67+
68+
super().__init__(**kwargs)
69+
70+
self.opt = ExcitationSolveScipy(
71+
maxiter=maxiter,
72+
tol=tol,
73+
num_samples=num_samples,
74+
hf_energy=hf_energy,
75+
save_parameters=save_parameters,
76+
param_scaling=param_scaling,
77+
)
78+
79+
def __call__(
80+
self,
81+
objective: Objective,
82+
variables: typing.List[Variable],
83+
initial_values: typing.Dict[Variable, numbers.Real] = None,
84+
*args,
85+
**kwargs,
86+
) -> scipy.optimize.OptimizeResult:
87+
objective = objective.contract()
88+
infostring = "{:15} : {}\n".format("Method", "ExcitationSolve")
89+
infostring += "{:15} : {} expectationvalues\n".format("Objective", objective.count_expectationvalues())
90+
91+
# if self.save_history and reset_history:
92+
# self.reset_history()
93+
94+
active_angles, passive_angles, variables = self.initialize_variables(objective, initial_values, variables)
95+
96+
# Transform the initial value directory into (ordered) arrays
97+
param_keys, param_values = zip(*active_angles.items())
98+
param_values = numpy.array(param_values)
99+
100+
# do the compilation here to avoid costly recompilation during the optimization
101+
compiled_objective = self.compile_objective(objective=objective, *args, **kwargs)
102+
E = _EvalContainer(
103+
objective=compiled_objective,
104+
param_keys=param_keys,
105+
samples=self.samples,
106+
passive_angles=passive_angles,
107+
save_history=self.save_history,
108+
print_level=self.print_level,
109+
)
110+
111+
res = self.opt.minimize(E, param_values, *args, **kwargs)
112+
113+
if self.save_history:
114+
self.history.energy_calls = self.opt.energies
115+
116+
return SciPyResults(energy=res.fun, history=self.history, variables=res.x, scipy_result=res)
117+
118+
119+
def minimize(
120+
objective: Objective,
121+
variables: typing.List[Variable],
122+
initial_values: typing.Dict[Variable, numbers.Real] = None,
123+
method: str = "excitationsolve",
124+
maxiter: int = 10,
125+
*args,
126+
**kwargs,
127+
):
128+
optimize = OptimizerExcitationSolve(
129+
maxiter=maxiter,
130+
*args,
131+
**kwargs,
132+
)
133+
return optimize(
134+
objective=objective,
135+
variables=variables,
136+
initial_values=initial_values,
137+
*args,
138+
**kwargs,
139+
)

0 commit comments

Comments
 (0)