From 13d8b008ecc1176ad303a6095d4ec855cd1b0aaa Mon Sep 17 00:00:00 2001 From: Stephan Philips <s.g.j.philips@tudelft.nl> Date: Mon, 15 Jun 2020 09:34:25 +0200 Subject: [PATCH] db tools --- core_tools/job_mgnt/calibrations/__init__.py | 0 .../job_mgnt/calibrations/calibration_data.py | 204 ++++++++++++++++++ .../calibrations/calibration_single.py | 54 +++++ .../job_mgnt/calibrations/sample_layout.py | 57 +++++ core_tools/job_mgnt/legacy/calibrations.py | 6 +- 5 files changed, 318 insertions(+), 3 deletions(-) create mode 100644 core_tools/job_mgnt/calibrations/__init__.py create mode 100644 core_tools/job_mgnt/calibrations/calibration_data.py create mode 100644 core_tools/job_mgnt/calibrations/calibration_single.py create mode 100644 core_tools/job_mgnt/calibrations/sample_layout.py diff --git a/core_tools/job_mgnt/calibrations/__init__.py b/core_tools/job_mgnt/calibrations/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/core_tools/job_mgnt/calibrations/calibration_data.py b/core_tools/job_mgnt/calibrations/calibration_data.py new file mode 100644 index 00000000..f8d25a89 --- /dev/null +++ b/core_tools/job_mgnt/calibrations/calibration_data.py @@ -0,0 +1,204 @@ +import sqlite3 + +class data_mgr(): + def __init__(self, cls_object, db_location): + ''' + Args: + cls_object (object) : calibration object + db_location (str) : location of the database + + ''' + self.filename_db = db_location + '.sqlite' + self.table_name = cls_object.__class__.__name__ + + # self.set_param = cls_object.set_param + # self.get_param = cls_object.get_param + + def __connect(self): + db = sqlite3.connect(self.filename_db) + cursor = db.cursor() + return db, cursor + + def __exec_command(self,cmd): + ''' + Execute command in the database. + + Args : + cmd (str) : command you want to execute in the database. + ''' + db, cursor = self.__connect() + cursor.execute(cmd) + db.commit() + db.close() + + def __query_db(self, cmd): + ''' + Aks for values in the database/execute command in the database. + + Args: + cmd (str) : command you want to execute in the database. + + Returns : + mydata (list<tuple>) : raw container with the data of your query + ''' + db, cursor = self.__connect() + cursor.execute(cmd) + mydata = cursor.fetchall() + db.commit() + db.close() + return mydata + + def update_table(self): + ''' + function that will construct if not already there the database where the data will be saved. + Note that is is no problem running this when no update is strictly needed (e.g. when you start up the module) + NOTE: that existing fields will not be updated. Use stash_table to rename it and create a new one if you want to do that. + ''' + # Get data that needs to be saved. + base_colomns = [('Time_human_readable','TEXT', 'a.u.')] + all_colls = base_colomns + self.calibration_params + + # Connect to the database. + db, cursor = self.__connect() + + # Check if table exists of data + cursor.execute("select count(*) from sqlite_master where type='table' and name='%s'"% self.table_name) + exists = True if cursor.fetchall()[0][0]==1 else False + + if not exists: + cursor.execute('CREATE TABLE %s (time DOUBLE PRIMARY KEY)' %self.table_name) + cursor.execute('CREATE TABLE %s (varname TEXT, unit TEXT)' %(self.table_name + '_units')) + cursor.execute("INSERT INTO %s VALUES ('%s', '%s')" % (self.table_name + '_units', 'time', 's')) + + # Get current columns in the data base: + cursor.execute("PRAGMA table_info('%s')"%self.table_name) + db_colomn_info = cursor.fetchall() + db_colomn_names= [i[1].lower() for i in db_colomn_info] + # Check if all the wanted coloms are there (suppose users made up their mind about the datype they want to use...) + + columms_to_add = [i for i in all_colls if i[0].lower() not in db_colomn_names] + + # Add missing colomn to table + for i in columms_to_add: + cursor.execute("ALTER TABLE %s ADD COLUMN '%s' %s" % (self.table_name, i[0], i[1])) + cursor.execute("INSERT INTO %s VALUES ('%s', '%s')" % (self.table_name + '_units', i[0], i[2])) + + # commit changes + db.commit() + # Close conn + db.close() + + def stash_table(self): + ''' + e.g. when you make a new sample. + save to self.table_name.date + heck if the table has the right entries. + ''' + time = datetime.now().strftime("%Y_%m_%d__%H_%M_%S") + db, cursor = self.__connect() + cursor.execute("ALTER TABLE %s RENAME TO %s"%(self.table_name, self.table_name + '_' + time)) + cursor.execute("ALTER TABLE %s RENAME TO %s"%(self.table_name+ '_units', self.table_name + '_units' + '_' + time)) + db.commit() + db.close() + + def delete_table(self): + ''' + Delete the current table. + ''' + db, cursor = self.__connect() + cursor.execute("DROP TABLE %s"% self.table_name) + cursor.execute("DROP TABLE %s"% (self.table_name + '_units')) + db.commit() + db.close() + + def get_all_parameter_names(self): + ''' + Get all the parameters that are currently in use. Note that SQLite is case insensitive. + ''' + db, cursor = self.__connect() + cursor.execute("PRAGMA table_info('%s')"%self.table_name) + db_colomn_info = cursor.fetchall() + db_colomn_names= [i[1].lower() for i in db_colomn_info] + db.close() + return db_colomn_names + + def save_calib_results(self, data_tuple): + ''' + saves the data_tuple to database, if you give a variable that not exist, this will will be discarded + TODO tell when people feed garbage. + Args: + data_tuple list<tuple<str, any>: input data for one row [(var_name, value)] + ''' + if type(data_tuple) != type(list()): + data_tuple = [data_tuple] + + fields = self.get_all_parameter_names() + + to_upload = [] + for i in fields: + var_found = False + if i == 'time': + to_upload.append(time.time()) + continue + if i == 'time_human_readable': + to_upload.append(datetime.now().strftime("'%Y/%m/%d-%H:%M:%S'")) + continue + for j in data_tuple: + if i == j[0].lower(): + to_upload.append(j[1]) + var_found = True + break + + if var_found==False: + to_upload.append('null') + + cmd = 'INSERT INTO %s VALUES ('%self.table_name + for i in to_upload: + cmd += str(i) + ',' + cmd = cmd[:-1] + cmd += ')' + + self.__exec_command(cmd) + + def get_parameter_latest(self, params, side_condition=None): + ''' + returns array with wanted params, if no params given, all parameters of the last calibration will be returned + params = string or array of strings containing the wanted parameter. + side_condition = tuple of values that should be set to a certain value (e.g) + return format: + list of dictionaries with field name, data and unit + Returns: + Format of param input, return None if not found. + ''' + input_is_str= False + # safe typing + if type(params) != list: + params = [params] + input_is_str = True + + + # Construction of query + cmd = 'SELECT MAX(time), ' + # param to select + for i in params: + cmd += i + ',' + cmd = cmd[:-1] + ' FROM %s '%self.table_name + cmd += 'WHERE ' + for i in params: + cmd += '%s IS NOT NULL AND '%i + if len(side_condition) != 0: + for i in side_condition: + if str(i[1]).lower() == 'null': + cmd += '%s IS %s and '%(i[0], i[1]) + else: + cmd += '%s = %s and '%(i[0], i[1]) + + cmd = cmd[:-4] + + if input_is_str: + return self.__query_db(cmd)[0][1:][0] + else: + return list(self.__query_db(cmd)[0][1:]) + +if __name__ == '__main__': + d = data_mgr('test', 'test/') \ No newline at end of file diff --git a/core_tools/job_mgnt/calibrations/calibration_single.py b/core_tools/job_mgnt/calibrations/calibration_single.py new file mode 100644 index 00000000..ddb2e825 --- /dev/null +++ b/core_tools/job_mgnt/calibrations/calibration_single.py @@ -0,0 +1,54 @@ +from calibration_data import data_mgr + +class CalibrationError(Exception): + pass + +class dep_mgr(): + dep = tuple() + +def calibration_wrapper(cls, function): + def run_function(*args, **kwargs): + try: + function(args, kwargs) + if cls._N_rep > cls._n: + run_function(args, kwargs) + except: + raise CalibrationError + + cls._N_rep = 0 + cls._n = 0 + + return run_function + +class calibration_generic(): + def __init__(self): + self.update_interval = 0 # 0 for do not update + self.auto_update = False # automatically rerun the last calibration after the update intercal exceeded + self.prioritize = True # first calibration or first measurement + self.dependencies = dep_mgr() + self.data_mgr = data_mgr() + + # iteration variables + self._N_rep = 0 + self._n = 0 + + def get_data(self, parameters ,set_vals = dict()): + self.data_mgr.get(set_vals) + + def save_data(self, set_vals): + self.data_mgr.set() + + def reiterate(self, N=1): + ''' + call this function in the + ''' + self._N_rep = N+1 + self._n = 0 + + + + +def ExampleCal(calibration_generic): + def __init__(self): + self.dependencies += my_cals.readout_of_dot_1 + self.dependencies += (my_cals.readout_of_dot_2, my_cals.tc_res) \ No newline at end of file diff --git a/core_tools/job_mgnt/calibrations/sample_layout.py b/core_tools/job_mgnt/calibrations/sample_layout.py new file mode 100644 index 00000000..4dbace25 --- /dev/null +++ b/core_tools/job_mgnt/calibrations/sample_layout.py @@ -0,0 +1,57 @@ +from typing import Union +import numpy as np + + +class SampleLayoutField(): + # formatter for a single field of the sample layout class + def __init__(self, std_field_name = '', input_names = tuple()): + ''' + Args: + std_field_name (str) : standard name to append before the input name + input_names (tuple<str>) : input names + ''' + self.variable_names = list() + self.std_field_name = '_' if std_field_name == '' else "_" + std_field_name + '_' + self += input_names + + def __add__(self, other): + if isinstance(other, Union[str, int, float].__args__): + return self + [other] + + if isinstance(other, Union[list, tuple, np.ndarray, range].__args__): + for var in other: + self.variable_names += [self.std_field_name + str(var)] + return self + raise ValueError('type not recognized?') + + def __radd__(self, other): + if isinstance(other, str): + return_var = tuple() + for var in self.variable_names: + return_var += (other + var,) + return return_var + + raise ValueError('type for adding not recognized. Only strings are supported') + +class MyExampleSampleLayout(): + def __init__(self): + self.qubits = SampleLayoutField('qubits') + self.qubit_pairs = SampleLayoutField() + self.res_barrier = SampleLayoutField() + self.n = SampleLayoutField() + self.SD = SampleLayoutField('SD') + + self.qubits += range(1,6) + self.qubit_pairs += (12,23,34,45) + self.res_barrier += (1,2) + self.n += range(1,6) + self.SD += range(1,3) + +if __name__ == '__main__': + # example usage of layout class + SL = MyExampleSampleLayout() + + print('FREQ' + SL.qubits) + print('J' + SL.qubit_pairs) + print('SD' + SL.SD) + print('tc_res' + SL.res_barrier) diff --git a/core_tools/job_mgnt/legacy/calibrations.py b/core_tools/job_mgnt/legacy/calibrations.py index b95b4704..f7bab324 100644 --- a/core_tools/job_mgnt/legacy/calibrations.py +++ b/core_tools/job_mgnt/legacy/calibrations.py @@ -1,9 +1,10 @@ +from datetime import datetime + import threading as th import time, logging, importlib, os, time import inspect import os import sqlite3 -from datetime import datetime class CalibrationMaster(): # Parent class for all the calibration classes. @@ -29,10 +30,9 @@ class CalibrationMaster(): self.my_database = None def __connect(self): - # Get path where the database is saved. module_location = inspect.getmodule(self).__file__ filename_db = os.path.splitext(module_location)[0] + '.sqlite' - # connect to your database + db = sqlite3.connect(filename_db) cursor = db.cursor() return db, cursor -- GitLab