• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

qiskit-community / qiskit-algorithms / 16423372178

21 Jul 2025 05:06PM CUT coverage: 90.0% (-0.5%) from 90.462%
16423372178

Pull #197

github

web-flow
Merge 69c7c00f6 into bd91342d0
Pull Request #197: Added V2 and ISA support

704 of 768 new or added lines in 51 files covered. (91.67%)

11 existing lines in 3 files now uncovered.

6561 of 7290 relevant lines covered (90.0%)

0.9 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

92.21
/qiskit_algorithms/amplitude_estimators/estimation_problem.py
1
# This code is part of a Qiskit project.
2
#
3
# (C) Copyright IBM 2020, 2024.
4
#
5
# This code is licensed under the Apache License, Version 2.0. You may
6
# obtain a copy of this license in the LICENSE.txt file in the root directory
7
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
8
#
9
# Any modifications or derivative works of this code must retain this
10
# copyright notice, and modified files need to carry a notice indicating
11
# that they have been altered from the originals.
12

13
"""The Estimation problem class."""
14

15
from __future__ import annotations
1✔
16
import warnings
1✔
17
from collections.abc import Callable
1✔
18

19
import numpy
1✔
20

21
from qiskit.circuit import QuantumCircuit, QuantumRegister
1✔
22
from qiskit.circuit.library import GroverOperator
1✔
23

24

25
class EstimationProblem:
1✔
26
    """The estimation problem is the input to amplitude estimation algorithm.
27

28
    This class contains all problem-specific information required to run an amplitude estimation
29
    algorithm. That means, it minimally contains the state preparation and the specification
30
    of the good state. It can further hold some post processing on the estimation of the amplitude
31
    or a custom Grover operator.
32
    """
33

34
    # pylint: disable=too-many-positional-arguments
35
    def __init__(
1✔
36
        self,
37
        state_preparation: QuantumCircuit,
38
        objective_qubits: int | list[int],
39
        grover_operator: QuantumCircuit | None = None,
40
        post_processing: (
41
            Callable[[list[float]], list[float]] | Callable[[float], float] | None
42
        ) = None,
43
        is_good_state: Callable[[str], bool] | None = None,
44
    ) -> None:
45
        r"""
46
        Args:
47
            state_preparation: A circuit preparing the input state, referred to as
48
                :math:`\mathcal{A}`.
49
            objective_qubits: A single qubit index or a list of qubit indices to specify which
50
                qubits to measure. The ``is_good_state`` function is applied on the bitstring of
51
                these objective qubits.
52
            grover_operator: The Grover operator :math:`\mathcal{Q}` used as unitary in the
53
                phase estimation circuit.
54
            post_processing: A mapping applied to the result of the algorithm
55
                :math:`0 \leq a \leq 1`, usually used to map the estimate to a target interval.
56
                Defaults to the identity.
57
            is_good_state: A function to check whether a string represents a good state. Defaults
58
                to all objective qubits being in state :math:`|1\rangle`.
59
        """
60
        self._state_preparation = state_preparation
1✔
61
        self._objective_qubits = objective_qubits
1✔
62
        self._grover_operator = grover_operator
1✔
63
        self._post_processing = post_processing
1✔
64
        self._is_good_state = is_good_state
1✔
65

66
    @property
1✔
67
    def state_preparation(self) -> QuantumCircuit | None:
1✔
68
        r"""Get the :math:`\mathcal{A}` operator encoding the amplitude :math:`a`.
69

70
        Returns:
71
            The :math:`\mathcal{A}` operator as `QuantumCircuit`.
72
        """
73
        return self._state_preparation
1✔
74

75
    @state_preparation.setter
1✔
76
    def state_preparation(self, state_preparation: QuantumCircuit) -> None:
1✔
77
        r"""Set the :math:`\mathcal{A}` operator, that encodes the amplitude to be estimated.
78

79
        Args:
80
            state_preparation: The new :math:`\mathcal{A}` operator.
81
        """
82
        self._state_preparation = state_preparation
