openquantumhardware / qick

QICK: Quantum Instrumentation Control Kit
MIT License
192 stars 81 forks source link

Interpolation between pulses in AxisSgInt4V1 #202

Closed sebastianhorvath closed 11 months ago

sebastianhorvath commented 11 months ago

I have run into an issue when generating multiple arb pulses with different envelopes using the AxisSgInt4V1 generator. When generating a sequence composed of one Gaussian pulse followed by a square pulse and running this sequence multiple times, the last few samples of the square pulse show up in the leading edge of the Gaussian pulse.

This leads to considerable distortions, particularly for generators with a slow sample rate (attached picture is for f_dds = 307.2 MHz). If I reload the bit stream then the first shot behaves as expected, and I can also work around it by playing a short pulse with both in-phase and quadrature data set to zeros after the square pulse. I therefore assume this is due to the interpolation filter including samples from the previous pulse.

tempImage0NgT4z

I don't believe this is an issue with the python code, since I originally noticed this using our tproc v2 codebase and then reproduced it with standard qick (the only code that's shared is the load_pulse_data method). That being said, for the sample rate of the tproc v1 firmware (f_dds=1720.32 MHz) the problem was much less pronounced.

I should note that this isn't a serious problem that's impacting our experiments since we can always interleave short pulses with envelopes set to zeros. However, I wanted to post on here in case you're not aware of it or someone else notices a similar issue.

Here's the code I ran using standard qick:

class LoopbackProgram(AveragerProgram):
    def __init__(self,soccfg,cfg):
        super().__init__(soccfg,cfg)

    def initialize(self):
        cfg=self.cfg   
        res_ch = cfg["res_ch"]
        self.declare_gen(ch=res_ch, nqz=1)

        sigma = cfg["sigma"]
        nsigma = 5
        samples_per_clock = self.soccfg['gens'][res_ch]['samps_per_clk']
        idata_gauss = helpers.gauss(mu=sigma*samples_per_clock*nsigma/2,
                              si=sigma*samples_per_clock,
                              length=sigma*samples_per_clock*nsigma,
                              maxv=np.iinfo(np.int16).max-1)

        idata_square = np.ones(len(idata_gauss)) * np.iinfo(np.int16).max-1
        self.add_pulse(ch=res_ch, name="gauss", idata=idata_gauss*0.2) 
        self.add_pulse(ch=res_ch, name="square", idata=idata_square) 

        self.synci(200)  # give processor some time to configure pulses

    def body(self):
        freq=self.freq2reg(self.cfg["pulse_freq"], gen_ch=self.cfg["res_ch"])
        self.synci(200) 
        self.set_pulse_registers(ch=self.cfg["res_ch"], style="arb", freq=freq, phase=0, gain=self.cfg["pulse_gain"], waveform="gauss")
        self.pulse(ch=self.cfg["res_ch"]) 
        self.synci(200) 
        self.set_pulse_registers(ch=self.cfg["res_ch"], style="arb", freq=freq, phase=0, gain=self.cfg["pulse_gain"], waveform="square")
        self.pulse(ch=self.cfg["res_ch"]) 

config={"res_ch": 4, # --Fixed
        "reps":1, # --Fixed
        "sigma": 100, # [Clock ticks]        
        "pulse_gain": 30000, # [DAC units]
        "pulse_freq": 50, # [MHz]
        "soft_avgs":10
       }

prog = LoopbackProgram(soccfg, config)
soc.reset_gens() # clear any DC or periodic values on generators
prog.config_all(soccfg)
soc.tproc.start()
meeg commented 11 months ago

Thanks - yes, I think this is the same problem described in #37. Your diagnosis is basically correct. My understanding is that if you play two arbitrary-envelope pulses, the second one will start with the first (!) samples of the first pulse. This is related to the interpolator, but it's really a bug.

Your workaround is correct. However, if you define your square pulses as const style instead of arb they should avoid this problem completely - have you tried?

sebastianhorvath commented 11 months ago

Cool thanks. Yep I agree, looks exactly like issue #37 -- I had a quick search through past issues but ended up missing it.

And that's right, if I change the square pulses to const this doesn't show up. The square/gauss pulse sequence I used is a bit contrived and I selected it primarily since it provides a nice visual of the issue.

In practice, the problem is primarily for pulses where we need a well defined phase relationship between adjacent pulses with an arb amplitude envelopes. However, for now we have enough channels w/ AxisSignalGenV6 generators to support those cases, and if we ever need run something like this with the interpolater the workaround should do the trick. Thanks!