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

qiskit-community / qiskit-algorithms / 10186952664

31 Jul 2024 08:09PM CUT coverage: 90.421% (-0.008%) from 90.429%
10186952664

Pull #195

github

web-flow
Merge 0b5b9e82d into bf5e90399
Pull Request #195: ComputeUncompute circuit transpilation option.

3 of 4 new or added lines in 1 file covered. (75.0%)

6362 of 7036 relevant lines covered (90.42%)

0.9 hits per line

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

93.24
/qiskit_algorithms/state_fidelities/compute_uncompute.py
1
# This code is part of a Qiskit project.
2
#
3
# (C) Copyright IBM 2022, 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
Compute-uncompute fidelity interface using primitives
14
"""
15

16
from __future__ import annotations
1✔
17
from collections.abc import Sequence
1✔
18
from copy import copy
1✔
19

20
from qiskit import QuantumCircuit
1✔
21
from qiskit.primitives import BaseSampler
1✔
22
from qiskit.primitives.primitive_job import PrimitiveJob
1✔
23
from qiskit.providers import Options
1✔
24
from qiskit.transpiler import PassManager
1✔
25

26
from ..exceptions import AlgorithmError
1✔
27
from .base_state_fidelity import BaseStateFidelity
1✔
28
from .state_fidelity_result import StateFidelityResult
1✔
29
from ..algorithm_job import AlgorithmJob
1✔
30

31

32
class ComputeUncompute(BaseStateFidelity):
1✔
33
    r"""
34
    This class leverages the sampler primitive to calculate the state
35
    fidelity of two quantum circuits following the compute-uncompute
36
    method (see [1] for further reference).
37
    The fidelity can be defined as the state overlap.
38

39
    .. math::
40

41
            |\langle\psi(x)|\phi(y)\rangle|^2
42

43
    where :math:`x` and :math:`y` are optional parametrizations of the
44
    states :math:`\psi` and :math:`\phi` prepared by the circuits
45
    ``circuit_1`` and ``circuit_2``, respectively.
46

47
    **Reference:**
48
    [1] Havlíček, V., Córcoles, A. D., Temme, K., Harrow, A. W., Kandala,
49
    A., Chow, J. M., & Gambetta, J. M. (2019). Supervised learning
50
    with quantum-enhanced feature spaces. Nature, 567(7747), 209-212.
51
    `arXiv:1804.11326v2 [quant-ph] <https://arxiv.org/pdf/1804.11326.pdf>`_
52

53
    """
54

55
    def __init__(
1✔
56
        self,
57
        sampler: BaseSampler,
58
        pass_manager: PassManager = None,
59
        options: Options | None = None,
60
        local: bool = False,
61
    ) -> None:
62
        r"""
63
        Args:
64
            sampler: Sampler primitive instance.
65
            pass_manager: Pass manager for the transpilation of the fidelity circuit.
66
            options: Primitive backend runtime options used for circuit execution.
67
                The order of priority is: options in ``run`` method > fidelity's
68
                default options > primitive's default setting.
69
                Higher priority setting overrides lower priority setting.
70
            local: If set to ``True``, the fidelity is averaged over
71
                single-qubit projectors
72

73
                .. math::
74

75
                    \hat{O} = \frac{1}{N}\sum_{i=1}^N|0_i\rangle\langle 0_i|,
76

77
                instead of the global projector :math:`|0\rangle\langle 0|^{\otimes n}`.
78
                This coincides with the standard (global) fidelity in the limit of
79
                the fidelity approaching 1. Might be used to increase the variance
80
                to improve trainability in algorithms such as :class:`~.time_evolvers.PVQD`.
81

82
        Raises:
83
            ValueError: If the sampler is not an instance of ``BaseSampler``.
84
        """
85
        if not isinstance(sampler, BaseSampler):
1✔
86
            raise ValueError(
×
87
                f"The sampler should be an instance of BaseSampler, " f"but got {type(sampler)}"
88
            )
89
        self._sampler: BaseSampler = sampler
1✔
90
        self._pass_manager: PassManager = pass_manager
1✔
91
        self._local = local
1✔
92
        self._default_options = Options()
1✔
93
        if options is not None:
1✔
94
            self._default_options.update_options(**options)
1✔
95
        super().__init__()
1✔
96

97
    def create_fidelity_circuit(
1✔
98
        self, circuit_1: QuantumCircuit, circuit_2: QuantumCircuit
99
    ) -> QuantumCircuit:
100
        """
101
        Combines ``circuit_1`` and ``circuit_2`` to create the
102
        fidelity circuit following the compute-uncompute method.
103
        Optionally, the circuit is transpiled using pass_manager parameter.
104

105
        Args:
106
            circuit_1: (Parametrized) quantum circuit.
107
            circuit_2: (Parametrized) quantum circuit.
108

109
        Returns:
110
            The fidelity quantum circuit corresponding to circuit_1 and circuit_2.
111
        """
112
        if len(circuit_1.clbits) > 0:
1✔
113
            circuit_1.remove_final_measurements()
1✔
114
        if len(circuit_2.clbits) > 0:
1✔
115
            circuit_2.remove_final_measurements()
1✔
116

117
        circuit = circuit_1.compose(circuit_2.inverse())
1✔
118
        circuit.measure_all()
1✔
119
        if self._pass_manager:
1✔
NEW
120
            return self._pass_manager.run(circuit)
×
121
        return circuit
1✔
122

123
    def _run(
1✔
124
        self,
125
        circuits_1: QuantumCircuit | Sequence[QuantumCircuit],
126
        circuits_2: QuantumCircuit | Sequence[QuantumCircuit],
127
        values_1: Sequence[float] | Sequence[Sequence[float]] | None = None,
128
        values_2: Sequence[float] | Sequence[Sequence[float]] | None = None,
129
        **options,
130
    ) -> AlgorithmJob:
131
        r"""
