Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
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
108 changes: 43 additions & 65 deletions docs/tutorials/06_grover.ipynb

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
from typing import Any, List, cast

from qiskit.circuit import QuantumCircuit, Gate
from qiskit.circuit.library import GroverOperator
from qiskit.circuit.library import grover_operator as grover_operator_builder
from qiskit.quantum_info import Statevector


Expand Down Expand Up @@ -205,10 +205,10 @@ def grover_operator(self) -> QuantumCircuit | None:

Returns:
The Grover operator, or None if neither the Grover operator nor the
:math:`\mathcal{A}` operator is set.
:math:`\mathcal{A}` operator is set.
"""
if self._grover_operator is None:
return GroverOperator(self.oracle, self.state_preparation)
return grover_operator_builder(self.oracle, self.state_preparation)
return self._grover_operator

@grover_operator.setter
Expand Down
6 changes: 2 additions & 4 deletions qiskit_algorithms/amplitude_amplifiers/grover.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ class Grover(AmplitudeAmplifier):

\mathcal{Q} = \mathcal{A} \mathcal{S}_0 \mathcal{A}^\dagger \mathcal{S}_f.

For more information, see the :class:`~qiskit.circuit.library.GroverOperator` in the
For more information, see the :func:`~qiskit.circuit.library.grover_operator` function in the
circuit library.

References:
Expand Down Expand Up @@ -217,9 +217,7 @@ def amplify(self, amplification_problem: AmplificationProblem) -> "GroverResult"
iterator: Iterator[int] = iter(self._iterations)
else:
max_iterations = max(10, 2**amplification_problem.oracle.num_qubits)
max_power = np.ceil(
2 ** (len(amplification_problem.grover_operator.reflection_qubits) / 2)
)
max_power = np.ceil(2 ** (len(amplification_problem.objective_qubits) / 2))
iterator = self._iterations

result = GroverResult()
Expand Down
12 changes: 7 additions & 5 deletions qiskit_algorithms/amplitude_estimators/estimation_problem.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# This code is part of a Qiskit project.
#
# (C) Copyright IBM 2020, 2024.
# (C) Copyright IBM 2020, 2025.
#
# 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
Expand All @@ -19,7 +19,7 @@
import numpy

from qiskit.circuit import QuantumCircuit, QuantumRegister
from qiskit.circuit.library import GroverOperator
from qiskit.circuit.library import grover_operator as grover_operator_builder


class EstimationProblem:
Expand Down Expand Up @@ -167,7 +167,8 @@ def grover_operator(self) -> QuantumCircuit | None:
r"""Get the :math:`\mathcal{Q}` operator, or Grover operator.

If the Grover operator is not set, we try to build it from the :math:`\mathcal{A}` operator
and `objective_qubits`. This only works if `objective_qubits` is a list of integers.
and `objective_qubits` using the :func:`~qiskit.circuit.library.grover_operator` function.
This only works if `objective_qubits` is a list of integers.

Returns:
The Grover operator, or None if neither the Grover operator nor the
Expand All @@ -190,15 +191,16 @@ def grover_operator(self) -> QuantumCircuit | None:
oracle.h(self.objective_qubits[-1])

# construct the grover operator
return GroverOperator(oracle, self.state_preparation)
return grover_operator_builder(oracle, self.state_preparation)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could use qiskit.__version__ to check whether to return GroverOperator or grover_operator here. This should also be documented then

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In order not to introduce a breaking change then, is it OK if the function uses GroverOperator if the Qiskit version is 2.1.2 or older, and otherwise uses grover_operator? That way, it would prevent a breaking change from users upgrading qiskit-algorithms without upgrading qiskit.

This of course assume that the next qiskit-algorithms release is done before Qiskit 2.2.0 is out! Or we could test for < 3.0, since this is when GroverOperator is removed if I'm not mistaken. What are your views on this?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good questions -- my preference would be to use the grover_operator function whenever possible (i.e. qiskit>=1.3) since it's more efficient than the GroverOperator. But maybe it's safest to just sell this as bugfix and start using grover_operator with qiskit>=2.1 since the GroverOperator was deprecated in 2.1.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I went for the latter and thus updated the release note to categorize this as a bug fix. Does that work for you?


