Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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: 11 additions & 18 deletions qctrlopencontrols/dynamic_decoupling_sequences/driven_controls.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ def convert_dds_to_driven_control(
minimum_segment_duration : float, optional
If set, further restricts the duration of every segment of the Driven Controls.
Defaults to 0, in which case it does not affect the duration of the pulses.
Must be greater or equal to 0, if set.
Copy link
Contributor

@charmasaur charmasaur Apr 1, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point! I think this is a reasonable requirement.

Copy link
Member Author

@leoadec leoadec Apr 1, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I was getting away without testing it explicitly before, but if I eliminate the gap >= 0 test then I have to make sure this isn't negative

kwargs : dict, optional
Options to make the corresponding filter type.
I.e. the options for primitive are described in doc for the PrimitivePulse class.
Expand Down Expand Up @@ -165,6 +166,10 @@ def convert_dds_to_driven_control(
{'type(dynamic_decoupling_sequence':
type(dynamic_decoupling_sequence)})

if minimum_segment_duration < 0.:
raise ArgumentsValueError('Minimum segment duration must be greater or equal to 0.',
{'minimum_segment_duration': minimum_segment_duration})

_check_maximum_rotation_rate(maximum_rabi_rate, maximum_detuning_rate)

sequence_duration = dynamic_decoupling_sequence.duration
Expand Down Expand Up @@ -207,12 +212,10 @@ def convert_dds_to_driven_control(
detuning_rotations = np.append(detuning_rotations, [0])

# check that the offsets are correctly sorted in time
time_differences = np.diff(offsets)
if not np.all(np.logical_or(np.greater(time_differences, 0.),
np.isclose(time_differences, 0.))):
if any(np.diff(offsets) <= 0.):
raise ArgumentsValueError("Pulse timing could not be properly deduced from "
"the sequence offsets. Make sure all offset are "
"correctly ordered in time.",
"in increasing order.",
{'dynamic_decoupling_sequence': dynamic_decoupling_sequence},
extras={'offsets': offsets})

Expand Down Expand Up @@ -254,24 +257,14 @@ def convert_dds_to_driven_control(
translation = pulse_start_ends[-1, 1] - sequence_duration
pulse_start_ends[-1, :] = pulse_start_ends[-1, :] - translation

# check that no adjacent pulses overlap
gap_durations = pulse_start_ends[1:, 0] - pulse_start_ends[:-1, 1]
if not np.all(np.logical_or(np.greater(gap_durations, 0.),
np.isclose(gap_durations, 0.))):
raise ArgumentsValueError("There is overlap between pulses in the sequence. "
"Try increasing the maximum rabi rate or maximum detuning rate.",
{'dynamic_decoupling_sequence': dynamic_decoupling_sequence,
'maximum_rabi_rate': maximum_rabi_rate,
'maximum_detuning_rate': maximum_detuning_rate},
extras={'deduced_pulse_start_timing': pulse_start_ends[:, 0],
'deduced_pulse_end_timing': pulse_start_ends[:, 1],
'gap_durations': gap_durations})

# check if the minimum_segment_duration is respected in the gaps between the pulses
# as minimum_segment_duration >= 0, this also excludes overlaps
gap_durations = pulse_start_ends[1:, 0] - pulse_start_ends[:-1, 1]
if not np.all(np.logical_or(np.greater(gap_durations, minimum_segment_duration),
np.isclose(gap_durations, minimum_segment_duration))):
raise ArgumentsValueError("Distance between pulses does not respect minimum_segment_duration. "
"Try decreasing the minimum_segment_duration.",
"Try decreasing the minimum_segment_duration or incresing "
"the maximum_rabi_rate or maximum_detuning_rate",
{'dynamic_decoupling_sequence': dynamic_decoupling_sequence,
'maximum_rabi_rate': maximum_rabi_rate,
'maximum_detuning_rate': maximum_detuning_rate,
Expand Down
30 changes: 10 additions & 20 deletions tests/test_dynamical_decoupling.py
Original file line number Diff line number Diff line change
Expand Up @@ -581,23 +581,13 @@ def test_conversion_of_tightly_packed_sequence():
a sequence tightly packed with pulses, where there is no time for a gap between
the pi/2-pulses and the adjacent pi-pulses.
"""
# create a sequence containing 30 pi-pulses and 2 pi/2-pulses at the extremities
# create a sequence containing 2 pi-pulses and 2 pi/2-pulses at the extremities
dynamic_decoupling_sequence = DynamicDecouplingSequence(
duration=3.0,
offsets=np.array([0., 0.05, 0.15, 0.25, 0.35, 0.45, 0.55, 0.65, 0.75, 0.85, 0.95,
1.05, 1.15, 1.25, 1.35, 1.45, 1.55, 1.65, 1.75, 1.85, 1.95, 2.05,
2.15, 2.25, 2.35, 2.45, 2.55, 2.65, 2.75, 2.85, 2.95, 3.]),
rabi_rotations=np.array([1.57079633, 3.14159265, 3.14159265, 3.14159265, 3.14159265,
3.14159265, 3.14159265, 3.14159265, 3.14159265, 3.14159265,
3.14159265, 3.14159265, 3.14159265, 3.14159265, 3.14159265,
3.14159265, 3.14159265, 3.14159265, 3.14159265, 3.14159265,
3.14159265, 3.14159265, 3.14159265, 3.14159265, 3.14159265,
3.14159265, 3.14159265, 3.14159265, 3.14159265, 3.14159265,
3.14159265, 1.57079633]),
azimuthal_angles=np.array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]),
detuning_rotations=np.array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]),
duration=0.2,
offsets=np.array([0., 0.05, 0.15, 0.2]),
rabi_rotations=np.array([1.57079633, 3.14159265, 3.14159265, 1.57079633]),
azimuthal_angles=np.array([0., 0., 0., 0.]),
detuning_rotations=np.array([0., 0., 0., 0.]),
name=None)

driven_control = convert_dds_to_driven_control(dynamic_decoupling_sequence,
Expand All @@ -606,11 +596,11 @@ def test_conversion_of_tightly_packed_sequence():
name=None)

# There is no space for a gap between the pi/2-pulses and the adjacent pi-pulses,
# so the resulting sequence should have 32 pulses + 29 gaps = 61 segments with non-zero duration
assert sum(np.greater(driven_control.durations, 0.)) == 61
# so the resulting sequence should have 4 pulses + 1 gaps = 5 segments with non-zero duration
assert sum(np.greater(driven_control.durations, 0.)) == 5

# ... of which 32 are X pulses (i.e. rabi_rotation > 0)
assert sum(np.greater(driven_control.rabi_rates, 0.)) == 32
# ... of which 4 are X pulses (i.e. rabi_rotation > 0)
assert sum(np.greater(driven_control.rabi_rates, 0.)) == 4

def test_free_evolution_conversion():

Expand Down