Qcodes
Qcodes copied to clipboard
Adding metadata dict more than one level deep creates invalid run
import qcodes as qc
from qcodes import ParamSpec, new_data_set, new_experiment
dataSet = new_data_set("sweep gate")
exp = new_experiment("majorana-qbuit", sample_name="flying brdige")
parameter_a = ParamSpec("a", "numeric")
parameter_b = ParamSpec("b", "numeric", key="value", number=1)
parameter_c = ParamSpec("c", "array")
dataSet = new_data_set("sweep gate", specs=[parameter_a,parameter_b,parameter_c], metadata={'a': {'b': 1}})
results in
Rolling back due to unhandled exception
Traceback (most recent call last):
File "c:\users\jenielse\source\repos\qcodes\qcodes\dataset\sqlite_base.py", line 819, in atomic
yield conn
File "c:\users\jenielse\source\repos\qcodes\qcodes\dataset\sqlite_base.py", line 781, in atomic_transaction
c = transaction(atomic_conn, sql, *args)
File "c:\users\jenielse\source\repos\qcodes\qcodes\dataset\sqlite_base.py", line 756, in transaction
c.execute(sql, args)
sqlite3.InterfaceError: Error binding parameter 0 - probably unsupported type.
---------------------------------------------------------------------------
InterfaceError Traceback (most recent call last)
c:\users\jenielse\source\repos\qcodes\qcodes\dataset\sqlite_base.py in atomic(conn)
818 conn.cursor().execute('BEGIN')
--> 819 yield conn
820 except Exception as e:
c:\users\jenielse\source\repos\qcodes\qcodes\dataset\sqlite_base.py in atomic_transaction(conn, sql, *args)
780 with atomic(conn) as atomic_conn:
--> 781 c = transaction(atomic_conn, sql, *args)
782 return c
c:\users\jenielse\source\repos\qcodes\qcodes\dataset\sqlite_base.py in transaction(conn, sql, *args)
755 if len(args) > 0:
--> 756 c.execute(sql, args)
757 else:
InterfaceError: Error binding parameter 0 - probably unsupported type.
The above exception was the direct cause of the following exception:
RuntimeError Traceback (most recent call last)
<ipython-input-8-6818d1064158> in <module>
----> 1 dataSet = new_data_set("sweep gate", specs=[parameter_a,parameter_b,parameter_c], metadata={'a': {'b': 1}})
2 exp
c:\users\jenielse\source\repos\qcodes\qcodes\dataset\data_set.py in new_data_set(name, exp_id, specs, values, metadata, conn)
1080 d = DataSet(path_to_db=None, run_id=None, conn=conn,
1081 name=name, specs=specs, values=values,
-> 1082 metadata=metadata, exp_id=exp_id)
1083
1084 return d
c:\users\jenielse\source\repos\qcodes\qcodes\dataset\data_set.py in __init__(self, path_to_db, run_id, conn, exp_id, name, specs, values, metadata)
268 _, run_id, __ = create_run(self.conn, exp_id, name,
269 generate_guid(),
--> 270 specs, values, metadata)
271 # this is really the UUID (an ever increasing count in the db)
272 self._run_id = run_id
c:\users\jenielse\source\repos\qcodes\qcodes\dataset\sqlite_base.py in create_run(conn, exp_id, name, guid, parameters, values, metadata)
2297 parameters)
2298 if metadata:
-> 2299 add_meta_data(conn, run_id, metadata)
2300 _update_experiment_run_counter(conn, exp_id, run_counter)
2301 _create_run_table(conn, formatted_name, parameters, values)
c:\users\jenielse\source\repos\qcodes\qcodes\dataset\sqlite_base.py in add_meta_data(conn, row_id, metadata, table_name)
2394 """
2395 try:
-> 2396 insert_meta_data(conn, row_id, table_name, metadata)
2397 except sqlite3.OperationalError as e:
2398 # this means that the column already exists
c:\users\jenielse\source\repos\qcodes\qcodes\dataset\sqlite_base.py in insert_meta_data(conn, row_id, table_name, metadata)
2362 for key in metadata.keys():
2363 insert_column(conn, table_name, key)
-> 2364 update_meta_data(conn, row_id, table_name, metadata)
2365
2366
c:\users\jenielse\source\repos\qcodes\qcodes\dataset\sqlite_base.py in update_meta_data(conn, row_id, table_name, metadata)
2376 - metadata: the metadata to add
2377 """
-> 2378 update_where(conn, table_name, 'rowid', row_id, **metadata)
2379
2380
c:\users\jenielse\source\repos\qcodes\qcodes\dataset\sqlite_base.py in update_where(conn, table, where_column, where_value, **updates)
989 {where_column} = ?
990 """
--> 991 atomic_transaction(conn, query, *values, where_value)
992
993
c:\users\jenielse\source\repos\qcodes\qcodes\dataset\sqlite_base.py in atomic_transaction(conn, sql, *args)
779 """
780 with atomic(conn) as atomic_conn:
--> 781 c = transaction(atomic_conn, sql, *args)
782 return c
783
~\AppData\Local\Continuum\miniconda3\envs\qcodes\lib\contextlib.py in __exit__(self, type, value, traceback)
97 value = type()
98 try:
---> 99 self.gen.throw(type, value, traceback)
100 except StopIteration as exc:
101 # Suppress StopIteration *unless* it's the same exception that
c:\users\jenielse\source\repos\qcodes\qcodes\dataset\sqlite_base.py in atomic(conn)
821 conn.rollback()
822 log.exception("Rolling back due to unhandled exception")
--> 823 raise RuntimeError("Rolling back due to unhandled exception") from e
824 else:
825 if is_outmost:
RuntimeError: Rolling back due to unhandled exception
Subsequent runs then fail even without the metadata because 2 rows with the same run_id are created
dataSet = new_data_set("sweep gate", specs=[parameter_a,parameter_b,parameter_c])
fails with:
Rolling back due to unhandled exception
Traceback (most recent call last):
File "c:\users\jenielse\source\repos\qcodes\qcodes\dataset\sqlite_base.py", line 819, in atomic
yield conn
File "c:\users\jenielse\source\repos\qcodes\qcodes\dataset\sqlite_base.py", line 1964, in _insert_run
*parameters)
File "c:\users\jenielse\source\repos\qcodes\qcodes\dataset\sqlite_base.py", line 2168, in _add_parameters_to_layout_and_deps
run_id = one(transaction(conn, sql), 'run_id')
File "c:\users\jenielse\source\repos\qcodes\qcodes\dataset\sqlite_base.py", line 285, in one
raise RuntimeError("Expected only one row")
RuntimeError: Expected only one row
---------------------------------------------------------------------------
RuntimeError Traceback (most recent call last)
c:\users\jenielse\source\repos\qcodes\qcodes\dataset\sqlite_base.py in atomic(conn)
818 conn.cursor().execute('BEGIN')
--> 819 yield conn
820 except Exception as e:
c:\users\jenielse\source\repos\qcodes\qcodes\dataset\sqlite_base.py in _insert_run(conn, exp_id, name, guid, parameters)
1963 _add_parameters_to_layout_and_deps(conn, formatted_name,
-> 1964 *parameters)
1965
c:\users\jenielse\source\repos\qcodes\qcodes\dataset\sqlite_base.py in _add_parameters_to_layout_and_deps(conn, formatted_name, *parameter)
2167 """
-> 2168 run_id = one(transaction(conn, sql), 'run_id')
2169 layout_args = []
c:\users\jenielse\source\repos\qcodes\qcodes\dataset\sqlite_base.py in one(curr, column)
284 if len(res) > 1:
--> 285 raise RuntimeError("Expected only one row")
286 elif len(res) == 0:
RuntimeError: Expected only one row
The above exception was the direct cause of the following exception:
RuntimeError Traceback (most recent call last)
<ipython-input-9-c45f733cf863> in <module>
----> 1 dataSet = new_data_set("sweep gate", specs=[parameter_a,parameter_b,parameter_c])
c:\users\jenielse\source\repos\qcodes\qcodes\dataset\data_set.py in new_data_set(name, exp_id, specs, values, metadata, conn)
1080 d = DataSet(path_to_db=None, run_id=None, conn=conn,
1081 name=name, specs=specs, values=values,
-> 1082 metadata=metadata, exp_id=exp_id)
1083
1084 return d
c:\users\jenielse\source\repos\qcodes\qcodes\dataset\data_set.py in __init__(self, path_to_db, run_id, conn, exp_id, name, specs, values, metadata)
268 _, run_id, __ = create_run(self.conn, exp_id, name,
269 generate_guid(),
--> 270 specs, values, metadata)
271 # this is really the UUID (an ever increasing count in the db)
272 self._run_id = run_id
c:\users\jenielse\source\repos\qcodes\qcodes\dataset\sqlite_base.py in create_run(conn, exp_id, name, guid, parameters, values, metadata)
2295 name,
2296 guid,
-> 2297 parameters)
2298 if metadata:
2299 add_meta_data(conn, run_id, metadata)
c:\users\jenielse\source\repos\qcodes\qcodes\dataset\sqlite_base.py in _insert_run(conn, exp_id, name, guid, parameters)
1986 time.time(),
1987 False,
-> 1988 desc_str)
1989 run_id = curr.lastrowid
1990 return run_counter, formatted_name, run_id
~\AppData\Local\Continuum\miniconda3\envs\qcodes\lib\contextlib.py in __exit__(self, type, value, traceback)
97 value = type()
98 try:
---> 99 self.gen.throw(type, value, traceback)
100 except StopIteration as exc:
101 # Suppress StopIteration *unless* it's the same exception that
c:\users\jenielse\source\repos\qcodes\qcodes\dataset\sqlite_base.py in atomic(conn)
821 conn.rollback()
822 log.exception("Rolling back due to unhandled exception")
--> 823 raise RuntimeError("Rolling back due to unhandled exception") from e
824 else:
825 if is_outmost:
RuntimeError: Rolling back due to unhandled exception
I would like to add that this issue persists, and also extends to attempting to add any metadata which is a sequence/set/iterable. For example, running the same code as in the original issue statement, but with the inner set replaced by a tuple or set or list produces the same error.
(This is of interest to me because I would like to be able to add multiple tags to measurements -- eg. 'calibration', 'published', 'good', 'erroneous', 'DC', 'RF', and so on)
@cprosko Thanks for your comment, For your usecase I would probably suggest serializing the data that you want to store inside the given metadata field using json similar to how we are doing the snapshot. We could probably think of a more elegant way to do that but this bug is mainly about ensuring that we fail in a meaningful way if you try to add nested fields as metadata
Hi Jens, thanks for the quick response. Sorry for my ignorance, but are you suggesting that I use some external functionality to QCoDes to save the information I want in json? Or is there some functionality within qcodes to accomplish something similar?
The reason why this functionality is important to me is because I aim to expand upon the measurements inspection widget developed by Bas Nijholt to enable filtering by different tags. In the best case scenario, a database would store these tags on each dataset, such that the proposed widget can load a database file and immediately sort/filter in this way. As such, it seemed logical to store the corresponding filtering tags, etc., in the metadata of each dataset.
As a final question then, is it important that DataSets cannot have lists of strings as metadata entries, or could I propose this as a feature request?
Thanks for your time, cheers.
Hi, This is as far as I can see a limitation of sqlite in the form that we use it. A field in the table can only be a primitive type (number string etc) not a sequence. We could extend add / read metadata to try to use json to automatically pack / unpack lists
Thanks for getting back to me. I understand the issue better now. I need to make this work for my purposes, so in the meantime, for these 'tags' I could do a hack solution of simply adding keys to the metadata such as 'tag_
Would you suggest though, making this json packing/unpacking idea a feature request? Thanks!