×
83

84
    @property
1✔
85
    def objective_qubits(self) -> list[int]:
1✔
86
        """Get the criterion for a measurement outcome to be in a 'good' state.
87

88
        Returns:
89
            The criterion as list of qubit indices.
90
        """
91
        if isinstance(self._objective_qubits, int):
1✔
UNCOV
92
            return [self._objective_qubits]
×
93

94
        return self._objective_qubits
1✔
95

96
    @objective_qubits.setter
1✔
97
    def objective_qubits(self, objective_qubits: int | list[int]) -> None:
1✔
98
        """Set the criterion for a measurement outcome to be in a 'good' state.
99

100
        Args:
101
            objective_qubits: The criterion as callable of list of qubit indices.
102
        """
103
        self._objective_qubits = objective_qubits
×
104

105
    @property
1✔
106
    def post_processing(self) -> Callable[[list[float]], list[float]] | Callable[[float], float]:
1✔
107
        """Apply post processing to the input value.
108

109
        Returns:
110
            A handle to the post processing function. Acts as identity by default.
111
        """
112
        if self._post_processing is None:
1✔
113
            return lambda x: x
1✔
114

115
        return self._post_processing
1✔
116

117
    @post_processing.setter
1✔
118
    def post_processing(
1✔
119
        self,
120
        post_processing: Callable[[list[float]], list[float]] | Callable[[float], float] | None,
121
    ) -> None:
122
        """Set the post processing function.
123

124
        Args:
125
            post_processing: A handle to the post processing function. If set to ``None``, the
126
                identity will be used as post processing.
127
        """
128
        self._post_processing = post_processing
×
129

130
    @property
1✔
131
    def has_good_state(self) -> bool:
1✔
132
        """Check whether an :attr:`is_good_state` function is set.
133

134
        Some amplitude estimators, such as :class:`.AmplitudeEstimation` do not support
135
        a custom implementation of the :attr:`is_good_state` function, and can only handle
136
        the default.
137

138
        Returns:
139
            ``True``, if a custom :attr:`is_good_state` is set, otherwise returns ``False``.
140
        """
141
        return self._is_good_state is not None
1✔
142

143
    @property
1✔
144
    def is_good_state(self) -> Callable[[str], bool]:
1✔
145
        """Checks whether a bitstring represents a good state.
146

147
        Returns:
148
            Handle to the ``is_good_state`` callable.
149
        """
150
        if self._is_good_state is None:
1✔
151
            return lambda x: all(bit == "1" for bit in x)
1✔
152

153
        return self._is_good_state
1✔
154

155
    @is_good_state.setter
1✔
156
    def is_good_state(self, is_good_state: Callable[[str], bool] | None) -> None:
1✔
157
        """Set the ``is_good_state`` function.
158

159
        Args:
160
            is_good_state: A function to determine whether a bitstring represents a good state.
161
                If set to ``None``, the good state will be defined as all bits being one.
162
        """
163
        self._is_good_state = is_good_state
×
164

165
    @property
1✔
166
    def grover_operator(self) -> QuantumCircuit | None:
1✔
167
        r"""Get the :math:`\mathcal{Q}` operator, or Grover operator.
168

169
        If the Grover operator is not set, we try to build it from the :math:`\mathcal{A}` operator
170
        and `objective_qubits`. This only works if `objective_qubits` is a list of integers.
171

172
        Returns:
173
            The Grover operator, or None if neither the Grover operator nor the
174
            :math:`\mathcal{A}` operator is  set.
175
        """
176
        if self._grover_operator is not None:
1✔
177
            return self._grover_operator
1✔
178

179
        # build the reflection about the bad state: a MCZ with open controls (thus X gates
180
        # around the controls) and X gates around the target to change from a phase flip on
181
        # |1> to a phase flip on |0>
182
        num_state_qubits = self.state_preparation.num_qubits - self.state_preparation.num_ancillas
1✔
183

184
        oracle = QuantumCircuit(num_state_qubits)
