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.
Files changed (83) hide show
  1. qiskit_aer/VERSION.txt +1 -0
  2. qiskit_aer/__init__.py +89 -0
  3. qiskit_aer/aererror.py +30 -0
  4. qiskit_aer/aerprovider.py +119 -0
  5. qiskit_aer/backends/__init__.py +20 -0
  6. qiskit_aer/backends/aer_compiler.py +1085 -0
  7. qiskit_aer/backends/aer_simulator.py +1025 -0
  8. qiskit_aer/backends/aerbackend.py +679 -0
  9. qiskit_aer/backends/backend_utils.py +567 -0
  10. qiskit_aer/backends/backendconfiguration.py +395 -0
  11. qiskit_aer/backends/backendproperties.py +590 -0
  12. qiskit_aer/backends/compatibility.py +287 -0
  13. qiskit_aer/backends/controller_wrappers.cp314-win_amd64.pyd +0 -0
  14. qiskit_aer/backends/libopenblas.dll +0 -0
  15. qiskit_aer/backends/name_mapping.py +306 -0
  16. qiskit_aer/backends/qasm_simulator.py +925 -0
  17. qiskit_aer/backends/statevector_simulator.py +330 -0
  18. qiskit_aer/backends/unitary_simulator.py +316 -0
  19. qiskit_aer/jobs/__init__.py +35 -0
  20. qiskit_aer/jobs/aerjob.py +143 -0
  21. qiskit_aer/jobs/utils.py +66 -0
  22. qiskit_aer/library/__init__.py +204 -0
  23. qiskit_aer/library/control_flow_instructions/__init__.py +16 -0
  24. qiskit_aer/library/control_flow_instructions/jump.py +47 -0
  25. qiskit_aer/library/control_flow_instructions/mark.py +30 -0
  26. qiskit_aer/library/control_flow_instructions/store.py +29 -0
  27. qiskit_aer/library/default_qubits.py +44 -0
  28. qiskit_aer/library/instructions_table.csv +21 -0
  29. qiskit_aer/library/save_instructions/__init__.py +44 -0
  30. qiskit_aer/library/save_instructions/save_amplitudes.py +168 -0
  31. qiskit_aer/library/save_instructions/save_clifford.py +63 -0
  32. qiskit_aer/library/save_instructions/save_data.py +129 -0
  33. qiskit_aer/library/save_instructions/save_density_matrix.py +91 -0
  34. qiskit_aer/library/save_instructions/save_expectation_value.py +257 -0
  35. qiskit_aer/library/save_instructions/save_matrix_product_state.py +71 -0
  36. qiskit_aer/library/save_instructions/save_probabilities.py +156 -0
  37. qiskit_aer/library/save_instructions/save_stabilizer.py +70 -0
  38. qiskit_aer/library/save_instructions/save_state.py +79 -0
  39. qiskit_aer/library/save_instructions/save_statevector.py +120 -0
  40. qiskit_aer/library/save_instructions/save_superop.py +62 -0
  41. qiskit_aer/library/save_instructions/save_unitary.py +63 -0
  42. qiskit_aer/library/set_instructions/__init__.py +19 -0
  43. qiskit_aer/library/set_instructions/set_density_matrix.py +78 -0
  44. qiskit_aer/library/set_instructions/set_matrix_product_state.py +83 -0
  45. qiskit_aer/library/set_instructions/set_stabilizer.py +77 -0
  46. qiskit_aer/library/set_instructions/set_statevector.py +78 -0
  47. qiskit_aer/library/set_instructions/set_superop.py +78 -0
  48. qiskit_aer/library/set_instructions/set_unitary.py +78 -0
  49. qiskit_aer/noise/__init__.py +265 -0
  50. qiskit_aer/noise/device/__init__.py +25 -0
  51. qiskit_aer/noise/device/models.py +397 -0
  52. qiskit_aer/noise/device/parameters.py +202 -0
  53. qiskit_aer/noise/errors/__init__.py +30 -0
  54. qiskit_aer/noise/errors/base_quantum_error.py +119 -0
  55. qiskit_aer/noise/errors/pauli_error.py +283 -0
  56. qiskit_aer/noise/errors/pauli_lindblad_error.py +363 -0
  57. qiskit_aer/noise/errors/quantum_error.py +451 -0
  58. qiskit_aer/noise/errors/readout_error.py +355 -0
  59. qiskit_aer/noise/errors/standard_errors.py +498 -0
  60. qiskit_aer/noise/noise_model.py +1231 -0
  61. qiskit_aer/noise/noiseerror.py +30 -0
  62. qiskit_aer/noise/passes/__init__.py +18 -0
  63. qiskit_aer/noise/passes/local_noise_pass.py +160 -0
  64. qiskit_aer/noise/passes/relaxation_noise_pass.py +137 -0
  65. qiskit_aer/primitives/__init__.py +44 -0
  66. qiskit_aer/primitives/estimator.py +751 -0
  67. qiskit_aer/primitives/estimator_v2.py +159 -0
  68. qiskit_aer/primitives/sampler.py +361 -0
  69. qiskit_aer/primitives/sampler_v2.py +256 -0
  70. qiskit_aer/quantum_info/__init__.py +32 -0
  71. qiskit_aer/quantum_info/states/__init__.py +16 -0
  72. qiskit_aer/quantum_info/states/aer_densitymatrix.py +313 -0
  73. qiskit_aer/quantum_info/states/aer_state.py +525 -0
  74. qiskit_aer/quantum_info/states/aer_statevector.py +302 -0
  75. qiskit_aer/utils/__init__.py +44 -0
  76. qiskit_aer/utils/noise_model_inserter.py +66 -0
  77. qiskit_aer/utils/noise_transformation.py +431 -0
  78. qiskit_aer/version.py +86 -0
  79. qiskit_aer-0.17.2.dist-info/METADATA +209 -0
  80. qiskit_aer-0.17.2.dist-info/RECORD +83 -0
  81. qiskit_aer-0.17.2.dist-info/WHEEL +5 -0
  82. qiskit_aer-0.17.2.dist-info/licenses/LICENSE.txt +203 -0
  83. qiskit_aer-0.17.2.dist-info/top_level.txt +1 -0
