Skip to content

Fix concatenation with zero n_coeffs #59

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Mar 11, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 15 additions & 14 deletions filter_functions/pulse_sequence.py
Original file line number Diff line number Diff line change
Expand Up @@ -1281,7 +1281,7 @@ def _concatenate_Hamiltonian(

# Concatenate the coefficients. Place them in the right time segments of
# the concatenated Hamiltonian.
concat_coeffs = np.zeros((len(concat_identifiers), sum(n_dt)), dtype=float)
concat_coeffs = np.full((len(concat_identifiers), sum(n_dt)), fill_value=np.nan)
flat_coeffs = [co for coeff in coeffs for co in coeff]
for i in range(len(concat_identifiers)):
# Get the indices in opers (and coeffs) for the i-th unique operator
Expand All @@ -1299,17 +1299,18 @@ def _concatenate_Hamiltonian(
# the remaining segments as usually the sensitivity is constant. If we
# cannot do this, we have to raise an exception since we cannot know
# the sensitivities at other moments in time if they are non-trivial.
for i, c_coeffs in enumerate(concat_coeffs):
zero_mask = (c_coeffs == 0)
if zero_mask.any() and not zero_mask.all():
nonzero_coeffs = c_coeffs[~zero_mask]
constant = (nonzero_coeffs == nonzero_coeffs[0]).all()
if constant:
# Fill with constant value
concat_coeffs[i, zero_mask] = nonzero_coeffs[0]
else:
raise ValueError('Not all pulses have the same noise operators and ' +
'non-trivial noise sensitivities so I cannot infer them.')
nan_mask = np.isnan(concat_coeffs)
test = nan_mask.any(axis=1)
for i, (concat_coeff, mask) in enumerate(zip(concat_coeffs[test], nan_mask[test])):
nonnan_coeff = concat_coeff[~mask]
if (nonnan_coeff == nonnan_coeff[0]).all():
# Constant value, use for empty segment
concat_coeffs[i, mask] = nonnan_coeff[0]
else:
raise ValueError('Not all pulses have the same noise operators and ' +
'non-trivial noise sensitivities so I cannot infer them.')
else:
concat_coeffs[np.isnan(concat_coeffs)] = 0

return concat_opers, concat_identifiers, concat_coeffs[sort_idx], pulse_identifier_mapping

Expand Down Expand Up @@ -1482,12 +1483,12 @@ def concatenate_without_filter_function(pulses: Iterable[PulseSequence],

# Compose new control Hamiltonian
control_values = _concatenate_Hamiltonian(
*list(zip(*[tuple(getattr(pulse, key) for key in control_keys) for pulse in pulses])),
*zip(*[[getattr(pulse, key) for key in control_keys] for pulse in pulses]),
kind='control'
)
# Compose new control Hamiltonian
noise_values = _concatenate_Hamiltonian(
*list(zip(*[tuple(getattr(pulse, key) for key in noise_keys) for pulse in pulses])),
*zip(*[[getattr(pulse, key) for key in noise_keys] for pulse in pulses]),
kind='noise'
)

Expand Down
18 changes: 18 additions & 0 deletions tests/test_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -469,6 +469,13 @@ def test_pulse_sequence_attributes_concat(self):
[X, rng.standard_normal(2)]],
[[Z, np.abs(rng.standard_normal(2))]],
[1, 1])
pulse_4 = ff.PulseSequence([[Y, rng.standard_normal(2)],
[X, rng.standard_normal(2)]],
[[Z, np.ones(2)]],
[1, 1])
pulse_5 = ff.PulseSequence([[Y, np.zeros(5), 'A_0']],
[[Y, np.zeros(5), 'B_1']],
1 - rng.random(5))

# Concatenate with different noise opers
pulses = [testutil.rand_pulse_sequence(2, 1) for _ in range(2)]
Expand All @@ -479,6 +486,7 @@ def test_pulse_sequence_attributes_concat(self):

pulse_12 = pulse_1 @ pulse_2
pulse_21 = pulse_2 @ pulse_1
pulse_45 = pulse_4 @ pulse_5

with self.assertRaises(TypeError):
_ = pulse_1 @ rng.standard_normal((2, 2))
Expand Down Expand Up @@ -515,6 +523,16 @@ def test_pulse_sequence_attributes_concat(self):
self.assertArrayEqual(pulse_12.n_coeffs, [[*z_coeff_1, *z_coeff_2]])
self.assertArrayEqual(pulse_21.n_coeffs, [[*z_coeff_2, *z_coeff_1]])

# Make sure zero coefficients are handled correctly
self.assertFalse(np.any(np.isnan(pulse_45.c_coeffs)))
self.assertFalse(np.any(np.isnan(pulse_45.n_coeffs)))
self.assertArrayEqual(pulse_45.c_coeffs,
[[*pulse_4.c_coeffs[0], *np.zeros(5)],
[*pulse_4.c_coeffs[1], *np.zeros(5)]])
self.assertArrayEqual(pulse_45.n_coeffs,
[[*pulse_4.n_coeffs[0], *[pulse_4.n_coeffs[0, 0]]*5],
[*[pulse_5.n_coeffs[0, 0]]*2, *pulse_5.n_coeffs[0]]])

omega = np.linspace(-100, 100, 101)
pulses = (pulse_1, pulse_2, pulse_12, pulse_21)
for pulse in pulses:
Expand Down