From 96d0ef9ac9e67c378edaed786aedd2565e490dcd Mon Sep 17 00:00:00 2001 From: Stavros Efthymiou <35475381+stavros11@users.noreply.github.com> Date: Fri, 6 Dec 2024 21:31:59 +0400 Subject: [PATCH 1/3] fix: duration interpolated sweeper --- .../_core/instruments/qm/controller.py | 22 ++++++++++++++----- .../_core/instruments/qm/program/arguments.py | 1 + .../instruments/qm/program/instructions.py | 5 ++++- 3 files changed, 22 insertions(+), 6 deletions(-) diff --git a/src/qibolab/_core/instruments/qm/controller.py b/src/qibolab/_core/instruments/qm/controller.py index ed06c9531..f51e3d379 100644 --- a/src/qibolab/_core/instruments/qm/controller.py +++ b/src/qibolab/_core/instruments/qm/controller.py @@ -348,7 +348,8 @@ def register_duration_sweeper_pulses( ): """Register pulse with many different durations. - Needed when sweeping duration. + Needed when sweeping duration. When interpolation is used, only + a single pulse with the minimum duration is used. """ for pulse in sweeper.pulses: if isinstance(pulse, (Align, Delay)): @@ -359,10 +360,19 @@ def register_duration_sweeper_pulses( original_pulse = ( pulse if params.amplitude_pulse is None else params.amplitude_pulse ) - for value in sweeper.values.astype(int): - sweep_pulse = original_pulse.model_copy(update={"duration": value}) - sweep_op = self.register_pulse(ids[0], configs[ids[0]], sweep_pulse) - params.duration_ops.append((value, sweep_op)) + values = sweeper.values.astype(int) + if sweeper.parameter is Parameter.duration_interpolated: + sweep_pulse = original_pulse.model_copy( + update={"duration": min(values)} + ) + params.interpolated_op = self.register_pulse( + ids[0], configs[ids[0]], sweep_pulse + ) + else: + for value in values: + sweep_pulse = original_pulse.model_copy(update={"duration": value}) + sweep_op = self.register_pulse(ids[0], configs[ids[0]], sweep_pulse) + params.duration_ops.append((value, sweep_op)) def register_amplitude_sweeper_pulses( self, args: ExecutionArguments, configs: dict[str, Config], sweeper: Sweeper @@ -448,6 +458,8 @@ def preprocess_sweeps( self.register_amplitude_sweeper_pulses(args, configs, sweeper) for sweeper in find_sweepers(sweepers, Parameter.duration): self.register_duration_sweeper_pulses(args, configs, sweeper) + for sweeper in find_sweepers(sweepers, Parameter.duration_interpolated): + self.register_duration_sweeper_pulses(args, configs, sweeper) def execute_program(self, program): """Executes an arbitrary program written in QUA language.""" diff --git a/src/qibolab/_core/instruments/qm/program/arguments.py b/src/qibolab/_core/instruments/qm/program/arguments.py index efebe2e95..4b1249697 100644 --- a/src/qibolab/_core/instruments/qm/program/arguments.py +++ b/src/qibolab/_core/instruments/qm/program/arguments.py @@ -24,6 +24,7 @@ class Parameters: duration: Optional[_Variable] = None duration_ops: list[tuple[float, str]] = field(default_factory=list) interpolated: bool = False + interpolated_op: Optional[str] = None element: Optional[str] = None lo_frequency: Optional[int] = None diff --git a/src/qibolab/_core/instruments/qm/program/instructions.py b/src/qibolab/_core/instruments/qm/program/instructions.py index 700a89d7a..89a6a979a 100644 --- a/src/qibolab/_core/instruments/qm/program/instructions.py +++ b/src/qibolab/_core/instruments/qm/program/instructions.py @@ -52,7 +52,10 @@ def _play_single_waveform( if acquisition is not None: acquisition.measure(op) else: - qua.play(op, element, duration=parameters.duration) + if parameters.duration is not None: + qua.play(parameters.interpolated_op, element, duration=parameters.duration) + else: + qua.play(op, element) def _play( From 6cefd7e4dbbfabb27f03a2322b11f4ff9cf5e36e Mon Sep 17 00:00:00 2001 From: Stavros Efthymiou <35475381+stavros11@users.noreply.github.com> Date: Wed, 11 Dec 2024 00:01:05 +0400 Subject: [PATCH 2/3] chore: add assertions --- src/qibolab/_core/instruments/qm/program/instructions.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/qibolab/_core/instruments/qm/program/instructions.py b/src/qibolab/_core/instruments/qm/program/instructions.py index 89a6a979a..616cdf961 100644 --- a/src/qibolab/_core/instruments/qm/program/instructions.py +++ b/src/qibolab/_core/instruments/qm/program/instructions.py @@ -33,6 +33,8 @@ def _delay(pulse: Delay, element: str, parameters: Parameters): def _play_multiple_waveforms(element: str, parameters: Parameters): """Sweeping pulse duration using distinctly uploaded waveforms.""" + assert not parameters.interpolated + assert parameters.interpolated_op is None with qua.switch_(parameters.duration, unsafe=True): for value, sweep_op in parameters.duration_ops: if parameters.amplitude is not None: @@ -53,6 +55,9 @@ def _play_single_waveform( acquisition.measure(op) else: if parameters.duration is not None: + # sweeping duration using interpolation + # distinctly uploaded waveforms are handled by ``_play_multiple_waveforms`` + assert len(parameters.duration_ops) == 0 qua.play(parameters.interpolated_op, element, duration=parameters.duration) else: qua.play(op, element) From ca35c733da1cbfe80c978f58bff67fe25e054aba Mon Sep 17 00:00:00 2001 From: Stavros Efthymiou <35475381+stavros11@users.noreply.github.com> Date: Wed, 11 Dec 2024 00:05:03 +0400 Subject: [PATCH 3/3] docs: improve docstring --- src/qibolab/_core/instruments/qm/controller.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/qibolab/_core/instruments/qm/controller.py b/src/qibolab/_core/instruments/qm/controller.py index f51e3d379..634b76bc6 100644 --- a/src/qibolab/_core/instruments/qm/controller.py +++ b/src/qibolab/_core/instruments/qm/controller.py @@ -346,10 +346,16 @@ def register_pulses(self, configs: dict[str, Config], sequence: PulseSequence): def register_duration_sweeper_pulses( self, args: ExecutionArguments, configs: dict[str, Config], sweeper: Sweeper ): - """Register pulse with many different durations. + """Register pulses needed to implement a duration sweep. - Needed when sweeping duration. When interpolation is used, only - a single pulse with the minimum duration is used. + For standard (non-interpolated) duration sweeps, we need to + upload distinct waveforms for every different duration. + + When interpolation is used, we need to upload a single pulse + with the minimum duration of the sweeper, because the QM real- + time interpolation can only stretch and not compress arbitrary + waveforms, as documented in + https://docs.quantum-machines.co/latest/docs/Guides/features/?h=interpo#dynamic-pulse-duration """ for pulse in sweeper.pulses: if isinstance(pulse, (Align, Delay)): @@ -369,6 +375,7 @@ def register_duration_sweeper_pulses( ids[0], configs[ids[0]], sweep_pulse ) else: + assert sweeper.parameter is Parameter.duration for value in values: sweep_pulse = original_pulse.model_copy(update={"duration": value}) sweep_op = self.register_pulse(ids[0], configs[ids[0]], sweep_pulse)