emu-sv 2.6.0__py3-none-any.whl → 2.7.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/dense_operator.py +19 -2
- emu_sv/density_matrix_state.py +81 -41
- emu_sv/sparse_operator.py +5 -3
- emu_sv/state_vector.py +54 -36
- emu_sv/sv_backend.py +15 -7
- emu_sv/sv_backend_impl.py +66 -33
- emu_sv/sv_config.py +11 -11
- {emu_sv-2.6.0.dist-info → emu_sv-2.7.0.dist-info}/METADATA +2 -2
- emu_sv-2.7.0.dist-info/RECORD +16 -0
- emu_sv-2.6.0.dist-info/RECORD +0 -16
- {emu_sv-2.6.0.dist-info → emu_sv-2.7.0.dist-info}/WHEEL +0 -0
emu_sv/__init__.py
CHANGED
emu_sv/dense_operator.py
CHANGED
|
@@ -20,7 +20,23 @@ dtype = torch.complex128
|
|
|
20
20
|
|
|
21
21
|
|
|
22
22
|
class DenseOperator(Operator[complex, torch.Tensor, StateVector]):
|
|
23
|
-
"""DenseOperator in
|
|
23
|
+
"""DenseOperator in emu-sv uses dense matrices. This class represents a
|
|
24
|
+
quantum operator backed by a dense PyTorch tensor for state-vector
|
|
25
|
+
simulation.
|
|
26
|
+
|
|
27
|
+
Args:
|
|
28
|
+
matrix (torch.Tensor): Square complex tensor of shape (2ⁿ, 2ⁿ)
|
|
29
|
+
representing the operator in the computational basis.
|
|
30
|
+
gpu (bool, optional): If True, place the operator on a CUDA device when
|
|
31
|
+
available. Default: True (and only 1 GPU).
|
|
32
|
+
|
|
33
|
+
Returns:
|
|
34
|
+
DenseOperator: An operator object wrapping the provided matrix.
|
|
35
|
+
|
|
36
|
+
Raises:
|
|
37
|
+
ValueError: If 'matrix' is not a 2-D square tensor.
|
|
38
|
+
RuntimeError: If gpu=True but CUDA is not available (if applicable).
|
|
39
|
+
"""
|
|
24
40
|
|
|
25
41
|
def __init__(
|
|
26
42
|
self,
|
|
@@ -121,7 +137,8 @@ class DenseOperator(Operator[complex, torch.Tensor, StateVector]):
|
|
|
121
137
|
Construct a DenseOperator from an operator representation.
|
|
122
138
|
|
|
123
139
|
Args:
|
|
124
|
-
eigenstates: the eigenstates of the basis to use, e.g. ("r", "g")
|
|
140
|
+
eigenstates: the eigenstates of the basis to use, e.g. ("r", "g")
|
|
141
|
+
or ("0", "1").
|
|
125
142
|
n_qudits: number of qudits in the system.
|
|
126
143
|
operations: which bitstrings make up the state with what weight.
|
|
127
144
|
|
emu_sv/density_matrix_state.py
CHANGED
|
@@ -15,7 +15,28 @@ dtype = torch.complex128
|
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
class DensityMatrix(State[complex, torch.Tensor]):
|
|
18
|
-
"""Represents
|
|
18
|
+
"""Represents an n-qubit density matrix ρ in the computational (|g⟩, |r⟩)
|
|
19
|
+
basis. The input should be a square complex tensor with shape (2ⁿ, 2ⁿ),
|
|
20
|
+
where n is the number of atoms. ρ must be Hermitian, positive semidefinite,
|
|
21
|
+
and has trace 1.
|
|
22
|
+
|
|
23
|
+
Args:
|
|
24
|
+
matrix (torch.Tensor): Square complex tensor of shape (2ⁿ, 2ⁿ),
|
|
25
|
+
Hermitian with trace 1, that represents the state in the
|
|
26
|
+
computational basis.
|
|
27
|
+
gpu (bool, optional): If True, place the operator on a CUDA device when
|
|
28
|
+
available. Default: True.
|
|
29
|
+
|
|
30
|
+
Returns:
|
|
31
|
+
DensityMatrix: A density-matrix wrapper around the provided tensor."
|
|
32
|
+
|
|
33
|
+
Raises:
|
|
34
|
+
ValueError: If matrix is not a square 2D tensor of shape (2ⁿ, 2ⁿ) or
|
|
35
|
+
fails validation (e.g., not Hermitian / trace != 1) if validation
|
|
36
|
+
is performed.
|
|
37
|
+
RuntimeError: If gpu=True but CUDA is not available (if the
|
|
38
|
+
implementation moves tensors to CUDA).
|
|
39
|
+
"""
|
|
19
40
|
|
|
20
41
|
# for the moment no need to check positivity and trace 1
|
|
21
42
|
def __init__(
|
|
@@ -65,12 +86,18 @@ class DensityMatrix(State[complex, torch.Tensor]):
|
|
|
65
86
|
Returns:
|
|
66
87
|
the inner product
|
|
67
88
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
89
|
+
Examples:
|
|
90
|
+
```python
|
|
91
|
+
density_bell_state = 0.5 * torch.tensor([[1, 0, 0, 1], [0, 0, 0, 0],
|
|
92
|
+
[0, 0, 0, 0], [1, 0, 0, 1]],dtype=torch.complex128)
|
|
93
|
+
density_c = DensityMatrix(density_bell_state, gpu=False)
|
|
94
|
+
density_c.overlap(density_c)
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Output:
|
|
98
|
+
```
|
|
99
|
+
tensor(1.+0.j, dtype=torch.complex128)
|
|
100
|
+
```
|
|
74
101
|
"""
|
|
75
102
|
|
|
76
103
|
assert isinstance(
|
|
@@ -86,22 +113,28 @@ class DensityMatrix(State[complex, torch.Tensor]):
|
|
|
86
113
|
|
|
87
114
|
@classmethod
|
|
88
115
|
def from_state_vector(cls, state: StateVector) -> DensityMatrix:
|
|
89
|
-
"""
|
|
116
|
+
"""
|
|
117
|
+
Convert a state vector to a density matrix.
|
|
90
118
|
This function takes a state vector |ψ❭ and returns the corresponding
|
|
91
119
|
density matrix ρ = |ψ❭❬ψ| representing the pure state |ψ❭.
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
120
|
+
|
|
121
|
+
Examples:
|
|
122
|
+
```python
|
|
123
|
+
bell_state_vec = 0.7071 * torch.tensor([1.0, 0.0, 0.0, 1.0j],
|
|
124
|
+
dtype=torch.complex128)
|
|
125
|
+
bell_state = StateVector(bell_state_vec, gpu=False)
|
|
126
|
+
density = DensityMatrix.from_state_vector(bell_state)
|
|
127
|
+
print(density.matrix)
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Output:
|
|
131
|
+
```
|
|
132
|
+
tensor([[0.5000+0.0000j, 0.0000+0.0000j, 0.0000+0.0000j, 0.0000-0.5000j],
|
|
101
133
|
[0.0000+0.0000j, 0.0000+0.0000j, 0.0000+0.0000j, 0.0000+0.0000j],
|
|
102
134
|
[0.0000+0.0000j, 0.0000+0.0000j, 0.0000+0.0000j, 0.0000+0.0000j],
|
|
103
135
|
[0.0000+0.5000j, 0.0000+0.0000j, 0.0000+0.0000j, 0.5000+0.0000j]],
|
|
104
136
|
dtype=torch.complex128)
|
|
137
|
+
```
|
|
105
138
|
"""
|
|
106
139
|
|
|
107
140
|
return cls(
|
|
@@ -116,7 +149,8 @@ class DensityMatrix(State[complex, torch.Tensor]):
|
|
|
116
149
|
n_qudits: int,
|
|
117
150
|
amplitudes: Mapping[str, complex],
|
|
118
151
|
) -> tuple[DensityMatrix, Mapping[str, complex]]:
|
|
119
|
-
"""
|
|
152
|
+
"""
|
|
153
|
+
Transforms a state given by a string into a density matrix.
|
|
120
154
|
|
|
121
155
|
Construct a state from the pulser abstract representation
|
|
122
156
|
https://pulser.readthedocs.io/en/stable/conventions.html
|
|
@@ -124,22 +158,29 @@ class DensityMatrix(State[complex, torch.Tensor]):
|
|
|
124
158
|
Args:
|
|
125
159
|
basis: A tuple containing the basis states (e.g., ('r', 'g')).
|
|
126
160
|
nqubits: the number of qubits.
|
|
127
|
-
strings: A dictionary mapping state strings to complex or floats
|
|
161
|
+
strings: A dictionary mapping state strings to complex or floats
|
|
162
|
+
amplitudes.
|
|
128
163
|
|
|
129
164
|
Returns:
|
|
130
165
|
The resulting state.
|
|
131
166
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
167
|
+
Example:
|
|
168
|
+
```python
|
|
169
|
+
eigenstates = ("r","g")
|
|
170
|
+
n = 2
|
|
171
|
+
dense_mat=DensityMatrix.from_state_amplitudes(eigenstates=eigenstates,
|
|
172
|
+
amplitudes={"rr":1.0,"gg":1.0})
|
|
173
|
+
print(dense_mat.matrix)
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
Output:
|
|
177
|
+
```
|
|
138
178
|
tensor([[0.5000+0.j, 0.0000+0.j, 0.0000+0.j, 0.5000+0.j],
|
|
139
179
|
[0.0000+0.j, 0.0000+0.j, 0.0000+0.j, 0.0000+0.j],
|
|
140
180
|
[0.0000+0.j, 0.0000+0.j, 0.0000+0.j, 0.0000+0.j],
|
|
141
181
|
[0.5000+0.j, 0.0000+0.j, 0.0000+0.j, 0.5000+0.j]],
|
|
142
182
|
dtype=torch.complex128)
|
|
183
|
+
```
|
|
143
184
|
"""
|
|
144
185
|
|
|
145
186
|
state_vector, amplitudes = StateVector._from_state_amplitudes(
|
|
@@ -166,16 +207,21 @@ class DensityMatrix(State[complex, torch.Tensor]):
|
|
|
166
207
|
Returns:
|
|
167
208
|
the measured bitstrings, by count
|
|
168
209
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
210
|
+
Examples:
|
|
211
|
+
```python
|
|
212
|
+
torch.manual_seed(1234)
|
|
213
|
+
from emu_sv import StateVector
|
|
214
|
+
bell_vec = 0.7071 * torch.tensor([1.0, 0.0, 0.0, 1.0j],
|
|
215
|
+
dtype=torch.complex128)
|
|
216
|
+
bell_state_vec = StateVector(bell_vec)
|
|
217
|
+
bell_density = DensityMatrix.from_state_vector(bell_state_vec)
|
|
218
|
+
bell_density.sample(1000)
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
Output:
|
|
222
|
+
```
|
|
223
|
+
Counter({'00': 517, '11': 483})
|
|
224
|
+
```
|
|
179
225
|
"""
|
|
180
226
|
|
|
181
227
|
probabilities = torch.abs(self.matrix.diagonal())
|
|
@@ -194,9 +240,3 @@ class DensityMatrix(State[complex, torch.Tensor]):
|
|
|
194
240
|
p_false_neg=p_false_neg,
|
|
195
241
|
)
|
|
196
242
|
return counts
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
if __name__ == "__main__":
|
|
200
|
-
import doctest
|
|
201
|
-
|
|
202
|
-
doctest.testmod()
|
emu_sv/sparse_operator.py
CHANGED
|
@@ -40,12 +40,14 @@ def sparse_kron(a: torch.Tensor, b: torch.Tensor) -> torch.Tensor:
|
|
|
40
40
|
|
|
41
41
|
|
|
42
42
|
class SparseOperator(Operator[complex, torch.Tensor, StateVector]):
|
|
43
|
-
"""This operator is used to represent a sparse matrix in CSR (Compressed
|
|
44
|
-
format for efficient computation on the
|
|
43
|
+
"""This operator is used to represent a sparse matrix in CSR (Compressed
|
|
44
|
+
Sparse Row) format for efficient computation on the emu-sv emulator
|
|
45
45
|
|
|
46
46
|
Args:
|
|
47
47
|
matrix (torch.Tensor): The CSR matrix representation of the operator.
|
|
48
|
-
|
|
48
|
+
|
|
49
|
+
gpu (bool): If True (by default), run on GPU when available; otherwise
|
|
50
|
+
fall back to CPU. If False, always run on CPU.
|
|
49
51
|
"""
|
|
50
52
|
|
|
51
53
|
def __init__(
|
emu_sv/state_vector.py
CHANGED
|
@@ -26,9 +26,11 @@ class StateVector(State[complex, torch.Tensor]):
|
|
|
26
26
|
manipulation, and measurement. The state vector must have a length
|
|
27
27
|
that is a power of 2, representing 2ⁿ basis states for n qubits.
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
Args:
|
|
30
30
|
vector: 1D tensor representation of a state vector.
|
|
31
31
|
gpu: store the vector on GPU if True, otherwise on CPU
|
|
32
|
+
eigenstates: sequence of eigenstates used as basis only qubit basis are
|
|
33
|
+
supported (default: ('r','g'))
|
|
32
34
|
"""
|
|
33
35
|
|
|
34
36
|
def __init__(
|
|
@@ -88,8 +90,14 @@ class StateVector(State[complex, torch.Tensor]):
|
|
|
88
90
|
The zero state
|
|
89
91
|
|
|
90
92
|
Examples:
|
|
91
|
-
|
|
93
|
+
```python
|
|
94
|
+
StateVector.zero(2,gpu=False)
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Output:
|
|
98
|
+
```
|
|
92
99
|
tensor([0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j], dtype=torch.complex128)
|
|
100
|
+
```
|
|
93
101
|
"""
|
|
94
102
|
|
|
95
103
|
device = "cuda" if gpu and DEVICE_COUNT > 0 else "cpu"
|
|
@@ -109,10 +117,14 @@ class StateVector(State[complex, torch.Tensor]):
|
|
|
109
117
|
The described state
|
|
110
118
|
|
|
111
119
|
Examples:
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
120
|
+
```python
|
|
121
|
+
StateVector.make(2,gpu=False)
|
|
122
|
+
```
|
|
115
123
|
|
|
124
|
+
Output:
|
|
125
|
+
```
|
|
126
|
+
tensor([1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j], dtype=torch.complex128)
|
|
127
|
+
```
|
|
116
128
|
"""
|
|
117
129
|
|
|
118
130
|
result = cls.zero(num_sites=num_sites, gpu=gpu)
|
|
@@ -235,23 +247,27 @@ class StateVector(State[complex, torch.Tensor]):
|
|
|
235
247
|
|
|
236
248
|
Args:
|
|
237
249
|
eigenstates: A tuple containing the basis states (e.g., ('r', 'g')).
|
|
238
|
-
amplitudes: A dictionary mapping state strings to complex or floats
|
|
250
|
+
amplitudes: A dictionary mapping state strings to complex or floats
|
|
251
|
+
amplitudes.
|
|
239
252
|
|
|
240
253
|
Returns:
|
|
241
254
|
The normalised resulting state.
|
|
242
255
|
|
|
243
256
|
Examples:
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
257
|
+
```python
|
|
258
|
+
basis = ("r","g")
|
|
259
|
+
st = StateVector.from_state_amplitudes(
|
|
260
|
+
eigenstates=basis,
|
|
261
|
+
amplitudes={"rr": 1.0, "gg": 1.0}
|
|
262
|
+
)
|
|
263
|
+
print(st)
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
Output:
|
|
267
|
+
```
|
|
253
268
|
tensor([0.7071+0.j, 0.0000+0.j, 0.0000+0.j, 0.7071+0.j],
|
|
254
269
|
dtype=torch.complex128)
|
|
270
|
+
```
|
|
255
271
|
"""
|
|
256
272
|
basis = set(eigenstates)
|
|
257
273
|
if basis == {"r", "g"}:
|
|
@@ -291,22 +307,30 @@ def inner(left: StateVector, right: StateVector) -> torch.Tensor:
|
|
|
291
307
|
the inner product
|
|
292
308
|
|
|
293
309
|
Examples:
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
310
|
+
```python
|
|
311
|
+
factor = math.sqrt(2.0)
|
|
312
|
+
basis = ("r","g")
|
|
313
|
+
string_state1 = {"gg":1.0,"rr":1.0}
|
|
314
|
+
state1 = StateVector.from_state_string(basis=basis,
|
|
315
|
+
nqubits=nqubits,strings=string_state1)
|
|
316
|
+
string_state2 = {"gr":1.0/factor,"rr":1.0/factor}
|
|
317
|
+
state2 = StateVector.from_state_string(basis=basis,
|
|
318
|
+
nqubits=nqubits,strings=string_state2)
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
```python
|
|
322
|
+
state1 = StateVector.from_state_amplitudes(eigenstates=basis,
|
|
323
|
+
amplitudes=string_state1)
|
|
324
|
+
string_state2 = {"gr":1.0/factor,"rr":1.0/factor}
|
|
325
|
+
state2 = StateVector.from_state_amplitudes(eigenstates=basis,
|
|
326
|
+
amplitudes=string_state2)
|
|
327
|
+
inner(state1,state2).item()
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
Output:
|
|
331
|
+
```
|
|
309
332
|
(0.49999999144286444+0j)
|
|
333
|
+
```
|
|
310
334
|
"""
|
|
311
335
|
|
|
312
336
|
assert (left.vector.shape == right.vector.shape) and (left.vector.dim() == 1), (
|
|
@@ -314,9 +338,3 @@ def inner(left: StateVector, right: StateVector) -> torch.Tensor:
|
|
|
314
338
|
" the same and both need to be 1D tesnor",
|
|
315
339
|
)
|
|
316
340
|
return left.inner(right)
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
if __name__ == "__main__":
|
|
320
|
-
import doctest
|
|
321
|
-
|
|
322
|
-
doctest.testmod()
|
emu_sv/sv_backend.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
from pulser.backend import EmulatorBackend
|
|
2
|
-
from pulser.backend import Results
|
|
1
|
+
from pulser.backend import EmulatorBackend, Results, BitStrings
|
|
3
2
|
from emu_sv.sv_config import SVConfig
|
|
4
3
|
from emu_sv.sv_backend_impl import create_impl
|
|
4
|
+
from emu_base import PulserData
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
class SVBackend(EmulatorBackend):
|
|
@@ -9,11 +9,14 @@ class SVBackend(EmulatorBackend):
|
|
|
9
9
|
A backend for emulating Pulser sequences using state vectors and sparse matrices.
|
|
10
10
|
Noisy simulation is supported by solving the Lindblad equation and using effective
|
|
11
11
|
noise channel or jump operators
|
|
12
|
+
|
|
13
|
+
Args:
|
|
14
|
+
config (SVConfig): Configuration for the SV backend.
|
|
12
15
|
"""
|
|
13
16
|
|
|
14
|
-
default_config = SVConfig()
|
|
17
|
+
default_config = SVConfig(observables=[BitStrings(evaluation_times=[1.0])])
|
|
15
18
|
|
|
16
|
-
def run(self) -> Results:
|
|
19
|
+
def run(self) -> Results | list[Results]:
|
|
17
20
|
"""
|
|
18
21
|
Emulates the given sequence.
|
|
19
22
|
|
|
@@ -21,6 +24,11 @@ class SVBackend(EmulatorBackend):
|
|
|
21
24
|
the simulation results
|
|
22
25
|
"""
|
|
23
26
|
assert isinstance(self._config, SVConfig)
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
+
pulser_data = PulserData(
|
|
28
|
+
sequence=self._sequence, config=self._config, dt=self._config.dt
|
|
29
|
+
)
|
|
30
|
+
results = []
|
|
31
|
+
for sequence_data in pulser_data.get_sequences():
|
|
32
|
+
impl = create_impl(sequence_data, self._config)
|
|
33
|
+
results.append(impl._run())
|
|
34
|
+
return Results.aggregate(results)
|
emu_sv/sv_backend_impl.py
CHANGED
|
@@ -2,13 +2,13 @@ from abc import abstractmethod
|
|
|
2
2
|
import time
|
|
3
3
|
import typing
|
|
4
4
|
import torch
|
|
5
|
+
import logging
|
|
5
6
|
|
|
6
7
|
from emu_sv.hamiltonian import RydbergHamiltonian
|
|
7
8
|
from emu_sv.lindblad_operator import RydbergLindbladian
|
|
8
|
-
from pulser import Sequence
|
|
9
9
|
|
|
10
10
|
from pulser.backend import Results, Observable, State, EmulationConfig
|
|
11
|
-
from emu_base import
|
|
11
|
+
from emu_base import SequenceData, get_max_rss
|
|
12
12
|
|
|
13
13
|
from emu_sv.state_vector import StateVector
|
|
14
14
|
from emu_sv.density_matrix_state import DensityMatrix
|
|
@@ -50,8 +50,7 @@ class Statistics(Observable):
|
|
|
50
50
|
or isinstance(state, DensityMatrix)
|
|
51
51
|
and state.matrix.is_cuda
|
|
52
52
|
)
|
|
53
|
-
|
|
54
|
-
config.logger.info(
|
|
53
|
+
logging.getLogger("emulators").info(
|
|
55
54
|
f"step = {len(self.data)}/{self.timestep_count}, "
|
|
56
55
|
+ f"RSS = {max_mem:.3f} MB, "
|
|
57
56
|
+ f"Δt = {duration:.3f} s"
|
|
@@ -70,19 +69,22 @@ class BaseSVBackendImpl:
|
|
|
70
69
|
|
|
71
70
|
well_prepared_qubits_filter: typing.Optional[torch.Tensor]
|
|
72
71
|
|
|
73
|
-
def __init__(self, config: SVConfig,
|
|
72
|
+
def __init__(self, config: SVConfig, data: SequenceData):
|
|
74
73
|
self._config = config
|
|
75
|
-
self.
|
|
76
|
-
self.target_times =
|
|
77
|
-
self.omega =
|
|
78
|
-
self.delta =
|
|
79
|
-
self.phi =
|
|
80
|
-
self.nsteps =
|
|
81
|
-
self.nqubits =
|
|
82
|
-
self.full_interaction_matrix =
|
|
74
|
+
self._data = data
|
|
75
|
+
self.target_times = data.target_times
|
|
76
|
+
self.omega = data.omega
|
|
77
|
+
self.delta = data.delta
|
|
78
|
+
self.phi = data.phi
|
|
79
|
+
self.nsteps = data.omega.shape[0]
|
|
80
|
+
self.nqubits = data.omega.shape[1]
|
|
81
|
+
self.full_interaction_matrix = data.full_interaction_matrix
|
|
83
82
|
self.state: State
|
|
84
83
|
self.time = time.time()
|
|
85
|
-
self.results = Results(
|
|
84
|
+
self.results = Results(
|
|
85
|
+
atom_order=data.qubit_ids,
|
|
86
|
+
total_duration=int(self.target_times[-1]),
|
|
87
|
+
)
|
|
86
88
|
self.statistics = Statistics(
|
|
87
89
|
evaluation_times=[t / self.target_times[-1] for t in self.target_times],
|
|
88
90
|
data=[],
|
|
@@ -100,7 +102,7 @@ class BaseSVBackendImpl:
|
|
|
100
102
|
|
|
101
103
|
if (
|
|
102
104
|
self._config.initial_state is not None
|
|
103
|
-
and self.
|
|
105
|
+
and self._data.noise_model.state_prep_error > 0.0
|
|
104
106
|
):
|
|
105
107
|
raise NotImplementedError(
|
|
106
108
|
"Initial state and state preparation error can not be together."
|
|
@@ -112,10 +114,10 @@ class BaseSVBackendImpl:
|
|
|
112
114
|
self.resolved_gpu = requested_gpu
|
|
113
115
|
|
|
114
116
|
def init_dark_qubits(self) -> None:
|
|
115
|
-
if self.
|
|
116
|
-
bad_atoms = self.
|
|
117
|
+
if self._data.noise_model.state_prep_error > 0.0:
|
|
118
|
+
bad_atoms = self._data.bad_atoms
|
|
117
119
|
self.well_prepared_qubits_filter = torch.tensor(
|
|
118
|
-
[bool(bad_atoms[x]) for x in self.
|
|
120
|
+
[bool(bad_atoms[x]) for x in self._data.qubit_ids]
|
|
119
121
|
)
|
|
120
122
|
else:
|
|
121
123
|
self.well_prepared_qubits_filter = None
|
|
@@ -132,6 +134,7 @@ class BaseSVBackendImpl:
|
|
|
132
134
|
"""One step of the evolution"""
|
|
133
135
|
dt = self._compute_dt(step_idx)
|
|
134
136
|
self._evolve_step(dt, step_idx)
|
|
137
|
+
step_idx += 1
|
|
135
138
|
self._apply_observables(step_idx)
|
|
136
139
|
self._save_statistics(step_idx)
|
|
137
140
|
|
|
@@ -142,9 +145,38 @@ class BaseSVBackendImpl:
|
|
|
142
145
|
def _evolve_step(self, dt: float, step_idx: int) -> None:
|
|
143
146
|
"""One step evolution"""
|
|
144
147
|
|
|
148
|
+
def _is_evaluation_time(
|
|
149
|
+
self,
|
|
150
|
+
observable: Observable,
|
|
151
|
+
t: float,
|
|
152
|
+
tolerance: float = 1e-10,
|
|
153
|
+
) -> bool:
|
|
154
|
+
"""Return True if ``t`` is a genuine sampling time for this observable.
|
|
155
|
+
|
|
156
|
+
Filters out nearby points that are close to, but not in, the
|
|
157
|
+
observable's evaluation times (within ``tolerance``).
|
|
158
|
+
Prevent false matches by using Pulser's tolerance
|
|
159
|
+
tol = 0.5 / total_duration. (deep inside pulser Observable class)
|
|
160
|
+
"""
|
|
161
|
+
times = observable.evaluation_times
|
|
162
|
+
|
|
163
|
+
is_observable_eval_time = (
|
|
164
|
+
times is not None
|
|
165
|
+
and self._config.is_time_in_evaluation_times(t, times, tol=tolerance)
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
is_default_eval_time = self._config.is_evaluation_time(t, tol=tolerance)
|
|
169
|
+
|
|
170
|
+
return is_observable_eval_time or is_default_eval_time
|
|
171
|
+
|
|
145
172
|
def _apply_observables(self, step_idx: int) -> None:
|
|
146
|
-
norm_time = self.target_times[step_idx
|
|
147
|
-
|
|
173
|
+
norm_time = self.target_times[step_idx] / self.target_times[-1]
|
|
174
|
+
callbacks_for_current_time_step = [
|
|
175
|
+
callback
|
|
176
|
+
for callback in self._config.observables
|
|
177
|
+
if self._is_evaluation_time(callback, norm_time)
|
|
178
|
+
]
|
|
179
|
+
for callback in callbacks_for_current_time_step:
|
|
148
180
|
callback(
|
|
149
181
|
self._config,
|
|
150
182
|
norm_time,
|
|
@@ -154,7 +186,8 @@ class BaseSVBackendImpl:
|
|
|
154
186
|
)
|
|
155
187
|
|
|
156
188
|
def _save_statistics(self, step_idx: int) -> None:
|
|
157
|
-
norm_time = self.target_times[step_idx
|
|
189
|
+
norm_time = self.target_times[step_idx] / self.target_times[-1]
|
|
190
|
+
|
|
158
191
|
self.statistics.data.append(time.time() - self.time)
|
|
159
192
|
self.statistics(
|
|
160
193
|
self._config,
|
|
@@ -167,6 +200,7 @@ class BaseSVBackendImpl:
|
|
|
167
200
|
self._current_H = None
|
|
168
201
|
|
|
169
202
|
def _run(self) -> Results:
|
|
203
|
+
self._apply_observables(0) # at t == 0 for pulser compatibility
|
|
170
204
|
for step in range(self.nsteps):
|
|
171
205
|
self.step(step)
|
|
172
206
|
|
|
@@ -175,16 +209,16 @@ class BaseSVBackendImpl:
|
|
|
175
209
|
|
|
176
210
|
class SVBackendImpl(BaseSVBackendImpl):
|
|
177
211
|
|
|
178
|
-
def __init__(self, config: SVConfig,
|
|
212
|
+
def __init__(self, config: SVConfig, data: SequenceData):
|
|
179
213
|
"""
|
|
180
214
|
For running sequences without noise. The state will evolve according
|
|
181
215
|
to e^(-iH t)
|
|
182
216
|
|
|
183
217
|
Args:
|
|
184
218
|
config: The configuration for the emulator.
|
|
185
|
-
|
|
219
|
+
data: The data for the sequence to be emulated.
|
|
186
220
|
"""
|
|
187
|
-
super().__init__(config,
|
|
221
|
+
super().__init__(config, data)
|
|
188
222
|
self.state: StateVector = (
|
|
189
223
|
StateVector.make(self.nqubits, gpu=self.resolved_gpu)
|
|
190
224
|
if self._config.initial_state is None
|
|
@@ -210,7 +244,7 @@ class SVBackendImpl(BaseSVBackendImpl):
|
|
|
210
244
|
|
|
211
245
|
class NoisySVBackendImpl(BaseSVBackendImpl):
|
|
212
246
|
|
|
213
|
-
def __init__(self, config: SVConfig,
|
|
247
|
+
def __init__(self, config: SVConfig, data: SequenceData):
|
|
214
248
|
"""
|
|
215
249
|
Initializes the NoisySVBackendImpl, master equation version.
|
|
216
250
|
This class handles the Lindblad operators and
|
|
@@ -218,12 +252,12 @@ class NoisySVBackendImpl(BaseSVBackendImpl):
|
|
|
218
252
|
|
|
219
253
|
Args:
|
|
220
254
|
config: The configuration for the emulator.
|
|
221
|
-
|
|
255
|
+
data: The data for the sequence to be emulated.
|
|
222
256
|
"""
|
|
223
257
|
|
|
224
|
-
super().__init__(config,
|
|
258
|
+
super().__init__(config, data)
|
|
225
259
|
|
|
226
|
-
self.pulser_lindblads =
|
|
260
|
+
self.pulser_lindblads = data.lindblad_ops
|
|
227
261
|
|
|
228
262
|
self.state: DensityMatrix = (
|
|
229
263
|
DensityMatrix.make(self.nqubits, gpu=self.resolved_gpu)
|
|
@@ -246,7 +280,7 @@ class NoisySVBackendImpl(BaseSVBackendImpl):
|
|
|
246
280
|
)
|
|
247
281
|
|
|
248
282
|
|
|
249
|
-
def create_impl(
|
|
283
|
+
def create_impl(data: SequenceData, config: SVConfig) -> BaseSVBackendImpl:
|
|
250
284
|
"""
|
|
251
285
|
Creates the backend implementation for the given sequence and config.
|
|
252
286
|
|
|
@@ -257,8 +291,7 @@ def create_impl(sequence: Sequence, config: SVConfig) -> BaseSVBackendImpl:
|
|
|
257
291
|
Returns:
|
|
258
292
|
An instance of SVBackendImpl.
|
|
259
293
|
"""
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
return NoisySVBackendImpl(config, pulse_data)
|
|
294
|
+
if data.lindblad_ops:
|
|
295
|
+
return NoisySVBackendImpl(config, data)
|
|
263
296
|
else:
|
|
264
|
-
return SVBackendImpl(config,
|
|
297
|
+
return SVBackendImpl(config, data)
|
emu_sv/sv_config.py
CHANGED
|
@@ -26,7 +26,6 @@ from pulser.backend import (
|
|
|
26
26
|
EnergySecondMoment,
|
|
27
27
|
EnergyVariance,
|
|
28
28
|
Occupation,
|
|
29
|
-
BitStrings,
|
|
30
29
|
)
|
|
31
30
|
|
|
32
31
|
|
|
@@ -55,11 +54,13 @@ class SVConfig(EmulationConfig):
|
|
|
55
54
|
kwargs: arguments that are passed to the base class
|
|
56
55
|
|
|
57
56
|
Examples:
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
57
|
+
```python
|
|
58
|
+
gpu = True
|
|
59
|
+
dt = 1.0 #this will impact the runtime
|
|
60
|
+
krylov_tolerance = 1e-8 #the simulation will be faster, but less accurate
|
|
61
|
+
SVConfig(gpu=gpu, dt=dt, krylov_tolerance=krylov_tolerance,
|
|
62
|
+
with_modulation=True) #the last arg is taken from the base class
|
|
63
|
+
```
|
|
63
64
|
"""
|
|
64
65
|
|
|
65
66
|
# Whether to warn if unexpected kwargs are received
|
|
@@ -70,7 +71,7 @@ class SVConfig(EmulationConfig):
|
|
|
70
71
|
def __init__(
|
|
71
72
|
self,
|
|
72
73
|
*,
|
|
73
|
-
dt:
|
|
74
|
+
dt: float = 10.0,
|
|
74
75
|
max_krylov_dim: int = 100,
|
|
75
76
|
krylov_tolerance: float = 1e-10,
|
|
76
77
|
gpu: bool | None = None,
|
|
@@ -79,7 +80,6 @@ class SVConfig(EmulationConfig):
|
|
|
79
80
|
log_file: pathlib.Path | None = None,
|
|
80
81
|
**kwargs: Any,
|
|
81
82
|
):
|
|
82
|
-
kwargs.setdefault("observables", [BitStrings(evaluation_times=[1.0])])
|
|
83
83
|
super().__init__(
|
|
84
84
|
dt=dt,
|
|
85
85
|
max_krylov_dim=max_krylov_dim,
|
|
@@ -92,13 +92,13 @@ class SVConfig(EmulationConfig):
|
|
|
92
92
|
)
|
|
93
93
|
|
|
94
94
|
self.monkeypatch_observables()
|
|
95
|
-
|
|
95
|
+
logger = init_logging(log_level, log_file)
|
|
96
96
|
|
|
97
97
|
if (self.noise_model.runs != 1 and self.noise_model.runs is not None) or (
|
|
98
98
|
self.noise_model.samples_per_run != 1
|
|
99
99
|
and self.noise_model.samples_per_run is not None
|
|
100
100
|
):
|
|
101
|
-
|
|
101
|
+
logger.warning(
|
|
102
102
|
"Warning: The runs and samples_per_run "
|
|
103
103
|
"values of the NoiseModel are ignored!"
|
|
104
104
|
)
|
|
@@ -145,4 +145,4 @@ class SVConfig(EmulationConfig):
|
|
|
145
145
|
obs_copy,
|
|
146
146
|
)
|
|
147
147
|
obs_list.append(obs_copy)
|
|
148
|
-
self.observables = tuple(obs_list)
|
|
148
|
+
self._backend_options["observables"] = tuple(obs_list)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: emu-sv
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.7.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.7.0
|
|
29
29
|
Description-Content-Type: text/markdown
|
|
30
30
|
|
|
31
31
|
<div align="center">
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
emu_sv/__init__.py,sha256=cdxnMYgeTP6kn5BsOW71XoSh8WpKWlxMC6Zq38iqJLA,837
|
|
2
|
+
emu_sv/custom_callback_implementations.py,sha256=_7XLIDzJ-p3DVqz-Jyv0eYbl8nih2x2p-pM4cBCLumA,6367
|
|
3
|
+
emu_sv/dense_operator.py,sha256=0x8FPaOym-36vF3UQtwrARA9mi-N__FIiGJj2F_4MAw,6558
|
|
4
|
+
emu_sv/density_matrix_state.py,sha256=byH5OGqQffr560CRmFngPGeV6fq33dYKEj3C0suhgdw,8351
|
|
5
|
+
emu_sv/hamiltonian.py,sha256=CqNGuWJlO2ZljK47wt130s-5uKiOldQUsC3tjwk1mKA,6106
|
|
6
|
+
emu_sv/lindblad_operator.py,sha256=pgjRNLBcvEM2-qxM8uy9wL74OtrD4A8trQeERi_AXH8,8892
|
|
7
|
+
emu_sv/sparse_operator.py,sha256=B0TRuzr0j8wx3l4a0Pd7RT4DhFs_s_vH-4mIj1077vM,7585
|
|
8
|
+
emu_sv/state_vector.py,sha256=r5VixGWuES_3qIhK2g4Qkn--3S8ZzMlPFQUmXw_NneM,10167
|
|
9
|
+
emu_sv/sv_backend.py,sha256=ZLyg3fL20ItgSZXxpgqYvFuABocclwpvuShB17mOsyY,1148
|
|
10
|
+
emu_sv/sv_backend_impl.py,sha256=01ibeAQDghpppRn28QdrAXvCDx6vo4O2xznigMlNng4,9898
|
|
11
|
+
emu_sv/sv_config.py,sha256=eb3zCUDJDpAkJrCNxgZgAeDym5drzGyp-MwQbEbIDRk,5364
|
|
12
|
+
emu_sv/time_evolution.py,sha256=Uy3qMdt3BlLB6Aq1-o5uajRTu_3fPuBCtcusHxFPPJc,13545
|
|
13
|
+
emu_sv/utils.py,sha256=t0nMDVo6DF5bQW-vbsyRMCmvkyNxCU-v0Enmns9aOAU,1151
|
|
14
|
+
emu_sv-2.7.0.dist-info/METADATA,sha256=yNM3g5QmB_01UWZXbewhPlRO-5drB1v4NGN6rKDZ16Y,3595
|
|
15
|
+
emu_sv-2.7.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
16
|
+
emu_sv-2.7.0.dist-info/RECORD,,
|
emu_sv-2.6.0.dist-info/RECORD
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
emu_sv/__init__.py,sha256=0ykYwInHIgbHkUig_cY9uFesmU4F8bc8D_DIV511yEo,837
|
|
2
|
-
emu_sv/custom_callback_implementations.py,sha256=_7XLIDzJ-p3DVqz-Jyv0eYbl8nih2x2p-pM4cBCLumA,6367
|
|
3
|
-
emu_sv/dense_operator.py,sha256=GvF0swsiFRqp83bpyaU_CXap2vm74-JLI5lHo-0Hbdk,5901
|
|
4
|
-
emu_sv/density_matrix_state.py,sha256=6QmLZvqEHLR64r0nD7D2jZIiAYOgciNVCjh3ywfvIs0,7243
|
|
5
|
-
emu_sv/hamiltonian.py,sha256=CqNGuWJlO2ZljK47wt130s-5uKiOldQUsC3tjwk1mKA,6106
|
|
6
|
-
emu_sv/lindblad_operator.py,sha256=pgjRNLBcvEM2-qxM8uy9wL74OtrD4A8trQeERi_AXH8,8892
|
|
7
|
-
emu_sv/sparse_operator.py,sha256=xHJapSAKaMCgT5nG0gzMXGk2fCfjHY03OTO_rysszns,7535
|
|
8
|
-
emu_sv/state_vector.py,sha256=v4rqC_qBGc5vO5EMHcHR6BdASjeKujO6_sCdd3pGd0c,9990
|
|
9
|
-
emu_sv/sv_backend.py,sha256=-soOkSEzEBK1dCKnYnbtvYjmNZtZra1_4jP3H1ROOtM,737
|
|
10
|
-
emu_sv/sv_backend_impl.py,sha256=-xWE30B5RI32nOG2pUR8lL3q-wufwvzxegiJexW5g4w,8952
|
|
11
|
-
emu_sv/sv_config.py,sha256=o1esIqflxfN1ZtdwoVBAIWlzZIf9B5X9pvsQe1zHfdg,5433
|
|
12
|
-
emu_sv/time_evolution.py,sha256=Uy3qMdt3BlLB6Aq1-o5uajRTu_3fPuBCtcusHxFPPJc,13545
|
|
13
|
-
emu_sv/utils.py,sha256=t0nMDVo6DF5bQW-vbsyRMCmvkyNxCU-v0Enmns9aOAU,1151
|
|
14
|
-
emu_sv-2.6.0.dist-info/METADATA,sha256=YNa_iPn5JUl5N9FMdaHOwF1Pm000JwsYkLUUcKB-8FQ,3595
|
|
15
|
-
emu_sv-2.6.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
16
|
-
emu_sv-2.6.0.dist-info/RECORD,,
|
|
File without changes
|