@grover_operator.setter
def grover_operator(self, grover_operator: QuantumCircuit | None) -> None:
r"""Set the :math:`\mathcal{Q}` operator.

Args:
grover_operator: The new :math:`\mathcal{Q}` operator. If set to ``None``,
the default construction via ``qiskit.circuit.library.GroverOperator`` is used.
the default construction via :func:`~qiskit.circuit.library.grover_operator` is
used when accessing the property.
"""
self._grover_operator = grover_operator

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
upgrade:
- |
:class:`AmplificationProblem` and :class:`EstimationProblem` now use the function
:func:`~qiskit.circuit.library.grover_operator` to construct the Grover operator
by default, instead of the class :class:`~qiskit.circuit.library.GroverOperator`,
to follow with the latter's deprecation in Qiskit 2.1. In particular, if these classes
are instantiated without providing a Grover operator, the new default construction
is used, and it won't be possible to access the attributes of the deprecated class.
24 changes: 13 additions & 11 deletions test/test_grover.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@
from test import QiskitAlgorithmsTestCase

import numpy as np
from ddt import data, ddt
from ddt import data, ddt, idata, unpack
from qiskit import QuantumCircuit
from qiskit.circuit.library import GroverOperator, PhaseOracle, PhaseOracleGate
from qiskit.circuit.library import grover_operator, GroverOperator, PhaseOracle, PhaseOracleGate
from qiskit.primitives import StatevectorSampler
from qiskit.quantum_info import Operator, Statevector
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
Expand All @@ -36,24 +36,24 @@ def setUp(self):
super().setUp()
oracle = QuantumCircuit(2)
oracle.cz(0, 1)
self._expected_grover_op = GroverOperator(oracle=oracle)

@data("oracle_only", "oracle_and_stateprep")
def test_groverop_getter(self, kind):
@idata(product(["oracle_only", "oracle_and_stateprep"], [GroverOperator, grover_operator]))
@unpack
def test_groverop_getter(self, kind, oracle_builder):
"""Test the default construction of the Grover operator."""
oracle = QuantumCircuit(2)
oracle.cz(0, 1)

if kind == "oracle_only":
problem = AmplificationProblem(oracle, is_good_state=["11"])
expected = GroverOperator(oracle)
expected = oracle_builder(oracle)
else:
stateprep = QuantumCircuit(2)
stateprep.ry(0.2, [0, 1])
problem = AmplificationProblem(
oracle, state_preparation=stateprep, is_good_state=["11"]
)
expected = GroverOperator(oracle, stateprep)
expected = oracle_builder(oracle, stateprep)

self.assertEqual(Operator(expected), Operator(problem.grover_operator))

Expand Down Expand Up @@ -204,11 +204,12 @@ def test_run_state_vector_oracle(self):
result = grover.amplify(problem)
self.assertIn(result.top_measurement, ["11"])

def test_run_custom_grover_operator(self):
@data(GroverOperator, grover_operator)
def test_run_custom_grover_operator(self, oracle_builder):
"""Test execution with a grover operator oracle"""
oracle = QuantumCircuit(2)
oracle.cz(0, 1)
grover_op = GroverOperator(oracle)
grover_op = oracle_builder(oracle)
problem = AmplificationProblem(
oracle=oracle, grover_operator=grover_op, is_good_state=["11"]
)
Expand All @@ -225,15 +226,16 @@ def test_optimal_num_iterations(self):
actual = Grover.optimal_num_iterations(num_solutions, num_qubits)
self.assertEqual(actual, expected)

def test_construct_circuit(self):
@data(GroverOperator, grover_operator)
def test_construct_circuit(self, oracle_builder):
"""Test construct_circuit"""
oracle = QuantumCircuit(2)
oracle.cz(0, 1)
problem = AmplificationProblem(oracle, is_good_state=["11"])
grover = Grover()
constructed = grover.construct_circuit(problem, 2, measurement=False)

grover_op = GroverOperator(oracle)
grover_op = oracle_builder(oracle)
expected = QuantumCircuit(2)
expected.h([0, 1])
expected.compose(grover_op.power(2), inplace=True)
Expand Down
Loading