132
        Computes the state overlap (fidelity) calculation between two
133
        (parametrized) circuits (first and second) for a specific set of parameter
134
        values (first and second) following the compute-uncompute method.
135

136
        Args:
137
            circuits_1: (Parametrized) quantum circuits preparing :math:`|\psi\rangle`.
138
            circuits_2: (Parametrized) quantum circuits preparing :math:`|\phi\rangle`.
139
            values_1: Numerical parameters to be bound to the first circuits.
140
            values_2: Numerical parameters to be bound to the second circuits.
141
            options: Primitive backend runtime options used for circuit execution.
142
                    The order of priority is: options in ``run`` method > fidelity's
143
                    default options > primitive's default setting.
144
                    Higher priority setting overrides lower priority setting.
145

146
        Returns:
147
            An AlgorithmJob for the fidelity calculation.
148

149
        Raises:
150
            ValueError: At least one pair of circuits must be defined.
151
            AlgorithmError: If the sampler job is not completed successfully.
152
        """
153

154
        circuits = self._construct_circuits(circuits_1, circuits_2)
1✔
155
        if len(circuits) == 0:
1✔
156
            raise ValueError(
×
157
                "At least one pair of circuits must be defined to calculate the state overlap."
158
            )
159
        values = self._construct_value_list(circuits_1, circuits_2, values_1, values_2)
1✔
160

161
        # The priority of run options is as follows:
162
        # options in `evaluate` method > fidelity's default options >
163
        # primitive's default options.
164
        opts = copy(self._default_options)
1✔
165
        opts.update_options(**options)
1✔
166

167
        sampler_job = self._sampler.run(circuits=circuits, parameter_values=values, **opts.__dict__)
1✔
168

169
        local_opts = self._get_local_options(opts.__dict__)
1✔
170
        return AlgorithmJob(ComputeUncompute._call, sampler_job, circuits, self._local, local_opts)
1✔
171

172
    @staticmethod
1✔
173
    def _call(
1✔
174
        job: PrimitiveJob, circuits: Sequence[QuantumCircuit], local: bool, local_opts: Options
175
    ) -> StateFidelityResult:
176
        try:
1✔
177
            result = job.result()
1✔
178
        except Exception as exc:
×
179
            raise AlgorithmError("Sampler job failed!") from exc
×
180

181
        if local:
1✔
182
            raw_fidelities = [
1✔
183
                ComputeUncompute._get_local_fidelity(prob_dist, circuit.num_qubits)
184
                for prob_dist, circuit in zip(result.quasi_dists, circuits)
185
            ]
186
        else:
187
            raw_fidelities = [
1✔
188
                ComputeUncompute._get_global_fidelity(prob_dist) for prob_dist in result.quasi_dists
189
            ]
190
        fidelities = ComputeUncompute._truncate_fidelities(raw_fidelities)
1✔
191

192
        return StateFidelityResult(
1✔
193
            fidelities=fidelities,
194
            raw_fidelities=raw_fidelities,
195
            metadata=result.metadata,
196
            options=local_opts,
197
        )
198

199
    @property
1✔
200
    def options(self) -> Options:
1✔
201
        """Return the union of estimator options setting and fidelity default options,
202
        where, if the same field is set in both, the fidelity's default options override
203
        the primitive's default setting.
204

205
        Returns:
206
            The fidelity default + estimator options.
207
        """
208
        return self._get_local_options(self._default_options.__dict__)
1✔
209

210
    def update_default_options(self, **options):
1✔
211
        """Update the fidelity's default options setting.
212

213
        Args:
214
            **options: The fields to update the default options.
215
        """
216

217
        self._default_options.update_options(**options)
1✔
218

219
    def _get_local_options(self, options: Options) -> Options:
1✔
220
        """Return the union of the primitive's default setting,
221
        the fidelity default options, and the options in the ``run`` method.
222
        The order of priority is: options in ``run`` method > fidelity's
223
                default options > primitive's default setting.
224

225
        Args:
226
            options: The fields to update the options
227

228
        Returns:
229
            The fidelity default + estimator + run options.
230
        """
231
        opts = copy(self._sampler.options)
1✔
232
        opts.update_options(**options)
1✔
233
        return opts
1✔
234

235
    @staticmethod
1✔
236
    def _get_global_fidelity(probability_distribution: dict[int, float]) -> float:
1✔
237
        """Process the probability distribution of a measurement to determine the
238
        global fidelity.
239

240
        Args:
241
            probability_distribution: Obtained from the measurement result
242

243
        Returns:
244
            The global fidelity.
245
        """
246
        return probability_distribution.get(0, 0)
1✔
247

248
    @staticmethod
1✔
249
    def _get_local_fidelity(probability_distribution: dict[int, float], num_qubits: int) -> float:
1✔
250
        """Process the probability distribution of a measurement to determine the
251
        local fidelity by averaging over single-qubit projectors.
252

253
        Args:
254
            probability_distribution: Obtained from the measurement result
255

256
        Returns:
257
            The local fidelity.
258
        """
259
        fidelity = 0.0
1✔
260
        for qubit in range(num_qubits):
1✔
261
            for bitstring, prob in probability_distribution.items():
1✔
262
                # Check whether the bit representing the current qubit is 0
263
                if not bitstring >> qubit & 1:
1✔
264
                    fidelity += prob / num_qubits
1✔
265
        return fidelity
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