Skip to content

Commit 2ae2924

Browse files
ken-nakanishimanoelmarqueswoodsp-ibm
authored
* add NFT optimizer * Add specific unit test for NFT Co-authored-by: Manoel Marques <[email protected]> Co-authored-by: Steve Wood <[email protected]> Co-authored-by: woodsp <[email protected]>
1 parent b0b04c2 commit 2ae2924

File tree

3 files changed

+237
-0
lines changed

3 files changed

+237
-0
lines changed

qiskit/aqua/components/optimizers/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
COBYLA
5252
L_BFGS_B
5353
NELDER_MEAD
54+
NFT
5455
P_BFGS
5556
POWELL
5657
SLSQP
@@ -93,6 +94,7 @@
9394
from .spsa import SPSA
9495
from .tnc import TNC
9596
from .aqgd import AQGD
97+
from .nft import NFT
9698
from .nlopts.crs import CRS
9799
from .nlopts.direct_l import DIRECT_L
98100
from .nlopts.direct_l_rand import DIRECT_L_RAND
@@ -106,6 +108,7 @@
106108
'COBYLA',
107109
'L_BFGS_B',
108110
'NELDER_MEAD',
111+
'NFT',
109112
'P_BFGS',
110113
'POWELL',
111114
'SLSQP',
Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
# -*- coding: utf-8 -*-
2+
3+
# This code is part of Qiskit.
4+
#
5+
# (C) Copyright IBM 2019, 2020.
6+
#
7+
# This code is licensed under the Apache License, Version 2.0. You may
8+
# obtain a copy of this license in the LICENSE.txt file in the root directory
9+
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
10+
#
11+
# Any modifications or derivative works of this code must retain this
12+
# copyright notice, and modified files need to carry a notice indicating
13+
# that they have been altered from the originals.
14+
15+
"""Nakanishi-Fujii-Todo algorithm."""
16+
17+
from typing import Optional
18+
import logging
19+
20+
import numpy as np
21+
from scipy.optimize import minimize
22+
from scipy.optimize import OptimizeResult
23+
from .optimizer import Optimizer
24+
25+
26+
logger = logging.getLogger(__name__)
27+
28+
29+
class NFT(Optimizer):
30+
"""
31+
Nakanishi-Fujii-Todo algorithm.
32+
33+
See https://arxiv.org/abs/1903.12166
34+
"""
35+
36+
_OPTIONS = ['maxiter', 'maxfev', 'disp', 'reset_interval']
37+
38+
# pylint: disable=unused-argument
39+
def __init__(self,
40+
maxiter: Optional[int] = None,
41+
maxfev: int = 1024,
42+
disp: bool = False,
43+
reset_interval: int = 32) -> None:
44+
"""
45+
Built out using scipy framework, for details, please refer to
46+
https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.minimize.html.
47+
48+
Args:
49+
maxiter: Maximum number of iterations to perform.
50+
maxfev: Maximum number of function evaluations to perform.
51+
disp: disp
52+
reset_interval: The minimum estimates directly once
53+
in ``reset_interval`` times.
54+
55+
Notes:
56+
In this optimization method, the optimization function have to satisfy
57+
three conditions written in [1]_.
58+
59+
References:
60+
.. [1] K. M. Nakanishi, K. Fujii, and S. Todo. 2019.
61+
Sequential minimal optimization for quantum-classical hybrid algorithms.
62+
arXiv preprint arXiv:1903.12166.
63+
"""
64+
super().__init__()
65+
for k, v in locals().items():
66+
if k in self._OPTIONS:
67+
self._options[k] = v
68+
69+
def get_support_level(self):
70+
""" return support level dictionary """
71+
return {
72+
'gradient': Optimizer.SupportLevel.ignored,
73+
'bounds': Optimizer.SupportLevel.ignored,
74+
'initial_point': Optimizer.SupportLevel.required
75+
}
76+
77+
def optimize(self, num_vars, objective_function, gradient_function=None,
78+
variable_bounds=None, initial_point=None):
79+
super().optimize(num_vars, objective_function, gradient_function,
80+
variable_bounds, initial_point)
81+
82+
res = minimize(objective_function, initial_point,
83+
method=nakanishi_fujii_todo, options=self._options)
84+
return res.x, res.fun, res.nfev
85+
86+
87+
# pylint: disable=invalid-name
88+
def nakanishi_fujii_todo(fun, x0, args=(), maxiter=None, maxfev=1024,
89+
reset_interval=32, eps=1e-32, callback=None, **_):
90+
"""
91+
Find the global minimum of a function using the nakanishi_fujii_todo
92+
algorithm [1].
93+
Args:
94+
fun (callable): ``f(x, *args)``
95+
Function to be optimized. ``args`` can be passed as an optional item
96+
in the dict ``minimizer_kwargs``.
97+
This function must satisfy the three condition written in Ref. [1].
98+
x0 (ndarray): shape (n,)
99+
Initial guess. Array of real elements of size (n,),
100+
where 'n' is the number of independent variables.
101+
args (tuple, optional):
102+
Extra arguments passed to the objective function.
103+
maxiter (int):
104+
Maximum number of iterations to perform.
105+
Default: None.
106+
maxfev (int):
107+
Maximum number of function evaluations to perform.
108+
Default: 1024.
109+
reset_interval (int):
110+
The minimum estimates directly once in ``reset_interval`` times.
111+
Default: 32.
112+
eps (float): eps
113+
**_ : additional options
114+
callback (callable, optional):
115+
Called after each iteration.
116+
Returns:
117+
OptimizeResult:
118+
The optimization result represented as a ``OptimizeResult`` object.
119+
Important attributes are: ``x`` the solution array. See
120+
`OptimizeResult` for a description of other attributes.
121+
Notes:
122+
In this optimization method, the optimization function have to satisfy
123+
three conditions written in [1].
124+
References:
125+
.. [1] K. M. Nakanishi, K. Fujii, and S. Todo. 2019.
126+
Sequential minimal optimization for quantum-classical hybrid algorithms.
127+
arXiv preprint arXiv:1903.12166.
128+
"""
129+
130+
x0 = np.asarray(x0)
131+
recycle_z0 = None
132+
niter = 0
133+
funcalls = 0
134+
135+
while True:
136+
137+
idx = niter % x0.size
138+
139+
if reset_interval > 0:
140+
if niter % reset_interval == 0:
141+
recycle_z0 = None
142+
143+
if recycle_z0 is None:
144+
z0 = fun(np.copy(x0), *args)
145+
funcalls += 1
146+
else:
147+
z0 = recycle_z0
148+
149+
p = np.copy(x0)
150+
p[idx] = x0[idx] + np.pi / 2
151+
z1 = fun(p, *args)
152+
funcalls += 1
153+
154+
p = np.copy(x0)
155+
p[idx] = x0[idx] - np.pi / 2
156+
z3 = fun(p, *args)
157+
funcalls += 1
158+
159+
z2 = z1 + z3 - z0
160+
c = (z1 + z3) / 2
161+
a = np.sqrt((z0 - z2) ** 2 + (z1 - z3) ** 2) / 2
162+
b = np.arctan((z1 - z3) / ((z0 - z2) + eps * (z0 == z2))) + x0[idx]
163+
b += 0.5 * np.pi + 0.5 * np.pi * np.sign((z0 - z2) + eps * (z0 == z2))
164+
165+
x0[idx] = b
166+
recycle_z0 = c - a
167+
168+
niter += 1
169+
170+
if callback is not None:
171+
callback(np.copy(x0))
172+
173+
if maxfev is not None:
174+
if funcalls >= maxfev:
175+
break
176+
177+
if maxiter is not None:
178+
if niter >= maxiter:
179+
break
180+
181+
return OptimizeResult(fun=fun(np.copy(x0)), x=x0, nit=niter, nfev=funcalls, success=(niter > 1))

