From bf0de4228ececccab93d8961753d8d7af855d9a2 Mon Sep 17 00:00:00 2001 From: FKMalino Date: Tue, 28 Aug 2018 10:31:49 +0200 Subject: [PATCH 01/26] Redefining martinis pulse in terms of freqs and anharmoninicties --- .../meta_instrument/LutMans/flux_lutman.py | 79 ++++++++++--------- 1 file changed, 40 insertions(+), 39 deletions(-) diff --git a/pycqed/instrument_drivers/meta_instrument/LutMans/flux_lutman.py b/pycqed/instrument_drivers/meta_instrument/LutMans/flux_lutman.py index 20b9822197..89593e7db1 100644 --- a/pycqed/instrument_drivers/meta_instrument/LutMans/flux_lutman.py +++ b/pycqed/instrument_drivers/meta_instrument/LutMans/flux_lutman.py @@ -323,19 +323,23 @@ def _add_waveform_parameters(self): initial_value=np.nan, parameter_class=ManualParameter) - self.add_parameter('cz_freq_01_max', vals=vals.Numbers(), + self.add_parameter('cz_f_q0', vals=vals.Numbers(), # initial value is chosen to not raise errors initial_value=6e9, unit='Hz', parameter_class=ManualParameter) + self.add_parameter('cz_f_q1', vals=vals.Numbers(), + # initial value is chosen to not raise errors + initial_value=6e9, + unit='Hz', parameter_class=ManualParameter) + self.add_parameter('cz_anharmonicity_q0', vals=vals.Numbers(), + # initial value is chosen to not raise errors + initial_value=-300e6, + unit='Hz', parameter_class=ManualParameter) + self.add_parameter('cz_J2', vals=vals.Numbers(), unit='Hz', # initial value is chosen to not raise errors initial_value=15e6, parameter_class=ManualParameter) - self.add_parameter('cz_freq_interaction', vals=vals.Numbers(), - # initial value is chosen to not raise errors - initial_value=5e9, - unit='Hz', - parameter_class=ManualParameter) self.add_parameter('cz_phase_corr_length', unit='s', initial_value=5e-9, vals=vals.Numbers(), @@ -450,11 +454,11 @@ def _gen_cz(self): lambda_2=self.cz_lambda_2(), lambda_3=self.cz_lambda_3(), theta_f=self.cz_theta_f(), - f_01_max=self.cz_freq_01_max(), + f_q0=self.cz_f_q0(), + f_q1=self.cz_f_q1(), + anharmonicity_q0=self.cz_anharmonicity_q0(), J2=self.cz_J2(), - f_interaction=self.cz_freq_interaction(), - sampling_rate=self.sampling_rate(), - return_unit='f01') + sampling_rate=self.sampling_rate()) return dac_scale_factor*self.detuning_to_amp( self.cz_freq_01_max() - CZ) else: @@ -465,13 +469,11 @@ def _gen_cz(self): lambda_2=self.cz_lambda_2(), lambda_3=self.cz_lambda_3(), theta_f=self.cz_theta_f(), - f_01_max=self.cz_freq_01_max(), - # V_per_phi0=self.cz_V_per_phi0(), + f_q0=self.cz_f_q0(), + f_q1=self.cz_f_q1(), + anharmonicity_q0=self.cz_anharmonicity_q0(), J2=self.cz_J2(), - # E_c=self.cz_E_c(), - f_interaction=self.cz_freq_interaction(), - sampling_rate=self.sampling_rate(), - return_unit='f01') + sampling_rate=self.sampling_rate()) half_CZ_A = dac_scale_factor*self.detuning_to_amp( self.cz_freq_01_max() - half_CZ_A) @@ -493,16 +495,14 @@ def _gen_cz(self): half_CZ_B = wf.martinis_flux_pulse( length=self.cz_length()*(1-self.czd_length_ratio()), - lambda_2=d_lambda_2, - lambda_3=d_lambda_3, - theta_f=d_theta_f, - f_01_max=self.cz_freq_01_max(), - # V_per_phi0=self.cz_V_per_phi0(), + lambda_2=self.cz_lambda_2(), + lambda_3=self.cz_lambda_3(), + theta_f=self.cz_theta_f(), + f_q0=self.cz_f_q0(), + f_q1=self.cz_f_q1(), + anharmonicity_q0=self.cz_anharmonicity_q0(), J2=self.cz_J2(), - # E_c=self.cz_E_c(), - f_interaction=self.cz_freq_interaction(), - sampling_rate=self.sampling_rate(), - return_unit='f01') + sampling_rate=self.sampling_rate()) half_CZ_B = dac_scale_factor*self.detuning_to_amp( self.cz_freq_01_max() - half_CZ_B, positive_branch=False) @@ -850,8 +850,8 @@ def load_waveform_onto_AWG_lookuptable(self, waveform_name: str, self.AWG.get_instr().set(codeword, waveform) def load_waveforms_onto_AWG_lookuptable( - self, regenerate_waveforms: bool=True, stop_start: bool = True, - force_load_sequencer_program: bool=False): + self, regenerate_waveforms: bool=True, stop_start: bool = True, + force_load_sequencer_program: bool=False): """ Loads all waveforms specified in the LutMap to an AWG for both this LutMap and the partner LutMap. @@ -1135,7 +1135,7 @@ def plot_cz_trajectory(self, ax=None, show=True): samples = np.arange(len(dac_amps)) amps = dac_amps*self.get_dac_val_to_amp_scalefactor() deltas = self.amp_to_detuning(amps) - freqs = self.cz_freq_01_max()-deltas + freqs = self.cz_f_q0()+self.cz_f_q1()-deltas ax.scatter(amps, freqs, c=samples, label='CZ trajectory') if show: plt.show() @@ -1152,19 +1152,20 @@ def plot_flux_arc(self, ax=None, show=True, f, ax = plt.subplots() amps = np.linspace(-2.5, 2.5, 101) # maximum voltage of AWG amp mode deltas = self.amp_to_detuning(amps) - freqs = self.cz_freq_01_max()-deltas + freqs_11 = self.cz_f_q0()+self.cz_f_q1()-deltas + freqs_20 = 2*self.cz_f_q0()-self.cz_anharmonicity_q0()-2*deltas - ax.plot(amps, freqs, label='$f_{01}$') - ax.axhline(self.cz_freq_interaction(), -5, 5, - label='$f_{\mathrm{int.}}$:'+' {:.3f} GHz'.format( - self.cz_freq_interaction()*1e-9), - c='C1') + ax.plot(amps, freqs_11, label='$f_{01}$') + ax.plot(amps, freqs_20, + label='$f_{\mathrm{int.}}$:'+' {:.3f} GHz'.format( + (self.cz_f_q1()*2-self.cz_anharmonicity_q0())*1e-9), + c='C1') - ax.axvline(0, 0, 1e10, linestyle='dotted', c='grey') + ax.axvline(0, 0, 2e10, linestyle='dotted', c='grey') ax.fill_between( - x=[-5, 5], - y1=[self.cz_freq_interaction()-self.cz_J2()]*2, - y2=[self.cz_freq_interaction()+self.cz_J2()]*2, + x=amps, + y1=freqs_20-self.cz_J2(), + y2=freqs_20+self.cz_J2(), label='$J_{\mathrm{2}}/2\pi$:'+' {:.3f} MHz'.format( self.cz_J2()*1e-6), color='C1', alpha=0.25) @@ -1178,7 +1179,7 @@ def plot_flux_arc(self, ax=None, show=True, set_xlabel(ax, 'AWG amplitude', 'V') set_ylabel(ax, 'Frequency', 'Hz') ax.set_xlim(-2.5, 2.5) - ax.set_ylim(3e9, np.max(freqs)+500e6) + ax.set_ylim(np.min(freqs_20)+500e6, np.max(freqs_20)+500e6) dac_val_axis = ax.twiny() dac_ax_lims = np.array(ax.get_xlim()) * \ From cfe9c4d4f3b1ee4182ecb6b2758eefda2a1e1ce0 Mon Sep 17 00:00:00 2001 From: FKMalino Date: Tue, 28 Aug 2018 10:31:55 +0200 Subject: [PATCH 02/26] Revert "Redefining martinis pulse in terms of freqs and anharmoninicties" This reverts commit bf0de4228ececccab93d8961753d8d7af855d9a2. --- .../meta_instrument/LutMans/flux_lutman.py | 79 +++++++++---------- 1 file changed, 39 insertions(+), 40 deletions(-) diff --git a/pycqed/instrument_drivers/meta_instrument/LutMans/flux_lutman.py b/pycqed/instrument_drivers/meta_instrument/LutMans/flux_lutman.py index 89593e7db1..20b9822197 100644 --- a/pycqed/instrument_drivers/meta_instrument/LutMans/flux_lutman.py +++ b/pycqed/instrument_drivers/meta_instrument/LutMans/flux_lutman.py @@ -323,23 +323,19 @@ def _add_waveform_parameters(self): initial_value=np.nan, parameter_class=ManualParameter) - self.add_parameter('cz_f_q0', vals=vals.Numbers(), + self.add_parameter('cz_freq_01_max', vals=vals.Numbers(), # initial value is chosen to not raise errors initial_value=6e9, unit='Hz', parameter_class=ManualParameter) - self.add_parameter('cz_f_q1', vals=vals.Numbers(), - # initial value is chosen to not raise errors - initial_value=6e9, - unit='Hz', parameter_class=ManualParameter) - self.add_parameter('cz_anharmonicity_q0', vals=vals.Numbers(), - # initial value is chosen to not raise errors - initial_value=-300e6, - unit='Hz', parameter_class=ManualParameter) - self.add_parameter('cz_J2', vals=vals.Numbers(), unit='Hz', # initial value is chosen to not raise errors initial_value=15e6, parameter_class=ManualParameter) + self.add_parameter('cz_freq_interaction', vals=vals.Numbers(), + # initial value is chosen to not raise errors + initial_value=5e9, + unit='Hz', + parameter_class=ManualParameter) self.add_parameter('cz_phase_corr_length', unit='s', initial_value=5e-9, vals=vals.Numbers(), @@ -454,11 +450,11 @@ def _gen_cz(self): lambda_2=self.cz_lambda_2(), lambda_3=self.cz_lambda_3(), theta_f=self.cz_theta_f(), - f_q0=self.cz_f_q0(), - f_q1=self.cz_f_q1(), - anharmonicity_q0=self.cz_anharmonicity_q0(), + f_01_max=self.cz_freq_01_max(), J2=self.cz_J2(), - sampling_rate=self.sampling_rate()) + f_interaction=self.cz_freq_interaction(), + sampling_rate=self.sampling_rate(), + return_unit='f01') return dac_scale_factor*self.detuning_to_amp( self.cz_freq_01_max() - CZ) else: @@ -469,11 +465,13 @@ def _gen_cz(self): lambda_2=self.cz_lambda_2(), lambda_3=self.cz_lambda_3(), theta_f=self.cz_theta_f(), - f_q0=self.cz_f_q0(), - f_q1=self.cz_f_q1(), - anharmonicity_q0=self.cz_anharmonicity_q0(), + f_01_max=self.cz_freq_01_max(), + # V_per_phi0=self.cz_V_per_phi0(), J2=self.cz_J2(), - sampling_rate=self.sampling_rate()) + # E_c=self.cz_E_c(), + f_interaction=self.cz_freq_interaction(), + sampling_rate=self.sampling_rate(), + return_unit='f01') half_CZ_A = dac_scale_factor*self.detuning_to_amp( self.cz_freq_01_max() - half_CZ_A) @@ -495,14 +493,16 @@ def _gen_cz(self): half_CZ_B = wf.martinis_flux_pulse( length=self.cz_length()*(1-self.czd_length_ratio()), - lambda_2=self.cz_lambda_2(), - lambda_3=self.cz_lambda_3(), - theta_f=self.cz_theta_f(), - f_q0=self.cz_f_q0(), - f_q1=self.cz_f_q1(), - anharmonicity_q0=self.cz_anharmonicity_q0(), + lambda_2=d_lambda_2, + lambda_3=d_lambda_3, + theta_f=d_theta_f, + f_01_max=self.cz_freq_01_max(), + # V_per_phi0=self.cz_V_per_phi0(), J2=self.cz_J2(), - sampling_rate=self.sampling_rate()) + # E_c=self.cz_E_c(), + f_interaction=self.cz_freq_interaction(), + sampling_rate=self.sampling_rate(), + return_unit='f01') half_CZ_B = dac_scale_factor*self.detuning_to_amp( self.cz_freq_01_max() - half_CZ_B, positive_branch=False) @@ -850,8 +850,8 @@ def load_waveform_onto_AWG_lookuptable(self, waveform_name: str, self.AWG.get_instr().set(codeword, waveform) def load_waveforms_onto_AWG_lookuptable( - self, regenerate_waveforms: bool=True, stop_start: bool = True, - force_load_sequencer_program: bool=False): + self, regenerate_waveforms: bool=True, stop_start: bool = True, + force_load_sequencer_program: bool=False): """ Loads all waveforms specified in the LutMap to an AWG for both this LutMap and the partner LutMap. @@ -1135,7 +1135,7 @@ def plot_cz_trajectory(self, ax=None, show=True): samples = np.arange(len(dac_amps)) amps = dac_amps*self.get_dac_val_to_amp_scalefactor() deltas = self.amp_to_detuning(amps) - freqs = self.cz_f_q0()+self.cz_f_q1()-deltas + freqs = self.cz_freq_01_max()-deltas ax.scatter(amps, freqs, c=samples, label='CZ trajectory') if show: plt.show() @@ -1152,20 +1152,19 @@ def plot_flux_arc(self, ax=None, show=True, f, ax = plt.subplots() amps = np.linspace(-2.5, 2.5, 101) # maximum voltage of AWG amp mode deltas = self.amp_to_detuning(amps) - freqs_11 = self.cz_f_q0()+self.cz_f_q1()-deltas - freqs_20 = 2*self.cz_f_q0()-self.cz_anharmonicity_q0()-2*deltas + freqs = self.cz_freq_01_max()-deltas - ax.plot(amps, freqs_11, label='$f_{01}$') - ax.plot(amps, freqs_20, - label='$f_{\mathrm{int.}}$:'+' {:.3f} GHz'.format( - (self.cz_f_q1()*2-self.cz_anharmonicity_q0())*1e-9), - c='C1') + ax.plot(amps, freqs, label='$f_{01}$') + ax.axhline(self.cz_freq_interaction(), -5, 5, + label='$f_{\mathrm{int.}}$:'+' {:.3f} GHz'.format( + self.cz_freq_interaction()*1e-9), + c='C1') - ax.axvline(0, 0, 2e10, linestyle='dotted', c='grey') + ax.axvline(0, 0, 1e10, linestyle='dotted', c='grey') ax.fill_between( - x=amps, - y1=freqs_20-self.cz_J2(), - y2=freqs_20+self.cz_J2(), + x=[-5, 5], + y1=[self.cz_freq_interaction()-self.cz_J2()]*2, + y2=[self.cz_freq_interaction()+self.cz_J2()]*2, label='$J_{\mathrm{2}}/2\pi$:'+' {:.3f} MHz'.format( self.cz_J2()*1e-6), color='C1', alpha=0.25) @@ -1179,7 +1178,7 @@ def plot_flux_arc(self, ax=None, show=True, set_xlabel(ax, 'AWG amplitude', 'V') set_ylabel(ax, 'Frequency', 'Hz') ax.set_xlim(-2.5, 2.5) - ax.set_ylim(np.min(freqs_20)+500e6, np.max(freqs_20)+500e6) + ax.set_ylim(3e9, np.max(freqs)+500e6) dac_val_axis = ax.twiny() dac_ax_lims = np.array(ax.get_xlim()) * \ From da6104afa934e6fc8903f6135b21d461b5beda0d Mon Sep 17 00:00:00 2001 From: FKMalino Date: Tue, 28 Aug 2018 10:32:04 +0200 Subject: [PATCH 03/26] Revert "Revert "Redefining martinis pulse in terms of freqs and anharmoninicties"" This reverts commit cfe9c4d4f3b1ee4182ecb6b2758eefda2a1e1ce0. --- .../meta_instrument/LutMans/flux_lutman.py | 79 ++++++++++--------- 1 file changed, 40 insertions(+), 39 deletions(-) diff --git a/pycqed/instrument_drivers/meta_instrument/LutMans/flux_lutman.py b/pycqed/instrument_drivers/meta_instrument/LutMans/flux_lutman.py index 20b9822197..89593e7db1 100644 --- a/pycqed/instrument_drivers/meta_instrument/LutMans/flux_lutman.py +++ b/pycqed/instrument_drivers/meta_instrument/LutMans/flux_lutman.py @@ -323,19 +323,23 @@ def _add_waveform_parameters(self): initial_value=np.nan, parameter_class=ManualParameter) - self.add_parameter('cz_freq_01_max', vals=vals.Numbers(), + self.add_parameter('cz_f_q0', vals=vals.Numbers(), # initial value is chosen to not raise errors initial_value=6e9, unit='Hz', parameter_class=ManualParameter) + self.add_parameter('cz_f_q1', vals=vals.Numbers(), + # initial value is chosen to not raise errors + initial_value=6e9, + unit='Hz', parameter_class=ManualParameter) + self.add_parameter('cz_anharmonicity_q0', vals=vals.Numbers(), + # initial value is chosen to not raise errors + initial_value=-300e6, + unit='Hz', parameter_class=ManualParameter) + self.add_parameter('cz_J2', vals=vals.Numbers(), unit='Hz', # initial value is chosen to not raise errors initial_value=15e6, parameter_class=ManualParameter) - self.add_parameter('cz_freq_interaction', vals=vals.Numbers(), - # initial value is chosen to not raise errors - initial_value=5e9, - unit='Hz', - parameter_class=ManualParameter) self.add_parameter('cz_phase_corr_length', unit='s', initial_value=5e-9, vals=vals.Numbers(), @@ -450,11 +454,11 @@ def _gen_cz(self): lambda_2=self.cz_lambda_2(), lambda_3=self.cz_lambda_3(), theta_f=self.cz_theta_f(), - f_01_max=self.cz_freq_01_max(), + f_q0=self.cz_f_q0(), + f_q1=self.cz_f_q1(), + anharmonicity_q0=self.cz_anharmonicity_q0(), J2=self.cz_J2(), - f_interaction=self.cz_freq_interaction(), - sampling_rate=self.sampling_rate(), - return_unit='f01') + sampling_rate=self.sampling_rate()) return dac_scale_factor*self.detuning_to_amp( self.cz_freq_01_max() - CZ) else: @@ -465,13 +469,11 @@ def _gen_cz(self): lambda_2=self.cz_lambda_2(), lambda_3=self.cz_lambda_3(), theta_f=self.cz_theta_f(), - f_01_max=self.cz_freq_01_max(), - # V_per_phi0=self.cz_V_per_phi0(), + f_q0=self.cz_f_q0(), + f_q1=self.cz_f_q1(), + anharmonicity_q0=self.cz_anharmonicity_q0(), J2=self.cz_J2(), - # E_c=self.cz_E_c(), - f_interaction=self.cz_freq_interaction(), - sampling_rate=self.sampling_rate(), - return_unit='f01') + sampling_rate=self.sampling_rate()) half_CZ_A = dac_scale_factor*self.detuning_to_amp( self.cz_freq_01_max() - half_CZ_A) @@ -493,16 +495,14 @@ def _gen_cz(self): half_CZ_B = wf.martinis_flux_pulse( length=self.cz_length()*(1-self.czd_length_ratio()), - lambda_2=d_lambda_2, - lambda_3=d_lambda_3, - theta_f=d_theta_f, - f_01_max=self.cz_freq_01_max(), - # V_per_phi0=self.cz_V_per_phi0(), + lambda_2=self.cz_lambda_2(), + lambda_3=self.cz_lambda_3(), + theta_f=self.cz_theta_f(), + f_q0=self.cz_f_q0(), + f_q1=self.cz_f_q1(), + anharmonicity_q0=self.cz_anharmonicity_q0(), J2=self.cz_J2(), - # E_c=self.cz_E_c(), - f_interaction=self.cz_freq_interaction(), - sampling_rate=self.sampling_rate(), - return_unit='f01') + sampling_rate=self.sampling_rate()) half_CZ_B = dac_scale_factor*self.detuning_to_amp( self.cz_freq_01_max() - half_CZ_B, positive_branch=False) @@ -850,8 +850,8 @@ def load_waveform_onto_AWG_lookuptable(self, waveform_name: str, self.AWG.get_instr().set(codeword, waveform) def load_waveforms_onto_AWG_lookuptable( - self, regenerate_waveforms: bool=True, stop_start: bool = True, - force_load_sequencer_program: bool=False): + self, regenerate_waveforms: bool=True, stop_start: bool = True, + force_load_sequencer_program: bool=False): """ Loads all waveforms specified in the LutMap to an AWG for both this LutMap and the partner LutMap. @@ -1135,7 +1135,7 @@ def plot_cz_trajectory(self, ax=None, show=True): samples = np.arange(len(dac_amps)) amps = dac_amps*self.get_dac_val_to_amp_scalefactor() deltas = self.amp_to_detuning(amps) - freqs = self.cz_freq_01_max()-deltas + freqs = self.cz_f_q0()+self.cz_f_q1()-deltas ax.scatter(amps, freqs, c=samples, label='CZ trajectory') if show: plt.show() @@ -1152,19 +1152,20 @@ def plot_flux_arc(self, ax=None, show=True, f, ax = plt.subplots() amps = np.linspace(-2.5, 2.5, 101) # maximum voltage of AWG amp mode deltas = self.amp_to_detuning(amps) - freqs = self.cz_freq_01_max()-deltas + freqs_11 = self.cz_f_q0()+self.cz_f_q1()-deltas + freqs_20 = 2*self.cz_f_q0()-self.cz_anharmonicity_q0()-2*deltas - ax.plot(amps, freqs, label='$f_{01}$') - ax.axhline(self.cz_freq_interaction(), -5, 5, - label='$f_{\mathrm{int.}}$:'+' {:.3f} GHz'.format( - self.cz_freq_interaction()*1e-9), - c='C1') + ax.plot(amps, freqs_11, label='$f_{01}$') + ax.plot(amps, freqs_20, + label='$f_{\mathrm{int.}}$:'+' {:.3f} GHz'.format( + (self.cz_f_q1()*2-self.cz_anharmonicity_q0())*1e-9), + c='C1') - ax.axvline(0, 0, 1e10, linestyle='dotted', c='grey') + ax.axvline(0, 0, 2e10, linestyle='dotted', c='grey') ax.fill_between( - x=[-5, 5], - y1=[self.cz_freq_interaction()-self.cz_J2()]*2, - y2=[self.cz_freq_interaction()+self.cz_J2()]*2, + x=amps, + y1=freqs_20-self.cz_J2(), + y2=freqs_20+self.cz_J2(), label='$J_{\mathrm{2}}/2\pi$:'+' {:.3f} MHz'.format( self.cz_J2()*1e-6), color='C1', alpha=0.25) @@ -1178,7 +1179,7 @@ def plot_flux_arc(self, ax=None, show=True, set_xlabel(ax, 'AWG amplitude', 'V') set_ylabel(ax, 'Frequency', 'Hz') ax.set_xlim(-2.5, 2.5) - ax.set_ylim(3e9, np.max(freqs)+500e6) + ax.set_ylim(np.min(freqs_20)+500e6, np.max(freqs_20)+500e6) dac_val_axis = ax.twiny() dac_ax_lims = np.array(ax.get_xlim()) * \ From 79ad9c7c8b5ac0c73d881653658795ae8f0fdbfc Mon Sep 17 00:00:00 2001 From: FKMalino Date: Tue, 28 Aug 2018 10:32:17 +0200 Subject: [PATCH 04/26] Changes to plotting --- .../waveform_control_CC/waveform.py | 75 +++++++------------ 1 file changed, 28 insertions(+), 47 deletions(-) diff --git a/pycqed/measurement/waveform_control_CC/waveform.py b/pycqed/measurement/waveform_control_CC/waveform.py index f516db6eea..6b2e1aa45d 100644 --- a/pycqed/measurement/waveform_control_CC/waveform.py +++ b/pycqed/measurement/waveform_control_CC/waveform.py @@ -329,12 +329,10 @@ def mod_square_VSM(amp_G, amp_D, length, f_modulation, def martinis_flux_pulse(length: float, lambda_2: float, lambda_3: float, theta_f: float, - f_01_max: float, J2: float, - V_offset: float=0, V_per_phi0: float=1, - E_c: float=250e6, f_bus: float =None, - f_interaction: float =None, - asymmetry: float =0, sampling_rate: float =1e9, - return_unit: str='V'): + f_q0: float, + f_q1: float, + anharmonicity_q0: float, + sampling_rate: float =1e9): """ Returns the pulse specified by Martinis and Geller Phys. Rev. A 90 022307 (2014). @@ -344,26 +342,22 @@ def martinis_flux_pulse(length: float, lambda_2: float, lambda_3: float, note that the lambda coefficients are rescaled to ensure that the center of the pulse has a value corresponding to theta_f. - length (float) lenght of the waveform (s) + length (float) lenght of the waveform (s) lambda_2 lambda_3 - theta_f (float) final angle of the interaction in degrees. - Determines the Voltage for the center of the waveform. - - f_01_max (float) qubit sweet spot frequency (Hz). - J2 (float) coupling between 11-02 (Hz), - approx sqrt(2) J1 (the 10-01 coupling). - E_c (float) Charging energy of the transmon (Hz). - f_bus (float) frequency of the bus (Hz). - f_interaction (float) interaction frequency (Hz). - asymmetry (float) qubit asymmetry - - sampling_rate (float) sampling rate of the AWG (Hz) - return_unit (enum: ['V', 'eps', 'f01', 'theta']) whether to return the - pulse expressed in units of theta: the reference frame of - the interaction, units of epsilon: detuning to the bus - eps=f12-f_bus + theta_f (float) final angle of the interaction in degrees. + Determines the Voltage for the center of the waveform. + + f_q0 (float) frequency of the fluxed qubit (Hz). + f_q1 (float) frequency of the lower, not fluxed qubit (Hz) + anharmonicity_q0 (float) anharmonicity of the fluxed qubit (Hz) + (negative for conventional transmon) + J2 (float) coupling between 11-02 (Hz), + approx sqrt(2) J1 (the 10-01 coupling). + + sampling_rate (float) sampling rate of the AWG (Hz) + """ # Define number of samples and time points @@ -379,9 +373,11 @@ def martinis_flux_pulse(length: float, lambda_2: float, lambda_3: float, # -tau_step/2 is to make sure final pt is excluded # Derived parameters - if f_interaction is None: - f_interaction = f_bus + E_c - theta_i = np.arctan(2*J2 / (f_01_max - f_interaction)) + f_initial = f_q0 + f_interaction = f_q1 - anharmonicity_q0 + detuning_initial = f_q0 + anharmonicity_q0 - f_q1 + theta_i = np.arctan(2*J2 / detuning_initial) + # Converting angle to radians as that is used under the hood theta_f = 2*np.pi*theta_f/360 if theta_f < theta_i: @@ -409,8 +405,8 @@ def martinis_flux_pulse(length: float, lambda_2: float, lambda_3: float, .format(theta_i)) # Transform from proper time to real time - t = np.array([np.trapz(np.sin(theta_wave)[:i+1], dx=1/(10*sampling_rate)) - for i in range(len(theta_wave))]) + t = np.array([np.trapz(np.sin(theta_wave_clipped)[:i+1], dx=1/(10*sampling_rate)) + for i in range(len(theta_wave_clipped))]) # Interpolate pulse at physical sampling distance t_samples = np.arange(0, length, 1/sampling_rate) @@ -425,35 +421,20 @@ def martinis_flux_pulse(length: float, lambda_2: float, lambda_3: float, # Theta is returned in radians here return np.nan_to_num(interp_wave) - # Convert to detuning from f_interaction + # Convert to detuning between f_20 and f_11 + # It is equal to detuning between f_11 and interaction point delta_f_wave = 2 * J2 / np.tan(interp_wave) - if return_unit == 'eps': - return np.nan_to_num(delta_f_wave) # Convert to parametrization of f_01 f_01_wave = delta_f_wave + f_interaction - if return_unit == 'f01': - return np.nan_to_num(f_01_wave) - - # Convert to voltage - voltage_wave = Qubit_freq_to_dac( - frequency=f_01_wave, - f_max=f_01_max, - E_c=E_c, - dac_sweet_spot=V_offset, - V_per_phi0=V_per_phi0, - asymmetry=asymmetry, - branch='positive') - + + return np.nan_to_num(f_01_wave) # why sometimes the last sample is nan is not known, # but we will surely figure it out someday. # (Brian and Adriaan, 14.11.2017) # This may be caused by the fill_value of the interp_wave (~30 lines up) # that was set to 0 instead of extrapolate. This caused # the np.tan(interp_wave) to divide by zero. (MAR 10-05-2018) - voltage_wave = np.nan_to_num(voltage_wave) - - return voltage_wave ############################################################################ # ############################################################################ From b2717012fd08950484a4c198cdfd08d5b78d9f38 Mon Sep 17 00:00:00 2001 From: Adriaan Date: Tue, 28 Aug 2018 11:53:32 +0200 Subject: [PATCH 05/26] new martinis flux pulse rewrite --- .../waveform_control_CC/waveform.py | 14 +- .../waveform_control_CC/waveforms_flux.py | 128 ++++++++++++++++++ 2 files changed, 139 insertions(+), 3 deletions(-) create mode 100644 pycqed/measurement/waveform_control_CC/waveforms_flux.py diff --git a/pycqed/measurement/waveform_control_CC/waveform.py b/pycqed/measurement/waveform_control_CC/waveform.py index 6b2e1aa45d..10dc080180 100644 --- a/pycqed/measurement/waveform_control_CC/waveform.py +++ b/pycqed/measurement/waveform_control_CC/waveform.py @@ -6,6 +6,14 @@ Prerequisites: Usage: Bugs: + +Contains most basic waveforms, basic means having a few parameters and a +straightforward translation to AWG amplitude, i.e., no knowledge of qubit +parameters. + +Examples of waveforms that are too advanced are flux pulses that require +knowledge of the flux sensitivity and interaction strengths and qubit +frequencies. See e.g., "waveform_control_CC/waveforms_flux.py". ''' import logging @@ -360,7 +368,7 @@ def martinis_flux_pulse(length: float, lambda_2: float, lambda_3: float, """ # Define number of samples and time points - + logging.warning("Deprecated, use waveforms_flux.martinis_flux_pulse") # Pulse is generated at a denser grid to allow for good interpolation # N.B. Not clear why interpolation is needed at all... -MAR July 2018 @@ -377,7 +385,7 @@ def martinis_flux_pulse(length: float, lambda_2: float, lambda_3: float, f_interaction = f_q1 - anharmonicity_q0 detuning_initial = f_q0 + anharmonicity_q0 - f_q1 theta_i = np.arctan(2*J2 / detuning_initial) - + # Converting angle to radians as that is used under the hood theta_f = 2*np.pi*theta_f/360 if theta_f < theta_i: @@ -427,7 +435,7 @@ def martinis_flux_pulse(length: float, lambda_2: float, lambda_3: float, # Convert to parametrization of f_01 f_01_wave = delta_f_wave + f_interaction - + return np.nan_to_num(f_01_wave) # why sometimes the last sample is nan is not known, # but we will surely figure it out someday. diff --git a/pycqed/measurement/waveform_control_CC/waveforms_flux.py b/pycqed/measurement/waveform_control_CC/waveforms_flux.py new file mode 100644 index 0000000000..bdd20d35a4 --- /dev/null +++ b/pycqed/measurement/waveform_control_CC/waveforms_flux.py @@ -0,0 +1,128 @@ +''' + File: waveform_flux.py + Author: Filip Malinowski and Adriaan Rol + Purpose: generate flux waveforms that require knowledge + of the qubit Hamiltonian. + Prerequisites: + Usage: + Bugs: + +For more basic waveforms see e.g., waveforms.py +''' +import logging +import scipy +import numpy as np + + +def martinis_flux_pulse(length: float, + theta_i: float, theta_f: float, + lambda_2: float, lambda_3: float=0, lambda_4: float=0, + sampling_rate: float =2.4e9): + """ + Returns the pulse specified by Martinis and Geller as θ(t) specified in + Phys. Rev. A 90 022307 (2014). + Note that θ still needs to be transformed into detuning from the + interaction and into AWG amplitude V(t). + + θ = θ_i + Σ_{n=1}^N (λ_n*(1-cos(n*2*pi*t/t_p))/2 + + Args: + length : lenght of the waveform (s) + lambda_2 : lambda coeffecients + lambda_3 : + lambda_3 : + theta_f : initial angle of interaction in degrees. + theta_f : final angle of the interaction in degrees. + sampling_rate : sampling rate of AWG in (Hz) + + This waveform is generated in several steps + 1. Generate a time grid, may include fine sampling. + 2. Generate θ(τ) using eqs 15 and 16 + 3. Transform from proper time τ to real time t using interpolation + + """ + if theta_f < theta_i: + raise ValueError( + 'theta_f ({:.2f} deg) < theta_i ({:.2f} deg):'.format( + theta_f/(2*np.pi)*360, theta_i/(2*np.pi)*360) + + 'final coupling weaker than initial coupling') + + # 1. Generate a time grid, may include fine sampling. + + # Pulse is generated at a denser grid to allow for good interpolation + # N.B. Not clear why interpolation is needed at all... -MAR July 2018 + fine_sampling_factor = 2 # 10 + nr_samples = int(np.round((length)*sampling_rate * fine_sampling_factor)) + rounded_length = nr_samples/(fine_sampling_factor * sampling_rate) + tau_step = 1/(fine_sampling_factor * sampling_rate) # denser points + # tau is a virtual time/proper time + taus = np.arange(0, rounded_length-tau_step/2, tau_step) + # -tau_step/2 is to make sure final pt is excluded + + # Determine lambda_1 using the constraint set by eq 16 from Martinis 2014 + # lambda_1 is scaled such that the final ("center") angle is theta_f + lambda_1 = (theta_f - theta_i) / (2) - lambda_3 + + # 2. Generate θ(τ) using eqs 15 and 16 + theta_wave = np.ones(nr_samples) * theta_i + theta_wave += lambda_1 * (1 - np.cos(2 * np.pi * taus / rounded_length)) + theta_wave += (lambda_1 * lambda_2 * + (1 - np.cos(4 * np.pi * taus / rounded_length))) + theta_wave += (lambda_1 * lambda_3 * + (1 - np.cos(6 * np.pi * taus / rounded_length))) + + # Clip wave to [theta_i, pi] to avoid poles in the wave expressed in freq + theta_wave_clipped = np.clip(theta_wave, theta_i, np.pi-.01) + if not np.array_equal(theta_wave, theta_wave_clipped): + logging.warning( + 'Martinis flux wave form has been clipped to [{}, 180 deg]' + .format(theta_i)) + + # 3. Transform from proper time τ to real time t using interpolation + t = np.array([np.trapz(np.sin(theta_wave_clipped)[:i+1], + dx=1/(10*sampling_rate)) + for i in range(len(theta_wave_clipped))]) + + # Interpolate pulse at physical sampling distance + t_samples = np.arange(0, length, 1/sampling_rate) + # Scaling factor for time-axis to get correct pulse length again + scale = t[-1]/t_samples[-1] + interp_wave = scipy.interpolate.interp1d( + t/scale, theta_wave_clipped, bounds_error=False, + fill_value='extrapolate')(t_samples) + + # Theta is returned in radians here + return np.nan_to_num(interp_wave) + + +def eps_to_theta(eps: float, g: float): + """ + Converts ε into θ as defined in Phys. Rev. A 90 022307 (2014) + + θ = arctan(Hx/Hz) + θ = arctan(2*g/ε) + + args: + ε: detuning + g: coupling strength + returns: + θ: interaction angle + """ + theta = np.arctan(2*g / eps) + return theta + + +def theta_to_eps(theta: float, g: float): + """ + Converts θ into ε as defined in Phys. Rev. A 90 022307 (2014) + Hz = Hx/tan(θ) + ε = 2g/tan(θ) + + args: + θ: interaction angle + ε: detuning + returns: + g: coupling strength + """ + eps = 2 * g / np.tan(theta) + return eps From 40c54bebc6abd80d6716a620f50eeb8b69d92eff Mon Sep 17 00:00:00 2001 From: Adriaan Date: Tue, 28 Aug 2018 14:48:34 +0200 Subject: [PATCH 06/26] added test for eps theta conversion and removed old code --- .../waveform_control_CC/waveform.py | 159 +++++++++--------- .../waveform_control_CC/waveforms_flux.py | 8 +- pycqed/tests/test_waveforms.py | 143 +--------------- pycqed/tests/test_waveforms_flux.py | 41 +++++ 4 files changed, 127 insertions(+), 224 deletions(-) create mode 100644 pycqed/tests/test_waveforms_flux.py diff --git a/pycqed/measurement/waveform_control_CC/waveform.py b/pycqed/measurement/waveform_control_CC/waveform.py index 10dc080180..cd7d3e7767 100644 --- a/pycqed/measurement/waveform_control_CC/waveform.py +++ b/pycqed/measurement/waveform_control_CC/waveform.py @@ -367,82 +367,83 @@ def martinis_flux_pulse(length: float, lambda_2: float, lambda_3: float, sampling_rate (float) sampling rate of the AWG (Hz) """ - # Define number of samples and time points - logging.warning("Deprecated, use waveforms_flux.martinis_flux_pulse") - - # Pulse is generated at a denser grid to allow for good interpolation - # N.B. Not clear why interpolation is needed at all... -MAR July 2018 - fine_sampling_factor = 1 # 10 - nr_samples = int(np.round((length)*sampling_rate * fine_sampling_factor)) - rounded_length = nr_samples/(fine_sampling_factor * sampling_rate) - tau_step = 1/(fine_sampling_factor * sampling_rate) # denser points - # tau is a virtual time/proper time - taus = np.arange(0, rounded_length-tau_step/2, tau_step) - # -tau_step/2 is to make sure final pt is excluded - - # Derived parameters - f_initial = f_q0 - f_interaction = f_q1 - anharmonicity_q0 - detuning_initial = f_q0 + anharmonicity_q0 - f_q1 - theta_i = np.arctan(2*J2 / detuning_initial) - - # Converting angle to radians as that is used under the hood - theta_f = 2*np.pi*theta_f/360 - if theta_f < theta_i: - raise ValueError( - 'theta_f ({:.2f} deg) < theta_i ({:.2f} deg):'.format( - theta_f/(2*np.pi)*360, theta_i/(2*np.pi)*360) - + 'final coupling weaker than initial coupling') - - # lambda_1 is scaled such that the final ("center") angle is theta_f - lambda_1 = (theta_f - theta_i) / (2 + 2 * lambda_3) - - # Calculate the wave - theta_wave = np.ones(nr_samples) * theta_i - theta_wave += lambda_1 * (1 - np.cos(2 * np.pi * taus / rounded_length)) - theta_wave += (lambda_1 * lambda_2 * - (1 - np.cos(4 * np.pi * taus / rounded_length))) - theta_wave += (lambda_1 * lambda_3 * - (1 - np.cos(6 * np.pi * taus / rounded_length))) - - # Clip wave to [theta_i, pi] to avoid poles in the wave expressed in freq - theta_wave_clipped = np.clip(theta_wave, theta_i, np.pi-.01) - if not np.array_equal(theta_wave, theta_wave_clipped): - logging.warning( - 'Martinis flux wave form has been clipped to [{}, 180 deg]' - .format(theta_i)) - - # Transform from proper time to real time - t = np.array([np.trapz(np.sin(theta_wave_clipped)[:i+1], dx=1/(10*sampling_rate)) - for i in range(len(theta_wave_clipped))]) - - # Interpolate pulse at physical sampling distance - t_samples = np.arange(0, length, 1/sampling_rate) - # Scaling factor for time-axis to get correct pulse length again - scale = t[-1]/t_samples[-1] - interp_wave = scipy.interpolate.interp1d( - t/scale, theta_wave_clipped, bounds_error=False, - fill_value='extrapolate')(t_samples) - - # Return in the specified units - if return_unit == 'theta': - # Theta is returned in radians here - return np.nan_to_num(interp_wave) - - # Convert to detuning between f_20 and f_11 - # It is equal to detuning between f_11 and interaction point - delta_f_wave = 2 * J2 / np.tan(interp_wave) - - # Convert to parametrization of f_01 - f_01_wave = delta_f_wave + f_interaction - - return np.nan_to_num(f_01_wave) - # why sometimes the last sample is nan is not known, - # but we will surely figure it out someday. - # (Brian and Adriaan, 14.11.2017) - # This may be caused by the fill_value of the interp_wave (~30 lines up) - # that was set to 0 instead of extrapolate. This caused - # the np.tan(interp_wave) to divide by zero. (MAR 10-05-2018) -############################################################################ -# -############################################################################ + raise NotImplementedError("This class is deprecated") +# # Define number of samples and time points +# logging.warning("Deprecated, use waveforms_flux.martinis_flux_pulse") + +# # Pulse is generated at a denser grid to allow for good interpolation +# # N.B. Not clear why interpolation is needed at all... -MAR July 2018 +# fine_sampling_factor = 1 # 10 +# nr_samples = int(np.round((length)*sampling_rate * fine_sampling_factor)) +# rounded_length = nr_samples/(fine_sampling_factor * sampling_rate) +# tau_step = 1/(fine_sampling_factor * sampling_rate) # denser points +# # tau is a virtual time/proper time +# taus = np.arange(0, rounded_length-tau_step/2, tau_step) +# # -tau_step/2 is to make sure final pt is excluded + +# # Derived parameters +# f_initial = f_q0 +# f_interaction = f_q1 - anharmonicity_q0 +# detuning_initial = f_q0 + anharmonicity_q0 - f_q1 +# theta_i = np.arctan(2*J2 / detuning_initial) + +# # Converting angle to radians as that is used under the hood +# theta_f = 2*np.pi*theta_f/360 +# if theta_f < theta_i: +# raise ValueError( +# 'theta_f ({:.2f} deg) < theta_i ({:.2f} deg):'.format( +# theta_f/(2*np.pi)*360, theta_i/(2*np.pi)*360) +# + 'final coupling weaker than initial coupling') + +# # lambda_1 is scaled such that the final ("center") angle is theta_f +# lambda_1 = (theta_f - theta_i) / (2 + 2 * lambda_3) + +# # Calculate the wave +# theta_wave = np.ones(nr_samples) * theta_i +# theta_wave += lambda_1 * (1 - np.cos(2 * np.pi * taus / rounded_length)) +# theta_wave += (lambda_1 * lambda_2 * +# (1 - np.cos(4 * np.pi * taus / rounded_length))) +# theta_wave += (lambda_1 * lambda_3 * +# (1 - np.cos(6 * np.pi * taus / rounded_length))) + +# # Clip wave to [theta_i, pi] to avoid poles in the wave expressed in freq +# theta_wave_clipped = np.clip(theta_wave, theta_i, np.pi-.01) +# if not np.array_equal(theta_wave, theta_wave_clipped): +# logging.warning( +# 'Martinis flux wave form has been clipped to [{}, 180 deg]' +# .format(theta_i)) + +# # Transform from proper time to real time +# t = np.array([np.trapz(np.sin(theta_wave_clipped)[:i+1], dx=1/(10*sampling_rate)) +# for i in range(len(theta_wave_clipped))]) + +# # Interpolate pulse at physical sampling distance +# t_samples = np.arange(0, length, 1/sampling_rate) +# # Scaling factor for time-axis to get correct pulse length again +# scale = t[-1]/t_samples[-1] +# interp_wave = scipy.interpolate.interp1d( +# t/scale, theta_wave_clipped, bounds_error=False, +# fill_value='extrapolate')(t_samples) + +# # Return in the specified units +# if return_unit == 'theta': +# # Theta is returned in radians here +# return np.nan_to_num(interp_wave) + +# # Convert to detuning between f_20 and f_11 +# # It is equal to detuning between f_11 and interaction point +# delta_f_wave = 2 * J2 / np.tan(interp_wave) + +# # Convert to parametrization of f_01 +# f_01_wave = delta_f_wave + f_interaction + +# return np.nan_to_num(f_01_wave) +# # why sometimes the last sample is nan is not known, +# # but we will surely figure it out someday. +# # (Brian and Adriaan, 14.11.2017) +# # This may be caused by the fill_value of the interp_wave (~30 lines up) +# # that was set to 0 instead of extrapolate. This caused +# # the np.tan(interp_wave) to divide by zero. (MAR 10-05-2018) +# ############################################################################ +# # +# ############################################################################ diff --git a/pycqed/measurement/waveform_control_CC/waveforms_flux.py b/pycqed/measurement/waveform_control_CC/waveforms_flux.py index bdd20d35a4..4131cd59f3 100644 --- a/pycqed/measurement/waveform_control_CC/waveforms_flux.py +++ b/pycqed/measurement/waveform_control_CC/waveforms_flux.py @@ -106,9 +106,11 @@ def eps_to_theta(eps: float, g: float): ε: detuning g: coupling strength returns: - θ: interaction angle + θ: interaction angle (radian) """ - theta = np.arctan(2*g / eps) + # Ignore divide by zero as it still gives a meaningful angle + with np.errstate(divide='ignore'): + theta = np.arctan(np.divide(2*g, eps)) return theta @@ -119,7 +121,7 @@ def theta_to_eps(theta: float, g: float): ε = 2g/tan(θ) args: - θ: interaction angle + θ: interaction angle (radian) ε: detuning returns: g: coupling strength diff --git a/pycqed/tests/test_waveforms.py b/pycqed/tests/test_waveforms.py index 164e724211..5121eb2beb 100644 --- a/pycqed/tests/test_waveforms.py +++ b/pycqed/tests/test_waveforms.py @@ -1,6 +1,5 @@ import numpy as np import unittest -import warnings from pycqed.measurement.waveform_control_CC import waveform as wf # These are test waveforms @@ -127,146 +126,6 @@ def test_mod_gauss_VSM(self): np.testing.assert_almost_equal(D_I, np.zeros(len(g_env))) np.testing.assert_almost_equal(D_Q, d_env) - def test_martinis_flux_pulse(self): - length = 200e-9 - lambda_2 = 0.015 - lambda_3 = 0 - theta_f = 8 - f_01_max = 6.089e9 - J2 = 4.2e6 - E_c = 0 - V_per_phi0 = np.pi/1.7178 - f_interaction = 4.940e9 - f_bus = None - asymmetry = 0 - sampling_rate = 1e9 - return_unit = 'V' - - theta_wave = wf.martinis_flux_pulse( - length=length, - lambda_2=lambda_2, - lambda_3=lambda_3, - theta_f=theta_f, - f_01_max=f_01_max, - J2=J2, - E_c=E_c, - V_per_phi0=V_per_phi0, - f_interaction=f_interaction, - f_bus=f_bus, - asymmetry=asymmetry, - sampling_rate=sampling_rate, - return_unit=return_unit) - - test_wave = np.array( - [0., 0.03471175, 0.06891321, 0.10213049, 0.13395662, - 0.16407213, 0.19225372, 0.21837228, 0.24238259, 0.26430828, - 0.28422502, 0.30224448, 0.31850026, 0.33313676, 0.34630067, - 0.35813509, 0.36877569, 0.37834846, 0.38696864, 0.39474049, - 0.40175751, 0.40810307, 0.41385119, 0.41906739, 0.42380953, - 0.42812868, 0.43206986, 0.43567281, 0.43897257, 0.44200007, - 0.44478268, 0.4473446, 0.44970728, 0.45188975, 0.45390895, - 0.45577993, 0.45751613, 0.45912955, 0.46063093, 0.46202989, - 0.46333506, 0.46455423, 0.46569439, 0.46676186, 0.46776234, - 0.468701, 0.46958252, 0.47041115, 0.47119076, 0.47192487, - 0.47261668, 0.47326913, 0.4738849, 0.47446644, 0.47501598, - 0.47553561, 0.47602721, 0.47649252, 0.47693314, 0.47735057, - 0.47774615, 0.47812115, 0.47847674, 0.47881399, 0.47913389, - 0.47943738, 0.4797253, 0.47999845, 0.48025757, 0.48050334, - 0.4807364, 0.48095734, 0.48116671, 0.48136502, 0.48155275, - 0.48173034, 0.48189819, 0.4820567, 0.48220622, 0.48234707, - 0.48247957, 0.48260399, 0.48272061, 0.48282967, 0.48293139, - 0.48302598, 0.48311363, 0.48319452, 0.4832688, 0.48333663, - 0.48339813, 0.48345343, 0.48350263, 0.48354583, 0.4835831, - 0.48361452, 0.48364015, 0.48366003, 0.48367421, 0.4836827, - 0.48368553, 0.4836827, 0.48367421, 0.48366003, 0.48364015, - 0.48361452, 0.4835831, 0.48354583, 0.48350263, 0.48345343, - 0.48339813, 0.48333663, 0.4832688, 0.48319452, 0.48311363, - 0.48302598, 0.48293139, 0.48282967, 0.48272061, 0.48260399, - 0.48247957, 0.48234707, 0.48220622, 0.4820567, 0.48189819, - 0.48173034, 0.48155275, 0.48136502, 0.48116671, 0.48095734, - 0.4807364, 0.48050334, 0.48025757, 0.47999845, 0.4797253, - 0.47943738, 0.47913389, 0.47881399, 0.47847674, 0.47812115, - 0.47774615, 0.47735057, 0.47693314, 0.47649252, 0.47602721, - 0.47553561, 0.47501598, 0.47446644, 0.4738849, 0.47326913, - 0.47261668, 0.47192487, 0.47119076, 0.47041115, 0.46958252, - 0.468701, 0.46776234, 0.46676186, 0.46569439, 0.46455423, - 0.46333506, 0.46202989, 0.46063093, 0.45912955, 0.45751613, - 0.45577993, 0.45390895, 0.45188975, 0.44970728, 0.4473446, - 0.44478268, 0.44200007, 0.43897257, 0.43567281, 0.43206986, - 0.42812868, 0.42380953, 0.41906739, 0.41385119, 0.40810307, - 0.40175751, 0.39474049, 0.38696864, 0.37834846, 0.36877569, - 0.35813509, 0.34630067, 0.33313676, 0.31850026, 0.30224448, - 0.28422502, 0.26430828, 0.24238259, 0.21837228, 0.19225372, - 0.16407213, 0.13395662, 0.10213049, 0.06891321, 0.03471175]) - - # FIXME: Only testing for the shape as the waveform has been updated - self.assertEqual(np.shape(theta_wave), np.shape(test_wave)) - # np.testing.assert_almost_equal(theta_wave, test_wave) - - lambda_2 = -0.02 - # FIXME: we should test if the right warning is raised. - # with warnings.catch_warnings(record=True) as w: - theta_wave = wf.martinis_flux_pulse( - length=length, - lambda_2=lambda_2, - lambda_3=lambda_3, - theta_f=theta_f, - f_01_max=f_01_max, - J2=J2, - E_c=E_c, - V_per_phi0=V_per_phi0, - f_interaction=f_interaction, - f_bus=f_bus, - asymmetry=asymmetry, - sampling_rate=sampling_rate, - return_unit=return_unit) - - test_wave_2 = np.array( - [0., 0.03234928, 0.06428708, 0.09542742, 0.12543163, - 0.1540241, 0.18100034, 0.20622772, 0.22964031, 0.25122954, - 0.2710329, 0.28912228, 0.30559317, 0.32055543, 0.33412579, - 0.34642229, 0.35756021, 0.3676494, 0.37679261, 0.38508466, - 0.39261214, 0.3994535, 0.40567941, 0.41135325, 0.41653169, - 0.42126526, 0.42559899, 0.42957296, 0.43322283, 0.43658032, - 0.43967369, 0.44252809, 0.44516595, 0.44760729, 0.44987001, - 0.45197009, 0.45392188, 0.45573822, 0.45743067, 0.45900961, - 0.4604844, 0.46186349, 0.46315449, 0.46436431, 0.4654992, - 0.46656483, 0.46756635, 0.46850846, 0.46939543, 0.47023115, - 0.47101919, 0.47176279, 0.47246495, 0.47312839, 0.47375563, - 0.47434897, 0.47491052, 0.47544225, 0.47594595, 0.47642329, - 0.47687579, 0.47730487, 0.47771185, 0.47809794, 0.47846427, - 0.47881187, 0.47914172, 0.47945471, 0.47975169, 0.48003341, - 0.48030062, 0.48055396, 0.48079408, 0.48102155, 0.4812369, - 0.48144065, 0.48163325, 0.48181516, 0.48198676, 0.48214843, - 0.48230053, 0.48244338, 0.48257727, 0.48270249, 0.4828193, - 0.48292792, 0.48302858, 0.48312148, 0.4832068, 0.48328471, - 0.48335536, 0.48341888, 0.4834754, 0.48352503, 0.48356785, - 0.48360394, 0.48363339, 0.48365623, 0.48367252, 0.48368228, - 0.48368553, 0.48368228, 0.48367252, 0.48365623, 0.48363339, - 0.48360394, 0.48356785, 0.48352503, 0.4834754, 0.48341888, - 0.48335536, 0.48328471, 0.4832068, 0.48312148, 0.48302858, - 0.48292792, 0.4828193, 0.48270249, 0.48257727, 0.48244338, - 0.48230053, 0.48214843, 0.48198676, 0.48181516, 0.48163325, - 0.48144065, 0.4812369, 0.48102155, 0.48079408, 0.48055396, - 0.48030062, 0.48003341, 0.47975169, 0.47945471, 0.47914172, - 0.47881187, 0.47846427, 0.47809794, 0.47771185, 0.47730487, - 0.47687579, 0.47642329, 0.47594595, 0.47544225, 0.47491052, - 0.47434897, 0.47375563, 0.47312839, 0.47246495, 0.47176279, - 0.47101919, 0.47023115, 0.46939543, 0.46850846, 0.46756635, - 0.46656483, 0.4654992, 0.46436431, 0.46315449, 0.46186349, - 0.4604844, 0.45900961, 0.45743067, 0.45573822, 0.45392188, - 0.45197009, 0.44987001, 0.44760729, 0.44516595, 0.44252809, - 0.43967369, 0.43658032, 0.43322283, 0.42957296, 0.42559899, - 0.42126526, 0.41653169, 0.41135325, 0.40567941, 0.3994535, - 0.39261214, 0.38508466, 0.37679261, 0.3676494, 0.35756021, - 0.34642229, 0.33412579, 0.32055543, 0.30559317, 0.28912228, - 0.2710329, 0.25122954, 0.22964031, 0.20622772, 0.18100034, - 0.1540241, 0.12543163, 0.09542742, 0.06428708, 0.03234928]) - - # FIXME: Only testing for the shape as the waveform has been updated - self.assertEqual(np.shape(theta_wave), np.shape(test_wave_2)) - # np.testing.assert_almost_equal(theta_wave, test_wave_2) - def test_mod_square_VSM(self): waveform = wf.mod_square_VSM(1, 0, 20e-9, f_modulation=0, sampling_rate=1e9) @@ -290,4 +149,4 @@ def test_mod_square_VSM(self): np.testing.assert_almost_equal(waveform[0], np.zeros(20)) np.testing.assert_almost_equal(waveform[1], np.zeros(20)) np.testing.assert_almost_equal(waveform[2], np.ones(20)) - np.testing.assert_almost_equal(waveform[3], np.zeros(20)) \ No newline at end of file + np.testing.assert_almost_equal(waveform[3], np.zeros(20)) diff --git a/pycqed/tests/test_waveforms_flux.py b/pycqed/tests/test_waveforms_flux.py new file mode 100644 index 0000000000..4e3c6ef8a5 --- /dev/null +++ b/pycqed/tests/test_waveforms_flux.py @@ -0,0 +1,41 @@ +import numpy as np +import unittest +from pycqed.measurement.waveform_control_CC import waveforms_flux as wfl + + +class Test_waveforms_flux(unittest.TestCase): + + def test_eps_theta_conversion(self): + + eps = 800e6 + J2 = 40e6 + theta = wfl.eps_to_theta(eps=eps, g=J2) + theta_exp = 5.71059 + self.assertAlmostEqual(np.rad2deg(theta), theta_exp, places=4) + + eps_inv = wfl.theta_to_eps(theta=theta, g=J2) + self.assertAlmostEqual(eps, eps_inv, places=4) + + def test_eps_zero_conversion(self): + + eps = 0.0 + J2 = 40e6 + theta = wfl.eps_to_theta(eps=eps, g=J2) + self.assertAlmostEqual(theta, np.pi/2) + + eps = np.zeros(5) + J2 = 40e6 + thetas = wfl.eps_to_theta(eps=eps, g=J2) + np.testing.assert_array_almost_equal(thetas, 0.5*np.pi*np.ones(5)) + + def test_eps_theta_conversion_arrays(self): + eps = np.linspace(800e6, 0, 5) + J2 = 40e6 + thetas = wfl.eps_to_theta(eps=eps, g=J2) + thetas_exp = np.array( + [0.09966865, 0.13255153, 0.19739556, 0.38050638, 1.57079633]) + np.testing.assert_array_almost_equal(thetas, thetas_exp) + + eps_inv = wfl.theta_to_eps(thetas, g=J2) + np.testing.assert_array_almost_equal(eps, eps_inv) + From 917fb8e494d25af3e2a6302c6a330412b10e63d9 Mon Sep 17 00:00:00 2001 From: Adriaan Date: Tue, 28 Aug 2018 16:00:59 +0200 Subject: [PATCH 07/26] added checks for theta_initial and theta_f check in CZ waveform --- .../waveform_control_CC/waveforms_flux.py | 18 +++++++--------- pycqed/tests/test_waveforms_flux.py | 21 +++++++++++++++++++ 2 files changed, 29 insertions(+), 10 deletions(-) diff --git a/pycqed/measurement/waveform_control_CC/waveforms_flux.py b/pycqed/measurement/waveform_control_CC/waveforms_flux.py index 4131cd59f3..61b59b2d48 100644 --- a/pycqed/measurement/waveform_control_CC/waveforms_flux.py +++ b/pycqed/measurement/waveform_control_CC/waveforms_flux.py @@ -10,7 +10,7 @@ For more basic waveforms see e.g., waveforms.py ''' import logging -import scipy +import scipy.interpolate import numpy as np @@ -31,8 +31,8 @@ def martinis_flux_pulse(length: float, lambda_2 : lambda coeffecients lambda_3 : lambda_3 : - theta_f : initial angle of interaction in degrees. - theta_f : final angle of the interaction in degrees. + theta_f : initial angle of interaction (rad). + theta_f : final angle of the interaction (rad). sampling_rate : sampling rate of AWG in (Hz) This waveform is generated in several steps @@ -44,7 +44,7 @@ def martinis_flux_pulse(length: float, if theta_f < theta_i: raise ValueError( 'theta_f ({:.2f} deg) < theta_i ({:.2f} deg):'.format( - theta_f/(2*np.pi)*360, theta_i/(2*np.pi)*360) + np.rad2deg(theta_f), np.rad2deg(theta_i)) + 'final coupling weaker than initial coupling') # 1. Generate a time grid, may include fine sampling. @@ -59,24 +59,22 @@ def martinis_flux_pulse(length: float, taus = np.arange(0, rounded_length-tau_step/2, tau_step) # -tau_step/2 is to make sure final pt is excluded - # Determine lambda_1 using the constraint set by eq 16 from Martinis 2014 # lambda_1 is scaled such that the final ("center") angle is theta_f + # Determine lambda_1 using the constraint set by eq 16 from Martinis 2014 lambda_1 = (theta_f - theta_i) / (2) - lambda_3 # 2. Generate θ(τ) using eqs 15 and 16 theta_wave = np.ones(nr_samples) * theta_i theta_wave += lambda_1 * (1 - np.cos(2 * np.pi * taus / rounded_length)) - theta_wave += (lambda_1 * lambda_2 * - (1 - np.cos(4 * np.pi * taus / rounded_length))) - theta_wave += (lambda_1 * lambda_3 * - (1 - np.cos(6 * np.pi * taus / rounded_length))) + theta_wave += lambda_2 * (1 - np.cos(4 * np.pi * taus / rounded_length)) + theta_wave += lambda_3 * (1 - np.cos(6 * np.pi * taus / rounded_length)) # Clip wave to [theta_i, pi] to avoid poles in the wave expressed in freq theta_wave_clipped = np.clip(theta_wave, theta_i, np.pi-.01) if not np.array_equal(theta_wave, theta_wave_clipped): logging.warning( 'Martinis flux wave form has been clipped to [{}, 180 deg]' - .format(theta_i)) + .format(np.rad2deg(theta_i))) # 3. Transform from proper time τ to real time t using interpolation t = np.array([np.trapz(np.sin(theta_wave_clipped)[:i+1], diff --git a/pycqed/tests/test_waveforms_flux.py b/pycqed/tests/test_waveforms_flux.py index 4e3c6ef8a5..7eaeb1dd62 100644 --- a/pycqed/tests/test_waveforms_flux.py +++ b/pycqed/tests/test_waveforms_flux.py @@ -39,3 +39,24 @@ def test_eps_theta_conversion_arrays(self): eps_inv = wfl.theta_to_eps(thetas, g=J2) np.testing.assert_array_almost_equal(eps, eps_inv) + + def test_martinis_flux_pulse_theta_bounds(self): + """ + Tests that the constraint setting theta_f and theta_i is working + correctly + """ + + for theta_f in [40, 60, 80, 90]: + theta_f = np.deg2rad(theta_f) + for lambda_2 in np.linspace(-.2, .2, 5): + for lambda_3 in np.linspace(-.2, .2, 5): + theta_i = wfl.eps_to_theta(800e6, 25e6) + thetas = wfl.martinis_flux_pulse( + 35e-9, theta_i=theta_i, theta_f=theta_f, + lambda_2=lambda_2, lambda_3=lambda_3, sampling_rate=1e9) + np.testing.assert_almost_equal(thetas[0], theta_i, decimal=3) + np.testing.assert_almost_equal(thetas[-1], theta_i, decimal=2) + np.testing.assert_almost_equal( + thetas[len(thetas)//2], theta_f, decimal=3) + + From 26ca2c6fd4ca39d8f736c06cc12f8fbdccdad8da Mon Sep 17 00:00:00 2001 From: Adriaan Date: Tue, 28 Aug 2018 16:17:24 +0200 Subject: [PATCH 08/26] reverted changes to flux lutman --- .../meta_instrument/LutMans/flux_lutman.py | 79 +++++++++---------- 1 file changed, 39 insertions(+), 40 deletions(-) diff --git a/pycqed/instrument_drivers/meta_instrument/LutMans/flux_lutman.py b/pycqed/instrument_drivers/meta_instrument/LutMans/flux_lutman.py index 89593e7db1..20b9822197 100644 --- a/pycqed/instrument_drivers/meta_instrument/LutMans/flux_lutman.py +++ b/pycqed/instrument_drivers/meta_instrument/LutMans/flux_lutman.py @@ -323,23 +323,19 @@ def _add_waveform_parameters(self): initial_value=np.nan, parameter_class=ManualParameter) - self.add_parameter('cz_f_q0', vals=vals.Numbers(), + self.add_parameter('cz_freq_01_max', vals=vals.Numbers(), # initial value is chosen to not raise errors initial_value=6e9, unit='Hz', parameter_class=ManualParameter) - self.add_parameter('cz_f_q1', vals=vals.Numbers(), - # initial value is chosen to not raise errors - initial_value=6e9, - unit='Hz', parameter_class=ManualParameter) - self.add_parameter('cz_anharmonicity_q0', vals=vals.Numbers(), - # initial value is chosen to not raise errors - initial_value=-300e6, - unit='Hz', parameter_class=ManualParameter) - self.add_parameter('cz_J2', vals=vals.Numbers(), unit='Hz', # initial value is chosen to not raise errors initial_value=15e6, parameter_class=ManualParameter) + self.add_parameter('cz_freq_interaction', vals=vals.Numbers(), + # initial value is chosen to not raise errors + initial_value=5e9, + unit='Hz', + parameter_class=ManualParameter) self.add_parameter('cz_phase_corr_length', unit='s', initial_value=5e-9, vals=vals.Numbers(), @@ -454,11 +450,11 @@ def _gen_cz(self): lambda_2=self.cz_lambda_2(), lambda_3=self.cz_lambda_3(), theta_f=self.cz_theta_f(), - f_q0=self.cz_f_q0(), - f_q1=self.cz_f_q1(), - anharmonicity_q0=self.cz_anharmonicity_q0(), + f_01_max=self.cz_freq_01_max(), J2=self.cz_J2(), - sampling_rate=self.sampling_rate()) + f_interaction=self.cz_freq_interaction(), + sampling_rate=self.sampling_rate(), + return_unit='f01') return dac_scale_factor*self.detuning_to_amp( self.cz_freq_01_max() - CZ) else: @@ -469,11 +465,13 @@ def _gen_cz(self): lambda_2=self.cz_lambda_2(), lambda_3=self.cz_lambda_3(), theta_f=self.cz_theta_f(), - f_q0=self.cz_f_q0(), - f_q1=self.cz_f_q1(), - anharmonicity_q0=self.cz_anharmonicity_q0(), + f_01_max=self.cz_freq_01_max(), + # V_per_phi0=self.cz_V_per_phi0(), J2=self.cz_J2(), - sampling_rate=self.sampling_rate()) + # E_c=self.cz_E_c(), + f_interaction=self.cz_freq_interaction(), + sampling_rate=self.sampling_rate(), + return_unit='f01') half_CZ_A = dac_scale_factor*self.detuning_to_amp( self.cz_freq_01_max() - half_CZ_A) @@ -495,14 +493,16 @@ def _gen_cz(self): half_CZ_B = wf.martinis_flux_pulse( length=self.cz_length()*(1-self.czd_length_ratio()), - lambda_2=self.cz_lambda_2(), - lambda_3=self.cz_lambda_3(), - theta_f=self.cz_theta_f(), - f_q0=self.cz_f_q0(), - f_q1=self.cz_f_q1(), - anharmonicity_q0=self.cz_anharmonicity_q0(), + lambda_2=d_lambda_2, + lambda_3=d_lambda_3, + theta_f=d_theta_f, + f_01_max=self.cz_freq_01_max(), + # V_per_phi0=self.cz_V_per_phi0(), J2=self.cz_J2(), - sampling_rate=self.sampling_rate()) + # E_c=self.cz_E_c(), + f_interaction=self.cz_freq_interaction(), + sampling_rate=self.sampling_rate(), + return_unit='f01') half_CZ_B = dac_scale_factor*self.detuning_to_amp( self.cz_freq_01_max() - half_CZ_B, positive_branch=False) @@ -850,8 +850,8 @@ def load_waveform_onto_AWG_lookuptable(self, waveform_name: str, self.AWG.get_instr().set(codeword, waveform) def load_waveforms_onto_AWG_lookuptable( - self, regenerate_waveforms: bool=True, stop_start: bool = True, - force_load_sequencer_program: bool=False): + self, regenerate_waveforms: bool=True, stop_start: bool = True, + force_load_sequencer_program: bool=False): """ Loads all waveforms specified in the LutMap to an AWG for both this LutMap and the partner LutMap. @@ -1135,7 +1135,7 @@ def plot_cz_trajectory(self, ax=None, show=True): samples = np.arange(len(dac_amps)) amps = dac_amps*self.get_dac_val_to_amp_scalefactor() deltas = self.amp_to_detuning(amps) - freqs = self.cz_f_q0()+self.cz_f_q1()-deltas + freqs = self.cz_freq_01_max()-deltas ax.scatter(amps, freqs, c=samples, label='CZ trajectory') if show: plt.show() @@ -1152,20 +1152,19 @@ def plot_flux_arc(self, ax=None, show=True, f, ax = plt.subplots() amps = np.linspace(-2.5, 2.5, 101) # maximum voltage of AWG amp mode deltas = self.amp_to_detuning(amps) - freqs_11 = self.cz_f_q0()+self.cz_f_q1()-deltas - freqs_20 = 2*self.cz_f_q0()-self.cz_anharmonicity_q0()-2*deltas + freqs = self.cz_freq_01_max()-deltas - ax.plot(amps, freqs_11, label='$f_{01}$') - ax.plot(amps, freqs_20, - label='$f_{\mathrm{int.}}$:'+' {:.3f} GHz'.format( - (self.cz_f_q1()*2-self.cz_anharmonicity_q0())*1e-9), - c='C1') + ax.plot(amps, freqs, label='$f_{01}$') + ax.axhline(self.cz_freq_interaction(), -5, 5, + label='$f_{\mathrm{int.}}$:'+' {:.3f} GHz'.format( + self.cz_freq_interaction()*1e-9), + c='C1') - ax.axvline(0, 0, 2e10, linestyle='dotted', c='grey') + ax.axvline(0, 0, 1e10, linestyle='dotted', c='grey') ax.fill_between( - x=amps, - y1=freqs_20-self.cz_J2(), - y2=freqs_20+self.cz_J2(), + x=[-5, 5], + y1=[self.cz_freq_interaction()-self.cz_J2()]*2, + y2=[self.cz_freq_interaction()+self.cz_J2()]*2, label='$J_{\mathrm{2}}/2\pi$:'+' {:.3f} MHz'.format( self.cz_J2()*1e-6), color='C1', alpha=0.25) @@ -1179,7 +1178,7 @@ def plot_flux_arc(self, ax=None, show=True, set_xlabel(ax, 'AWG amplitude', 'V') set_ylabel(ax, 'Frequency', 'Hz') ax.set_xlim(-2.5, 2.5) - ax.set_ylim(np.min(freqs_20)+500e6, np.max(freqs_20)+500e6) + ax.set_ylim(3e9, np.max(freqs)+500e6) dac_val_axis = ax.twiny() dac_ax_lims = np.array(ax.get_xlim()) * \ From c83d17c29303002f5805979e195867adb8d9ae5d Mon Sep 17 00:00:00 2001 From: Adriaan Date: Tue, 28 Aug 2018 19:03:32 +0200 Subject: [PATCH 09/26] split tests of fluxlutman and mwlutman --- .../meta_instrument/LutMans/flux_lutman.py | 73 ++++-- .../{test_LutMans.py => test_flux_lutman.py} | 212 +---------------- pycqed/tests/test_mw_lutman.py | 218 ++++++++++++++++++ 3 files changed, 276 insertions(+), 227 deletions(-) rename pycqed/tests/{test_LutMans.py => test_flux_lutman.py} (61%) create mode 100644 pycqed/tests/test_mw_lutman.py diff --git a/pycqed/instrument_drivers/meta_instrument/LutMans/flux_lutman.py b/pycqed/instrument_drivers/meta_instrument/LutMans/flux_lutman.py index 20b9822197..a9bc529e67 100644 --- a/pycqed/instrument_drivers/meta_instrument/LutMans/flux_lutman.py +++ b/pycqed/instrument_drivers/meta_instrument/LutMans/flux_lutman.py @@ -1,7 +1,6 @@ from .base_lutman import Base_LutMan import numpy as np import logging -from scipy.optimize import minimize from copy import copy from qcodes.instrument.parameter import ManualParameter, InstrumentRefParameter from qcodes.utils import validators as vals @@ -70,11 +69,19 @@ def __init__(self, name, **kw): super().__init__(name, **kw) self._wave_dict_dist = dict() self.sampling_rate(2.4e9) + self._add_qubit_parameters() - def _add_cfg_parameters(self): + def _add_qubit_parameters(self): + """ + Adds parameters responsible for keeping track of qubit frequencies, + coupling strengths etc. + N.B. Currently this is geared towards a 2-qubit device. Ideally, + these parameters would be extracted from the relevant qubit objects + in a way that does not violate the layers of abstraction. + """ self.add_parameter( - 'polycoeffs_freq_conv', + 'q_polycoeffs_freq_01_det', docstring='coefficients of the polynomial used to convert ' 'amplitude in V to detuning in Hz. N.B. it is important to ' 'include both the AWG range and channel amplitude in the params.\n' @@ -86,6 +93,53 @@ def _add_cfg_parameters(self): # initial value is chosen to not raise errors initial_value=np.array([2e9, 0, 0]), parameter_class=ManualParameter) + self.add_parameter( + 'q_polycoeffs_anharmonicity', + docstring='coefficients of the polynomial used to calculate ' + 'the anharmonicity (Hz) as a function of amplitude in V. ' + 'N.B. it is important to ' + 'include both the AWG range and channel amplitude in the params.\n', + vals=vals.Arrays(), + # initial value sets a flux independent anharmonicity of 300MHz + initial_value=np.array([0, 0, -300e6]), + parameter_class=ManualParameter) + + + self.add_parameter('q_freq_01', vals=vals.Numbers(), + docstring='Current operating frequency of qubit', + # initial value is chosen to not raise errors + initial_value=6e9, + unit='Hz', parameter_class=ManualParameter) + + self.add_parameter('q_freq_10', vals=vals.Numbers(), + docstring='Current operating frequency of qubit' + ' with which a CZ gate can be performed.', + # initial value is chosen to not raise errors + initial_value=6e9, + unit='Hz', parameter_class=ManualParameter) + self.add_parameter('cz_J2', vals=vals.Numbers(), unit='Hz', + docstring='effective coupling between the 11 and ' + '02 states.', + # initial value is chosen to not raise errors + initial_value=15e6, + parameter_class=ManualParameter) + + + def _add_cfg_parameters(self): + + # self.add_parameter( + # 'polycoeffs_freq_conv', + # docstring='coefficients of the polynomial used to convert ' + # 'amplitude in V to detuning in Hz. N.B. it is important to ' + # 'include both the AWG range and channel amplitude in the params.\n' + # 'In order to convert a set of cryoscope flux arc coefficients to ' + # ' units of Volts they can be rescaled using [c0*sc**2, c1*sc, c2]' + # ' where sc is the desired scaling factor that includes the sq_amp ' + # 'used and the range of the AWG (5 in amp mode).', + # vals=vals.Arrays(), + # # initial value is chosen to not raise errors + # initial_value=np.array([2e9, 0, 0]), + # parameter_class=ManualParameter) self.add_parameter('cfg_awg_channel', initial_value=1, @@ -323,19 +377,6 @@ def _add_waveform_parameters(self): initial_value=np.nan, parameter_class=ManualParameter) - self.add_parameter('cz_freq_01_max', vals=vals.Numbers(), - # initial value is chosen to not raise errors - initial_value=6e9, - unit='Hz', parameter_class=ManualParameter) - self.add_parameter('cz_J2', vals=vals.Numbers(), unit='Hz', - # initial value is chosen to not raise errors - initial_value=15e6, - parameter_class=ManualParameter) - self.add_parameter('cz_freq_interaction', vals=vals.Numbers(), - # initial value is chosen to not raise errors - initial_value=5e9, - unit='Hz', - parameter_class=ManualParameter) self.add_parameter('cz_phase_corr_length', unit='s', initial_value=5e-9, vals=vals.Numbers(), diff --git a/pycqed/tests/test_LutMans.py b/pycqed/tests/test_flux_lutman.py similarity index 61% rename from pycqed/tests/test_LutMans.py rename to pycqed/tests/test_flux_lutman.py index 60088b1959..275c7b4430 100644 --- a/pycqed/tests/test_LutMans.py +++ b/pycqed/tests/test_flux_lutman.py @@ -4,198 +4,11 @@ from pycqed.instrument_drivers.meta_instrument import lfilt_kernel_object as lko from pycqed.instrument_drivers.meta_instrument.LutMans import mw_lutman as mwl -from pycqed.instrument_drivers.meta_instrument.LutMans import flux_lutman as flm from pycqed.measurement.waveform_control_CC import waveform as wf from pycqed.instrument_drivers.meta_instrument.LutMans.base_lutman import \ get_redundant_codewords - -class Test_MW_LutMan(unittest.TestCase): - - @classmethod - def setUpClass(self): - self.AWG = v8.VirtualAWG8('DummyAWG8') - - self.AWG8_MW_LutMan = mwl.AWG8_MW_LutMan('MW_LutMan') - self.AWG8_MW_LutMan.AWG(self.AWG.name) - self.AWG8_MW_LutMan.channel_I(1) - self.AWG8_MW_LutMan.channel_Q(2) - self.AWG8_MW_LutMan.mw_modulation(100e6) - self.AWG8_MW_LutMan.sampling_rate(2.4e9) - self.AWG8_MW_LutMan.set_default_lutmap() - - self.AWG8_VSM_MW_LutMan = mwl.AWG8_VSM_MW_LutMan('MW_LutMan_VSM') - self.AWG8_VSM_MW_LutMan.AWG(self.AWG.name) - self.AWG8_VSM_MW_LutMan.channel_GI(1) - self.AWG8_VSM_MW_LutMan.channel_GQ(2) - self.AWG8_VSM_MW_LutMan.channel_DI(3) - self.AWG8_VSM_MW_LutMan.channel_DQ(4) - self.AWG8_VSM_MW_LutMan.mw_modulation(100e6) - self.AWG8_VSM_MW_LutMan.sampling_rate(2.4e9) - self.AWG8_VSM_MW_LutMan.set_default_lutmap() - - self.CBox_MW_LutMan = mwl.CBox_MW_LutMan('CBox_MW_LutMan') - self.QWG_MW_LutMan = mwl.QWG_MW_LutMan('QWG_MW_LutMan') - - def test__program_hash_differs_AWG8_lutman(self): - - # set to a random value to ensure different - self.AWG8_MW_LutMan._awgs_mw_sequencer_program_expected_hash(351340) - hash_differs = self.AWG8_MW_LutMan._program_hash_differs() - self.assertTrue(hash_differs) - - self.AWG8_MW_LutMan._update_expected_program_hash() - hash_differs = self.AWG8_MW_LutMan._program_hash_differs() - self.assertFalse(hash_differs) - - def test__program_hash_differs_AWG8_VSM_lutman(self): - - # set to a random value to ensure different - self.AWG8_VSM_MW_LutMan._awgs_mwG_sequencer_program_expected_hash( - 351340) - hash_differs = self.AWG8_VSM_MW_LutMan._program_hash_differs() - self.assertTrue(hash_differs) - - self.AWG8_VSM_MW_LutMan._update_expected_program_hash() - hash_differs = self.AWG8_VSM_MW_LutMan._program_hash_differs() - self.assertFalse(hash_differs) - - def test__program_hash_updated_when_loading_program(self): - self.AWG8_MW_LutMan._awgs_mw_sequencer_program_expected_hash(351340) - hash_differs = self.AWG8_MW_LutMan._program_hash_differs() - self.assertTrue(hash_differs) - - self.AWG8_MW_LutMan.load_waveforms_onto_AWG_lookuptable() - hash_differs = self.AWG8_MW_LutMan._program_hash_differs() - self.assertFalse(hash_differs) - - def test_uploading_standard_pulses(self): - # Tests that all waveforms are present and no error is raised. - self.AWG8_MW_LutMan.load_waveforms_onto_AWG_lookuptable() - expected_wf = wf.mod_gauss( - amp=self.AWG8_MW_LutMan.mw_amp180(), - sigma_length=self.AWG8_MW_LutMan.mw_gauss_width(), - f_modulation=self.AWG8_MW_LutMan.mw_modulation(), - sampling_rate=self.AWG8_MW_LutMan.sampling_rate(), phase=0, - motzoi=self.AWG8_MW_LutMan.mw_motzoi())[0] - - uploaded_wf = self.AWG.get('wave_ch1_cw001') - np.testing.assert_array_almost_equal(expected_wf, uploaded_wf) - - expected_wf_spec = wf.block_pulse( - length=self.AWG8_MW_LutMan.spec_length(), - amp=self.AWG8_MW_LutMan.spec_amp(), - sampling_rate=self.AWG8_MW_LutMan.sampling_rate(), - delay=0, phase=0)[0] - uploaded_wf = self.AWG.get('wave_ch1_cw008') - np.testing.assert_array_almost_equal(expected_wf_spec, uploaded_wf) - - def test_lut_mapping_AWG8(self): - self.AWG8_MW_LutMan.set_default_lutmap() - expected_dict = { - 'rY90': ('wave_ch1_cw004', - 'wave_ch2_cw004'), - 'I': ('wave_ch1_cw000', - 'wave_ch2_cw000'), - 'rY180': ('wave_ch1_cw002', - 'wave_ch2_cw002'), - 'rX180': ('wave_ch1_cw001', - 'wave_ch2_cw001'), - 'rPhi90': ('wave_ch1_cw007', - 'wave_ch2_cw007'), - 'rX90': ('wave_ch1_cw003', - 'wave_ch2_cw003'), - 'rYm90': ('wave_ch1_cw006', - 'wave_ch2_cw006'), - 'rXm90': ('wave_ch1_cw005', - 'wave_ch2_cw005'), - 'spec': ('wave_ch1_cw008', - 'wave_ch2_cw008')} - # Does not check the full lutmap - dict_contained_in(expected_dict, self.AWG8_MW_LutMan.LutMap()) - - def test_lut_mapping_CBox(self): - self.CBox_MW_LutMan.set_default_lutmap() - expected_dict = {'I': 0, - 'rX180': 1, - 'rY180': 2, - 'rX90': 3, - 'rY90': 4, - 'rXm90': 5, - 'rYm90': 6, - 'rPhi90': 7} - - self.assertDictEqual.__self__.maxDiff = None - self.assertDictEqual(expected_dict, self.CBox_MW_LutMan.LutMap()) - - def test_lut_mapping_AWG8_VSM(self): - self.AWG8_VSM_MW_LutMan.set_default_lutmap() - expected_dict = { - 'rY90': ('wave_ch1_cw004', - 'wave_ch2_cw004', - 'wave_ch3_cw004', - 'wave_ch4_cw004'), - 'I': ('wave_ch1_cw000', - 'wave_ch2_cw000', - 'wave_ch3_cw000', - 'wave_ch4_cw000'), - 'rY180': ('wave_ch1_cw002', - 'wave_ch2_cw002', - 'wave_ch3_cw002', - 'wave_ch4_cw002'), - 'rX180': ('wave_ch1_cw001', - 'wave_ch2_cw001', - 'wave_ch3_cw001', - 'wave_ch4_cw001'), - 'rPhi90': ('wave_ch1_cw007', - 'wave_ch2_cw007', - 'wave_ch3_cw007', - 'wave_ch4_cw007'), - 'rX90': ('wave_ch1_cw003', - 'wave_ch2_cw003', - 'wave_ch3_cw003', - 'wave_ch4_cw003'), - 'rYm90': ('wave_ch1_cw006', - 'wave_ch2_cw006', - 'wave_ch3_cw006', - 'wave_ch4_cw006'), - 'rXm90': ('wave_ch1_cw005', - 'wave_ch2_cw005', - 'wave_ch3_cw005', - 'wave_ch4_cw005'), - 'spec': ('wave_ch1_cw008', - 'wave_ch2_cw008', - 'wave_ch3_cw008', - 'wave_ch4_cw008')} - # Does not check the full lutmap - dict_contained_in(expected_dict, self.AWG8_VSM_MW_LutMan.LutMap()) - - def test_realtime_loading_square_wf_AWG8_VSM(self): - self.AWG8_VSM_MW_LutMan.load_waveform_realtime('square', wf_nr=3) - - def test_uploading_standard_pulses_AWG8_VSM(self): - # Tests that all waveforms are present and no error is raised. - self.AWG8_VSM_MW_LutMan.load_waveforms_onto_AWG_lookuptable() - expected_wfs = wf.mod_gauss_VSM( - amp=self.AWG8_VSM_MW_LutMan.mw_amp180(), - sigma_length=self.AWG8_VSM_MW_LutMan.mw_gauss_width(), - f_modulation=self.AWG8_VSM_MW_LutMan.mw_modulation(), - sampling_rate=self.AWG8_VSM_MW_LutMan.sampling_rate(), phase=0, - motzoi=self.AWG8_VSM_MW_LutMan.mw_motzoi()) - - for i in range(4): - uploaded_wf = self.AWG.get('wave_ch{}_cw001'.format(i+1)) - np.testing.assert_array_almost_equal(expected_wfs[i], uploaded_wf) - - @classmethod - def tearDownClass(self): - for inststr in list(self.AWG._all_instruments): - try: - inst = self.AWG.find_instrument(inststr) - inst.close() - except KeyError: - pass - +from pycqed.instrument_drivers.meta_instrument.LutMans import flux_lutman as flm class Test_Flux_LutMan(unittest.TestCase): @@ -495,26 +308,3 @@ def tearDownClass(self): inst.close() except KeyError: pass - - -class Test_LutMan_Utils(unittest.TestCase): - def test_get_redundant_codewords(self): - target_cw = 5 - red_cws_A = get_redundant_codewords(target_cw, 4, 0) - for cw in red_cws_A: - print(bin(cw)) - self.assertEqual(cw & 15, target_cw) - self.assertEqual(len(red_cws_A), 2**4) - - red_cws_B = get_redundant_codewords(target_cw, 4, 4) - for cw in red_cws_B: - print(bin(cw)) - self.assertEqual((cw & (256-16)) >> 4, target_cw) - self.assertEqual(len(red_cws_B), 2**4) - - -def dict_contained_in(subset, superset): - if subset.items() <= superset.items(): - return True - else: - raise ValueError diff --git a/pycqed/tests/test_mw_lutman.py b/pycqed/tests/test_mw_lutman.py new file mode 100644 index 0000000000..3805cac83a --- /dev/null +++ b/pycqed/tests/test_mw_lutman.py @@ -0,0 +1,218 @@ +import unittest +import numpy as np +import pycqed.instrument_drivers.virtual_instruments.virtual_AWG8 as v8 + +from pycqed.instrument_drivers.meta_instrument.LutMans import mw_lutman as mwl +from pycqed.measurement.waveform_control_CC import waveform as wf +from pycqed.instrument_drivers.meta_instrument.LutMans.base_lutman import \ + get_redundant_codewords + + +class Test_MW_LutMan(unittest.TestCase): + + @classmethod + def setUpClass(self): + self.AWG = v8.VirtualAWG8('DummyAWG8') + + self.AWG8_MW_LutMan = mwl.AWG8_MW_LutMan('MW_LutMan') + self.AWG8_MW_LutMan.AWG(self.AWG.name) + self.AWG8_MW_LutMan.channel_I(1) + self.AWG8_MW_LutMan.channel_Q(2) + self.AWG8_MW_LutMan.mw_modulation(100e6) + self.AWG8_MW_LutMan.sampling_rate(2.4e9) + self.AWG8_MW_LutMan.set_default_lutmap() + + self.AWG8_VSM_MW_LutMan = mwl.AWG8_VSM_MW_LutMan('MW_LutMan_VSM') + self.AWG8_VSM_MW_LutMan.AWG(self.AWG.name) + self.AWG8_VSM_MW_LutMan.channel_GI(1) + self.AWG8_VSM_MW_LutMan.channel_GQ(2) + self.AWG8_VSM_MW_LutMan.channel_DI(3) + self.AWG8_VSM_MW_LutMan.channel_DQ(4) + self.AWG8_VSM_MW_LutMan.mw_modulation(100e6) + self.AWG8_VSM_MW_LutMan.sampling_rate(2.4e9) + self.AWG8_VSM_MW_LutMan.set_default_lutmap() + + self.CBox_MW_LutMan = mwl.CBox_MW_LutMan('CBox_MW_LutMan') + self.QWG_MW_LutMan = mwl.QWG_MW_LutMan('QWG_MW_LutMan') + + def test__program_hash_differs_AWG8_lutman(self): + + # set to a random value to ensure different + self.AWG8_MW_LutMan._awgs_mw_sequencer_program_expected_hash(351340) + hash_differs = self.AWG8_MW_LutMan._program_hash_differs() + self.assertTrue(hash_differs) + + self.AWG8_MW_LutMan._update_expected_program_hash() + hash_differs = self.AWG8_MW_LutMan._program_hash_differs() + self.assertFalse(hash_differs) + + def test__program_hash_differs_AWG8_VSM_lutman(self): + + # set to a random value to ensure different + self.AWG8_VSM_MW_LutMan._awgs_mwG_sequencer_program_expected_hash( + 351340) + hash_differs = self.AWG8_VSM_MW_LutMan._program_hash_differs() + self.assertTrue(hash_differs) + + self.AWG8_VSM_MW_LutMan._update_expected_program_hash() + hash_differs = self.AWG8_VSM_MW_LutMan._program_hash_differs() + self.assertFalse(hash_differs) + + def test__program_hash_updated_when_loading_program(self): + self.AWG8_MW_LutMan._awgs_mw_sequencer_program_expected_hash(351340) + hash_differs = self.AWG8_MW_LutMan._program_hash_differs() + self.assertTrue(hash_differs) + + self.AWG8_MW_LutMan.load_waveforms_onto_AWG_lookuptable() + hash_differs = self.AWG8_MW_LutMan._program_hash_differs() + self.assertFalse(hash_differs) + + def test_uploading_standard_pulses(self): + # Tests that all waveforms are present and no error is raised. + self.AWG8_MW_LutMan.load_waveforms_onto_AWG_lookuptable() + expected_wf = wf.mod_gauss( + amp=self.AWG8_MW_LutMan.mw_amp180(), + sigma_length=self.AWG8_MW_LutMan.mw_gauss_width(), + f_modulation=self.AWG8_MW_LutMan.mw_modulation(), + sampling_rate=self.AWG8_MW_LutMan.sampling_rate(), phase=0, + motzoi=self.AWG8_MW_LutMan.mw_motzoi())[0] + + uploaded_wf = self.AWG.get('wave_ch1_cw001') + np.testing.assert_array_almost_equal(expected_wf, uploaded_wf) + + expected_wf_spec = wf.block_pulse( + length=self.AWG8_MW_LutMan.spec_length(), + amp=self.AWG8_MW_LutMan.spec_amp(), + sampling_rate=self.AWG8_MW_LutMan.sampling_rate(), + delay=0, phase=0)[0] + uploaded_wf = self.AWG.get('wave_ch1_cw008') + np.testing.assert_array_almost_equal(expected_wf_spec, uploaded_wf) + + def test_lut_mapping_AWG8(self): + self.AWG8_MW_LutMan.set_default_lutmap() + expected_dict = { + 'rY90': ('wave_ch1_cw004', + 'wave_ch2_cw004'), + 'I': ('wave_ch1_cw000', + 'wave_ch2_cw000'), + 'rY180': ('wave_ch1_cw002', + 'wave_ch2_cw002'), + 'rX180': ('wave_ch1_cw001', + 'wave_ch2_cw001'), + 'rPhi90': ('wave_ch1_cw007', + 'wave_ch2_cw007'), + 'rX90': ('wave_ch1_cw003', + 'wave_ch2_cw003'), + 'rYm90': ('wave_ch1_cw006', + 'wave_ch2_cw006'), + 'rXm90': ('wave_ch1_cw005', + 'wave_ch2_cw005'), + 'spec': ('wave_ch1_cw008', + 'wave_ch2_cw008')} + # Does not check the full lutmap + dict_contained_in(expected_dict, self.AWG8_MW_LutMan.LutMap()) + + def test_lut_mapping_CBox(self): + self.CBox_MW_LutMan.set_default_lutmap() + expected_dict = {'I': 0, + 'rX180': 1, + 'rY180': 2, + 'rX90': 3, + 'rY90': 4, + 'rXm90': 5, + 'rYm90': 6, + 'rPhi90': 7} + + self.assertDictEqual.__self__.maxDiff = None + self.assertDictEqual(expected_dict, self.CBox_MW_LutMan.LutMap()) + + def test_lut_mapping_AWG8_VSM(self): + self.AWG8_VSM_MW_LutMan.set_default_lutmap() + expected_dict = { + 'rY90': ('wave_ch1_cw004', + 'wave_ch2_cw004', + 'wave_ch3_cw004', + 'wave_ch4_cw004'), + 'I': ('wave_ch1_cw000', + 'wave_ch2_cw000', + 'wave_ch3_cw000', + 'wave_ch4_cw000'), + 'rY180': ('wave_ch1_cw002', + 'wave_ch2_cw002', + 'wave_ch3_cw002', + 'wave_ch4_cw002'), + 'rX180': ('wave_ch1_cw001', + 'wave_ch2_cw001', + 'wave_ch3_cw001', + 'wave_ch4_cw001'), + 'rPhi90': ('wave_ch1_cw007', + 'wave_ch2_cw007', + 'wave_ch3_cw007', + 'wave_ch4_cw007'), + 'rX90': ('wave_ch1_cw003', + 'wave_ch2_cw003', + 'wave_ch3_cw003', + 'wave_ch4_cw003'), + 'rYm90': ('wave_ch1_cw006', + 'wave_ch2_cw006', + 'wave_ch3_cw006', + 'wave_ch4_cw006'), + 'rXm90': ('wave_ch1_cw005', + 'wave_ch2_cw005', + 'wave_ch3_cw005', + 'wave_ch4_cw005'), + 'spec': ('wave_ch1_cw008', + 'wave_ch2_cw008', + 'wave_ch3_cw008', + 'wave_ch4_cw008')} + # Does not check the full lutmap + dict_contained_in(expected_dict, self.AWG8_VSM_MW_LutMan.LutMap()) + + def test_realtime_loading_square_wf_AWG8_VSM(self): + self.AWG8_VSM_MW_LutMan.load_waveform_realtime('square', wf_nr=3) + + def test_uploading_standard_pulses_AWG8_VSM(self): + # Tests that all waveforms are present and no error is raised. + self.AWG8_VSM_MW_LutMan.load_waveforms_onto_AWG_lookuptable() + expected_wfs = wf.mod_gauss_VSM( + amp=self.AWG8_VSM_MW_LutMan.mw_amp180(), + sigma_length=self.AWG8_VSM_MW_LutMan.mw_gauss_width(), + f_modulation=self.AWG8_VSM_MW_LutMan.mw_modulation(), + sampling_rate=self.AWG8_VSM_MW_LutMan.sampling_rate(), phase=0, + motzoi=self.AWG8_VSM_MW_LutMan.mw_motzoi()) + + for i in range(4): + uploaded_wf = self.AWG.get('wave_ch{}_cw001'.format(i+1)) + np.testing.assert_array_almost_equal(expected_wfs[i], uploaded_wf) + + @classmethod + def tearDownClass(self): + for inststr in list(self.AWG._all_instruments): + try: + inst = self.AWG.find_instrument(inststr) + inst.close() + except KeyError: + pass + + +class Test_LutMan_Utils(unittest.TestCase): + def test_get_redundant_codewords(self): + target_cw = 5 + red_cws_A = get_redundant_codewords(target_cw, 4, 0) + for cw in red_cws_A: + print(bin(cw)) + self.assertEqual(cw & 15, target_cw) + self.assertEqual(len(red_cws_A), 2**4) + + red_cws_B = get_redundant_codewords(target_cw, 4, 4) + for cw in red_cws_B: + print(bin(cw)) + self.assertEqual((cw & (256-16)) >> 4, target_cw) + self.assertEqual(len(red_cws_B), 2**4) + + +def dict_contained_in(subset, superset): + if subset.items() <= superset.items(): + return True + else: + raise ValueError From 20ba51ece1728b2adfd17956fc9d485241f67aa7 Mon Sep 17 00:00:00 2001 From: Adriaan Date: Tue, 28 Aug 2018 19:24:45 +0200 Subject: [PATCH 10/26] basic levels integrated in flux lutman --- .../meta_instrument/LutMans/flux_lutman.py | 94 ++++++++++++++----- pycqed/tests/test_flux_lutman.py | 5 +- 2 files changed, 69 insertions(+), 30 deletions(-) diff --git a/pycqed/instrument_drivers/meta_instrument/LutMans/flux_lutman.py b/pycqed/instrument_drivers/meta_instrument/LutMans/flux_lutman.py index a9bc529e67..5cf0e0d239 100644 --- a/pycqed/instrument_drivers/meta_instrument/LutMans/flux_lutman.py +++ b/pycqed/instrument_drivers/meta_instrument/LutMans/flux_lutman.py @@ -94,7 +94,7 @@ def _add_qubit_parameters(self): initial_value=np.array([2e9, 0, 0]), parameter_class=ManualParameter) self.add_parameter( - 'q_polycoeffs_anharmonicity', + 'q_polycoeffs_anharm', docstring='coefficients of the polynomial used to calculate ' 'the anharmonicity (Hz) as a function of amplitude in V. ' 'N.B. it is important to ' @@ -104,7 +104,6 @@ def _add_qubit_parameters(self): initial_value=np.array([0, 0, -300e6]), parameter_class=ManualParameter) - self.add_parameter('q_freq_01', vals=vals.Numbers(), docstring='Current operating frequency of qubit', # initial value is chosen to not raise errors @@ -124,7 +123,6 @@ def _add_qubit_parameters(self): initial_value=15e6, parameter_class=ManualParameter) - def _add_cfg_parameters(self): # self.add_parameter( @@ -214,12 +212,46 @@ def _set_cfg_operating_mode(self, val): def _get_cfg_operating_mode(self): return self._cfg_operating_mode - def amp_to_detuning(self, amp): + + def get_polycoeffs_state(self, state: str): """ - Converts amplitude to detuning in Hz. + Args: + state (str) : string of 2 numbers denoting the state. The numbers + correspond to the number of excitations in each qubits. + The LSQ (right) corresponds to the qubit being fluxed and + under control of this flux lutman. - Requires "polycoeffs_freq_conv" to be set to the polynomial values - extracted from the cryoscope flux arc. + Get's the polynomial coefficients that are used to calculate the + energy levels of specific states. + Note that avoided crossings are not taken into account here. + + + """ + polycoeffs = np.zeros(3) + if state == '01': + polycoeffs += self.q_polycoeffs_freq_01_det() + polycoeffs[2] += self.q_freq_01() + elif state == '11': + polycoeffs += self.q_polycoeffs_freq_01_det() + polycoeffs[2] += self.q_freq_01() + self.q_freq_10() + elif state == '02': + polycoeffs += 2*self.q_polycoeffs_freq_01_det() + polycoeffs += self.q_polycoeffs_anharm() + polycoeffs[2] += 2*self.q_freq_01() + else: + raise NotImplementedError() + return polycoeffs + + + def amp_to_frequency(self, amp: float, state: str='01'): + """ + Converts pulse amplitude in Volt to energy in Hz for a particular state + Args: + amp (float) : amplitude in Volt + state (str) : string of 2 numbers denoting the state. The numbers + correspond to the number of excitations in each qubits. + The LSQ (right) corresponds to the qubit being fluxed and + under control of this flux lutman. N.B. this method assumes that the polycoeffs are with respect to the amplitude in units of V, including rescaling due to the channel @@ -228,16 +260,18 @@ def amp_to_detuning(self, amp): amp_Volts = amp_dac_val * channel_amp * channel_range """ - return np.polyval(self.polycoeffs_freq_conv(), amp) + polycoeffs = self.get_polycoeffs_state(state=state) + + return np.polyval(polycoeffs, amp) - def detuning_to_amp(self, freq, positive_branch=True): + def frequency_to_amp(self, freq: float, state:str='01', + positive_branch=True): """ - Converts detuning in Hz to amplitude. + Converts amplitude to detuning in Hz. Requires "polycoeffs_freq_conv" to be set to the polynomial values extracted from the cryoscope flux arc. - N.B. this method assumes that the polycoeffs are with respect to the amplitude in units of V, including rescaling due to the channel amplitude and range settings of the AWG8. @@ -249,7 +283,8 @@ def detuning_to_amp(self, freq, positive_branch=True): if isinstance(freq, (list, np.ndarray)): return np.array([self.detuning_to_amp( f, positive_branch=positive_branch) for f in freq]) - p = np.poly1d(self.polycoeffs_freq_conv()) + polycoeffs = self.get_polycoeffs_state(state=state) + p = np.poly1d(polycoeffs) sols = (p-freq).roots # sols returns 2 solutions (for a 2nd order polynomial) @@ -377,7 +412,6 @@ def _add_waveform_parameters(self): initial_value=np.nan, parameter_class=ManualParameter) - self.add_parameter('cz_phase_corr_length', unit='s', initial_value=5e-9, vals=vals.Numbers(), parameter_class=ManualParameter) @@ -1182,6 +1216,14 @@ def plot_cz_trajectory(self, ax=None, show=True): plt.show() return ax + def plot_level_diagram(self, ax=None, show=True, + plot_cz_trajectory=False): + + if ax is None: + f, ax = plt.subplots() + + return ax + def plot_flux_arc(self, ax=None, show=True, plot_cz_trajectory=False): """ @@ -1192,23 +1234,23 @@ def plot_flux_arc(self, ax=None, show=True, if ax is None: f, ax = plt.subplots() amps = np.linspace(-2.5, 2.5, 101) # maximum voltage of AWG amp mode - deltas = self.amp_to_detuning(amps) - freqs = self.cz_freq_01_max()-deltas + + freqs = self.amp_to_frequency(amps, state='01') ax.plot(amps, freqs, label='$f_{01}$') - ax.axhline(self.cz_freq_interaction(), -5, 5, - label='$f_{\mathrm{int.}}$:'+' {:.3f} GHz'.format( - self.cz_freq_interaction()*1e-9), - c='C1') + # ax.axhline(self.cz_freq_interaction(), -5, 5, + # label='$f_{\mathrm{int.}}$:'+' {:.3f} GHz'.format( + # self.cz_freq_interaction()*1e-9), + # c='C1') ax.axvline(0, 0, 1e10, linestyle='dotted', c='grey') - ax.fill_between( - x=[-5, 5], - y1=[self.cz_freq_interaction()-self.cz_J2()]*2, - y2=[self.cz_freq_interaction()+self.cz_J2()]*2, - label='$J_{\mathrm{2}}/2\pi$:'+' {:.3f} MHz'.format( - self.cz_J2()*1e-6), - color='C1', alpha=0.25) + # ax.fill_between( + # x=[-5, 5], + # y1=[self.cz_freq_interaction()-self.cz_J2()]*2, + # y2=[self.cz_freq_interaction()+self.cz_J2()]*2, + # label='$J_{\mathrm{2}}/2\pi$:'+' {:.3f} MHz'.format( + # self.q_J2()*1e-6), + # color='C1', alpha=0.25) title = ('Calibration visualization\n{}\nchannel {}'.format( self.AWG(), self.cfg_awg_channel())) diff --git a/pycqed/tests/test_flux_lutman.py b/pycqed/tests/test_flux_lutman.py index 275c7b4430..7914440b43 100644 --- a/pycqed/tests/test_flux_lutman.py +++ b/pycqed/tests/test_flux_lutman.py @@ -3,13 +3,12 @@ import pycqed.instrument_drivers.virtual_instruments.virtual_AWG8 as v8 from pycqed.instrument_drivers.meta_instrument import lfilt_kernel_object as lko -from pycqed.instrument_drivers.meta_instrument.LutMans import mw_lutman as mwl -from pycqed.measurement.waveform_control_CC import waveform as wf from pycqed.instrument_drivers.meta_instrument.LutMans.base_lutman import \ get_redundant_codewords from pycqed.instrument_drivers.meta_instrument.LutMans import flux_lutman as flm + class Test_Flux_LutMan(unittest.TestCase): @classmethod @@ -58,7 +57,6 @@ def setUpClass(self): self.fluxlutman_partner.set_default_lutmap() self.fluxlutman_partner.instr_partner_lutman('fluxlutman_main') - def test__program_hash_differs_AWG8_flux_lutman(self): # set to a random value to ensure different @@ -112,7 +110,6 @@ def test_partner_lutman_loading(self): # self.fluxlutman.cfg_operating_mode('Codeword_normal') # self.fluxlutman.load_waveforms_onto_AWG_lookuptable() - def test_plot_flux_arc(self): self.fluxlutman.plot_flux_arc(show=False, plot_cz_trajectory=True) From 42825cc1c9501b7094bf7a26bdf07948d2d79c3d Mon Sep 17 00:00:00 2001 From: Adriaan Date: Tue, 28 Aug 2018 19:32:16 +0200 Subject: [PATCH 11/26] basic plotting of levels in flux lutman --- .../meta_instrument/LutMans/flux_lutman.py | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/pycqed/instrument_drivers/meta_instrument/LutMans/flux_lutman.py b/pycqed/instrument_drivers/meta_instrument/LutMans/flux_lutman.py index 5cf0e0d239..c3208eb9ec 100644 --- a/pycqed/instrument_drivers/meta_instrument/LutMans/flux_lutman.py +++ b/pycqed/instrument_drivers/meta_instrument/LutMans/flux_lutman.py @@ -231,15 +231,17 @@ def get_polycoeffs_state(self, state: str): if state == '01': polycoeffs += self.q_polycoeffs_freq_01_det() polycoeffs[2] += self.q_freq_01() - elif state == '11': - polycoeffs += self.q_polycoeffs_freq_01_det() - polycoeffs[2] += self.q_freq_01() + self.q_freq_10() elif state == '02': polycoeffs += 2*self.q_polycoeffs_freq_01_det() polycoeffs += self.q_polycoeffs_anharm() polycoeffs[2] += 2*self.q_freq_01() + elif state == '10': + polycoeffs[2] += self.q_freq_10() + elif state == '11': + polycoeffs += self.q_polycoeffs_freq_01_det() + polycoeffs[2] += self.q_freq_01() + self.q_freq_10() else: - raise NotImplementedError() + raise NotImplementedError('State {} not recognized'.format(state)) return polycoeffs @@ -1236,8 +1238,15 @@ def plot_flux_arc(self, ax=None, show=True, amps = np.linspace(-2.5, 2.5, 101) # maximum voltage of AWG amp mode freqs = self.amp_to_frequency(amps, state='01') - ax.plot(amps, freqs, label='$f_{01}$') + freqs = self.amp_to_frequency(amps, state='02') + ax.plot(amps, freqs, label='$f_{02}$') + freqs = self.amp_to_frequency(amps, state='10') + ax.plot(amps, freqs, label='$f_{10}$') + freqs = self.amp_to_frequency(amps, state='11') + ax.plot(amps, freqs, label='$f_{11}$') + + # ax.axhline(self.cz_freq_interaction(), -5, 5, # label='$f_{\mathrm{int.}}$:'+' {:.3f} GHz'.format( # self.cz_freq_interaction()*1e-9), From b539134808f78b514fd68a82fa22e415333a8bb9 Mon Sep 17 00:00:00 2001 From: Adriaan Date: Wed, 29 Aug 2018 09:50:29 +0200 Subject: [PATCH 12/26] set up known failures before fixing tests in CZ flux arc --- .../meta_instrument/LutMans/flux_lutman.py | 11 ++++---- pycqed/tests/test_flux_lutman.py | 26 +++++++++---------- 2 files changed, 18 insertions(+), 19 deletions(-) diff --git a/pycqed/instrument_drivers/meta_instrument/LutMans/flux_lutman.py b/pycqed/instrument_drivers/meta_instrument/LutMans/flux_lutman.py index c3208eb9ec..1bc46e0be9 100644 --- a/pycqed/instrument_drivers/meta_instrument/LutMans/flux_lutman.py +++ b/pycqed/instrument_drivers/meta_instrument/LutMans/flux_lutman.py @@ -116,7 +116,7 @@ def _add_qubit_parameters(self): # initial value is chosen to not raise errors initial_value=6e9, unit='Hz', parameter_class=ManualParameter) - self.add_parameter('cz_J2', vals=vals.Numbers(), unit='Hz', + self.add_parameter('q_J2', vals=vals.Numbers(), unit='Hz', docstring='effective coupling between the 11 and ' '02 states.', # initial value is chosen to not raise errors @@ -212,7 +212,6 @@ def _set_cfg_operating_mode(self, val): def _get_cfg_operating_mode(self): return self._cfg_operating_mode - def get_polycoeffs_state(self, state: str): """ Args: @@ -244,7 +243,6 @@ def get_polycoeffs_state(self, state: str): raise NotImplementedError('State {} not recognized'.format(state)) return polycoeffs - def amp_to_frequency(self, amp: float, state: str='01'): """ Converts pulse amplitude in Volt to energy in Hz for a particular state @@ -495,8 +493,11 @@ def generate_standard_waveforms(self): self._wave_dict['i'] = self._gen_i() self._wave_dict['square'] = self._gen_square() self._wave_dict['park'] = self._gen_park() - self._wave_dict['cz'] = self._gen_cz() - self._wave_dict['cz_z'] = self._gen_cz_z(regenerate_cz=False) + + # FIXME: reenable this + self._wave_dict['cz'] = np.zeros(10) # self._gen_cz() + self._wave_dict['cz_z'] = np.zeros(10) # self._gen_cz_z(regenerate_cz=False) + self._wave_dict['idle_z'] = self._gen_idle_z() self._wave_dict['custom_wf'] = self._gen_custom_wf() self._wave_dict['multi_square'] = self._gen_multi_square( diff --git a/pycqed/tests/test_flux_lutman.py b/pycqed/tests/test_flux_lutman.py index 7914440b43..ff51850b83 100644 --- a/pycqed/tests/test_flux_lutman.py +++ b/pycqed/tests/test_flux_lutman.py @@ -23,17 +23,20 @@ def setUpClass(self): self.fluxlutman.AWG(self.AWG.name) self.fluxlutman.sampling_rate(2.4e9) self.fluxlutman.cz_theta_f(80) - self.fluxlutman.cz_freq_01_max(6.8e9) - self.fluxlutman.cz_J2(4.1e6) + + self.fluxlutman.q_freq_01(6.8e9) + self.fluxlutman.q_freq_10(5.0e9) + self.fluxlutman.q_J2(41e6) self.fluxlutman.cfg_awg_channel(1) + self.k0.cfg_awg_channel(self.fluxlutman.cfg_awg_channel()) - # self.fluxlutman.cz_E_c(250e6) - self.fluxlutman.cz_freq_interaction(5.1e9) self.fluxlutman.cfg_max_wf_length(5e-6) poly_coeffs = np.array([1.95027142e+09, -3.22560292e+08, 5.25834946e+07]) - self.fluxlutman.polycoeffs_freq_conv(poly_coeffs) + self.fluxlutman.q_polycoeffs_freq_01_det(poly_coeffs) + self.fluxlutman.q_polycoeffs_anharm(np.array([0, 0, -300e6])) + self.fluxlutman.set_default_lutmap() self.fluxlutman.instr_partner_lutman('fluxlutman_partner') @@ -43,17 +46,8 @@ def setUpClass(self): self.fluxlutman_partner.instr_distortion_kernel(self.k0.name) self.fluxlutman_partner.AWG(self.AWG.name) self.fluxlutman_partner.sampling_rate(2.4e9) - self.fluxlutman_partner.cz_theta_f(80) - self.fluxlutman_partner.cz_freq_01_max(6.8e9) - self.fluxlutman_partner.cz_J2(4.1e6) self.fluxlutman_partner.cfg_awg_channel(2) - - self.fluxlutman_partner.cz_freq_interaction(5.1e9) self.fluxlutman_partner.cfg_max_wf_length(5e-6) - - poly_coeffs = np.array([1.95027142e+09, -3.22560292e+08, - 5.25834946e+07]) - self.fluxlutman_partner.polycoeffs_freq_conv(poly_coeffs) self.fluxlutman_partner.set_default_lutmap() self.fluxlutman_partner.instr_partner_lutman('fluxlutman_main') @@ -110,9 +104,11 @@ def test_partner_lutman_loading(self): # self.fluxlutman.cfg_operating_mode('Codeword_normal') # self.fluxlutman.load_waveforms_onto_AWG_lookuptable() + @unittest.expectedFailure def test_plot_flux_arc(self): self.fluxlutman.plot_flux_arc(show=False, plot_cz_trajectory=True) + @unittest.expectedFailure def test_plot_cz_trajectory(self): self.fluxlutman.plot_cz_trajectory(show=False) @@ -120,6 +116,7 @@ def test_standard_cz_waveform(self): self.fluxlutman.czd_double_sided(False) self.fluxlutman.generate_standard_waveforms() + @unittest.expectedFailure def test_double_sided_cz_waveform(self): """ This test mostly tests if the parameters have some effect. @@ -188,6 +185,7 @@ def test_double_sided_cz_waveform(self): np.testing.assert_raises(AssertionError, np.testing.assert_array_equal, czA, czC) + @unittest.expectedFailure def test_freq_amp_conversions(self): # Test the basic inversion From 3c4855f2fd3d5bb7fd199056722548c4336470b5 Mon Sep 17 00:00:00 2001 From: Adriaan Date: Wed, 29 Aug 2018 10:14:58 +0200 Subject: [PATCH 13/26] added tests and fixed bugs for frequency conversion --- .../meta_instrument/LutMans/flux_lutman.py | 19 ++++---- pycqed/tests/test_flux_lutman.py | 48 +++++++++++++++++++ 2 files changed, 59 insertions(+), 8 deletions(-) diff --git a/pycqed/instrument_drivers/meta_instrument/LutMans/flux_lutman.py b/pycqed/instrument_drivers/meta_instrument/LutMans/flux_lutman.py index 1bc46e0be9..5f3740e7c1 100644 --- a/pycqed/instrument_drivers/meta_instrument/LutMans/flux_lutman.py +++ b/pycqed/instrument_drivers/meta_instrument/LutMans/flux_lutman.py @@ -82,16 +82,19 @@ def _add_qubit_parameters(self): """ self.add_parameter( 'q_polycoeffs_freq_01_det', - docstring='coefficients of the polynomial used to convert ' - 'amplitude in V to detuning in Hz. N.B. it is important to ' + docstring='Coefficients of the polynomial used to convert ' + 'amplitude in V to detuning in Hz. \nN.B. it is important to ' 'include both the AWG range and channel amplitude in the params.\n' + 'N.B.2 Sign convention: positive detuning means frequency is ' + 'higher than current frequency, negative detuning means its ' + 'smaller.\n' 'In order to convert a set of cryoscope flux arc coefficients to ' ' units of Volts they can be rescaled using [c0*sc**2, c1*sc, c2]' ' where sc is the desired scaling factor that includes the sq_amp ' 'used and the range of the AWG (5 in amp mode).', vals=vals.Arrays(), # initial value is chosen to not raise errors - initial_value=np.array([2e9, 0, 0]), + initial_value=np.array([-2e9, 0, 0]), parameter_class=ManualParameter) self.add_parameter( 'q_polycoeffs_anharm', @@ -264,7 +267,7 @@ def amp_to_frequency(self, amp: float, state: str='01'): return np.polyval(polycoeffs, amp) - def frequency_to_amp(self, freq: float, state:str='01', + def frequency_to_amp(self, freq: float, state: str='01', positive_branch=True): """ Converts amplitude to detuning in Hz. @@ -281,8 +284,8 @@ def frequency_to_amp(self, freq: float, state:str='01', """ # recursive allows dealing with an array of freqs if isinstance(freq, (list, np.ndarray)): - return np.array([self.detuning_to_amp( - f, positive_branch=positive_branch) for f in freq]) + return np.array([self.frequency_to_amp( + f, state=state, positive_branch=positive_branch) for f in freq]) polycoeffs = self.get_polycoeffs_state(state=state) p = np.poly1d(polycoeffs) sols = (p-freq).roots @@ -495,8 +498,8 @@ def generate_standard_waveforms(self): self._wave_dict['park'] = self._gen_park() # FIXME: reenable this - self._wave_dict['cz'] = np.zeros(10) # self._gen_cz() - self._wave_dict['cz_z'] = np.zeros(10) # self._gen_cz_z(regenerate_cz=False) + self._wave_dict['cz'] = np.zeros(10) # self._gen_cz() + self._wave_dict['cz_z'] = np.zeros(10) # self._gen_cz_z(regenerate_cz=False) self._wave_dict['idle_z'] = self._gen_idle_z() self._wave_dict['custom_wf'] = self._gen_custom_wf() diff --git a/pycqed/tests/test_flux_lutman.py b/pycqed/tests/test_flux_lutman.py index ff51850b83..c8078aff07 100644 --- a/pycqed/tests/test_flux_lutman.py +++ b/pycqed/tests/test_flux_lutman.py @@ -185,6 +185,54 @@ def test_double_sided_cz_waveform(self): np.testing.assert_raises(AssertionError, np.testing.assert_array_equal, czA, czC) + def test_transition_freq_calc_01(self): + """ + Tests methods used to determine energy levels and their conversion + to amplitude + """ + freq_01 = self.fluxlutman.amp_to_frequency(amp=0, state='01') + freq_01_expected = self.fluxlutman.q_freq_01() + \ + self.fluxlutman.q_polycoeffs_freq_01_det()[2] + self.assertEqual(freq_01, freq_01_expected) + + def test_transition_freq_calc_02(self): + freq_02 = self.fluxlutman.amp_to_frequency(amp=0, state='02') + freq_02_expected = \ + 2*(self.fluxlutman.q_freq_01() + + self.fluxlutman.q_polycoeffs_freq_01_det()[2]) + \ + self.fluxlutman.q_polycoeffs_anharm()[2] + self.assertEqual(freq_02, freq_02_expected) + + def test_transition_freq_calc_10(self): + freq_10 = self.fluxlutman.amp_to_frequency(amp=0, state='10') + freq_10_expected = self.fluxlutman.q_freq_10() + + self.assertEqual(freq_10, freq_10_expected) + + def test_transition_freq_calc_11(self): + freq_11 = self.fluxlutman.amp_to_frequency(amp=0, state='11') + freq_11_expected = \ + (self.fluxlutman.q_freq_01() + + self.fluxlutman.q_polycoeffs_freq_01_det()[2]) + \ + self.fluxlutman.q_freq_10() + + self.assertEqual(freq_11, freq_11_expected) + + def test_transition_freq_inversion(self): + state = '02' + amps = np.linspace(.3, 1, 11) + freqs_02 = self.fluxlutman.amp_to_frequency(amp=amps, state=state) + amps_inv = self.fluxlutman.frequency_to_amp(freqs_02, state=state, + positive_branch=True) + np.testing.assert_array_almost_equal(amps, amps_inv) + + amps = np.linspace(-.3, -1, 11) + freqs_02 = self.fluxlutman.amp_to_frequency(amp=amps, state=state) + amps_inv = self.fluxlutman.frequency_to_amp(freqs_02, state=state, + positive_branch=False) + np.testing.assert_array_almost_equal(amps, amps_inv) + + @unittest.expectedFailure def test_freq_amp_conversions(self): From c08f5897f8724538c8acccacadd946ebdaa101f7 Mon Sep 17 00:00:00 2001 From: Adriaan Date: Wed, 29 Aug 2018 10:29:40 +0200 Subject: [PATCH 14/26] added method to calculate detuning between levels --- .../meta_instrument/LutMans/flux_lutman.py | 58 ++++++++++++------- pycqed/tests/test_flux_lutman.py | 40 ++++++++----- 2 files changed, 64 insertions(+), 34 deletions(-) diff --git a/pycqed/instrument_drivers/meta_instrument/LutMans/flux_lutman.py b/pycqed/instrument_drivers/meta_instrument/LutMans/flux_lutman.py index 5f3740e7c1..b92dd2ec25 100644 --- a/pycqed/instrument_drivers/meta_instrument/LutMans/flux_lutman.py +++ b/pycqed/instrument_drivers/meta_instrument/LutMans/flux_lutman.py @@ -128,20 +128,6 @@ def _add_qubit_parameters(self): def _add_cfg_parameters(self): - # self.add_parameter( - # 'polycoeffs_freq_conv', - # docstring='coefficients of the polynomial used to convert ' - # 'amplitude in V to detuning in Hz. N.B. it is important to ' - # 'include both the AWG range and channel amplitude in the params.\n' - # 'In order to convert a set of cryoscope flux arc coefficients to ' - # ' units of Volts they can be rescaled using [c0*sc**2, c1*sc, c2]' - # ' where sc is the desired scaling factor that includes the sq_amp ' - # 'used and the range of the AWG (5 in amp mode).', - # vals=vals.Arrays(), - # # initial value is chosen to not raise errors - # initial_value=np.array([2e9, 0, 0]), - # parameter_class=ManualParameter) - self.add_parameter('cfg_awg_channel', initial_value=1, vals=vals.Ints(1, 8), @@ -246,7 +232,8 @@ def get_polycoeffs_state(self, state: str): raise NotImplementedError('State {} not recognized'.format(state)) return polycoeffs - def amp_to_frequency(self, amp: float, state: str='01'): + + def calc_amp_to_freq(self, amp: float, state: str='01'): """ Converts pulse amplitude in Volt to energy in Hz for a particular state Args: @@ -267,7 +254,7 @@ def amp_to_frequency(self, amp: float, state: str='01'): return np.polyval(polycoeffs, amp) - def frequency_to_amp(self, freq: float, state: str='01', + def calc_freq_to_amp(self, freq: float, state: str='01', positive_branch=True): """ Converts amplitude to detuning in Hz. @@ -284,7 +271,7 @@ def frequency_to_amp(self, freq: float, state: str='01', """ # recursive allows dealing with an array of freqs if isinstance(freq, (list, np.ndarray)): - return np.array([self.frequency_to_amp( + return np.array([self.calc_freq_to_amp( f, state=state, positive_branch=positive_branch) for f in freq]) polycoeffs = self.get_polycoeffs_state(state=state) p = np.poly1d(polycoeffs) @@ -299,6 +286,35 @@ def frequency_to_amp(self, freq: float, state: str='01', # imaginary part is ignored, instead sticking to closest real value return np.real(sol) + def calc_amp_to_eps(self, amp: float, + state_A: str='01', state_B: str='02'): + """ + Calculates detuning between two levels as a function of pulse + amplitude in Volt. + + f(V) = f_B (V) - f_A (V) + + Args: + amp (float) : amplitude in Volt + state_A (str) : string of 2 numbers denoting the state. The numbers + correspond to the number of excitations in each qubits. + The LSQ (right) corresponds to the qubit being fluxed and + under control of this flux lutman. + state_B (str) : + + N.B. this method assumes that the polycoeffs are with respect to the + amplitude in units of V, including rescaling due to the channel + amplitude and range settings of the AWG8. + See also `self.get_dac_val_to_amp_scalefactor`. + + amp_Volts = amp_dac_val * channel_amp * channel_range + """ + polycoeffs_A = self.get_polycoeffs_state(state=state_A) + polycoeffs_B = self.get_polycoeffs_state(state=state_B) + polycoeffs = polycoeffs_B - polycoeffs_A + return np.polyval(polycoeffs, amp) + + def get_dac_val_to_amp_scalefactor(self): """ Returns the scale factor to transform an amplitude in 'dac value' to an @@ -1241,13 +1257,13 @@ def plot_flux_arc(self, ax=None, show=True, f, ax = plt.subplots() amps = np.linspace(-2.5, 2.5, 101) # maximum voltage of AWG amp mode - freqs = self.amp_to_frequency(amps, state='01') + freqs = self.calc_amp_to_freq(amps, state='01') ax.plot(amps, freqs, label='$f_{01}$') - freqs = self.amp_to_frequency(amps, state='02') + freqs = self.calc_amp_to_freq(amps, state='02') ax.plot(amps, freqs, label='$f_{02}$') - freqs = self.amp_to_frequency(amps, state='10') + freqs = self.calc_amp_to_freq(amps, state='10') ax.plot(amps, freqs, label='$f_{10}$') - freqs = self.amp_to_frequency(amps, state='11') + freqs = self.calc_amp_to_freq(amps, state='11') ax.plot(amps, freqs, label='$f_{11}$') diff --git a/pycqed/tests/test_flux_lutman.py b/pycqed/tests/test_flux_lutman.py index c8078aff07..44aeb18c16 100644 --- a/pycqed/tests/test_flux_lutman.py +++ b/pycqed/tests/test_flux_lutman.py @@ -185,32 +185,32 @@ def test_double_sided_cz_waveform(self): np.testing.assert_raises(AssertionError, np.testing.assert_array_equal, czA, czC) - def test_transition_freq_calc_01(self): + def test_calc_amp_to_freq_01(self): """ Tests methods used to determine energy levels and their conversion to amplitude """ - freq_01 = self.fluxlutman.amp_to_frequency(amp=0, state='01') + freq_01 = self.fluxlutman.calc_amp_to_freq(amp=0, state='01') freq_01_expected = self.fluxlutman.q_freq_01() + \ self.fluxlutman.q_polycoeffs_freq_01_det()[2] self.assertEqual(freq_01, freq_01_expected) - def test_transition_freq_calc_02(self): - freq_02 = self.fluxlutman.amp_to_frequency(amp=0, state='02') + def test_calc_amp_to_freq_02(self): + freq_02 = self.fluxlutman.calc_amp_to_freq(amp=0, state='02') freq_02_expected = \ 2*(self.fluxlutman.q_freq_01() + self.fluxlutman.q_polycoeffs_freq_01_det()[2]) + \ self.fluxlutman.q_polycoeffs_anharm()[2] self.assertEqual(freq_02, freq_02_expected) - def test_transition_freq_calc_10(self): - freq_10 = self.fluxlutman.amp_to_frequency(amp=0, state='10') + def test_calc_amp_to_freq_10(self): + freq_10 = self.fluxlutman.calc_amp_to_freq(amp=0, state='10') freq_10_expected = self.fluxlutman.q_freq_10() self.assertEqual(freq_10, freq_10_expected) - def test_transition_freq_calc_11(self): - freq_11 = self.fluxlutman.amp_to_frequency(amp=0, state='11') + def test_calc_amp_to_freq_11(self): + freq_11 = self.fluxlutman.calc_amp_to_freq(amp=0, state='11') freq_11_expected = \ (self.fluxlutman.q_freq_01() + self.fluxlutman.q_polycoeffs_freq_01_det()[2]) + \ @@ -218,20 +218,34 @@ def test_transition_freq_calc_11(self): self.assertEqual(freq_11, freq_11_expected) - def test_transition_freq_inversion(self): + def test_calc_transition_freq_inversion(self): state = '02' amps = np.linspace(.3, 1, 11) - freqs_02 = self.fluxlutman.amp_to_frequency(amp=amps, state=state) - amps_inv = self.fluxlutman.frequency_to_amp(freqs_02, state=state, + freqs_02 = self.fluxlutman.calc_amp_to_freq(amp=amps, state=state) + amps_inv = self.fluxlutman.calc_freq_to_amp(freqs_02, state=state, positive_branch=True) np.testing.assert_array_almost_equal(amps, amps_inv) amps = np.linspace(-.3, -1, 11) - freqs_02 = self.fluxlutman.amp_to_frequency(amp=amps, state=state) - amps_inv = self.fluxlutman.frequency_to_amp(freqs_02, state=state, + freqs_02 = self.fluxlutman.calc_amp_to_freq(amp=amps, state=state) + amps_inv = self.fluxlutman.calc_freq_to_amp(freqs_02, state=state, positive_branch=False) np.testing.assert_array_almost_equal(amps, amps_inv) + def test_calc_amp_to_eps(self): + state_A = '02' + state_B = '11' + amps = np.linspace(-1, 1, 11) + eps = self.fluxlutman.calc_amp_to_eps(amp=amps, state_A=state_A, + state_B=state_B) + + freqs_02 = self.fluxlutman.calc_amp_to_freq(amp=amps, state=state_A) + freqs_11 = self.fluxlutman.calc_amp_to_freq(amp=amps, state=state_B) + expected_eps = freqs_11 - freqs_02 + np.testing.assert_array_almost_equal(eps, expected_eps) + + + @unittest.expectedFailure def test_freq_amp_conversions(self): From 9ba0dd6843d57d49f656e44d8bb1d18b6180e961 Mon Sep 17 00:00:00 2001 From: Adriaan Date: Wed, 29 Aug 2018 11:06:54 +0200 Subject: [PATCH 15/26] added tests for calculating detuning --- .../meta_instrument/LutMans/flux_lutman.py | 67 +++++++++++++------ pycqed/tests/test_flux_lutman.py | 19 ++++++ 2 files changed, 65 insertions(+), 21 deletions(-) diff --git a/pycqed/instrument_drivers/meta_instrument/LutMans/flux_lutman.py b/pycqed/instrument_drivers/meta_instrument/LutMans/flux_lutman.py index b92dd2ec25..239237d508 100644 --- a/pycqed/instrument_drivers/meta_instrument/LutMans/flux_lutman.py +++ b/pycqed/instrument_drivers/meta_instrument/LutMans/flux_lutman.py @@ -216,7 +216,9 @@ def get_polycoeffs_state(self, state: str): """ polycoeffs = np.zeros(3) - if state == '01': + if state == '00': + pass + elif state == '01': polycoeffs += self.q_polycoeffs_freq_01_det() polycoeffs[2] += self.q_freq_01() elif state == '02': @@ -257,10 +259,8 @@ def calc_amp_to_freq(self, amp: float, state: str='01'): def calc_freq_to_amp(self, freq: float, state: str='01', positive_branch=True): """ - Converts amplitude to detuning in Hz. - - Requires "polycoeffs_freq_conv" to be set to the polynomial values - extracted from the cryoscope flux arc. + Calculates amplitude in Volt corresponding to the energy of a state + in Hz. N.B. this method assumes that the polycoeffs are with respect to the amplitude in units of V, including rescaling due to the channel @@ -269,22 +269,9 @@ def calc_freq_to_amp(self, freq: float, state: str='01', amp_Volts = amp_dac_val * channel_amp * channel_range """ - # recursive allows dealing with an array of freqs - if isinstance(freq, (list, np.ndarray)): - return np.array([self.calc_freq_to_amp( - f, state=state, positive_branch=positive_branch) for f in freq]) - polycoeffs = self.get_polycoeffs_state(state=state) - p = np.poly1d(polycoeffs) - sols = (p-freq).roots - - # sols returns 2 solutions (for a 2nd order polynomial) - if positive_branch: - sol = np.max(sols) - else: - sol = np.min(sols) - # imaginary part is ignored, instead sticking to closest real value - return np.real(sol) + return self.calc_eps_to_amp(eps=freq, state_B=state, state_A='00', + positive_branch=positive_branch) def calc_amp_to_eps(self, amp: float, state_A: str='01', state_B: str='02'): @@ -292,7 +279,7 @@ def calc_amp_to_eps(self, amp: float, Calculates detuning between two levels as a function of pulse amplitude in Volt. - f(V) = f_B (V) - f_A (V) + ε(V) = f_B (V) - f_A (V) Args: amp (float) : amplitude in Volt @@ -314,6 +301,44 @@ def calc_amp_to_eps(self, amp: float, polycoeffs = polycoeffs_B - polycoeffs_A return np.polyval(polycoeffs, amp) + def calc_eps_to_amp(self, eps, + state_A: str='01', state_B: str='02', + positive_branch=True): + """ + Calculates amplitude in Volt corresponding to an energy difference + between two states in Hz. + V(ε) = V(f_b - f_a) + + N.B. this method assumes that the polycoeffs are with respect to the + amplitude in units of V, including rescaling due to the channel + amplitude and range settings of the AWG8. + See also `self.get_dac_val_to_amp_scalefactor`. + + amp_Volts = amp_dac_val * channel_amp * channel_range + """ + # recursive allows dealing with an array of freqs + if isinstance(eps, (list, np.ndarray)): + return np.array([self.calc_eps_to_amp( + eps=e, state_A=state_A, state_B=state_B, + positive_branch=positive_branch) for e in eps]) + + polycoeffs_A = self.get_polycoeffs_state(state=state_A) + polycoeffs_B = self.get_polycoeffs_state(state=state_B) + polycoeffs = polycoeffs_B - polycoeffs_A + + p = np.poly1d(polycoeffs) + sols = (p-eps).roots + + # sols returns 2 solutions (for a 2nd order polynomial) + if positive_branch: + sol = np.max(sols) + else: + sol = np.min(sols) + + # imaginary part is ignored, instead sticking to closest real value + return np.real(sol) + + def get_dac_val_to_amp_scalefactor(self): """ diff --git a/pycqed/tests/test_flux_lutman.py b/pycqed/tests/test_flux_lutman.py index 44aeb18c16..153ea2af69 100644 --- a/pycqed/tests/test_flux_lutman.py +++ b/pycqed/tests/test_flux_lutman.py @@ -245,6 +245,25 @@ def test_calc_amp_to_eps(self): np.testing.assert_array_almost_equal(eps, expected_eps) + def test_calc_detuning_freq_inversion(self): + state_A = '02' + state_B = '11' + + amps = np.linspace(.3, 1, 11) + freqs_02 = self.fluxlutman.calc_amp_to_eps( + amp=amps, state_A=state_A, state_B=state_B) + amps_inv = self.fluxlutman.calc_eps_to_amp( + freqs_02, state_A=state_A, state_B=state_B, positive_branch=True) + np.testing.assert_array_almost_equal(amps, amps_inv) + + amps = np.linspace(-.3, -1, 11) + freqs_02 = self.fluxlutman.calc_amp_to_eps( + amp=amps, state_A=state_A, state_B=state_B) + amps_inv = self.fluxlutman.calc_eps_to_amp( + freqs_02, state_A=state_A, state_B=state_B, positive_branch=False) + np.testing.assert_array_almost_equal(amps, amps_inv) + + @unittest.expectedFailure From 17a224489e311453ccec0a5957a220a2579ee674 Mon Sep 17 00:00:00 2001 From: Adriaan Date: Wed, 29 Aug 2018 11:18:17 +0200 Subject: [PATCH 16/26] deleted very old qumis based qwg lutman and tests --- .../meta_instrument/QWG_LookuptableManager.py | 649 ------------------ pycqed/tests/test_qasm_QWG_flux_seqs.py | 200 ------ 2 files changed, 849 deletions(-) delete mode 100644 pycqed/instrument_drivers/meta_instrument/QWG_LookuptableManager.py delete mode 100644 pycqed/tests/test_qasm_QWG_flux_seqs.py diff --git a/pycqed/instrument_drivers/meta_instrument/QWG_LookuptableManager.py b/pycqed/instrument_drivers/meta_instrument/QWG_LookuptableManager.py deleted file mode 100644 index ecfa7caa58..0000000000 --- a/pycqed/instrument_drivers/meta_instrument/QWG_LookuptableManager.py +++ /dev/null @@ -1,649 +0,0 @@ -from qcodes.instrument.base import Instrument -from qcodes.instrument.parameter import ManualParameter -from qcodes.utils import validators as vals -import logging -from pycqed.measurement.waveform_control_CC import waveform as wf -from pycqed.instrument_drivers.pq_parameters import InstrumentParameter -import numpy as np -from qcodes.plots.pyqtgraph import QtPlot - - -class QWG_LookuptableManager(Instrument): - - def __init__(self, name, QWG, **kw): - logging.warning('The QWG_LookuptableManager is deprecated.') - logging.info(__name__ + ' : Initializing instrument') - super().__init__(name, **kw) - self.add_parameter('QWG', parameter_class=InstrumentParameter) - - self.add_parameter('Q_amp180', - unit='V', - vals=vals.Numbers(-1, 1), - parameter_class=ManualParameter, - initial_value=0.1) - self.add_parameter('Q_amp90_scale', - vals=vals.Numbers(-1, 1), - parameter_class=ManualParameter, - initial_value=0.5) - self.add_parameter('Q_motzoi', vals=vals.Numbers(-2, 2), - parameter_class=ManualParameter, - initial_value=0.0) - self.add_parameter('Q_gauss_width', - vals=vals.Numbers(min_value=1e-9), unit='s', - parameter_class=ManualParameter, - initial_value=4e-9) - - self.add_parameter('spec_pulse_type', - vals=vals.Enum('block', 'gauss'), - parameter_class=ManualParameter, - initial_value='block') - self.add_parameter('spec_amp', - unit='V', - vals=vals.Numbers(0, 1), - parameter_class=ManualParameter, - initial_value=0.4) - self.add_parameter( - 'spec_length', vals=vals.Numbers(min_value=1e-9), unit='s', - parameter_class=ManualParameter, - docstring=('length of the block pulse if spec_pulse_type' + - 'is "block", gauss_width if spec_pulse_type is gauss.'), - initial_value=100e-9) - - def load_pulses_onto_AWG_lookuptable(self): - self.QWG.get_instr().stop() - - # Microwave pulses - G_amp = self.Q_amp180()/self.QWG.get_instr().get('ch{}_amp'.format(1)) - # Amplitude is set using the channel amplitude (at least for now) - G, D = wf.gauss_pulse(G_amp, self.Q_gauss_width(), - motzoi=self.Q_motzoi(), - sampling_rate=1e9) # sampling rate of QWG - self.QWG.get_instr().deleteWaveformAll() - self.QWG.get_instr().createWaveformReal('X180_q0_I', G) - self.QWG.get_instr().createWaveformReal('X180_q0_Q', D) - self.QWG.get_instr().createWaveformReal('X90_q0_I', - self.Q_amp90_scale()*G) - self.QWG.get_instr().createWaveformReal('X90_q0_Q', - self.Q_amp90_scale()*D) - - self.QWG.get_instr().createWaveformReal('Y180_q0_I', D) - self.QWG.get_instr().createWaveformReal('Y180_q0_Q', -G) - self.QWG.get_instr().createWaveformReal('Y90_q0_I', - self.Q_amp90_scale()*D) - self.QWG.get_instr().createWaveformReal('Y90_q0_Q', - -self.Q_amp90_scale()*G) - - self.QWG.get_instr().createWaveformReal('mX90_q0_I', - -self.Q_amp90_scale()*G) - self.QWG.get_instr().createWaveformReal('mX90_q0_Q', - -self.Q_amp90_scale()*D) - self.QWG.get_instr().createWaveformReal('mY90_q0_I', - -self.Q_amp90_scale()*D) - self.QWG.get_instr().createWaveformReal('mY90_q0_Q', - self.Q_amp90_scale()*G) - - # Spec pulse - if self.spec_pulse_type() == 'gauss': - spec_G, spec_Q = wf.gauss_pulse(self.spec_amp(), - self.spec_length(), - motzoi=0, sampling_rate=1e9) - elif self.spec_pulse_type() == 'block': - spec_G, spec_Q = wf.block_pulse(self.spec_amp(), - self.spec_length(), - sampling_rate=1e9) - self.QWG.get_instr().createWaveformReal('spec_q0_I', spec_G) - self.QWG.get_instr().createWaveformReal('spec_q0_Q', spec_Q) - - # Filler waveform - - self.QWG.get_instr().createWaveformReal('zero', [0]*4) - self.QWG.get_instr().codeword_0_ch1_waveform('X180_q0_I') - self.QWG.get_instr().codeword_0_ch2_waveform('X180_q0_Q') - self.QWG.get_instr().codeword_0_ch3_waveform('X180_q0_I') - self.QWG.get_instr().codeword_0_ch4_waveform('X180_q0_Q') - - self.QWG.get_instr().codeword_1_ch1_waveform('Y180_q0_I') - self.QWG.get_instr().codeword_1_ch2_waveform('Y180_q0_Q') - self.QWG.get_instr().codeword_1_ch3_waveform('Y180_q0_I') - self.QWG.get_instr().codeword_1_ch4_waveform('Y180_q0_Q') - - self.QWG.get_instr().codeword_2_ch1_waveform('X90_q0_I') - self.QWG.get_instr().codeword_2_ch2_waveform('X90_q0_Q') - self.QWG.get_instr().codeword_2_ch3_waveform('X90_q0_I') - self.QWG.get_instr().codeword_2_ch4_waveform('X90_q0_Q') - - self.QWG.get_instr().codeword_3_ch1_waveform('Y90_q0_I') - self.QWG.get_instr().codeword_3_ch2_waveform('Y90_q0_Q') - self.QWG.get_instr().codeword_3_ch3_waveform('Y90_q0_I') - self.QWG.get_instr().codeword_3_ch4_waveform('Y90_q0_Q') - - self.QWG.get_instr().codeword_4_ch1_waveform('mX90_q0_I') - self.QWG.get_instr().codeword_4_ch2_waveform('mX90_q0_Q') - self.QWG.get_instr().codeword_4_ch3_waveform('mX90_q0_I') - self.QWG.get_instr().codeword_4_ch4_waveform('mX90_q0_Q') - - self.QWG.get_instr().codeword_5_ch1_waveform('mY90_q0_I') - self.QWG.get_instr().codeword_5_ch2_waveform('mY90_q0_Q') - self.QWG.get_instr().codeword_5_ch3_waveform('mY90_q0_I') - self.QWG.get_instr().codeword_5_ch4_waveform('mY90_q0_Q') - - self.QWG.get_instr().codeword_6_ch1_waveform('spec_q0_I') - self.QWG.get_instr().codeword_6_ch2_waveform('spec_q0_Q') - self.QWG.get_instr().codeword_6_ch3_waveform('spec_q0_I') - self.QWG.get_instr().codeword_6_ch4_waveform('spec_q0_Q') - - self.QWG.get_instr().start() - self.QWG.get_instr().getOperationComplete() - - -class QWG_FluxLookuptableManager(Instrument): - - def __init__(self, name, **kw): - logging.info(__name__ + ' : Initializing instrument') - super().__init__(name, **kw) - self.add_parameter('QWG', parameter_class=InstrumentParameter) - self.add_parameter('F_kernel_instr', - parameter_class=InstrumentParameter) - - self.add_parameter('F_amp', unit='frac', - docstring=('Amplitude of flux pulse as fraction of ' - 'the peak amplitude. Beware of factor 2' - ' with Vpp in the QWG'), - initial_value=0.5, - parameter_class=ManualParameter) - self.add_parameter('F_length', unit='s', - parameter_class=ManualParameter, - initial_value=200e-9) - self.add_parameter('F_ch', label='Flux channel', - vals=vals.Ints(), - parameter_class=ManualParameter) - self.add_parameter('F_delay', label='Flux pulse delay', - unit='s', - initial_value=0, - vals=vals.Numbers(), - parameter_class=ManualParameter) - self.add_parameter('F_compensation_delay', - label='compens. pulse delay', - unit='s', - initial_value=4e-6, - vals=vals.Numbers(), - parameter_class=ManualParameter) - self.add_parameter('F_lambda_1', - docstring='first lambda coef. for martinis pulse', - label='Lambda_1', - unit='', - initial_value=0, - vals=vals.Numbers(), - parameter_class=ManualParameter) - self.add_parameter('F_lambda_2', - docstring='second lambda coef. for martinis pulse', - label='Lambda_2', - unit='', - initial_value=0, - vals=vals.Numbers(), - parameter_class=ManualParameter) - self.add_parameter('F_lambda_3', - docstring='third lambda coef. for martinis pulse', - label='Lambda_3', - unit='', - initial_value=0, - vals=vals.Numbers(), - parameter_class=ManualParameter) - self.add_parameter('F_theta_f', - docstring='theta_f for martinis pulse', - label='theta_f', - unit='deg', - initial_value=90, - vals=vals.Numbers(), - parameter_class=ManualParameter) - self.add_parameter('F_J2', - docstring='coupling between 11-02', - label='J2', - unit='Hz', - initial_value=10e6, - vals=vals.Numbers(), - parameter_class=ManualParameter) - self.add_parameter('F_f_interaction', - label='interaction frequency', - unit='Hz', - initial_value=5e9, - vals=vals.Numbers(), - parameter_class=ManualParameter) - self.add_parameter('F_dac_flux_coef', - docstring='conversion factor AWG voltage to flux', - label='dac flux coef', - unit='(V^-1)', - initial_value=1.0, - vals=vals.Numbers(), - parameter_class=ManualParameter) - self.add_parameter('F_E_c', - label='qubit E_c', - unit='Hz', - initial_value=250e6, - vals=vals.Numbers(), - parameter_class=ManualParameter) - self.add_parameter('F_f_01_max', - label='sweet spot freq', - unit='Hz', - initial_value=6e9, - vals=vals.Numbers(), - parameter_class=ManualParameter) - self.add_parameter('F_asymmetry', - label='qubit asymmetry', - unit='Hz', - initial_value=0.0, - vals=vals.Numbers(), - parameter_class=ManualParameter) - self.add_parameter('codeword_dict', - docstring='Dict assigning codewords to pulses', - label='codeword dict', - unit='', - initial_value={}, - vals=vals.Anything(), - parameter_class=ManualParameter) - self.add_parameter('sampling_rate', - docstring='Sampling rate of the QWG', - label='sampling rate', - unit='Hz', - initial_value=1.0e9, - vals=vals.Numbers(), - parameter_class=ManualParameter) - self.add_parameter('Z_length', - docstring=('Duration of single qubit Z pulse in' - ' seconds'), - label='Z length', - unit='s', - initial_value=10e-9, - vals=vals.Numbers(), - parameter_class=ManualParameter) - self.add_parameter('Z_amp', - docstring=('Amplitude of the single qubit phase ' - 'correction in CZ pulses.'), - label='Z amplitude', - unit='frac', - initial_value=0.0, - vals=vals.Numbers(), - parameter_class=ManualParameter) - self.add_parameter('Z_amp_grover', - docstring=('Amplitude of the single qubit phase ' - 'correction for the second CZ pulse ' - "used in Grover's algorithm."), - label='Z amplitude 2', - unit='frac', - initial_value=0.0, - vals=vals.Numbers(), - parameter_class=ManualParameter) - - self.add_parameter('max_waveform_length', unit='s', - parameter_class=ManualParameter, - initial_value=30e-6) - self.add_parameter('codeword_channels', - parameter_class=ManualParameter, - docstring='Channels used for triggering specific ' - 'codewords.', - vals=vals.Lists(vals.Ints())) - - self.add_parameter('disable_CZ', - parameter_class=ManualParameter, - vals=vals.Bool(), - initial_value=False) - self.add_parameter('pulse_map', - initial_value={'cz': 'adiabatic_Z', - 'cz_grover': 'adiabatic_Z_grover', - 'square': 'square'}, - parameter_class=ManualParameter, - vals=vals.Dict()) - self.add_parameter('wave_dict_unit', - get_cmd=self._get_wave_dict_unit, - set_cmd=self._set_wave_dict_unit, - docstring='Unit in which the waveforms are ' - 'specified.\n' - '"frac" means "fraction of the maximum QWG ' - 'range".\n' - '"V" means volts.', - vals=vals.Enum('frac', 'V')) - self._wave_dict_unit = 'frac' # Initial value for wave_dict_unit - self.add_parameter('V_offset', - unit='V', - label='V offset', - docstring='pulsed sweet spot offset', - parameter_class=ManualParameter, - initial_value=0, - vals=vals.Numbers()) - self.add_parameter('V_per_phi0', - unit='V', - label='V per phi_0', - docstring='pulsed voltage required for one phi0 ' - 'of flux', - parameter_class=ManualParameter, - initial_value=1, - vals=vals.Numbers()) - self.add_parameter('S_gauss_sigma', - unit='s', - label='gauss sigma', - docstring='Width (sigma) of Gaussian shaped flux ' - 'pulse.', - parameter_class=ManualParameter, - initial_value=40e-9, - vals=vals.Numbers()) - self.add_parameter('S_amp', - unit='frac', - label='S_amp', - docstring='Amplitude of scoping flux pulse.', - parameter_class=ManualParameter, - initial_value=0.5, - vals=vals.Numbers()) - - self._wave_dict = {} - - def _get_wave_dict_unit(self): - return self._wave_dict_unit - - def _set_wave_dict_unit(self, val): - self.F_amp.unit = val - self.S_amp.unit = val - self._wave_dict_unit = val - - def standard_waveforms(self): - ''' - Returns standard waveforms, without delays or distortions applied. - ''' - # Block pulses - # Pulse is not IQ modulated, so block_Q is not used. - block = wf.single_channel_block(self.F_amp(), self.F_length(), - sampling_rate=self.sampling_rate()) - - gaussI, gaussQ = wf.gauss_pulse(self.S_amp(), self.S_gauss_sigma(), - nr_sigma=4, - sampling_rate=self.sampling_rate(), - axis='x', phase=0, motzoi=0, delay=0, - subtract_offset='first') - - # New version of fast adiabatic pulse - martinis_pulse_v2 = wf.martinis_flux_pulse( - length=self.F_length(), - lambda_2=self.F_lambda_2(), - lambda_3=self.F_lambda_3(), - theta_f=self.F_theta_f(), - f_01_max=self.F_f_01_max(), - J2=self.F_J2(), - E_c=self.F_E_c(), - V_per_phi0=self.V_per_phi0(), - V_offset=self.V_offset(), - f_interaction=self.F_f_interaction(), - f_bus=None, - asymmetry=self.F_asymmetry(), - sampling_rate=self.sampling_rate(), - return_unit='V') - - # Flux pulse for single qubit phase correction - z_nr_samples = int(np.round(self.Z_length() * self.sampling_rate())) - single_qubit_phase_correction = np.ones(z_nr_samples) * self.Z_amp() - single_qubit_phase_correction_grover = \ - np.ones(z_nr_samples) * self.Z_amp_grover() - - if self.disable_CZ(): - martinis_pulse_v2 *= 0 - # Construct phase corrected pulses - martinis_phase_corrected = np.concatenate( - [martinis_pulse_v2, single_qubit_phase_correction]) - martinis_phase_corrected_grover = np.concatenate( - [martinis_pulse_v2, single_qubit_phase_correction_grover]) - - return {'square': block, - 'adiabatic': martinis_pulse_v2, - 'adiabatic_Z': martinis_phase_corrected, - 'adiabatic_Z_grover': martinis_phase_corrected_grover, - 'gauss': gaussI} - - def generate_standard_pulses(self): - ''' - Generates all flux pulses that are defined in the method - 'standard_waveforms'. - - The complete pulses with delays and distortions are stored in - self._wave_dict, which is also returned by this method. - ''' - self._wave_dict = {} - - waveforms = self.standard_waveforms() - - # Insert delays and compensation pulses, apply distortions - wait_samples = np.zeros(int(np.round(self.F_delay() * - self.sampling_rate()))) - wait_samples_2 = np.zeros(int(np.round(self.F_compensation_delay() * - self.sampling_rate()))) - - for key in waveforms.keys(): - delayed_wave = np.concatenate( - [wait_samples, np.array(waveforms[key]), wait_samples_2, - - -1 * np.array(waveforms[key])]) - if self.F_kernel_instr() is not None: - k = self.F_kernel_instr.get_instr() - self._wave_dict[key] = k.convolve_kernel( - [k.kernel(), delayed_wave], - length_samples=int(np.round(self.max_waveform_length() * - self.sampling_rate()))) - # the waveform is cut off after length_samples. - # this is particularly important considering hardware (memory) - # limits of the QWG. - else: - logging.warning('No distortion kernel specified,' - ' not distorting flux pulse') - self._wave_dict[key] = delayed_wave - - return self._wave_dict - - def generate_composite_flux_pulse(self, time_tuples: list, - end_time_ns: float): - """ - takes a list time_tuples (time_in_ns, pulse_name) and creates a - composite pulse using the parameters stored in the flux lutman. - - This pulse does not include distortions and compensation pulses as - these are added when the pulse is uploaded using - "load_custom_pulse_onto_AWG_lookuptable". - """ - - # only intended for QWG which has a sampling rate of 1GSps - if self.sampling_rate() != 1e9: - raise ValueError('this function only works for 1GSps ' - 'sampling rate') - end_sample = end_time_ns - - pulse_map = self.pulse_map() - base_waveforms = self.standard_waveforms() - - composite_waveform = np.zeros(end_sample) - - for time_ns, operation in time_tuples: - waveform = base_waveforms[pulse_map[operation.lower()]] - composite_waveform[time_ns:time_ns+len(waveform)] += waveform - - return composite_waveform - - def regenerate_pulse(self, pulse_name): - ''' - Regenerates a single pulse. The pulse is updated in self._wave_dict - and also returned by this method. - - Args: - pulse_name (str): name of the pulse to regenerate - ''' - if not self._wave_dict: - # Pulses have never been generated since instatiation. - self.generate_standard_pulses() - - if pulse_name not in self._wave_dict.keys(): - raise KeyError( - 'Pulse {} not in wave dictionary.'.format(pulse_name)) - - # Get the plain waveform - waveform = self.standard_waveforms()[pulse_name] - - # Insert delays and compensation pulses, apply distortions - wait_samples = np.zeros(int(self.F_delay()*self.sampling_rate())) - wait_samples_comp_pulses = np.zeros(int(self.F_compensation_delay() - * self.sampling_rate())) - k = self.F_kernel_instr.get_instr() - - delayed_wave = np.concatenate([wait_samples, np.array(waveform), - wait_samples_comp_pulses, - -1*np.array(waveform)]) - distorted_wave = k.convolve_kernel( - [k.kernel(), delayed_wave], - length_samples=int(np.round(self.max_waveform_length() - * self.sampling_rate()))) - - self._wave_dict[pulse_name] = distorted_wave - return distorted_wave - - def load_pulse_onto_AWG_lookuptable(self, pulse_name, - regenerate_pulse=True): - ''' - Load a specific pulse onto the lookuptable. - - Args: - pulse_name (str): Name of the pulse to be loaded - regenerate_pulses (bool): should the pulses be generated again - ''' - if regenerate_pulse: - self.regenerate_pulse(pulse_name) - - if pulse_name not in self.codeword_dict().keys(): - raise KeyError( - 'Pulse {} not in codeword mapping.'.format(pulse_name)) - - if self.wave_dict_unit() == 'V': - # Rescale wave by Vpp/2 of the QWG, such that the output wave - # amplitude is actually as specified in the wave dictionary. - V_out = self.QWG.get_instr().get('ch{}_amp' - .format(self.F_ch())) / 2 - outWave = self._wave_dict[pulse_name] / V_out - if np.any(np.abs(outWave) > 1): - raise RuntimeError('Waveform values out of range [-1, 1]. ' - 'Try increasing QWG.ch{}_amp.' - .format(self.F_ch())) - - elif self.wave_dict_unit() == 'frac': - # Do not rescale; wave dictionary is in units of - # "fraction of Vpp/2" - outWave = self._wave_dict[pulse_name] - - self.QWG.get_instr().createWaveformReal( - pulse_name+self.name, outWave) - self.QWG.get_instr().set('codeword_{}_ch{}_waveform'.format( - self.codeword_dict()[pulse_name], self.F_ch()), - pulse_name+self.name) - - def load_pulses_onto_AWG_lookuptable(self, regenerate_pulses=True): - ''' - Load all standard pulses to the QWG lookuptable. - - Args: - regenerate_pulses (bool): should the pulses be generated again - ''' - self.QWG.get_instr().stop() - if regenerate_pulses: - self.generate_standard_pulses() - - for pulse_name in self._wave_dict: - self.load_pulse_onto_AWG_lookuptable( - pulse_name, regenerate_pulse=False) - self.QWG.get_instr().start() - self.QWG.get_instr().getOperationComplete() - - def load_custom_pulse_onto_AWG_lookuptable(self, waveform, - append_compensation: bool=True, - distort: bool=True, - pulse_name='custom', - codeword=0, - disable_start_stop: bool=False): - ''' - Load a user-defined waveform onto the QWG. The pulse is delayed by - self.F_delay() and a compensation pulse is added after - self.F_compensation_delay() if append_compensation is True. - - Args: - waveform (array): - Samples of the pulse that will be loaded onto QWG. - append_compensation (bool): - Should the compensation pulse be added. - pulse_name (string): - The name of the pulse. This is also used in the QWG. - codeword (int): - QWG codeword used for the pulse. - disable_start_stop (bool): - Disables the starting and stopping of the QWG. This can - be used to speed up loading many custom pulses onto the - QWG. - ''' - if not disable_start_stop: - self.QWG.get_instr().stop() - - # Insert delays and compensation pulses, apply distortions - wait_samples = np.zeros(int(self.F_delay()*self.sampling_rate())) - wait_samples_comp_pulses = np.zeros(int(self.F_compensation_delay() - * self.sampling_rate())) - - delayed_wave = np.concatenate([wait_samples, np.array(waveform)]) - if append_compensation: - delayed_wave = np.concatenate( - [delayed_wave, - wait_samples_comp_pulses, -1 * np.array(waveform)]) - - if self.F_kernel_instr() is not None: - k = self.F_kernel_instr.get_instr() - distorted_wave = k.convolve_kernel( - [k.kernel(), delayed_wave], - length_samples=int(self.max_waveform_length() * - self.sampling_rate())) - else: - logging.warning('No distortion kernel specified, not distorting ' - 'flux pulse.') - distorted_wave = delayed_wave - - # Scale to Vpp if necessary - if self.wave_dict_unit() == 'V': - # Rescale wave by Vpp/2 of the QWG, such that the output wave - # amplitude is actually as specified in the wave dictionary. - V_out = self.QWG.get_instr().get('ch{}_amp' - .format(self.F_ch())) / 2 - outWave = distorted_wave / V_out - if np.any(np.abs(outWave) > 1): - raise RuntimeError('Waveform values out of range [-1, 1]. ' - 'Try increasing QWG.ch{}_amp.' - .format(self.F_ch())) - elif self.wave_dict_unit() == 'frac': - # Do not rescale; wave dictionary is in units of - # "fraction of Vpp/2" - outWave = distorted_wave - - # Upload pulse - self.QWG.get_instr().createWaveformReal(pulse_name, outWave) - self.QWG.get_instr().set('codeword_{}_ch{}_waveform'.format( - codeword, self.F_ch()), pulse_name) - - if not disable_start_stop: - self.QWG.get_instr().start() - self.QWG.get_instr().getOperationComplete() - - def render_wave(self, wave_name, show=True, QtPlot_win=None): - ''' - Plots the specified wave. - - Args: - ''' - x = (np.arange(len(self._wave_dict[wave_name])))*1e-9 - y = self._wave_dict[wave_name] - - if QtPlot_win is None: - QtPlot_win = QtPlot(window_title=wave_name, - figsize=(600, 400)) - QtPlot_win.add( - x=x, y=y, name=wave_name, - symbol='o', symbolSize=5, - xlabel='Time', xunit='s', ylabel='Amplitude', yunit='V') - - return QtPlot_win diff --git a/pycqed/tests/test_qasm_QWG_flux_seqs.py b/pycqed/tests/test_qasm_QWG_flux_seqs.py deleted file mode 100644 index a00ab85ed0..0000000000 --- a/pycqed/tests/test_qasm_QWG_flux_seqs.py +++ /dev/null @@ -1,200 +0,0 @@ -""" -This module contains tests for the QASM compiler by Xiang Fu -""" -import json -import unittest -import numpy as np -from pycqed.measurement.waveform_control_CC import waveform as wf -import pycqed as pq -from pycqed.utilities import general as gen -from pycqed.measurement import sweep_functions as swf -from pycqed.measurement.waveform_control_CC import qasm_compiler as qcx -from pycqed.instrument_drivers.physical_instruments._controlbox.Assembler \ - import Assembler -from os.path import join -from pycqed.measurement.waveform_control_CC import \ - QWG_fluxing_seqs as qwfs - -from pycqed.measurement.waveform_control_CC.qasm_compiler_helpers import \ - get_timepoints_from_label, get_timetuples_since_event - - -from pycqed.instrument_drivers.meta_instrument import QWG_LookuptableManager \ - as qlm - - -class Test_QWG_flux_seqs(unittest.TestCase): - - @classmethod - def setUpClass(self): - self.test_file_dir = join( - pq.__path__[0], 'tests', 'qasm_files') - self.config_fn = join(self.test_file_dir, 'config.json') - - self.times = gen.gen_sweep_pts(start=100e-9, stop=5e-6, step=200e-9) - self.clocks = np.round(self.times/5e-9).astype(int) - self.simple_config_fn = join(self.test_file_dir, 'config_simple.json') - self.jump_to_start = ("beq r14, r14, Exp_Start " + - "\t# Jump to start ad nauseam") - self.QWG_flux_lutman = qlm.QWG_FluxLookuptableManager( - 'QWG_flux_lutman') - - with open(self.simple_config_fn) as data_file: - self.config_simple = json.load(data_file) - - def test_qwg_chevron(self): - qasm_file = qwfs.chevron_block_seq('q0', 'q1', RO_target='q0', - no_of_points=5) - qasm_fn = qasm_file.name - qumis_fn = join(self.test_file_dir, "output.qumis") - compiler = qcx.QASM_QuMIS_Compiler(self.simple_config_fn, - verbosity_level=6) - compiler.compile(qasm_fn, qumis_fn) - qumis = compiler.qumis_instructions - m = open(compiler.qumis_fn).read() - qumis_from_file = m.splitlines() - self.assertEqual(qumis, qumis_from_file) - self.assertEqual(compiler.qumis_instructions[2], 'Exp_Start: ') - self.assertEqual(compiler.qumis_instructions[-1], self.jump_to_start) - - # finally test that it can be converted into valid instructions - asm = Assembler(qumis_fn) - asm.convert_to_instructions() - - def test_qwg_swapN(self): - nr_pulses = [1, 3, 5, 11] - qasm_file = qwfs.SWAPN('q0', 'q1', RO_target='q0', - nr_pulses=nr_pulses) - qasm_fn = qasm_file.name - qumis_fn = join(self.test_file_dir, "output.qumis") - compiler = qcx.QASM_QuMIS_Compiler(self.simple_config_fn, - verbosity_level=2) - compiler.compile(qasm_fn, qumis_fn) - qumis = compiler.qumis_instructions - m = open(compiler.qumis_fn).read() - qumis_from_file = m.splitlines() - self.assertEqual(qumis, qumis_from_file) - self.assertEqual(compiler.qumis_instructions[2], 'Exp_Start: ') - self.assertEqual(compiler.qumis_instructions[-1], self.jump_to_start) - - for i in range(3): - time_pts = get_timepoints_from_label( - target_label='square', timing_grid=compiler.timing_grid, - start_label='qwg_trigger_{}'.format(i), - end_label='ro') - self.assertEqual(len(time_pts['target_tps']), nr_pulses[i]) - - time_pts = get_timepoints_from_label( - target_label='ro', timing_grid=compiler.timing_grid) - self.assertEqual(len(time_pts['target_tps']), len(nr_pulses)+4) - # finally test that it can be converted into valid instructions - asm = Assembler(qumis_fn) - asm.convert_to_instructions() - - def test_time_tuples_since_event(self): - nr_pulses = [1, 3, 5, 11] - qasm_file = qwfs.SWAPN('q0', 'q1', RO_target='q0', - nr_pulses=nr_pulses) - qasm_fn = qasm_file.name - qumis_fn = join(self.test_file_dir, "output.qumis") - compiler = qcx.QASM_QuMIS_Compiler(self.simple_config_fn, - verbosity_level=2) - compiler.compile(qasm_fn, qumis_fn) - - for i in range(3): - time_tuples, end_time = get_timetuples_since_event( - start_label='qwg_trigger_{}'.format(i), - target_labels=['square', 'cz'], - timing_grid=compiler.timing_grid, end_label='ro') - - self.assertEqual(len(time_tuples), nr_pulses[i]) - for time_tuple in time_tuples: - self.assertEqual(time_tuple[1], 'square') - self.assertGreater(time_tuple[0], 0) - - def test_QWG_flux_QASM_sweep(self): - qasm_file = qwfs.chevron_block_seq('Q0', 'Q1', no_of_points=6) - qasm_fn = qasm_file.name - - s = swf.QWG_flux_QASM_Sweep( - qasm_fn=qasm_fn, config=self.config_simple, - CBox=None, QWG_flux_lutmans=[self.QWG_flux_lutman], - verbosity_level=1, upload=False) - s.sweep_points = np.arange(6) - s.prepare() - - def test_QWG_flux_waveform_time_tuples(self): - self.QWG_flux_lutman.F_amp(0.5) - self.QWG_flux_lutman.F_length(20e-9) - self.QWG_flux_lutman.Z_amp(0.05) # amp of Z_compensation - - end_time = 300 - time_tuples = [] - waveform = self.QWG_flux_lutman.generate_composite_flux_pulse( - time_tuples, end_time) - np.testing.assert_array_equal(np.zeros(300), waveform) - - time_tuples = [(5, 'cz'), (50, 'cz'), (120, 'square')] - waveform = self.QWG_flux_lutman.generate_composite_flux_pulse( - time_tuples, end_time) - # test_QWG_flux_lutman_basic_waveforms below tests that the - # CZ_expected is what we want - std_wfs = self.QWG_flux_lutman.standard_waveforms() - CZ_exp = std_wfs['adiabatic_Z'] - sq_exp = std_wfs['square'] - - expected_waveform = np.zeros(300) - expected_waveform[5:5+len(CZ_exp)] = CZ_exp - expected_waveform[50:50+len(CZ_exp)] = CZ_exp - expected_waveform[120:120+len(sq_exp)] = sq_exp - np.testing.assert_array_equal(waveform, expected_waveform) - - def test_QWG_flux_lutman_basic_wfs_square(self): - """ - Test the correct generation of the basic waveforms. - It tests the generation of the block pulse and the adiabatic pulse - based on the parameters specified in the QWG_flux_lutman - """ - wf_dict = self.QWG_flux_lutman.generate_standard_pulses() - test_block = np.concatenate([0.5*np.ones(1000), np.zeros(500), - -0.5*np.ones(1000)]) - np.testing.assert_array_equal(wf_dict['square'], test_block) - - simple_block = self.QWG_flux_lutman.standard_waveforms()['square'] - np.testing.assert_array_equal(simple_block, 0.5*np.ones(1000)) - - def test_QWG_flux_lutman_basic_wfs_CZ(self): - """ - Test the basic waveform CZ and CZ with phase correction - """ - self.QWG_flux_lutman.sampling_rate(1e9) - self.QWG_flux_lutman.F_amp(0.5) - self.QWG_flux_lutman.F_length(1e-6) - self.QWG_flux_lutman.F_compensation_delay(500e-9) - self.QWG_flux_lutman.Z_amp(0.05) - corr_len = 10 - self.QWG_flux_lutman.Z_length(corr_len*1e-9) - - CZ = self.QWG_flux_lutman.standard_waveforms()['adiabatic'] - CZ_expected = wf.martinis_flux_pulse( - length=self.QWG_flux_lutman.F_length(), - lambda_2=self.QWG_flux_lutman.F_lambda_2(), - lambda_3=self.QWG_flux_lutman.F_lambda_3(), - theta_f=self.QWG_flux_lutman.F_theta_f(), - f_01_max=self.QWG_flux_lutman.F_f_01_max(), - J2=self.QWG_flux_lutman.F_J2(), - E_c=self.QWG_flux_lutman.F_E_c(), - V_per_phi0=self.QWG_flux_lutman.V_per_phi0(), - f_interaction=self.QWG_flux_lutman.F_f_interaction(), - f_bus=None, - asymmetry=self.QWG_flux_lutman.F_asymmetry(), - sampling_rate=self.QWG_flux_lutman.sampling_rate(), - return_unit='V') - np.testing.assert_array_equal(CZ, CZ_expected) - CZ_phase_corr = \ - self.QWG_flux_lutman.standard_waveforms()['adiabatic_Z'] - - # Test CZ_with_phase_correction - Z_corr = np.ones(corr_len) * 0.05 - CZ_phase_corr_expected = np.concatenate([CZ, Z_corr]) - np.testing.assert_array_equal(CZ_phase_corr, CZ_phase_corr_expected) From 7562c5c78372454eee6961cf79370331bb7951c5 Mon Sep 17 00:00:00 2001 From: Adriaan Date: Wed, 29 Aug 2018 11:51:08 +0200 Subject: [PATCH 17/26] restored the CZ waveform, need to add tests --- .../meta_instrument/LutMans/flux_lutman.py | 76 +++++++++---------- pycqed/tests/test_flux_lutman.py | 26 ------- 2 files changed, 34 insertions(+), 68 deletions(-) diff --git a/pycqed/instrument_drivers/meta_instrument/LutMans/flux_lutman.py b/pycqed/instrument_drivers/meta_instrument/LutMans/flux_lutman.py index 239237d508..b4e18f5c8f 100644 --- a/pycqed/instrument_drivers/meta_instrument/LutMans/flux_lutman.py +++ b/pycqed/instrument_drivers/meta_instrument/LutMans/flux_lutman.py @@ -6,6 +6,7 @@ from qcodes.utils import validators as vals from pycqed.instrument_drivers.pq_parameters import NP_NANs from pycqed.measurement.waveform_control_CC import waveform as wf +from pycqed.measurement.waveform_control_CC import waveforms_flux as wfl from pycqed.measurement.openql_experiments.openql_helpers import clocks_to_s from qcodes.plots.pyqtgraph import QtPlot import matplotlib.pyplot as plt @@ -539,7 +540,7 @@ def generate_standard_waveforms(self): self._wave_dict['park'] = self._gen_park() # FIXME: reenable this - self._wave_dict['cz'] = np.zeros(10) # self._gen_cz() + self._wave_dict['cz'] = self._gen_cz() self._wave_dict['cz_z'] = np.zeros(10) # self._gen_cz_z(regenerate_cz=False) self._wave_dict['idle_z'] = self._gen_idle_z() @@ -565,37 +566,32 @@ def _gen_park(self): def _gen_cz(self): dac_scale_factor = self.get_amp_to_dac_val_scale_factor() + eps_i = self.calc_amp_to_eps(0, state_A='11', state_B='02') + theta_i = wfl.eps_to_theta(eps_i, g=self.q_J2()) if not self.czd_double_sided(): - CZ = wf.martinis_flux_pulse( - length=self.cz_length(), - lambda_2=self.cz_lambda_2(), - lambda_3=self.cz_lambda_3(), - theta_f=self.cz_theta_f(), - f_01_max=self.cz_freq_01_max(), - J2=self.cz_J2(), - f_interaction=self.cz_freq_interaction(), - sampling_rate=self.sampling_rate(), - return_unit='f01') - return dac_scale_factor*self.detuning_to_amp( - self.cz_freq_01_max() - CZ) + CZ_theta = wfl.martinis_flux_pulse( + self.cz_length(), theta_i=theta_i, + theta_f=np.deg2rad(self.cz_theta_f()), + lambda_2=self.cz_lambda_2(), lambda_3=self.cz_lambda_3()) + CZ_eps = wfl.theta_to_eps(CZ_theta, g=self.q_J2()) + CZ_amp = self.calc_eps_to_amp(CZ_eps, state_A='11', state_B='02') + + CZ = dac_scale_factor*CZ_amp + return CZ + else: # Simple double sided CZ pulse implemented in most basic form. # repeats the same CZ gate twice and sticks it together. - half_CZ_A = wf.martinis_flux_pulse( - length=self.cz_length()*self.czd_length_ratio(), - lambda_2=self.cz_lambda_2(), - lambda_3=self.cz_lambda_3(), - theta_f=self.cz_theta_f(), - f_01_max=self.cz_freq_01_max(), - # V_per_phi0=self.cz_V_per_phi0(), - J2=self.cz_J2(), - # E_c=self.cz_E_c(), - f_interaction=self.cz_freq_interaction(), - sampling_rate=self.sampling_rate(), - return_unit='f01') - half_CZ_A = dac_scale_factor*self.detuning_to_amp( - self.cz_freq_01_max() - half_CZ_A) + CZ_theta_A = wfl.martinis_flux_pulse( + self.cz_length()/2, theta_i=theta_i, + theta_f=np.deg2rad(self.cz_theta_f()), + lambda_2=self.cz_lambda_2(), lambda_3=self.cz_lambda_3()) + CZ_eps_A = wfl.theta_to_eps(CZ_theta_A, g=self.q_J2()) + CZ_amp_A = self.calc_eps_to_amp( + CZ_eps_A, state_A='11', state_B='02', positive_branch=True) + + CZ_A = dac_scale_factor*CZ_amp_A # Generate the second CZ pulse. If the params are np.nan, default # to the main parameter @@ -613,24 +609,20 @@ def _gen_cz(self): else: d_lambda_3 = self.cz_lambda_3() - half_CZ_B = wf.martinis_flux_pulse( - length=self.cz_length()*(1-self.czd_length_ratio()), - lambda_2=d_lambda_2, - lambda_3=d_lambda_3, - theta_f=d_theta_f, - f_01_max=self.cz_freq_01_max(), - # V_per_phi0=self.cz_V_per_phi0(), - J2=self.cz_J2(), - # E_c=self.cz_E_c(), - f_interaction=self.cz_freq_interaction(), - sampling_rate=self.sampling_rate(), - return_unit='f01') - half_CZ_B = dac_scale_factor*self.detuning_to_amp( - self.cz_freq_01_max() - half_CZ_B, positive_branch=False) + CZ_theta_B = wfl.martinis_flux_pulse( + self.cz_length()/2, theta_i=theta_i, + theta_f=np.deg2rad(d_theta_f), + lambda_2=d_lambda_2, lambda_3=d_lambda_3) + CZ_eps_B = wfl.theta_to_eps(CZ_theta_B, g=self.q_J2()) + CZ_amp_B = self.calc_eps_to_amp( + CZ_eps_B, state_A='11', state_B='02', positive_branch=False) + + CZ_B = dac_scale_factor*CZ_amp_B + # Combine both halves of the double sided CZ gate amp_rat = self.czd_amp_ratio() waveform = np.concatenate( - [half_CZ_A, amp_rat*half_CZ_B + self.czd_amp_offset()]) + [CZ_A, amp_rat*CZ_B + self.czd_amp_offset()]) return waveform diff --git a/pycqed/tests/test_flux_lutman.py b/pycqed/tests/test_flux_lutman.py index 153ea2af69..a92df24575 100644 --- a/pycqed/tests/test_flux_lutman.py +++ b/pycqed/tests/test_flux_lutman.py @@ -263,32 +263,6 @@ def test_calc_detuning_freq_inversion(self): freqs_02, state_A=state_A, state_B=state_B, positive_branch=False) np.testing.assert_array_almost_equal(amps, amps_inv) - - - - @unittest.expectedFailure - def test_freq_amp_conversions(self): - - # Test the basic inversion - test_amps = np.linspace(0.1, .5, 11) - freqs = self.fluxlutman.amp_to_detuning(test_amps) - recovered_amps = self.fluxlutman.detuning_to_amp(freqs) - np.testing.assert_array_almost_equal(test_amps, recovered_amps) - - # Test that the top of the parabola is given if asked for "impossible" - # solutions - recovered_amp = self.fluxlutman.detuning_to_amp(0) - self.assertAlmostEqual(recovered_amp, 0.082696256708720065) - recovered_amp = self.fluxlutman.detuning_to_amp(-5e9) - self.assertAlmostEqual(recovered_amp, 0.082696256708720065) - - # Test negative branch of parabola - test_amps = np.linspace(-0.1, -.5, 11) - freqs = self.fluxlutman.amp_to_detuning(test_amps) - recovered_amps = self.fluxlutman.detuning_to_amp( - freqs, positive_branch=False) - np.testing.assert_array_almost_equal(test_amps, recovered_amps) - def test_custom_wf(self): self.fluxlutman.generate_standard_waveforms() From 534e10fac0e405c0bb03090eb76505243ee82e92 Mon Sep 17 00:00:00 2001 From: Adriaan Date: Wed, 29 Aug 2018 12:03:14 +0200 Subject: [PATCH 18/26] restored double sided and added some comments --- .../instrument_drivers/meta_instrument/LutMans/flux_lutman.py | 4 +++- pycqed/tests/test_flux_lutman.py | 2 -- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pycqed/instrument_drivers/meta_instrument/LutMans/flux_lutman.py b/pycqed/instrument_drivers/meta_instrument/LutMans/flux_lutman.py index b4e18f5c8f..864c252548 100644 --- a/pycqed/instrument_drivers/meta_instrument/LutMans/flux_lutman.py +++ b/pycqed/instrument_drivers/meta_instrument/LutMans/flux_lutman.py @@ -541,7 +541,7 @@ def generate_standard_waveforms(self): # FIXME: reenable this self._wave_dict['cz'] = self._gen_cz() - self._wave_dict['cz_z'] = np.zeros(10) # self._gen_cz_z(regenerate_cz=False) + self._wave_dict['cz_z'] = self._gen_cz_z(regenerate_cz=False) self._wave_dict['idle_z'] = self._gen_idle_z() self._wave_dict['custom_wf'] = self._gen_custom_wf() @@ -567,6 +567,7 @@ def _gen_cz(self): dac_scale_factor = self.get_amp_to_dac_val_scale_factor() eps_i = self.calc_amp_to_eps(0, state_A='11', state_B='02') + # beware theta in radian theta_i = wfl.eps_to_theta(eps_i, g=self.q_J2()) if not self.czd_double_sided(): @@ -577,6 +578,7 @@ def _gen_cz(self): CZ_eps = wfl.theta_to_eps(CZ_theta, g=self.q_J2()) CZ_amp = self.calc_eps_to_amp(CZ_eps, state_A='11', state_B='02') + # convert amplitude in V to amplitude in awg dac value CZ = dac_scale_factor*CZ_amp return CZ diff --git a/pycqed/tests/test_flux_lutman.py b/pycqed/tests/test_flux_lutman.py index a92df24575..929a3c5ca4 100644 --- a/pycqed/tests/test_flux_lutman.py +++ b/pycqed/tests/test_flux_lutman.py @@ -116,7 +116,6 @@ def test_standard_cz_waveform(self): self.fluxlutman.czd_double_sided(False) self.fluxlutman.generate_standard_waveforms() - @unittest.expectedFailure def test_double_sided_cz_waveform(self): """ This test mostly tests if the parameters have some effect. @@ -244,7 +243,6 @@ def test_calc_amp_to_eps(self): expected_eps = freqs_11 - freqs_02 np.testing.assert_array_almost_equal(eps, expected_eps) - def test_calc_detuning_freq_inversion(self): state_A = '02' state_B = '11' From 6e9f0b6560ce4c51fbedad8a0c6ed2718b654cd4 Mon Sep 17 00:00:00 2001 From: Adriaan Date: Wed, 29 Aug 2018 17:47:15 +0200 Subject: [PATCH 19/26] added plot_level_diagrams --- .../meta_instrument/LutMans/flux_lutman.py | 76 ++++++++++--------- pycqed/tests/test_flux_lutman.py | 16 +--- 2 files changed, 44 insertions(+), 48 deletions(-) diff --git a/pycqed/instrument_drivers/meta_instrument/LutMans/flux_lutman.py b/pycqed/instrument_drivers/meta_instrument/LutMans/flux_lutman.py index 864c252548..2950fc3aba 100644 --- a/pycqed/instrument_drivers/meta_instrument/LutMans/flux_lutman.py +++ b/pycqed/instrument_drivers/meta_instrument/LutMans/flux_lutman.py @@ -235,7 +235,6 @@ def get_polycoeffs_state(self, state: str): raise NotImplementedError('State {} not recognized'.format(state)) return polycoeffs - def calc_amp_to_freq(self, amp: float, state: str='01'): """ Converts pulse amplitude in Volt to energy in Hz for a particular state @@ -339,8 +338,6 @@ def calc_eps_to_amp(self, eps, # imaginary part is ignored, instead sticking to closest real value return np.real(sol) - - def get_dac_val_to_amp_scalefactor(self): """ Returns the scale factor to transform an amplitude in 'dac value' to an @@ -430,8 +427,10 @@ def _add_waveform_parameters(self): initial_value=80, parameter_class=ManualParameter) - self.add_parameter('czd_length_ratio', vals=vals.Numbers(0, 1), - initial_value=0.5, + self.add_parameter('czd_length_ratio', + vals=vals.MultiType(vals.Numbers(0, 1), + vals.Enum('auto')), + initial_value='auto', parameter_class=ManualParameter) self.add_parameter( 'czd_lambda_2', @@ -567,7 +566,7 @@ def _gen_cz(self): dac_scale_factor = self.get_amp_to_dac_val_scale_factor() eps_i = self.calc_amp_to_eps(0, state_A='11', state_B='02') - # beware theta in radian + # Beware theta in radian! theta_i = wfl.eps_to_theta(eps_i, g=self.q_J2()) if not self.czd_double_sided(): @@ -1257,59 +1256,68 @@ def plot_cz_trajectory(self, ax=None, show=True): plt.show() return ax - def plot_level_diagram(self, ax=None, show=True, - plot_cz_trajectory=False): - - if ax is None: - f, ax = plt.subplots() - - return ax - - def plot_flux_arc(self, ax=None, show=True, - plot_cz_trajectory=False): + def plot_level_diagram(self, ax=None, show=True): """ - Plots the flux arc as used in the lutman based on the polynomial - coefficients + Plots the level diagram as specified by the q_ parameters. """ if ax is None: f, ax = plt.subplots() + amps = np.linspace(-2.5, 2.5, 101) # maximum voltage of AWG amp mode freqs = self.calc_amp_to_freq(amps, state='01') ax.plot(amps, freqs, label='$f_{01}$') + ax.text(0, self.calc_amp_to_freq(0, state='01'), '01', color='C0', + ha='left', va='bottom', clip_on=True) + freqs = self.calc_amp_to_freq(amps, state='02') ax.plot(amps, freqs, label='$f_{02}$') + ax.text(0, self.calc_amp_to_freq(0, state='02'), '02', color='C1', + ha='left', va='bottom', clip_on=True) + freqs = self.calc_amp_to_freq(amps, state='10') ax.plot(amps, freqs, label='$f_{10}$') + ax.text(0, self.calc_amp_to_freq(0, state='10'), '10', color='C2', + ha='left', va='bottom', clip_on=True) + freqs = self.calc_amp_to_freq(amps, state='11') ax.plot(amps, freqs, label='$f_{11}$') + ax.text(0, self.calc_amp_to_freq(0, state='11'), '11', color='C3', + ha='left', va='bottom', clip_on=True) + ax.axvline(0, 0, 1e10, linestyle='dotted', c='grey') + amp_J2 = self.calc_eps_to_amp(0, state_A='11', state_B='02') + amp_J1 = self.calc_eps_to_amp(0, state_A='10', state_B='01') - # ax.axhline(self.cz_freq_interaction(), -5, 5, - # label='$f_{\mathrm{int.}}$:'+' {:.3f} GHz'.format( - # self.cz_freq_interaction()*1e-9), - # c='C1') + ax.axvline(amp_J2, ls='--', lw=1, c='C4') + ax.axvline(amp_J1, ls='--', lw=1, c='C6') - ax.axvline(0, 0, 1e10, linestyle='dotted', c='grey') - # ax.fill_between( - # x=[-5, 5], - # y1=[self.cz_freq_interaction()-self.cz_J2()]*2, - # y2=[self.cz_freq_interaction()+self.cz_J2()]*2, - # label='$J_{\mathrm{2}}/2\pi$:'+' {:.3f} MHz'.format( - # self.q_J2()*1e-6), - # color='C1', alpha=0.25) + f_11_02 = self.calc_amp_to_freq(amp_J2, state='11') + ax.plot([amp_J2], [f_11_02], + color='C4', marker='o', label='11-02') + ax.text(amp_J2, f_11_02, + '({:.3f},{:.2f})'.format(amp_J2, f_11_02*1e-9), + color='C4', + ha='left', va='bottom', clip_on=True) + + f_10_01 = self.calc_amp_to_freq(amp_J1, state='01') + + ax.plot([amp_J1], [f_10_01], + color='C5', marker='o', label='10-01') + ax.text(amp_J1, f_10_01, + '({:.3f},{:.2f})'.format(amp_J1, f_10_01*1e-9), + color='C5', ha='left', va='bottom', clip_on=True) title = ('Calibration visualization\n{}\nchannel {}'.format( self.AWG(), self.cfg_awg_channel())) - if plot_cz_trajectory: - self.plot_cz_trajectory(ax=ax, show=False) - leg = ax.legend(title=title, loc=(1.05, .7)) + leg = ax.legend(title=title, loc=(1.05, .3)) leg._legend_box.align = 'center' set_xlabel(ax, 'AWG amplitude', 'V') set_ylabel(ax, 'Frequency', 'Hz') ax.set_xlim(-2.5, 2.5) - ax.set_ylim(3e9, np.max(freqs)+500e6) + + ax.set_ylim(0, self.calc_amp_to_freq(0, state='02')*1.1) dac_val_axis = ax.twiny() dac_ax_lims = np.array(ax.get_xlim()) * \ diff --git a/pycqed/tests/test_flux_lutman.py b/pycqed/tests/test_flux_lutman.py index 929a3c5ca4..1d1d229c39 100644 --- a/pycqed/tests/test_flux_lutman.py +++ b/pycqed/tests/test_flux_lutman.py @@ -93,20 +93,8 @@ def test_partner_lutman_loading(self): self.assertEqual(self.AWG._realtime_w0[0], [.3]) self.assertEqual(self.AWG._realtime_w1[0], [.5]) - # Commented out because this mode is deprecated - # def test_operating_mode_program_loading(self): - # self.fluxlutman.cfg_operating_mode('Codeword_normal') - # self.fluxlutman.load_waveforms_onto_AWG_lookuptable() - - # self.fluxlutman.cfg_operating_mode('CW_single_02') - # self.fluxlutman.load_waveforms_onto_AWG_lookuptable() - - # self.fluxlutman.cfg_operating_mode('Codeword_normal') - # self.fluxlutman.load_waveforms_onto_AWG_lookuptable() - - @unittest.expectedFailure - def test_plot_flux_arc(self): - self.fluxlutman.plot_flux_arc(show=False, plot_cz_trajectory=True) + def test_plot_level_diagram(self): + self.fluxlutman.plot_level_diagram(show=False) @unittest.expectedFailure def test_plot_cz_trajectory(self): From 5c123f160744ee9f894e1556c877c380ec842422 Mon Sep 17 00:00:00 2001 From: Adriaan Date: Wed, 29 Aug 2018 18:11:00 +0200 Subject: [PATCH 20/26] added method to plot CZ trajectory for debugging --- .../meta_instrument/LutMans/flux_lutman.py | 46 ++++++++++++++----- pycqed/tests/test_flux_lutman.py | 5 +- 2 files changed, 36 insertions(+), 15 deletions(-) diff --git a/pycqed/instrument_drivers/meta_instrument/LutMans/flux_lutman.py b/pycqed/instrument_drivers/meta_instrument/LutMans/flux_lutman.py index 2950fc3aba..e1990d741b 100644 --- a/pycqed/instrument_drivers/meta_instrument/LutMans/flux_lutman.py +++ b/pycqed/instrument_drivers/meta_instrument/LutMans/flux_lutman.py @@ -483,7 +483,7 @@ def _add_waveform_parameters(self): ' the CZ waveform should evaluate to. This is realized by adding' ' an offset to the phase correction pulse.\nBy setting this ' 'parameter to np.nan no offset correction is performed.', - initial_value=0, + initial_value=np.nan, unit='dac value * samples', vals=vals.MultiType(vals.Numbers(), NP_NANs()), parameter_class=ManualParameter) @@ -563,6 +563,9 @@ def _gen_park(self): return np.zeros(42) def _gen_cz(self): + """ + Generates the CZ waveform. + """ dac_scale_factor = self.get_amp_to_dac_val_scale_factor() eps_i = self.calc_amp_to_eps(0, state_A='11', state_B='02') @@ -1237,24 +1240,43 @@ def distort_waveform(self, waveform, inverse=False): # Plotting methods # ################################# - def plot_cz_trajectory(self, ax=None, show=True): + def plot_cz_trajectory(self, axs=None, show=True, + extra_plot_samples: int=50): """ Plots the cz trajectory in frequency space. """ - if ax is None: - f, ax = plt.subplots() - extra_samples = 10 + if axs is None: + f, axs = plt.subplots(figsize=(5, 7), nrows=3, sharex=True) nr_plot_samples = int((self.cz_length()+self.cz_phase_corr_length()) * - self.sampling_rate() + extra_samples) + self.sampling_rate() + extra_plot_samples) + dac_amps = self._wave_dict['cz_z'][:nr_plot_samples] - samples = np.arange(len(dac_amps)) - amps = dac_amps*self.get_dac_val_to_amp_scalefactor() - deltas = self.amp_to_detuning(amps) - freqs = self.cz_freq_01_max()-deltas - ax.scatter(amps, freqs, c=samples, label='CZ trajectory') + t = np.arange(0, len(dac_amps))*1/self.sampling_rate() + + CZ_amp = dac_amps*self.get_dac_val_to_amp_scalefactor() + CZ_eps = self.calc_amp_to_eps(CZ_amp, '11', '02') + CZ_theta = wfl.eps_to_theta(CZ_eps, self.q_J2()) + + axs[0].plot(t, np.rad2deg(CZ_theta), marker='.') + axs[0].fill_between(t, np.rad2deg(CZ_theta), color='C0', alpha=.5) + set_ylabel(axs[0], r'$\theta$', 'deg') + + axs[1].plot(t, CZ_eps, marker='.') + axs[1].fill_between(t, CZ_eps, color='C0', alpha=.5) + set_ylabel(axs[1], r'$\epsilon_{11-02}$', 'Hz') + + axs[2].plot(t, CZ_amp, marker='.') + axs[2].fill_between(t, CZ_amp, color='C0', alpha=.1) + set_xlabel(axs[2], 'Time', 's') + set_ylabel(axs[2], r'Amp.', 'V') + # axs[2].set_ylim(-1, 1) + axs[2].axhline(0, lw=.2, color='grey') + CZ_amp_pred = self.distort_waveform(CZ_amp)[:len(CZ_amp)] + axs[2].plot(t, CZ_amp_pred, marker='.') + axs[2].fill_between(t, CZ_amp_pred, color='C1', alpha=.3) if show: plt.show() - return ax + return axs def plot_level_diagram(self, ax=None, show=True): """ diff --git a/pycqed/tests/test_flux_lutman.py b/pycqed/tests/test_flux_lutman.py index 1d1d229c39..ae86e86f1f 100644 --- a/pycqed/tests/test_flux_lutman.py +++ b/pycqed/tests/test_flux_lutman.py @@ -51,7 +51,7 @@ def setUpClass(self): self.fluxlutman_partner.set_default_lutmap() self.fluxlutman_partner.instr_partner_lutman('fluxlutman_main') - def test__program_hash_differs_AWG8_flux_lutman(self): + def test_program_hash_differs_AWG8_flux_lutman(self): # set to a random value to ensure different self.fluxlutman._awgs_fl_sequencer_program_expected_hash(351340) @@ -96,7 +96,6 @@ def test_partner_lutman_loading(self): def test_plot_level_diagram(self): self.fluxlutman.plot_level_diagram(show=False) - @unittest.expectedFailure def test_plot_cz_trajectory(self): self.fluxlutman.plot_cz_trajectory(show=False) @@ -280,7 +279,7 @@ def test_custom_wf(self): def test_generate_standard_flux_waveforms(self): self.fluxlutman.generate_standard_waveforms() - self.fluxlutman.render_wave('cz') + def test_upload_and_distort(self): self.fluxlutman.load_waveforms_onto_AWG_lookuptable() From 15415e767ce9c78771346a14a1c1b12dfcdf3d73 Mon Sep 17 00:00:00 2001 From: Adriaan Date: Wed, 29 Aug 2018 18:52:48 +0200 Subject: [PATCH 21/26] added beginning of lenght ratio calculation --- .../meta_instrument/LutMans/flux_lutman.py | 20 ++++++++++++++++++- .../waveform_control_CC/waveforms_flux.py | 4 ++-- pycqed/tests/test_flux_lutman.py | 11 ++++++++++ 3 files changed, 32 insertions(+), 3 deletions(-) diff --git a/pycqed/instrument_drivers/meta_instrument/LutMans/flux_lutman.py b/pycqed/instrument_drivers/meta_instrument/LutMans/flux_lutman.py index e1990d741b..80902a75fc 100644 --- a/pycqed/instrument_drivers/meta_instrument/LutMans/flux_lutman.py +++ b/pycqed/instrument_drivers/meta_instrument/LutMans/flux_lutman.py @@ -338,6 +338,18 @@ def calc_eps_to_amp(self, eps, # imaginary part is ignored, instead sticking to closest real value return np.real(sol) + def calc_net_zero_length_ratio(self): + """ + Determines the lenght ratio of the net-zero pulses based on the + parameter "czd_length_ratio" + """ + if self.czd_length_ratio() != 'auto': + return self.czd_length_ratio() + else: + print('TODO') + raise NotImplementedError() + + def get_dac_val_to_amp_scalefactor(self): """ Returns the scale factor to transform an amplitude in 'dac value' to an @@ -430,7 +442,13 @@ def _add_waveform_parameters(self): self.add_parameter('czd_length_ratio', vals=vals.MultiType(vals.Numbers(0, 1), vals.Enum('auto')), - initial_value='auto', + initial_value=0.5, + docstring='When using a net-zero pulse, this ' + 'parameter is used to determine the length ratio' + ' of the positive and negative parts of the pulse.' + 'If this is set to "auto", the ratio will be ' + 'automatically determined to ensure the integral ' + 'of the net-zero pulse is close to zero.', parameter_class=ManualParameter) self.add_parameter( 'czd_lambda_2', diff --git a/pycqed/measurement/waveform_control_CC/waveforms_flux.py b/pycqed/measurement/waveform_control_CC/waveforms_flux.py index 61b59b2d48..67a70d9f16 100644 --- a/pycqed/measurement/waveform_control_CC/waveforms_flux.py +++ b/pycqed/measurement/waveform_control_CC/waveforms_flux.py @@ -24,7 +24,7 @@ def martinis_flux_pulse(length: float, Note that θ still needs to be transformed into detuning from the interaction and into AWG amplitude V(t). - θ = θ_i + Σ_{n=1}^N (λ_n*(1-cos(n*2*pi*t/t_p))/2 + θ(τ) = θ_i + Σ_{n=1}^N (λ_n*(1-cos(n*2*pi*τ/τ_p))/2 Args: length : lenght of the waveform (s) @@ -38,7 +38,7 @@ def martinis_flux_pulse(length: float, This waveform is generated in several steps 1. Generate a time grid, may include fine sampling. 2. Generate θ(τ) using eqs 15 and 16 - 3. Transform from proper time τ to real time t using interpolation + 3. Transform from proper time "τ" to real time "t" using interpolation """ if theta_f < theta_i: diff --git a/pycqed/tests/test_flux_lutman.py b/pycqed/tests/test_flux_lutman.py index ae86e86f1f..240a78b0cf 100644 --- a/pycqed/tests/test_flux_lutman.py +++ b/pycqed/tests/test_flux_lutman.py @@ -334,6 +334,17 @@ def test_uploading_composite_waveform(self): uploaded_wf_instr = self.AWG.wave_ch1_cw003() np.testing.assert_array_almost_equal(uploaded_wf_lutman, uploaded_wf_instr) + @unittest.expectedFailure + def test_length_ratio(self): + self.flux_lutman.czd_length_ratio(.5) + lr = self.fluxlutman.calc_net_zero_length_ratio() + self.assertEqual(lr, 0.5) + + self.flux_lutman.czd_length_ratio('auto') + lr = self.fluxlutman.calc_net_zero_length_ratio() + # raises not implemented + + @classmethod def tearDownClass(self): From eea57bf3af79d0b58a648f5f553a1e2ac98a36f6 Mon Sep 17 00:00:00 2001 From: Adriaan Date: Thu, 30 Aug 2018 10:42:23 +0200 Subject: [PATCH 22/26] added auto calculation option for CZ lenght ratio --- .../meta_instrument/LutMans/flux_lutman.py | 56 +++++++++++++------ pycqed/tests/test_flux_lutman.py | 49 ++++++++++++---- 2 files changed, 77 insertions(+), 28 deletions(-) diff --git a/pycqed/instrument_drivers/meta_instrument/LutMans/flux_lutman.py b/pycqed/instrument_drivers/meta_instrument/LutMans/flux_lutman.py index 80902a75fc..50c92ce6d8 100644 --- a/pycqed/instrument_drivers/meta_instrument/LutMans/flux_lutman.py +++ b/pycqed/instrument_drivers/meta_instrument/LutMans/flux_lutman.py @@ -341,13 +341,23 @@ def calc_eps_to_amp(self, eps, def calc_net_zero_length_ratio(self): """ Determines the lenght ratio of the net-zero pulses based on the - parameter "czd_length_ratio" + parameter "czd_length_ratio". + + If czd_length_ratio is set to auto, uses the interaction amplitudes + to determine the scaling of lengths. Note that this is a coarse + approximation. """ if self.czd_length_ratio() != 'auto': return self.czd_length_ratio() else: - print('TODO') - raise NotImplementedError() + amp_J2_pos = self.calc_eps_to_amp(0, state_A='11', state_B='02', + positive_branch=True) + amp_J2_neg = self.calc_eps_to_amp(0, state_A='11', state_B='02', + positive_branch=False) + + # lr chosen to satisfy (amp_pos*lr + amp_neg*(1-lr) = 0 ) + lr = - amp_J2_neg/(amp_J2_pos-amp_J2_neg) + return lr def get_dac_val_to_amp_scalefactor(self): @@ -372,10 +382,10 @@ def get_dac_val_to_amp_scalefactor(self): # channel range of 5 corresponds to -2.5V to +2.5V channel_range_pp = AWG.get('sigouts_{}_range'.format(awg_ch)) # direct_mode = AWG.get('sigouts_{}_direct'.format(awg_ch)) - scale_factor = channel_amp*(channel_range_pp/2) - return scale_factor + scalefactor = channel_amp*(channel_range_pp/2) + return scalefactor - def get_amp_to_dac_val_scale_factor(self): + def get_amp_to_dac_val_scalefactor(self): if self.get_dac_val_to_amp_scalefactor() == 0: # Give a warning and don't raise an error as things should not # break because of this. @@ -585,7 +595,7 @@ def _gen_cz(self): Generates the CZ waveform. """ - dac_scale_factor = self.get_amp_to_dac_val_scale_factor() + dac_scalefactor = self.get_amp_to_dac_val_scalefactor() eps_i = self.calc_amp_to_eps(0, state_A='11', state_B='02') # Beware theta in radian! theta_i = wfl.eps_to_theta(eps_i, g=self.q_J2()) @@ -599,21 +609,23 @@ def _gen_cz(self): CZ_amp = self.calc_eps_to_amp(CZ_eps, state_A='11', state_B='02') # convert amplitude in V to amplitude in awg dac value - CZ = dac_scale_factor*CZ_amp + CZ = dac_scalefactor*CZ_amp return CZ else: # Simple double sided CZ pulse implemented in most basic form. # repeats the same CZ gate twice and sticks it together. + length_ratio = self.calc_net_zero_length_ratio() + CZ_theta_A = wfl.martinis_flux_pulse( - self.cz_length()/2, theta_i=theta_i, + self.cz_length()*length_ratio, theta_i=theta_i, theta_f=np.deg2rad(self.cz_theta_f()), lambda_2=self.cz_lambda_2(), lambda_3=self.cz_lambda_3()) CZ_eps_A = wfl.theta_to_eps(CZ_theta_A, g=self.q_J2()) CZ_amp_A = self.calc_eps_to_amp( CZ_eps_A, state_A='11', state_B='02', positive_branch=True) - CZ_A = dac_scale_factor*CZ_amp_A + CZ_A = dac_scalefactor*CZ_amp_A # Generate the second CZ pulse. If the params are np.nan, default # to the main parameter @@ -632,14 +644,14 @@ def _gen_cz(self): d_lambda_3 = self.cz_lambda_3() CZ_theta_B = wfl.martinis_flux_pulse( - self.cz_length()/2, theta_i=theta_i, + self.cz_length()*(1-length_ratio), theta_i=theta_i, theta_f=np.deg2rad(d_theta_f), lambda_2=d_lambda_2, lambda_3=d_lambda_3) CZ_eps_B = wfl.theta_to_eps(CZ_theta_B, g=self.q_J2()) CZ_amp_B = self.calc_eps_to_amp( CZ_eps_B, state_A='11', state_B='02', positive_branch=False) - CZ_B = dac_scale_factor*CZ_amp_B + CZ_B = dac_scalefactor*CZ_amp_B # Combine both halves of the double sided CZ gate amp_rat = self.czd_amp_ratio() @@ -1299,13 +1311,17 @@ def plot_cz_trajectory(self, axs=None, show=True, def plot_level_diagram(self, ax=None, show=True): """ Plots the level diagram as specified by the q_ parameters. + 1. Plotting levels + 2. Annotating feature of interest + 3. Adding legend etc. + 4. Add a twin x-axis to denote scale in dac amplitude + """ if ax is None: f, ax = plt.subplots() - - amps = np.linspace(-2.5, 2.5, 101) # maximum voltage of AWG amp mode - + # 1. Plotting levels + amps = np.linspace(-2.5, 2.5, 101) # maximum voltage of AWG in amp mode freqs = self.calc_amp_to_freq(amps, state='01') ax.plot(amps, freqs, label='$f_{01}$') ax.text(0, self.calc_amp_to_freq(0, state='01'), '01', color='C0', @@ -1325,6 +1341,8 @@ def plot_level_diagram(self, ax=None, show=True): ax.plot(amps, freqs, label='$f_{11}$') ax.text(0, self.calc_amp_to_freq(0, state='11'), '11', color='C3', ha='left', va='bottom', clip_on=True) + + # 2. Annotating feature of interest ax.axvline(0, 0, 1e10, linestyle='dotted', c='grey') amp_J2 = self.calc_eps_to_amp(0, state_A='11', state_B='02') @@ -1349,6 +1367,7 @@ def plot_level_diagram(self, ax=None, show=True): '({:.3f},{:.2f})'.format(amp_J1, f_10_01*1e-9), color='C5', ha='left', va='bottom', clip_on=True) + # 3. Adding legend etc. title = ('Calibration visualization\n{}\nchannel {}'.format( self.AWG(), self.cfg_awg_channel())) leg = ax.legend(title=title, loc=(1.05, .3)) @@ -1359,9 +1378,10 @@ def plot_level_diagram(self, ax=None, show=True): ax.set_ylim(0, self.calc_amp_to_freq(0, state='02')*1.1) + # 4. Add a twin x-axis to denote scale in dac amplitude dac_val_axis = ax.twiny() dac_ax_lims = np.array(ax.get_xlim()) * \ - self.get_amp_to_dac_val_scale_factor() + self.get_amp_to_dac_val_scalefactor() dac_val_axis.set_xlim(dac_ax_lims) set_xlabel(dac_val_axis, 'AWG amplitude', 'dac') @@ -1473,8 +1493,8 @@ def get_dac_val_to_amp_scalefactor(self): awg_ch = self.cfg_awg_channel() channel_amp = AWG.get('ch{}_amp'.format(awg_ch)) - scale_factor = channel_amp - return scale_factor + scalefactor = channel_amp + return scalefactor ######################################################################### # Convenience functions below diff --git a/pycqed/tests/test_flux_lutman.py b/pycqed/tests/test_flux_lutman.py index 240a78b0cf..f6663fddfc 100644 --- a/pycqed/tests/test_flux_lutman.py +++ b/pycqed/tests/test_flux_lutman.py @@ -13,13 +13,28 @@ class Test_Flux_LutMan(unittest.TestCase): @classmethod def setUpClass(self): + # gets called at initialization of test class self.AWG = v8.VirtualAWG8('DummyAWG8') self.fluxlutman = flm.AWG8_Flux_LutMan('fluxlutman_main') self.k0 = lko.LinDistortionKernel('k0') + self.fluxlutman_partner = flm.AWG8_Flux_LutMan('fluxlutman_partner') + + @classmethod + def setUp(self): + # gets called before every test method self.fluxlutman.instr_distortion_kernel(self.k0.name) self.k0.instr_AWG(self.AWG.name) + self.k0.filter_model_00( + {'model': 'exponential', 'params': {'tau': 1e-8, 'amp': -0.08}}) + self.k0.filter_model_01( + {'model': 'exponential', 'params': {'tau': 6e-9, 'amp': -0.01}}) + self.k0.filter_model_02( + {'model': 'exponential', 'params': {'tau': 1.8e-9, 'amp': -0.1}}) + self.k0.filter_model_03( + {'model': 'exponential', 'params': {'tau': 1.e-9, 'amp': -0.1}}) + self.fluxlutman.AWG(self.AWG.name) self.fluxlutman.sampling_rate(2.4e9) self.fluxlutman.cz_theta_f(80) @@ -32,8 +47,8 @@ def setUpClass(self): self.k0.cfg_awg_channel(self.fluxlutman.cfg_awg_channel()) self.fluxlutman.cfg_max_wf_length(5e-6) - poly_coeffs = np.array([1.95027142e+09, -3.22560292e+08, - 5.25834946e+07]) + poly_coeffs = -np.array([1.95027142e+09, -3.22560292e+08, + 5.25834946e+07]) self.fluxlutman.q_polycoeffs_freq_01_det(poly_coeffs) self.fluxlutman.q_polycoeffs_anharm(np.array([0, 0, -300e6])) @@ -42,7 +57,6 @@ def setUpClass(self): ################################################ - self.fluxlutman_partner = flm.AWG8_Flux_LutMan('fluxlutman_partner') self.fluxlutman_partner.instr_distortion_kernel(self.k0.name) self.fluxlutman_partner.AWG(self.AWG.name) self.fluxlutman_partner.sampling_rate(2.4e9) @@ -51,6 +65,14 @@ def setUpClass(self): self.fluxlutman_partner.set_default_lutmap() self.fluxlutman_partner.instr_partner_lutman('fluxlutman_main') + self.AWG.awgs_0_outputs_0_amplitude(1) + self.AWG.sigouts_0_range(5) + + self.fluxlutman.czd_amp_ratio(1.) + self.fluxlutman.czd_lambda_2(np.nan) + self.fluxlutman.czd_lambda_3(np.nan) + self.fluxlutman.czd_theta_f(np.nan) + def test_program_hash_differs_AWG8_flux_lutman(self): # set to a random value to ensure different @@ -80,7 +102,7 @@ def test_amp_to_dac_val_conversions(self): sf = self.fluxlutman.get_dac_val_to_amp_scalefactor() self.assertEqual(sf, 0.2*0.8/2) - sc_inv = self.fluxlutman.get_amp_to_dac_val_scale_factor() + sc_inv = self.fluxlutman.get_amp_to_dac_val_scalefactor() self.assertEqual(sc_inv, 1/sf) self.fluxlutman.cfg_awg_channel(1) @@ -88,12 +110,13 @@ def test_amp_to_dac_val_conversions(self): def test_partner_lutman_loading(self): self.fluxlutman.sq_amp(.3) self.fluxlutman_partner.sq_amp(.5) + self.k0.reset_kernels() self.fluxlutman.load_waveform_realtime('square') - self.assertEqual(self.AWG._realtime_w0[0], [.3]) self.assertEqual(self.AWG._realtime_w1[0], [.5]) def test_plot_level_diagram(self): + self.AWG.awgs_0_outputs_0_amplitude(.73) self.fluxlutman.plot_level_diagram(show=False) def test_plot_cz_trajectory(self): @@ -280,7 +303,6 @@ def test_custom_wf(self): def test_generate_standard_flux_waveforms(self): self.fluxlutman.generate_standard_waveforms() - def test_upload_and_distort(self): self.fluxlutman.load_waveforms_onto_AWG_lookuptable() @@ -334,15 +356,22 @@ def test_uploading_composite_waveform(self): uploaded_wf_instr = self.AWG.wave_ch1_cw003() np.testing.assert_array_almost_equal(uploaded_wf_lutman, uploaded_wf_instr) - @unittest.expectedFailure + def test_length_ratio(self): - self.flux_lutman.czd_length_ratio(.5) + self.fluxlutman.czd_length_ratio(.5) lr = self.fluxlutman.calc_net_zero_length_ratio() self.assertEqual(lr, 0.5) - self.flux_lutman.czd_length_ratio('auto') + self.fluxlutman.czd_length_ratio('auto') + + amp_J2_pos = self.fluxlutman.calc_eps_to_amp( + 0, state_A='11', state_B='02', positive_branch=True) + amp_J2_neg = self.fluxlutman.calc_eps_to_amp( + 0, state_A='11', state_B='02', positive_branch=False) lr = self.fluxlutman.calc_net_zero_length_ratio() - # raises not implemented + integral = lr*amp_J2_pos + (1-lr)*amp_J2_neg + np.testing.assert_almost_equal(integral, 0) + From d6fd31212cc6825d2d71024882683680381da4ee Mon Sep 17 00:00:00 2001 From: fbattistel <39129759+fbattistel@users.noreply.github.com> Date: Thu, 30 Aug 2018 12:03:57 +0200 Subject: [PATCH 23/26] bug found and fixed in waveforms_flux it was wrong the integration step to convert from virtual time to physical time --- .../measurement/waveform_control_CC/waveforms_flux.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/pycqed/measurement/waveform_control_CC/waveforms_flux.py b/pycqed/measurement/waveform_control_CC/waveforms_flux.py index 67a70d9f16..c02eb42618 100644 --- a/pycqed/measurement/waveform_control_CC/waveforms_flux.py +++ b/pycqed/measurement/waveform_control_CC/waveforms_flux.py @@ -24,14 +24,14 @@ def martinis_flux_pulse(length: float, Note that θ still needs to be transformed into detuning from the interaction and into AWG amplitude V(t). - θ(τ) = θ_i + Σ_{n=1}^N (λ_n*(1-cos(n*2*pi*τ/τ_p))/2 + θ(τ) = θ_i + Σ_{n=1}^N λ_n*(1-cos(n*2*pi*τ/τ_p)) Args: length : lenght of the waveform (s) lambda_2 : lambda coeffecients lambda_3 : lambda_3 : - theta_f : initial angle of interaction (rad). + theta_i : initial angle of interaction (rad). theta_f : final angle of the interaction (rad). sampling_rate : sampling rate of AWG in (Hz) @@ -68,6 +68,7 @@ def martinis_flux_pulse(length: float, theta_wave += lambda_1 * (1 - np.cos(2 * np.pi * taus / rounded_length)) theta_wave += lambda_2 * (1 - np.cos(4 * np.pi * taus / rounded_length)) theta_wave += lambda_3 * (1 - np.cos(6 * np.pi * taus / rounded_length)) + theta_wave += lambda_4 * (1 - np.cos(8 * np.pi * taus / rounded_length)) # Clip wave to [theta_i, pi] to avoid poles in the wave expressed in freq theta_wave_clipped = np.clip(theta_wave, theta_i, np.pi-.01) @@ -78,7 +79,7 @@ def martinis_flux_pulse(length: float, # 3. Transform from proper time τ to real time t using interpolation t = np.array([np.trapz(np.sin(theta_wave_clipped)[:i+1], - dx=1/(10*sampling_rate)) + dx=1/(fine_sampling_factor*sampling_rate)) for i in range(len(theta_wave_clipped))]) # Interpolate pulse at physical sampling distance @@ -120,9 +121,9 @@ def theta_to_eps(theta: float, g: float): args: θ: interaction angle (radian) - ε: detuning - returns: g: coupling strength + returns: + ε: detuning """ eps = 2 * g / np.tan(theta) return eps From 47582b04614e59ff8682945710b6fbb28ac9c2a9 Mon Sep 17 00:00:00 2001 From: Adriaan Date: Thu, 30 Aug 2018 13:21:37 +0200 Subject: [PATCH 24/26] cleaned up some and added more tests --- .../meta_instrument/LutMans/flux_lutman.py | 2 +- .../waveform_control_CC/waveform.py | 122 +----------------- pycqed/tests/test_flux_lutman.py | 12 +- pycqed/tests/test_waveforms_flux.py | 14 +- 4 files changed, 24 insertions(+), 126 deletions(-) diff --git a/pycqed/instrument_drivers/meta_instrument/LutMans/flux_lutman.py b/pycqed/instrument_drivers/meta_instrument/LutMans/flux_lutman.py index 50c92ce6d8..e32cb290e9 100644 --- a/pycqed/instrument_drivers/meta_instrument/LutMans/flux_lutman.py +++ b/pycqed/instrument_drivers/meta_instrument/LutMans/flux_lutman.py @@ -232,7 +232,7 @@ def get_polycoeffs_state(self, state: str): polycoeffs += self.q_polycoeffs_freq_01_det() polycoeffs[2] += self.q_freq_01() + self.q_freq_10() else: - raise NotImplementedError('State {} not recognized'.format(state)) + raise ValueError('State {} not recognized'.format(state)) return polycoeffs def calc_amp_to_freq(self, amp: float, state: str='01'): diff --git a/pycqed/measurement/waveform_control_CC/waveform.py b/pycqed/measurement/waveform_control_CC/waveform.py index cd7d3e7767..01ea1e85e3 100644 --- a/pycqed/measurement/waveform_control_CC/waveform.py +++ b/pycqed/measurement/waveform_control_CC/waveform.py @@ -300,8 +300,9 @@ def mod_gauss_VSM(amp, sigma_length, f_modulation, axis='x', phase=0, Q_phase_delay=Q_phase_delay) return G_I_mod, G_Q_mod, D_I_mod, D_Q_mod + def mod_square(amp, length, f_modulation, phase=0, - motzoi=0, sampling_rate=1e9): + motzoi=0, sampling_rate=1e9): ''' Simple modulated gauss pulse. All inputs are in s and Hz. ''' @@ -328,122 +329,3 @@ def mod_square_VSM(amp_G, amp_D, length, f_modulation, D_I_mod, D_Q_mod = mod_pulse(D_I, D_Q, f_modulation, sampling_rate=sampling_rate) return G_I_mod, G_Q_mod, D_I_mod, D_Q_mod - - -##################################################### -# Flux pulses -##################################################### - - -def martinis_flux_pulse(length: float, lambda_2: float, lambda_3: float, - theta_f: float, - f_q0: float, - f_q1: float, - anharmonicity_q0: float, - sampling_rate: float =1e9): - """ - Returns the pulse specified by Martinis and Geller - Phys. Rev. A 90 022307 (2014). - - \theta = \theta _0 + \sum_{n=1}^\infty (\lambda_n*(1-\cos(n*2*pi*t/t_p))/2 - - note that the lambda coefficients are rescaled to ensure that the center - of the pulse has a value corresponding to theta_f. - - length (float) lenght of the waveform (s) - lambda_2 - lambda_3 - - theta_f (float) final angle of the interaction in degrees. - Determines the Voltage for the center of the waveform. - - f_q0 (float) frequency of the fluxed qubit (Hz). - f_q1 (float) frequency of the lower, not fluxed qubit (Hz) - anharmonicity_q0 (float) anharmonicity of the fluxed qubit (Hz) - (negative for conventional transmon) - J2 (float) coupling between 11-02 (Hz), - approx sqrt(2) J1 (the 10-01 coupling). - - sampling_rate (float) sampling rate of the AWG (Hz) - - """ - raise NotImplementedError("This class is deprecated") -# # Define number of samples and time points -# logging.warning("Deprecated, use waveforms_flux.martinis_flux_pulse") - -# # Pulse is generated at a denser grid to allow for good interpolation -# # N.B. Not clear why interpolation is needed at all... -MAR July 2018 -# fine_sampling_factor = 1 # 10 -# nr_samples = int(np.round((length)*sampling_rate * fine_sampling_factor)) -# rounded_length = nr_samples/(fine_sampling_factor * sampling_rate) -# tau_step = 1/(fine_sampling_factor * sampling_rate) # denser points -# # tau is a virtual time/proper time -# taus = np.arange(0, rounded_length-tau_step/2, tau_step) -# # -tau_step/2 is to make sure final pt is excluded - -# # Derived parameters -# f_initial = f_q0 -# f_interaction = f_q1 - anharmonicity_q0 -# detuning_initial = f_q0 + anharmonicity_q0 - f_q1 -# theta_i = np.arctan(2*J2 / detuning_initial) - -# # Converting angle to radians as that is used under the hood -# theta_f = 2*np.pi*theta_f/360 -# if theta_f < theta_i: -# raise ValueError( -# 'theta_f ({:.2f} deg) < theta_i ({:.2f} deg):'.format( -# theta_f/(2*np.pi)*360, theta_i/(2*np.pi)*360) -# + 'final coupling weaker than initial coupling') - -# # lambda_1 is scaled such that the final ("center") angle is theta_f -# lambda_1 = (theta_f - theta_i) / (2 + 2 * lambda_3) - -# # Calculate the wave -# theta_wave = np.ones(nr_samples) * theta_i -# theta_wave += lambda_1 * (1 - np.cos(2 * np.pi * taus / rounded_length)) -# theta_wave += (lambda_1 * lambda_2 * -# (1 - np.cos(4 * np.pi * taus / rounded_length))) -# theta_wave += (lambda_1 * lambda_3 * -# (1 - np.cos(6 * np.pi * taus / rounded_length))) - -# # Clip wave to [theta_i, pi] to avoid poles in the wave expressed in freq -# theta_wave_clipped = np.clip(theta_wave, theta_i, np.pi-.01) -# if not np.array_equal(theta_wave, theta_wave_clipped): -# logging.warning( -# 'Martinis flux wave form has been clipped to [{}, 180 deg]' -# .format(theta_i)) - -# # Transform from proper time to real time -# t = np.array([np.trapz(np.sin(theta_wave_clipped)[:i+1], dx=1/(10*sampling_rate)) -# for i in range(len(theta_wave_clipped))]) - -# # Interpolate pulse at physical sampling distance -# t_samples = np.arange(0, length, 1/sampling_rate) -# # Scaling factor for time-axis to get correct pulse length again -# scale = t[-1]/t_samples[-1] -# interp_wave = scipy.interpolate.interp1d( -# t/scale, theta_wave_clipped, bounds_error=False, -# fill_value='extrapolate')(t_samples) - -# # Return in the specified units -# if return_unit == 'theta': -# # Theta is returned in radians here -# return np.nan_to_num(interp_wave) - -# # Convert to detuning between f_20 and f_11 -# # It is equal to detuning between f_11 and interaction point -# delta_f_wave = 2 * J2 / np.tan(interp_wave) - -# # Convert to parametrization of f_01 -# f_01_wave = delta_f_wave + f_interaction - -# return np.nan_to_num(f_01_wave) -# # why sometimes the last sample is nan is not known, -# # but we will surely figure it out someday. -# # (Brian and Adriaan, 14.11.2017) -# # This may be caused by the fill_value of the interp_wave (~30 lines up) -# # that was set to 0 instead of extrapolate. This caused -# # the np.tan(interp_wave) to divide by zero. (MAR 10-05-2018) -# ############################################################################ -# # -# ############################################################################ diff --git a/pycqed/tests/test_flux_lutman.py b/pycqed/tests/test_flux_lutman.py index f6663fddfc..ab7c4ad2dd 100644 --- a/pycqed/tests/test_flux_lutman.py +++ b/pycqed/tests/test_flux_lutman.py @@ -194,6 +194,10 @@ def test_double_sided_cz_waveform(self): np.testing.assert_raises(AssertionError, np.testing.assert_array_equal, czA, czC) + def test_calc_amp_to_freq_unknown_state(self): + with self.assertRaises(ValueError): + self.fluxlutman.get_polycoeffs_state('22') + def test_calc_amp_to_freq_01(self): """ Tests methods used to determine energy levels and their conversion @@ -303,7 +307,10 @@ def test_custom_wf(self): def test_generate_standard_flux_waveforms(self): self.fluxlutman.generate_standard_waveforms() - def test_upload_and_distort(self): + def test_load_waveforms_onto_AWG_lookuptable(self): + self.fluxlutman.cfg_distort(True) + self.fluxlutman.load_waveforms_onto_AWG_lookuptable() + self.fluxlutman.cfg_distort(False) self.fluxlutman.load_waveforms_onto_AWG_lookuptable() def test_generate_composite(self): @@ -373,6 +380,9 @@ def test_length_ratio(self): np.testing.assert_almost_equal(integral, 0) + def test_render_wave(self): + self.fluxlutman.render_wave('cz_z', time_units='lut_index') + self.fluxlutman.render_wave('cz_z', time_units='s') @classmethod diff --git a/pycqed/tests/test_waveforms_flux.py b/pycqed/tests/test_waveforms_flux.py index 7eaeb1dd62..a2fda34cd2 100644 --- a/pycqed/tests/test_waveforms_flux.py +++ b/pycqed/tests/test_waveforms_flux.py @@ -39,7 +39,6 @@ def test_eps_theta_conversion_arrays(self): eps_inv = wfl.theta_to_eps(thetas, g=J2) np.testing.assert_array_almost_equal(eps, eps_inv) - def test_martinis_flux_pulse_theta_bounds(self): """ Tests that the constraint setting theta_f and theta_i is working @@ -54,9 +53,16 @@ def test_martinis_flux_pulse_theta_bounds(self): thetas = wfl.martinis_flux_pulse( 35e-9, theta_i=theta_i, theta_f=theta_f, lambda_2=lambda_2, lambda_3=lambda_3, sampling_rate=1e9) - np.testing.assert_almost_equal(thetas[0], theta_i, decimal=3) - np.testing.assert_almost_equal(thetas[-1], theta_i, decimal=2) + np.testing.assert_almost_equal( + thetas[0], theta_i, decimal=3) + np.testing.assert_almost_equal( + thetas[-1], theta_i, decimal=2) np.testing.assert_almost_equal( thetas[len(thetas)//2], theta_f, decimal=3) - + with self.assertRaises(ValueError): + theta_i = np.deg2rad(40) + theta_f = np.deg2rad(30) + thetas = wfl.martinis_flux_pulse( + 35e-9, theta_i=theta_i, theta_f=theta_f, + lambda_2=lambda_2, lambda_3=lambda_3, sampling_rate=1e9) From ab2b2914cb9bfddc36d5e11dd137eeb845ad8666 Mon Sep 17 00:00:00 2001 From: Adriaan Date: Thu, 30 Aug 2018 13:34:57 +0200 Subject: [PATCH 25/26] removed martinis pulse from conventional tek sequences --- .../waveform_control/pulse_library.py | 74 +------------------ 1 file changed, 1 insertion(+), 73 deletions(-) diff --git a/pycqed/measurement/waveform_control/pulse_library.py b/pycqed/measurement/waveform_control/pulse_library.py index 00550ae517..22323331d3 100644 --- a/pycqed/measurement/waveform_control/pulse_library.py +++ b/pycqed/measurement/waveform_control/pulse_library.py @@ -3,7 +3,6 @@ Library containing pulse shapes. ''' from pycqed.measurement.waveform_control.pulse import Pulse, apply_modulation -from pycqed.measurement.waveform_control_CC.waveform import martinis_flux_pulse from pycqed.utilities.general import int_to_bin class MW_IQmod_pulse(Pulse): @@ -341,78 +340,7 @@ def chan_wf(self, chan, tvals): class MartinisFluxPulse(Pulse): - - def __init__(self, channel=None, channels=None, name='Martinis flux pulse', - **kw): - Pulse.__init__(self, name) - if channel is None and channels is None: - raise ValueError('Must specify either channel or channels') - elif channels is None: - self.channel = channel # this is just for convenience, internally - # this is the part the sequencer element wants to communicate with - self.channels.append(channel) - else: - self.channels = channels - - self.amplitude = kw.pop('amplitude', 1) - - self.length = kw.pop('length', None) - self.lambda_coeffs = kw.pop('lambda_coeffs', None) - - self.theta_f = kw.pop('theta_f', None) - self.g2 = kw.pop('g2', None) - self.E_c = kw.pop('E_c', None) - self.f_bus = kw.pop('f_bus', None) - self.f_01_max = kw.pop('f_01_max', None) - self.dac_flux_coefficient = kw.pop('dac_flux_coefficient', None) - - self.kernel_path = kw.get('kernel_path', None) - if self.kernel_path is not None: - kernelvec = np.loadtxt(self.kernel_path) - self.kernel = np.zeros((len(kernelvec), len(kernelvec))) - for i in range(len(kernelvec)): - for j in range(len(kernelvec)): - self.kernel[i, j] = kernelvec[i-j] - del(kernelvec) - else: - ValueError('Must specify kernel path') - - def __call__(self, **kw): - self.amplitude = kw.pop('amplitude', self.amplitude) - - self.pulse_buffer = kw.pop( - 'pulse_buffer', self.pulse_buffer) - - self.length = kw.pop( - 'length', self.length) - self.lambda_coeffs = kw.pop('lambda_coeffs', self.lambda_coeffs) - self.theta_f = kw.pop('theta_f', self.theta_f) - self.g2 = kw.pop('g2', self.g2) - self.E_c = kw.pop('E_c', self.E_c) - self.f_bus = kw.pop('f_bus', self.f_bus) - self.f_01_max = kw.pop('f_01_max', self.f_01_max) - self.dac_flux_coefficient = kw.pop( - 'dac_flux_coefficient', self.dac_flux_coefficient) - - self.channel = kw.pop('channel', self.channel) - self.channels = kw.pop('channels', self.channels) - self.channels.append(self.channel) - return self - - def chan_wf(self, chan, tvals): - t = tvals - tvals[0] - martinis_pulse = martinis_flux_pulse( - length=self.length, - lambda_coeffs=self.lambda_coeffs, - theta_f=self.theta_f, g2=self.g2, - E_c=self.E_c, f_bus=self.f_bus, - f_01_max=self.f_01_max, - dac_flux_coefficient=self.dac_flux_coefficient, - return_unit='V') - - # amplitude is not used because of parameterization but - # we use the sign of the amplitude to set the flux compensation - return martinis_pulse*np.sign(self.amplitude) + raise NotImplementedError() class QWG_Codeword(Pulse): From 473feabc6bceeb006c55e4f38d7f3dab16ed6d75 Mon Sep 17 00:00:00 2001 From: Adriaan Date: Thu, 30 Aug 2018 13:47:22 +0200 Subject: [PATCH 26/26] ... --- pycqed/measurement/waveform_control/pulse_library.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pycqed/measurement/waveform_control/pulse_library.py b/pycqed/measurement/waveform_control/pulse_library.py index 22323331d3..51ee39da8b 100644 --- a/pycqed/measurement/waveform_control/pulse_library.py +++ b/pycqed/measurement/waveform_control/pulse_library.py @@ -340,7 +340,7 @@ def chan_wf(self, chan, tvals): class MartinisFluxPulse(Pulse): - raise NotImplementedError() + pass class QWG_Codeword(Pulse):