Qcodes icon indicating copy to clipboard operation
Qcodes copied to clipboard

Adding metadata dict more than one level deep creates invalid run

Open jenshnielsen opened this issue 6 years ago • 5 comments

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

jenshnielsen avatar Jan 14 '19 15:01 jenshnielsen

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 avatar Feb 01 '22 10:02 cprosko

@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

jenshnielsen avatar Feb 01 '22 12:02 jenshnielsen

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.

cprosko avatar Feb 01 '22 12:02 cprosko

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

jenshnielsen avatar Feb 01 '22 13:02 jenshnielsen

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_' with an empty value, such that tags can be added and searched with functionality as is.

Would you suggest though, making this json packing/unpacking idea a feature request? Thanks!

cprosko avatar Feb 01 '22 16:02 cprosko