Source code for qibolab.instruments.qblox.acquisition

from dataclasses import dataclass
from typing import List, Optional

import numpy as np

SAMPLING_RATE = 1


[docs]def demodulate(input_i, input_q, frequency): """Demodulates and integrates the acquired pulse.""" # DOWN Conversion # qblox does not remove the offsets in hardware modulated_i = input_i - np.mean(input_i) modulated_q = input_q - np.mean(input_q) num_samples = modulated_i.shape[0] time = np.arange(num_samples) phase = 2 * np.pi * frequency * time / SAMPLING_RATE cosalpha = np.cos(phase) sinalpha = np.sin(phase) demod_matrix = np.sqrt(2) * np.array([[cosalpha, sinalpha], [-sinalpha, cosalpha]]) result = np.einsum("ijt,jt->i", demod_matrix, np.stack([modulated_i, modulated_q])) return np.sqrt(2) * result / num_samples
[docs]@dataclass class AveragedAcquisition: """Software Demodulation. Every readout pulse triggers an acquisition, where the 16384 i and q samples of the waveform acquired by the ADC are saved into a dedicated memory within the FPGA. This is what qblox calls *scoped acquisition*. The results of multiple shots are averaged in this memory, and cannot be retrieved independently. The resulting waveforms averages (i and q) are then demodulated and integrated in software (and finally divided by the number of samples). Since Software Demodulation relies on the data of the scoped acquisition and that data is the average of all acquisitions, **only one readout pulse per qubit is supported**, so that the averages all correspond to reading the same quantum state. """ results: dict """Data returned by qblox acquisition.""" duration: int """Duration of the readout pulse.""" frequency: int """Frequency of the readout pulse used for demodulation.""" i: Optional[List[float]] = None q: Optional[List[float]] = None @property def scope(self): return self.results["acquisition"]["scope"] @property def raw_i(self): """Average of the i waveforms for every readout pulse.""" return np.array(self.scope["path0"]["data"][0 : self.duration]) @property def raw_q(self): """Average of the q waveforms for every readout pulse.""" return np.array(self.scope["path1"]["data"][0 : self.duration]) @property def data(self): """Acquisition data to be returned to the platform. Ignores the data available in acquisition results and returns only i and q voltages. """ # TODO: to be updated once the functionality of ExecutionResults is extended if self.i is None or self.q is None: self.i, self.q = demodulate(self.raw_i, self.raw_q, self.frequency) return (self.i, self.q)
[docs]@dataclass class DemodulatedAcquisition: """Hardware Demodulation. With hardware demodulation activated, the FPGA can demodulate, integrate (average over time), and classify each shot individually, saving the results on separate bins. The raw data of each acquisition continues to be averaged as with software modulation, so there is no way to access the raw data of each shot (unless executed one shot at a time). The FPGA uses fixed point arithmetic for the demodulation and integration; if the power level of the signal at the input port is low (the minimum resolution of the ADC is 240uV) rounding precission errors can accumulate and render wrong results. It is advisable to have a power level at least higher than 5mV. """ scope: dict """Data returned by scope qblox acquisition.""" bins: dict """Binned acquisition data returned by qblox.""" duration: int """Duration of the readout pulse.""" @property def raw(self): return self.scope["acquisition"]["scope"] @property def integration(self): return self.bins["integration"] @property def shots_i(self): """I-component after demodulating and integrating every shot waveform.""" return np.array(self.integration["path0"]) / self.duration @property def shots_q(self): """Q-component after demodulating and integrating every shot waveform.""" return np.array(self.integration["path1"]) / self.duration @property def raw_i(self): """Average of the raw i waveforms for every readout pulse.""" return np.array(self.raw["path0"]["data"][0 : self.duration]) @property def raw_q(self): """Average of the raw q waveforms for every readout pulse.""" return np.array(self.raw["path1"]["data"][0 : self.duration]) @property def classified(self): """List with the results of demodulating, integrating and classifying every shot.""" return np.array(self.bins["threshold"])