forked from Qiskit/qiskit
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add NFT optimizer (qiskit-community/qiskit-aqua#729)
* add NFT optimizer * Add specific unit test for NFT Co-authored-by: Manoel Marques <manoel@us.ibm.com> Co-authored-by: Steve Wood <40241007+woodsp-ibm@users.noreply.github.com> Co-authored-by: woodsp <woodsp@us.ibm.com>
- Loading branch information
1 parent
0468b95
commit 83a8b39
Showing
2 changed files
with
184 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,181 @@ | ||
# -*- coding: utf-8 -*- | ||
|
||
# This code is part of Qiskit. | ||
# | ||
# (C) Copyright IBM 2019, 2020. | ||
# | ||
# This code is licensed under the Apache License, Version 2.0. You may | ||
# obtain a copy of this license in the LICENSE.txt file in the root directory | ||
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. | ||
# | ||
# Any modifications or derivative works of this code must retain this | ||
# copyright notice, and modified files need to carry a notice indicating | ||
# that they have been altered from the originals. | ||
|
||
"""Nakanishi-Fujii-Todo algorithm.""" | ||
|
||
from typing import Optional | ||
import logging | ||
|
||
import numpy as np | ||
from scipy.optimize import minimize | ||
from scipy.optimize import OptimizeResult | ||
from .optimizer import Optimizer | ||
|
||
|
||
logger = logging.getLogger(__name__) | ||
|
||
|
||
class NFT(Optimizer): | ||
""" | ||
Nakanishi-Fujii-Todo algorithm. | ||
See https://arxiv.org/abs/1903.12166 | ||
""" | ||
|
||
_OPTIONS = ['maxiter', 'maxfev', 'disp', 'reset_interval'] | ||
|
||
# pylint: disable=unused-argument | ||
def __init__(self, | ||
maxiter: Optional[int] = None, | ||
maxfev: int = 1024, | ||
disp: bool = False, | ||
reset_interval: int = 32) -> None: | ||
""" | ||
Built out using scipy framework, for details, please refer to | ||
https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.minimize.html. | ||
Args: | ||
maxiter: Maximum number of iterations to perform. | ||
maxfev: Maximum number of function evaluations to perform. | ||
disp: disp | ||
reset_interval: The minimum estimates directly once | ||
in ``reset_interval`` times. | ||
Notes: | ||
In this optimization method, the optimization function have to satisfy | ||
three conditions written in [1]_. | ||
References: | ||
.. [1] K. M. Nakanishi, K. Fujii, and S. Todo. 2019. | ||
Sequential minimal optimization for quantum-classical hybrid algorithms. | ||
arXiv preprint arXiv:1903.12166. | ||
""" | ||
super().__init__() | ||
for k, v in locals().items(): | ||
if k in self._OPTIONS: | ||
self._options[k] = v | ||
|
||
def get_support_level(self): | ||
""" return support level dictionary """ | ||
return { | ||
'gradient': Optimizer.SupportLevel.ignored, | ||
'bounds': Optimizer.SupportLevel.ignored, | ||
'initial_point': Optimizer.SupportLevel.required | ||
} | ||
|
||
def optimize(self, num_vars, objective_function, gradient_function=None, | ||
variable_bounds=None, initial_point=None): | ||
super().optimize(num_vars, objective_function, gradient_function, | ||
variable_bounds, initial_point) | ||
|
||
res = minimize(objective_function, initial_point, | ||
method=nakanishi_fujii_todo, options=self._options) | ||
return res.x, res.fun, res.nfev | ||
|
||
|
||
# pylint: disable=invalid-name | ||
def nakanishi_fujii_todo(fun, x0, args=(), maxiter=None, maxfev=1024, | ||
reset_interval=32, eps=1e-32, callback=None, **_): | ||
""" | ||
Find the global minimum of a function using the nakanishi_fujii_todo | ||
algorithm [1]. | ||
Args: | ||
fun (callable): ``f(x, *args)`` | ||
Function to be optimized. ``args`` can be passed as an optional item | ||
in the dict ``minimizer_kwargs``. | ||
This function must satisfy the three condition written in Ref. [1]. | ||
x0 (ndarray): shape (n,) | ||
Initial guess. Array of real elements of size (n,), | ||
where 'n' is the number of independent variables. | ||
args (tuple, optional): | ||
Extra arguments passed to the objective function. | ||
maxiter (int): | ||
Maximum number of iterations to perform. | ||
Default: None. | ||
maxfev (int): | ||
Maximum number of function evaluations to perform. | ||
Default: 1024. | ||
reset_interval (int): | ||
The minimum estimates directly once in ``reset_interval`` times. | ||
Default: 32. | ||
eps (float): eps | ||
**_ : additional options | ||
callback (callable, optional): | ||
Called after each iteration. | ||
Returns: | ||
OptimizeResult: | ||
The optimization result represented as a ``OptimizeResult`` object. | ||
Important attributes are: ``x`` the solution array. See | ||
`OptimizeResult` for a description of other attributes. | ||
Notes: | ||
In this optimization method, the optimization function have to satisfy | ||
three conditions written in [1]. | ||
References: | ||
.. [1] K. M. Nakanishi, K. Fujii, and S. Todo. 2019. | ||
Sequential minimal optimization for quantum-classical hybrid algorithms. | ||
arXiv preprint arXiv:1903.12166. | ||
""" | ||
|
||
x0 = np.asarray(x0) | ||
recycle_z0 = None | ||
niter = 0 | ||
funcalls = 0 | ||
|
||
while True: | ||
|
||
idx = niter % x0.size | ||
|
||
if reset_interval > 0: | ||
if niter % reset_interval == 0: | ||
recycle_z0 = None | ||
|
||
if recycle_z0 is None: | ||
z0 = fun(np.copy(x0), *args) | ||
funcalls += 1 | ||
else: | ||
z0 = recycle_z0 | ||
|
||
p = np.copy(x0) | ||
p[idx] = x0[idx] + np.pi / 2 | ||
z1 = fun(p, *args) | ||
funcalls += 1 | ||
|
||
p = np.copy(x0) | ||
p[idx] = x0[idx] - np.pi / 2 | ||
z3 = fun(p, *args) | ||
funcalls += 1 | ||
|
||
z2 = z1 + z3 - z0 | ||
c = (z1 + z3) / 2 | ||
a = np.sqrt((z0 - z2) ** 2 + (z1 - z3) ** 2) / 2 | ||
b = np.arctan((z1 - z3) / ((z0 - z2) + eps * (z0 == z2))) + x0[idx] | ||
b += 0.5 * np.pi + 0.5 * np.pi * np.sign((z0 - z2) + eps * (z0 == z2)) | ||
|
||
x0[idx] = b | ||
recycle_z0 = c - a | ||
|
||
niter += 1 | ||
|
||
if callback is not None: | ||
callback(np.copy(x0)) | ||
|
||
if maxfev is not None: | ||
if funcalls >= maxfev: | ||
break | ||
|
||
if maxiter is not None: | ||
if niter >= maxiter: | ||
break | ||
|
||
return OptimizeResult(fun=fun(np.copy(x0)), x=x0, nit=niter, nfev=funcalls, success=(niter > 1)) |