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

Allow Custom Lambda for Iterative Grover, see #893 #1005

Merged
merged 8 commits into from
May 21, 2020
1 change: 1 addition & 0 deletions .pylintdict
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ babbush
backend
backends
barenco
Baritompa
barkoutsos
basebackend
baseoperator
Expand Down
50 changes: 38 additions & 12 deletions qiskit/aqua/algorithms/amplitude_amplifiers/grover.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,11 @@ def __init__(self,
oracle: Oracle, init_state: Optional[InitialState] = None,
incremental: bool = False,
num_iterations: int = 1,
lam: float = 1.34,
rotation_counts: Optional[list] = None,
mct_mode: str = 'basic',
quantum_instance: Optional[Union[QuantumInstance, BaseBackend]] = None) -> None:
# pylint: disable=line-too-long
r"""
Args:
oracle: The oracle component
Expand All @@ -96,8 +99,14 @@ def __init__(self,
higher number of iterations for the repetition of the amplitude amplification
until a target is found or the maximal number :math:`\log N` (:math:`N` being the
total number of elements in the set from the oracle used) of iterations is
reached. The implementation follows Section 4 of Boyer et al.
<https://arxiv.org/abs/quant-ph/9605034>
reached. The implementation follows Section 4 of [2].
lam: For incremental search mode, the maximum number of repetition of amplitude
amplification increases by factor lam in every round,
:math:`R_{i+1} = lam \times R_{i}`. If this parameter is not set, the default
value lam = 1.34 is used, which is proved to be optimal [1].
rotation_counts: For incremental mode, if rotation_counts is defined, parameter *lam*
is ignored. rotation_counts is the list of integers that defines the number of
repetition of amplitude amplification for each round.
num_iterations: How many times the marking and reflection phase sub-circuit is
repeated to amplify the amplitude(s) of the target(s). Has a minimum value of 1.
mct_mode: Multi-Control Toffoli mode ('basic' | 'basic-dirty-ancilla' |
Expand All @@ -106,6 +115,12 @@ def __init__(self,

Raises:
AquaError: evaluate_classically() missing from the input oracle

References:
[1]: Baritompa et al., Grover's Quantum Algorithm Applied to Global Optimization
`<https://www.researchgate.net/publication/220133694_Grover%27s_Quantum_Algorithm_Applied_to_Global_Optimization>`_
[2]: Boyer et al., Tight bounds on quantum searching
`<https://arxiv.org/abs/quant-ph/9605034>`_
"""
validate_min('num_iterations', num_iterations, 1)
validate_in_set('mct_mode', mct_mode,
Expand All @@ -129,6 +144,8 @@ def __init__(self,
self._diffusion_circuit = self._construct_diffusion_circuit()
self._max_num_iterations = np.ceil(2 ** (len(oracle.variable_register) / 2))
self._incremental = incremental
self._lam = lam
self._rotation_counts = rotation_counts
self._num_iterations = num_iterations if not incremental else 1
if incremental:
logger.debug('Incremental mode specified, ignoring "num_iterations".')
Expand Down Expand Up @@ -248,21 +265,30 @@ def construct_circuit(self, measurement=False):

def _run(self):
if self._incremental:
current_max_num_iterations, lam = 1, 6 / 5

def _try_current_max_num_iterations():
target_num_iterations = self.random.randint(current_max_num_iterations) + 1
def _try_target_num_iterations():
self._qc_amplitude_amplification = QuantumCircuit()
for _ in range(target_num_iterations):
for _ in range(int(target_num_iterations)):
self._qc_amplitude_amplification += self.qc_amplitude_amplification_iteration
return self._run_with_existing_iterations()

while current_max_num_iterations < self._max_num_iterations:
assignment, oracle_evaluation = _try_current_max_num_iterations()
if oracle_evaluation:
break
current_max_num_iterations = \
min(lam * current_max_num_iterations, self._max_num_iterations)
if self._rotation_counts:
for target_num_iterations in self._rotation_counts:
assignment, oracle_evaluation = _try_target_num_iterations()
if oracle_evaluation:
break
if target_num_iterations > self._max_num_iterations:
break
else:
current_max_num_iterations = 1
while current_max_num_iterations < self._max_num_iterations:
target_num_iterations = self.random.randint(current_max_num_iterations) + 1
assignment, oracle_evaluation = _try_target_num_iterations()
if oracle_evaluation:
break
current_max_num_iterations = \
min(self._lam * current_max_num_iterations, self._max_num_iterations)

else:
self._qc_amplitude_amplification = QuantumCircuit()
for _ in range(self._num_iterations):
Expand Down
15 changes: 11 additions & 4 deletions test/aqua/test_grover.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,21 +40,28 @@
MCT_MODES = ['basic', 'basic-dirty-ancilla', 'advanced', 'noancilla']
SIMULATORS = ['statevector_simulator', 'qasm_simulator']
OPTIMIZATIONS = [True, False]
ROTATION_COUNTS = [
None,
[0, 0, 0, 1, 1, 0, 1, 1, 2, 1, 2, 3, 1, 4, 5, 1, 6, 2, 7, 9,
11, 13, 16, 5, 20, 24, 28, 34, 2, 41, 49, 4, 60]
]


@ddt
class TestGrover(QiskitAquaTestCase):
""" Grover test """
@idata(
[x[0] + list(x[1:]) for x in list(itertools.product(TESTS, MCT_MODES,
SIMULATORS, OPTIMIZATIONS))]
[x[0] + list(x[1:]) for x in list(itertools.product(TESTS, MCT_MODES, SIMULATORS,
OPTIMIZATIONS, ROTATION_COUNTS))]
)
@unpack
def test_grover(self, input_test, sol, oracle_cls, mct_mode, simulator, optimization):
def test_grover(self, input_test, sol, oracle_cls, mct_mode,
simulator, optimization, rotation_counts):
""" grover test """
groundtruth = sol
oracle = oracle_cls(input_test, optimization=optimization)
grover = Grover(oracle, incremental=True, mct_mode=mct_mode)
grover = Grover(oracle, incremental=True,
rotation_counts=rotation_counts, mct_mode=mct_mode)
backend = BasicAer.get_backend(simulator)
quantum_instance = QuantumInstance(backend, shots=1000)

Expand Down