Skip to content
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
36 changes: 17 additions & 19 deletions qctrlopencontrols/dynamic_decoupling_sequences/driven_controls.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ def convert_dds_to_driven_control(
Parameters
----------
dynamic_decoupling_sequence : qctrlopencontrols.DynamicDecouplingSequence
The base DDS
The base DDS. Its offsets should be sorted in ascending order in time.
maximum_rabi_rate : float, optional
Maximum Rabi Rate; Defaults to 2*pi.
Must be greater than 0 and less or equal to UPPER_BOUND_RABI_RATE, if set.
Expand All @@ -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 @@ -206,6 +211,14 @@ def convert_dds_to_driven_control(
azimuthal_angles = np.append(azimuthal_angles, [0])
detuning_rotations = np.append(detuning_rotations, [0])

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

offsets = offsets[np.newaxis, :]
rabi_rotations = rabi_rotations[np.newaxis, :]
azimuthal_angles = azimuthal_angles[np.newaxis, :]
Expand Down Expand Up @@ -244,29 +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

# three conditions to check
# 1. Control segment start times should be monotonically increasing
# 2. Control segment end times should be monotonically increasing
# 3. Adjacent segments should not be overlapping
if (np.any(pulse_start_ends[0:-1, 0] - pulse_start_ends[1:, 0] > 0.) or
np.any(pulse_start_ends[0:-1, 1] - pulse_start_ends[1:, 1] > 0.) or
np.any(pulse_start_ends[1:, 0]-pulse_start_ends[0:-1, 1] < 0.)):

raise ArgumentsValueError('Pulse timing could not be properly deduced from '
'the sequence operation offsets. 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]})

# 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 increasing "
"the maximum_rabi_rate or the maximum_detuning_rate.",
{'dynamic_decoupling_sequence': dynamic_decoupling_sequence,
'maximum_rabi_rate': maximum_rabi_rate,
'maximum_detuning_rate': maximum_detuning_rate,
Expand Down
26 changes: 26 additions & 0 deletions tests/test_dynamical_decoupling.py
Original file line number Diff line number Diff line change
Expand Up @@ -575,6 +575,32 @@ def test_conversion_of_pulses_with_arbitrary_detuning_rotations():
assert _all_greater_or_close(driven_control.duration, minimum_segment_duration)


def test_conversion_of_tightly_packed_sequence():
"""
Tests if the method to convert a DDS to driven controls handles properly
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 2 pi-pulses and 2 pi/2-pulses at the extremities
dynamic_decoupling_sequence = DynamicDecouplingSequence(
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,
maximum_rabi_rate= 20. * np.pi,
minimum_segment_duration=0.,
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 4 pulses + 1 gaps = 5 segments with non-zero duration
assert sum(np.greater(driven_control.durations, 0.)) == 5

# ... 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