qiskit-aer 0.17.2__cp314-cp314-win_amd64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- qiskit_aer/VERSION.txt +1 -0
- qiskit_aer/__init__.py +89 -0
- qiskit_aer/aererror.py +30 -0
- qiskit_aer/aerprovider.py +119 -0
- qiskit_aer/backends/__init__.py +20 -0
- qiskit_aer/backends/aer_compiler.py +1085 -0
- qiskit_aer/backends/aer_simulator.py +1025 -0
- qiskit_aer/backends/aerbackend.py +679 -0
- qiskit_aer/backends/backend_utils.py +567 -0
- qiskit_aer/backends/backendconfiguration.py +395 -0
- qiskit_aer/backends/backendproperties.py +590 -0
- qiskit_aer/backends/compatibility.py +287 -0
- qiskit_aer/backends/controller_wrappers.cp314-win_amd64.pyd +0 -0
- qiskit_aer/backends/libopenblas.dll +0 -0
- qiskit_aer/backends/name_mapping.py +306 -0
- qiskit_aer/backends/qasm_simulator.py +925 -0
- qiskit_aer/backends/statevector_simulator.py +330 -0
- qiskit_aer/backends/unitary_simulator.py +316 -0
- qiskit_aer/jobs/__init__.py +35 -0
- qiskit_aer/jobs/aerjob.py +143 -0
- qiskit_aer/jobs/utils.py +66 -0
- qiskit_aer/library/__init__.py +204 -0
- qiskit_aer/library/control_flow_instructions/__init__.py +16 -0
- qiskit_aer/library/control_flow_instructions/jump.py +47 -0
- qiskit_aer/library/control_flow_instructions/mark.py +30 -0
- qiskit_aer/library/control_flow_instructions/store.py +29 -0
- qiskit_aer/library/default_qubits.py +44 -0
- qiskit_aer/library/instructions_table.csv +21 -0
- qiskit_aer/library/save_instructions/__init__.py +44 -0
- qiskit_aer/library/save_instructions/save_amplitudes.py +168 -0
- qiskit_aer/library/save_instructions/save_clifford.py +63 -0
- qiskit_aer/library/save_instructions/save_data.py +129 -0
- qiskit_aer/library/save_instructions/save_density_matrix.py +91 -0
- qiskit_aer/library/save_instructions/save_expectation_value.py +257 -0
- qiskit_aer/library/save_instructions/save_matrix_product_state.py +71 -0
- qiskit_aer/library/save_instructions/save_probabilities.py +156 -0
- qiskit_aer/library/save_instructions/save_stabilizer.py +70 -0
- qiskit_aer/library/save_instructions/save_state.py +79 -0
- qiskit_aer/library/save_instructions/save_statevector.py +120 -0
- qiskit_aer/library/save_instructions/save_superop.py +62 -0
- qiskit_aer/library/save_instructions/save_unitary.py +63 -0
- qiskit_aer/library/set_instructions/__init__.py +19 -0
- qiskit_aer/library/set_instructions/set_density_matrix.py +78 -0
- qiskit_aer/library/set_instructions/set_matrix_product_state.py +83 -0
- qiskit_aer/library/set_instructions/set_stabilizer.py +77 -0
- qiskit_aer/library/set_instructions/set_statevector.py +78 -0
- qiskit_aer/library/set_instructions/set_superop.py +78 -0
- qiskit_aer/library/set_instructions/set_unitary.py +78 -0
- qiskit_aer/noise/__init__.py +265 -0
- qiskit_aer/noise/device/__init__.py +25 -0
- qiskit_aer/noise/device/models.py +397 -0
- qiskit_aer/noise/device/parameters.py +202 -0
- qiskit_aer/noise/errors/__init__.py +30 -0
- qiskit_aer/noise/errors/base_quantum_error.py +119 -0
- qiskit_aer/noise/errors/pauli_error.py +283 -0
- qiskit_aer/noise/errors/pauli_lindblad_error.py +363 -0
- qiskit_aer/noise/errors/quantum_error.py +451 -0
- qiskit_aer/noise/errors/readout_error.py +355 -0
- qiskit_aer/noise/errors/standard_errors.py +498 -0
- qiskit_aer/noise/noise_model.py +1231 -0
- qiskit_aer/noise/noiseerror.py +30 -0
- qiskit_aer/noise/passes/__init__.py +18 -0
- qiskit_aer/noise/passes/local_noise_pass.py +160 -0
- qiskit_aer/noise/passes/relaxation_noise_pass.py +137 -0
- qiskit_aer/primitives/__init__.py +44 -0
- qiskit_aer/primitives/estimator.py +751 -0
- qiskit_aer/primitives/estimator_v2.py +159 -0
- qiskit_aer/primitives/sampler.py +361 -0
- qiskit_aer/primitives/sampler_v2.py +256 -0
- qiskit_aer/quantum_info/__init__.py +32 -0
- qiskit_aer/quantum_info/states/__init__.py +16 -0
- qiskit_aer/quantum_info/states/aer_densitymatrix.py +313 -0
- qiskit_aer/quantum_info/states/aer_state.py +525 -0
- qiskit_aer/quantum_info/states/aer_statevector.py +302 -0
- qiskit_aer/utils/__init__.py +44 -0
- qiskit_aer/utils/noise_model_inserter.py +66 -0
- qiskit_aer/utils/noise_transformation.py +431 -0
- qiskit_aer/version.py +86 -0
- qiskit_aer-0.17.2.dist-info/METADATA +209 -0
- qiskit_aer-0.17.2.dist-info/RECORD +83 -0
- qiskit_aer-0.17.2.dist-info/WHEEL +5 -0
- qiskit_aer-0.17.2.dist-info/licenses/LICENSE.txt +203 -0
- qiskit_aer-0.17.2.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,431 @@
|
|
|
1
|
+
# This code is part of Qiskit.
|
|
2
|
+
#
|
|
3
|
+
# (C) Copyright IBM 2018, 2019.
|
|
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
|
+
"""
|
|
14
|
+
Noise transformation module
|
|
15
|
+
|
|
16
|
+
The goal of this module is to transform one 1-qubit noise channel
|
|
17
|
+
(given by the QuantumError class) into another, built from specified
|
|
18
|
+
"building blocks" (given as Kraus matrices) such that the new channel is
|
|
19
|
+
as close as possible to the original one in the Hilber-Schmidt metric.
|
|
20
|
+
|
|
21
|
+
For a typical use case, consider a simulator for circuits built from the
|
|
22
|
+
Clifford group. Computations on such circuits can be simulated at
|
|
23
|
+
polynomial time and space, but not all noise channels can be used in such
|
|
24
|
+
a simulation. To enable noisy Clifford simulation one can transform the
|
|
25
|
+
given noise channel into the closest one, Hilbert-Schmidt wise, that can be
|
|
26
|
+
used in a Clifford simulator.
|
|
27
|
+
"""
|
|
28
|
+
# pylint: disable=import-outside-toplevel
|
|
29
|
+
|
|
30
|
+
import copy
|
|
31
|
+
import logging
|
|
32
|
+
from typing import Sequence, List, Union, Callable
|
|
33
|
+
|
|
34
|
+
import numpy as np
|
|
35
|
+
|
|
36
|
+
from qiskit.circuit import Reset
|
|
37
|
+
from qiskit.circuit.library.standard_gates import (
|
|
38
|
+
IGate,
|
|
39
|
+
XGate,
|
|
40
|
+
YGate,
|
|
41
|
+
ZGate,
|
|
42
|
+
HGate,
|
|
43
|
+
SGate,
|
|
44
|
+
SdgGate,
|
|
45
|
+
)
|
|
46
|
+
from qiskit.compiler import transpile
|
|
47
|
+
from qiskit.exceptions import MissingOptionalLibraryError
|
|
48
|
+
from qiskit.quantum_info.operators.channel import Kraus, SuperOp, Chi
|
|
49
|
+
from qiskit.quantum_info.operators.channel.quantum_channel import QuantumChannel
|
|
50
|
+
from qiskit.transpiler.exceptions import TranspilerError
|
|
51
|
+
from ..noise.errors import QuantumError
|
|
52
|
+
from ..noise.noise_model import NoiseModel
|
|
53
|
+
from ..noise.noiseerror import NoiseError
|
|
54
|
+
|
|
55
|
+
logger = logging.getLogger(__name__)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def transform_noise_model(noise_model: NoiseModel, func: Callable) -> NoiseModel:
|
|
59
|
+
"""Return a new noise model by applyign a function to all quantum errors.
|
|
60
|
+
|
|
61
|
+
This returns a new noise model containing the resulting errors from
|
|
62
|
+
applying the supplied function to all QuantumErrors in the noise model.
|
|
63
|
+
This function should have singature `func(error: QuantumError) -> QuantumError`
|
|
64
|
+
where the returned quantum error is defined on the same number of qubits
|
|
65
|
+
as the original error.
|
|
66
|
+
|
|
67
|
+
Args:
|
|
68
|
+
noise_model: the noise model to be transformed.
|
|
69
|
+
func: function for transforming QuantumErrors.
|
|
70
|
+
Returns:
|
|
71
|
+
The transpiled noise model.
|
|
72
|
+
|
|
73
|
+
Raises:
|
|
74
|
+
NoiseError: if the transformation failed.
|
|
75
|
+
"""
|
|
76
|
+
# Make a deep copy of the noise model so we can update its
|
|
77
|
+
# internal dicts without affecting the original model
|
|
78
|
+
new_noise = copy.deepcopy(noise_model)
|
|
79
|
+
|
|
80
|
+
for key, error in new_noise._default_quantum_errors.items():
|
|
81
|
+
new_noise._default_quantum_errors[key] = func(error)
|
|
82
|
+
|
|
83
|
+
for inst_name, noise_dic in new_noise._local_quantum_errors.items():
|
|
84
|
+
for qubits, error in noise_dic.items():
|
|
85
|
+
new_noise._local_quantum_errors[inst_name][qubits] = func(error)
|
|
86
|
+
|
|
87
|
+
return new_noise
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def transpile_quantum_error(error: QuantumError, **transpile_kwargs) -> QuantumError:
|
|
91
|
+
"""Return a new quantum error containin transpiled circuits.
|
|
92
|
+
|
|
93
|
+
This returns a new QuantumError containing the circuits resulting from
|
|
94
|
+
transpiling all error circuits using :func:`qiskit.transpile` with the
|
|
95
|
+
passed keyword agruments.
|
|
96
|
+
|
|
97
|
+
Args:
|
|
98
|
+
error: the quantum error to be transformed.
|
|
99
|
+
transpile_kwargs: kwargs for passing to qiskit transpile function.
|
|
100
|
+
|
|
101
|
+
Returns:
|
|
102
|
+
The transformed quantum error.
|
|
103
|
+
|
|
104
|
+
Raises:
|
|
105
|
+
NoiseError: if the transformation failed.
|
|
106
|
+
"""
|
|
107
|
+
try:
|
|
108
|
+
transpiled_circs = transpile(error.circuits, **transpile_kwargs)
|
|
109
|
+
except TranspilerError as err:
|
|
110
|
+
raise NoiseError(
|
|
111
|
+
f"Failed to transpile circuits in {error} with kwargs {transpile_kwargs}"
|
|
112
|
+
) from err
|
|
113
|
+
return QuantumError(zip(transpiled_circs, error.probabilities))
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def transpile_noise_model(noise_model: NoiseModel, **transpile_kwargs) -> NoiseModel:
|
|
117
|
+
"""Return a new noise model with transpiled QuantumErrors.
|
|
118
|
+
|
|
119
|
+
This returns a new noise model containing the resulting errors from
|
|
120
|
+
transpiling all QuantumErrors in the noise model
|
|
121
|
+
using :func:`transpile_quantum_error` function with the passed
|
|
122
|
+
keyword agruments.
|
|
123
|
+
|
|
124
|
+
Args:
|
|
125
|
+
noise_model: the noise model to be transformed.
|
|
126
|
+
transpile_kwargs: kwargs for passing to qiskit transpile function.
|
|
127
|
+
|
|
128
|
+
Returns:
|
|
129
|
+
The transpiled noise model.
|
|
130
|
+
|
|
131
|
+
Raises:
|
|
132
|
+
NoiseError: if the transformation failed.
|
|
133
|
+
"""
|
|
134
|
+
|
|
135
|
+
def func(error):
|
|
136
|
+
return transpile_quantum_error(error, **transpile_kwargs)
|
|
137
|
+
|
|
138
|
+
return transform_noise_model(noise_model, func)
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def approximate_quantum_error(
|
|
142
|
+
error, *, operator_string=None, operator_dict=None, operator_list=None
|
|
143
|
+
):
|
|
144
|
+
r"""
|
|
145
|
+
Return a ``QuantumError`` object that approximates an error
|
|
146
|
+
as a mixture of specified operators (channels).
|
|
147
|
+
|
|
148
|
+
The approximation is done by minimizing the Hilbert-Schmidt distance
|
|
149
|
+
between the process matrix of the target error channel (:math:`T`) and
|
|
150
|
+
the process matrix of the output channel (:math:`S = \sum_i{p_i S_i}`),
|
|
151
|
+
i.e. :math:`Tr[(T-S)^\dagger (T-S)]`,
|
|
152
|
+
where
|
|
153
|
+
:math:`[p_1, p_2, ..., p_n]` denote probabilities and
|
|
154
|
+
:math:`[S_1, S_2, ..., S_n]` denote basis operators (channels).
|
|
155
|
+
|
|
156
|
+
See `arXiv:1207.0046 <http://arxiv.org/abs/1207.0046>`_ for the details.
|
|
157
|
+
|
|
158
|
+
Args:
|
|
159
|
+
error (QuantumError or QuantumChannel): the error to be approximated.
|
|
160
|
+
The number of qubits must be 1 or 2.
|
|
161
|
+
operator_string (string): a name for a pre-made set of
|
|
162
|
+
building blocks for the output channel (Default: None).
|
|
163
|
+
Possible values are ``'pauli'``, ``'reset'``, ``'clifford'``.
|
|
164
|
+
operator_dict (dict): a dictionary whose values are the
|
|
165
|
+
building blocks for the output channel (Default: None).
|
|
166
|
+
E.g. {"x": XGate(), "y": YGate()}, keys "x" and "y"
|
|
167
|
+
are not used in transformation.
|
|
168
|
+
operator_list (list): list of building block operators for the
|
|
169
|
+
output channel (Default: None). E.g. [XGate(), YGate()]
|
|
170
|
+
|
|
171
|
+
Returns:
|
|
172
|
+
QuantumError: the approximate quantum error.
|
|
173
|
+
|
|
174
|
+
Raises:
|
|
175
|
+
NoiseError: if any invalid argument is specified or approximation failed.
|
|
176
|
+
MissingOptionalLibraryError: if cvxpy is not installed.
|
|
177
|
+
|
|
178
|
+
Note:
|
|
179
|
+
The operator input precedence is: ``list`` < ``dict`` < ``string``.
|
|
180
|
+
If a ``string`` is given, ``dict`` is overwritten;
|
|
181
|
+
if a ``dict`` is given, ``list`` is overwritten.
|
|
182
|
+
The ``string`` supports only 1- or 2-qubit errors and
|
|
183
|
+
its possible values are ``'pauli'``, ``'reset'``, ``'clifford'``.
|
|
184
|
+
The ``'clifford'`` does not support 2-qubit errors.
|
|
185
|
+
"""
|
|
186
|
+
if not isinstance(error, (QuantumError, QuantumChannel)):
|
|
187
|
+
raise NoiseError(f"Invalid input error type: {error.__class__.__name__}")
|
|
188
|
+
|
|
189
|
+
if error.num_qubits > 2:
|
|
190
|
+
raise NoiseError(
|
|
191
|
+
"Only 1-qubit and 2-qubit noises can be converted, "
|
|
192
|
+
f"{error.num_qubits}-qubit noise found in model"
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
if operator_string is not None:
|
|
196
|
+
valid_operator_strings = _PRESET_OPERATOR_TABLE.keys()
|
|
197
|
+
operator_string = operator_string.lower()
|
|
198
|
+
if operator_string not in valid_operator_strings:
|
|
199
|
+
raise NoiseError(
|
|
200
|
+
f"{operator_string} is not a valid operator_string. "
|
|
201
|
+
f"It must be one of {valid_operator_strings}"
|
|
202
|
+
)
|
|
203
|
+
try:
|
|
204
|
+
operator_list = _PRESET_OPERATOR_TABLE[operator_string][error.num_qubits]
|
|
205
|
+
except KeyError as err:
|
|
206
|
+
raise NoiseError(
|
|
207
|
+
f"Preset '{operator_string}' operators do not support the "
|
|
208
|
+
f"approximation of errors with {error.num_qubits} qubits"
|
|
209
|
+
) from err
|
|
210
|
+
if operator_dict is not None:
|
|
211
|
+
_, operator_list = zip(*operator_dict.items())
|
|
212
|
+
if operator_list is not None:
|
|
213
|
+
if not isinstance(operator_list, Sequence):
|
|
214
|
+
raise NoiseError(f"operator_list is not a sequence: {operator_list}")
|
|
215
|
+
try:
|
|
216
|
+
channel_list = [
|
|
217
|
+
op if isinstance(op, QuantumChannel) else QuantumError([(op, 1)])
|
|
218
|
+
for op in operator_list
|
|
219
|
+
] # preserve operator_list
|
|
220
|
+
except NoiseError as err:
|
|
221
|
+
raise NoiseError(f"Invalid type found in operator list: {operator_list}") from err
|
|
222
|
+
|
|
223
|
+
probabilities = _transform_by_operator_list(channel_list, error)[1:]
|
|
224
|
+
identity_prob = np.round(1 - sum(probabilities), 9)
|
|
225
|
+
if identity_prob < 0 or identity_prob > 1:
|
|
226
|
+
raise NoiseError(f"Channel probabilities sum to {1 - identity_prob}")
|
|
227
|
+
noise_ops = [((IGate(), [0]), identity_prob)]
|
|
228
|
+
for operator, probability in zip(operator_list, probabilities):
|
|
229
|
+
noise_ops.append((operator, probability))
|
|
230
|
+
return QuantumError(noise_ops)
|
|
231
|
+
|
|
232
|
+
raise NoiseError("Quantum error approximation failed - no approximating operators detected")
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
def approximate_noise_model(model, *, operator_string=None, operator_dict=None, operator_list=None):
|
|
236
|
+
"""
|
|
237
|
+
Replace all noises in a noise model with ones approximated
|
|
238
|
+
by a mixture of operators (channels).
|
|
239
|
+
|
|
240
|
+
Args:
|
|
241
|
+
model (NoiseModel): the noise model to be approximated.
|
|
242
|
+
All noises in the model must be 1- or 2-qubit noises.
|
|
243
|
+
operator_string (string): a name for a pre-made set of
|
|
244
|
+
building blocks for the output channel (Default: None).
|
|
245
|
+
Possible values are ``'pauli'``, ``'reset'``, ``'clifford'``.
|
|
246
|
+
operator_dict (dict): a dictionary whose values are the
|
|
247
|
+
building blocks for the output channel (Default: None).
|
|
248
|
+
E.g. {"x": XGate(), "y": YGate()}, keys "x" and "y"
|
|
249
|
+
are not used in transformation.
|
|
250
|
+
operator_list (list): list of building block operators for the
|
|
251
|
+
output channel (Default: None). E.g. [XGate(), YGate()]
|
|
252
|
+
|
|
253
|
+
Returns:
|
|
254
|
+
NoiseModel: the approximate noise model.
|
|
255
|
+
|
|
256
|
+
Raises:
|
|
257
|
+
NoiseError: if any invalid argument is specified or approximation failed.
|
|
258
|
+
MissingOptionalLibraryError: if cvxpy is not installed.
|
|
259
|
+
|
|
260
|
+
Note:
|
|
261
|
+
The operator input precedence is: ``list`` < ``dict`` < ``string``.
|
|
262
|
+
If a ``string`` is given, ``dict`` is overwritten;
|
|
263
|
+
if a ``dict`` is given, ``list`` is overwritten.
|
|
264
|
+
The ``string`` supports only 1- or 2-qubit errors and
|
|
265
|
+
its possible values are ``'pauli'``, ``'reset'``, ``'clifford'``.
|
|
266
|
+
The ``'clifford'`` does not support 2-qubit errors.
|
|
267
|
+
"""
|
|
268
|
+
|
|
269
|
+
def approximate(noise):
|
|
270
|
+
return approximate_quantum_error(
|
|
271
|
+
noise,
|
|
272
|
+
operator_string=operator_string,
|
|
273
|
+
operator_dict=operator_dict,
|
|
274
|
+
operator_list=operator_list,
|
|
275
|
+
)
|
|
276
|
+
|
|
277
|
+
return transform_noise_model(model, approximate)
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
# pauli operators
|
|
281
|
+
_PAULIS_Q0 = [[(IGate(), [0])], [(XGate(), [0])], [(YGate(), [0])], [(ZGate(), [0])]]
|
|
282
|
+
_PAULIS_Q1 = [[(IGate(), [1])], [(XGate(), [1])], [(YGate(), [1])], [(ZGate(), [1])]]
|
|
283
|
+
_PAULIS_Q0Q1 = [op_q0 + op_q1 for op_q0 in _PAULIS_Q0 for op_q1 in _PAULIS_Q1]
|
|
284
|
+
# reset operators
|
|
285
|
+
_RESET_Q0_TO_0 = [(Reset(), [0])]
|
|
286
|
+
_RESET_Q0_TO_1 = [(Reset(), [0]), (XGate(), [0])]
|
|
287
|
+
_RESET_Q0 = [[(IGate(), [0])], _RESET_Q0_TO_0, _RESET_Q0_TO_1]
|
|
288
|
+
_RESET_Q1_TO_0 = [(Reset(), [1])]
|
|
289
|
+
_RESET_Q1_TO_1 = [(Reset(), [1]), (XGate(), [1])]
|
|
290
|
+
_RESET_Q1 = [[(IGate(), [1])], _RESET_Q1_TO_0, _RESET_Q1_TO_1]
|
|
291
|
+
_RESET_Q0Q1 = [op_q0 + op_q1 for op_q0 in _RESET_Q0 for op_q1 in _RESET_Q1]
|
|
292
|
+
# clifford operators
|
|
293
|
+
_CLIFFORD_GATES = [
|
|
294
|
+
(IGate(),),
|
|
295
|
+
(SGate(),),
|
|
296
|
+
(SdgGate(),),
|
|
297
|
+
(ZGate(),),
|
|
298
|
+
# u2 gates
|
|
299
|
+
(HGate(),),
|
|
300
|
+
(HGate(), ZGate()),
|
|
301
|
+
(ZGate(), HGate()),
|
|
302
|
+
(HGate(), SGate()),
|
|
303
|
+
(SGate(), HGate()),
|
|
304
|
+
(HGate(), SdgGate()),
|
|
305
|
+
(SdgGate(), HGate()),
|
|
306
|
+
(SGate(), HGate(), SGate()),
|
|
307
|
+
(SdgGate(), HGate(), SGate()),
|
|
308
|
+
(ZGate(), HGate(), SGate()),
|
|
309
|
+
(SGate(), HGate(), SdgGate()),
|
|
310
|
+
(SdgGate(), HGate(), SdgGate()),
|
|
311
|
+
(ZGate(), HGate(), SdgGate()),
|
|
312
|
+
(SGate(), HGate(), ZGate()),
|
|
313
|
+
(SdgGate(), HGate(), ZGate()),
|
|
314
|
+
(ZGate(), HGate(), ZGate()),
|
|
315
|
+
# u3 gates
|
|
316
|
+
(XGate(),),
|
|
317
|
+
(YGate(),),
|
|
318
|
+
(SGate(), XGate()),
|
|
319
|
+
(SdgGate(), XGate()),
|
|
320
|
+
]
|
|
321
|
+
# preset operator table
|
|
322
|
+
_PRESET_OPERATOR_TABLE = {
|
|
323
|
+
"pauli": {
|
|
324
|
+
1: _PAULIS_Q0[1:],
|
|
325
|
+
2: _PAULIS_Q0Q1[1:],
|
|
326
|
+
},
|
|
327
|
+
"reset": {
|
|
328
|
+
1: _RESET_Q0[1:],
|
|
329
|
+
2: _RESET_Q0Q1[1:],
|
|
330
|
+
},
|
|
331
|
+
"clifford": {
|
|
332
|
+
1: [[(gate, [0]) for gate in _CLIFFORD_GATES[j]] for j in range(1, 24)],
|
|
333
|
+
},
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
|
|
337
|
+
def _transform_by_operator_list(
|
|
338
|
+
basis_ops: Sequence[Union[QuantumChannel, QuantumError]],
|
|
339
|
+
target: Union[QuantumChannel, QuantumError],
|
|
340
|
+
) -> List[float]:
|
|
341
|
+
r"""
|
|
342
|
+
Transform (or approximate) the target quantum channel into a mixture of
|
|
343
|
+
basis operators (channels) and return the mixing probabilities.
|
|
344
|
+
|
|
345
|
+
The approximate channel is found by minimizing the Hilbert-Schmidt
|
|
346
|
+
distance between the process matrix of the target channel (:math:`T`) and
|
|
347
|
+
the process matrix of the output channel (:math:`S = \sum_i{p_i S_i}`),
|
|
348
|
+
i.e. :math:`Tr[(T-S)^\dagger (T-S)]`,
|
|
349
|
+
where
|
|
350
|
+
:math:`[p_1, p_2, ..., p_n]` denote probabilities and
|
|
351
|
+
:math:`[S_1, S_2, ..., S_n]` denote basis operators (channels).
|
|
352
|
+
|
|
353
|
+
Such an optimization can represented as a quadratic program:
|
|
354
|
+
Minimize :math:`p^T A p + b^T p`,
|
|
355
|
+
subject to :math:`p \ge 0`, `\sum_i{p_i} = 1`, `\sum_i{p_i} = 1`.
|
|
356
|
+
The last constraint is for making the approximation conservative by
|
|
357
|
+
forcing the output channel have more error than the target channel
|
|
358
|
+
in the sense that a "fidelity" against identity channel should be worse.
|
|
359
|
+
|
|
360
|
+
See `arXiv:1207.0046 <http://arxiv.org/abs/1207.0046>`_ for the details.
|
|
361
|
+
|
|
362
|
+
Args:
|
|
363
|
+
target: Quantum channel to be transformed.
|
|
364
|
+
basis_ops: a list of channel objects constructing the output channel.
|
|
365
|
+
|
|
366
|
+
Returns:
|
|
367
|
+
list: A list of amplitudes (probabilities) of basis that define the output channel.
|
|
368
|
+
|
|
369
|
+
Raises:
|
|
370
|
+
MissingOptionalLibraryError: if cvxpy is not installed.
|
|
371
|
+
"""
|
|
372
|
+
# pylint: disable=invalid-name
|
|
373
|
+
N = 2 ** basis_ops[0].num_qubits
|
|
374
|
+
# add identity channel in front
|
|
375
|
+
basis_ops = [Kraus(np.eye(N))] + basis_ops
|
|
376
|
+
|
|
377
|
+
# create objective function coefficients
|
|
378
|
+
basis_ops_mats = [Chi(op).data for op in basis_ops]
|
|
379
|
+
T = Chi(target).data
|
|
380
|
+
n = len(basis_ops_mats)
|
|
381
|
+
A = np.zeros((n, n))
|
|
382
|
+
for i, S_i in enumerate(basis_ops_mats):
|
|
383
|
+
for j, S_j in enumerate(basis_ops_mats):
|
|
384
|
+
# A[i][j] = 1/2 * {Tr(S_i^† S_j) - Tr(S_j^† S_i)} = Re[Tr(S_i^† S_j)]
|
|
385
|
+
if i < j:
|
|
386
|
+
A[i][j] = _hs_inner_prod_real(S_i, S_j)
|
|
387
|
+
elif i > j:
|
|
388
|
+
A[i][j] = A[j][i]
|
|
389
|
+
else: # i==j
|
|
390
|
+
A[i][i] = _hs_norm(S_i)
|
|
391
|
+
b = -2 * np.array([_hs_inner_prod_real(T, S_i) for S_i in basis_ops_mats])
|
|
392
|
+
# c = _hs_norm(T)
|
|
393
|
+
|
|
394
|
+
# create honesty constraint coefficients
|
|
395
|
+
def fidelity(channel): # fidelity w.r.t. identity omitting the N^-2 factor
|
|
396
|
+
return float(np.abs(np.trace(SuperOp(channel))))
|
|
397
|
+
|
|
398
|
+
source_fids = np.array([fidelity(op) for op in basis_ops])
|
|
399
|
+
target_fid = fidelity(target)
|
|
400
|
+
|
|
401
|
+
try:
|
|
402
|
+
import cvxpy
|
|
403
|
+
except ImportError as err:
|
|
404
|
+
logger.error("cvxpy module needs to be installed to use this feature.")
|
|
405
|
+
raise MissingOptionalLibraryError(
|
|
406
|
+
libname="cvxpy",
|
|
407
|
+
name="Transformation/Approximation of noise",
|
|
408
|
+
pip_install="pip install cvxpy",
|
|
409
|
+
msg="CVXPY is required to solve an optimization problem of"
|
|
410
|
+
" approximating a noise channel.",
|
|
411
|
+
) from err
|
|
412
|
+
# create quadratic program
|
|
413
|
+
x = cvxpy.Variable(n)
|
|
414
|
+
prob = cvxpy.Problem(
|
|
415
|
+
cvxpy.Minimize(cvxpy.quad_form(x, A) + b.T @ x),
|
|
416
|
+
constraints=[cvxpy.sum(x) <= 1, x >= 0, source_fids.T @ x <= target_fid],
|
|
417
|
+
)
|
|
418
|
+
# solve quadratic program
|
|
419
|
+
prob.solve()
|
|
420
|
+
probabilities = x.value
|
|
421
|
+
return probabilities
|
|
422
|
+
|
|
423
|
+
|
|
424
|
+
def _hs_norm(A): # pylint: disable=invalid-name
|
|
425
|
+
# Tr(A^† A)
|
|
426
|
+
return np.trace(np.conj(A).T @ A).real
|
|
427
|
+
|
|
428
|
+
|
|
429
|
+
def _hs_inner_prod_real(A, B): # pylint: disable=invalid-name
|
|
430
|
+
# Re[Tr(A^† B)]
|
|
431
|
+
return np.trace(np.conj(A.T) @ B).real
|
qiskit_aer/version.py
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# This code is part of Qiskit.
|
|
2
|
+
#
|
|
3
|
+
# (C) Copyright IBM 2018, 2019.
|
|
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
|
+
"""
|
|
14
|
+
Helper tools for getting Terra addon version information
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
import os
|
|
18
|
+
import subprocess
|
|
19
|
+
|
|
20
|
+
ROOT_DIR = os.path.dirname(os.path.abspath(__file__))
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _minimal_ext_cmd(cmd):
|
|
24
|
+
# construct minimal environment
|
|
25
|
+
env = {}
|
|
26
|
+
for k in ["SYSTEMROOT", "PATH"]:
|
|
27
|
+
v = os.environ.get(k)
|
|
28
|
+
if v is not None:
|
|
29
|
+
env[k] = v
|
|
30
|
+
# LANGUAGE is used on win32
|
|
31
|
+
env["LANGUAGE"] = "C"
|
|
32
|
+
env["LANG"] = "C"
|
|
33
|
+
env["LC_ALL"] = "C"
|
|
34
|
+
with subprocess.Popen(
|
|
35
|
+
cmd,
|
|
36
|
+
stdout=subprocess.PIPE,
|
|
37
|
+
stderr=subprocess.PIPE,
|
|
38
|
+
env=env,
|
|
39
|
+
cwd=os.path.join(os.path.dirname(ROOT_DIR)),
|
|
40
|
+
encoding="utf-8",
|
|
41
|
+
) as proc:
|
|
42
|
+
out = proc.communicate()[0]
|
|
43
|
+
if proc.returncode > 0:
|
|
44
|
+
raise OSError
|
|
45
|
+
return out
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def git_version():
|
|
49
|
+
"""Get the current git head sha1."""
|
|
50
|
+
# Determine if we're at main
|
|
51
|
+
try:
|
|
52
|
+
out = _minimal_ext_cmd(["git", "rev-parse", "HEAD"])
|
|
53
|
+
git_revision = out.strip().decode("ascii")
|
|
54
|
+
except OSError:
|
|
55
|
+
git_revision = "Unknown"
|
|
56
|
+
|
|
57
|
+
return git_revision
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
with open(os.path.join(ROOT_DIR, "VERSION.txt"), "r", encoding="utf-8") as version_file:
|
|
61
|
+
VERSION = version_file.read().strip()
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def get_version_info():
|
|
65
|
+
"""Get the full version string."""
|
|
66
|
+
# Adding the git rev number needs to be done inside
|
|
67
|
+
# write_version_py(), otherwise the import of scipy.version messes
|
|
68
|
+
# up the build under Python 3.
|
|
69
|
+
full_version = VERSION
|
|
70
|
+
|
|
71
|
+
if not os.path.exists(
|
|
72
|
+
os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(ROOT_DIR))), ".git")
|
|
73
|
+
):
|
|
74
|
+
return full_version
|
|
75
|
+
try:
|
|
76
|
+
release = _minimal_ext_cmd(["git", "tag", "-l", "--points-at", "HEAD"])
|
|
77
|
+
except Exception: # pylint: disable=broad-except
|
|
78
|
+
return full_version
|
|
79
|
+
if not release:
|
|
80
|
+
git_revision = git_version()
|
|
81
|
+
full_version += ".dev0+" + git_revision[:7]
|
|
82
|
+
|
|
83
|
+
return full_version
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
__version__ = get_version_info()
|