emu-sv 2.2.0__py3-none-any.whl → 2.3.0__py3-none-any.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.
- emu_sv/__init__.py +1 -1
- emu_sv/custom_callback_implementations.py +33 -8
- emu_sv/dense_operator.py +0 -21
- emu_sv/density_matrix_state.py +7 -3
- emu_sv/lindblad_operator.py +56 -36
- emu_sv/state_vector.py +7 -3
- emu_sv/sv_backend_impl.py +32 -4
- emu_sv/sv_config.py +15 -22
- emu_sv/utils.py +26 -0
- {emu_sv-2.2.0.dist-info → emu_sv-2.3.0.dist-info}/METADATA +2 -2
- emu_sv-2.3.0.dist-info/RECORD +15 -0
- emu_sv-2.2.0.dist-info/RECORD +0 -15
- {emu_sv-2.2.0.dist-info → emu_sv-2.3.0.dist-info}/WHEEL +0 -0
emu_sv/__init__.py
CHANGED
|
@@ -6,12 +6,12 @@ from pulser.backend import (
|
|
|
6
6
|
EnergySecondMoment,
|
|
7
7
|
EnergyVariance,
|
|
8
8
|
Occupation,
|
|
9
|
-
Energy,
|
|
10
9
|
)
|
|
11
10
|
from emu_sv.density_matrix_state import DensityMatrix
|
|
12
11
|
from emu_sv.state_vector import StateVector
|
|
13
12
|
from emu_sv.dense_operator import DenseOperator
|
|
14
13
|
from emu_sv.hamiltonian import RydbergHamiltonian
|
|
14
|
+
from emu_sv.lindblad_operator import RydbergLindbladian
|
|
15
15
|
|
|
16
16
|
dtype = torch.float64
|
|
17
17
|
|
|
@@ -134,6 +134,26 @@ def energy_variance_sv_impl(
|
|
|
134
134
|
return en_var.cpu()
|
|
135
135
|
|
|
136
136
|
|
|
137
|
+
def energy_variance_sv_den_mat_impl(
|
|
138
|
+
self: EnergyVariance,
|
|
139
|
+
*,
|
|
140
|
+
config: EmulationConfig,
|
|
141
|
+
state: DensityMatrix,
|
|
142
|
+
hamiltonian: RydbergLindbladian,
|
|
143
|
+
) -> torch.Tensor:
|
|
144
|
+
"""
|
|
145
|
+
Custom implementation of the energy variance tr(ρH²)-tr(ρH)² for the
|
|
146
|
+
lindblad equation solver.
|
|
147
|
+
"""
|
|
148
|
+
h_dense_matrix = hamiltonian.h_eff(state.matrix) # Hρ
|
|
149
|
+
gpu = state.matrix.is_cuda
|
|
150
|
+
h_squared_dense_mat = hamiltonian.expect(
|
|
151
|
+
DensityMatrix(h_dense_matrix, gpu=gpu)
|
|
152
|
+
) # tr(ρH²)
|
|
153
|
+
en_var: torch.Tensor = h_squared_dense_mat - hamiltonian.expect(state) ** 2 # tr(ρH)²
|
|
154
|
+
return en_var.cpu()
|
|
155
|
+
|
|
156
|
+
|
|
137
157
|
def energy_second_moment_sv_impl(
|
|
138
158
|
self: EnergySecondMoment,
|
|
139
159
|
*,
|
|
@@ -150,15 +170,20 @@ def energy_second_moment_sv_impl(
|
|
|
150
170
|
return en_2_mom.cpu()
|
|
151
171
|
|
|
152
172
|
|
|
153
|
-
def
|
|
154
|
-
self:
|
|
173
|
+
def energy_second_moment_den_mat_impl(
|
|
174
|
+
self: EnergyVariance,
|
|
155
175
|
*,
|
|
156
176
|
config: EmulationConfig,
|
|
157
|
-
state:
|
|
158
|
-
hamiltonian:
|
|
177
|
+
state: DensityMatrix,
|
|
178
|
+
hamiltonian: RydbergLindbladian,
|
|
159
179
|
) -> torch.Tensor:
|
|
160
180
|
"""
|
|
161
|
-
Custom implementation of the energy
|
|
181
|
+
Custom implementation of the second moment of energy tr(ρH²) for the
|
|
182
|
+
lindblad equation solver.
|
|
162
183
|
"""
|
|
163
|
-
|
|
164
|
-
|
|
184
|
+
h_dense_matrix = hamiltonian.h_eff(state.matrix) # Hρ
|
|
185
|
+
gpu = state.matrix.is_cuda
|
|
186
|
+
|
|
187
|
+
return hamiltonian.expect(
|
|
188
|
+
DensityMatrix(h_dense_matrix, gpu=gpu)
|
|
189
|
+
).cpu() # tr(ρH²) = tr(ρ H H)
|
emu_sv/dense_operator.py
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
import itertools
|
|
4
3
|
from functools import reduce
|
|
5
4
|
|
|
6
5
|
import torch
|
|
@@ -20,25 +19,6 @@ from pulser.backend.state import Eigenstate
|
|
|
20
19
|
dtype = torch.complex128
|
|
21
20
|
|
|
22
21
|
|
|
23
|
-
def _validate_operator_targets(operations: FullOp, nqubits: int) -> None:
|
|
24
|
-
"""Check for `operator_for_string` method"""
|
|
25
|
-
for tensorop in operations:
|
|
26
|
-
target_qids = (factor[1] for factor in tensorop[1])
|
|
27
|
-
target_qids_list = list(itertools.chain(*target_qids))
|
|
28
|
-
target_qids_set = set(target_qids_list)
|
|
29
|
-
if len(target_qids_set) < len(target_qids_list):
|
|
30
|
-
# Either the qubit id has been defined twice in an operation:
|
|
31
|
-
for qids in target_qids:
|
|
32
|
-
if len(set(qids)) < len(qids):
|
|
33
|
-
raise ValueError("Duplicate atom ids in argument list.")
|
|
34
|
-
# Or it was defined in two different operations
|
|
35
|
-
raise ValueError("Each qubit can be targeted by only one operation.")
|
|
36
|
-
if max(target_qids_set) >= nqubits:
|
|
37
|
-
raise ValueError(
|
|
38
|
-
"The operation targets more qubits than there are in the register."
|
|
39
|
-
)
|
|
40
|
-
|
|
41
|
-
|
|
42
22
|
class DenseOperator(Operator[complex, torch.Tensor, StateVector]):
|
|
43
23
|
"""DenseOperator in EMU-SV use dense matrices"""
|
|
44
24
|
|
|
@@ -149,7 +129,6 @@ class DenseOperator(Operator[complex, torch.Tensor, StateVector]):
|
|
|
149
129
|
A DenseOperator instance corresponding to the given representation.
|
|
150
130
|
"""
|
|
151
131
|
|
|
152
|
-
_validate_operator_targets(operations, n_qudits)
|
|
153
132
|
assert len(set(eigenstates)) == 2, "Only qubits are supported in EMU-SV."
|
|
154
133
|
|
|
155
134
|
operators_with_tensors: dict[str, torch.Tensor | QuditOp] = dict()
|
emu_sv/density_matrix_state.py
CHANGED
|
@@ -4,7 +4,7 @@ import math
|
|
|
4
4
|
from typing import Mapping, TypeVar, Type, Sequence
|
|
5
5
|
import torch
|
|
6
6
|
from pulser.backend import State
|
|
7
|
-
from emu_base import DEVICE_COUNT
|
|
7
|
+
from emu_base import DEVICE_COUNT, apply_measurement_errors
|
|
8
8
|
from emu_sv.state_vector import StateVector
|
|
9
9
|
from emu_sv.utils import index_to_bitstring
|
|
10
10
|
from pulser.backend.state import Eigenstate
|
|
@@ -176,8 +176,6 @@ class DensityMatrix(State[complex, torch.Tensor]):
|
|
|
176
176
|
Counter({'00': 517, '11': 483})
|
|
177
177
|
"""
|
|
178
178
|
|
|
179
|
-
assert p_false_neg == p_false_pos == 0.0, "Error rates must be 0.0"
|
|
180
|
-
|
|
181
179
|
probabilities = torch.abs(self.matrix.diagonal())
|
|
182
180
|
|
|
183
181
|
outcomes = torch.multinomial(probabilities, num_shots, replacement=True)
|
|
@@ -187,6 +185,12 @@ class DensityMatrix(State[complex, torch.Tensor]):
|
|
|
187
185
|
[index_to_bitstring(self.n_qudits, outcome) for outcome in outcomes]
|
|
188
186
|
)
|
|
189
187
|
|
|
188
|
+
if p_false_neg > 0 or p_false_pos > 0:
|
|
189
|
+
counts = apply_measurement_errors(
|
|
190
|
+
counts,
|
|
191
|
+
p_false_pos=p_false_pos,
|
|
192
|
+
p_false_neg=p_false_neg,
|
|
193
|
+
)
|
|
190
194
|
return counts
|
|
191
195
|
|
|
192
196
|
|
emu_sv/lindblad_operator.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import torch
|
|
2
2
|
from emu_base import compute_noise_from_lindbladians, matmul_2x2_with_batched
|
|
3
|
-
|
|
3
|
+
from emu_sv.density_matrix_state import DensityMatrix
|
|
4
4
|
|
|
5
5
|
dtype = torch.complex128
|
|
6
6
|
sigmax = torch.tensor([[0.0, 1.0], [1.0, 0.0]], dtype=dtype)
|
|
@@ -129,6 +129,52 @@ class RydbergLindbladian:
|
|
|
129
129
|
|
|
130
130
|
return density_matrix.view(orignal_shape)
|
|
131
131
|
|
|
132
|
+
def h_eff(
|
|
133
|
+
self,
|
|
134
|
+
density_matrix: torch.Tensor,
|
|
135
|
+
lindblad_ops: torch.Tensor = torch.zeros(2, 2, dtype=dtype),
|
|
136
|
+
) -> torch.Tensor:
|
|
137
|
+
"""Compute the effective Hamiltonian, Heff = Hρ -0.5i ∑ₖ Lₖ† Lₖ ρ, applied
|
|
138
|
+
to a density matrix ρ.
|
|
139
|
+
- libdlad_ops by default are 2x2 zero matrix"""
|
|
140
|
+
H_den_matrix = torch.zeros_like(density_matrix, dtype=dtype, device=self.device)
|
|
141
|
+
|
|
142
|
+
for qubit in range(len(self.omegas)):
|
|
143
|
+
H_q = self._local_terms_hamiltonian(qubit, lindblad_ops.to(self.device))
|
|
144
|
+
H_den_matrix += self.apply_local_op_to_density_matrix(
|
|
145
|
+
density_matrix, H_q, qubit
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
H_den_matrix += self._apply_interaction_terms(density_matrix)
|
|
149
|
+
return H_den_matrix
|
|
150
|
+
|
|
151
|
+
def _local_terms_hamiltonian(
|
|
152
|
+
self, qubit: int, lindblad_ops: torch.Tensor
|
|
153
|
+
) -> torch.Tensor:
|
|
154
|
+
"""Construct the Hamiltonian for single qubit terms:
|
|
155
|
+
∑ᵢ 𝛺ᵢ /2 𝜎ₓ^ i − 𝛿ⁱ nᵢ + jump operators terms , including the sum
|
|
156
|
+
of Lindblad terms, when 𝜙ᵢ equals to 0.0.
|
|
157
|
+
When 𝜙ᵢ not equals to 0.0:
|
|
158
|
+
∑ᵢ 𝛺ᵢ /2 (cos (𝜙ᵢ) 𝜎ₓ^ i + sin(𝜙ᵢ)𝜎_yⁱ) − 𝛿ⁱ nᵢ + jump operators terms
|
|
159
|
+
"""
|
|
160
|
+
omega = self.omegas[qubit]
|
|
161
|
+
delta = self.deltas[qubit]
|
|
162
|
+
|
|
163
|
+
sigma_x = sigmax.to(device=self.device)
|
|
164
|
+
n = n_op.to(device=self.device)
|
|
165
|
+
|
|
166
|
+
if not self.complex:
|
|
167
|
+
return omega * sigma_x - delta * n + lindblad_ops.to(self.device)
|
|
168
|
+
|
|
169
|
+
phi = self.phis[qubit]
|
|
170
|
+
sigma_y = sigmay.to(device=self.device)
|
|
171
|
+
complex_part = torch.cos(phi) * sigma_x + torch.sin(phi) * sigma_y
|
|
172
|
+
return omega * complex_part - delta * n + lindblad_ops.to(self.device)
|
|
173
|
+
|
|
174
|
+
def _apply_interaction_terms(self, density_matrix: torch.Tensor) -> torch.Tensor:
|
|
175
|
+
"""Apply the interaction terms ∑ᵢⱼ Uᵢⱼ nᵢ nⱼ to the density matrix."""
|
|
176
|
+
return self.diag.view(-1, 1) * density_matrix
|
|
177
|
+
|
|
132
178
|
def __matmul__(self, density_matrix: torch.Tensor) -> torch.Tensor:
|
|
133
179
|
"""Apply the i*RydbergLindbladian operator to the density matrix ρ
|
|
134
180
|
in the following way:
|
|
@@ -142,41 +188,8 @@ class RydbergLindbladian:
|
|
|
142
188
|
sum_lindblad_local = compute_noise_from_lindbladians(self.pulser_linblads).to(
|
|
143
189
|
self.device
|
|
144
190
|
)
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
H_den_matrix = torch.zeros_like(density_matrix, dtype=dtype, device=self.device)
|
|
148
|
-
|
|
149
|
-
if not self.complex:
|
|
150
|
-
for qubit, (omega, delta) in enumerate(zip(self.omegas, self.deltas)):
|
|
151
|
-
H_q = (
|
|
152
|
-
omega * sigmax.to(device=self.device)
|
|
153
|
-
- delta * n_op.to(device=self.device)
|
|
154
|
-
+ sum_lindblad_local
|
|
155
|
-
)
|
|
156
|
-
H_den_matrix += self.apply_local_op_to_density_matrix(
|
|
157
|
-
density_matrix, H_q, qubit
|
|
158
|
-
)
|
|
159
|
-
else:
|
|
160
|
-
for qubit, (omega, delta, phi) in enumerate(
|
|
161
|
-
zip(self.omegas, self.deltas, self.phis)
|
|
162
|
-
):
|
|
163
|
-
H_q = (
|
|
164
|
-
omega
|
|
165
|
-
* (
|
|
166
|
-
(
|
|
167
|
-
torch.cos(phi) * sigmax.to(device=self.device)
|
|
168
|
-
+ torch.sin(phi) * sigmay.to(device=self.device)
|
|
169
|
-
)
|
|
170
|
-
)
|
|
171
|
-
- delta * n_op.to(device=self.device)
|
|
172
|
-
+ sum_lindblad_local
|
|
173
|
-
)
|
|
174
|
-
H_den_matrix += self.apply_local_op_to_density_matrix(
|
|
175
|
-
density_matrix, H_q, qubit
|
|
176
|
-
)
|
|
177
|
-
|
|
178
|
-
# apply the interaction terms ∑ᵢⱼ Uᵢⱼ nᵢ nⱼ
|
|
179
|
-
H_den_matrix += self.diag.view(-1, 1) * density_matrix
|
|
191
|
+
# Heff = Hρ -0.5i ∑ₖ Lₖ† Lₖ ρ
|
|
192
|
+
H_den_matrix = self.h_eff(density_matrix, sum_lindblad_local)
|
|
180
193
|
|
|
181
194
|
# Heff - Heff^†= [H, ρ] - 0.5i ∑ₖ Lₖ† Lₖρ - ρ 0.5i ∑ₖ Lₖ† Lₖρ
|
|
182
195
|
H_den_matrix = H_den_matrix - H_den_matrix.conj().T
|
|
@@ -195,3 +208,10 @@ class RydbergLindbladian:
|
|
|
195
208
|
)
|
|
196
209
|
|
|
197
210
|
return H_den_matrix + 1.0j * L_den_matrix_Ldag
|
|
211
|
+
|
|
212
|
+
def expect(self, state: DensityMatrix) -> torch.Tensor:
|
|
213
|
+
"""Return the energy expectation value E=tr(H𝜌)"""
|
|
214
|
+
en = (self.h_eff(state.matrix)).trace()
|
|
215
|
+
|
|
216
|
+
assert torch.allclose(en.imag, torch.zeros_like(en.imag), atol=1e-8)
|
|
217
|
+
return en.real
|
emu_sv/state_vector.py
CHANGED
|
@@ -8,7 +8,7 @@ import torch
|
|
|
8
8
|
|
|
9
9
|
from emu_sv.utils import index_to_bitstring
|
|
10
10
|
|
|
11
|
-
from emu_base import DEVICE_COUNT
|
|
11
|
+
from emu_base import DEVICE_COUNT, apply_measurement_errors
|
|
12
12
|
from pulser.backend import State
|
|
13
13
|
from pulser.backend.state import Eigenstate
|
|
14
14
|
|
|
@@ -155,7 +155,6 @@ class StateVector(State[complex, torch.Tensor]):
|
|
|
155
155
|
Returns:
|
|
156
156
|
the measured bitstrings, by count
|
|
157
157
|
"""
|
|
158
|
-
assert p_false_neg == p_false_pos == 0.0, "Error rates must be 0.0"
|
|
159
158
|
|
|
160
159
|
probabilities = torch.abs(self.vector) ** 2
|
|
161
160
|
|
|
@@ -166,7 +165,12 @@ class StateVector(State[complex, torch.Tensor]):
|
|
|
166
165
|
[index_to_bitstring(self.n_qudits, outcome) for outcome in outcomes]
|
|
167
166
|
)
|
|
168
167
|
|
|
169
|
-
|
|
168
|
+
if p_false_neg > 0 or p_false_pos > 0:
|
|
169
|
+
counts = apply_measurement_errors(
|
|
170
|
+
counts,
|
|
171
|
+
p_false_pos=p_false_pos,
|
|
172
|
+
p_false_neg=p_false_neg,
|
|
173
|
+
)
|
|
170
174
|
return counts
|
|
171
175
|
|
|
172
176
|
def __add__(self, other: State) -> StateVector:
|
emu_sv/sv_backend_impl.py
CHANGED
|
@@ -10,6 +10,7 @@ from resource import RUSAGE_SELF, getrusage
|
|
|
10
10
|
|
|
11
11
|
from pulser.backend import Results, Observable, State, EmulationConfig
|
|
12
12
|
from emu_base import PulserData
|
|
13
|
+
from emu_base.noise import pick_dark_qubits
|
|
13
14
|
|
|
14
15
|
from emu_sv.state_vector import StateVector
|
|
15
16
|
from emu_sv.density_matrix_state import DensityMatrix
|
|
@@ -77,6 +78,8 @@ class BaseSVBackendImpl:
|
|
|
77
78
|
This class is used to handle the state vector and density matrix evolution.
|
|
78
79
|
"""
|
|
79
80
|
|
|
81
|
+
well_prepared_qubits_filter: typing.Optional[torch.Tensor]
|
|
82
|
+
|
|
80
83
|
def __init__(self, config: SVConfig, pulser_data: PulserData):
|
|
81
84
|
self._config = config
|
|
82
85
|
self._pulser_data = pulser_data
|
|
@@ -86,6 +89,7 @@ class BaseSVBackendImpl:
|
|
|
86
89
|
self.phi = pulser_data.phi
|
|
87
90
|
self.nsteps = pulser_data.omega.shape[0]
|
|
88
91
|
self.nqubits = pulser_data.omega.shape[1]
|
|
92
|
+
self.full_interaction_matrix = pulser_data.full_interaction_matrix
|
|
89
93
|
self.state: State
|
|
90
94
|
self.time = time.time()
|
|
91
95
|
self.results = Results(atom_order=(), total_duration=self.target_times[-1])
|
|
@@ -95,7 +99,6 @@ class BaseSVBackendImpl:
|
|
|
95
99
|
timestep_count=self.nsteps,
|
|
96
100
|
)
|
|
97
101
|
self._current_H: None | RydbergLindbladian | RydbergHamiltonian = None
|
|
98
|
-
|
|
99
102
|
if self._config.initial_state is not None and (
|
|
100
103
|
self._config.initial_state.n_qudits != self.nqubits
|
|
101
104
|
):
|
|
@@ -103,6 +106,31 @@ class BaseSVBackendImpl:
|
|
|
103
106
|
"Mismatch in number of atoms: initial state has "
|
|
104
107
|
f"{self._config.initial_state.n_qudits} and the sequence has {self.nqubits}"
|
|
105
108
|
)
|
|
109
|
+
self.init_dark_qubits()
|
|
110
|
+
|
|
111
|
+
if (
|
|
112
|
+
self._config.initial_state is not None
|
|
113
|
+
and self._config.noise_model.state_prep_error > 0.0
|
|
114
|
+
):
|
|
115
|
+
raise NotImplementedError(
|
|
116
|
+
"Initial state and state preparation error can not be together."
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
def init_dark_qubits(self) -> None:
|
|
120
|
+
if self._config.noise_model.state_prep_error > 0.0:
|
|
121
|
+
self.well_prepared_qubits_filter = pick_dark_qubits(
|
|
122
|
+
self._config.noise_model.state_prep_error, self.nqubits
|
|
123
|
+
)
|
|
124
|
+
else:
|
|
125
|
+
self.well_prepared_qubits_filter = None
|
|
126
|
+
|
|
127
|
+
if self.well_prepared_qubits_filter is not None:
|
|
128
|
+
|
|
129
|
+
self.full_interaction_matrix[self.well_prepared_qubits_filter, :] = 0.0
|
|
130
|
+
self.full_interaction_matrix[:, self.well_prepared_qubits_filter] = 0.0
|
|
131
|
+
self.omega[:, self.well_prepared_qubits_filter] = 0.0
|
|
132
|
+
self.delta[:, self.well_prepared_qubits_filter] = 0.0
|
|
133
|
+
self.phi[:, self.well_prepared_qubits_filter] = 0.0
|
|
106
134
|
|
|
107
135
|
def step(self, step_idx: int) -> None:
|
|
108
136
|
"""One step of the evolution"""
|
|
@@ -153,7 +181,7 @@ class SVBackendImpl(BaseSVBackendImpl):
|
|
|
153
181
|
|
|
154
182
|
def __init__(self, config: SVConfig, pulser_data: PulserData):
|
|
155
183
|
"""
|
|
156
|
-
For running sequences without noise. The state will evolve
|
|
184
|
+
For running sequences without noise. The state will evolve according
|
|
157
185
|
to e^(-iH t)
|
|
158
186
|
|
|
159
187
|
Args:
|
|
@@ -179,7 +207,7 @@ class SVBackendImpl(BaseSVBackendImpl):
|
|
|
179
207
|
self.omega[step_idx],
|
|
180
208
|
self.delta[step_idx],
|
|
181
209
|
self.phi[step_idx],
|
|
182
|
-
self.
|
|
210
|
+
self.full_interaction_matrix,
|
|
183
211
|
self.state.vector,
|
|
184
212
|
self._config.krylov_tolerance,
|
|
185
213
|
)
|
|
@@ -216,7 +244,7 @@ class NoisySVBackendImpl(BaseSVBackendImpl):
|
|
|
216
244
|
self.omega[step_idx],
|
|
217
245
|
self.delta[step_idx],
|
|
218
246
|
self.phi[step_idx],
|
|
219
|
-
self.
|
|
247
|
+
self.full_interaction_matrix,
|
|
220
248
|
self.state.matrix,
|
|
221
249
|
self._config.krylov_tolerance,
|
|
222
250
|
self.pulser_lindblads,
|
emu_sv/sv_config.py
CHANGED
|
@@ -5,13 +5,17 @@ import sys
|
|
|
5
5
|
from types import MethodType
|
|
6
6
|
from typing import Any, ClassVar
|
|
7
7
|
|
|
8
|
+
from emu_sv.utils import choose
|
|
9
|
+
|
|
8
10
|
from emu_sv.custom_callback_implementations import (
|
|
11
|
+
qubit_occupation_sv_impl,
|
|
12
|
+
qubit_occupation_sv_den_mat_impl,
|
|
9
13
|
correlation_matrix_sv_impl,
|
|
10
14
|
correlation_matrix_sv_den_mat_impl,
|
|
11
15
|
energy_second_moment_sv_impl,
|
|
16
|
+
energy_second_moment_den_mat_impl,
|
|
12
17
|
energy_variance_sv_impl,
|
|
13
|
-
|
|
14
|
-
qubit_occupation_sv_den_mat_impl,
|
|
18
|
+
energy_variance_sv_den_mat_impl,
|
|
15
19
|
)
|
|
16
20
|
|
|
17
21
|
from pulser.backend import (
|
|
@@ -103,10 +107,6 @@ class SVConfig(EmulationConfig):
|
|
|
103
107
|
"Warning: The runs and samples_per_run "
|
|
104
108
|
"values of the NoiseModel are ignored!"
|
|
105
109
|
)
|
|
106
|
-
if "SPAM" in self.noise_model.noise_types:
|
|
107
|
-
raise NotImplementedError(
|
|
108
|
-
"SPAM errors are currently not supported in emu-sv."
|
|
109
|
-
)
|
|
110
110
|
|
|
111
111
|
def _expected_kwargs(self) -> set[str]:
|
|
112
112
|
return super()._expected_kwargs() | {
|
|
@@ -127,34 +127,27 @@ class SVConfig(EmulationConfig):
|
|
|
127
127
|
|
|
128
128
|
if isinstance(obs, Occupation):
|
|
129
129
|
obs_copy.apply = MethodType( # type: ignore[method-assign]
|
|
130
|
-
(
|
|
131
|
-
qubit_occupation_sv_impl
|
|
132
|
-
if self.noise_model.noise_types == ()
|
|
133
|
-
else qubit_occupation_sv_den_mat_impl
|
|
134
|
-
),
|
|
130
|
+
choose(qubit_occupation_sv_impl, qubit_occupation_sv_den_mat_impl),
|
|
135
131
|
obs_copy,
|
|
136
132
|
)
|
|
137
133
|
if isinstance(obs, CorrelationMatrix):
|
|
138
134
|
obs_copy.apply = MethodType( # type: ignore[method-assign]
|
|
139
|
-
(
|
|
140
|
-
correlation_matrix_sv_impl
|
|
141
|
-
if self.noise_model.noise_types == ()
|
|
142
|
-
else correlation_matrix_sv_den_mat_impl
|
|
135
|
+
choose(
|
|
136
|
+
correlation_matrix_sv_impl, correlation_matrix_sv_den_mat_impl
|
|
143
137
|
),
|
|
144
138
|
obs_copy,
|
|
145
139
|
)
|
|
146
140
|
if isinstance(obs, EnergyVariance):
|
|
147
|
-
if self.noise_model.noise_types != ():
|
|
148
|
-
raise Exception("Not implemented for density matrix")
|
|
149
141
|
obs_copy.apply = MethodType( # type: ignore[method-assign]
|
|
150
|
-
energy_variance_sv_impl,
|
|
142
|
+
choose(energy_variance_sv_impl, energy_variance_sv_den_mat_impl),
|
|
143
|
+
obs_copy,
|
|
151
144
|
)
|
|
152
145
|
elif isinstance(obs, EnergySecondMoment):
|
|
153
|
-
if self.noise_model.noise_types != ():
|
|
154
|
-
raise Exception("Not implemented for density matrix")
|
|
155
|
-
|
|
156
146
|
obs_copy.apply = MethodType( # type: ignore[method-assign]
|
|
157
|
-
|
|
147
|
+
choose(
|
|
148
|
+
energy_second_moment_sv_impl, energy_second_moment_den_mat_impl
|
|
149
|
+
),
|
|
150
|
+
obs_copy,
|
|
158
151
|
)
|
|
159
152
|
obs_list.append(obs_copy)
|
|
160
153
|
self.observables = tuple(obs_list)
|
emu_sv/utils.py
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
from typing import Callable
|
|
2
|
+
|
|
3
|
+
from pyparsing import Any
|
|
4
|
+
|
|
5
|
+
|
|
1
6
|
def index_to_bitstring(nqubits: int, index: int) -> str:
|
|
2
7
|
"""
|
|
3
8
|
Convert an integer index into its corresponding bitstring representation.
|
|
@@ -6,3 +11,24 @@ def index_to_bitstring(nqubits: int, index: int) -> str:
|
|
|
6
11
|
msg = f"index {index} can not exceed Hilbert space size d**{nqubits}"
|
|
7
12
|
assert index < 2**nqubits, msg
|
|
8
13
|
return format(index, f"0{nqubits}b")
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def choose(
|
|
17
|
+
state_vector_version: Callable,
|
|
18
|
+
density_matrix_version: Callable,
|
|
19
|
+
) -> Callable:
|
|
20
|
+
"""Returns the observable result function that chooses the correct
|
|
21
|
+
implementation based on the type of state (StateVector or DensityMatrix).
|
|
22
|
+
"""
|
|
23
|
+
from emu_sv.state_vector import StateVector
|
|
24
|
+
from emu_sv.density_matrix_state import DensityMatrix
|
|
25
|
+
|
|
26
|
+
def result(self: Any, *, state: StateVector | DensityMatrix, **kwargs: Any) -> Any:
|
|
27
|
+
if isinstance(state, StateVector):
|
|
28
|
+
return state_vector_version(self, state=state, **kwargs)
|
|
29
|
+
elif isinstance(state, DensityMatrix):
|
|
30
|
+
return density_matrix_version(self, state=state, **kwargs)
|
|
31
|
+
else:
|
|
32
|
+
raise TypeError(f"Unsupported state: {type(state).__name__}")
|
|
33
|
+
|
|
34
|
+
return result
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: emu-sv
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.3.0
|
|
4
4
|
Summary: Pasqal State Vector based pulse emulator built on PyTorch
|
|
5
5
|
Project-URL: Documentation, https://pasqal-io.github.io/emulators/
|
|
6
6
|
Project-URL: Repository, https://github.com/pasqal-io/emulators
|
|
@@ -25,7 +25,7 @@ Classifier: Programming Language :: Python :: 3.10
|
|
|
25
25
|
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
26
26
|
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
27
27
|
Requires-Python: >=3.10
|
|
28
|
-
Requires-Dist: emu-base==2.
|
|
28
|
+
Requires-Dist: emu-base==2.3.0
|
|
29
29
|
Description-Content-Type: text/markdown
|
|
30
30
|
|
|
31
31
|
<div align="center">
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
emu_sv/__init__.py,sha256=BWI-O-rXdn7xncmWEUdxtx7gFbWflcPD2lKcsll6t9w,771
|
|
2
|
+
emu_sv/custom_callback_implementations.py,sha256=_7XLIDzJ-p3DVqz-Jyv0eYbl8nih2x2p-pM4cBCLumA,6367
|
|
3
|
+
emu_sv/dense_operator.py,sha256=AvgntJNwwtf3Wl66CIWSwUezVYV3vignCL24SQQQwQg,5901
|
|
4
|
+
emu_sv/density_matrix_state.py,sha256=BJpFzxjgWQOb_D6dehdA6dRUUQKP1iVccTDrZKicwRE,7198
|
|
5
|
+
emu_sv/hamiltonian.py,sha256=CqNGuWJlO2ZljK47wt130s-5uKiOldQUsC3tjwk1mKA,6106
|
|
6
|
+
emu_sv/lindblad_operator.py,sha256=pgjRNLBcvEM2-qxM8uy9wL74OtrD4A8trQeERi_AXH8,8892
|
|
7
|
+
emu_sv/state_vector.py,sha256=oZeTr44Wt1zYEmPc3yN-Zz8aKxIBTjKA0XM23T8MQkU,9871
|
|
8
|
+
emu_sv/sv_backend.py,sha256=-soOkSEzEBK1dCKnYnbtvYjmNZtZra1_4jP3H1ROOtM,737
|
|
9
|
+
emu_sv/sv_backend_impl.py,sha256=ISls8DGZNHrH3Z7kmrzk3oQU3J8lgBg2V4KG09Zt3jA,9263
|
|
10
|
+
emu_sv/sv_config.py,sha256=J4yb9-Na5hAbShDySzPfbt0WXvL8JpfrTEikTK8nH9s,5943
|
|
11
|
+
emu_sv/time_evolution.py,sha256=_VH4f2RF6lGKzO08WxTYJ5Aw8_pTTMRKcyMnIuxH03I,13382
|
|
12
|
+
emu_sv/utils.py,sha256=t0nMDVo6DF5bQW-vbsyRMCmvkyNxCU-v0Enmns9aOAU,1151
|
|
13
|
+
emu_sv-2.3.0.dist-info/METADATA,sha256=yc7Z-DlF125zrnUeErZ24WrRnFKjbyPxeNAfz6RwjDI,3595
|
|
14
|
+
emu_sv-2.3.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
15
|
+
emu_sv-2.3.0.dist-info/RECORD,,
|
emu_sv-2.2.0.dist-info/RECORD
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
emu_sv/__init__.py,sha256=vVU7zIUsHPgFXUBWMn1lUtkWUg-avdFRqLUrtbmPvDI,771
|
|
2
|
-
emu_sv/custom_callback_implementations.py,sha256=j_G5x-xSnFSyKE81QN_DKoiTaF9JvNWaM1N6k2_t0Oo,5501
|
|
3
|
-
emu_sv/dense_operator.py,sha256=NfgzVpnNitc5ZSM4RlfpAc5Ls2wFPNsTxdeFdhJSg1o,6909
|
|
4
|
-
emu_sv/density_matrix_state.py,sha256=5W_UKIAYHb0k3ryRLQ2dbFUgrb5ju5jceDGAekM2gNE,7035
|
|
5
|
-
emu_sv/hamiltonian.py,sha256=CqNGuWJlO2ZljK47wt130s-5uKiOldQUsC3tjwk1mKA,6106
|
|
6
|
-
emu_sv/lindblad_operator.py,sha256=Rlxh24dXVUAutSrSacNO2ilNVlxKix8pfFt7h2CfVOg,7893
|
|
7
|
-
emu_sv/state_vector.py,sha256=zKHCdgl_eRIOPE4qVKO53ig9UyYTQ7a_guNFXgynU7g,9753
|
|
8
|
-
emu_sv/sv_backend.py,sha256=-soOkSEzEBK1dCKnYnbtvYjmNZtZra1_4jP3H1ROOtM,737
|
|
9
|
-
emu_sv/sv_backend_impl.py,sha256=mdPWBLDwH0q7EEwQTmLNLLx5tycMmsCQbUifIHvciMk,8059
|
|
10
|
-
emu_sv/sv_config.py,sha256=ixMTgDXKll4bXsYtAe4a_9Gng0bhwCgS42KKMwZCFHI,6308
|
|
11
|
-
emu_sv/time_evolution.py,sha256=_VH4f2RF6lGKzO08WxTYJ5Aw8_pTTMRKcyMnIuxH03I,13382
|
|
12
|
-
emu_sv/utils.py,sha256=-axfQ2tqw0C7I9yw-28g7lytyk373DNBjDALh4kLBrM,302
|
|
13
|
-
emu_sv-2.2.0.dist-info/METADATA,sha256=aw71GDNhy2P9-zjXjXszkLyaOY95qCpPkOmG2lAmYZg,3595
|
|
14
|
-
emu_sv-2.2.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
15
|
-
emu_sv-2.2.0.dist-info/RECORD,,
|
|
File without changes
|