From 3ecda668251a6f26212221e3b017dfe3b128a89b Mon Sep 17 00:00:00 2001 From: Marios Samiotis Date: Thu, 28 Mar 2024 20:06:38 +0100 Subject: [PATCH] calibrate_ssro_coarse works --- .../multiplexed_readout_analysis.py | 406 +++++++++++++----- .../meta_instrument/HAL_Device.py | 92 +++- .../qubit_objects/HAL_Transmon.py | 76 ++-- pycqed/measurement/sweep_functions.py | 9 +- 4 files changed, 429 insertions(+), 154 deletions(-) diff --git a/pycqed/analysis_v2/multiplexed_readout_analysis.py b/pycqed/analysis_v2/multiplexed_readout_analysis.py index 3b4d02aa35..147c0d9e54 100644 --- a/pycqed/analysis_v2/multiplexed_readout_analysis.py +++ b/pycqed/analysis_v2/multiplexed_readout_analysis.py @@ -988,7 +988,12 @@ def extract_data(self): def process_data(self): - length = int(len(self.raw_data_dict['data'][:, 0])/2) + # Leo DC change. + # Why is the weight function half the length? + #length = int(len(self.raw_data_dict['data'][:, 0])/2) + length = int(len(self.raw_data_dict['data'][:, 0])) + + self.proc_data_dict['Time_data'] = np.arange(length)/1.8e9 self.proc_data_dict['Channel_0_data'] = self.raw_data_dict['data'][:, 1][:length] self.proc_data_dict['Channel_1_data'] = self.raw_data_dict['data'][:, 2][:length] @@ -1028,7 +1033,8 @@ def __init__(self, q_target: str, A_ground, A_excited, t_start: str = None, t_stop: str = None, label: str = '', - options_dict: dict = None, extract_only: bool = False, + options_dict: dict = None, + extract_only: bool = False, auto=True): super().__init__(t_start=t_start, t_stop=t_stop, @@ -1069,9 +1075,19 @@ def process_data(self): W_I = I_e - I_g W_Q = Q_e - Q_g + # remove average + W_I-=np.average(W_I) + W_Q-=np.average(W_Q) + #normalize weights - W_I = W_I/np.max(W_I) - W_Q = W_Q/np.max(W_Q) + maxabsW_I=np.max([np.abs(np.max(W_I)), np.abs(np.min(W_I))]) + maxabsW_Q=np.max([np.abs(np.max(W_Q)), np.abs(np.min(W_Q))]) + maxabs=np.max([maxabsW_I, maxabsW_Q]) + W_I = W_I/maxabs + W_Q = W_Q/maxabs + + #W_I = W_I/np.max(W_I) + #W_Q = W_Q/np.max(W_Q) C = W_I + 1j*W_Q @@ -1502,7 +1518,7 @@ def __init__(self, self.thresholds = thresholds if error_type is None: self.error_type = 'all' - elif error_type == 'all' or error_type == 'meas' or error_type == 'flip': + elif error_type is 'all' or error_type is 'meas' or error_type is 'flip': self.error_type = error_type else: raise ValueError('Error type "{}" not supported.'.format(error_type)) @@ -1549,18 +1565,18 @@ def process_data(self): [self.nr_measurements*j:self.nr_measurements*j+self.cycles] # Digitize data shots = np.array([-1 if s < self.thresholds[q] else 1 for s in raw_shots]) - if state == '0': + if state is '0': shots_f = np.pad(shots, pad_width=1, mode='constant', constant_values=-1)[:-1] # introduce 0 in begining # Detect errors error = (shots_f[1:]-shots_f[:-1])/2 - elif state == '1': + elif state is '1': shots_f = -1*shots shots_f = np.pad(shots_f, pad_width=1, mode='constant', constant_values=-1)[:-1] # introduce 0 in begining # Detect errors error = (shots_f[1:]-shots_f[:-1])/2 - elif state == 'pi': + elif state is 'pi': shots_f = np.pad(shots, pad_width=1, mode='constant', constant_values=-1)[:-1] # introduce 0 in begining # Detect errors error = shots_f[:-1]+shots_f[1:]-1 @@ -1576,11 +1592,11 @@ def process_data(self): # count errors nr_errors = np.sum(abs(error)) # Get RTE - if self.error_type == 'all': + if self.error_type is 'all': RTE = next((i+1 for i, x in enumerate(error) if x), None) # All errors - elif self.error_type == 'meas': + elif self.error_type is 'meas': RTE = next((i+1 for i, x in enumerate(measr) if x), None) # Errors due to misdiagnosis - elif self.error_type == 'flip': + elif self.error_type is 'flip': RTE = next((i+1 for i, x in enumerate(flipr) if x), None) # Errors due to flips if RTE is None: @@ -1732,18 +1748,21 @@ def run_post_extract(self): close_figs=self.options_dict.get('close_figs', True), tag_tstamp=self.options_dict.get('tag_tstamp', True)) +###### RDC changed on 02-02-2023 (commented the previous code and add the new one) ########## + class measurement_QND_analysis(ba.BaseDataAnalysis): """ - This analysis extracts measurement QND metrics + This analysis extracts measurement QND metrics For details on the procedure see: arXiv:2110.04285 """ def __init__(self, qubit:str, - t_start: str = None, + f_state: bool = False, + t_start: str = None, t_stop: str = None, label: str = '', - options_dict: dict = None, + options_dict: dict = None, extract_only: bool = False, auto=True ): @@ -1754,6 +1773,7 @@ def __init__(self, extract_only=extract_only) self.qubit = qubit + self.f_state = f_state if auto: self.run_analysis() @@ -1765,26 +1785,79 @@ def extract_data(self): """ self.get_timestamps() self.timestamp = self.timestamps[0] - data_fp = get_datafilepath_from_timestamp(self.timestamp) param_spec = {'data': ('Experimental Data/Data', 'dset'), 'value_names': ('Experimental Data', 'attr:value_names')} - self.raw_data_dict = h5d.extract_pars_from_datafile( data_fp, param_spec) - # Parts added to be compatible with base analysis data requirements self.raw_data_dict['timestamps'] = self.timestamps self.raw_data_dict['folder'] = os.path.split(data_fp)[0] def process_data(self): - - Cal_0, Cal_1 = (self.raw_data_dict['data'][3::5,1], self.raw_data_dict['data'][3::5,2]), (self.raw_data_dict['data'][4::5,1], self.raw_data_dict['data'][4::5,2]) - M1, M2, M3 = self.raw_data_dict['data'][0::5,1], self.raw_data_dict['data'][1::5,1], self.raw_data_dict['data'][2::5,1] - th = estimate_threshold(Cal_0[0], Cal_1[0]) - M1_dig = np.array([ 0 if m np.mean(I1_proc): + I0_proc *= -1 + I1_proc *= -1 + IM1_proc *= -1 + IM2_proc *= -1 + IM3_proc *= -1 + # Calculate optimal threshold + ubins_A_0, ucounts_A_0 = np.unique(I0_proc, return_counts=True) + ubins_A_1, ucounts_A_1 = np.unique(I1_proc, return_counts=True) + ucumsum_A_0 = np.cumsum(ucounts_A_0) + ucumsum_A_1 = np.cumsum(ucounts_A_1) + # merge |0> and |1> shot bins + all_bins_A = np.unique(np.sort(np.concatenate((ubins_A_0, ubins_A_1)))) + # interpolate cumsum for all bins + int_cumsum_A_0 = np.interp(x=all_bins_A, xp=ubins_A_0, fp=ucumsum_A_0, left=0) + int_cumsum_A_1 = np.interp(x=all_bins_A, xp=ubins_A_1, fp=ucumsum_A_1, left=0) + norm_cumsum_A_0 = int_cumsum_A_0/np.max(int_cumsum_A_0) + norm_cumsum_A_1 = int_cumsum_A_1/np.max(int_cumsum_A_1) + # Calculating threshold + F_vs_th = (1-(1-abs(norm_cumsum_A_0-norm_cumsum_A_1))/2) + opt_idxs = np.argwhere(F_vs_th == np.amax(F_vs_th)) + opt_idx = int(round(np.average(opt_idxs))) + threshold = all_bins_A[opt_idx] + # digitize data + P0_dig = np.array([ 0 if s=0: + WeightFunction_I = np.concatenate([WeightFunction_I, np.zeros(NumZeros)]) + else: + WeightFunction_I = WeightFunction_I[:NumZeros] + + WFlength=WFlength_Q + NumZeros=4096-WFlength + if NumZeros>=0: + WeightFunction_Q = np.concatenate([WeightFunction_Q, np.zeros(NumZeros)]) + else: + WeightFunction_Q = WeightFunction_Q[:NumZeros] + + + Q_target.ro_acq_weight_func_I(WeightFunction_I) + Q_target.ro_acq_weight_func_Q(WeightFunction_Q) + + # this ws the original line of code. + #Q_target.ro_acq_weight_func_I(B.qoi['W_I']) + #Q_target.ro_acq_weight_func_Q(B.qoi['W_Q']) + Q_target.ro_acq_weight_type('optimal') if verify: + # do an SSRO run using the new weight functions. Q_target._prep_ro_integration_weights() Q_target._prep_ro_instantiate_detectors() + ssro_dict= self.measure_ssro_single_qubit( qubits=qubits, - q_target=q_target - ) + q_target=q_target, + integration_length = Q_target.ro_acq_integration_length(), + initialize=True) + + # This bit added by LDC to update fit results. + Q_target.F_init(1-ssro_dict['Post_residual_excitation']) + Q_target.F_ssro(ssro_dict['Post_F_a']) + if return_analysis: return ssro_dict else: diff --git a/pycqed/instrument_drivers/meta_instrument/qubit_objects/HAL_Transmon.py b/pycqed/instrument_drivers/meta_instrument/qubit_objects/HAL_Transmon.py index a895a440e6..a551ee8eec 100644 --- a/pycqed/instrument_drivers/meta_instrument/qubit_objects/HAL_Transmon.py +++ b/pycqed/instrument_drivers/meta_instrument/qubit_objects/HAL_Transmon.py @@ -1536,6 +1536,7 @@ def calibrate_ssro_fine( self, MC: Optional[MeasurementControl] = None, nested_MC: Optional[MeasurementControl] = None, + nr_shots_per_case: int = 2 ** 13, # 8192 start_freq=None, start_amp=None, start_freq_step=None, @@ -1571,7 +1572,7 @@ def calibrate_ssro_fine( ''' ## check single-qubit ssro first, if assignment fidelity below 92.5%, run optimizer - self.measure_ssro(post_select=True) + self.measure_ssro(nr_shots_per_case=nr_shots_per_case, post_select=True) if self.F_ssro() > check_threshold: return True @@ -1611,7 +1612,6 @@ def calibrate_ssro_fine( ad_func_pars = {'adaptive_function': nelder_mead, 'x0': [self.ro_freq(), self.ro_pulse_amp()], 'initial_step': [start_freq_step, start_amp_step], - 'no_improv_break': 10, 'minimize': False, 'maxiter': 20, 'f_termination': optimize_threshold} @@ -2438,11 +2438,13 @@ def calibrate_optimal_weights( optimal_IQ: bool = False, measure_transients_CCL_switched: bool = False, prepare: bool = True, - disable_metadata: bool = False, + disable_metadata: bool = True, nr_shots_per_case: int = 2 ** 13, post_select: bool = False, averages: int = 2 ** 15, post_select_threshold: float = None, + depletion_analysis: bool = False, + depletion_optimization_window = None ) -> bool: """ Measures readout transients for the qubit in ground and excited state to indicate @@ -2478,10 +2480,18 @@ def calibrate_optimal_weights( analyze=analyze, depletion_analysis=False) else: - transients = self.measure_transients(MC=MC, analyze=analyze, - depletion_analysis=False, - disable_metadata=disable_metadata) - if analyze: + if depletion_analysis: + a, transients = self.measure_transients(MC=MC, analyze=analyze, + depletion_analysis=depletion_analysis, + disable_metadata=disable_metadata, + depletion_optimization_window = depletion_optimization_window) + else: + transients = self.measure_transients(MC=MC, analyze=analyze, + depletion_analysis=depletion_analysis, + disable_metadata=disable_metadata) + + + if analyze and depletion_analysis == False: ma.Input_average_analysis(IF=self.ro_freq_mod()) self.ro_acq_averages(old_avg) @@ -2496,24 +2506,12 @@ def calibrate_optimal_weights( # fixme: deviding the weight functions by four to not have overflow in # thresholding of the UHFQC weight_scale_factor = 1. / (4 * np.max([maxI, maxQ])) - W_func_I = np.array(weight_scale_factor * optimized_weights_I) - W_func_Q = np.array(weight_scale_factor * optimized_weights_Q) - - # Smooth optimal weight functions - T = np.arange(len(W_func_I))/1.8e9 - W_demod_func_I = np.real( (W_func_I + 1j*W_func_Q)*np.exp(2j*np.pi * T * self.ro_freq_mod()) ) - W_demod_func_Q = np.imag( (W_func_I + 1j*W_func_Q)*np.exp(2j*np.pi * T * self.ro_freq_mod()) ) - - from scipy.signal import medfilt - W_dsmooth_func_I = medfilt(W_demod_func_I, 101) - W_dsmooth_func_Q = medfilt(W_demod_func_Q, 101) - - W_smooth_func_I = np.real( (W_dsmooth_func_I + 1j*W_dsmooth_func_Q)*np.exp(-2j*np.pi * T * self.ro_freq_mod()) ) - W_smooth_func_Q = np.imag( (W_dsmooth_func_I + 1j*W_dsmooth_func_Q)*np.exp(-2j*np.pi * T * self.ro_freq_mod()) ) + optimized_weights_I = np.array(weight_scale_factor * optimized_weights_I) + optimized_weights_Q = np.array(weight_scale_factor * optimized_weights_Q) if update: - self.ro_acq_weight_func_I(np.array(W_smooth_func_I)) - self.ro_acq_weight_func_Q(np.array(W_smooth_func_Q)) + self.ro_acq_weight_func_I(optimized_weights_I) + self.ro_acq_weight_func_Q(optimized_weights_Q) if optimal_IQ: self.ro_acq_weight_type('optimal IQ') else: @@ -2530,7 +2528,11 @@ def calibrate_optimal_weights( return ssro_dict if verify: warnings.warn('Not verifying as settings were not updated.') - return True + + if depletion_analysis: + return a + else: + return True ########################################################################## # measure_ functions (overrides for class Qubit) @@ -2656,7 +2658,7 @@ def measure_ssro( SNR_detector: bool = False, shots_per_meas: int = 2 ** 16, vary_residual_excitation: bool = True, - disable_metadata: bool = False, + disable_metadata: bool = True, label: str = '' ): # USED_BY: device_dependency_graphs_v2.py, @@ -3005,13 +3007,18 @@ def measure_transients( depletion_analysis_plot: bool = True, depletion_optimization_window=None, disable_metadata: bool = False, - plot_max_time=None + plot_max_time=None, + averages: int=2**15 ): # docstring from parent class if MC is None: MC = self.instr_MC.get_instr() if plot_max_time is None: - plot_max_time = self.ro_acq_integration_length() + 250e-9 + plot_max_time = self.ro_acq_integration_length() + 1000e-9 + + # store the original averaging settings so that we can restore them at the end. + old_avg = self.ro_acq_averages() + self.ro_acq_averages(averages) if prepare: self.prepare_for_timedomain() @@ -3052,17 +3059,30 @@ def measure_transients( data = MC.run( 'Measure_transients{}_{}'.format(self.msmt_suffix, i), disable_snapshot_metadata=disable_metadata) + dset = data['dset'] transients.append(dset.T[1:]) if analyze: ma.MeasurementAnalysis() + + # restore initial averaging settings. + self.ro_acq_averages(old_avg) + if depletion_analysis: + print('Sweeping parameters:') + print(r"- amp0 = {}".format(self.ro_pulse_up_amp_p0())) + print(r"- amp1 = {}".format(self.ro_pulse_up_amp_p1())) + print(r"- amp2 = {}".format(self.ro_pulse_down_amp0())) + print(r"- amp3 = {}".format(self.ro_pulse_down_amp1())) + print(r"- phi2 = {}".format(self.ro_pulse_down_phi0())) + print(r"- phi3 = {}".format(self.ro_pulse_down_phi1())) + a = ma.Input_average_analysis( IF=self.ro_freq_mod(), optimization_window=depletion_optimization_window, plot=depletion_analysis_plot, plot_max_time=plot_max_time) - return a + return a, [np.array(t, dtype=np.float64) for t in transients] # before it was only a else: return [np.array(t, dtype=np.float64) for t in transients] diff --git a/pycqed/measurement/sweep_functions.py b/pycqed/measurement/sweep_functions.py index 60157057c9..55c8b5f76b 100644 --- a/pycqed/measurement/sweep_functions.py +++ b/pycqed/measurement/sweep_functions.py @@ -463,13 +463,14 @@ def __init__(self, name, qubit, ro_lutman, idx, parameter): self.idx = idx def set_parameter(self, val): - LO_freq = self.ro_lm.LO_freq() - IF_freq = val - LO_freq + # LO_freq = self.qubit.ro_freq() - self.qubit.ro_freq_mod() + # LO_freq = self.ro_lm.LO_freq() + # IF_freq = val - LO_freq # Parameter 1 will be qubit.ro_freq() - # self.qubit.ro_freq.set(val) + self.qubit.ro_freq.set(val) # Parameter 2 will be qubit.ro_freq_mod() - self.qubit.ro_freq_mod.set(IF_freq) + # self.qubit.ro_freq_mod.set(IF_freq) # self.ro_lm.set('M_modulation_R{}'.format(self.idx), IF_freq) # self.ro_lm.load_waveforms_onto_AWG_lookuptable()