Skip to content

Commit

Permalink
Fix SPAM errors introducing overhead (#529)
Browse files Browse the repository at this point in the history
* Handle eps_p = eps = 0 case

* Improve SPAM simulation performance

* Improve sample_state performance using tuple indexing

* Improvements of sample_state() performance

* Join meas_errors checks in simresults sample_state

* Update comments on simresults sample_state
  • Loading branch information
dakk authored Jun 2, 2023
1 parent 10fef9c commit 742c534
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 23 deletions.
52 changes: 29 additions & 23 deletions pulser-simulation/pulser_simulation/simresults.py
Original file line number Diff line number Diff line change
Expand Up @@ -521,28 +521,34 @@ def sample_state(
quantum states at time t.
"""
sampled_state = super().sample_state(t, n_samples, t_tol)
if self._meas_errors is None:
if self._meas_errors is None or (
self._meas_errors["epsilon"] == 0.0
and self._meas_errors["epsilon_prime"] == 0
):
return sampled_state

detected_sample_dict: Counter = Counter()
for shot, n_detects in sampled_state.items():
eps = self._meas_errors["epsilon"]
eps_p = self._meas_errors["epsilon_prime"]
# Shot as an array of 1s and 0s
shot_arr = np.array(list(shot), dtype=int)
# Probability of flipping each bit
flip_probs = np.array([eps_p if x == "1" else eps for x in shot])
# 1 if it flips, 0 if it stays the same
flips = (
np.random.uniform(size=(n_detects, len(flip_probs)))
< flip_probs
).astype(int)
# XOR betwen the original array and the flips
# Gives an array of n_detects individual shots
new_shots = shot_arr ^ flips
# Count all the new_shots
detected_sample_dict += Counter(
"".join(map(str, measured)) for measured in new_shots
)

return detected_sample_dict
eps = self._meas_errors["epsilon"]
eps_p = self._meas_errors["epsilon_prime"]
shots = list(sampled_state.keys())
n_detects_list = list(sampled_state.values())

# Convert shots to a 2D array
shot_arr = np.array([list(shot) for shot in shots], dtype=int)
# Compute flip probabilities
flip_probs = np.where(shot_arr == 1, eps_p, eps)
# Repeat flip_probs based on n_detects_list
flip_probs_repeated = np.repeat(flip_probs, n_detects_list, axis=0)
# Generate random matrix of shape (sum(n_detects_list), len(shot))
random_matrix = np.random.uniform(
size=(np.sum(n_detects_list), len(shot_arr[0]))
)
# Compare random matrix with flip probabilities
flips = random_matrix < flip_probs_repeated
# Perform XOR between original array and flips
new_shots = shot_arr.repeat(n_detects_list, axis=0) ^ flips
# Count all the new_shots
# We are not converting to str before because tuple indexing is faster
detected_sample_dict: Counter = Counter(map(tuple, new_shots))
return Counter(
{"".join(map(str, k)): v for k, v in detected_sample_dict.items()}
)
20 changes: 20 additions & 0 deletions tests/test_simulation.py
Original file line number Diff line number Diff line change
Expand Up @@ -643,6 +643,26 @@ def test_noise(seq, matrices):
assert np.all(sim2.samples["Local"][basis][t][qty] == 0.0)


def test_noise_with_zero_epsilons(seq, matrices):
np.random.seed(3)
sim = Simulation(seq, sampling_rate=0.01)

sim2 = Simulation(
seq,
sampling_rate=0.01,
config=SimConfig(
noise=("SPAM"), eta=0.0, epsilon=0.0, epsilon_prime=0.0
),
)
assert sim2.config.spam_dict == {
"eta": 0,
"epsilon": 0.0,
"epsilon_prime": 0.0,
}

assert sim.run().sample_final_state() == sim2.run().sample_final_state()


def test_dephasing():
np.random.seed(123)
reg = Register.from_coordinates([(0, 0)], prefix="q")
Expand Down

0 comments on commit 742c534

Please sign in to comment.