emu-sv 1.0.1__py3-none-any.whl → 2.0.1__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 +18 -22
- emu_sv/custom_callback_implementations.py +67 -45
- emu_sv/dense_operator.py +91 -90
- emu_sv/density_matrix_state.py +195 -0
- emu_sv/hamiltonian.py +10 -8
- emu_sv/state_vector.py +98 -67
- emu_sv/sv_backend.py +86 -71
- emu_sv/sv_config.py +93 -36
- emu_sv/utils.py +8 -0
- {emu_sv-1.0.1.dist-info → emu_sv-2.0.1.dist-info}/METADATA +2 -2
- emu_sv-2.0.1.dist-info/RECORD +13 -0
- emu_sv-1.0.1.dist-info/RECORD +0 -11
- {emu_sv-1.0.1.dist-info → emu_sv-2.0.1.dist-info}/WHEEL +0 -0
emu_sv/sv_backend.py
CHANGED
|
@@ -1,101 +1,125 @@
|
|
|
1
|
-
from emu_base.base_classes.backend import Backend, BackendConfig
|
|
2
|
-
from emu_base.base_classes.results import Results
|
|
3
|
-
from emu_sv.sv_config import SVConfig
|
|
4
|
-
from pulser import Sequence
|
|
5
|
-
from emu_base.pulser_adapter import PulserData
|
|
6
|
-
from emu_sv.time_evolution import do_time_step
|
|
7
|
-
from emu_sv import StateVector
|
|
8
1
|
import torch
|
|
9
|
-
from time import time
|
|
10
2
|
from resource import RUSAGE_SELF, getrusage
|
|
11
|
-
|
|
12
|
-
|
|
3
|
+
import time
|
|
4
|
+
import typing
|
|
5
|
+
|
|
6
|
+
from pulser.backend import EmulatorBackend, Results, Observable, State, EmulationConfig
|
|
7
|
+
|
|
8
|
+
from emu_base import PulserData
|
|
9
|
+
|
|
10
|
+
from emu_sv.state_vector import StateVector
|
|
11
|
+
from emu_sv.sv_config import SVConfig
|
|
12
|
+
from emu_sv.time_evolution import do_time_step
|
|
13
|
+
|
|
13
14
|
|
|
14
15
|
_TIME_CONVERSION_COEFF = 0.001 # Omega and delta are given in rad/ms, dt in ns
|
|
15
16
|
|
|
16
17
|
|
|
17
|
-
class SVBackend(
|
|
18
|
+
class SVBackend(EmulatorBackend):
|
|
18
19
|
"""
|
|
19
20
|
A backend for emulating Pulser sequences using state vectors and sparse matrices.
|
|
20
21
|
"""
|
|
21
22
|
|
|
22
|
-
|
|
23
|
+
default_config = SVConfig()
|
|
24
|
+
|
|
25
|
+
def run(self) -> Results:
|
|
23
26
|
"""
|
|
24
27
|
Emulates the given sequence.
|
|
25
28
|
|
|
26
|
-
Args:
|
|
27
|
-
sequence: a Pulser sequence to simulate
|
|
28
|
-
sv_config: the backends config. Should be of type SVConfig
|
|
29
|
-
|
|
30
29
|
Returns:
|
|
31
30
|
the simulation results
|
|
32
31
|
"""
|
|
33
|
-
assert isinstance(
|
|
34
|
-
|
|
35
|
-
self.validate_sequence(sequence)
|
|
36
|
-
|
|
37
|
-
results = Results()
|
|
32
|
+
assert isinstance(self._config, SVConfig)
|
|
38
33
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
target_times =
|
|
34
|
+
pulser_data = PulserData(
|
|
35
|
+
sequence=self._sequence, config=self._config, dt=self._config.dt
|
|
36
|
+
)
|
|
37
|
+
self.target_times = pulser_data.target_times
|
|
38
|
+
self.time = time.time()
|
|
39
|
+
omega, delta, phi = pulser_data.omega, pulser_data.delta, pulser_data.phi
|
|
43
40
|
|
|
44
41
|
nsteps = omega.shape[0]
|
|
45
42
|
nqubits = omega.shape[1]
|
|
46
|
-
device = "cuda" if sv_config.gpu and DEVICE_COUNT > 0 else "cpu"
|
|
47
43
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
44
|
+
self.results = Results(atom_order=(), total_duration=self.target_times[-1])
|
|
45
|
+
self.statistics = Statistics(
|
|
46
|
+
evaluation_times=[t / self.target_times[-1] for t in self.target_times],
|
|
47
|
+
data=[],
|
|
48
|
+
timestep_count=nsteps,
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
if self._config.initial_state is not None:
|
|
52
|
+
state = self._config.initial_state
|
|
53
|
+
state = StateVector(state.vector.clone(), gpu=state.vector.is_cuda)
|
|
51
54
|
else:
|
|
52
|
-
state = StateVector.make(nqubits, gpu=
|
|
55
|
+
state = StateVector.make(nqubits, gpu=self._config.gpu)
|
|
53
56
|
|
|
54
57
|
for step in range(nsteps):
|
|
55
|
-
|
|
56
|
-
dt = target_times[step + 1] - target_times[step]
|
|
58
|
+
dt = self.target_times[step + 1] - self.target_times[step]
|
|
57
59
|
|
|
58
60
|
state.vector, H = do_time_step(
|
|
59
61
|
dt * _TIME_CONVERSION_COEFF,
|
|
60
62
|
omega[step],
|
|
61
63
|
delta[step],
|
|
62
64
|
phi[step],
|
|
63
|
-
|
|
65
|
+
pulser_data.full_interaction_matrix,
|
|
64
66
|
state.vector,
|
|
65
|
-
|
|
67
|
+
self._config.krylov_tolerance,
|
|
66
68
|
)
|
|
67
69
|
|
|
68
|
-
|
|
70
|
+
# callbacks in observables and self.statistics in H
|
|
71
|
+
# have "# type: ignore[arg-type]" because H has it's own type
|
|
72
|
+
# meaning H is not inherited from Operator class.
|
|
73
|
+
# We decided that ignore[arg-type] is better compared to
|
|
74
|
+
# having many unused NotImplemented methods
|
|
75
|
+
for callback in self._config.observables:
|
|
69
76
|
callback(
|
|
70
|
-
|
|
71
|
-
target_times[step + 1],
|
|
77
|
+
self._config,
|
|
78
|
+
self.target_times[step + 1] / self.target_times[-1],
|
|
72
79
|
state,
|
|
73
80
|
H, # type: ignore[arg-type]
|
|
74
|
-
results,
|
|
81
|
+
self.results,
|
|
75
82
|
)
|
|
76
83
|
|
|
77
|
-
|
|
78
|
-
self.
|
|
79
|
-
|
|
80
|
-
step
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
sv_config=sv_config,
|
|
84
|
+
self.statistics.data.append(time.time() - self.time)
|
|
85
|
+
self.statistics(
|
|
86
|
+
self._config,
|
|
87
|
+
self.target_times[step + 1] / self.target_times[-1],
|
|
88
|
+
state,
|
|
89
|
+
H, # type: ignore[arg-type]
|
|
90
|
+
self.results,
|
|
85
91
|
)
|
|
92
|
+
self.time = time.time()
|
|
86
93
|
|
|
87
|
-
return results
|
|
94
|
+
return self.results
|
|
88
95
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
96
|
+
|
|
97
|
+
class Statistics(Observable):
|
|
98
|
+
def __init__(
|
|
99
|
+
self,
|
|
100
|
+
evaluation_times: typing.Sequence[float] | None,
|
|
101
|
+
data: list[float],
|
|
95
102
|
timestep_count: int,
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
103
|
+
):
|
|
104
|
+
super().__init__(evaluation_times=evaluation_times)
|
|
105
|
+
self.data = data
|
|
106
|
+
self.timestep_count = timestep_count
|
|
107
|
+
|
|
108
|
+
@property
|
|
109
|
+
def _base_tag(self) -> str:
|
|
110
|
+
return "statistics"
|
|
111
|
+
|
|
112
|
+
def apply(
|
|
113
|
+
self,
|
|
114
|
+
*,
|
|
115
|
+
config: EmulationConfig,
|
|
116
|
+
state: State,
|
|
117
|
+
**kwargs: typing.Any,
|
|
118
|
+
) -> dict:
|
|
119
|
+
"""Calculates the observable to store in the Results."""
|
|
120
|
+
assert isinstance(state, StateVector)
|
|
121
|
+
assert isinstance(config, SVConfig)
|
|
122
|
+
duration = self.data[-1]
|
|
99
123
|
if state.vector.is_cuda:
|
|
100
124
|
max_mem_per_device = (
|
|
101
125
|
torch.cuda.max_memory_allocated(device) * 1e-6
|
|
@@ -105,22 +129,13 @@ class SVBackend(Backend):
|
|
|
105
129
|
else:
|
|
106
130
|
max_mem = getrusage(RUSAGE_SELF).ru_maxrss * 1e-3
|
|
107
131
|
|
|
108
|
-
|
|
109
|
-
f"step = {
|
|
132
|
+
config.logger.info(
|
|
133
|
+
f"step = {len(self.data)}/{self.timestep_count}, "
|
|
110
134
|
+ f"RSS = {max_mem:.3f} MB, "
|
|
111
135
|
+ f"Δt = {duration:.3f} s"
|
|
112
136
|
)
|
|
113
137
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
assert "steps" in results.statistics
|
|
119
|
-
assert len(results.statistics["steps"]) == step
|
|
120
|
-
|
|
121
|
-
results.statistics["steps"].append(
|
|
122
|
-
{
|
|
123
|
-
"RSS": max_mem,
|
|
124
|
-
"duration": duration,
|
|
125
|
-
}
|
|
126
|
-
)
|
|
138
|
+
return {
|
|
139
|
+
"RSS": max_mem,
|
|
140
|
+
"duration": duration,
|
|
141
|
+
}
|
emu_sv/sv_config.py
CHANGED
|
@@ -1,35 +1,34 @@
|
|
|
1
|
-
from emu_base.base_classes import (
|
|
2
|
-
CorrelationMatrix,
|
|
3
|
-
QubitDensity,
|
|
4
|
-
EnergyVariance,
|
|
5
|
-
SecondMomentOfEnergy,
|
|
6
|
-
)
|
|
7
|
-
|
|
8
1
|
import copy
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
from
|
|
13
|
-
from typing import Any
|
|
2
|
+
import logging
|
|
3
|
+
import pathlib
|
|
4
|
+
import sys
|
|
5
|
+
from types import MethodType
|
|
6
|
+
from typing import Any, ClassVar
|
|
14
7
|
|
|
15
8
|
from emu_sv.custom_callback_implementations import (
|
|
16
|
-
qubit_density_sv_impl,
|
|
17
|
-
energy_variance_sv_impl,
|
|
18
|
-
second_moment_sv_impl,
|
|
19
9
|
correlation_matrix_sv_impl,
|
|
10
|
+
energy_second_moment_sv_impl,
|
|
11
|
+
energy_variance_sv_impl,
|
|
12
|
+
qubit_occupation_sv_impl,
|
|
20
13
|
)
|
|
21
14
|
|
|
22
|
-
from
|
|
15
|
+
from pulser.backend import (
|
|
16
|
+
CorrelationMatrix,
|
|
17
|
+
EmulationConfig,
|
|
18
|
+
EnergySecondMoment,
|
|
19
|
+
EnergyVariance,
|
|
20
|
+
Occupation,
|
|
21
|
+
BitStrings,
|
|
22
|
+
)
|
|
23
23
|
|
|
24
24
|
|
|
25
|
-
class SVConfig(
|
|
25
|
+
class SVConfig(EmulationConfig):
|
|
26
26
|
"""
|
|
27
27
|
The configuration of the emu-sv SVBackend. The kwargs passed to this class
|
|
28
28
|
are passed on to the base class.
|
|
29
29
|
See the API for that class for a list of available options.
|
|
30
30
|
|
|
31
31
|
Args:
|
|
32
|
-
initial_state: the initial state to use in the simulation
|
|
33
32
|
dt: the timestep size that the solver uses. Note that observables are
|
|
34
33
|
only calculated if the evaluation_times are divisible by dt.
|
|
35
34
|
max_krylov_dim:
|
|
@@ -48,43 +47,101 @@ class SVConfig(BackendConfig):
|
|
|
48
47
|
>>> with_modulation=True) #the last arg is taken from the base class
|
|
49
48
|
"""
|
|
50
49
|
|
|
50
|
+
# Whether to warn if unexpected kwargs are received
|
|
51
|
+
_enforce_expected_kwargs: ClassVar[bool] = True
|
|
52
|
+
|
|
51
53
|
def __init__(
|
|
52
54
|
self,
|
|
53
55
|
*,
|
|
54
|
-
initial_state: StateVector | None = None,
|
|
55
56
|
dt: int = 10,
|
|
56
57
|
max_krylov_dim: int = 100,
|
|
57
58
|
krylov_tolerance: float = 1e-10,
|
|
58
59
|
gpu: bool = True,
|
|
60
|
+
interaction_cutoff: float = 0.0,
|
|
61
|
+
log_level: int = logging.INFO,
|
|
62
|
+
log_file: pathlib.Path | None = None,
|
|
59
63
|
**kwargs: Any,
|
|
60
64
|
):
|
|
61
|
-
|
|
65
|
+
kwargs.setdefault("observables", [BitStrings(evaluation_times=[1.0])])
|
|
66
|
+
super().__init__(
|
|
67
|
+
dt=dt,
|
|
68
|
+
max_krylov_dim=max_krylov_dim,
|
|
69
|
+
gpu=gpu,
|
|
70
|
+
krylov_tolerance=krylov_tolerance,
|
|
71
|
+
interaction_cutoff=interaction_cutoff,
|
|
72
|
+
log_level=log_level,
|
|
73
|
+
log_file=log_file,
|
|
74
|
+
**kwargs,
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
self.monkeypatch_observables()
|
|
62
78
|
|
|
63
|
-
self.
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
79
|
+
self.logger = logging.getLogger("global_logger")
|
|
80
|
+
if log_file is None:
|
|
81
|
+
logging.basicConfig(
|
|
82
|
+
level=log_level, format="%(message)s", stream=sys.stdout, force=True
|
|
83
|
+
) # default to stream = sys.stderr
|
|
84
|
+
else:
|
|
85
|
+
logging.basicConfig(
|
|
86
|
+
level=log_level,
|
|
87
|
+
format="%(message)s",
|
|
88
|
+
filename=str(log_file),
|
|
89
|
+
filemode="w",
|
|
90
|
+
force=True,
|
|
91
|
+
)
|
|
92
|
+
if (self.noise_model.runs != 1 and self.noise_model.runs is not None) or (
|
|
93
|
+
self.noise_model.samples_per_run != 1
|
|
94
|
+
and self.noise_model.samples_per_run is not None
|
|
95
|
+
):
|
|
96
|
+
self.logger.warning(
|
|
97
|
+
"Warning: The runs and samples_per_run "
|
|
98
|
+
"values of the NoiseModel are ignored!"
|
|
99
|
+
)
|
|
68
100
|
|
|
69
|
-
|
|
101
|
+
def _expected_kwargs(self) -> set[str]:
|
|
102
|
+
return super()._expected_kwargs() | {
|
|
103
|
+
"dt",
|
|
104
|
+
"max_krylov_dim",
|
|
105
|
+
"krylov_tolerance",
|
|
106
|
+
"gpu",
|
|
107
|
+
"interaction_cutoff",
|
|
108
|
+
"log_level",
|
|
109
|
+
"log_file",
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
def monkeypatch_observables(self) -> None:
|
|
113
|
+
obs_list = []
|
|
114
|
+
for _, obs in enumerate(self.observables): # monkey patch
|
|
70
115
|
obs_copy = copy.deepcopy(obs)
|
|
71
|
-
if isinstance(obs,
|
|
116
|
+
if isinstance(obs, Occupation):
|
|
72
117
|
obs_copy.apply = MethodType( # type: ignore[method-assign]
|
|
73
|
-
|
|
118
|
+
qubit_occupation_sv_impl, obs_copy
|
|
74
119
|
)
|
|
75
|
-
self.callbacks[num] = obs_copy
|
|
76
120
|
elif isinstance(obs, EnergyVariance):
|
|
77
121
|
obs_copy.apply = MethodType( # type: ignore[method-assign]
|
|
78
|
-
energy_variance_sv_impl,
|
|
122
|
+
energy_variance_sv_impl, obs_copy
|
|
79
123
|
)
|
|
80
|
-
|
|
81
|
-
elif isinstance(obs, SecondMomentOfEnergy):
|
|
124
|
+
elif isinstance(obs, EnergySecondMoment):
|
|
82
125
|
obs_copy.apply = MethodType( # type: ignore[method-assign]
|
|
83
|
-
|
|
126
|
+
energy_second_moment_sv_impl, obs_copy
|
|
84
127
|
)
|
|
85
|
-
self.callbacks[num] = obs_copy
|
|
86
128
|
elif isinstance(obs, CorrelationMatrix):
|
|
87
129
|
obs_copy.apply = MethodType( # type: ignore[method-assign]
|
|
88
|
-
correlation_matrix_sv_impl,
|
|
130
|
+
correlation_matrix_sv_impl, obs_copy
|
|
89
131
|
)
|
|
90
|
-
|
|
132
|
+
obs_list.append(obs_copy)
|
|
133
|
+
self.observables = tuple(obs_list)
|
|
134
|
+
|
|
135
|
+
def init_logging(self) -> None:
|
|
136
|
+
if self.log_file is None:
|
|
137
|
+
logging.basicConfig(
|
|
138
|
+
level=self.log_level, format="%(message)s", stream=sys.stdout, force=True
|
|
139
|
+
) # default to stream = sys.stderr
|
|
140
|
+
else:
|
|
141
|
+
logging.basicConfig(
|
|
142
|
+
level=self.log_level,
|
|
143
|
+
format="%(message)s",
|
|
144
|
+
filename=str(self.log_file),
|
|
145
|
+
filemode="w",
|
|
146
|
+
force=True,
|
|
147
|
+
)
|
emu_sv/utils.py
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
def index_to_bitstring(nqubits: int, index: int) -> str:
|
|
2
|
+
"""
|
|
3
|
+
Convert an integer index into its corresponding bitstring representation.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
msg = f"index {index} can not exceed Hilbert space size d**{nqubits}"
|
|
7
|
+
assert index < 2**nqubits, msg
|
|
8
|
+
return format(index, f"0{nqubits}b")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: emu-sv
|
|
3
|
-
Version:
|
|
3
|
+
Version: 2.0.1
|
|
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==
|
|
28
|
+
Requires-Dist: emu-base==2.0.1
|
|
29
29
|
Description-Content-Type: text/markdown
|
|
30
30
|
|
|
31
31
|
<div align="center">
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
emu_sv/__init__.py,sha256=VYP3ZjQxPQINv-VC6URdtivwJDCAaBx1Ld5iCEdTNzw,701
|
|
2
|
+
emu_sv/custom_callback_implementations.py,sha256=zvsSiDIc56gwybKq87VFZyKsniTDye6-oFd2-R0shpg,3447
|
|
3
|
+
emu_sv/dense_operator.py,sha256=NfgzVpnNitc5ZSM4RlfpAc5Ls2wFPNsTxdeFdhJSg1o,6909
|
|
4
|
+
emu_sv/density_matrix_state.py,sha256=6UBLUXaJaUdzOhflrKolcnH8737JszX7sry1WmbyakI,6993
|
|
5
|
+
emu_sv/hamiltonian.py,sha256=zZq-_yUVhhafHxKkTZN0hivzzWFwC9JkTEk4MV1fBag,6168
|
|
6
|
+
emu_sv/state_vector.py,sha256=iX_33hGzn5ZsmG-LWE_Tjhu-Tvy4_lBOjHieT0C-VbA,9335
|
|
7
|
+
emu_sv/sv_backend.py,sha256=LE8tfoo-5-o6O39ixUMZAGo-xM8m9Xwcl_HmM1pSYtw,4433
|
|
8
|
+
emu_sv/sv_config.py,sha256=QRy0VbCugmY6TQZ48nD6RxPJbpu0wzN7-E1Sud7YxLQ,5106
|
|
9
|
+
emu_sv/time_evolution.py,sha256=48C0DL_SOu7Jdjk2QKBNPsevOpQlgsPYUHE7cScY-ZM,796
|
|
10
|
+
emu_sv/utils.py,sha256=-axfQ2tqw0C7I9yw-28g7lytyk373DNBjDALh4kLBrM,302
|
|
11
|
+
emu_sv-2.0.1.dist-info/METADATA,sha256=FSrCZuRIQ25LNbffZ2cz73nxbX-n1JbG9xgblJecYjc,3513
|
|
12
|
+
emu_sv-2.0.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
13
|
+
emu_sv-2.0.1.dist-info/RECORD,,
|
emu_sv-1.0.1.dist-info/RECORD
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
emu_sv/__init__.py,sha256=rcIsdgWgk5n75z0fSQtQsVgrFs_5jtA52WwGsTxDgAs,845
|
|
2
|
-
emu_sv/custom_callback_implementations.py,sha256=H7fy0trUtY00wid6VeRJ2inUazFbkMlgavTfm8QtGq8,2754
|
|
3
|
-
emu_sv/dense_operator.py,sha256=69rv1J5jsHSRoPsgZqKJZnttCgMLIk4tDCBsOaOBVR8,7034
|
|
4
|
-
emu_sv/hamiltonian.py,sha256=Rg_tLGTgaL4Y8xWtO1P2axRQNrdKfHRnHwnv_BfZTEI,6091
|
|
5
|
-
emu_sv/state_vector.py,sha256=s26ktVkuBID9IU6ySMup8ci7M5Tn6HHCT4jcyZGxV_w,8036
|
|
6
|
-
emu_sv/sv_backend.py,sha256=lIi_aTyfW-pzrfxwJHSoF5klwJnWtLv3awwU4DSL4m4,3770
|
|
7
|
-
emu_sv/sv_config.py,sha256=NUAxqYG0NTWbarVreFr1Tb-8FzB0LVQ0fYnb2QSCzxo,3161
|
|
8
|
-
emu_sv/time_evolution.py,sha256=48C0DL_SOu7Jdjk2QKBNPsevOpQlgsPYUHE7cScY-ZM,796
|
|
9
|
-
emu_sv-1.0.1.dist-info/METADATA,sha256=hNCpmCxzB_i_ciIRzqebQ9seyPhp8F0DiuceBYG4T7k,3513
|
|
10
|
-
emu_sv-1.0.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
11
|
-
emu_sv-1.0.1.dist-info/RECORD,,
|
|
File without changes
|