1✔
185
        oracle.h(self.objective_qubits[-1])
1✔
186
        if len(self.objective_qubits) == 1:
1✔
187
            oracle.x(self.objective_qubits[0])
1✔
188
        else:
189
            oracle.mcx(self.objective_qubits[:-1], self.objective_qubits[-1])
1✔
190
        oracle.h(self.objective_qubits[-1])
1✔
191

192
        # construct the grover operator
193
        return GroverOperator(oracle, self.state_preparation)
1✔
194

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

199
        Args:
200
            grover_operator: The new :math:`\mathcal{Q}` operator. If set to ``None``,
201
                the default construction via ``qiskit.circuit.library.GroverOperator`` is used.
202
        """
203
        self._grover_operator = grover_operator
×
204

205
    def rescale(self, scaling_factor: float) -> "EstimationProblem":
1✔
206
        """Rescale the good state amplitude in the estimation problem.
207

208
        Args:
209
            scaling_factor: The scaling factor in [0, 1].
210

211
        Returns:
212
            A rescaled estimation problem.
213
        """
214
        if self._grover_operator is not None:
1✔
215
            warnings.warn("Rescaling discards the Grover operator.")
1✔
216

217
        # rescale the amplitude by a factor of 1/4 by adding an auxiliary qubit
218
        rescaled_stateprep = _rescale_amplitudes(self.state_preparation, scaling_factor)
1✔
219
        num_qubits = self.state_preparation.num_qubits
1✔
220
        objective_qubits = self.objective_qubits + [num_qubits]
1✔
221

222
        # add the scaling qubit to the good state qualifier
223
        def is_good_state(bitstr):
1✔
224
            return self.is_good_state(bitstr[1:]) and bitstr[0] == "1"
1✔
225

226
        # rescaled estimation problem
227
        problem = EstimationProblem(
1✔
228
            rescaled_stateprep,
229
            objective_qubits=objective_qubits,
230
            post_processing=self.post_processing,
231
            is_good_state=is_good_state,
232
        )
233

234
        return problem
1✔
235

236

237
def _rescale_amplitudes(circuit: QuantumCircuit, scaling_factor: float) -> QuantumCircuit:
1✔
238
    r"""Uses an auxiliary qubit to scale the amplitude of :math:`|1\rangle` by ``scaling_factor``.
239

240
    Explained in Section 2.1. of [1].
241

242
    For example, for a scaling factor of 0.25 this turns this circuit
243

244
    .. parsed-literal::
245

246
                      ┌───┐
247
        state_0: ─────┤ H ├─────────■────
248
                  ┌───┴───┴───┐ ┌───┴───┐
249
          obj_0: ─┤ RY(0.125) ├─┤ RY(1) ├
250
                  └───────────┘ └───────┘
251

252
    into
253

254
    .. parsed-literal::
255

256
                      ┌───┐
257
        state_0: ─────┤ H ├─────────■────
258
                  ┌───┴───┴───┐ ┌───┴───┐
259
          obj_0: ─┤ RY(0.125) ├─┤ RY(1) ├
260
                 ┌┴───────────┴┐└───────┘
261
      scaling_0: ┤ RY(0.50536) ├─────────
262
                 └─────────────┘
263

264
    References:
265

266
        [1]: K. Nakaji. Faster Amplitude Estimation, 2020;
267
            `arXiv:2002.02417 <https://arxiv.org/pdf/2003.02417.pdf>`_
268

269
    Args:
270
        circuit: The circuit whose amplitudes to rescale.
271
        scaling_factor: The rescaling factor.
272

273
    Returns:
274
        A copy of the circuit with an additional qubit and RY gate for the rescaling.
275
    """
276
    qr = QuantumRegister(1, "scaling")
1✔
277
    rescaled = QuantumCircuit(*circuit.qregs, qr)
1✔
278
    rescaled.compose(circuit, circuit.qubits, inplace=True)
1✔
279
    rescaled.ry(2 * numpy.arcsin(scaling_factor), qr)
1✔
280
    return rescaled
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc