def test_basic_subscription(dataset, basic_subscriber): xparam = ParamSpec(name='x', paramtype='numeric', label='x parameter', unit='V') yparam = ParamSpec(name='y', paramtype='numeric', label='y parameter', unit='Hz', depends_on=[xparam]) dataset.add_parameter(xparam) dataset.add_parameter(yparam) sub_id = dataset.subscribe(basic_subscriber, min_wait=0, min_count=1, state={}) assert len(dataset.subscribers) == 1 assert list(dataset.subscribers.keys()) == [sub_id] expected_state = {} for x in range(10): y = -x**2 dataset.add_result({'x': x, 'y': y}) expected_state[x + 1] = [(x, y)] assert dataset.subscribers[sub_id].state == expected_state
def test_copy(name1, name2): ps_indep = ParamSpec(name1, "numeric") ps = ParamSpec(name2, "numeric", depends_on=[ps_indep, 'other_param']) ps_copy = ps.copy() assert ps_copy == ps assert hash(ps_copy) == hash(ps) att_names = ["name", "type", "label", "unit", "_inferred_from", "_depends_on"] attributes = {} for att in att_names: val = getattr(ps, att) valc = getattr(ps_copy, att) assert val == valc attributes[att] = val # Modifying the copy should not change the original for att in att_names: if not att.startswith('_'): setattr(ps_copy, att, attributes[att] + "_modified") else: setattr(ps_copy, att, attributes[att] + ['bob']) assert getattr(ps, att) == attributes[att] assert ps_copy != ps assert hash(ps_copy) != hash(ps)
def test_depends_on(name1, name2, name3): ps2 = ParamSpec(name2, "numeric") ps3 = ParamSpec(name3, "numeric") ps1 = ParamSpec(name1, "numeric", depends_on=[ps2, ps3, 'foo']) assert ps1.depends_on == f"{ps2.name}, {ps3.name}, foo" assert ps1.depends_on_ == [ps2.name, ps3.name, "foo"]
def test_add_inferred_from(name1, name2, name3): ps1 = ParamSpec(name1, "numeric") ps2 = ParamSpec(name2, "numeric") ps3 = ParamSpec(name3, "numeric") ps1.add_inferred_from([ps2, ps3]) assert ps1.inferred_from == f"{ps2.name}, {ps3.name}"
def test_add_depends_on(name1, name2, name3): ps1 = ParamSpec(name1, "numeric") ps2 = ParamSpec(name2, "numeric") ps3 = ParamSpec(name3, "numeric") ps1.add_depends_on([ps2, ps3]) assert ps1.depends_on == f"{ps2.name}, {ps3.name}"
def test_wrong_input_raises(): for pspecs in [['p1', 'p2', 'p3'], [ParamSpec('p1', paramtype='numeric'), 'p2'], ['p1', ParamSpec('p2', paramtype='text')]]: with pytest.raises(ValueError): InterDependencies(pspecs)
def test_inferred_from(name1, name2, name3): ps2 = ParamSpec(name2, "numeric") ps3 = ParamSpec(name3, "numeric") ps1 = ParamSpec(name1, "numeric", inferred_from=[ps2, ps3, 'bar']) assert ps1.inferred_from == f"{ps2.name}, {ps3.name}, bar" assert ps1.inferred_from_ == [ps2.name, ps3.name, "bar"]
def test_repr(name): okay_types = ['array', 'numeric', 'text'] for okt in okay_types: if name.isidentifier(): ps = ParamSpec(name, okt) assert ps.__repr__() == f"{name} ({okt})" else: with pytest.raises(ValueError): ps = ParamSpec(name, okt)
def version_0_deserializations(): """ The paramspecs that the above serializations should deserialize to """ ps = [] ps.append(ParamSpec('dmm_v1', paramtype='numeric', label='Gate v1', unit='V', inferred_from=[], depends_on=['dac_ch1', 'dac_ch2'])) ps.append(ParamSpec('some_name', paramtype='array', label='My Array ParamSpec', unit='Ars', inferred_from=['p1', 'p2'], depends_on=[])) return ps
def test_repr(name): okay_types = ['array', 'numeric', 'text'] for okt in okay_types: if name.isidentifier(): ps = ParamSpec(name, okt) expected_repr = (f"ParamSpec('{name}', '{okt}', '', '', " "inferred_from=[], depends_on=[])") assert ps.__repr__() == expected_repr else: with pytest.raises(ValueError): ps = ParamSpec(name, okt)
def test_atomic_creation(experiment): """" Test that dataset creation is atomic. Test for https://github.com/QCoDeS/Qcodes/issues/1444 """ def just_throw(*args): raise RuntimeError("This breaks adding metadata") # first we patch add_meta_data to throw an exception # if create_data is not atomic this would create a partial # run in the db. Causing the next create_run to fail with patch('qcodes.dataset.sqlite.queries.add_meta_data', new=just_throw): x = ParamSpec('x', 'numeric') t = ParamSpec('t', 'numeric') y = ParamSpec('y', 'numeric', depends_on=['x', 't']) with pytest.raises(RuntimeError, match="Rolling back due to unhandled exception")as e: mut_queries.create_run(experiment.conn, experiment.exp_id, name='testrun', guid=generate_guid(), parameters=[x, t, y], metadata={'a': 1}) assert error_caused_by(e, "This breaks adding metadata") # since we are starting from an empty database and the above transaction # should be rolled back there should be no runs in the run table runs = mut_conn.transaction(experiment.conn, 'SELECT run_id FROM runs').fetchall() assert len(runs) == 0 with shadow_conn(experiment.path_to_db) as new_conn: runs = mut_conn.transaction(new_conn, 'SELECT run_id FROM runs').fetchall() assert len(runs) == 0 # if the above was not correctly rolled back we # expect the next creation of a run to fail mut_queries.create_run(experiment.conn, experiment.exp_id, name='testrun', guid=generate_guid(), parameters=[x, t, y], metadata={'a': 1}) runs = mut_conn.transaction(experiment.conn, 'SELECT run_id FROM runs').fetchall() assert len(runs) == 1 with shadow_conn(experiment.path_to_db) as new_conn: runs = mut_conn.transaction(new_conn, 'SELECT run_id FROM runs').fetchall() assert len(runs) == 1
def test_depends_on(name1, name2, name3): ps2 = ParamSpec(name2, "numeric") ps3 = ParamSpec(name3, "numeric") ps1 = ParamSpec(name1, "numeric", depends_on=[ps2, ps3, 'foo']) assert ps1.depends_on == f"{ps2.name}, {ps3.name}, foo" assert ps1.depends_on_ == [ps2.name, ps3.name, "foo"] with pytest.raises(ValueError, match=f"ParamSpec {name1} got string foo as depends_on. " "It needs a Sequence of ParamSpecs or strings"): ParamSpec(name1, "numeric", depends_on='foo')
def test_inferred_from(name1, name2, name3): ps2 = ParamSpec(name2, "numeric") ps3 = ParamSpec(name3, "numeric") ps1 = ParamSpec(name1, "numeric", inferred_from=[ps2, ps3, 'bar']) assert ps1.inferred_from == f"{ps2.name}, {ps3.name}, bar" assert ps1.inferred_from_ == [ps2.name, ps3.name, "bar"] with pytest.raises(ValueError, match=f"ParamSpec {name1} got string foo as " f"inferred_from. " "It needs a Sequence of ParamSpecs or strings"): ParamSpec(name1, "numeric", inferred_from='foo')
def some_paramspecs(): """ Some different paramspecs for testing. The idea is that we just add a new group of paramspecs as the need arises """ groups = {} # A valid group. Corresponding to a heatmap with a text label at each point first = {} first['ps1'] = ParamSpec('ps1', paramtype='numeric', label='Raw Data 1', unit='V') first['ps2'] = ParamSpec('ps2', paramtype='array', label='Raw Data 2', unit='V') first['ps3'] = ParamSpec('ps3', paramtype='text', label='Axis 1', unit='', inferred_from=[first['ps1']]) first['ps4'] = ParamSpec('ps4', paramtype='numeric', label='Axis 2', unit='V', inferred_from=[first['ps2']]) first['ps5'] = ParamSpec('ps5', paramtype='numeric', label='Signal', unit='Conductance', depends_on=[first['ps3'], first['ps4']]) first['ps6'] = ParamSpec('ps6', paramtype='text', label='Goodness', unit='', depends_on=[first['ps3'], first['ps4']]) groups[1] = first # a small, valid group second = {} second['ps1'] = ParamSpec('ps1', paramtype='numeric', label='setpoint', unit='Hz') second['ps2'] = ParamSpec('ps2', paramtype='numeric', label='signal', unit='V', depends_on=[second['ps1']]) groups[2] = second return groups
def _register_parameter_with_setpoints(self, parameter: ParameterWithSetpoints, setpoints: Optional[setpoints_type], basis: Optional[setpoints_type], paramtype: str) -> None: """ Register an ParameterWithSetpoints and the setpoints belonging to the Parameter """ name = str(parameter) my_setpoints = list(setpoints) if setpoints else [] for sp in parameter.setpoints: if not isinstance(sp, Parameter): raise RuntimeError("The setpoints of a " "ParameterWithSetpoints " "must be a Parameter") spname = sp.full_name splabel = sp.label spunit = sp.unit spparamspec = ParamSpec(name=spname, paramtype=paramtype, label=splabel, unit=spunit) self.parameters[spname] = spparamspec my_setpoints.append(spname) self._register_parameter(name, parameter.label, parameter.unit, my_setpoints, basis, paramtype)
def _register_parameter(self, name: str, label: str, unit: str, setpoints: setpoints_type, basis: setpoints_type, paramtype: str) -> None: """ Generate ParamSpecs and register them for an individual parameter """ if setpoints is not None: sp_strings = [str(sp) for sp in setpoints] else: sp_strings = [] if basis is not None: bs_strings = [str(bs) for bs in basis] else: bs_strings = [] # validate all dependencies depends_on, inf_from = self._registration_validation(name, sp_strings, bs_strings) paramspec = ParamSpec(name=name, paramtype=paramtype, label=label, unit=unit, inferred_from=inf_from, depends_on=depends_on) # ensure the correct order if name in self.parameters.keys(): self.parameters.pop(name) self.parameters[name] = paramspec log.info(f'Registered {name} in the Measurement.')
def test_numpy_types(): """ Test that we can save numpy types in the data set """ p = ParamSpec(name="p", paramtype="numeric") test_set = qc.new_data_set("test-dataset") test_set.add_parameter(p) test_set.mark_started() idps = InterDependencies_(standalones=(p.base_version(), )) 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_saving_numeric_values_as_text(numeric_type): """ Test the saving numeric values into 'text' parameter raises an exception """ p = ParamSpec("p", "text") test_set = qc.new_data_set("test-dataset") test_set.add_parameter(p) test_set.mark_started() idps = InterDependencies_(standalones=(p.base_version(), )) 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_basic_subscription(dataset, basic_subscriber): xparam = ParamSpec(name='x', paramtype='numeric', label='x parameter', unit='V') yparam = ParamSpec(name='y', paramtype='numeric', label='y parameter', unit='Hz', depends_on=[xparam]) dataset.add_parameter(xparam) dataset.add_parameter(yparam) sub_id = dataset.subscribe(basic_subscriber, min_wait=0, min_count=1, state={}) assert len(dataset.subscribers) == 1 assert list(dataset.subscribers.keys()) == [sub_id] expected_state = {} for x in range(10): y = -x**2 dataset.add_result({'x': x, 'y': y}) expected_state[x + 1] = [(x, y)] @retry_until_does_not_throw(exception_class_to_expect=AssertionError, delay=0, tries=10) def assert_expected_state(): assert dataset.subscribers[sub_id].state == expected_state assert_expected_state() dataset.unsubscribe(sub_id) assert len(dataset.subscribers) == 0 assert list(dataset.subscribers.keys()) == [] # Ensure the trigger for the subscriber have been removed from the database get_triggers_sql = "SELECT * FROM sqlite_master WHERE TYPE = 'trigger';" triggers = atomic_transaction(dataset.conn, get_triggers_sql).fetchall() assert len(triggers) == 0
def test_new_to_old(some_paramspecbases): (ps1, ps2, ps3, ps4) = some_paramspecbases idps_new = InterDependencies_(dependencies={ps1: (ps2, ps3)}, standalones=(ps4, )) paramspec1 = ParamSpec(name=ps1.name, paramtype=ps1.type, label=ps1.label, unit=ps1.unit, depends_on=[ps2.name, ps3.name]) paramspec2 = ParamSpec(name=ps2.name, paramtype=ps2.type, label=ps2.label, unit=ps2.unit) paramspec3 = ParamSpec(name=ps3.name, paramtype=ps3.type, label=ps3.label, unit=ps3.unit) paramspec4 = ParamSpec(name=ps4.name, paramtype=ps4.type, label=ps4.label, unit=ps4.unit) idps_old_expected = InterDependencies(paramspec2, paramspec3, paramspec1, paramspec4) assert new_to_old(idps_new) == idps_old_expected # idps_new = InterDependencies_(inferences={ps1: (ps2, ps3)}, standalones=(ps4, )) paramspec1 = ParamSpec(name=ps1.name, paramtype=ps1.type, label=ps1.label, unit=ps1.unit, inferred_from=[ps2.name, ps3.name]) paramspec2 = ParamSpec(name=ps2.name, paramtype=ps2.type, label=ps2.label, unit=ps2.unit) paramspec3 = ParamSpec(name=ps3.name, paramtype=ps3.type, label=ps3.label, unit=ps3.unit) paramspec4 = ParamSpec(name=ps4.name, paramtype=ps4.type, label=ps4.label, unit=ps4.unit) idps_old_expected = InterDependencies(paramspec2, paramspec3, paramspec1, paramspec4) assert new_to_old(idps_new) == idps_old_expected
def test_creation(name, sp1, sp2, inff1, inff2, paramtype): invalid_types = ['np.array', 'ndarray', 'lala', '', Number, ndarray, 0, None] for inv_type in invalid_types: with pytest.raises(ValueError): ParamSpec(name, inv_type) if not inff1.isidentifier(): inff1 = 'inff1' if not sp1.isidentifier(): sp1 = 'sp1' if not name.isidentifier(): with pytest.raises(ValueError): ps = ParamSpec(name, paramtype[0], label=None, unit='V', inferred_from=(inff1, inff2), depends_on=(sp1, sp2)) name = 'name' ps = ParamSpec(name, paramtype[1], label=None, unit='V', inferred_from=(inff1, inff2), depends_on=(sp1, sp2)) assert ps.inferred_from == f'{inff1}, {inff2}' assert ps.depends_on == f'{sp1}, {sp2}' ps1 = ParamSpec(sp1, paramtype[2]) p1 = ParamSpec(name, paramtype[3], depends_on=(ps1, sp2)) assert p1.depends_on == ps.depends_on ps2 = ParamSpec(inff1, paramtype[4]) p2 = ParamSpec(name, paramtype[5], inferred_from=(ps2, inff2)) assert p2.inferred_from == ps.inferred_from
def test_hash(paramspecs): p1 = ParamSpec(**paramspecs[0]) p2 = ParamSpec(**paramspecs[1]) # call __hash__ p1_h = hash(p1) p2_h = hash(p2) # make a set p_set = {p1, p2} # test that the hash equality follows object equality if p1 == p2: assert p1_h == p2_h assert 1 == len(p_set) else: assert p1_h != p2_h assert 2 == len(p_set)
def scalar_dataset_with_nulls(dataset): """ A very simple dataset. A scalar is varied, and two parameters are measured one by one """ sp = ParamSpec('setpoint', 'numeric') val1 = ParamSpec('first_value', 'numeric', depends_on=(sp,)) val2 = ParamSpec('second_value', 'numeric', depends_on=(sp,)) for p in [sp, val1, val2]: dataset.add_parameter(p) dataset.mark_started() dataset.add_results([{sp.name: 0, val1.name: 1}, {sp.name: 0, val2.name: 2}]) dataset.mark_completed() yield dataset
def test_base_version(paramspecs): kwargs = paramspecs[0] ps = ParamSpec(**kwargs) ps_base = ParamSpecBase(name=kwargs['name'], paramtype=kwargs['paramtype'], label=kwargs['label'], unit=kwargs['unit']) assert ps.base_version() == ps_base
def scalar_dataset(dataset): n_params = 3 n_rows = 10**3 params_indep = [ParamSpec(f'param_{i}', 'numeric', label=f'param_{i}', unit='V') for i in range(n_params)] params = params_indep + [ParamSpec(f'param_{n_params}', 'numeric', label=f'param_{n_params}', unit='Ohm', depends_on=params_indep)] for p in params: dataset.add_parameter(p) dataset.mark_started() dataset.add_results([{p.name: np.int(n_rows*10*pn+i) for pn, p in enumerate(params)} for i in range(n_rows)]) dataset.mark_completed() yield dataset
def test_serialize(): p1 = ParamSpec('p1', 'numeric', 'paramspec one', 'no unit', depends_on=['some', 'thing'], inferred_from=['bab', 'bob']) ser = p1.serialize() assert ser['name'] == p1.name assert ser['paramtype'] == p1.type assert ser['label'] == p1.label assert ser['unit'] == p1.unit assert ser['depends_on'] == p1._depends_on assert ser['inferred_from'] == p1._inferred_from
def test_copy(name1, name2, name3): ps_indep = ParamSpec(name1, "numeric") ps_indep_2 = ParamSpec(name2, "numeric") ps = ParamSpec(name3, "numeric", depends_on=[ps_indep]) ps_copy = ps.copy() attributes = {} for att in ["name", "type", "label", "unit"]: val = getattr(ps, att) valc = getattr(ps_copy, att) assert val == valc attributes[att] = val # Modifying the copy should not change the original for att in ["name", "type", "label", "unit"]: setattr(ps_copy, att, attributes[att] + "_modified") assert getattr(ps, att) == attributes[att] ps_copy.add_depends_on([ps_indep_2]) assert ps_copy.depends_on == f"{ps_indep.name}, {ps_indep_2.name}" assert ps.depends_on == f"{ps_indep.name}"
def new_to_old(idps: InterDependencies_) -> InterDependencies: """ Create a new InterDependencies object (old style) from an existing InterDependencies_ object (new style). Leaves the original object unchanged. Only meant to be used for ensuring backwards-compatibility until we update sqlite module to forget about ParamSpecs """ paramspecs: Dict[str, ParamSpec] = {} # first the independent parameters for indeps in idps.dependencies.values(): for indep in indeps: paramspecs.update({indep.name: ParamSpec(name=indep.name, paramtype=indep.type, label=indep.label, unit=indep.unit)}) for inffs in idps.inferences.values(): for inff in inffs: paramspecs.update({inff.name: ParamSpec(name=inff.name, paramtype=inff.type, label=inff.label, unit=inff.unit)}) for ps_base in idps._paramspec_to_id.keys(): paramspecs.update({ps_base.name: ParamSpec(name=ps_base.name, paramtype=ps_base.type, label=ps_base.label, unit=ps_base.unit)}) for ps, indeps in idps.dependencies.items(): for indep in indeps: paramspecs[ps.name]._depends_on.append(indep.name) for ps, inffs in idps.inferences.items(): for inff in inffs: paramspecs[ps.name]._inferred_from.append(inff.name) return InterDependencies(*tuple(paramspecs.values()))
def register_custom_parameter( self, name: str, label: str = None, unit: str = None, basis: Sequence[Union[str, _BaseParameter]] = None, setpoints: Sequence[Union[str, _BaseParameter]] = None) -> None: """ Register a custom parameter with this measurement Args: name: The name that this parameter will have in the dataset. Must be unique (will overwrite an existing parameter with the same name!) label: The label unit: The unit basis: A list of either QCoDeS Parameters or the names of parameters already registered in the measurement that this parameter is inferred from setpoints: A list of either QCoDeS Parameters or the names of of parameters already registered in the measurement that are the setpoints of this parameter """ # validate dependencies if setpoints: sp_strings = [str(sp) for sp in setpoints] else: sp_strings = [] if basis: bs_strings = [str(bs) for bs in basis] else: bs_strings = [] # validate all dependencies depends_on, inf_from = self._registration_validation( name, sp_strings, bs_strings) parspec = ParamSpec(name=name, paramtype='numeric', label=label, unit=unit, inferred_from=inf_from, depends_on=depends_on) # ensure the correct order if name in self.parameters.keys(): self.parameters.pop(name) self.parameters[name] = parspec
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"]]