test/aqua/test_optimizer_nft.py

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
# -*- coding: utf-8 -*-
2+
3+
# This code is part of Qiskit.
4+
#
5+
# (C) Copyright IBM 2020
6+
#
7+
# This code is licensed under the Apache License, Version 2.0. You may
8+
# obtain a copy of this license in the LICENSE.txt file in the root directory
9+
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
10+
#
11+
# Any modifications or derivative works of this code must retain this
12+
# copyright notice, and modified files need to carry a notice indicating
13+
# that they have been altered from the originals.
14+
15+
""" Test of NFT optimizer """
16+
17+
from test.aqua import QiskitAquaTestCase
18+
from qiskit import BasicAer
19+
20+
from qiskit.aqua import QuantumInstance, aqua_globals
21+
from qiskit.aqua.operators import WeightedPauliOperator
22+
from qiskit.aqua.components.variational_forms import RY
23+
from qiskit.aqua.components.optimizers import NFT
24+
from qiskit.aqua.algorithms import VQE
25+
26+
27+
class TestOptimizerNFT(QiskitAquaTestCase):
28+
""" Test NFT optimizer using RY with VQE """
29+
30+
def setUp(self):
31+
super().setUp()
32+
self.seed = 50
33+
aqua_globals.random_seed = self.seed
34+
pauli_dict = {
35+
'paulis': [{"coeff": {"imag": 0.0, "real": -1.052373245772859}, "label": "II"},
36+
{"coeff": {"imag": 0.0, "real": 0.39793742484318045}, "label": "IZ"},
37+
{"coeff": {"imag": 0.0, "real": -0.39793742484318045}, "label": "ZI"},
38+
{"coeff": {"imag": 0.0, "real": -0.01128010425623538}, "label": "ZZ"},
39+
{"coeff": {"imag": 0.0, "real": 0.18093119978423156}, "label": "XX"}
40+
]
41+
}
42+
self.qubit_op = WeightedPauliOperator.from_dict(pauli_dict)
43+
44+
def test_nft(self):
45+
""" Test NFT optimizer by using it """
46+
47+
result = VQE(self.qubit_op,
48+
RY(self.qubit_op.num_qubits),
49+
NFT()).run(
50+
QuantumInstance(BasicAer.get_backend('statevector_simulator'),
51+
seed_simulator=aqua_globals.random_seed,
52+
seed_transpiler=aqua_globals.random_seed))
53+
self.assertAlmostEqual(result.eigenvalue.real, -1.857275, places=6)

0 commit comments

Comments
 (0)