Skip to content
This repository has been archived by the owner on Dec 7, 2021. It is now read-only.

Add NFT optimizer #729

Merged
merged 27 commits into from
Mar 25, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
95d62b5
add NFT optimizer
ken-nakanishi Nov 20, 2019
669c568
update docstring
ken-nakanishi Nov 20, 2019
3a7dbfa
fix lint
manoelmarques Nov 20, 2019
35271fe
Merge branch 'master' into add_nft
manoelmarques Nov 20, 2019
9dda0cb
Merge branch 'master' into add_nft
manoelmarques Nov 21, 2019
4edc200
Merge branch 'master' into add_nft
manoelmarques Nov 24, 2019
1216fa5
Merge branch 'master' into add_nft
manoelmarques Dec 17, 2019
ace71bc
Merge branch 'master' into add_nft
manoelmarques Dec 21, 2019
941bdfa
Merge branch 'master' into add_nft
manoelmarques Dec 27, 2019
a7943f8
fix lint
manoelmarques Dec 27, 2019
075ab1b
Merge branch 'master' into add_nft
manoelmarques Jan 3, 2020
3a3c0c5
Merge branch 'master' into add_nft
manoelmarques Jan 6, 2020
76a5f82
Merge branch 'master' into add_nft
manoelmarques Jan 7, 2020
b9fec3a
remove json schema
manoelmarques Jan 8, 2020
63d68f8
fix conflicts
manoelmarques Jan 9, 2020
ab6eefe
Fix docstring
manoelmarques Jan 10, 2020
1ff6d80
Merge branch 'master' into add_nft
manoelmarques Jan 11, 2020
148ce62
Merge branch 'master' into add_nft
manoelmarques Jan 14, 2020
e97c53f
Merge branch 'master' into add_nft
manoelmarques Jan 16, 2020
75b8568
Merge branch 'master' into add_nft
manoelmarques Jan 19, 2020
c93a8d5
merge and flix conflict
manoelmarques Feb 1, 2020
2133e9b
fix copyright year
manoelmarques Feb 1, 2020
5574ae2
Merge branch 'master' into add_nft
manoelmarques Feb 2, 2020
743462b
Merge branch 'master' into add_nft
woodsp-ibm Mar 25, 2020
5419e5d
Add specific unit test for NFT
woodsp-ibm Mar 25, 2020
068c116
Spelling and changelog
woodsp-ibm Mar 25, 2020
c23dba4
Spelling and changelog
woodsp-ibm Mar 25, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .pylintdict
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,7 @@ msq
multiclass
multinomial
multiprocess
Nakanishi
namelist
nan
narray
Expand All @@ -337,6 +338,7 @@ neq
nevals
newtons's
nfev
nft
nk
nlopt
nmo
Expand Down Expand Up @@ -521,6 +523,7 @@ timelimit
timestamp
tnc
toctree
Todo
toffoli
tol
tomo
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ Changelog](http://keepachangelog.com/en/1.0.0/).
Added
-----

- NFT optimizer, part of a project of Qiskit Camp Asia 2019 (#729)
- Algorithm interface and result classes (#849)
- Chemistry FCIDump file driver (#859)
- Chemistry stack automatic Z2 symmetry reduction (#870)
Expand Down
3 changes: 3 additions & 0 deletions qiskit/aqua/components/optimizers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
COBYLA
L_BFGS_B
NELDER_MEAD
NFT
P_BFGS
POWELL
SLSQP
Expand Down Expand Up @@ -93,6 +94,7 @@
from .spsa import SPSA
from .tnc import TNC
from .aqgd import AQGD
from .nft import NFT
from .nlopts.crs import CRS
from .nlopts.direct_l import DIRECT_L
from .nlopts.direct_l_rand import DIRECT_L_RAND
Expand All @@ -106,6 +108,7 @@
'COBYLA',
'L_BFGS_B',
'NELDER_MEAD',
'NFT',
'P_BFGS',
'POWELL',
'SLSQP',
Expand Down
181 changes: 181 additions & 0 deletions qiskit/aqua/components/optimizers/nft.py
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))
53 changes: 53 additions & 0 deletions test/aqua/test_optimizer_nft.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# -*- coding: utf-8 -*-

# This code is part of Qiskit.
#
# (C) Copyright IBM 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.

""" Test of NFT optimizer """

from test.aqua import QiskitAquaTestCase
from qiskit import BasicAer

from qiskit.aqua import QuantumInstance, aqua_globals
from qiskit.aqua.operators import WeightedPauliOperator
from qiskit.aqua.components.variational_forms import RY
from qiskit.aqua.components.optimizers import NFT
from qiskit.aqua.algorithms import VQE


class TestOptimizerNFT(QiskitAquaTestCase):
""" Test NFT optimizer using RY with VQE """

def setUp(self):
super().setUp()
self.seed = 50
aqua_globals.random_seed = self.seed
pauli_dict = {
'paulis': [{"coeff": {"imag": 0.0, "real": -1.052373245772859}, "label": "II"},
{"coeff": {"imag": 0.0, "real": 0.39793742484318045}, "label": "IZ"},
{"coeff": {"imag": 0.0, "real": -0.39793742484318045}, "label": "ZI"},
{"coeff": {"imag": 0.0, "real": -0.01128010425623538}, "label": "ZZ"},
{"coeff": {"imag": 0.0, "real": 0.18093119978423156}, "label": "XX"}
]
}
self.qubit_op = WeightedPauliOperator.from_dict(pauli_dict)

def test_nft(self):
""" Test NFT optimizer by using it """

result = VQE(self.qubit_op,
RY(self.qubit_op.num_qubits),
NFT()).run(
QuantumInstance(BasicAer.get_backend('statevector_simulator'),
seed_simulator=aqua_globals.random_seed,
seed_transpiler=aqua_globals.random_seed))
self.assertAlmostEqual(result.eigenvalue.real, -1.857275, places=6)