Skip to content

Commit 14ffda0

Browse files
committed
1QPar DAG works!
1 parent e50809e commit 14ffda0

File tree

5 files changed

+780
-245
lines changed

5 files changed

+780
-245
lines changed

pycqed/analysis_v2/multi_analysis.py

Lines changed: 232 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import matplotlib.pylab as pl
33
import matplotlib.pyplot as plt
44
from matplotlib.colors import LinearSegmentedColormap
5+
from collections import OrderedDict
56
import numpy as np
67
import pycqed.analysis_v2.base_analysis as ba
78
from 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

810998
class Multi_Motzoi_Analysis(ba.BaseDataAnalysis):
811999
def __init__(

0 commit comments

Comments
 (0)