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