22import matplotlib .pylab as pl
33import matplotlib .pyplot as plt
44from matplotlib .colors import LinearSegmentedColormap
5+ from collections import OrderedDict
56import numpy as np
67import pycqed .analysis_v2 .base_analysis as ba
78from pycqed .analysis .analysis_toolbox import get_datafilepath_from_timestamp
@@ -257,6 +258,7 @@ def __init__(
257258 do_fitting : bool = False ,
258259 save_qois : bool = True ,
259260 auto = True ,
261+ device = None ,
260262 qubits : list = None ,
261263 times : list = None ,
262264 artificial_detuning : float = None
@@ -266,6 +268,7 @@ def __init__(
266268 t_start = ts
267269 )
268270
271+ self .device = device
269272 self .qubits = qubits
270273 self .times = times
271274 if artificial_detuning is None :
@@ -286,9 +289,11 @@ def extract_data(self):
286289 for i , q in enumerate (self .qubits ):
287290 self .raw_data_dict ['{}_data' .format (q )] = data ['data' ][:,i + 1 ]
288291 self .raw_data_dict ['{}_times' .format (q )] = self .times [i ]
289- param_spec_old_freq = {'{}_freq_old' .format (q ): ('Instrument settings/{}' .format (q ), 'attr:freq_qubit' )}
290- old_freq = h5d .extract_pars_from_datafile (data_fp , param_spec_old_freq )
291- self .raw_data_dict ['{}_freq_old' .format (q )] = float (old_freq ['{}_freq_old' .format (q )])
292+
293+ qubit_object = self .device .find_instrument (q )
294+ old_freq = qubit_object .freq_qubit ()
295+
296+ self .raw_data_dict ['{}_freq_old' .format (q )] = old_freq
292297 self .raw_data_dict ['folder' ] = os .path .dirname (data_fp )
293298
294299 def process_data (self ):
@@ -719,57 +724,241 @@ def process_data(self):
719724 ### fit to normalized data ###
720725 x = number_flips [:- 4 ]
721726 y = self .proc_data_dict ['{}_nor_data' .format (q )][0 :- 4 ]
722-
723- ### cos fit ###
724- cos_fit_mod = fit_mods .CosModel
725- params = cos_fit_mod .guess (cos_fit_mod ,data = y ,t = x )
726- cos_mod = lmfit .Model (fit_mods .CosFunc )
727- fit_res_cos = cos_mod .fit (data = y ,t = x ,params = params )
728-
729- t = np .linspace (x [0 ],x [- 1 ],200 )
730- cos_fit = fit_mods .CosFunc (t = t ,amplitude = fit_res_cos .best_values ['amplitude' ],
731- frequency = fit_res_cos .best_values ['frequency' ],
732- phase = fit_res_cos .best_values ['phase' ],
733- offset = fit_res_cos .best_values ['offset' ])
734- self .proc_data_dict ['{}_cos_fit_data' .format (q )] = cos_fit
735- self .proc_data_dict ['{}_cos_fit_res' .format (q )] = fit_res_cos
736- self .proc_data_dict ['quantities_of_interest' ][q ]['cos_fit' ] = fit_res_cos .best_values
737-
738-
739-
740- ### line fit ###
741- poly_mod = lmfit .models .PolynomialModel (degree = 1 )
742- c0_guess = x [0 ]
743- c1_guess = (y [- 1 ]- y [0 ])/ (x [- 1 ]- x [0 ])
744- poly_mod .set_param_hint ('c0' ,value = c0_guess ,vary = True )
745- poly_mod .set_param_hint ('c1' ,value = c1_guess ,vary = True )
746- poly_mod .set_param_hint ('frequency' , expr = '-c1/(2*pi)' )
747- params = poly_mod .make_params ()
748- fit_res_line = poly_mod .fit (data = y ,x = x ,params = params )
749- self .proc_data_dict ['{}_line_fit_data' .format (q )] = fit_res_line .best_fit
750- self .proc_data_dict ['{}_line_fit_res' .format (q )] = fit_res_line
751- self .proc_data_dict ['quantities_of_interest' ][q ]['line_fit' ] = fit_res_line .best_values
752- ### calculating scale factors###
753- sf_cos = (1 + fit_res_cos .params ['frequency' ])** 2
754- phase = np .rad2deg (fit_res_cos .params ['phase' ])% 360
755- if phase > 180 :
756- sf_cos = 1 / sf_cos
727+
728+ self .prepare_fitting (x = x , y = y )
729+ self .run_fitting ()
730+
731+ self .proc_data_dict ['{}_cos_fit_data' .format (q )] = self .fit_dicts ["cos_fit" ]["fit_res" ].best_fit
732+ self .proc_data_dict ['{}_cos_fit_res' .format (q )] = self .fit_dicts ["cos_fit" ]["fit_res" ]
733+ self .proc_data_dict ['quantities_of_interest' ][q ]['cos_fit' ] = self .fit_dicts ["cos_fit" ]["fit_res" ].best_values
734+
735+ self .proc_data_dict ['{}_line_fit_data' .format (q )] = self .fit_dicts ["line_fit" ]["fit_res" ].best_fit
736+ self .proc_data_dict ['{}_line_fit_res' .format (q )] = self .fit_dicts ["line_fit" ]["fit_res" ]
737+ self .proc_data_dict ['quantities_of_interest' ][q ]['line_fit' ] = self .fit_dicts ["line_fit" ]["fit_res" ].best_values
738+
739+ sf_cos = self .get_scale_factor_cos ()
757740 self .proc_data_dict ['quantities_of_interest' ][q ]['cos_fit' ]['sf' ] = sf_cos
758-
759- sf_line = ( 1 + fit_res_line . params [ 'frequency' ]) ** 2
741+
742+ sf_line = self . get_scale_factor_line ()
760743 self .proc_data_dict ['quantities_of_interest' ][q ]['line_fit' ]['sf' ] = sf_line
761744 ### choose correct sf ###
762745 msg = 'Scale factor based on '
763- if fit_res_line . bic < fit_res_cos .bic :
746+ if self . fit_dicts [ "line_fit" ][ "fit_res" ]. bic < self . fit_dicts [ "cos_fit" ][ "fit_res" ] .bic :
764747 scale_factor = sf_line
765748 msg += 'line fit\n '
766749 else :
767750 scale_factor = sf_cos
768751 msg += 'cos fit\n '
769752 msg += 'line fit: {:.4f}\n ' .format (sf_line )
770- msg += 'cos fit: {:.4f}' .format (scale_factor )
753+ msg += 'cos fit: {:.4f}' .format (sf_cos )
771754 self .proc_data_dict ['{}_scale_factor' .format (q )] = scale_factor
772755 self .proc_data_dict ['{}_scale_factor_msg' .format (q )] = msg
756+
757+ def prepare_fitting (self , x , y ):
758+ self .fit_dicts = OrderedDict ()
759+
760+
761+ # Sinusoidal fit
762+ # --------------
763+ # Even though we expect an exponentially damped oscillation we use
764+ # a simple cosine as this gives more reliable fitting and we are only
765+ # interested in extracting the oscillation frequency.
766+ cos_mod = lmfit .Model (fit_mods .CosFunc )
767+
768+ guess_pars = fit_mods .Cos_guess (
769+ model = cos_mod ,
770+ t = x ,
771+ data = y ,
772+ )
773+
774+ # constrain the amplitude to positive and close to 0.5
775+ guess_pars ["amplitude" ].value = 0.45
776+ guess_pars ["amplitude" ].vary = True
777+ guess_pars ["amplitude" ].min = 0.4
778+ guess_pars ["amplitude" ].max = 0.5
779+
780+ # force the offset to 0.5
781+ guess_pars ["offset" ].value = 0.5
782+ guess_pars ["offset" ].vary = False
783+
784+
785+ guess_pars ["phase" ].vary = True
786+
787+ guess_pars ["frequency" ].vary = True
788+
789+ self .fit_dicts ["cos_fit" ] = {
790+ "fit_fn" : fit_mods .CosFunc ,
791+ "fit_xvals" : {"t" : x },
792+ "fit_yvals" : {"data" : y },
793+ "guess_pars" : guess_pars ,
794+ }
795+
796+ # Linear fit
797+ #-----------
798+ # In the case that the amplitude is close to perfect, we will not see a full period of oscillation.
799+ # We resort to a linear fit to extract the oscillation frequency from the slop of the best fit
800+ poly_mod = lmfit .models .PolynomialModel (degree = 1 )
801+ # for historical reasons, the slope 'c1' is here converted to a frequency.
802+ poly_mod .set_param_hint ("frequency" , expr = "-c1/(2*pi)" )
803+ guess_pars = poly_mod .guess (
804+ x = x ,
805+ data = y ,
806+ )
807+ # Constrain the offset close to nominal 0.5
808+ guess_pars ["c0" ].value = 0.5
809+ guess_pars ["c0" ].vary = True
810+ guess_pars ["c0" ].min = 0.45
811+ guess_pars ["c0" ].max = 0.55
812+
813+
814+ self .fit_dicts ["line_fit" ] = {
815+ "model" : poly_mod ,
816+ "fit_xvals" : {"x" : x },
817+ "fit_yvals" : {"data" : y },
818+ "guess_pars" : guess_pars ,
819+ }
820+
821+ def get_scale_factor_cos (self ):
822+
823+ # extract the frequency
824+ frequency = self .fit_dicts ["cos_fit" ]["fit_res" ].params ["frequency" ]
825+
826+ # extract phase modulo 2pi
827+ phase = np .mod (self .fit_dicts ["cos_fit" ]["fit_res" ].params ["phase" ],2 * np .pi )
828+
829+ # resolve ambiguity in the fit, making sign of frequency meaningful.
830+ frequency *= np .sign (phase - np .pi )
831+
832+ # calculate the scale factor
833+ scale_factor = 1 / (1 + 2 * frequency )
834+
835+ return scale_factor
836+
837+ def get_scale_factor_line (self ):
838+
839+ # extract the slope
840+ frequency = self .fit_dicts ["line_fit" ]["fit_res" ].params ["frequency" ]
841+
842+
843+ scale_factor = 1 / (1 - 4 * frequency )
844+ # no phase sign check is needed here as this is contained in the
845+ # sign of the coefficient
846+
847+ return scale_factor
848+
849+ def run_fitting (self ):
850+ '''
851+ This function does the fitting and saving of the parameters
852+ based on the fit_dict options.
853+
854+
855+ There are two ways of fitting, specified in fit_dict['fitting_type']
856+
857+ - Using the model-fit procedure of lmfit, this is the default
858+ fit_dict['fitting_type'] = 'model'
859+ - Using the minimizer routine of lmfit, this needs to be specified by
860+ fit_dict['fitting_type'] = 'minimize'
861+
862+
863+ Initial guesses can be passed on in several different ways.
864+
865+ - as fit_dict['guess_pars'] directly as the model with guess parameters,
866+ that needs to be made in the respective analysis and passed on
867+ like fit_dict['guess_pars'] = model.make_params()
868+ If this argument is passed on, no other guesses will be performed.
869+ This is not implemented yet for the 'minimize' fitting type.
870+
871+ - as a guess function that will be run. This can be passed explicitly as
872+ fit_dict['fit_guess_fn']
873+ or also by giving the model specified in fit_dict['model'] an
874+ argument .guess
875+ The guess function can be given parameters in
876+ fit_dict['guessfn_pars']
877+
878+ - as fit_dict['guess_dict'], which is a dictionary containing the guess
879+ parameters. These guess parameters will converted into the parameter
880+ objects required by either model fit or minimize.
881+
882+ '''
883+ from pycqed .analysis_v2 .base_analysis import _complex_residual_function
884+
885+ self .fit_res = {}
886+ for key , fit_dict in self .fit_dicts .items ():
887+ guess_dict = fit_dict .get ('guess_dict' , None )
888+ guess_pars = fit_dict .get ('guess_pars' , None )
889+ guessfn_pars = fit_dict .get ('guessfn_pars' , {})
890+ fit_yvals = fit_dict ['fit_yvals' ]
891+ fit_xvals = fit_dict ['fit_xvals' ]
892+
893+ fitting_type = fit_dict .get ('fitting_type' , 'model' )
894+
895+ model = fit_dict .get ('model' , None )
896+ if model is None :
897+ fit_fn = fit_dict .get ('fit_fn' , None )
898+ model = fit_dict .get ('model' , lmfit .Model (fit_fn ))
899+ fit_guess_fn = fit_dict .get ('fit_guess_fn' , None )
900+ if fit_guess_fn is None :
901+ if fitting_type == 'model' and fit_dict .get ('fit_guess' , True ):
902+ fit_guess_fn = model .guess
903+
904+ if guess_pars is None : # if you pass on guess_pars, immediately go to the fitting
905+ if fit_guess_fn is not None : # Run the guess funtions here
906+ if fitting_type == 'minimize' :
907+ guess_pars = fit_guess_fn (** fit_yvals , ** fit_xvals , ** guessfn_pars )
908+ params = lmfit .Parameters ()
909+ for gd_key , val in guess_pars .items ():
910+ params .add (gd_key )
911+ for attr , attr_val in val .items ():
912+ setattr (params [gd_key ], attr , attr_val )
913+
914+ # a fit function should return lmfit parameter objects
915+ # but can also work by returning a dictionary of guesses
916+ elif fitting_type == 'model' :
917+ guess_pars = fit_guess_fn (** fit_yvals , ** fit_xvals , ** guessfn_pars )
918+ if not isinstance (guess_pars , lmfit .Parameters ):
919+ for gd_key , val in list (guess_pars .items ()):
920+ model .set_param_hint (gd_key , ** val )
921+ guess_pars = model .make_params ()
922+
923+ # A guess can also be specified as a dictionary.
924+ # additionally this can be used to overwrite values
925+ # from the guess functions.
926+ if guess_dict is not None :
927+ for gd_key , val in guess_dict .items ():
928+ for attr , attr_val in val .items ():
929+ # e.g. setattr(guess_pars['frequency'], 'value', 20e6)
930+ setattr (guess_pars [gd_key ], attr , attr_val )
931+ elif guess_dict is not None :
932+ if fitting_type == 'minimize' :
933+ params = lmfit .Parameters ()
934+ for gd_key , val in list (guess_dict .items ()):
935+ params .add (gd_key )
936+ for attr , attr_val in val .items ():
937+ setattr (params [gd_key ], attr , attr_val )
938+
939+ elif fitting_type == 'model' :
940+ for gd_key , val in list (guess_dict .items ()):
941+ model .set_param_hint (gd_key , ** val )
942+ guess_pars = model .make_params ()
943+ else :
944+ if fitting_type == 'minimize' :
945+ raise NotImplementedError (
946+ 'Conversion from guess_pars to params with lmfit.Parameters() needs to be implemented' )
947+ # TODO: write a method that converts the type model.make_params() to a lmfit.Parameters() object
948+ if fitting_type == 'model' : # Perform the fitting
949+ fit_dict ['fit_res' ] = model .fit (** fit_xvals , ** fit_yvals ,
950+ params = guess_pars )
951+ self .fit_res [key ] = fit_dict ['fit_res' ]
952+ elif fitting_type == 'minimize' : # Perform the fitting
953+
954+ fit_dict ['fit_res' ] = lmfit .minimize (fcn = _complex_residual_function ,
955+ params = params ,
956+ args = (fit_fn , fit_xvals , fit_yvals ))
957+ # save the initial params
958+ fit_dict ['fit_res' ].initial_params = params
959+ fit_dict ['fit_res' ].userkws = fit_xvals # save the x values
960+ fit_dict ['fit_res' ].fit_fn = fit_fn # save the fit function
961+ self .fit_res [key ] = fit_dict ['fit_res' ]
773962
774963 def prepare_plots (self ):
775964 for q in self .qubits :
@@ -800,12 +989,11 @@ def plot_Multi_flipping(qubit, data,title, ax=None, **kwargs):
800989
801990 ax .plot (number_flips ,nor_data ,'-o' )
802991 ax .plot (number_flips [:- 4 ],fit_data_line ,'-' ,label = 'line fit' )
803- ax .plot (t ,fit_data_cos ,'-' ,label = 'cosine fit' )
992+ ax .plot (number_flips [: - 4 ] ,fit_data_cos ,'-' ,label = 'cosine fit' )
804993 ax .legend ()
805994 ax .set (ylabel = r'$F$ $|1 \rangle$' )
806995 ax .set (xlabel = r'number of flips (#)' )
807996 ax .set_title (title )
808- 4444444
809997
810998class Multi_Motzoi_Analysis (ba .BaseDataAnalysis ):
811999 def __init__ (
0 commit comments