Skip to content
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

Implemented delay qblox qprogram #793

Merged
merged 18 commits into from
Sep 9, 2024
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
4 changes: 4 additions & 0 deletions src/qililab/platform/platform.py
Original file line number Diff line number Diff line change
Expand Up @@ -777,6 +777,7 @@ def execute_qprogram( # pylint: disable=too-many-locals
for bus in buses
if isinstance(bus.system_control, ReadoutSystemControl)
}
delays = {bus.alias: int(bus.get_parameter(Parameter.DELAY)) for bus in buses}
# Determine what should be the initial value of the markers for each bus.
# This depends on the model of the associated Qblox module and the `output` setting of the associated sequencer.
markers = {}
Expand All @@ -797,6 +798,7 @@ def execute_qprogram( # pylint: disable=too-many-locals
return self._execute_qprogram_with_qblox(
qprogram=qprogram,
times_of_flight=times_of_flight,
delays=delays,
markers=markers,
bus_mapping=bus_mapping,
calibration=calibration,
Expand Down Expand Up @@ -834,6 +836,7 @@ def _execute_qprogram_with_qblox( # pylint: disable=too-many-locals
self,
qprogram: QProgram,
times_of_flight: dict[str, int],
delays: dict[str, int],
markers: dict[str, str],
bus_mapping: dict[str, str] | None = None,
calibration: Calibration | None = None,
Expand All @@ -846,6 +849,7 @@ def _execute_qprogram_with_qblox( # pylint: disable=too-many-locals
bus_mapping=bus_mapping,
calibration=calibration,
times_of_flight=times_of_flight,
delays=delays,
markers=markers,
)
buses = {bus_alias: self._get_bus_by_alias(alias=bus_alias) for bus_alias in sequences}
Expand Down
46 changes: 37 additions & 9 deletions src/qililab/qprogram/qblox_compiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,9 @@ def __init__(self) -> None:
# Time of flight. Defaults to minimum_wait_duration and is updated if times_of_flight parameter is provided during compilation.
self.time_of_flight = QbloxCompiler.minimum_wait_duration

# Delay. Defaults 0 delay and is updated if delays parameter is provided within the runcard.
self.delay = 0


class QbloxCompiler: # pylint: disable=too-few-public-methods
"""A class for compiling QProgram to QBlox hardware."""
Expand Down Expand Up @@ -130,12 +133,13 @@ def __init__(self) -> None:
self._buses: dict[str, BusCompilationInfo]
self._sync_counter: int

def compile(
def compile( # noqa: max-complexity=25
self,
qprogram: QProgram,
bus_mapping: dict[str, str] | None = None,
calibration: Calibration | None = None,
times_of_flight: dict[str, int] | None = None,
delays: dict[str, int] | None = None,
markers: dict[str, str] | None = None,
) -> tuple[Sequences, Acquisitions]:
"""Compile QProgram to qpysequence.Sequence
Expand All @@ -150,9 +154,21 @@ def compile(
"""

def traverse(block: Block):
delay_implemented = False
for bus in self._buses:
self._buses[bus].qprogram_block_stack.append(block)
for element in block.elements:
for element in block.elements: # pylint: disable=too-many-nested-blocks
if isinstance(element, Play) and not delay_implemented:
for bus in self._buses:
if self._buses[bus].delay > 0:
self._handle_wait(element=Wait(bus=bus, duration=self._buses[bus].delay), delay=True)
elif self._buses[bus].delay < 0:
for other_buses in self._buses:
if other_buses != bus:
self._handle_wait(
element=Wait(bus=other_buses, duration=-self._buses[bus].delay), delay=True
)
delay_implemented = True
handler = self._handlers.get(type(element))
if not handler:
raise NotImplementedError(f"{element.__class__} is currently not supported in QBlox.")
Expand All @@ -162,7 +178,7 @@ def traverse(block: Block):
if not self._qprogram.qblox.disable_autosync and isinstance(
element, (ForLoop, Parallel, Loop, Average)
):
self._handle_sync(element=Sync(buses=None))
self._handle_sync(element=Sync(buses=None), delay=True)
if appended:
for bus in self._buses:
self._buses[bus].qpy_block_stack.pop()
Expand All @@ -187,6 +203,11 @@ def traverse(block: Block):
for bus in self._buses.keys() & times_of_flight.keys():
self._buses[bus].time_of_flight = times_of_flight[bus]

# Pre-processing: Update delay
if delays is not None:
for bus in self._buses.keys() & delays.keys():
self._buses[bus].delay = delays[bus]

# Pre-processing: Set markers ON/OFF
for bus in self._buses:
mask = markers[bus] if markers is not None and bus in markers else "0000"
Expand Down Expand Up @@ -389,7 +410,7 @@ def _handle_set_markers(self, element: SetMarkers):
component=QPyInstructions.SetMrk(marker_outputs=marker_outputs)
)

def _handle_wait(self, element: Wait):
def _handle_wait(self, element: Wait, delay: bool = False):
duration: QPyProgram.Register | int
if isinstance(element.duration, Variable):
duration = self._buses[element.bus].variable_to_register[element.duration]
Expand All @@ -400,7 +421,8 @@ def _handle_wait(self, element: Wait):
else:
convert = QbloxCompiler._convert_value(element)
duration = convert(element.duration)
self._buses[element.bus].static_duration += duration
if not delay:
self._buses[element.bus].static_duration += duration
# loop over wait instructions if static duration is longer than allowed qblox max wait time of 2**16 -4
self._handle_add_waits(bus=element.bus, duration=duration)

Expand All @@ -423,7 +445,7 @@ def _handle_add_waits(self, bus: str, duration: int):
component=QPyInstructions.Wait(wait_time=duration % INST_MAX_WAIT)
)

def _handle_sync(self, element: Sync):
def _handle_sync(self, element: Sync, delay: bool = False):
# Get the buses involved in the sync operation.
buses = set(element.buses or self._buses)

Expand All @@ -442,16 +464,22 @@ def _handle_sync(self, element: Sync):
self.__handle_dynamic_sync(buses=buses)
else:
# If no, calculating the difference is trivial.
self.__handle_static_sync(buses=buses)
self.__handle_static_sync(buses=buses, delay=delay)

# In any case, mark al buses as synced.
for bus in buses:
self._buses[bus].marked_for_sync = False

def __handle_static_sync(self, buses: set[str]):
def __handle_static_sync(self, buses: set[str], delay: bool = False):
max_duration = max(self._buses[bus].static_duration for bus in buses)
if delay:
max_delay = max(self._buses[bus].delay for bus in buses)
for bus in buses:
duration_diff = max_duration - self._buses[bus].static_duration
if delay:
delay_diff = max_delay - self._buses[bus].delay
duration_diff = max_duration - self._buses[bus].static_duration + delay_diff
else:
duration_diff = max_duration - self._buses[bus].static_duration
if duration_diff > 0:
# loop over wait instructions if static duration is longer than allowed qblox max wait time of 2**16 -4
self._handle_add_waits(bus=bus, duration=duration_diff)
Expand Down
117 changes: 117 additions & 0 deletions tests/qprogram/test_qblox_compiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -1187,6 +1187,123 @@ def test_multiple_play_operations_with_no_Q_waveform(self, multiple_play_operati
"""
assert is_q1asm_equal(sequences["drive"], drive_str)

def test_delay(self, average_with_for_loop_nshots: QProgram):
compiler = QbloxCompiler()
sequences, _ = compiler.compile(qprogram=average_with_for_loop_nshots, delays={"drive": 20})

assert len(sequences) == 2
assert "drive" in sequences
assert "readout" in sequences

drive_str = """
setup:
wait_sync 4
set_mrk 0
upd_param 4

main:
move 1000, R0
avg_0:
move 3, R1
move 0, R2
loop_0:
wait 20
play 0, 1, 40
wait 2960
add R2, 1, R2
loop R1, @loop_0
loop R0, @avg_0
set_mrk 0
upd_param 4
stop
"""
readout_str = """
setup:
wait_sync 4
set_mrk 0
upd_param 4

main:
move 1000, R0
avg_0:
move 1, R1
move 0, R2
move 0, R3
move 3, R4
move 0, R5
loop_0:
play 0, 1, 1000
acquire_weighed 0, R3, R2, R1, 2000
add R3, 1, R3
wait 20
add R5, 1, R5
loop R4, @loop_0
loop R0, @avg_0
set_mrk 0
upd_param 4
stop
"""
assert is_q1asm_equal(sequences["drive"], drive_str)
assert is_q1asm_equal(sequences["readout"], readout_str)

def test_negative_delay(self, average_with_for_loop_nshots: QProgram):
compiler = QbloxCompiler()
sequences, _ = compiler.compile(qprogram=average_with_for_loop_nshots, delays={"drive": -20})

assert len(sequences) == 2
assert "drive" in sequences
assert "readout" in sequences

drive_str = """
setup:
wait_sync 4
set_mrk 0
upd_param 4

main:
move 1000, R0
avg_0:
move 3, R1
move 0, R2
loop_0:
play 0, 1, 40
wait 2980
add R2, 1, R2
loop R1, @loop_0
loop R0, @avg_0
set_mrk 0
upd_param 4
stop
"""
readout_str = """
setup:
wait_sync 4
set_mrk 0
upd_param 4

main:
move 1000, R0
avg_0:
move 1, R1
move 0, R2
move 0, R3
move 3, R4
move 0, R5
loop_0:
wait 20
play 0, 1, 1000
acquire_weighed 0, R3, R2, R1, 2000
add R3, 1, R3
add R5, 1, R5
loop R4, @loop_0
loop R0, @avg_0
set_mrk 0
upd_param 4
stop
"""
assert is_q1asm_equal(sequences["drive"], drive_str)
assert is_q1asm_equal(sequences["readout"], readout_str)

@pytest.mark.parametrize(
"start,stop,step,expected_result",
[(0, 10, 1, 11), (10, 0, -1, 11), (1, 2.05, 0.1, 11)],
Expand Down
Loading