Skip to content

Commit

Permalink
current state of development, cryoscope still does not work
Browse files Browse the repository at this point in the history
  • Loading branch information
msamiotis committed May 4, 2024
1 parent 80d4dbf commit 2883353
Show file tree
Hide file tree
Showing 6 changed files with 5,155 additions and 16 deletions.
14 changes: 14 additions & 0 deletions pycqed/instrument_drivers/library/DIO.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,20 @@ def calibrate(sender: CalInterface = None,
[27, 26, 25]
],
"trigger_bits": [31,15]
},
"cryoscope_flux": { # alias for "awg8-flux"
# NB: please note that internally one HDQWG AWG unit handles 2 channels, which requires special handling of the waveforms
"control_bits": [
[2, 1, 0],
[5, 4, 3],
[8, 7, 6],
[11, 10, 9],
[18, 17, 16],
[21, 20, 19],
[24, 23, 22],
[27, 26, 25]
],
"trigger_bits": [31,15]
}
}

Expand Down
164 changes: 151 additions & 13 deletions pycqed/instrument_drivers/meta_instrument/HAL_Device.py
Original file line number Diff line number Diff line change
Expand Up @@ -2681,19 +2681,157 @@ def measure_cryoscope(
filter_dict = {'params': fltr,
'model': 'exponential', 'real-time': True }
if fltr['amp'] > 0:
print('Amplitude of filter is positive (overfitting).')
print('Filter not updated.')
return True
else:
# Check wich is the first empty exponential filter
for i in range(4):
_fltr = lin_dist_kern.get(f'filter_model_0{i}')
if _fltr == {}:
lin_dist_kern.set(f'filter_model_0{i}', filter_dict)
return True
else:
print(f'filter_model_0{i} used.')
print('All exponential filter tabs are full. Filter not updated.')
log.warning('Amplitude of filter is positive (overfitting).')
# Check wich is the first empty exponential filter
for i in range(4):
_fltr = lin_dist_kern.get(f'filter_model_0{i}')
if _fltr == {}:
lin_dist_kern.set(f'filter_model_0{i}', filter_dict)
return True
else:
print(f'filter_model_0{i} used.')
print('All exponential filter tabs are full. Filter not updated.')
return True

def measure_cryoscope_fast(
self,
qubits,
times,
MC=None,
nested_MC=None,
double_projections: bool = False,
wait_time_flux: int = 0,
update_FIRs: bool=False,
update_IIRs: bool=False,
waveform_name: str = "square",
max_delay=None,
twoq_pair=[2, 0],
disable_metadata: bool = False,
init_buffer=0,
analyze: bool = True,
prepare_for_timedomain: bool = True,
):
"""
Performs a cryoscope experiment to measure the shape of a flux pulse.
Args:
qubits (list):
a list of two target qubits
times (array):
array of measurment times
label (str):
used to label the experiment
waveform_name (str {"square", "custom_wf"}) :
defines the name of the waveform used in the
cryoscope. Valid values are either "square" or "custom_wf"
max_delay {float, "auto"} :
determines the delay in the pulse sequence
if set to "auto" this is automatically set to the largest
pulse duration for the cryoscope.
prepare_for_timedomain (bool):
calls self.prepare_for_timedomain on start
"""
assert self.ro_acq_weight_type() == 'optimal'
assert not (update_FIRs and update_IIRs), 'Can only either update IIRs or FIRs'
if update_FIRs or update_IIRs:
assert analyze==True, 'Analsis has to run for filter update'
if MC is None:
MC = self.instr_MC.get_instr()
if nested_MC is None:
nested_MC = self.instr_nested_MC.get_instr()
for q in qubits:
assert q in self.qubits()
Q_idxs = [self.find_instrument(q).cfg_qubit_nr() for q in qubits]
if prepare_for_timedomain:
self.prepare_for_timedomain(qubits=qubits)
if max_delay is None:
max_delay = 0
else:
max_delay = np.max(times) + 40e-9
Fl_lutmans = [self.find_instrument(q).instr_LutMan_Flux.get_instr() \
for q in qubits]

if 0 in Fl_lutmans[0].LutMap():
Fl_lutmans[0].LutMap().pop(0)
square_pulse_gates = []
for entry in range(len(Fl_lutmans[0].LutMap())):
square_pulse_gates.append(Fl_lutmans[0].LutMap()[entry+1]['name'])

p = mqo.Cryoscope_fast(
qubit_idxs=Q_idxs,
flux_cw=square_pulse_gates,
twoq_pair=twoq_pair,
wait_time_flux=wait_time_flux,
platf_cfg=self.cfg_openql_platform_fn(),
cc=self.instr_CC.get_instr().name,
double_projections=double_projections,
)
self.instr_CC.get_instr().eqasm_program(p.filename)
self.instr_CC.get_instr().start()

s = swf.OpenQL_Sweep(
openql_program=p,
parameter_name='Time',
unit='s',
CCL=self.instr_CC.get_instr()
)
s.sweep_control = 'soft'

MC.set_sweep_function(s)
MC.set_sweep_points(times)

if double_projections:
# Cryoscope v2
values_per_point = 4
values_per_point_suffex = ["cos", "sin", "mcos", "msin"]
else:
# Cryoscope v1
values_per_point = 2
values_per_point_suffex = ["cos", "sin"]

d = self.get_int_avg_det(
qubits=qubits,
values_per_point=values_per_point,
values_per_point_suffex=values_per_point_suffex,
single_int_avg=True,
always_prepare=True
)
MC.set_detector_function(d)
label = 'Cryoscope_{}_amps'.format('_'.join(qubits))
MC.run(label,disable_snapshot_metadata=disable_metadata)
# Run analysis
if analyze:
a = ma2.cv2.multi_qubit_cryoscope_analysis(
label='Cryoscope',
update_IIRs=update_IIRs,
update_FIRs=update_FIRs)
if update_FIRs:
for qubit, fltr in a.proc_data_dict['conv_filters'].items():
lin_dist_kern = self.find_instrument(f'lin_dist_kern_{qubit}')
filter_dict = {'params': {'weights': fltr},
'model': 'FIR', 'real-time': True }
lin_dist_kern.filter_model_04(filter_dict)
elif update_IIRs:
for qubit, fltr in a.proc_data_dict['exponential_filter'].items():
lin_dist_kern = self.find_instrument(f'lin_dist_kern_{qubit}')
filter_dict = {'params': fltr,
'model': 'exponential', 'real-time': True }
if fltr['amp'] > 0:
log.warning('Amplitude of filter is positive (overfitting).')
# Check wich is the first empty exponential filter
for i in range(4):
_fltr = lin_dist_kern.get(f'filter_model_0{i}')
if _fltr == {}:
lin_dist_kern.set(f'filter_model_0{i}', filter_dict)
return True
else:
print(f'filter_model_0{i} used.')
print('All exponential filter tabs are full. Filter not updated.')
return True

def measure_cryoscope_vs_amp(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,11 @@
7: {"name": "custom_wf", "type": "custom"},
}

# _def_lm = {
# 0: {"name": "i", "type": "idle"},
# }
# for pulse_label in range(144): # 480 comes from len(times = np.arange(0.0e-9, 200.0e-9, 1/2.4e9))
# _def_lm[pulse_label + 1] = {"name": f"square_{pulse_label + 1:03}", "type": "square"}

class Base_Flux_LutMan(Base_LutMan):
"""
Expand Down Expand Up @@ -142,6 +147,16 @@ def __init__(self, name, **kw):
def set_default_lutmap(self):
"""Set the default lutmap for standard microwave drive pulses."""
self.LutMap(_def_lm.copy())

def generate_cryoscope_lutmap(self, duration):
cryoscope_lutmap = {
0: {"name": "i", "type": "idle"},
}
times = np.arange(0.0e-9, duration, 1/2.4e9)
pulse_number = len(times)
for pulse_label in range(pulse_number):
cryoscope_lutmap[pulse_label + 1] = {"name": f"square_{pulse_label + 1:03}", "type": "square"}
self.LutMap(cryoscope_lutmap.copy())

def generate_standard_waveforms(self):
"""
Expand All @@ -165,6 +180,28 @@ def generate_standard_waveforms(self):
# The vcz pulse itself has all parameters necessary for the correction
self._wave_dict[wave_name] = self._gen_cz(which_gate=which_gate)

def generate_cryoscope_waveforms(self, duration):
"""
Generate all the standard waveforms and populates self._wave_dict
"""
self._wave_dict = {}
self._wave_dict["i"] = self._gen_i()

self.generate_cryoscope_lutmap(duration = duration)

times = np.arange(0.0e-9, 200.0e-9, 1/2.4e9)
loop_index = 0
self.cfg_max_wf_length(duration+40e-9)

for _, waveform in self.LutMap().items():
if waveform["name"] == 'i':
pass
else:
wave_name = waveform["name"]
self.sq_length(times[loop_index])
self._wave_dict[wave_name] = self._gen_square()
loop_index += 1

def generate_cz_waveforms(self):
"""
Generate CZ waveforms and populates self._wave_dict
Expand Down Expand Up @@ -813,6 +850,7 @@ def load_waveform_onto_AWG_lookuptable(
Loads a specific waveform to the AWG
"""

AWG = self.AWG.get_instr()
# Here we are ductyping to determine if the waveform name or the
# codeword was specified.
if type(wave_id) == str:
Expand All @@ -831,6 +869,13 @@ def load_waveform_onto_AWG_lookuptable(
self._wave_dict[waveform_name] = gen_wf_func(
which_gate=waveform_name[3:]
)
if AWG.cfg_codeword_protocol() == "cryoscope_flux":
if waveform_name == 'i':
waveform_name_string = 'i'
else:
waveform_name_string = 'square'
gen_wf_func = getattr(self, "_gen_{}".format(waveform_name_string))
self._wave_dict[waveform_name_string] = gen_wf_func()
else:
gen_wf_func = getattr(self, "_gen_{}".format(waveform_name))
self._wave_dict[waveform_name] = gen_wf_func()
Expand Down Expand Up @@ -869,6 +914,10 @@ def load_waveforms_onto_AWG_lookuptable(
"""

AWG = self.AWG.get_instr()
awg_ch = self.cfg_awg_channel()

AWG.cfg_codeword_protocol("cryoscope_flux")
AWG._get_cryoscope_waveform_table(awg_nr = awg_ch/2)

if stop_start:
AWG.stop()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@ def _add_extra_parameters(self):

self.add_parameter(
'cfg_codeword_protocol', initial_value='identical',
vals=validators.Enum('identical', 'microwave', 'novsm_microwave', 'flux'), docstring=(
vals=validators.Enum('identical', 'microwave', 'novsm_microwave', 'flux', 'cryoscope_flux'), docstring=(
'Used in the configure codeword method to determine what DIO'
' pins are used in for which AWG numbers.'),
parameter_class=ManualParameter)
Expand Down Expand Up @@ -376,6 +376,36 @@ def _get_waveform_table(self, awg_nr: int) -> list:
wf_table.append((zibase.gen_waveform_name(ch, dio_cw),
zibase.gen_waveform_name(ch+1, dio_cw)))
return wf_table

def _get_cryoscope_waveform_table(self, awg_nr: int) -> list:
"""
Returns the waveform table.
The waveform table determines the mapping of waveforms to DIO codewords.
The index of the table corresponds to the DIO codeword.
The entry is a tuple of waveform names.
Example:
["wave_ch7_cw000", "wave_ch8_cw000",
"wave_ch7_cw001", "wave_ch8_cw001",
"wave_ch7_cw002", "wave_ch8_cw002"]
The waveform table generated depends on the awg_nr and the codeword
protocol.
"""
ch = awg_nr*2
wf_table = []
if 'cryoscope_flux' in self.cfg_codeword_protocol():
for cw in range(self._num_codewords):
# 481 comes from len(times = np.arange(0.0e-9, 200.0e-9, 1/2.4e9))
# plus the idenity gate 'i'
wf_table.append((zibase.gen_waveform_name(ch, cw),
zibase.gen_waveform_name(ch+1, cw)))
else:
for dio_cw in range(self._num_codewords):
wf_table.append((zibase.gen_waveform_name(ch, dio_cw),
zibase.gen_waveform_name(ch+1, dio_cw)))
return wf_table

def _codeword_table_preamble(self, awg_nr):
"""
Expand All @@ -386,12 +416,14 @@ def _codeword_table_preamble(self, awg_nr):
program = ''

wf_table = self._get_waveform_table(awg_nr=awg_nr)
if self.cfg_codeword_protocol() == 'cryoscope_flux':
wf_table = self._get_cryoscope_waveform_table(awg_nr=awg_nr)
for dio_cw, (wf_l, wf_r) in enumerate(wf_table):
csvname_l = self.devname + '_' + wf_l
csvname_r = self.devname + '_' + wf_r

# FIXME: Unfortunately, 'static' here also refers to configuration required for flux HDAWG8
if self.cfg_sideband_mode() == 'static' or self.cfg_codeword_protocol() == 'flux':
if self.cfg_sideband_mode() == 'static' or self.cfg_codeword_protocol() == 'flux' or self.cfg_codeword_protocol() == 'cryoscope_flux':
# program += 'assignWaveIndex(\"{}\", \"{}\", {});\n'.format(
# csvname_l, csvname_r, dio_cw)
program += 'setWaveDIO({}, \"{}\", \"{}\");\n'.format(
Expand Down Expand Up @@ -468,7 +500,7 @@ def _configure_codeword_protocol(self):
self.set(param, 0)

# Set amp or direct mode
if self.cfg_codeword_protocol() == 'flux':
if self.cfg_codeword_protocol() == 'flux' or self.cfg_codeword_protocol() == 'cryoscope_flux':
# when doing flux pulses, set everything to amp mode
for ch in range(8):
self.set('sigouts_{}_direct'.format(ch), 0)
Expand Down
Loading

0 comments on commit 2883353

Please sign in to comment.