@@ -0,0 +1,119 @@
1
+ # This code is part of Qiskit.
2
+ #
3
+ # (C) Copyright IBM 2018-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
+ Base quantum error class for Aer noise model
14
+ """
15
+ import copy
16
+ import uuid
17
+ from abc import abstractmethod
18
+
19
+ from qiskit.circuit import QuantumCircuit, Instruction, QuantumRegister
20
+ from qiskit.quantum_info.operators.base_operator import BaseOperator
21
+ from qiskit.quantum_info.operators.channel import Kraus
22
+
23
+
24
+ class BaseQuantumError(BaseOperator):
25
+ """Base quantum error class for Aer noise model"""
26
+
27
+ def __init__(self, num_qubits: int):
28
+ """Base class for a quantum error supported by Aer."""
29
+ # Unique ID for BaseQuantumError
30
+ self._id = uuid.uuid4().hex
31
+ super().__init__(num_qubits=num_qubits)
32
+
33
+ def _repr_name(self) -> str:
34
+ return f"{type(self).__name__}[{self.id}]"
35
+
36
+ def __repr__(self):
37
+ """Display QuantumError."""
38
+ return f"<{self._repr_name()}>"
39
+
40
+ def __hash__(self):
41
+ return hash(self._id)
42
+
43
+ @property
44
+ def id(self): # pylint: disable=invalid-name
45
+ """Return unique ID string for error"""
46
+ return self._id
47
+
48
+ def copy(self):
49
+ """Make a copy of current QuantumError."""
50
+ # pylint: disable=no-value-for-parameter
51
+ # The constructor of subclasses from raw data should be a copy
52
+ return copy.deepcopy(self)
53
+
54
+ def to_instruction(self):
55
+ """Convert the QuantumError to a circuit Instruction."""
56
+ return QuantumChannelInstruction(self)
57
+
58
+ @abstractmethod
59
+ def ideal(self):
60
+ """Return True if this error object is composed only of identity operations.
61
+ Note that the identity check is best effort and up to global phase."""
62
+
63
+ @abstractmethod
64
+ def to_quantumchannel(self):
65
+ """Convert the QuantumError to a SuperOp quantum channel.
66
+ Required to enable SuperOp(QuantumError)."""
67
+
68
+ @abstractmethod
69
+ def to_dict(self):
70
+ """Return the current error as a dictionary."""
71
+
72
+ @abstractmethod
73
+ def compose(self, other, qargs=None, front=False):
74
+ pass
75
+
76
+ @abstractmethod
77
+ def tensor(self, other):
78
+ pass
79
+
80
+ @abstractmethod
81
+ def expand(self, other):
82
+ return other.tensor(self)
83
+
84
+ def __rmul__(self, other):
85
+ raise NotImplementedError(
86
+ f"'{type(self).__name__}' does not support scalar multiplication."
87
+ )
88
+
89
+ def __truediv__(self, other):
90
+ raise NotImplementedError(f"'{type(self).__name__}' does not support division.")
91
+
92
+ def __add__(self, other):
93
+ raise NotImplementedError(f"'{type(self).__name__}' does not support addition.")
94
+
95
+ def __sub__(self, other):
96
+ raise NotImplementedError(f"'{type(self).__name__}' does not support subtraction.")
97
+
98
+ def __neg__(self):
99
+ raise NotImplementedError(f"'{type(self).__name__}' does not support negation.")
100
+
101
+
102
+ class QuantumChannelInstruction(Instruction):
103
+ """Container instruction for adding BaseQuantumError to circuit"""
104
+
105
+ def __init__(self, quantum_error):
106
+ """Initialize a quantum error circuit instruction.
107
+
108
+ Args:
109
+ quantum_error (BaseQuantumError): the error to add as an instruction.
110
+ """
111
+ super().__init__("quantum_channel", quantum_error.num_qubits, 0, [])
112
+ self._quantum_error = quantum_error
113
+
114
+ def _define(self):
115
+ """Allow unrolling to a Kraus instruction"""
116
+ q = QuantumRegister(self.num_qubits, "q")
117
+ qc = QuantumCircuit(q, name=self.name)
118
+ qc._append(Kraus(self._quantum_error).to_instruction(), q, [])
119
+ self.definition = qc
@@ -0,0 +1,283 @@
1
+ # This code is part of Qiskit.
2
+ #
3
+ # (C) Copyright IBM 2018-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
+ """
14
+ Class for representing a Pauli noise channel generated by a Pauli Lindblad dissipator.
15
+ """
16
+
17
+ from __future__ import annotations
18
+ from collections.abc import Sequence
19
+ import numpy as np
20
+
21
+ from qiskit.quantum_info import Pauli, PauliList, SparsePauliOp, SuperOp
22
+ from qiskit.quantum_info.operators.mixins import TolerancesMixin
23
+ from .base_quantum_error import BaseQuantumError
24
+ from .quantum_error import QuantumError
25
+ from ..noiseerror import NoiseError
26
+
27
+
28
+ class PauliError(BaseQuantumError, TolerancesMixin):
29
+ r"""A Pauli channel quantum error.
30
+
31
+ This represents an N-qubit quantum error channel :math:`E(ρ) = \sum_j p_j P_j ρ P_j`
32
+ where :math:`P_j` are N-qubit :class:`~.Pauli` operators.
33
+
34
+ The list of Pauli terms are stored as a :class:`~.PauliList` and can be accessed
35
+ via the :attr:`paulis` attribute. The array of probabilities :math:`p_j` can be
36
+ accessed via the :attr:`probabilities` attribute.
37
+
38
+ .. note::
39
+
40
+ This operator can also represent a non-physical (non-CPTP) channel where some
41
+ probabilities are negative or don't sum to 1. Non-physical operators
42
+ cannot be converted to a :class:`~.QuantumError` or used in an
43
+ :class:`~.AerSimulator` simulation. You can check if an operator is physical
44
+ using the :meth:`is_cptp` method.
45
+ """
46
+
47
+ def __init__(
48
+ self,
49
+ paulis: Sequence[Pauli],
50
+ probabilities: Sequence[float],
51
+ ):
52
+ """Initialize a Pauli error channel.
53
+
54
+ Args:
55
+ paulis: A sequence of Pauli channel terms.
56
+ probabilities: A sequence of the probability for each Pauli channel term.
57
+
58
+ Raises:
59
+ NoiseError: If inputs are invalid.
60
+ """
61
+ self._paulis = PauliList(paulis)
62
+ self._probabilities = np.asarray(probabilities, dtype=float)
63
+ if self._probabilities.shape != (len(self._paulis),):
64
+ raise NoiseError("Input Paulis and probabilities are different lengths.")
65
+ super().__init__(num_qubits=self._paulis.num_qubits)
66
+
67
+ def __repr__(self):
68
+ return f"{type(self).__name__}({self.paulis.to_labels()}, {self.probabilities.tolist()})"
69
+
70
+ def __eq__(self, other):
71
+ # Use BaseOperator eq to check type and shape
72
+ if not super().__eq__(other):
73
+ return False
74
+ lhs = self.simplify()
75
+ rhs = other.simplify()
76
+ if lhs.size != rhs.size:
77
+ return False
78
+ lpaulis, lprobs = sort_paulis(lhs.paulis, lhs.probabilities)
79
+ rpaulis, rprobs = sort_paulis(rhs.paulis, rhs.probabilities)
80
+ return np.allclose(lprobs, rprobs) and lpaulis == rpaulis
81
+
82
+ @property
83
+ def size(self):
84
+ """Return the number of error circuit."""
85
+ return len(self.paulis)
86
+
87
+ @property
88
+ def paulis(self) -> PauliList:
89
+ """Return the Pauli channel error terms"""
90
+ return self._paulis
91
+
92
+ @property
93
+ def probabilities(self) -> np.ndarray:
94
+ """Return the Pauli channel probabilities"""
95
+ return self._probabilities
96
+
97
+ @property
98
+ def settings(self):
99
+ """Settings for IBM RuntimeEncoder JSON encoding"""
100
+ return {
101
+ "paulis": self.paulis,
102
+ "probabilities": self.probabilities,
103
+ }
104
+
105
+ def ideal(self) -> bool:
106
+ """Return True if this error object is composed only of identity operations.
107
+ Note that the identity check is best effort and up to global phase."""
108
+ if not self.is_cptp():
109
+ return False
110
+ non_zero = self.paulis[~np.isclose(self.probabilities, 0)]
111
+ return not (np.any(non_zero.z) or np.any(non_zero.x))
112
+
113
+ def is_cptp(self, atol: float | None = None, rtol: float | None = None) -> bool:
114
+ """Return True if completely-positive trace-preserving (CPTP)."""
115
+ return self.is_cp(atol=atol, rtol=rtol) and self.is_tp(atol=atol, rtol=rtol)
116
+
117
+ def is_tp(self, atol: float | None = None, rtol: float | None = None) -> bool:
118
+ """Test if a channel is trace-preserving (TP)"""
119
+ if atol is None:
120
+ atol = self.atol
121
+ if rtol is None:
122
+ rtol = self.rtol
123
+ return np.isclose(np.sum(self.probabilities), 1, atol=atol, rtol=rtol)
124
+
125
+ def is_cp(self, atol: float | None = None, rtol: float | None = None) -> bool:
126
+ """Test if Choi-matrix is completely-positive (CP)"""
127
+ if atol is None:
128
+ atol = self.atol
129
+ if rtol is None:
130
+ rtol = self.rtol
131
+ neg_probs = self.probabilities[self.probabilities < 0]
132
+ return np.allclose(neg_probs, 0, atol=atol, rtol=rtol)
133
+
134
+ def tensor(self, other: PauliError) -> PauliError:
135
+ if not isinstance(other, PauliError):
136
+ raise NoiseError("other must be a PauliError")
137
+ left = SparsePauliOp(self.paulis, self.probabilities, copy=False, ignore_pauli_phase=True)
138
+ right = SparsePauliOp(
139
+ other.paulis, other.probabilities, copy=False, ignore_pauli_phase=True
140
+ )
141
+ tens = left.tensor(right)
142
+ return PauliError(tens.paulis, tens.coeffs.real)
143
+
144
+ def expand(self, other: PauliError) -> PauliError:
145
+ if not isinstance(other, PauliError):
146
+ raise NoiseError("other must be a PauliError")
147
+ return other.tensor(self)
148
+
149
+ def compose(self, other, qargs=None, front=False) -> PauliError:
150
+ if qargs is None:
151
+ qargs = getattr(other, "qargs", None)
152
+ if not isinstance(other, PauliError):
153
+ raise NoiseError("other must be a PauliError")
154
+
155
+ # This is similar to SparsePauliOp.compose but doesn't need to track
156
+ # phases since it is equivalent to the abeliean Pauli group compose
157
+
158
+ # Validate composition dimensions and qargs match
159
+ self._op_shape.compose(other._op_shape, qargs, front)
160
+
161
+ if qargs is not None:
162
+ x1, z1 = self.paulis.x[:, qargs], self.paulis.z[:, qargs]
163
+ else:
164
+ x1, z1 = self.paulis.x, self.paulis.z
165
+ x2, z2 = other.paulis.x, other.paulis.z
166
+ num_qubits = other.num_qubits
167
+
168
+ x3 = np.logical_xor(x1[:, np.newaxis], x2).reshape((-1, num_qubits))
169
+ z3 = np.logical_xor(z1[:, np.newaxis], z2).reshape((-1, num_qubits))
170
+
171
+ if qargs is None:
172
+ paulis = PauliList.from_symplectic(z3, x3)
173
+ else:
174
+ x4 = np.repeat(self.paulis.x, other.size, axis=0)
175
+ z4 = np.repeat(self.paulis.z, other.size, axis=0)
176
+ x4[:, qargs] = x3
177
+ z4[:, qargs] = z3
178
+ paulis = PauliList.from_symplectic(z4, x4)
179
+
180
+ probabilities = np.multiply.outer(self.probabilities, other.probabilities).ravel()
181
+ return PauliError(paulis, probabilities)
182
+
183
+ def simplify(self, atol: float | None = None, rtol: float | None = None) -> PauliError:
184
+ """Simplify PauliList by combining duplicates and removing zeros.
185
+
186
+ Args:
187
+ atol (float): Optional. Absolute tolerance for checking if
188
+ coefficients are zero (Default: 1e-8).
189
+ rtol (float): Optional. relative tolerance for checking if
190
+ coefficients are zero (Default: 1e-5).
191
+
192
+ Returns:
193
+ SparsePauliOp: the simplified SparsePauliOp operator.
194
+ """
195
+ if atol is None:
196
+ atol = self.atol
197
+ if rtol is None:
198
+ rtol = self.rtol
199
+ simplified = SparsePauliOp(self.paulis, self.probabilities).simplify(atol=atol, rtol=rtol)
200
+ return PauliError(simplified.paulis, simplified.coeffs.real)
201
+
202
+ def to_quantum_error(self) -> "QuantumError":
203
+ """Convert to a general QuantumError object."""
204
+ if not self.is_cptp():
205
+ raise NoiseError("Cannot convert non-CPTP PauliError to a QuantumError")
206
+ return QuantumError(list(zip(self.paulis, self.probabilities)))
207
+
208
+ def to_quantumchannel(self) -> SuperOp:
209
+ """Convert to a dense N-qubit QuantumChannel"""
210
+ # Sum terms as superoperator
211
+ # We could do this more efficiently as a PTM or Chi, but would need
212
+ # to map Pauli terms to integer index.
213
+ chan = SuperOp(np.zeros(2 * [4**self.num_qubits]))
214
+ for pauli, coeff in zip(self.paulis, self.probabilities):
215
+ chan += coeff * SuperOp(pauli)
216
+ return chan
217
+
218
+ def to_dict(self) -> dict:
219
+ """Return the current error as a dictionary."""
220
+ # Assemble noise circuits for Aer simulator
221
+ qubits = list(range(self.num_qubits))
222
+ instructions = [
223
+ [{"name": "pauli", "params": [pauli.to_label()], "qubits": qubits}]
224
+ for pauli in self.paulis
225
+ ]
226
+ # Construct error dict
227
+ error = {
228
+ "type": "qerror",
229
+ "id": self.id,
230
+ "operations": [],
231
+ "instructions": instructions,
232
+ "probabilities": self.probabilities.tolist(),
233
+ }
234
+ return error
235
+
236
+ @staticmethod
237
+ def from_dict(error: dict) -> PauliError:
238
+ """Implement current error from a dictionary."""
239
+ # check if dictionary
240
+ if not isinstance(error, dict):
241
+ raise NoiseError("error is not a dictionary")
242
+ # check expected keys "type, id, operations, instructions, probabilities"
243
+ if (
244
+ ("type" not in error)
245
+ or ("id" not in error)
246
+ or ("operations" not in error)
247
+ or ("instructions" not in error)
248
+ or ("probabilities" not in error)
249
+ ):
250
+ raise NoiseError("error dictionary not containing expected keys")
251
+ instructions = error["instructions"]
252
+ probabilities = error["probabilities"]
253
+ if len(instructions) != len(probabilities):
254
+ raise NoiseError("probabilities not matching with instructions")
255
+ # parse instructions and turn to noise_ops
256
+ paulis = []
257
+ for inst in instructions:
258
+ if len(inst) != 1 or inst[0]["name"] != "pauli":
259
+ raise NoiseError("Invalid PauliError dict")
260
+ paulis.append(inst[0]["params"][0])
261
+
262
+ return PauliError(paulis, probabilities)
263
+
264
+
265
+ def sort_paulis(paulis: PauliList, coeffs: Sequence | None = None) -> tuple[PauliList, Sequence]:
266
+ """Sort terms in a way that can be used for equality checks between simplified error ops"""
267
+ if coeffs is not None and len(coeffs) != len(paulis):
268
+ raise ValueError("paulis and coefffs must have the same length.")
269
+
270
+ # Get packed bigs tableau of Paulis
271
+ # Use numpy sorted and enumerate to implement an argsort of
272
+ # rows based on python tuple sorting
273
+ tableau = np.hstack([paulis.x, paulis.z])
274
+ packed = np.packbits(tableau, axis=1)
275
+ if coeffs is None:
276
+ unsorted = ((*row.tolist(), i) for i, row in enumerate(packed))
277
+ else:
278
+ unsorted = ((*row.tolist(), coeff, i) for i, (row, coeff) in enumerate(zip(packed, coeffs)))
279
+ index = [tup[-1] for tup in sorted(unsorted)]
280
+
281
+ if coeffs is None:
282
+ return paulis[index]
283
+ return paulis[index], coeffs[index]