pasqal-io / Pulser

Library for pulse-level/analog control of neutral atom devices. Emulator with QuTiP.
Apache License 2.0
180 stars 65 forks source link

Function to estimate delay needed between two pulses #761

Open HaroldErbin opened 1 week ago

HaroldErbin commented 1 week ago

I was wondering if it would be possible to have a function in Sequence (or Channel or anywhere convenient) to estimate the delay needed between two pulses on a physical device?

Context: I want to simulate the dynamics for a global pulse for the maximum duration allowed by AnalogDevice. In some cases, I want to start the dynamics with a "short" pulse to change the initial state. To stay below device.max_sequence_duration, I want to reduce the duration of the long pulse. This is easy for MockDevice since the pulses are sharp, but the pulses are modified for AnalogDevice to respect the physical constraints of the device. In particular, a delay can be added between the two pulses. Since I cannot get the duration of the delay before adding the long pulse to the sequence, but seq.add() raises an error because maximal duration is exceeded, I am stuck:

RuntimeError: The sequence's duration exceeded the maximum duration allowed by the device (4000 ns).

Here is the relevant part of my code:

device = pulser.AnalogDevice
channel = seq.declared_channels["ising"]
seq = pulser.Sequence(register, device)
seq.declare_channel("ising", "rydberg_global")

pi2_wf = pulser.BlackmanWaveform.from_max_val(channel.max_amp, np.pi / 2)
hadamard = pulser.Pulse.ConstantDetuning(
    amplitude=pi2_wf, detuning=0, phase=np.pi / 2, post_phase_shift=np.pi
)
seq.add(hadamard, "ising")

duration = 4000
cst_pulse = pulser.Pulse.ConstantPulse(
        duration=duration, amplitude=Omega, detuning=delta, phase=0.
)
# this add delay but also raises error
seq.add(hadamard, "ising")

I tried to investigate the source, but did not manage to extract a simple function to get the delay. For example, I thought that this would work:

state_prep_duration = hadamard.duration
delay = seq._schedule._find_add_delay(state_prep_duration, channel="ising", protocol="min-delay")

but it does not give the delay observed inside the full sequence (which I get by just decreasing duration until it works, which is not generalizable):

Channel: ising
t: 0 | Initial targets: 0, 1, 2, 3 | Phase Reference: 0.0 
t: 0->500 | Pulse(Amp=Blackman(Area: 1.57) rad/µs, Detuning=0 rad/µs, Phase=1.57) | Targets: 0, 1, 2, 3
t: 500->696 | Delay 
t: 696->3956 | Pulse(Amp=5 rad/µs, Detuning=5 rad/µs, Phase=3.14) | Targets: 0, 1, 2, 3
HGSilveri commented 1 week ago

Hi @HaroldErbin !

Thank you for the detailed issue. I agree with you that what you are trying to do is not easy and we'll add a more convenient way to do it. In the meantime, I can tell what you should do to estimate the full delay that's being added between the pulses. In your case, this delay is being enforced because the phase is changing between the two pulses, which takes some time and can't be done while the amplitude is not zero. So you have to account for two contributions:

  1. The time it takes for the previous pulse to ramp down. You can get this through Pulse.fall_time()
  2. The necessary time needed to change the phase. This is given by Channel.phase_jump_time.

So, in your case,

ising_ch_obj = seq.declared_channels["ising"]
full_delay = hadamard.fall_time(ising_ch_obj) + ising_ch_obj.phase_jump_time

I hope this helps. I'll keep the issue open until we add a better way to do this.

HaroldErbin commented 1 week ago

Hi @HGSilveri . Thanks a lot for your reply! After posting, I had arrived at the following:

full_delay = hadamard.get_full_duration(ising_ch_obj) - hadamard.duration + ising_ch_obj.phase_jump_time

but I was not sure why it worked, and your explanations are clarifying it.

HGSilveri commented 1 week ago

Hi @HGSilveri . Thanks a lot for your reply! After posting, I had arrived at the following:

full_delay = hadamard.get_full_duration(ising_ch_obj) - hadamard.duration + ising_ch_obj.phase_jump_time

but I was not sure why it worked, and your explanations are clarifying it.

Indeed, this is equivalent to what I suggested and should give exactly the same result :)