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,256 @@
1
+ # This code is part of Qiskit.
2
+ #
3
+ # (C) Copyright IBM 2022.
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
+ Sampler V2 class.
15
+ """
16
+
17
+ from __future__ import annotations
18
+
19
+ import warnings
20
+ from collections import defaultdict
21
+ from dataclasses import dataclass, field
22
+ from typing import Iterable
23
+
24
+ import numpy as np
25
+ from qiskit.primitives.backend_sampler_v2 import (
26
+ _analyze_circuit,
27
+ _MeasureInfo,
28
+ _memory_array,
29
+ _samples_to_packed_array,
30
+ )
31
+ from qiskit.primitives.base import BaseSamplerV2
32
+ from qiskit.primitives.containers import (
33
+ BitArray,
34
+ DataBin,
35
+ PrimitiveResult,
36
+ SamplerPubLike,
37
+ SamplerPubResult,
38
+ )
39
+ from qiskit.primitives.containers.sampler_pub import SamplerPub
40
+ from qiskit.primitives.primitive_job import PrimitiveJob
41
+ from qiskit.result import Result
42
+
43
+ from qiskit_aer import AerSimulator
44
+
45
+
46
+ @dataclass
47
+ class Options:
48
+ """Options for :class:`~.SamplerV2`."""
49
+
50
+ backend_options: dict = field(default_factory=dict)
51
+ """backend_options: Options passed to AerSimulator."""
52
+
53
+ run_options: dict = field(default_factory=dict)
54
+ """run_options: Options passed to run."""
55
+
56
+
57
+ class SamplerV2(BaseSamplerV2):
58
+ """
59
+ Aer implementation of SamplerV2 class.
60
+
61
+ Each tuple of ``(circuit, <optional> parameter values, <optional> shots)``, called a sampler
62
+ primitive unified bloc (PUB), produces its own array-valued result. The :meth:`~run` method can
63
+ be given many pubs at once.
64
+
65
+ * ``backend_options``: Options passed to AerSimulator.
66
+ Default: {}.
67
+
68
+ * ``run_options``: Options passed to :meth:`AerSimulator.run`.
69
+ Default: {}.
70
+
71
+ """
72
+
73
+ def __init__(
74
+ self,
75
+ *,
76
+ default_shots: int = 1024,
77
+ seed: int | None = None,
78
+ options: dict | None = None,
79
+ ):
80
+ """
81
+ Args:
82
+ default_shots: The default shots for the sampler if not specified during run.
83
+ seed: The seed for random number generation.
84
+ If None, a random seeded default RNG will be used.
85
+ options:
86
+ the backend options (``backend_options``), and
87
+ the runtime options (``run_options``).
88
+
89
+ """
90
+ self._default_shots = default_shots
91
+ self._seed = seed
92
+
93
+ self._options = Options(**options) if options else Options()
94
+ self._backend = AerSimulator(**self.options.backend_options)
95
+
96
+ @classmethod
97
+ def from_backend(cls, backend, **options):
98
+ """make new sampler that uses external backend"""
99
+ sampler = cls(**options)
100
+ if isinstance(backend, AerSimulator):
101
+ sampler._backend = backend
102
+ else:
103
+ sampler._backend = AerSimulator.from_backend(backend)
104
+ return sampler
105
+
106
+ @property
107
+ def default_shots(self) -> int:
108
+ """Return the default shots"""
109
+ return self._default_shots
110
+
111
+ @property
112
+ def seed(self) -> int | None:
113
+ """Return the seed for random number generation."""
114
+ return self._seed
115
+
116
+ @property
117
+ def options(self) -> Options:
118
+ """Return the options"""
119
+ return self._options
120
+
121
+ def run(
122
+ self, pubs: Iterable[SamplerPubLike], *, shots: int | None = None
123
+ ) -> PrimitiveJob[PrimitiveResult[SamplerPubResult]]:
124
+ if shots is None:
125
+ shots = self._default_shots
126
+ coerced_pubs = [SamplerPub.coerce(pub, shots) for pub in pubs]
127
+ self._validate_pubs(coerced_pubs)
128
+ job = PrimitiveJob(self._run, coerced_pubs)
129
+ job._submit()
130
+ return job
131
+
132
+ def _validate_pubs(self, pubs: list[SamplerPub]):
133
+ for i, pub in enumerate(pubs):
134
+ if len(pub.circuit.cregs) == 0:
135
+ warnings.warn(
136
+ f"The {i}-th pub's circuit has no output classical registers and so the result "
137
+ "will be empty. Did you mean to add measurement instructions?",
138
+ UserWarning,
139
+ )
140
+
141
+ def _run(self, pubs: list[SamplerPub]) -> PrimitiveResult[SamplerPubResult]:
142
+ pub_dict = defaultdict(list)
143
+ # consolidate pubs with the same number of shots
144
+ for i, pub in enumerate(pubs):
145
+ pub_dict[pub.shots].append(i)
146
+
147
+ results = [None] * len(pubs)
148
+ for shots, lst in pub_dict.items():
149
+ # run pubs with the same number of shots at once
150
+ pub_results = self._run_pubs([pubs[i] for i in lst], shots)
151
+ # reconstruct the result of pubs
152
+ for i, pub_result in zip(lst, pub_results):
153
+ results[i] = pub_result
154
+ return PrimitiveResult(results, metadata={"version": 2})
155
+
156
+ def _run_pubs(self, pubs: list[SamplerPub], shots: int) -> list[SamplerPubResult]:
157
+ """Compute results for pubs that all require the same value of ``shots``."""
158
+ circuits = [pub.circuit for pub in pubs]
159
+ parameter_binds = [_convert_parameter_bindings(pub) for pub in pubs]
160
+
161
+ # adjust run_options not to overwrite existings options
162
+ run_options = self.options.run_options.copy()
163
+ for key in ["shots", "parameter_binds", "memory"]:
164
+ if key in run_options:
165
+ del run_options[key]
166
+ if self._seed is not None and "seed_simulator" in run_options:
167
+ del run_options["seed_simulator"]
168
+
169
+ # run circuits
170
+ result = self._backend.run(
171
+ circuits,
172
+ shots=shots,
173
+ seed_simulator=self._seed,
174
+ parameter_binds=parameter_binds,
175
+ memory=True,
176
+ **run_options,
177
+ ).result()
178
+
179
+ result_memory = _prepare_memory(result)
180
+
181
+ # pack memory to an ndarray of uint8
182
+ results = []
183
+ start = 0
184
+ for pub in pubs:
185
+ meas_info, max_num_bytes = _analyze_circuit(pub.circuit)
186
+ p_v = pub.parameter_values
187
+ end = start + p_v.size
188
+ results.append(
189
+ self._postprocess_pub(
190
+ result_memory[start:end],
191
+ shots,
192
+ p_v.shape,
193
+ meas_info,
194
+ max_num_bytes,
195
+ pub.circuit.metadata,
196
+ result.metadata,
197
+ )
198
+ )
199
+ start = end
200
+
201
+ return results
202
+
203
+ def _postprocess_pub(
204
+ self,
205
+ result_memory: list[list[str]],
206
+ shots: int,
207
+ shape: tuple[int, ...],
208
+ meas_info: list[_MeasureInfo],
209
+ max_num_bytes: int,
210
+ circuit_metadata: dict,
211
+ simulator_metadata: dict,
212
+ ) -> SamplerPubResult:
213
+ """Converts the memory data into an array of bit arrays with the shape of the pub."""
214
+ arrays = {
215
+ item.creg_name: np.zeros(shape + (shots, item.num_bytes), dtype=np.uint8)
216
+ for item in meas_info
217
+ }
218
+ memory_array = _memory_array(result_memory, max_num_bytes)
219
+
220
+ for samples, index in zip(memory_array, np.ndindex(*shape)):
221
+ for item in meas_info:
222
+ ary = _samples_to_packed_array(samples, item.num_bits, item.start)
223
+ arrays[item.creg_name][index] = ary
224
+
225
+ meas = {
226
+ item.creg_name: BitArray(arrays[item.creg_name], item.num_bits) for item in meas_info
227
+ }
228
+ return SamplerPubResult(
229
+ DataBin(**meas, shape=shape),
230
+ metadata={
231
+ "shots": shots,
232
+ "circuit_metadata": circuit_metadata,
233
+ "simulator_metadata": simulator_metadata,
234
+ },
235
+ )
236
+
237
+
238
+ def _convert_parameter_bindings(pub: SamplerPub) -> dict:
239
+ circuit = pub.circuit
240
+ parameter_values = pub.parameter_values
241
+ parameter_binds = {}
242
+ param_array = parameter_values.as_array(circuit.parameters)
243
+ parameter_binds = {p: param_array[..., i].ravel() for i, p in enumerate(circuit.parameters)}
244
+ return parameter_binds
245
+
246
+
247
+ def _prepare_memory(result: Result) -> list[list[str]]:
248
+ """There is no split of results due to max_experiments for Aer"""
249
+ lst = []
250
+ for exp in result.results:
251
+ if hasattr(exp.data, "memory") and exp.data.memory:
252
+ lst.append(exp.data.memory)
253
+ else:
254
+ # no measure in a circuit
255
+ lst.append(["0x0"] * exp.shots)
256
+ return lst
@@ -0,0 +1,32 @@
1
+ # This code is part of Qiskit.
2
+ #
3
+ # (C) Copyright IBM 2022.
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
+ =================================================
15
+ Aer Quantum Info (:mod:`qiskit_aer.quantum_info`)
16
+ =================================================
17
+
18
+ .. currentmodule:: qiskit_aer.quantum_info
19
+
20
+ States
21
+ ======
22
+
23
+ .. autosummary::
24
+ :toctree: ../stubs/
25
+
26
+ AerStatevector
27
+ AerDensityMatrix
28
+
29
+ """
30
+
31
+ from .states import AerStatevector
32
+ from .states import AerDensityMatrix
@@ -0,0 +1,16 @@
1
+ # This code is part of Qiskit.
2
+ #
3
+ # (C) Copyright IBM 2022.
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
+ """Aer Quantum States."""
14
+
15
+ from .aer_statevector import AerStatevector
16
+ from .aer_densitymatrix import AerDensityMatrix
@@ -0,0 +1,313 @@
1
+ # This code is part of Qiskit.
2
+ #
3
+ # (C) Copyright IBM 2017, 2019, 2020, 2021, 2022, 2023.
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
+ DensityMatrix quantum state class.
15
+ """
16
+ import copy
17
+ import numpy as np
18
+
19
+ from qiskit.circuit import QuantumCircuit, Instruction
20
+ from qiskit.exceptions import QiskitError
21
+ from qiskit.quantum_info.states import DensityMatrix
22
+ from qiskit.quantum_info.operators.predicates import is_hermitian_matrix
23
+
24
+ from qiskit_aer import AerSimulator
25
+ from .aer_statevector import AerStatevector
26
+ from .aer_state import AerState
27
+ from ...backends.aerbackend import AerError
28
+ from ...backends.backend_utils import BASIS_GATES
29
+
30
+
31
+ class AerDensityMatrix(DensityMatrix):
32
+ """AerDensityMatrix class
33
+ This class inherits :class:`DensityMatrix`.
34
+ """
35
+
36
+ def __init__(self, data, dims=None, **configs):
37
+ """
38
+ Args:
39
+ data (np.array or list or Statevector or AerStatevector or DensityMatrix or
40
+ AerDensityMatrix or QuantumCircuit or qiskit.circuit.Instruction):
41
+ Data from which the densitymatrix can be constructed. This can be either a complex
42
+ vector, another densitymatrix or statevector or a ``QuantumCircuit`` or
43
+ ``Instruction`` (``Operator`` is not supported in the current implementation).
44
+ If the data is a circuit or instruction, the densitymatrix is constructed by
45
+ assuming that all qubits are initialized to the zero state.
46
+ dims (int or tuple or list): Optional. The subsystem dimension of
47
+ the state (See additional information).
48
+ configs (kwargs): configurations of :class:`AerDensityMatrix`. `_aer_state` and `method`
49
+ are valid.
50
+
51
+ Raises:
52
+ AerError: if input data is not valid.
53
+ Additional Information:
54
+ The ``dims`` kwarg is used to ``AerDensityMatrix`` constructor.
55
+ """
56
+ if "_aer_state" in configs:
57
+ self._aer_state = configs.pop("_aer_state")
58
+ else:
59
+ if "method" not in configs:
60
+ configs["method"] = "density_matrix"
61
+ elif configs["method"] != "density_matrix":
62
+ method = configs["method"]
63
+ raise AerError(f"Method {method} is not supported")
64
+ if isinstance(data, (QuantumCircuit, Instruction)):
65
+ data, aer_state = AerDensityMatrix._from_instruction(data, None, configs)
66
+ elif isinstance(data, list):
67
+ data = self._from_1d_array(np.array(data, dtype=complex))
68
+ data, aer_state = AerDensityMatrix._from_ndarray(data, configs)
69
+ elif isinstance(data, np.ndarray):
70
+ data = self._from_1d_array(data)
71
+ data, aer_state = AerDensityMatrix._from_ndarray(data, configs)
72
+ elif isinstance(data, AerDensityMatrix):
73
+ aer_state = data._aer_state
74
+ if dims is None:
75
+ dims = data._op_shape._dims_l
76
+ data = data._data.copy()
77
+ elif isinstance(data, DensityMatrix):
78
+ data, aer_state = AerDensityMatrix._from_ndarray(
79
+ np.array(data.data, dtype=complex), configs
80
+ )
81
+ elif hasattr(data, "to_operator"):
82
+ # If the data object has a 'to_operator' attribute this is given
83
+ # higher preference than the 'to_matrix' method for initializing
84
+ # an Operator object.
85
+ op = data.to_operator()
86
+ data, aer_state = AerDensityMatrix._from_ndarray(op.data, configs)
87
+ if dims is None:
88
+ dims = op.output_dims()
89
+ elif hasattr(data, "to_matrix"):
90
+ # If no 'to_operator' attribute exists we next look for a
91
+ # 'to_matrix' attribute to a matrix that will be cast into
92
+ # a complex numpy matrix.
93
+ data, aer_state = AerDensityMatrix._from_ndarray(
94
+ np.asarray(data.to_matrix(), dtype=complex), configs
95
+ )
96
+ else:
97
+ raise AerError(f"Input data is not supported: type={data.__class__}, data={data}")
98
+
99
+ self._aer_state = aer_state
100
+
101
+ super().__init__(data, dims=dims)
102
+
103
+ self._result = None
104
+ self._configs = configs
105
+
106
+ def seed(self, value=None):
107
+ """Set the seed for the quantum state RNG."""
108
+ if value is None or isinstance(value, int):
109
+ self._aer_state.set_seed(value)
110
+ else:
111
+ raise AerError(f"This seed is not supported: type={value.__class__}, value={value}")
112
+
113
+ def _last_result(self):
114
+ if self._result is None:
115
+ self._result = self._aer_state.last_result()
116
+ return self._result
117
+
118
+ def metadata(self):
119
+ """Return result metadata of an operation that executed lastly."""
120
+ if self._last_result() is None:
121
+ raise AerError("AerState was not used and metadata does not exist.")
122
+ return self._last_result()["metadata"]
123
+
124
+ def __copy__(self):
125
+ return copy.deepcopy(self)
126
+
127
+ def __deepcopy__(self, _memo=None):
128
+ ret = AerDensityMatrix(self._data.copy(), **self._configs)
129
+ ret._op_shape = copy.deepcopy(self._op_shape)
130
+ ret._rng_generator = copy.deepcopy(self._rng_generator)
131
+ return ret
132
+
133
+ def conjugate(self):
134
+ return AerDensityMatrix(np.conj(self._data), dims=self.dims())
135
+
136
+ def tensor(self, other):
137
+ """Return the tensor product state self ⊗ other.
138
+ Args:
139
+ other (AerDensityMatrix): a quantum state object.
140
+ Returns:
141
+ AerDensityMatrix: the tensor product operator self ⊗ other.
142
+ Raises:
143
+ QiskitError: if other is not a quantum state.
144
+ """
145
+ if not isinstance(other, AerDensityMatrix):
146
+ other = AerDensityMatrix(other)
147
+ ret = copy.copy(self)
148
+ ret._data = np.kron(self._data, other._data)
149
+ ret._op_shape = self._op_shape.tensor(other._op_shape)
150
+ return ret
151
+
152
+ def expand(self, other):
153
+ """Return the tensor product state other ⊗ self.
154
+ Args:
155
+ other (AerDensityMatrix): a quantum state object.
156
+ Returns:
157
+ AerDensityMatrix: the tensor product state other ⊗ self.
158
+ Raises:
159
+ QiskitError: if other is not a quantum state.
160
+ """
161
+ if not isinstance(other, AerDensityMatrix):
162
+ other = AerDensityMatrix(other)
163
+ ret = copy.copy(self)
164
+ ret._data = np.kron(other._data, self._data)
165
+ ret._op_shape = self._op_shape.expand(other._op_shape)
166
+ return ret
167
+
168
+ def _add(self, other):
169
+ """Return the linear combination self + other.
170
+ Args:
171
+ other (AerDensityMatrix): a quantum state object.
172
+ Returns:
173
+ AerDensityMatrix: the linear combination self + other.
174
+ Raises:
175
+ QiskitError: if other is not a quantum state, or has
176
+ incompatible dimensions.
177
+ """
178
+ if not isinstance(other, AerDensityMatrix):
179
+ other = AerDensityMatrix(other)
180
+ self._op_shape._validate_add(other._op_shape)
181
+ ret = copy.copy(self)
182
+ ret._data = self.data + other.data
183
+ return ret
184
+
185
+ def sample_memory(self, shots, qargs=None):
186
+ if qargs is None:
187
+ qubits = np.arange(self._aer_state.num_qubits)
188
+ else:
189
+ qubits = np.array(qargs)
190
+
191
+ self._aer_state.close()
192
+ self._aer_state.renew()
193
+ self._aer_state.initialize(self._data, copy=False)
194
+ samples = self._aer_state.sample_memory(qubits, shots)
195
+ self._data = self._aer_state.move_to_ndarray()
196
+ return samples
197
+
198
+ @staticmethod
199
+ def _from_1d_array(data):
200
+ # Convert statevector into a density matrix
201
+ ndim = data.ndim
202
+ shape = data.shape
203
+ if ndim == 2 and shape[0] == shape[1]:
204
+ pass # We good
205
+ elif ndim == 1:
206
+ data = np.outer(data, np.conj(data))
207
+ elif ndim == 2 and shape[1] == 1:
208
+ data = np.reshape(data, shape[0])
209
+ else:
210
+ raise QiskitError("Invalid AerDensityMatrix input: not a square matrix.")
211
+ return data
212
+
213
+ @staticmethod
214
+ def _from_ndarray(init_data, configs):
215
+ aer_state = AerState(method="density_matrix")
216
+
217
+ options = AerSimulator._default_options()
218
+ for config_key, config_value in configs.items():
219
+ if options.get(config_key):
220
+ aer_state.configure(config_key, config_value)
221
+
222
+ if len(init_data) == 0:
223
+ raise AerError("initial data must be larger than 0")
224
+
225
+ num_qubits = int(np.log2(len(init_data)))
226
+
227
+ aer_state.allocate_qubits(num_qubits)
228
+ aer_state.initialize(data=init_data)
229
+
230
+ return aer_state.move_to_ndarray(), aer_state
231
+
232
+ @classmethod
233
+ def from_instruction(cls, instruction):
234
+ return AerDensityMatrix(instruction)
235
+
236
+ @staticmethod
237
+ def _from_instruction(inst, init_data, configs):
238
+ aer_state = AerState(method="density_matrix")
239
+
240
+ for config_key, config_value in configs.items():
241
+ aer_state.configure(config_key, config_value)
242
+
243
+ basis_gates = BASIS_GATES["density_matrix"]
244
+ custom_insts = ["reset", "kraus", "barrier"]
245
+
246
+ aer_state.allocate_qubits(inst.num_qubits)
247
+ num_qubits = inst.num_qubits
248
+
249
+ if init_data is not None:
250
+ aer_state.initialize(data=init_data, copy=True)
251
+ else:
252
+ aer_state.initialize()
253
+
254
+ if isinstance(inst, QuantumCircuit) and inst.global_phase != 0:
255
+ aer_state.apply_global_phase(inst.global_phase)
256
+
257
+ if isinstance(inst, QuantumCircuit):
258
+ AerStatevector._aer_evolve_circuit(
259
+ aer_state, inst, range(num_qubits), basis_gates, custom_insts
260
+ )
261
+ else:
262
+ AerStatevector._aer_evolve_instruction(
263
+ aer_state, inst, range(num_qubits), basis_gates, custom_insts
264
+ )
265
+
266
+ return aer_state.move_to_ndarray(), aer_state
267
+
268
+ def reset(self, qargs=None):
269
+ # Normally, DensityMatrix.reset returns DensityMatrix, which should
270
+ # be converted to AerDensityMatrix if necessary.
271
+ density_matrix = super().reset(qargs=qargs)
272
+ if isinstance(density_matrix, DensityMatrix):
273
+ density_matrix = AerDensityMatrix(density_matrix)
274
+ return density_matrix
275
+
276
+ @classmethod
277
+ def from_label(cls, label):
278
+ return AerDensityMatrix(AerStatevector.from_label(label))
279
+
280
+ @staticmethod
281
+ def from_int(i, dims):
282
+ size = np.prod(dims)
283
+ state = np.zeros((size, size), dtype=complex)
284
+ state[i, i] = 1.0
285
+ return AerDensityMatrix(state, dims=dims)
286
+
287
+ def to_statevector(self, atol=None, rtol=None):
288
+ """Return a statevector from a pure density matrix.
289
+ Args:
290
+ atol (float): Absolute tolerance for checking operation validity.
291
+ rtol (float): Relative tolerance for checking operation validity.
292
+ Returns:
293
+ AerStatevector: The pure density matrix's corresponding statevector.
294
+ Corresponds to the eigenvector of the only non-zero eigenvalue.
295
+ Raises:
296
+ QiskitError: if the state is not pure.
297
+ """
298
+ if atol is None:
299
+ atol = self.atol
300
+ if rtol is None:
301
+ rtol = self.rtol
302
+
303
+ if not is_hermitian_matrix(self.data, atol=atol, rtol=rtol):
304
+ raise QiskitError("Not a valid density matrix (non-hermitian).")
305
+
306
+ evals, evecs = np.linalg.eig(self.data)
307
+
308
+ nonzero_evals = evals[abs(evals) > atol]
309
+ if len(nonzero_evals) != 1 or not np.isclose(nonzero_evals[0], 1, atol=atol, rtol=rtol):
310
+ raise QiskitError("Density matrix is not a pure state")
311
+
312
+ psi = evecs[:, np.argmax(evals)] # eigenvectors returned in columns.
313
+ return AerStatevector(psi)