diff --git a/core_tools/sweeps/Modulated_scans/__pycache__/DEMOD_tests.cpython-36.pyc b/core_tools/sweeps/Modulated_scans/__pycache__/DEMOD_tests.cpython-36.pyc deleted file mode 100644 index 90eda691eadb4195dd78ad233e84c973190e639a..0000000000000000000000000000000000000000 Binary files a/core_tools/sweeps/Modulated_scans/__pycache__/DEMOD_tests.cpython-36.pyc and /dev/null differ diff --git a/core_tools/sweeps/Modulated_scans/__pycache__/__init__.cpython-36.pyc b/core_tools/sweeps/Modulated_scans/__pycache__/__init__.cpython-36.pyc deleted file mode 100644 index 831f8c239cfe43ec222fe837d223d4138741ad99..0000000000000000000000000000000000000000 Binary files a/core_tools/sweeps/Modulated_scans/__pycache__/__init__.cpython-36.pyc and /dev/null differ diff --git a/core_tools/sweeps/Modulated_scans/__pycache__/mod_test.cpython-36.pyc b/core_tools/sweeps/Modulated_scans/__pycache__/mod_test.cpython-36.pyc deleted file mode 100644 index 5d0692147c08b2735e9306bc7f9fb7c78204c976..0000000000000000000000000000000000000000 Binary files a/core_tools/sweeps/Modulated_scans/__pycache__/mod_test.cpython-36.pyc and /dev/null differ diff --git a/core_tools/sweeps/__pycache__/__init__.cpython-37.opt-2.pyc b/core_tools/sweeps/__pycache__/__init__.cpython-37.opt-2.pyc deleted file mode 100644 index 650a11821c64ae38743818bdb25ae9b01d2f65f5..0000000000000000000000000000000000000000 Binary files a/core_tools/sweeps/__pycache__/__init__.cpython-37.opt-2.pyc and /dev/null differ diff --git a/core_tools/sweeps/__pycache__/__init__.cpython-37.pyc b/core_tools/sweeps/__pycache__/__init__.cpython-37.pyc deleted file mode 100644 index 29eb9bf6d3c33b76b1150636201cf6da20d80b84..0000000000000000000000000000000000000000 Binary files a/core_tools/sweeps/__pycache__/__init__.cpython-37.pyc and /dev/null differ diff --git a/core_tools/sweeps/__pycache__/pulse_lib_sweep.cpython-37.opt-2.pyc b/core_tools/sweeps/__pycache__/pulse_lib_sweep.cpython-37.opt-2.pyc deleted file mode 100644 index 5a9babac6b1d37f0cbeb18195eff2ec2fe9fb6de..0000000000000000000000000000000000000000 Binary files a/core_tools/sweeps/__pycache__/pulse_lib_sweep.cpython-37.opt-2.pyc and /dev/null differ diff --git a/core_tools/sweeps/__pycache__/pulse_lib_sweep.cpython-37.pyc b/core_tools/sweeps/__pycache__/pulse_lib_sweep.cpython-37.pyc deleted file mode 100644 index f93f2884ba64901205a20bd2845f59363058b6e0..0000000000000000000000000000000000000000 Binary files a/core_tools/sweeps/__pycache__/pulse_lib_sweep.cpython-37.pyc and /dev/null differ diff --git a/core_tools/sweeps/progressbar.py b/core_tools/sweeps/progressbar.py new file mode 100644 index 0000000000000000000000000000000000000000..47b13fd96876a92e2c0d022caba4066b38f51f6a --- /dev/null +++ b/core_tools/sweeps/progressbar.py @@ -0,0 +1,25 @@ +import tqdm + +class progress_bar(): + def __init__(self, n_tot): + self.bar = tqdm.tqdm(total = n_tot, unit='points', ncols=80) + self.n = 0 + + def __add__(self, n): + self.bar.update(n) + self.n += n + return self + + def close(self): + self.bar.close() + +if __name__ == '__main__': + import time + + p = progress_bar(50) + + for i in range(50): + time.sleep(0.01) + p += 1 + + p.close() \ No newline at end of file diff --git a/core_tools/sweeps/pulselib2qcodes.py b/core_tools/sweeps/pulselib2qcodes.py new file mode 100644 index 0000000000000000000000000000000000000000..6fabf80ab5665c749c10c4b8a881a5e8377c809f --- /dev/null +++ b/core_tools/sweeps/pulselib2qcodes.py @@ -0,0 +1,180 @@ +from core_tools.sweeps.sweep_utility import sweep_info +from qcodes import Parameter + +class PulseLibParameter(Parameter): + setpoints = None + flat_index = 0 + + def add_setpoints(self, setpoints, sequencer, lowest_level): + self.setpoints = setpoints + self.sequencer = sequencer + self.lowest_level = lowest_level + + def get_raw(self): + current_val = self.setpoints[self.flat_index%len(self.setpoints)] + + self.flat_index += 1 + if self.flat_index > np.prod(self.sequencer.shape): + self.flat_index = 0 + + return current_val + + def set_raw(self, value): + if self.lowest_level: + if self.flat_index == 0: + self.sequencer.upload(np.unravel_index(flat_index, self.sequencer.shape)) + + index = np.unravel_index(flat_index, self.shape) + self.sequencer.play(index) + + if flat_index < np.prod(self.shape) - 1: + self.sequencer.upload(np.unravel_index(flat_index+1, self.shape)) + + self.sequencer.uploader.wait_until_AWG_idle() + + ''' + @ sander, how can we make sure that a unused upload is removed when the garbage collector collects this? + (e.g. when a set is performed to reset parameters -- normally this does not happen, but user might accidentatly do this.) + ''' + +def pulselib_2_qcodes(awg_sequence): + ''' + convert pulse sequencer object in qcodes parameters that are usable in sweeps. + + Args: + awg_sequence (pulselib.sequencer.sequencer) : sequence object + + Returns: + set_param (list<PulseLibParameter>) : set paramters for the pulselib to be used in the sweep + ''' + set_param = list() + for i in range(len(awg_sequence.shape)): + param = PulseLibParameter(name = awg_sequence.labels[i].replace(" ", "_"), label=awg_sequence.labels[i], unit= awg_sequence.units[i]) + param.add_setpoints(awg_sequence.setpoints[i], awg_sequence, False) + set_param.append(sweep_info(param, n_points = len(awg_sequence.setpoints[i]))) + + set_param[0].param.lowest_level=True + + return set_param[::-1] + + + +if __name__ == '__main__': + + from qcodes import Parameter, MultiParameter, new_experiment + import numpy as np + import time + new_experiment("name", "testing") + + class test_AWG_sequence0D(object): + """docstring for test_AWG_sequence""" + def __init__(self): + super(test_AWG_sequence0D, self).__init__() + self.shape = (1, ) + self.uploader = uploader() + self.units = ("V",) + self.labels = ("y axis",) + self.setpoints = (np.linspace(20,50,1),) + def play(self, idx): + time.sleep(0.01) + pass + + + def upload(self, idx): + pass + class test_AWG_sequence1D(object): + """docstring for test_AWG_sequence""" + def __init__(self): + super(test_AWG_sequence1D, self).__init__() + self.shape = (50, ) + self.uploader = uploader() + self.units = ("V",) + self.labels = ("y axis",) + self.setpoints = (np.linspace(20,50,50),) + def play(self, idx): + time.sleep(0.01) + pass + + + def upload(self, idx): + pass + class test_AWG_sequence2D(object): + """docstring for test_AWG_sequence""" + def __init__(self): + super(test_AWG_sequence2D, self).__init__() + self.shape = (50, 50) + self.units = ("V", "V") + self.labels = ("x axis", "y axis") + self.setpoints = (np.linspace(20,50,50), np.linspace(50,125,50)) + self.uploader = uploader() + + def play(self, idx): + time.sleep(0.01) + pass + + + def upload(self, idx): + pass + class dummy_parameter(Parameter): + def __init__(self, name, label=None, unit=None): + + super().__init__(name=name, + instrument=None, + labels=( "digitzer_response"), + units=("unit1" )) + class dummy_multi_parameter(MultiParameter): + def __init__(self, name, label=None, unit=None): + + super().__init__(name=name, + instrument=None, + names=("test12","test1234"), + shapes=( (200, ) , (200, ), ), + labels=( "digitzer_response", "D2"), + units=("unit1", "unit2"), ) + self.setpoints = ( (np.linspace(70,500,200), ), (np.linspace(70,500,200), )) + self.setpoint_shapes = ( (200, ), (200, )) + self.setpoint_labels = ( ("I channel", ), ('Q channel', )) + self.setpoint_units = ( ("mV", ), ("mV", )) + self.setpoint_names = ( ("test_name", ), ("testname_2", )) + self.i = 2 + def get_raw(self): + self.i +=1 + return (np.linspace(0,500,200)+self.i, np.linspace(0,500,200)+self.i+100) + class dummy_multi_parameter_2dawg(MultiParameter): + def __init__(self, name, label=None, unit=None): + + super().__init__(name=name, + instrument=None, + names=("test12","test1234"), + shapes=( tuple() , tuple() ), + labels=( "digitzer_response", "D2"), + units=("unit1", "unit2"), ) + self.setpoints = ( tuple(), tuple()) + self.setpoint_shapes = ( tuple(), tuple()) + self.setpoint_labels = ( ("I channel", ), ('Q channel', )) + self.setpoint_units = ( ("mV", ), ("mV", )) + self.setpoint_names = ( ("I_channel", ), ("Q_channel", )) + self.i = 2 + def get_raw(self): + self.i +=1 + return (self.i, self.i+100) + class uploader(object): + """docstring for uploader""" + def __init__(self, ): + super(uploader, self).__init__() + + def wait_until_AWG_idle(self): + ''' + check if the AWG is doing playback, when done, release this function + ''' + time.sleep(0.01) + pass + + measurment_parameter = dummy_multi_parameter("digitzer_1", label="qubit_1 (spin up)", unit="%") + measurment_parameter2D = dummy_multi_parameter_2dawg("digitzer_1", label="qubit_1 (spin up)", unit="%") + + awg_sequence0D = test_AWG_sequence0D() + awg_sequence1D = test_AWG_sequence1D() + awg_sequence2D = test_AWG_sequence2D() + test = pulselib_2_qcodes(awg_sequence0D) + print(test) \ No newline at end of file diff --git a/core_tools/sweeps/sweep_utility.py b/core_tools/sweeps/sweep_utility.py new file mode 100644 index 0000000000000000000000000000000000000000..52f3c871663c80003b865714e7b06950ceee9774 --- /dev/null +++ b/core_tools/sweeps/sweep_utility.py @@ -0,0 +1,103 @@ +from dataclasses import dataclass +from qcodes import Parameter + +class KILL_EXP(Exception): + pass + +@dataclass +class sweep_info(): + ''' + data class that hold the sweep info for one of the paramters. + -- also contains a looper - (should this one move to somewhere else?) + ''' + _param : Parameter = None + start : float = 0 + stop : float = 0 + n_points : int = 50 + delay : float = 0 + + def __post_init__(self): + self.param = self._param + + @property + def param(self): + return self._param + + @param.setter + def param(self, input_param): + self.param_val = input_param.get() + self._param = input_param + + def reset_param(self): + self._param.set(self.param_val) + +def get_measure_data(m_instr): + ''' + measure date for given paramters in m_instr + + Args: + m_instr (list<qc.Parameter>) : list with parameters to be measured + Returns + my_data (list<qc.Parameter>), np.ndarray/str/float/int>) + ''' + my_data = [] + for instr in m_instr: + my_data.append( (instr, instr.get())) + + return my_data + +class PulseLibParameter(Parameter): + setpoints = None + flat_index = 0 + + def add_setpoints(self, setpoints, sequencer, lowest_level): + self.setpoints = setpoints + self.sequencer = sequencer + self.lowest_level = lowest_level + + def get_raw(self): + current_val = self.setpoints[self.flat_index%len(self.setpoints)] + + self.flat_index += 1 + if self.flat_index > np.prod(self.sequencer.shape): + self.flat_index = 0 + + return current_val + + def set_raw(self, value): + if self.lowest_level: + if self.flat_index == 0: + self.sequencer.upload(np.unravel_index(flat_index, self.sequencer.shape)) + + index = np.unravel_index(flat_index, self.shape) + self.sequencer.play(index) + + if flat_index < np.prod(self.shape) - 1: + self.sequencer.upload(np.unravel_index(flat_index+1, self.shape)) + + self.sequencer.uploader.wait_until_AWG_idle() + + ''' + @ sander, how can we make sure that a unused upload is removed when the garbage collector collects this? + (e.g. when a set is performed to reset parameters -- normally this does not happen, but user might accidentatly do this.) + ''' + +def pulselib_2_qcodes(awg_sequence): + ''' + convert pulse sequencer object in qcodes parameters that are usable in sweeps. + + Args: + awg_sequence (pulselib.sequencer.sequencer) : sequence object + + Returns: + set_param (list<PulseLibParameter>) : set paramters for the pulselib to be used in the sweep + ''' + set_param = list() + for i in range(len(awg_sequence.shape)): + param = PulseLibParameter(name = awg_sequence.labels[i].replace(" ", "_"), label=awg_sequence.labels[i], unit= awg_sequence.units[i]) + param.add_setpoints(awg_sequence.setpoints[i], awg_sequence, False) + set_param.append(sweep_info(param, n_points = len(awg_sequence.setpoints[i]))) + + set_param[0].param.lowest_level=True + + return set_param[::-1] diff --git a/core_tools/sweeps/sweeps.py b/core_tools/sweeps/sweeps.py new file mode 100644 index 0000000000000000000000000000000000000000..8dac614ca1132759e211b28125c0f6a969cc9ebf --- /dev/null +++ b/core_tools/sweeps/sweeps.py @@ -0,0 +1,179 @@ +from qcodes.instrument.specialized_parameters import ElapsedTimeParameter +from qcodes.dataset.measurements import Measurement +from pulse_lib.sequencer import sequencer + +from core_tools.sweeps.sweep_utility import pulselib_2_qcodes, sweep_info, get_measure_data, KILL_EXP +from core_tools.job_mgnt.job_meta import job_meta + +import numpy as np +import time + +class scan_generic(metaclass=job_meta): + ''' + function that handeles the loop action and defines the run class. + ''' + def __init__(self, *args, reset_param=False): + ''' + init of the scan function + + Args: + args (*list) : provide here the sweep info and meaurment parameters + reset_param (bool) : reset the setpoint parametes to their original value after the meaurement + ''' + self.meas = Measurement() + + self.set_vars = [] + self.m_instr = [] + self.reset_param = reset_param + + set_points = [] + for arg in args: + if isinstance(arg, sweep_info): + self.meas.register_parameter(arg.param) + self.set_vars.append(arg) + set_points.append(arg.param) + elif isinstance(arg, sequencer): + set_vars_pulse_lib = pulselib_2_qcodes(arg) + for var in set_vars_pulse_lib: + self.meas.register_parameter(var.param) + set_points.append(var.param) + self.set_vars += set_vars_pulse_lib + else: + self.m_instr.append(arg) + for instr in self.m_instr: + self.meas.register_parameter(instr, setpoints=tuple(set_points[::-1])) + + self.n_tot = 1 + for swp_info in self.set_vars: + self.n_tot *= swp_info.n_points + + def run(self): + ''' + run function + -- starts the meaurement and saves the data + -- optionally also resets the paramters + -- wrapped by the job_meta class (allows for progress bar to appear) + ''' + with self.meas.run() as datasaver: + self._loop(self.set_vars, self.m_instr, tuple(), datasaver) + dataset = datasaver.dataset + + if self.reset_param: + for param in self.set_vars: + param.reset_param() + + return dataset + + def _loop(self, set_param, m_instr, to_save, datasaver): + if len(set_param) == 0: + if self.KILL == False: + datasaver.add_result(*to_save, *get_measure_data(m_instr)) + self.n += 1 + else: + raise KILL_EXP + else: + param_info = set_param[0] + for value in np.linspace(param_info.start, param_info.stop, param_info.n_points): + if not isinstance(param_info.param, ElapsedTimeParameter): + param_info.param(value) + time.sleep(param_info.delay) + self._loop(set_param[1:], m_instr, to_save + ((param_info.param, param_info.param()),), datasaver) + + +def do0D(*m_instr): + ''' + do a 0D scan + + Args: + m_instr (*list) : list of parameters to measure + ''' + return scan_generic(*m_instr, reset_param=False) + +def do1D(param, start, stop, n_points, delay, *m_instr, reset_param=False): + ''' + do a 1D scan + + Args: + param (qc.Parameter) : parameter to be swept + start (float) : start value of the sweep + stop (float) : stop value of the sweep + delay (float) : time to wait after the set of the parameter + m_instr (*list) : list of parameters to measure + reset_param (bool) : reset the setpoint parametes to their original value after the meaurement + ''' + m_param = sweep_info(param, start, stop, n_points, delay) + return scan_generic(m_param, *m_instr, reset_param=reset_param) + +def do2D(param_1, start_1, stop_1, n_points_1, delay_1, + param_2, start_2, stop_2, n_points_2, delay_2, *m_instr, reset_param=False): + ''' + do a 2D scan + + Args: + param_1 (qc.Parameter) : parameter to be swept + start_1 (float) : start value of the sweep + stop_1 (float) : stop value of the sweep + delay_1 (float) : time to wait after the set of the parameter + param_2 (qc.Parameter) : parameter to be swept + start_2 (float) : start value of the sweep + stop_2 (float) : stop value of the sweep + delay_2 (float) : time to wait after the set of the parameter + m_instr (*list) : list of parameters to measure + reset_param (bool) : reset the setpoint parametes to their original value after the meaurement + ''' + m_param_1 = sweep_info(param_1, start_1, stop_1, n_points_1, delay_1) + m_param_2 = sweep_info(param_2, start_2, stop_2, n_points_2, delay_2) + return scan_generic(m_param_2, m_param_1, *m_instr, reset_param=reset_param) + +if __name__ == '__main__': + + import os + import datetime + + import numpy as np + import scipy.optimize as opt + import matplotlib.pyplot as plt + + import qcodes as qc + from qcodes.dataset.plotting import plot_dataset + from qcodes.dataset.data_set import load_by_run_spec + from qcodes.dataset.sqlite.database import initialise_or_create_database_at + from qcodes.dataset.experiment_container import load_or_create_experiment + from qcodes.tests.instrument_mocks import MockParabola + + station = qc.station.Station() + station.add_component(MockParabola(name='MockParabola')) + + class MyCounter(qc.Parameter): + def __init__(self, name): + # only name is required + super().__init__(name, label='Times this has been read', + docstring='counts how many times get has been called ' + 'but can be reset to any integer >= 0 by set') + self._count = 0 + + # you must provide a get method, a set method, or both. + def get_raw(self): + self._count += 1 + return self._count + + def set_raw(self, val): + self._count = val + + + now = str(datetime.datetime.now()) + tutorial_db_path = os.path.join(os.getcwd(), 'linking_datasets_tutorial.db') + initialise_or_create_database_at(tutorial_db_path) + load_or_create_experiment('tutorial ' + now, 'no sample') + my_param = MyCounter('test_instr') + from qcodes.instrument.specialized_parameters import ElapsedTimeParameter + + x = qc.Parameter(name='x', label='Voltage_x', unit='V', + set_cmd=None, get_cmd=None) + y = qc.Parameter(name='y', label='Voltage_y', unit='V', + set_cmd=None, get_cmd=None) + timer = ElapsedTimeParameter('time') + # do0D(my_param).run() + # do1D(x, 0, 100, 50, 0.1 , my_param, reset_param=True).run() + do2D(x, 0, 20, 20, 0.0, y, 0, 80, 30, 0.01, my_param).run() + do2D(x, 0, 20, 20, 0.0, timer, 0, 80, 30, 0.1, my_param).run() \ No newline at end of file diff --git a/core_tools/sweeps/Modulated_scans/DEMOD_tests.py b/core_tools/sweeps/sweeps_old/Modulated_scans/DEMOD_tests.py similarity index 100% rename from core_tools/sweeps/Modulated_scans/DEMOD_tests.py rename to core_tools/sweeps/sweeps_old/Modulated_scans/DEMOD_tests.py diff --git a/core_tools/sweeps/Modulated_scans/__init__.py b/core_tools/sweeps/sweeps_old/Modulated_scans/__init__.py similarity index 100% rename from core_tools/sweeps/Modulated_scans/__init__.py rename to core_tools/sweeps/sweeps_old/Modulated_scans/__init__.py diff --git a/core_tools/sweeps/Modulated_scans/mod_test.py b/core_tools/sweeps/sweeps_old/Modulated_scans/mod_test.py similarity index 100% rename from core_tools/sweeps/Modulated_scans/mod_test.py rename to core_tools/sweeps/sweeps_old/Modulated_scans/mod_test.py diff --git a/core_tools/sweeps/sweeps_old/__init__.py b/core_tools/sweeps/sweeps_old/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/core_tools/sweeps/exp_readout_runner.py b/core_tools/sweeps/sweeps_old/exp_readout_runner.py similarity index 100% rename from core_tools/sweeps/exp_readout_runner.py rename to core_tools/sweeps/sweeps_old/exp_readout_runner.py diff --git a/core_tools/sweeps/pulse_lib_sweep.py b/core_tools/sweeps/sweeps_old/pulse_lib_sweep.py similarity index 100% rename from core_tools/sweeps/pulse_lib_sweep.py rename to core_tools/sweeps/sweeps_old/pulse_lib_sweep.py