diff --git a/src/qibolab/_core/instruments/qm/controller.py b/src/qibolab/_core/instruments/qm/controller.py index ed06c9531..634b76bc6 100644 --- a/src/qibolab/_core/instruments/qm/controller.py +++ b/src/qibolab/_core/instruments/qm/controller.py @@ -346,9 +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. + 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)): @@ -359,10 +366,20 @@ 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: + 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) + params.duration_ops.append((value, sweep_op)) def register_amplitude_sweeper_pulses( self, args: ExecutionArguments, configs: dict[str, Config], sweeper: Sweeper @@ -448,6 +465,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..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: @@ -52,7 +54,13 @@ 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: + # 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) def _play(