def test_add_experiments(empty_temp_db, experiment_name, sample_name, dataset_name): global n_experiments n_experiments += 1 _ = new_experiment(experiment_name, sample_name=sample_name) exps = experiments() assert len(exps) == n_experiments exp = exps[-1] assert exp.name == experiment_name assert exp.sample_name == sample_name assert exp.last_counter == 0 dataset = new_data_set(dataset_name) dsid = dataset.run_id loaded_dataset = load_by_id(dsid) expected_ds_counter = 1 assert loaded_dataset.name == dataset_name assert loaded_dataset.counter == expected_ds_counter assert loaded_dataset.table_name == "{}-{}-{}".format(dataset_name, exp.exp_id, loaded_dataset.counter) expected_ds_counter += 1 dataset = new_data_set(dataset_name) dsid = dataset.run_id loaded_dataset = load_by_id(dsid) assert loaded_dataset.name == dataset_name assert loaded_dataset.counter == expected_ds_counter assert loaded_dataset.table_name == "{}-{}-{}".format(dataset_name, exp.exp_id, loaded_dataset.counter)
def __enter__(self) -> DataSaver: # TODO: should user actions really precede the dataset? # first do whatever bootstrapping the user specified for func, args in self.enteractions: func(*args) # next set up the "datasaver" if self.experiment is not None: self.ds = qc.new_data_set(self.name, self.experiment.exp_id, conn=self.experiment.conn) else: self.ds = qc.new_data_set(self.name) # .. and give the dataset a snapshot as metadata if self.station is None: station = qc.Station.default else: station = self.station if station: self.ds.add_snapshot( json.dumps({'station': station.snapshot()}, cls=NumpyJSONEncoder)) if self._interdependencies == InterDependencies_(): raise RuntimeError("No parameters supplied") else: self.ds.set_interdependencies(self._interdependencies) links = [ Link(head=self.ds.guid, **pdict) for pdict in self._parent_datasets ] self.ds.parent_dataset_links = links self.ds.mark_started(start_bg_writer=self._write_in_background) # register all subscribers for (callble, state) in self.subscribers: # We register with minimal waiting time. # That should make all subscribers be called when data is flushed # to the database log.debug(f'Subscribing callable {callble} with state {state}') self.ds.subscribe(callble, min_wait=0, min_count=1, state=state) print(f'Starting experimental run with id: {self.ds.run_id}.' f' {self._extra_log_info}') log.info(f'Starting measurement with guid: {self.ds.guid}.' f' {self._extra_log_info}') log.info(f'Using background writing: {self._write_in_background}') self.datasaver = DataSaver( dataset=self.ds, write_period=self.write_period, interdeps=self._interdependencies, write_in_background=self._write_in_background) return self.datasaver
def __enter__(self) -> DataSaver: # TODO: should user actions really precede the dataset? # first do whatever bootstrapping the user specified for func, args in self.enteractions: func(*args) # next set up the "datasaver" if self.experiment is not None: self.ds = qc.new_data_set(self.name, self.experiment.exp_id, conn=self.experiment.conn) else: self.ds = qc.new_data_set(self.name) # .. and give the dataset a snapshot as metadata if self.station is None: station = qc.Station.default else: station = self.station if station: self.ds.add_snapshot( json.dumps({'station': station.snapshot()}, cls=NumpyJSONEncoder)) if self.parameters is not None: for paramspec in self.parameters.values(): self.ds.add_parameter(paramspec) else: raise RuntimeError("No parameters supplied") self.ds.mark_started() # register all subscribers for (callble, state) in self.subscribers: # We register with minimal waiting time. # That should make all subscribers be called when data is flushed # to the database log.debug(f'Subscribing callable {callble} with state {state}') self.ds.subscribe(callble, min_wait=0, min_count=1, state=state) print(f'Starting experimental run with id: {self.ds.run_id}') self.datasaver = DataSaver(dataset=self.ds, write_period=self.write_period, parameters=self.parameters) return self.datasaver
def test_numpy_types(bg_writing): """ Test that we can save numpy types in the data set """ p = ParamSpecBase(name="p", paramtype="numeric") test_set = qc.new_data_set("test-dataset") test_set.set_interdependencies(InterDependencies_(standalones=(p, ))) test_set.mark_started(start_bg_writer=bg_writing) idps = InterDependencies_(standalones=(p, )) data_saver = DataSaver(dataset=test_set, write_period=0, interdeps=idps, write_in_background=bg_writing) dtypes = [ np.int8, np.int16, np.int32, np.int64, np.float16, np.float32, np.float64 ] for dtype in dtypes: data_saver.add_result(("p", dtype(2))) data_saver.flush_data_to_database() test_set.mark_completed() data = test_set.get_parameter_data("p")["p"]["p"] expected_data = np.ones(len(dtypes)) expected_data[:] = 2 np.testing.assert_array_equal(data, expected_data)
def test_default_callback(bg_writing): """ The Web UI needs to know the results of an experiment with the metadata. So a default_callback class variable is set by the Web UI with a callback to introspect the data. """ test_set = None reset_callback_globals() try: DataSaver.default_callback = { 'run_tables_subscription_callback': callback, 'run_tables_subscription_min_wait': 1, 'run_tables_subscription_min_count': 2 } test_set = qc.new_data_set("test-dataset") test_set.add_metadata('snapshot', 'reasonable_snapshot') DataSaver(dataset=test_set, write_period=0, interdeps=InterDependencies_, write_in_background=bg_writing) test_set.mark_started(start_bg_writer=bg_writing) test_set.mark_completed() assert CALLBACK_SNAPSHOT == 'reasonable_snapshot' assert CALLBACK_RUN_ID > 0 assert CALLBACK_COUNT > 0 finally: DataSaver.default_callback = None if test_set is not None: test_set.conn.close()
def test_string_with_wrong_paramtype_via_datasaver(experiment): """ Test that it is not possible to add a string value for a non-text parameter via DataSaver object """ p = ParamSpecBase("p", "numeric") test_set = qc.new_data_set("test-dataset") idps = InterDependencies_(standalones=(p, )) test_set.set_interdependencies(idps) test_set.mark_started() idps = InterDependencies_(standalones=(p, )) data_saver = DataSaver(dataset=test_set, write_period=0, interdeps=idps, write_in_background=False) try: msg = re.escape('Parameter p is of type "numeric", but got a ' "result of type <U9 (some text).") with pytest.raises(ValueError, match=msg): data_saver.add_result(("p", "some text")) finally: data_saver.dataset.conn.close()
def test_write_data_to_text_file_length_exception(tmp_path): dataset = new_data_set("dataset") xparam = ParamSpecBase("x", 'numeric') yparam = ParamSpecBase("y", 'numeric') zparam = ParamSpecBase("z", 'numeric') idps = InterDependencies_(dependencies={ yparam: (xparam, ), zparam: (xparam, ) }) dataset.set_interdependencies(idps) dataset.mark_started() results1 = [{'x': 0, 'y': 1}] results2 = [{'x': 0, 'z': 2}] results3 = [{'x': 1, 'z': 3}] dataset.add_results(results1) dataset.add_results(results2) dataset.add_results(results3) dataset.mark_completed() temp_dir = str(tmp_path) with pytest.raises(Exception, match='different length'): dataset.write_data_to_text_file(path=temp_dir, single_file=True, single_file_name='yz')
def test_default_callback(experiment): """ The Web UI needs to know the results of an experiment with the metadata. So a default_callback class variable is set by the Web UI with a callback to introspect the data. """ test_set = None reset_callback_globals() try: DataSaver.default_callback = { 'run_tables_subscription_callback': callback, 'run_tables_subscription_min_wait': 1, 'run_tables_subscription_min_count': 2, } test_set = qc.new_data_set("test-dataset") test_set.add_metadata('snapshot', 123) DataSaver(dataset=test_set, write_period=0, parameters={}) test_set.mark_complete() assert CALLBACK_SNAPSHOT == 123 assert CALLBACK_RUN_ID > 0 assert CALLBACK_COUNT > 0 finally: DataSaver.default_callback = None if test_set is not None: test_set.conn.close()
def test_saving_numeric_values_as_text(numeric_type): """ Test the saving numeric values into 'text' parameter raises an exception """ p = ParamSpecBase("p", "text") test_set = qc.new_data_set("test-dataset") test_set.set_interdependencies(InterDependencies_(standalones=(p, ))) test_set.mark_started() idps = InterDependencies_(standalones=(p, )) data_saver = DataSaver(dataset=test_set, write_period=0, interdeps=idps) try: value = numeric_type(2) gottype = np.array(value).dtype msg = re.escape(f'Parameter {p.name} is of type ' f'"{p.type}", but got a result of ' f'type {gottype} ({value}).') with pytest.raises(ValueError, match=msg): data_saver.add_result((p.name, value)) finally: data_saver.dataset.conn.close()
def test_adding_too_many_results(experiment): """ This test really tests the "chunking" functionality of the insert_many_values function of the sqlite_base module """ dataset = new_data_set("test_adding_too_many_results") xparam = ParamSpec("x", "numeric", label="x parameter", unit='V') yparam = ParamSpec("y", 'numeric', label='y parameter', unit='Hz', depends_on=[xparam]) dataset.add_parameter(xparam) dataset.add_parameter(yparam) n_max = qc.SQLiteSettings.limits['MAX_VARIABLE_NUMBER'] vals = np.linspace(0, 1, int(n_max/2)+2) results = [{'x': val} for val in vals] dataset.add_results(results) vals = np.linspace(0, 1, int(n_max/2)+1) results = [{'x': val, 'y': val} for val in vals] dataset.add_results(results) vals = np.linspace(0, 1, n_max*3) results = [{'x': val} for val in vals] dataset.add_results(results)
def test_add_data_array(): exps = experiments() assert len(exps) == 1 exp = exps[0] assert exp.name == "test-experiment" assert exp.sample_name == "test-sample" assert exp.last_counter == 0 mydataset = new_data_set("test", specs=[ParamSpec("x", "numeric"), ParamSpec("y", "array")]) mydataset.mark_started() expected_x = [] expected_y = [] for x in range(100): expected_x.append([x]) y = np.random.random_sample(10) expected_y.append([y]) mydataset.add_result({"x": x, "y": y}) shadow_ds = make_shadow_dataset(mydataset) assert mydataset.get_data('x') == expected_x assert shadow_ds.get_data('x') == expected_x y_data = mydataset.get_data('y') np.testing.assert_allclose(y_data, expected_y) y_data = shadow_ds.get_data('y') np.testing.assert_allclose(y_data, expected_y)
def test_add_data_1d(experiment): exps = experiments() assert len(exps) == 1 exp = exps[0] assert exp.name == "test-experiment" assert exp.sample_name == "test-sample" assert exp.last_counter == 0 psx = ParamSpec("x", "numeric") psy = ParamSpec("y", "numeric", depends_on=['x']) mydataset = new_data_set("test-dataset", specs=[psx, psy]) expected_x = [] expected_y = [] for x in range(100): expected_x.append([x]) y = 3 * x + 10 expected_y.append([y]) mydataset.add_result({"x": x, "y": y}) assert mydataset.get_data('x') == expected_x assert mydataset.get_data('y') == expected_y with pytest.raises(ValueError): mydataset.add_result({'y': 500}) assert mydataset.completed is False mydataset.mark_complete() assert mydataset.completed is True with pytest.raises(ValueError): mydataset.add_result({'y': 500}) with pytest.raises(CompletedError): mydataset.add_result({'x': 5})
def test_adding_too_many_results(): """ This test really tests the "chunking" functionality of the insert_many_values function of the sqlite.query_helpers module """ dataset = new_data_set("test_adding_too_many_results") xparam = ParamSpecBase("x", "numeric", label="x parameter", unit='V') yparam = ParamSpecBase("y", 'numeric', label='y parameter', unit='Hz') idps = InterDependencies_(dependencies={yparam: (xparam,)}) dataset.set_interdependencies(idps) dataset.mark_started() n_max = qc.SQLiteSettings.limits['MAX_VARIABLE_NUMBER'] vals = np.linspace(0, 1, int(n_max/2)+2) results = [{'x': val} for val in vals] dataset.add_results(results) vals = np.linspace(0, 1, int(n_max/2)+1) results = [{'x': val, 'y': val} for val in vals] dataset.add_results(results) vals = np.linspace(0, 1, n_max*3) results = [{'x': val} for val in vals] dataset.add_results(results)
def test_load_2dsoftsweep(): qc.config.core.db_location = DBPATH initialise_database() exp = load_or_create_experiment('2d_softsweep', sample_name='no sample') # define some test data x = np.linspace(0, 1., 5) y = np.linspace(0, 1., 5) xx, yy = np.meshgrid(x, y, indexing='ij') zz = np.random.rand(*xx.shape) # put data into a new dataset ds = new_data_set('2d_softsweep', specs=[ParamSpec('x', 'numeric', unit='A'), ParamSpec('y', 'numeric', unit='B'), ParamSpec('z', 'numeric', unit='C', depends_on=['x', 'y']), ], ) def get_next_result(): for x, y, z in zip(xx.reshape(-1), yy.reshape(-1), zz.reshape(-1)): yield dict(x=x, y=y, z=z) results = get_next_result() for r in results: ds.add_result(r) ds.mark_complete() # retrieve data as data dict run_id = ds.run_id ddict = datadict_from_path_and_run_id(DBPATH, run_id) assert np.all(np.isclose(ddict.data_vals('z'), zz.reshape(-1), atol=1e-15)) assert np.all(np.isclose(ddict.data_vals('x'), xx.reshape(-1), atol=1e-15)) assert np.all(np.isclose(ddict.data_vals('y'), yy.reshape(-1), atol=1e-15))
def dataset(experiment): dataset = new_data_set("test-dataset") try: yield dataset finally: dataset.unsubscribe_all() dataset.conn.close()
def test_numpy_types(): """ Test that we can save numpy types in the data set """ p = ParamSpecBase(name="p", paramtype="numeric") test_set = qc.new_data_set("test-dataset") test_set.set_interdependencies(InterDependencies_(standalones=(p, ))) test_set.mark_started() idps = InterDependencies_(standalones=(p, )) data_saver = DataSaver(dataset=test_set, write_period=0, interdeps=idps) dtypes = [ np.int8, np.int16, np.int32, np.int64, np.float16, np.float32, np.float64 ] for dtype in dtypes: data_saver.add_result(("p", dtype(2))) data_saver.flush_data_to_database() data = test_set.get_data("p") assert data == [[2] for _ in range(len(dtypes))]
def test_add_data_array(): exps = experiments() assert len(exps) == 1 exp = exps[0] assert exp.name == "test-experiment" assert exp.sample_name == "test-sample" assert exp.last_counter == 0 idps = InterDependencies_( standalones=(ParamSpecBase("x", "numeric"), ParamSpecBase("y", "array"))) mydataset = new_data_set("test") mydataset.set_interdependencies(idps) mydataset.mark_started() expected_x = np.arange(100) expected_y = [] for x in range(100): y = np.random.random_sample(10) expected_y.append(y) mydataset.add_results([{"x": x, "y": y}]) shadow_ds = make_shadow_dataset(mydataset) np.testing.assert_array_equal(mydataset.get_parameter_data()['x']['x'], np.array(expected_x)) np.testing.assert_array_equal(shadow_ds.get_parameter_data()['x']['x'], np.array(expected_x)) y_data = mydataset.get_parameter_data()['y']['y'] np.testing.assert_allclose(y_data, expected_y) y_data = shadow_ds.get_parameter_data()['y']['y'] np.testing.assert_allclose(y_data, expected_y)
def test_empty_ds_parameters(): ds = new_data_set("mydataset") assert ds.parameters is None ds.mark_started() assert ds.parameters is None ds.mark_completed() assert ds.parameters is None
def test_update_qcloader(qtbot): qc.config.core.db_location = DBPATH initialise_database() exp = load_or_create_experiment('2d_softsweep', sample_name='no sample') # define test data x = np.linspace(0, 1., 5) y = np.linspace(0, 1., 5) xx, yy = np.meshgrid(x, y, indexing='ij') zz = np.random.rand(*xx.shape) def get_2dsoftsweep_results(): for x, y, z in zip(xx.reshape(-1), yy.reshape(-1), zz.reshape(-1)): yield dict(x=x, y=y, z=z) # create data set _ds = new_data_set( '2d_softsweep', exp_id=exp.exp_id, specs=[ ParamSpec('x', 'numeric', unit='A'), ParamSpec('y', 'numeric', unit='B'), ParamSpec('z', 'numeric', unit='C', depends_on=['x', 'y']), ], ) run_id = _ds.run_id results = get_2dsoftsweep_results() # setting up the flowchart nodes, fc = make_sequential_flowchart([QCodesDSLoader]) loader = nodes[0] loader.pathAndId = DBPATH, run_id def check(): nresults = _ds.number_of_results loader.update() ddict = fc.output()['dataOut'] z_in = zz.reshape(-1)[:nresults] z_out = ddict.data_vals('z') if z_out is not None: assert z_in.size == z_out.size assert np.allclose(z_in, z_out, atol=1e-15) # insert data in small chunks, and check while True: try: ninsertions = np.random.randint(0, 5) for n in range(ninsertions): _ds.add_result(next(results)) except StopIteration: _ds.mark_complete() break check() check()
def _make_mock_empty_dataset(): dataset = new_data_set("dataset") xparam = ParamSpecBase("x", "numeric") yparam = ParamSpecBase("y", "numeric") zparam = ParamSpecBase("z", "numeric") idps = InterDependencies_(dependencies={yparam: (xparam,), zparam: (xparam,)}) dataset.set_interdependencies(idps) dataset.mark_started() dataset.mark_completed() return dataset
def _make_mock_dataset_complex(): dataset = new_data_set("dataset") xparam = ParamSpecBase("x", "numeric") yparam = ParamSpecBase("y", "complex") idps = InterDependencies_(dependencies={yparam: (xparam,)}) dataset.set_interdependencies(idps) dataset.mark_started() results = [{"x": 0, "y": 1 + 1j}] dataset.add_results(results) dataset.mark_completed() return dataset
def _make_mock_dataset_label_unit(): dataset = new_data_set("dataset") xparam = ParamSpecBase("x", "numeric", label="x label", unit="x unit") yparam = ParamSpecBase("y", "numeric", label="y label", unit="y unit") zparam = ParamSpecBase("z", "numeric", label="z label", unit="z unit") idps = InterDependencies_(dependencies={yparam: (xparam,), zparam: (xparam,)}) dataset.set_interdependencies(idps) dataset.mark_started() results = [{"x": 0, "y": 1, "z": 2}] dataset.add_results(results) dataset.mark_completed() return dataset
def _make_mock_dataset_nonunique_index(): dataset = new_data_set("dataset") xparam = ParamSpecBase("x", 'numeric') yparam = ParamSpecBase("y", 'numeric') zparam = ParamSpecBase("z", 'numeric') idps = InterDependencies_( dependencies={yparam: (xparam,), zparam: (xparam,)}) dataset.set_interdependencies(idps) dataset.mark_started() results = [{'x': 0, 'y': 1, 'z': 2}, {'x': 0, 'y': 1, 'z': 2}] dataset.add_results(results) dataset.mark_completed() return dataset
def test_string_via_dataset(experiment): """ Test that we can save text into database via DataSet API """ p = ParamSpec("p", "text") test_set = qc.new_data_set("test-dataset") test_set.add_parameter(p) test_set.add_result({"p": "some text"}) test_set.mark_complete() assert test_set.get_data("p") == [["some text"]]
def test_string(experiment): """ Test that we can save text in the data set """ p = ParamSpec("p", "text") test_set = qc.new_data_set("test-dataset") test_set.add_parameter(p) data_saver = DataSaver(dataset=test_set, write_period=0, parameters={"p": p}) data_saver.add_result(("p", "some text")) data_saver.flush_data_to_database() assert test_set.get_data("p") == [["some text"]]
def test_string_via_dataset(experiment): """ Test that we can save text into database via DataSet API """ p = ParamSpecBase("p", "text") test_set = qc.new_data_set("test-dataset") idps = InterDependencies_(standalones=(p, )) test_set.set_interdependencies(idps) test_set.mark_started() test_set.add_results([{"p": "some text"}]) test_set.mark_completed() assert test_set.get_parameter_data()["p"]["p"] == [["some text"]]
def test_modify_result(experiment): dataset = new_data_set("test_modify_result") xparam = ParamSpec("x", "numeric", label="x parameter", unit='V') yparam = ParamSpec("y", 'numeric', label='y parameter', unit='Hz', depends_on=[xparam]) zparam = ParamSpec("z", 'array', label='z parameter', unit='sqrt(Hz)', depends_on=[xparam]) dataset.add_parameter(xparam) dataset.add_parameter(yparam) dataset.add_parameter(zparam) xdata = 0 ydata = 1 zdata = np.linspace(0, 1, 100) dataset.add_result({'x': 0, 'y': 1, 'z': zdata}) assert dataset.get_data('x')[0][0] == xdata assert dataset.get_data('y')[0][0] == ydata assert (dataset.get_data('z')[0][0] == zdata).all() with pytest.raises(ValueError): dataset.modify_result(0, {' x': 1}) xdata = 1 ydata = 12 zdata = np.linspace(0, 1, 99) dataset.modify_result(0, {'x': xdata}) assert dataset.get_data('x')[0][0] == xdata dataset.modify_result(0, {'y': ydata}) assert dataset.get_data('y')[0][0] == ydata dataset.modify_result(0, {'z': zdata}) assert (dataset.get_data('z')[0][0] == zdata).all() dataset.mark_complete() with pytest.raises(CompletedError): dataset.modify_result(0, {'x': 2})
def test_write_data_to_text_file_save(): dataset = new_data_set("dataset") xparam = ParamSpecBase("x", 'numeric') yparam = ParamSpecBase("y", 'numeric') idps = InterDependencies_(dependencies={yparam: (xparam, )}) dataset.set_interdependencies(idps) dataset.mark_started() results = [{'x': 0, 'y': 1}] dataset.add_results(results) dataset.mark_completed() with tempfile.TemporaryDirectory() as temp_dir: dataset.write_data_to_text_file(path=temp_dir) assert os.listdir(temp_dir) == ['y.dat'] with open(temp_dir + "//y.dat") as f: assert f.readlines() == ['0\t1\n']
def test_write_data_to_text_file_save(tmp_path_factory): dataset = new_data_set("dataset") xparam = ParamSpecBase("x", 'numeric') yparam = ParamSpecBase("y", 'numeric') idps = InterDependencies_(dependencies={yparam: (xparam,)}) dataset.set_interdependencies(idps) dataset.mark_started() results = [{'x': 0, 'y': 1}] dataset.add_results(results) dataset.mark_completed() path = str(tmp_path_factory.mktemp("write_data_to_text_file_save")) dataset.write_data_to_text_file(path=path) assert os.listdir(path) == ['y.dat'] with open(os.path.join(path, "y.dat")) as f: assert f.readlines() == ['0\t1\n']
def test_add_data_1d(): exps = experiments() assert len(exps) == 1 exp = exps[0] assert exp.name == "test-experiment" assert exp.sample_name == "test-sample" assert exp.last_counter == 0 psx = ParamSpecBase("x", "numeric") psy = ParamSpecBase("y", "numeric") idps = InterDependencies_(dependencies={psy: (psx, )}) mydataset = new_data_set("test-dataset") mydataset.set_interdependencies(idps) mydataset.mark_started() expected_x = [] expected_y = [] for x in range(100): expected_x.append(x) y = 3 * x + 10 expected_y.append(y) mydataset.add_results([{"x": x, "y": y}]) shadow_ds = make_shadow_dataset(mydataset) np.testing.assert_array_equal(mydataset.get_parameter_data()['y']['x'], expected_x) np.testing.assert_array_equal(mydataset.get_parameter_data()['y']['y'], expected_y) np.testing.assert_array_equal(shadow_ds.get_parameter_data()['y']['x'], expected_x) np.testing.assert_array_equal(shadow_ds.get_parameter_data()['y']['y'], expected_y) assert mydataset.completed is False mydataset.mark_completed() assert mydataset.completed is True with pytest.raises(CompletedError): mydataset.add_results([{'y': 500}]) with pytest.raises(CompletedError): mydataset.add_results([{'x': 5}])