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,1231 @@
|
|
|
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
|
+
Noise model class for Aer simulators.
|
|
14
|
+
"""
|
|
15
|
+
import copy
|
|
16
|
+
import json
|
|
17
|
+
import logging
|
|
18
|
+
from typing import Optional
|
|
19
|
+
from warnings import warn, catch_warnings, filterwarnings
|
|
20
|
+
|
|
21
|
+
import numpy as np
|
|
22
|
+
|
|
23
|
+
from qiskit.circuit import QuantumCircuit, Instruction, Delay, Reset
|
|
24
|
+
from qiskit.circuit.library.generalized_gates import PauliGate, UnitaryGate
|
|
25
|
+
from qiskit.providers import QubitProperties
|
|
26
|
+
from qiskit.transpiler import PassManager
|
|
27
|
+
from qiskit.utils import apply_prefix
|
|
28
|
+
from .device.models import _excited_population, _truncate_t2_value
|
|
29
|
+
from .device.models import basic_device_gate_errors
|
|
30
|
+
from .device.models import basic_device_readout_errors
|
|
31
|
+
from .errors.base_quantum_error import BaseQuantumError
|
|
32
|
+
from .errors.quantum_error import QuantumError
|
|
33
|
+
from .errors.readout_error import ReadoutError
|
|
34
|
+
from .noiseerror import NoiseError
|
|
35
|
+
from .passes import RelaxationNoisePass
|
|
36
|
+
from ..backends.backend_utils import BASIS_GATES
|
|
37
|
+
|
|
38
|
+
logger = logging.getLogger(__name__)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class AerJSONEncoder(json.JSONEncoder):
|
|
42
|
+
"""
|
|
43
|
+
JSON encoder for NumPy arrays and complex numbers.
|
|
44
|
+
|
|
45
|
+
This functions as the standard JSON Encoder but adds support
|
|
46
|
+
for encoding:
|
|
47
|
+
complex numbers z as lists [z.real, z.imag]
|
|
48
|
+
ndarrays as nested lists.
|
|
49
|
+
"""
|
|
50
|
+
|
|
51
|
+
# pylint: disable=method-hidden,arguments-differ
|
|
52
|
+
def default(self, o):
|
|
53
|
+
if isinstance(o, np.ndarray):
|
|
54
|
+
return o.tolist()
|
|
55
|
+
if isinstance(o, complex):
|
|
56
|
+
return [o.real, o.imag]
|
|
57
|
+
if hasattr(o, "to_dict"):
|
|
58
|
+
return o.to_dict()
|
|
59
|
+
return super().default(o)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class QuantumErrorLocation(Instruction):
|
|
63
|
+
"""Instruction for referencing a multi-qubit error in a NoiseModel"""
|
|
64
|
+
|
|
65
|
+
_directive = True
|
|
66
|
+
|
|
67
|
+
def __init__(self, qerror):
|
|
68
|
+
"""Construct a new quantum error location instruction.
|
|
69
|
+
|
|
70
|
+
Args:
|
|
71
|
+
qerror (QuantumError): the quantum error to reference.
|
|
72
|
+
"""
|
|
73
|
+
super().__init__("qerror_loc", qerror.num_qubits, 0, [], label=qerror.id)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
class NoiseModel:
|
|
77
|
+
"""Noise model class for Aer simulators.
|
|
78
|
+
|
|
79
|
+
This class is used to represent noise model for the
|
|
80
|
+
:class:`~qiskit_aer.QasmSimulator`. It can be used to construct
|
|
81
|
+
custom noise models for simulator, or to automatically generate a basic
|
|
82
|
+
device noise model for an IBMQ backend. See the
|
|
83
|
+
:mod:`~qiskit_aer.noise` module documentation for additional
|
|
84
|
+
information.
|
|
85
|
+
|
|
86
|
+
**Example: Basic device noise model**
|
|
87
|
+
|
|
88
|
+
An approximate :class:`NoiseModel` can be generated automatically from the
|
|
89
|
+
properties of real device backends from the IBMQ provider using the
|
|
90
|
+
:meth:`~NoiseModel.from_backend` method.
|
|
91
|
+
|
|
92
|
+
.. code-block:: python
|
|
93
|
+
|
|
94
|
+
from qiskit import IBMQ, Aer
|
|
95
|
+
from qiskit_aer.noise import NoiseModel
|
|
96
|
+
|
|
97
|
+
provider = IBMQ.load_account()
|
|
98
|
+
backend = provider.get_backend('ibmq_vigo')
|
|
99
|
+
noise_model = NoiseModel.from_backend(backend)
|
|
100
|
+
print(noise_model)
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
**Example: Custom noise model**
|
|
104
|
+
|
|
105
|
+
Custom noise models can be used by adding :class:`QuantumError` to circuit
|
|
106
|
+
gate, reset or measure instructions, and :class:`ReadoutError` to measure
|
|
107
|
+
instructions.
|
|
108
|
+
|
|
109
|
+
.. code-block:: python
|
|
110
|
+
|
|
111
|
+
import qiskit_aer.noise as noise
|
|
112
|
+
|
|
113
|
+
# Error probabilities
|
|
114
|
+
prob_1 = 0.001 # 1-qubit gate
|
|
115
|
+
prob_2 = 0.01 # 2-qubit gate
|
|
116
|
+
|
|
117
|
+
# Depolarizing quantum errors
|
|
118
|
+
error_1 = noise.depolarizing_error(prob_1, 1)
|
|
119
|
+
error_2 = noise.depolarizing_error(prob_2, 2)
|
|
120
|
+
|
|
121
|
+
# Add errors to noise model
|
|
122
|
+
noise_model = noise.NoiseModel()
|
|
123
|
+
noise_model.add_all_qubit_quantum_error(error_1, ['rz', 'sx', 'x'])
|
|
124
|
+
noise_model.add_all_qubit_quantum_error(error_2, ['cx'])
|
|
125
|
+
print(noise_model)
|
|
126
|
+
|
|
127
|
+
"""
|
|
128
|
+
|
|
129
|
+
# Checks for standard 1-3 qubit instructions
|
|
130
|
+
_1qubit_instructions = set(
|
|
131
|
+
[
|
|
132
|
+
"u1",
|
|
133
|
+
"u2",
|
|
134
|
+
"u3",
|
|
135
|
+
"u",
|
|
136
|
+
"p",
|
|
137
|
+
"r",
|
|
138
|
+
"rx",
|
|
139
|
+
"ry",
|
|
140
|
+
"rz",
|
|
141
|
+
"id",
|
|
142
|
+
"x",
|
|
143
|
+
"y",
|
|
144
|
+
"z",
|
|
145
|
+
"h",
|
|
146
|
+
"s",
|
|
147
|
+
"sdg",
|
|
148
|
+
"sx",
|
|
149
|
+
"sxdg",
|
|
150
|
+
"t",
|
|
151
|
+
"tdg",
|
|
152
|
+
]
|
|
153
|
+
)
|
|
154
|
+
_2qubit_instructions = set(
|
|
155
|
+
[
|
|
156
|
+
"swap",
|
|
157
|
+
"cx",
|
|
158
|
+
"cy",
|
|
159
|
+
"cz",
|
|
160
|
+
"csx",
|
|
161
|
+
"cp",
|
|
162
|
+
"cu",
|
|
163
|
+
"cu1",
|
|
164
|
+
"cu2",
|
|
165
|
+
"cu3",
|
|
166
|
+
"rxx",
|
|
167
|
+
"ryy",
|
|
168
|
+
"rzz",
|
|
169
|
+
"rzx",
|
|
170
|
+
"ecr",
|
|
171
|
+
]
|
|
172
|
+
)
|
|
173
|
+
_3qubit_instructions = set(["ccx", "cswap"])
|
|
174
|
+
|
|
175
|
+
def __init__(self, basis_gates=None):
|
|
176
|
+
"""Initialize an empty noise model.
|
|
177
|
+
|
|
178
|
+
Args:
|
|
179
|
+
basis_gates (list[str] or None): Specify an initial basis_gates
|
|
180
|
+
for the noise model. If None a default value of ['id', 'rz', 'sx', 'cx']
|
|
181
|
+
is used (Default: None).
|
|
182
|
+
|
|
183
|
+
Additional Information:
|
|
184
|
+
Errors added to the noise model will have their instruction
|
|
185
|
+
appended to the noise model basis_gates if the instruction is in
|
|
186
|
+
the :class:`~qiskit_aer.QasmSimulator` basis_gates. If
|
|
187
|
+
the instruction is not in the
|
|
188
|
+
:class:`~qiskit_aer.QasmSimulator` basis_gates it is
|
|
189
|
+
assumed to be a label for a standard gate, and that gate should be
|
|
190
|
+
added to the `NoiseModel` basis_gates either using the init method,
|
|
191
|
+
or the :meth:`add_basis_gates` method.
|
|
192
|
+
"""
|
|
193
|
+
if basis_gates is None:
|
|
194
|
+
# Default basis gates is id, rz, sx, cx so that all standard
|
|
195
|
+
# non-identity instructions can be unrolled to rz, sx, cx,
|
|
196
|
+
# and identities won't be unrolled
|
|
197
|
+
self._basis_gates = set(["id", "rz", "sx", "cx"])
|
|
198
|
+
else:
|
|
199
|
+
self._basis_gates = set(name for name, _ in self._instruction_names_labels(basis_gates))
|
|
200
|
+
# Store gates with a noise model defined
|
|
201
|
+
self._noise_instructions = set()
|
|
202
|
+
# Store qubits referenced in noise model.
|
|
203
|
+
# These include gate qubits in local quantum and readout errors.
|
|
204
|
+
self._noise_qubits = set()
|
|
205
|
+
# Default (all-qubit) quantum errors are stored as:
|
|
206
|
+
# dict(str: QuantumError)
|
|
207
|
+
# where they keys are the instruction str label
|
|
208
|
+
self._default_quantum_errors = {}
|
|
209
|
+
# Local quantum errors are stored as:
|
|
210
|
+
# dict(str: dict(tuple: QuantumError))
|
|
211
|
+
# where the outer keys are the instruction str label and the
|
|
212
|
+
# inner dict keys are the gate qubits
|
|
213
|
+
self._local_quantum_errors = {}
|
|
214
|
+
# Default (all-qubit) readout error is stored as a single
|
|
215
|
+
# ReadoutError object since there may only be one defined.
|
|
216
|
+
self._default_readout_error = None
|
|
217
|
+
# Local readout errors are stored as:
|
|
218
|
+
# dict(tuple: ReadoutError)
|
|
219
|
+
# where the dict keys are the gate qubits.
|
|
220
|
+
self._local_readout_errors = {}
|
|
221
|
+
# Custom noise passes
|
|
222
|
+
self._custom_noise_passes = []
|
|
223
|
+
|
|
224
|
+
@property
|
|
225
|
+
def basis_gates(self):
|
|
226
|
+
"""Return basis_gates for compiling to the noise model."""
|
|
227
|
+
# Convert noise instructions to basis_gates string
|
|
228
|
+
return sorted(self._basis_gates)
|
|
229
|
+
|
|
230
|
+
@property
|
|
231
|
+
def noise_instructions(self):
|
|
232
|
+
"""Return the set of noisy instructions for this noise model."""
|
|
233
|
+
return sorted(self._noise_instructions)
|
|
234
|
+
|
|
235
|
+
@property
|
|
236
|
+
def noise_qubits(self):
|
|
237
|
+
"""Return the set of noisy qubits for this noise model."""
|
|
238
|
+
return sorted(self._noise_qubits)
|
|
239
|
+
|
|
240
|
+
@classmethod
|
|
241
|
+
def from_backend(
|
|
242
|
+
cls,
|
|
243
|
+
backend,
|
|
244
|
+
gate_error=True,
|
|
245
|
+
readout_error=True,
|
|
246
|
+
thermal_relaxation=True,
|
|
247
|
+
temperature=0,
|
|
248
|
+
gate_lengths=None,
|
|
249
|
+
gate_length_units="ns",
|
|
250
|
+
warnings=None,
|
|
251
|
+
):
|
|
252
|
+
"""Return a noise model derived from a devices backend properties.
|
|
253
|
+
|
|
254
|
+
This function generates a noise model based on:
|
|
255
|
+
|
|
256
|
+
* 1 and 2 qubit gate errors consisting of a
|
|
257
|
+
:func:`depolarizing_error` followed
|
|
258
|
+
by a :func:`thermal_relaxation_error`.
|
|
259
|
+
|
|
260
|
+
* Single qubit :class:`ReadoutError` on all measurements.
|
|
261
|
+
|
|
262
|
+
The error (noise) parameters are tuned for each individual qubit based on
|
|
263
|
+
the :math:`T_1`, :math:`T_2`, frequency and readout error parameters for
|
|
264
|
+
each qubit, and the gate error and gate time parameters for each gate
|
|
265
|
+
obtained from the device backend properties.
|
|
266
|
+
|
|
267
|
+
Note that if un-physical parameters are supplied, they are internally truncated to
|
|
268
|
+
the theoretical bound values. For example, if :math:`T_2 > 2 T_1`, :math:`T_2`
|
|
269
|
+
parameter will be truncated to :math:`2 T_1`.
|
|
270
|
+
|
|
271
|
+
**Additional Information**
|
|
272
|
+
|
|
273
|
+
The noise model includes the following errors:
|
|
274
|
+
|
|
275
|
+
* If ``readout_error=True`` include single qubit readout
|
|
276
|
+
errors on measurements.
|
|
277
|
+
|
|
278
|
+
* If ``gate_error=True`` and ``thermal_relaxation=True`` include:
|
|
279
|
+
|
|
280
|
+
* Single-qubit gate errors consisting of a :func:`depolarizing_error`
|
|
281
|
+
followed by a :func:`thermal_relaxation_error` for the qubit the
|
|
282
|
+
gate acts on.
|
|
283
|
+
|
|
284
|
+
* Two-qubit gate errors consisting of a 2-qubit
|
|
285
|
+
:func:`depolarizing_error` followed by single qubit
|
|
286
|
+
:func:`thermal_relaxation_error` on each qubit participating in
|
|
287
|
+
the gate.
|
|
288
|
+
|
|
289
|
+
* If ``gate_error=True`` is ``True`` and ``thermal_relaxation=False``:
|
|
290
|
+
|
|
291
|
+
* An N-qubit :func:`depolarizing_error` on each N-qubit gate.
|
|
292
|
+
|
|
293
|
+
* If ``gate_error=False`` and ``thermal_relaxation=True`` include
|
|
294
|
+
single-qubit :func:`thermal_relaxation_errors` on each qubits
|
|
295
|
+
participating in a multi-qubit gate.
|
|
296
|
+
|
|
297
|
+
For best practice in simulating a backend make sure that the
|
|
298
|
+
circuit is compiled using the set of basis gates in the noise
|
|
299
|
+
module by setting ``basis_gates=noise_model.basis_gates``
|
|
300
|
+
and using the device coupling map with
|
|
301
|
+
``coupling_map=backend.configuration().coupling_map``
|
|
302
|
+
|
|
303
|
+
**Specifying custom gate times**
|
|
304
|
+
|
|
305
|
+
The ``gate_lengths`` kwarg can be used to specify custom gate times
|
|
306
|
+
to add gate errors using the :math:`T_1` and :math:`T_2` values from
|
|
307
|
+
the backend properties. This should be passed as a list of tuples
|
|
308
|
+
``gate_lengths=[(name, value), ...]``
|
|
309
|
+
where ``name`` is the gate name string, and ``value`` is the gate time
|
|
310
|
+
in nanoseconds.
|
|
311
|
+
|
|
312
|
+
If a custom gate is specified that already exists in
|
|
313
|
+
the backend properties, the ``gate_lengths`` value will override the
|
|
314
|
+
gate time value from the backend properties.
|
|
315
|
+
If non-default values are used gate_lengths should be a list
|
|
316
|
+
|
|
317
|
+
Args:
|
|
318
|
+
backend (Backend): backend. For BackendV2, `warnings`
|
|
319
|
+
options are ignored, and their default values are used.
|
|
320
|
+
gate_error (bool): Include depolarizing gate errors (Default: True).
|
|
321
|
+
readout_error (Bool): Include readout errors in model
|
|
322
|
+
(Default: True).
|
|
323
|
+
thermal_relaxation (Bool): Include thermal relaxation errors
|
|
324
|
+
(Default: True).
|
|
325
|
+
temperature (double): qubit temperature in milli-Kelvin (mK) for
|
|
326
|
+
thermal relaxation errors (Default: 0).
|
|
327
|
+
gate_lengths (list): Custom gate times for thermal relaxation errors.
|
|
328
|
+
Used to extend or override the gate times in
|
|
329
|
+
the backend properties (Default: None))
|
|
330
|
+
gate_length_units (str): Time units for gate length values in
|
|
331
|
+
gate_lengths. Can be 'ns', 'ms', 'us',
|
|
332
|
+
or 's' (Default: 'ns').
|
|
333
|
+
warnings (bool): DEPRECATED, Display warnings (Default: None).
|
|
334
|
+
|
|
335
|
+
Returns:
|
|
336
|
+
NoiseModel: An approximate noise model for the device backend.
|
|
337
|
+
|
|
338
|
+
Raises:
|
|
339
|
+
NoiseError: If the input backend is not valid.
|
|
340
|
+
"""
|
|
341
|
+
if warnings is not None:
|
|
342
|
+
warn(
|
|
343
|
+
'"warnings" argument has been deprecated as of qiskit-aer 0.12.0 '
|
|
344
|
+
"and will be removed no earlier than 3 months from that release date. "
|
|
345
|
+
"Use the warnings filter in Python standard library instead.",
|
|
346
|
+
DeprecationWarning,
|
|
347
|
+
stacklevel=2,
|
|
348
|
+
)
|
|
349
|
+
else:
|
|
350
|
+
warnings = True
|
|
351
|
+
|
|
352
|
+
backend_interface_version = getattr(backend, "version", None)
|
|
353
|
+
if not isinstance(backend_interface_version, int):
|
|
354
|
+
backend_interface_version = 0
|
|
355
|
+
|
|
356
|
+
target = None
|
|
357
|
+
if backend_interface_version == 2:
|
|
358
|
+
if not warnings:
|
|
359
|
+
warn(
|
|
360
|
+
"When a BackendV2 is supplied, `warnings`"
|
|
361
|
+
" are ignored, and their default values are used.",
|
|
362
|
+
UserWarning,
|
|
363
|
+
)
|
|
364
|
+
properties = None
|
|
365
|
+
basis_gates = backend.operation_names
|
|
366
|
+
target = backend.target
|
|
367
|
+
if gate_lengths:
|
|
368
|
+
# Update target based on gate_lengths and gate_length_units
|
|
369
|
+
target = copy.deepcopy(target)
|
|
370
|
+
for op_name, qubits, value in gate_lengths:
|
|
371
|
+
prop = target[op_name][qubits]
|
|
372
|
+
prop.duration = apply_prefix(value, gate_length_units) # convert to seconds
|
|
373
|
+
target.update_instruction_properties(op_name, qubits, prop)
|
|
374
|
+
all_qubit_properties = backend.target.qubit_properties
|
|
375
|
+
if not all_qubit_properties:
|
|
376
|
+
warn(
|
|
377
|
+
f"Qiskit backend {backend} has no QubitProperties, so the resulting"
|
|
378
|
+
" noise model will not include any thermal relaxation errors.",
|
|
379
|
+
UserWarning,
|
|
380
|
+
)
|
|
381
|
+
dt = backend.dt
|
|
382
|
+
elif backend_interface_version <= 1:
|
|
383
|
+
# BackendV1 will be removed in Qiskit 2.0, so we will remove this soon
|
|
384
|
+
warn(
|
|
385
|
+
" from_backend using V1 based backend is deprecated as of Aer 0.15"
|
|
386
|
+
" and will be removed no sooner than 3 months from that release"
|
|
387
|
+
" date. Please use backends based on V2.",
|
|
388
|
+
DeprecationWarning,
|
|
389
|
+
stacklevel=2,
|
|
390
|
+
)
|
|
391
|
+
properties = backend.properties()
|
|
392
|
+
configuration = backend.configuration()
|
|
393
|
+
basis_gates = configuration.basis_gates
|
|
394
|
+
all_qubit_properties = [
|
|
395
|
+
QubitProperties(
|
|
396
|
+
t1=properties.t1(q), t2=properties.t2(q), frequency=properties.frequency(q)
|
|
397
|
+
)
|
|
398
|
+
for q in range(configuration.num_qubits)
|
|
399
|
+
]
|
|
400
|
+
dt = getattr(configuration, "dt", 0)
|
|
401
|
+
if not properties:
|
|
402
|
+
raise NoiseError(f"Qiskit backend {backend} does not have a BackendProperties")
|
|
403
|
+
else:
|
|
404
|
+
raise NoiseError(f"{backend} is not a Qiskit backend")
|
|
405
|
+
|
|
406
|
+
noise_model = NoiseModel(basis_gates=basis_gates)
|
|
407
|
+
|
|
408
|
+
# Add single-qubit readout errors
|
|
409
|
+
if readout_error:
|
|
410
|
+
for qubits, error in basic_device_readout_errors(properties, target=target):
|
|
411
|
+
noise_model.add_readout_error(error, qubits, warnings=warnings)
|
|
412
|
+
|
|
413
|
+
# Add gate errors
|
|
414
|
+
with catch_warnings():
|
|
415
|
+
filterwarnings("ignore", category=DeprecationWarning, module="qiskit_aer.noise")
|
|
416
|
+
gate_errors = basic_device_gate_errors(
|
|
417
|
+
properties,
|
|
418
|
+
gate_error=gate_error,
|
|
419
|
+
thermal_relaxation=thermal_relaxation,
|
|
420
|
+
gate_lengths=gate_lengths,
|
|
421
|
+
gate_length_units=gate_length_units,
|
|
422
|
+
temperature=temperature,
|
|
423
|
+
warnings=warnings,
|
|
424
|
+
target=target,
|
|
425
|
+
)
|
|
426
|
+
for name, qubits, error in gate_errors:
|
|
427
|
+
noise_model.add_quantum_error(error, name, qubits, warnings=warnings)
|
|
428
|
+
|
|
429
|
+
if thermal_relaxation and all_qubit_properties:
|
|
430
|
+
# Add delay errors via RelaxationNiose pass
|
|
431
|
+
try:
|
|
432
|
+
excited_state_populations = [
|
|
433
|
+
_excited_population(freq=q.frequency, temperature=temperature)
|
|
434
|
+
for q in all_qubit_properties
|
|
435
|
+
]
|
|
436
|
+
except ValueError:
|
|
437
|
+
excited_state_populations = None
|
|
438
|
+
try:
|
|
439
|
+
t1s = [prop.t1 for prop in all_qubit_properties]
|
|
440
|
+
t2s = [_truncate_t2_value(prop.t1, prop.t2) for prop in all_qubit_properties]
|
|
441
|
+
delay_pass = RelaxationNoisePass(
|
|
442
|
+
t1s=[np.inf if x is None else x for x in t1s], # replace None with np.inf
|
|
443
|
+
t2s=[np.inf if x is None else x for x in t2s], # replace None with np.inf
|
|
444
|
+
dt=dt,
|
|
445
|
+
op_types=Delay,
|
|
446
|
+
excited_state_populations=excited_state_populations,
|
|
447
|
+
target=target,
|
|
448
|
+
)
|
|
449
|
+
noise_model._custom_noise_passes.append(delay_pass)
|
|
450
|
+
except ValueError:
|
|
451
|
+
# Device does not have the required T1 or T2 information
|
|
452
|
+
# in its properties
|
|
453
|
+
pass
|
|
454
|
+
|
|
455
|
+
return noise_model
|
|
456
|
+
|
|
457
|
+
@classmethod
|
|
458
|
+
def from_backend_properties(
|
|
459
|
+
cls,
|
|
460
|
+
backend_properties: "BackendProperties",
|
|
461
|
+
gate_error: bool = True,
|
|
462
|
+
readout_error: bool = True,
|
|
463
|
+
thermal_relaxation: bool = True,
|
|
464
|
+
temperature: float = 0,
|
|
465
|
+
gate_lengths: Optional[list] = None,
|
|
466
|
+
gate_length_units: str = "ns",
|
|
467
|
+
dt: Optional[float] = None,
|
|
468
|
+
):
|
|
469
|
+
"""Return a noise model derived from a backend properties.
|
|
470
|
+
We assume the structure of the class `BackendProperties` in `qiskit-ibm-runtime`
|
|
471
|
+
|
|
472
|
+
This method basically generates a noise model in the same way as
|
|
473
|
+
:meth:`~.NoiseModel.from_backend`. One small difference is that the ``dt`` option is
|
|
474
|
+
required to be set manually if you want to add thermal relaxation noises to delay
|
|
475
|
+
instructions with durations in ``dt`` time unit. Because it is not supplied by a
|
|
476
|
+
:class:`BackendProperties` object unlike a :class:`Backend` object.
|
|
477
|
+
Note that the resulting noise model is the same as described in
|
|
478
|
+
:meth:`~.NoiseModel.from_backend` so please refer to it for the details.
|
|
479
|
+
|
|
480
|
+
Args:
|
|
481
|
+
backend_properties (BackendProperties): The property of backend.
|
|
482
|
+
We assume the structure of the class `BackendProperties` in `qiskit-ibm-runtime`
|
|
483
|
+
gate_error (Bool): Include depolarizing gate errors (Default: True).
|
|
484
|
+
readout_error (Bool): Include readout errors in model (Default: True).
|
|
485
|
+
thermal_relaxation (Bool): Include thermal relaxation errors (Default: True).
|
|
486
|
+
If no ``t1`` and ``t2`` values are provided (i.e. None) in ``target`` for a qubit,
|
|
487
|
+
an identity ``QuantumError` (i.e. effectively no thermal relaxation error)
|
|
488
|
+
will be added to the qubit even if this flag is set to True.
|
|
489
|
+
If no ``frequency`` is not defined (i.e. None) in ``target`` for a qubit,
|
|
490
|
+
no excitation is considered in the thermal relaxation error on the qubit
|
|
491
|
+
even with non-zero ``temperature``.
|
|
492
|
+
temperature (double): qubit temperature in milli-Kelvin (mK) for
|
|
493
|
+
thermal relaxation errors (Default: 0).
|
|
494
|
+
gate_lengths (Optional[list]): Custom gate times for thermal relaxation errors.
|
|
495
|
+
Used to extend or override the gate times in
|
|
496
|
+
the backend properties (Default: None))
|
|
497
|
+
gate_length_units (str): Time units for gate length values in
|
|
498
|
+
gate_lengths. Can be 'ns', 'ms', 'us',
|
|
499
|
+
or 's' (Default: 'ns').
|
|
500
|
+
dt (Optional[float]): Backend sample time (resolution) in seconds (Default: None).
|
|
501
|
+
Required to convert time unit of durations to seconds
|
|
502
|
+
if including thermal relaxation errors on delay instructions.
|
|
503
|
+
|
|
504
|
+
Returns:
|
|
505
|
+
NoiseModel: An approximate noise model for the device backend.
|
|
506
|
+
|
|
507
|
+
Raises:
|
|
508
|
+
NoiseError: If the input backend properties are not valid.
|
|
509
|
+
"""
|
|
510
|
+
for required_field in ["gates", "qubits", "frequency", "t1", "t2"]:
|
|
511
|
+
if not hasattr(backend_properties, required_field):
|
|
512
|
+
raise NoiseError(
|
|
513
|
+
f"Backend properties are missing the required field '{required_field}'"
|
|
514
|
+
)
|
|
515
|
+
basis_gates = set()
|
|
516
|
+
for prop in backend_properties.gates:
|
|
517
|
+
basis_gates.add(prop.gate)
|
|
518
|
+
basis_gates = list(basis_gates)
|
|
519
|
+
num_qubits = len(backend_properties.qubits)
|
|
520
|
+
noise_model = NoiseModel(basis_gates=basis_gates)
|
|
521
|
+
|
|
522
|
+
# Add single-qubit readout errors
|
|
523
|
+
if readout_error:
|
|
524
|
+
for qubits, error in basic_device_readout_errors(backend_properties):
|
|
525
|
+
noise_model.add_readout_error(error, qubits)
|
|
526
|
+
|
|
527
|
+
gate_errors = basic_device_gate_errors(
|
|
528
|
+
backend_properties,
|
|
529
|
+
gate_error=gate_error,
|
|
530
|
+
thermal_relaxation=thermal_relaxation,
|
|
531
|
+
gate_lengths=gate_lengths,
|
|
532
|
+
gate_length_units=gate_length_units,
|
|
533
|
+
temperature=temperature,
|
|
534
|
+
)
|
|
535
|
+
for name, qubits, error in gate_errors:
|
|
536
|
+
noise_model.add_quantum_error(error, name, qubits)
|
|
537
|
+
|
|
538
|
+
if thermal_relaxation:
|
|
539
|
+
# Add delay errors via RelaxationNiose pass
|
|
540
|
+
try:
|
|
541
|
+
excited_state_populations = [
|
|
542
|
+
_excited_population(
|
|
543
|
+
freq=backend_properties.frequency(q), temperature=temperature
|
|
544
|
+
)
|
|
545
|
+
for q in range(num_qubits)
|
|
546
|
+
]
|
|
547
|
+
except ValueError:
|
|
548
|
+
excited_state_populations = None
|
|
549
|
+
try:
|
|
550
|
+
delay_pass = RelaxationNoisePass(
|
|
551
|
+
t1s=[backend_properties.t1(q) for q in range(num_qubits)],
|
|
552
|
+
t2s=[
|
|
553
|
+
_truncate_t2_value(backend_properties.t1(q), backend_properties.t2(q))
|
|
554
|
+
for q in range(num_qubits)
|
|
555
|
+
],
|
|
556
|
+
dt=dt,
|
|
557
|
+
op_types=Delay,
|
|
558
|
+
excited_state_populations=excited_state_populations,
|
|
559
|
+
)
|
|
560
|
+
noise_model._custom_noise_passes.append(delay_pass)
|
|
561
|
+
except ValueError:
|
|
562
|
+
# Device does not have the required T1 or T2 information
|
|
563
|
+
# in its properties
|
|
564
|
+
pass
|
|
565
|
+
|
|
566
|
+
return noise_model
|
|
567
|
+
|
|
568
|
+
def is_ideal(self): # pylint: disable=too-many-return-statements
|
|
569
|
+
"""Return True if the noise model has no noise terms."""
|
|
570
|
+
# Get default errors
|
|
571
|
+
if self._default_quantum_errors:
|
|
572
|
+
return False
|
|
573
|
+
if self._default_readout_error:
|
|
574
|
+
return False
|
|
575
|
+
if self._local_quantum_errors:
|
|
576
|
+
return False
|
|
577
|
+
if self._local_readout_errors:
|
|
578
|
+
return False
|
|
579
|
+
if self._custom_noise_passes:
|
|
580
|
+
return False
|
|
581
|
+
return True
|
|
582
|
+
|
|
583
|
+
def __repr__(self):
|
|
584
|
+
"""Noise model repr"""
|
|
585
|
+
return "<NoiseModel on {}>".format(list(self._noise_instructions))
|
|
586
|
+
|
|
587
|
+
def __str__(self):
|
|
588
|
+
"""Noise model string representation"""
|
|
589
|
+
|
|
590
|
+
# Check if noise model is ideal
|
|
591
|
+
if self.is_ideal():
|
|
592
|
+
return "NoiseModel: Ideal"
|
|
593
|
+
|
|
594
|
+
# Get default errors
|
|
595
|
+
default_error_ops = []
|
|
596
|
+
for inst in self._default_quantum_errors:
|
|
597
|
+
default_error_ops.append("{}".format(inst))
|
|
598
|
+
if self._default_readout_error is not None:
|
|
599
|
+
if "measure" not in default_error_ops:
|
|
600
|
+
default_error_ops.append("measure")
|
|
601
|
+
|
|
602
|
+
# Get local errors
|
|
603
|
+
local_error_ops = []
|
|
604
|
+
for inst, dic in self._local_quantum_errors.items():
|
|
605
|
+
for qubits in dic.keys():
|
|
606
|
+
local_error_ops.append((inst, qubits))
|
|
607
|
+
for qubits in self._local_readout_errors:
|
|
608
|
+
tmp = ("measure", qubits)
|
|
609
|
+
if tmp not in local_error_ops:
|
|
610
|
+
local_error_ops.append(tmp)
|
|
611
|
+
|
|
612
|
+
output = "NoiseModel:"
|
|
613
|
+
output += "\n Basis gates: {}".format(self.basis_gates)
|
|
614
|
+
if self._noise_instructions:
|
|
615
|
+
output += "\n Instructions with noise: {}".format(list(self._noise_instructions))
|
|
616
|
+
if self._noise_qubits:
|
|
617
|
+
output += "\n Qubits with noise: {}".format(list(self._noise_qubits))
|
|
618
|
+
if default_error_ops:
|
|
619
|
+
output += "\n All-qubits errors: {}".format(default_error_ops)
|
|
620
|
+
if local_error_ops:
|
|
621
|
+
output += "\n Specific qubit errors: {}".format(local_error_ops)
|
|
622
|
+
return output
|
|
623
|
+
|
|
624
|
+
def __eq__(self, other):
|
|
625
|
+
"""Test if two noise models are equal."""
|
|
626
|
+
# This returns True if both noise models have:
|
|
627
|
+
# the same basis_gates
|
|
628
|
+
# the same noise_qubits
|
|
629
|
+
# the same noise_instructions
|
|
630
|
+
if (
|
|
631
|
+
not isinstance(other, NoiseModel)
|
|
632
|
+
or self.basis_gates != other.basis_gates
|
|
633
|
+
or self.noise_qubits != other.noise_qubits
|
|
634
|
+
or self.noise_instructions != other.noise_instructions
|
|
635
|
+
):
|
|
636
|
+
return False
|
|
637
|
+
# Check default readout errors is equal
|
|
638
|
+
if not self._readout_errors_equal(other):
|
|
639
|
+
return False
|
|
640
|
+
# Check quantum errors equal
|
|
641
|
+
if not self._all_qubit_quantum_errors_equal(other):
|
|
642
|
+
return False
|
|
643
|
+
if not self._local_quantum_errors_equal(other):
|
|
644
|
+
return False
|
|
645
|
+
# If we made it here they are equal
|
|
646
|
+
return True
|
|
647
|
+
|
|
648
|
+
def reset(self):
|
|
649
|
+
"""Reset the noise model."""
|
|
650
|
+
self.__init__() # pylint: disable = unnecessary-dunder-call
|
|
651
|
+
|
|
652
|
+
def add_basis_gates(self, instructions):
|
|
653
|
+
"""Add additional gates to the noise model basis_gates.
|
|
654
|
+
|
|
655
|
+
This should be used to add any gates that are identified by a
|
|
656
|
+
custom gate label in the noise model.
|
|
657
|
+
|
|
658
|
+
Args:
|
|
659
|
+
instructions (list[str] or
|
|
660
|
+
list[Instruction]): the instructions error applies to.
|
|
661
|
+
"""
|
|
662
|
+
for name, _ in self._instruction_names_labels(instructions):
|
|
663
|
+
# If the instruction is in the default basis gates for the
|
|
664
|
+
# AerSimulator we add it to the basis gates.
|
|
665
|
+
if name in BASIS_GATES["automatic"]:
|
|
666
|
+
if name not in ["measure", "reset", "initialize", "kraus", "superop", "roerror"]:
|
|
667
|
+
self._basis_gates.add(name)
|
|
668
|
+
|
|
669
|
+
def add_all_qubit_quantum_error(self, error, instructions, warnings=True):
|
|
670
|
+
"""
|
|
671
|
+
Add a quantum error to the noise model that applies to all qubits.
|
|
672
|
+
|
|
673
|
+
Args:
|
|
674
|
+
error (QuantumError): the quantum error object.
|
|
675
|
+
instructions (str or list[str] or
|
|
676
|
+
Instruction or
|
|
677
|
+
list[Instruction]): the instructions error applies to.
|
|
678
|
+
warnings (bool): Display warning if appending to an instruction that
|
|
679
|
+
already has an error (Default: True).
|
|
680
|
+
|
|
681
|
+
Raises:
|
|
682
|
+
NoiseError: if the input parameters are invalid.
|
|
683
|
+
|
|
684
|
+
Additional Information:
|
|
685
|
+
If the error object is ideal it will not be added to the model.
|
|
686
|
+
"""
|
|
687
|
+
# Format input as QuantumError
|
|
688
|
+
if not isinstance(error, BaseQuantumError):
|
|
689
|
+
try:
|
|
690
|
+
error = QuantumError(error)
|
|
691
|
+
except NoiseError as ex:
|
|
692
|
+
raise NoiseError("Input is not a valid quantum error.") from ex
|
|
693
|
+
# Check if error is ideal and if so don't add to the noise model
|
|
694
|
+
if error.ideal():
|
|
695
|
+
return
|
|
696
|
+
|
|
697
|
+
# Add instructions
|
|
698
|
+
for name, label in self._instruction_names_labels(instructions):
|
|
699
|
+
self._check_number_of_qubits(error, name)
|
|
700
|
+
if label in self._default_quantum_errors:
|
|
701
|
+
new_error = self._default_quantum_errors[label].compose(error)
|
|
702
|
+
self._default_quantum_errors[label] = new_error
|
|
703
|
+
if warnings:
|
|
704
|
+
logger.warning(
|
|
705
|
+
"WARNING: all-qubit error already exists for "
|
|
706
|
+
'instruction "%s", '
|
|
707
|
+
"composing with additional error.",
|
|
708
|
+
label,
|
|
709
|
+
)
|
|
710
|
+
else:
|
|
711
|
+
self._default_quantum_errors[label] = error
|
|
712
|
+
# Check if a specific qubit error has been applied for this instruction
|
|
713
|
+
if label in self._local_quantum_errors:
|
|
714
|
+
local_qubits = self._keys2str(self._local_quantum_errors[label].keys())
|
|
715
|
+
if warnings:
|
|
716
|
+
logger.warning(
|
|
717
|
+
"WARNING: all-qubit error for instruction "
|
|
718
|
+
'"%s" will not apply to qubits: '
|
|
719
|
+
"%s as specific error already exists.",
|
|
720
|
+
label,
|
|
721
|
+
local_qubits,
|
|
722
|
+
)
|
|
723
|
+
self._noise_instructions.add(label)
|
|
724
|
+
self.add_basis_gates(name)
|
|
725
|
+
|
|
726
|
+
def add_quantum_error(self, error, instructions, qubits, warnings=True):
|
|
727
|
+
"""
|
|
728
|
+
Add a quantum error to the noise model.
|
|
729
|
+
|
|
730
|
+
Args:
|
|
731
|
+
error (QuantumError): the quantum error object.
|
|
732
|
+
instructions (str or list[str] or
|
|
733
|
+
Instruction or
|
|
734
|
+
list[Instruction]): the instructions error applies to.
|
|
735
|
+
qubits (Sequence[int]): qubits instruction error applies to.
|
|
736
|
+
warnings (bool): Display warning if appending to an instruction that
|
|
737
|
+
already has an error (Default: True).
|
|
738
|
+
|
|
739
|
+
Raises:
|
|
740
|
+
NoiseError: if the input parameters are invalid.
|
|
741
|
+
|
|
742
|
+
Additional Information:
|
|
743
|
+
If the error object is ideal it will not be added to the model.
|
|
744
|
+
"""
|
|
745
|
+
# Error checking
|
|
746
|
+
if not isinstance(error, BaseQuantumError):
|
|
747
|
+
try:
|
|
748
|
+
error = QuantumError(error)
|
|
749
|
+
except NoiseError as ex:
|
|
750
|
+
raise NoiseError("Input is not a valid quantum error.") from ex
|
|
751
|
+
try:
|
|
752
|
+
qubits = tuple(qubits)
|
|
753
|
+
except TypeError as ex:
|
|
754
|
+
raise NoiseError("Qubits must be convertible to a tuple of integers") from ex
|
|
755
|
+
# Check if error is ideal and if so don't add to the noise model
|
|
756
|
+
if error.ideal():
|
|
757
|
+
return
|
|
758
|
+
# Add noise qubits
|
|
759
|
+
for qubit in qubits:
|
|
760
|
+
self._noise_qubits.add(qubit)
|
|
761
|
+
# Add instructions
|
|
762
|
+
for name, label in self._instruction_names_labels(instructions):
|
|
763
|
+
self._check_number_of_qubits(error, name)
|
|
764
|
+
if not isinstance(label, str):
|
|
765
|
+
raise NoiseError("QuantumCircuit invalid instructions.")
|
|
766
|
+
# Check number of qubits is correct for standard instructions
|
|
767
|
+
self._check_number_of_qubits(error, name)
|
|
768
|
+
if label in self._local_quantum_errors:
|
|
769
|
+
qubit_dict = self._local_quantum_errors[label]
|
|
770
|
+
else:
|
|
771
|
+
qubit_dict = {}
|
|
772
|
+
|
|
773
|
+
# Convert qubits list to hashable string
|
|
774
|
+
if error.num_qubits != len(qubits):
|
|
775
|
+
raise NoiseError(
|
|
776
|
+
"Number of qubits ({}) does not match "
|
|
777
|
+
" the error size ({})".format(len(qubits), error.num_qubits)
|
|
778
|
+
)
|
|
779
|
+
if qubits in qubit_dict:
|
|
780
|
+
new_error = qubit_dict[qubits].compose(error)
|
|
781
|
+
qubit_dict[qubits] = new_error
|
|
782
|
+
if warnings:
|
|
783
|
+
logger.warning(
|
|
784
|
+
"WARNING: quantum error already exists for "
|
|
785
|
+
'instruction "%s" on qubits %s '
|
|
786
|
+
", appending additional error.",
|
|
787
|
+
label,
|
|
788
|
+
qubits,
|
|
789
|
+
)
|
|
790
|
+
else:
|
|
791
|
+
qubit_dict[qubits] = error
|
|
792
|
+
# Add updated dictionary
|
|
793
|
+
self._local_quantum_errors[label] = qubit_dict
|
|
794
|
+
|
|
795
|
+
# Check if all-qubit error is already defined for this instruction
|
|
796
|
+
if label in self._default_quantum_errors:
|
|
797
|
+
if warnings:
|
|
798
|
+
logger.warning(
|
|
799
|
+
'WARNING: Specific error for instruction "%s" '
|
|
800
|
+
"on qubits %s overrides previously defined "
|
|
801
|
+
"all-qubit error for these qubits.",
|
|
802
|
+
label,
|
|
803
|
+
qubits,
|
|
804
|
+
)
|
|
805
|
+
self._noise_instructions.add(label)
|
|
806
|
+
self.add_basis_gates(name)
|
|
807
|
+
|
|
808
|
+
def add_all_qubit_readout_error(self, error, warnings=True):
|
|
809
|
+
"""
|
|
810
|
+
Add a single-qubit readout error that applies measure on all qubits.
|
|
811
|
+
|
|
812
|
+
Args:
|
|
813
|
+
error (ReadoutError): the quantum error object.
|
|
814
|
+
warnings (bool): Display warning if appending to an instruction that
|
|
815
|
+
already has an error (Default: True)
|
|
816
|
+
|
|
817
|
+
Raises:
|
|
818
|
+
NoiseError: if the input parameters are invalid.
|
|
819
|
+
|
|
820
|
+
Additional Information:
|
|
821
|
+
If the error object is ideal it will not be added to the model.
|
|
822
|
+
"""
|
|
823
|
+
|
|
824
|
+
# Error checking
|
|
825
|
+
if not isinstance(error, ReadoutError):
|
|
826
|
+
try:
|
|
827
|
+
error = ReadoutError(error)
|
|
828
|
+
except NoiseError as ex:
|
|
829
|
+
raise NoiseError("Input is not a valid readout error.") from ex
|
|
830
|
+
|
|
831
|
+
# Check if error is ideal and if so don't add to the noise model
|
|
832
|
+
if error.ideal():
|
|
833
|
+
return
|
|
834
|
+
|
|
835
|
+
# Check number of qubits is correct for standard instructions
|
|
836
|
+
if error.number_of_qubits != 1:
|
|
837
|
+
raise NoiseError("All-qubit readout errors must defined as single-qubit errors.")
|
|
838
|
+
if self._default_readout_error is not None:
|
|
839
|
+
if warnings:
|
|
840
|
+
logger.warning(
|
|
841
|
+
"WARNING: all-qubit readout error already exists, "
|
|
842
|
+
"overriding with new readout error."
|
|
843
|
+
)
|
|
844
|
+
self._default_readout_error = error
|
|
845
|
+
|
|
846
|
+
# Check if a specific qubit error has been applied for this instruction
|
|
847
|
+
if self._local_readout_errors:
|
|
848
|
+
local_qubits = self._keys2str(self._local_readout_errors.keys())
|
|
849
|
+
if warnings:
|
|
850
|
+
logger.warning(
|
|
851
|
+
"WARNING: The all-qubit readout error will not "
|
|
852
|
+
"apply to measure of qubits qubits: %s "
|
|
853
|
+
"as specific readout errors already exist.",
|
|
854
|
+
local_qubits,
|
|
855
|
+
)
|
|
856
|
+
self._noise_instructions.add("measure")
|
|
857
|
+
|
|
858
|
+
def add_readout_error(self, error, qubits, warnings=True):
|
|
859
|
+
"""
|
|
860
|
+
Add a readout error to the noise model.
|
|
861
|
+
|
|
862
|
+
Args:
|
|
863
|
+
error (ReadoutError): the quantum error object.
|
|
864
|
+
qubits (list[int] or tuple[int]): qubits instruction error applies to.
|
|
865
|
+
warnings (bool): Display warning if appending to an instruction that
|
|
866
|
+
already has an error [Default: True]
|
|
867
|
+
|
|
868
|
+
Raises:
|
|
869
|
+
NoiseError: if the input parameters are invalid.
|
|
870
|
+
|
|
871
|
+
Additional Information:
|
|
872
|
+
If the error object is ideal it will not be added to the model.
|
|
873
|
+
"""
|
|
874
|
+
|
|
875
|
+
# Error checking
|
|
876
|
+
if not isinstance(error, ReadoutError):
|
|
877
|
+
try:
|
|
878
|
+
error = ReadoutError(error)
|
|
879
|
+
except NoiseError as ex:
|
|
880
|
+
raise NoiseError("Input is not a valid readout error.") from ex
|
|
881
|
+
try:
|
|
882
|
+
qubits = tuple(qubits)
|
|
883
|
+
except TypeError as ex:
|
|
884
|
+
raise NoiseError("Qubits must be convertible to a tuple of integers") from ex
|
|
885
|
+
|
|
886
|
+
# Check if error is ideal and if so don't add to the noise model
|
|
887
|
+
if error.ideal():
|
|
888
|
+
return
|
|
889
|
+
|
|
890
|
+
# Add noise qubits
|
|
891
|
+
for qubit in qubits:
|
|
892
|
+
self._noise_qubits.add(qubit)
|
|
893
|
+
|
|
894
|
+
# Check error matches qubit size
|
|
895
|
+
if error.number_of_qubits != len(qubits):
|
|
896
|
+
raise NoiseError(
|
|
897
|
+
"Number of qubits ({}) does not match the readout "
|
|
898
|
+
"error size ({})".format(len(qubits), error.number_of_qubits)
|
|
899
|
+
)
|
|
900
|
+
# Check if we are overriding a previous error
|
|
901
|
+
if qubits in self._local_readout_errors:
|
|
902
|
+
if warnings:
|
|
903
|
+
logger.warning(
|
|
904
|
+
"WARNING: readout error already exists for qubits "
|
|
905
|
+
"%s, overriding with new readout error.",
|
|
906
|
+
qubits,
|
|
907
|
+
)
|
|
908
|
+
self._local_readout_errors[qubits] = error
|
|
909
|
+
|
|
910
|
+
# Check if all-qubit readout error is already defined
|
|
911
|
+
if self._default_readout_error is not None:
|
|
912
|
+
if warnings:
|
|
913
|
+
logger.warning(
|
|
914
|
+
"WARNING: Specific readout error on qubits "
|
|
915
|
+
"%s overrides previously defined "
|
|
916
|
+
"all-qubit readout error for these qubits.",
|
|
917
|
+
qubits,
|
|
918
|
+
)
|
|
919
|
+
self._noise_instructions.add("measure")
|
|
920
|
+
|
|
921
|
+
def to_dict(self, serializable=False):
|
|
922
|
+
"""
|
|
923
|
+
Return the noise model as a dictionary.
|
|
924
|
+
|
|
925
|
+
Args:
|
|
926
|
+
serializable (bool): if `True`, return a dict containing only types
|
|
927
|
+
that can be serializable by the stdlib `json` module.
|
|
928
|
+
|
|
929
|
+
Returns:
|
|
930
|
+
dict: a dictionary for a noise model.
|
|
931
|
+
"""
|
|
932
|
+
error_list = []
|
|
933
|
+
|
|
934
|
+
# Add default quantum errors
|
|
935
|
+
for name, error in self._default_quantum_errors.items():
|
|
936
|
+
error_dict = error.to_dict()
|
|
937
|
+
error_dict["operations"] = [name]
|
|
938
|
+
error_list.append(error_dict)
|
|
939
|
+
|
|
940
|
+
# Add specific qubit errors
|
|
941
|
+
for name, qubit_dict in self._local_quantum_errors.items():
|
|
942
|
+
for qubits, error in qubit_dict.items():
|
|
943
|
+
error_dict = error.to_dict()
|
|
944
|
+
error_dict["operations"] = [name]
|
|
945
|
+
error_dict["gate_qubits"] = [qubits]
|
|
946
|
+
error_list.append(error_dict)
|
|
947
|
+
|
|
948
|
+
# Add default readout error
|
|
949
|
+
if self._default_readout_error is not None:
|
|
950
|
+
error_dict = self._default_readout_error.to_dict()
|
|
951
|
+
error_list.append(error_dict)
|
|
952
|
+
|
|
953
|
+
# Add local readout error
|
|
954
|
+
for qubits, error in self._local_readout_errors.items():
|
|
955
|
+
error_dict = error.to_dict()
|
|
956
|
+
error_dict["gate_qubits"] = [qubits]
|
|
957
|
+
error_list.append(error_dict)
|
|
958
|
+
|
|
959
|
+
ret = {"errors": error_list}
|
|
960
|
+
if serializable:
|
|
961
|
+
ret = json.loads(json.dumps(ret, cls=AerJSONEncoder))
|
|
962
|
+
|
|
963
|
+
return ret
|
|
964
|
+
|
|
965
|
+
@staticmethod
|
|
966
|
+
def from_dict(noise_dict):
|
|
967
|
+
"""
|
|
968
|
+
Load NoiseModel from a dictionary.
|
|
969
|
+
Args:
|
|
970
|
+
noise_dict (dict): A serialized noise model.
|
|
971
|
+
Returns:
|
|
972
|
+
NoiseModel: the noise model.
|
|
973
|
+
Raises:
|
|
974
|
+
NoiseError: if dict cannot be converted to NoiseModel.
|
|
975
|
+
"""
|
|
976
|
+
warn(
|
|
977
|
+
"from_dict has been deprecated as of qiskit-aer 0.15.0"
|
|
978
|
+
" and will be removed no earlier than 3 months from that release date.",
|
|
979
|
+
DeprecationWarning,
|
|
980
|
+
stacklevel=2,
|
|
981
|
+
)
|
|
982
|
+
|
|
983
|
+
def inst_dic_list_to_circuit(dic_list):
|
|
984
|
+
num_qubits = max(max(dic["qubits"]) for dic in dic_list) + 1
|
|
985
|
+
circ = QuantumCircuit(num_qubits)
|
|
986
|
+
for dic in dic_list:
|
|
987
|
+
if dic["name"] == "reset":
|
|
988
|
+
circ.append(Reset(), qargs=dic["qubits"])
|
|
989
|
+
elif dic["name"] == "kraus":
|
|
990
|
+
circ.append(
|
|
991
|
+
Instruction(
|
|
992
|
+
name="kraus",
|
|
993
|
+
num_qubits=len(dic["qubits"]),
|
|
994
|
+
num_clbits=0,
|
|
995
|
+
params=dic["params"],
|
|
996
|
+
),
|
|
997
|
+
qargs=dic["qubits"],
|
|
998
|
+
)
|
|
999
|
+
elif dic["name"] == "unitary":
|
|
1000
|
+
circ.append(UnitaryGate(data=dic["params"][0]), qargs=dic["qubits"])
|
|
1001
|
+
elif dic["name"] == "pauli":
|
|
1002
|
+
circ.append(PauliGate(dic["params"][0]), qargs=dic["qubits"])
|
|
1003
|
+
else:
|
|
1004
|
+
with catch_warnings():
|
|
1005
|
+
filterwarnings(
|
|
1006
|
+
"ignore",
|
|
1007
|
+
category=DeprecationWarning,
|
|
1008
|
+
module="qiskit_aer.noise.errors.errorutils",
|
|
1009
|
+
)
|
|
1010
|
+
circ.append(
|
|
1011
|
+
UnitaryGate(
|
|
1012
|
+
label=dic["name"], data=_standard_gate_unitary(dic["name"])
|
|
1013
|
+
),
|
|
1014
|
+
qargs=dic["qubits"],
|
|
1015
|
+
)
|
|
1016
|
+
return circ
|
|
1017
|
+
|
|
1018
|
+
# Return noise model
|
|
1019
|
+
noise_model = NoiseModel()
|
|
1020
|
+
|
|
1021
|
+
# Get error terms
|
|
1022
|
+
errors = noise_dict.get("errors", [])
|
|
1023
|
+
|
|
1024
|
+
for error in errors:
|
|
1025
|
+
error_type = error["type"]
|
|
1026
|
+
|
|
1027
|
+
# Add QuantumError
|
|
1028
|
+
if error_type == "qerror":
|
|
1029
|
+
circuits = [inst_dic_list_to_circuit(dics) for dics in error["instructions"]]
|
|
1030
|
+
noise_ops = tuple(zip(circuits, error["probabilities"]))
|
|
1031
|
+
qerror = QuantumError(noise_ops)
|
|
1032
|
+
qerror._id = error.get("id", None) or qerror.id
|
|
1033
|
+
instruction_names = error["operations"]
|
|
1034
|
+
all_gate_qubits = error.get("gate_qubits", None)
|
|
1035
|
+
if all_gate_qubits is not None:
|
|
1036
|
+
for gate_qubits in all_gate_qubits:
|
|
1037
|
+
# Add local quantum error
|
|
1038
|
+
noise_model.add_quantum_error(
|
|
1039
|
+
qerror, instruction_names, gate_qubits, warnings=False
|
|
1040
|
+
)
|
|
1041
|
+
else:
|
|
1042
|
+
# Add all-qubit quantum error
|
|
1043
|
+
noise_model.add_all_qubit_quantum_error(
|
|
1044
|
+
qerror, instruction_names, warnings=False
|
|
1045
|
+
)
|
|
1046
|
+
|
|
1047
|
+
# Add ReadoutError
|
|
1048
|
+
elif error_type == "roerror":
|
|
1049
|
+
probabilities = error["probabilities"]
|
|
1050
|
+
all_gate_qubits = error.get("gate_qubits", None)
|
|
1051
|
+
roerror = ReadoutError(probabilities)
|
|
1052
|
+
# Add local readout error
|
|
1053
|
+
if all_gate_qubits is not None:
|
|
1054
|
+
for gate_qubits in all_gate_qubits:
|
|
1055
|
+
noise_model.add_readout_error(roerror, gate_qubits, warnings=False)
|
|
1056
|
+
# Add all-qubit readout error
|
|
1057
|
+
else:
|
|
1058
|
+
noise_model.add_all_qubit_readout_error(roerror, warnings=False)
|
|
1059
|
+
# Invalid error type
|
|
1060
|
+
else:
|
|
1061
|
+
raise NoiseError("Invalid error type: {}".format(error_type))
|
|
1062
|
+
return noise_model
|
|
1063
|
+
|
|
1064
|
+
def _instruction_names_labels(self, instructions):
|
|
1065
|
+
"""Return two lists of instruction name strings and label strings."""
|
|
1066
|
+
if not isinstance(instructions, (list, tuple)):
|
|
1067
|
+
instructions = [instructions]
|
|
1068
|
+
names_labels = []
|
|
1069
|
+
for inst in instructions:
|
|
1070
|
+
# If instruction does not have a label we use the name
|
|
1071
|
+
# as the label
|
|
1072
|
+
if isinstance(inst, Instruction):
|
|
1073
|
+
name = inst.name
|
|
1074
|
+
label = getattr(inst, "label", inst.name)
|
|
1075
|
+
names_labels.append((name, label))
|
|
1076
|
+
elif isinstance(inst, str):
|
|
1077
|
+
names_labels.append((inst, inst))
|
|
1078
|
+
else:
|
|
1079
|
+
raise NoiseError("Invalid instruction type {}".format(inst))
|
|
1080
|
+
return names_labels
|
|
1081
|
+
|
|
1082
|
+
def _check_number_of_qubits(self, error, name):
|
|
1083
|
+
"""
|
|
1084
|
+
Check if error is corrected number of qubits for standard instruction.
|
|
1085
|
+
|
|
1086
|
+
Args:
|
|
1087
|
+
error (QuantumError): the quantum error object.
|
|
1088
|
+
name (str): qobj instruction name to apply error to.
|
|
1089
|
+
|
|
1090
|
+
Raises:
|
|
1091
|
+
NoiseError: If instruction and error qubit number do not match.
|
|
1092
|
+
"""
|
|
1093
|
+
|
|
1094
|
+
def error_message(gate_qubits):
|
|
1095
|
+
msg = (
|
|
1096
|
+
"{} qubit QuantumError".format(error.num_qubits)
|
|
1097
|
+
+ " cannot be applied to {} qubit".format(gate_qubits)
|
|
1098
|
+
+ ' instruction "{}".'.format(name)
|
|
1099
|
+
)
|
|
1100
|
+
return msg
|
|
1101
|
+
|
|
1102
|
+
if name in self._1qubit_instructions and error.num_qubits != 1:
|
|
1103
|
+
raise NoiseError(error_message(1))
|
|
1104
|
+
if name in self._2qubit_instructions and error.num_qubits != 2:
|
|
1105
|
+
raise NoiseError(error_message(2))
|
|
1106
|
+
if name in self._3qubit_instructions and error.num_qubits != 3:
|
|
1107
|
+
raise NoiseError(error_message(3))
|
|
1108
|
+
|
|
1109
|
+
def _keys2str(self, keys):
|
|
1110
|
+
"""Convert dicitonary keys to comma seperated print string."""
|
|
1111
|
+
tmp = "".join(["{}, ".format(key) for key in keys])
|
|
1112
|
+
return tmp[:-2]
|
|
1113
|
+
|
|
1114
|
+
def _readout_errors_equal(self, other):
|
|
1115
|
+
"""Check two noise models have equal readout errors"""
|
|
1116
|
+
# Check default readout error is equal
|
|
1117
|
+
if self._default_readout_error != other._default_readout_error:
|
|
1118
|
+
return False
|
|
1119
|
+
# Check local readout errors are equal
|
|
1120
|
+
if sorted(self._local_readout_errors.keys()) != sorted(other._local_readout_errors.keys()):
|
|
1121
|
+
return False
|
|
1122
|
+
for key, value in self._local_readout_errors.items():
|
|
1123
|
+
if value != other._local_readout_errors[key]:
|
|
1124
|
+
return False
|
|
1125
|
+
return True
|
|
1126
|
+
|
|
1127
|
+
def _all_qubit_quantum_errors_equal(self, other):
|
|
1128
|
+
"""Check two noise models have equal local quantum errors"""
|
|
1129
|
+
if sorted(self._default_quantum_errors.keys()) != sorted(
|
|
1130
|
+
other._default_quantum_errors.keys()
|
|
1131
|
+
):
|
|
1132
|
+
return False
|
|
1133
|
+
for key, value in self._default_quantum_errors.items():
|
|
1134
|
+
if value != other._default_quantum_errors[key]:
|
|
1135
|
+
return False
|
|
1136
|
+
return True
|
|
1137
|
+
|
|
1138
|
+
def _local_quantum_errors_equal(self, other):
|
|
1139
|
+
"""Check two noise models have equal local quantum errors"""
|
|
1140
|
+
if sorted(self._local_quantum_errors.keys()) != sorted(other._local_quantum_errors.keys()):
|
|
1141
|
+
return False
|
|
1142
|
+
for key, value in self._local_quantum_errors.items():
|
|
1143
|
+
inner_dict2 = other._local_quantum_errors[key]
|
|
1144
|
+
if sorted(value.keys()) != sorted(inner_dict2.keys()):
|
|
1145
|
+
return False
|
|
1146
|
+
for inner_key, inner_value in value.items():
|
|
1147
|
+
if inner_value != inner_dict2[inner_key]:
|
|
1148
|
+
return False
|
|
1149
|
+
if value != other._local_quantum_errors[key]:
|
|
1150
|
+
return False
|
|
1151
|
+
return True
|
|
1152
|
+
|
|
1153
|
+
def _pass_manager(self) -> Optional[PassManager]:
|
|
1154
|
+
"""
|
|
1155
|
+
Return the pass manager that add custom noises defined as noise passes
|
|
1156
|
+
(stored in the _custom_noise_passes field). Note that the pass manager
|
|
1157
|
+
does not include passes to add other noises (stored in the different field).
|
|
1158
|
+
"""
|
|
1159
|
+
passes = []
|
|
1160
|
+
passes.extend(self._custom_noise_passes)
|
|
1161
|
+
if len(passes) > 0:
|
|
1162
|
+
return PassManager(passes)
|
|
1163
|
+
return None
|
|
1164
|
+
|
|
1165
|
+
|
|
1166
|
+
def _standard_gate_unitary(name):
|
|
1167
|
+
# To be removed with from_dict
|
|
1168
|
+
unitary_matrices = {
|
|
1169
|
+
("id", "I"): np.eye(2, dtype=complex),
|
|
1170
|
+
("x", "X"): np.array([[0, 1], [1, 0]], dtype=complex),
|
|
1171
|
+
("y", "Y"): np.array([[0, -1j], [1j, 0]], dtype=complex),
|
|
1172
|
+
("z", "Z"): np.array([[1, 0], [0, -1]], dtype=complex),
|
|
1173
|
+
("h", "H"): np.array([[1, 1], [1, -1]], dtype=complex) / np.sqrt(2),
|
|
1174
|
+
("s", "S"): np.array([[1, 0], [0, 1j]], dtype=complex),
|
|
1175
|
+
("sdg", "Sdg"): np.array([[1, 0], [0, -1j]], dtype=complex),
|
|
1176
|
+
("t", "T"): np.array([[1, 0], [0, np.exp(1j * np.pi / 4)]], dtype=complex),
|
|
1177
|
+
("tdg", "Tdg"): np.array([[1, 0], [0, np.exp(-1j * np.pi / 4)]], dtype=complex),
|
|
1178
|
+
("cx", "CX", "cx_01"): np.array(
|
|
1179
|
+
[[1, 0, 0, 0], [0, 0, 0, 1], [0, 0, 1, 0], [0, 1, 0, 0]], dtype=complex
|
|
1180
|
+
),
|
|
1181
|
+
("cx_10",): np.array(
|
|
1182
|
+
[[1, 0, 0, 0], [0, 0, 0, 1], [0, 0, 1, 0], [0, 1, 0, 0]], dtype=complex
|
|
1183
|
+
),
|
|
1184
|
+
("cz", "CZ"): np.array(
|
|
1185
|
+
[[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, -1]], dtype=complex
|
|
1186
|
+
),
|
|
1187
|
+
("swap", "SWAP"): np.array(
|
|
1188
|
+
[[1, 0, 0, 0], [0, 0, 1, 0], [0, 1, 0, 0], [0, 0, 0, 1]], dtype=complex
|
|
1189
|
+
),
|
|
1190
|
+
("ccx", "CCX", "ccx_012", "ccx_102"): np.array(
|
|
1191
|
+
[
|
|
1192
|
+
[1, 0, 0, 0, 0, 0, 0, 0],
|
|
1193
|
+
[0, 1, 0, 0, 0, 0, 0, 0],
|
|
1194
|
+
[0, 0, 1, 0, 0, 0, 0, 0],
|
|
1195
|
+
[0, 0, 0, 0, 0, 0, 0, 1],
|
|
1196
|
+
[0, 0, 0, 0, 1, 0, 0, 0],
|
|
1197
|
+
[0, 0, 0, 0, 0, 1, 0, 0],
|
|
1198
|
+
[0, 0, 0, 0, 0, 0, 1, 0],
|
|
1199
|
+
[0, 0, 0, 1, 0, 0, 0, 0],
|
|
1200
|
+
],
|
|
1201
|
+
dtype=complex,
|
|
1202
|
+
),
|
|
1203
|
+
("ccx_021", "ccx_201"): np.array(
|
|
1204
|
+
[
|
|
1205
|
+
[1, 0, 0, 0, 0, 0, 0, 0],
|
|
1206
|
+
[0, 1, 0, 0, 0, 0, 0, 0],
|
|
1207
|
+
[0, 0, 1, 0, 0, 0, 0, 0],
|
|
1208
|
+
[0, 0, 0, 1, 0, 0, 0, 0],
|
|
1209
|
+
[0, 0, 0, 0, 1, 0, 0, 0],
|
|
1210
|
+
[0, 0, 0, 0, 0, 0, 0, 1],
|
|
1211
|
+
[0, 0, 0, 0, 0, 0, 1, 0],
|
|
1212
|
+
[0, 0, 0, 0, 0, 1, 0, 0],
|
|
1213
|
+
],
|
|
1214
|
+
dtype=complex,
|
|
1215
|
+
),
|
|
1216
|
+
("ccx_120", "ccx_210"): np.array(
|
|
1217
|
+
[
|
|
1218
|
+
[1, 0, 0, 0, 0, 0, 0, 0],
|
|
1219
|
+
[0, 1, 0, 0, 0, 0, 0, 0],
|
|
1220
|
+
[0, 0, 1, 0, 0, 0, 0, 0],
|
|
1221
|
+
[0, 0, 0, 1, 0, 0, 0, 0],
|
|
1222
|
+
[0, 0, 0, 0, 1, 0, 0, 0],
|
|
1223
|
+
[0, 0, 0, 0, 0, 1, 0, 0],
|
|
1224
|
+
[0, 0, 0, 0, 0, 0, 0, 1],
|
|
1225
|
+
[0, 0, 0, 0, 0, 0, 1, 0],
|
|
1226
|
+
],
|
|
1227
|
+
dtype=complex,
|
|
1228
|
+
),
|
|
1229
|
+
}
|
|
1230
|
+
|
|
1231
|
+
return next((value for key, value in unitary_matrices.items() if name in key), None)
|