From 206938ae4cbbf31dcbdb4ad1911ff205ae3ba483 Mon Sep 17 00:00:00 2001 From: Prasanna Pakkiam Date: Tue, 21 May 2024 15:38:52 +1000 Subject: [PATCH] Bug fixes and working towards automated X-Gate calibration. --- .../Experiments/Experimental/ExpCalibXrot.py | 7 +- .../Experimental/ExpCalibXrotAuto.py | 126 ++++++++++++++++++ .../Experimental/ExpGateBenchmarks.py | 2 +- sqdtoolz/Utilities/QubitGates.py | 2 +- 4 files changed, 132 insertions(+), 5 deletions(-) create mode 100644 sqdtoolz/Experiments/Experimental/ExpCalibXrotAuto.py diff --git a/sqdtoolz/Experiments/Experimental/ExpCalibXrot.py b/sqdtoolz/Experiments/Experimental/ExpCalibXrot.py index 59fb4f3..ae17d7d 100644 --- a/sqdtoolz/Experiments/Experimental/ExpCalibXrot.py +++ b/sqdtoolz/Experiments/Experimental/ExpCalibXrot.py @@ -5,7 +5,7 @@ from sqdtoolz.Experiments.Experimental.ExpCalibGE import* class ExpCalibXrot(Experiment): - def __init__(self, name, expt_config, wfmt_qubit_drive, SPEC_qubit, rotDenom, numPeriods=4, iq_indices = [0,1], **kwargs): + def __init__(self, name, expt_config, wfmt_qubit_drive, SPEC_qubit, rotDenom, numPeriods=4, numPeriodsStep=1, iq_indices = [0,1], **kwargs): #This assumes that Pi-X and frequency (i.e. Ramsey) have been reasonably calibrated... #Rotation angle is np.pi / rotDenom @@ -16,6 +16,7 @@ def __init__(self, name, expt_config, wfmt_qubit_drive, SPEC_qubit, rotDenom, nu self._rotDenom = rotDenom self._numTotalRepeats = numPeriods * self._rotDenom * 2 + self._numPeriodsStep = numPeriodsStep # self._range_amps = kwargs.get('range_amps', None) self._post_processor = kwargs.get('post_processor', None) @@ -64,7 +65,7 @@ def _run(self, file_path, sweep_vars=[], **kwargs): wfm.set_digital_segments('readout', 'qubit', ['read']) self._temp_vars = self._expt_config.update_waveforms(wfm, [('Num Pulses', wfm.get_waveform_segment('qubit', 'drivePulses'), 'NumRepeats')] ) - sweep_vars = [(self._temp_vars[0], np.arange(0,self._numTotalRepeats+1))] + sweep_vars = [(self._temp_vars[0], np.arange(0,self._numTotalRepeats+1, self._numPeriodsStep))] kwargs['skip_init_instruments'] = True @@ -90,7 +91,7 @@ def _post_process(self, data): data_y = self.norm_expt.normalise_data(data_raw_IQ, ax=axs[1]) # dpkt = dfit.get_fitted_plot(data_x, data_y, 'Drive Amplitude', 'IQ Amplitude', fig, axs[0]) - x_vals = np.arange(0,self._numTotalRepeats+1) + x_vals = np.arange(0,self._numTotalRepeats+1, self._numPeriodsStep) axs[0].plot(x_vals, data_y, 'kx') axs[0].set_xlabel('Number of Pulses'); axs[0].set_ylabel('Normalised Population') axs[0].grid(visible=True, which='minor'); axs[0].grid(visible=True, which='major', color='k'); diff --git a/sqdtoolz/Experiments/Experimental/ExpCalibXrotAuto.py b/sqdtoolz/Experiments/Experimental/ExpCalibXrotAuto.py new file mode 100644 index 0000000..66ecabd --- /dev/null +++ b/sqdtoolz/Experiments/Experimental/ExpCalibXrotAuto.py @@ -0,0 +1,126 @@ +import numpy as np +from sqdtoolz.Experiment import * +from sqdtoolz.Variable import VariablePropertyTransient +from sqdtoolz.HAL.WaveformGeneric import * +from sqdtoolz.HAL.WaveformSegments import * +from sqdtoolz.Utilities.DataFitting import * +from sqdtoolz.Experiments.Experimental.ExpCalibGE import * +from sqdtoolz.Utilities.QubitGates import QubitGatesBase + +import matplotlib.pyplot as plt +import scipy.interpolate +import scipy.optimize +from sqdtoolz.Utilities.Miscellaneous import Miscellaneous + +class ExpCalibXrotAuto(Experiment): + def __init__(self, name, expt_config, qubit_gate_obj, gate_calib, ampl_offsets, num_samples, sample_skip_step=1, **kwargs): + super().__init__(name, expt_config) + + # assert isinstance(qubit_gate_obj, QubitGatesBase), "qubit_gate_obj must be a QubitGates object (e.g. TransmonGates)" + self._qubit_gate_obj = qubit_gate_obj + + if gate_calib == 'X': + self._samples = np.arange(0, num_samples*2, 2)*sample_skip_step + elif gate_calib == 'X/2': + self._samples = np.arange(0, num_samples*sample_skip_step, sample_skip_step)*2+1 + else: + assert False, "gate_calib must be: 'X' or 'X/2'" + self._ampl_offsets = ampl_offsets + + #Calculate default load time via T1 of qubit or default to 80e-6 + def_load_time = self._qubit_gate_obj.get_qubit_SPEC()['GE T1'].Value * 6 + if def_load_time == 0: + def_load_time = 80e-6 + #Override the load-time if one is specified explicitly + self.load_time = kwargs.get('load_time', def_load_time) + + self.readout_time = kwargs.get('readout_time', 2e-6) + + self.normalise_reps = kwargs.get('normalise_reps', 10) + + self._gate_calib = gate_calib + self._sweep_range = kwargs.get('sweep_range', (-0.5,0.5)) + + def _run(self, file_path, sweep_vars=[], **kwargs): + assert len(sweep_vars) == 0, "Cannot specify sweeping variables in this experiment." + + self._setup_progress_bar(**kwargs) + self._expt_config.init_instruments() + + data_file = FileIOWriter(file_path + 'data.h5') + varAmpl = VariableInternalTransient('AmplValue') + varNumG = VariableInternalTransient('NumGates') + + self._qubit_gate_obj.calib_normalisation(self._expt_config, self.load_time, self.readout_time, file_path) + self._file_path = file_path + + origAmplX = self._qubit_gate_obj.get_qubit_SPEC()['GE X-Gate Amplitude'].Value + origAmplXon2 = self._qubit_gate_obj.get_qubit_SPEC()['GE X/2-Gate Amplitude'].Value + + for m, ampl_off in enumerate(self._ampl_offsets): + if self._gate_calib == 'X': + cur_gate = 'X' + self._qubit_gate_obj.get_qubit_SPEC()['GE X-Gate Amplitude'].Value = origAmplX + ampl_off + elif self._gate_calib == 'X/2': + cur_gate = 'X/2' + self._qubit_gate_obj.get_qubit_SPEC()['GE X/2-Gate Amplitude'].Value = origAmplXon2 + ampl_off + + for g in self._samples: + cur_seq = [cur_gate]*g + final_data = self._qubit_gate_obj.run_circuit(cur_seq, self._expt_config, self.load_time, self.readout_time) + data_file.push_datapkt(final_data, [(varAmpl, self._ampl_offsets), (varNumG, self._samples)]) + self._update_progress_bar((m+1)/self._ampl_offsets.size) + data_file.close() + + self._qubit_gate_obj.get_qubit_SPEC()['GE X-Gate Amplitude'].Value = origAmplX + self._qubit_gate_obj.get_qubit_SPEC()['GE X/2-Gate Amplitude'].Value = origAmplXon2 + + self.file_io_read_calib = FileIOReader(file_path + 'dataCalib.h5') + + return FileIOReader(file_path + 'data.h5') + + def _post_process(self, data): + arr = data.get_numpy_array() + probs = [self._qubit_gate_obj.normalise_data(x) for x in arr] + + sds = [np.std(y) for y in probs] + + fig, ax = plt.subplots(ncols=3); fig.set_figwidth(18); ax[1].grid(); ax[2].grid() + + leSpline = scipy.interpolate.UnivariateSpline(self._ampl_offsets, sds, k=2) + leSpline.set_smoothing_factor(np.mean(np.diff(self._ampl_offsets)*2)) + fitX = np.linspace(self._ampl_offsets[0], self._ampl_offsets[-1], 50) + + sol = scipy.optimize.minimize(leSpline, np.mean(self._ampl_offsets)) + + ax[0].pcolormesh(self._samples, self._ampl_offsets, probs) + ax[0].set_xlabel('Number of Gates') + ax[0].set_ylabel('Amplitude Offset') + ax[0].set_title('Ex. Population (should be flat)') + + for cur_pops in probs: + ax[1].plot(self._samples, cur_pops, 'x-') + ax[1].legend([Miscellaneous.get_units(x) for x in self._ampl_offsets]) + ax[1].set_xlabel('Number of Gates') + ax[1].set_ylabel('Excited State Population') + ax[1].set_title('Population traces over gate count') + + ax[2].plot(fitX, leSpline(fitX), 'k') + ax[2].plot(self._ampl_offsets, sds) + ax[2].plot(sol.x, leSpline(sol.x), 'x') + ax[2].set_xlabel('Amplitude Offset') + ax[2].set_ylabel('SD in population') + ax[2].set_title('Optimising Amplitude Offset') + + fig.show() + fig.savefig(self._file_path + 'Fitted Parameters.png') + + self._opt_val = sol.x[0] + + return data + + def commit_to_SPEC(self): + if self._gate_calib == 'X': + self._qubit_gate_obj.get_qubit_SPEC()['GE X-Gate Amplitude'].Value += self._opt_val + elif self._gate_calib == 'X/2': + self._qubit_gate_obj.get_qubit_SPEC()['GE X/2-Gate Amplitude'].Value += self._opt_val diff --git a/sqdtoolz/Experiments/Experimental/ExpGateBenchmarks.py b/sqdtoolz/Experiments/Experimental/ExpGateBenchmarks.py index 360c337..d801ffd 100644 --- a/sqdtoolz/Experiments/Experimental/ExpGateBenchmarks.py +++ b/sqdtoolz/Experiments/Experimental/ExpGateBenchmarks.py @@ -64,7 +64,7 @@ def _run(self, file_path, sweep_vars=[], **kwargs): final_data = self._qubit_gate_obj.run_circuit(cur_seq, self._expt_config, self.load_time, self.readout_time) data_file.push_datapkt(final_data, [(varTrial, np.arange(len(self._gate_seqs)))]) all_seqs[m] = cur_seq - self._update_progress_bar(m+1)/len(self._gate_seqs) + self._update_progress_bar((m+1)/len(self._gate_seqs)) with open(file_path + 'Benchmark_Sequences.json', 'w') as outfile: json.dump(all_seqs, outfile, indent=4) data_file.close() diff --git a/sqdtoolz/Utilities/QubitGates.py b/sqdtoolz/Utilities/QubitGates.py index 09830b9..f15f2c5 100644 --- a/sqdtoolz/Utilities/QubitGates.py +++ b/sqdtoolz/Utilities/QubitGates.py @@ -149,7 +149,7 @@ def calib_normalisation(self, expt_config, load_time, readout_time, file_path = def normalise_data(self, data): #data given as Nx2 array of N IQ-values... - return self.norm_calib(data) + return self.norm_calib.normalise_data(data) def get_qubit_SPEC(self): return self._spec_qubit