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_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_not_eq_for_list_attr(): """ test that two paramspecs that differ only in list attrs are different """ p1 = ParamSpec(name='foo', paramtype='numeric', depends_on=['a', 'b']) p2 = ParamSpec(name='foo', paramtype='numeric', depends_on=['c', 'd']) assert p1 != p2
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_data_to_dynamic_columns 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_data_to_dynamic_columns", 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_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 version_0_objects(): """ The ParamSpecs that the dictionaries above represent """ 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_convert_to_dict(): p1 = ParamSpec('p1', 'numeric', 'paramspec one', 'no unit', depends_on=['some', 'thing'], inferred_from=['bab', 'bob']) ser = p1._to_dict() 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_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_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 test_not_eq_for_str_attr(): """ test that two paramspecs that differ only in str attrs are different """ p1 = ParamSpec(name='foo', label='myfoo', paramtype='numeric', depends_on=['a', 'b']) p2 = ParamSpec(name='foo', label='someotherfoo', paramtype='numeric', depends_on=['a', 'b']) assert p1 != p2
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 _from_dict(cls, ser: Dict[str, Any]) -> 'InterDependencies': """ Create an InterDependencies object from a dictionary """ paramspecs = [ParamSpec._from_dict(sps) for sps in ser['paramspecs']] idp = cls(*paramspecs) return idp
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 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 _2to3_get_paramspecs( conn: ConnectionPlus, layout_ids: List[int], layouts: Mapping[int, Tuple[str, str, str, str]], dependencies: Mapping[int, Sequence[int]], deps: Sequence[int], indeps: Sequence[int], result_table_name: str, ) -> Dict[int, ParamSpec]: paramspecs: Dict[int, ParamSpec] = {} the_rest = set(layout_ids).difference(set(deps).union(set(indeps))) # We ensure that we first retrieve the ParamSpecs on which other ParamSpecs # depend, then the dependent ParamSpecs and finally the rest for layout_id in list(indeps) + list(deps) + list(the_rest): (name, label, unit, inferred_from_str) = layouts[layout_id] # get the data type sql = f'PRAGMA TABLE_INFO("{result_table_name}")' c = transaction(conn, sql) paramtype = None for row in c.fetchall(): if row['name'] == name: paramtype = row['type'] break if paramtype is None: raise TypeError(f"Could not determine type of {name} during the" f"db upgrade of {result_table_name}") inferred_from: List[str] = [] depends_on: List[str] = [] # this parameter depends on another parameter if layout_id in deps: setpoints = dependencies[layout_id] depends_on = [paramspecs[idp].name for idp in setpoints] if inferred_from_str != '': inferred_from = inferred_from_str.split(', ') paramspec = ParamSpec(name=name, paramtype=paramtype, label=label, unit=unit, depends_on=depends_on, inferred_from=inferred_from) paramspecs[layout_id] = paramspec return paramspecs
def _extend_with_paramspec(self, ps: ParamSpec) -> 'InterDependencies_': """ Create a new InterDependencies_ object extended with the provided ParamSpec. A helper function for DataSet's add_parameter function. Note that this function will only work as expected if the ParamSpecs are extended into the InterDependencies_ in the "logical order", i.e. independent ParamSpecs before dependent ones. """ base_ps = ps.base_version() old_standalones = set(self.standalones.copy()) new_standalones: Tuple[ParamSpecBase, ...] if len(ps.depends_on_) > 0: deps_list = [ self._id_to_paramspec[name] for name in ps.depends_on_ ] new_deps = {base_ps: tuple(deps_list)} old_standalones = old_standalones.difference(set(deps_list)) else: new_deps = {} if len(ps.inferred_from_) > 0: inffs_list = [ self._id_to_paramspec[name] for name in ps.inferred_from_ ] new_inffs = {base_ps: tuple(inffs_list)} old_standalones = old_standalones.difference(set(inffs_list)) else: new_inffs = {} if new_deps == new_inffs == {}: new_standalones = (base_ps, ) else: old_standalones = old_standalones.difference({base_ps}) new_standalones = () new_deps.update(self.dependencies.copy()) new_inffs.update(self.inferences.copy()) new_standalones = tuple(list(new_standalones) + list(old_standalones)) new_idps = InterDependencies_(dependencies=new_deps, inferences=new_inffs, standalones=new_standalones) return new_idps
def test_hash_with_deferred_and_inferred_as_paramspecs(paramspecs, add_to_1_inf, add_to_1_dep, add_to_2_inf, add_to_2_dep): """ Test that hashing works if 'inferred_from' and/or 'depends_on' contain actual ParamSpec instances and not just strings. """ assume(add_to_1_inf or add_to_1_dep or add_to_2_inf or add_to_2_dep) # Add ParamSpecs to 'inferred_from' and/or 'depend_on' lists next to # strings (that are generated by the main strategy) if add_to_1_inf: paramspecs[0]['inferred_from'].append(ParamSpec(**paramspecs[2])) if add_to_1_dep: paramspecs[0]['depends_on'].append(ParamSpec(**paramspecs[3])) if add_to_2_inf: paramspecs[1]['inferred_from'].append(ParamSpec(**paramspecs[4])) if add_to_2_dep: paramspecs[1]['depends_on'].append(ParamSpec(**paramspecs[5])) 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 test_get_dependents(experiment): x = ParamSpec('x', 'numeric') t = ParamSpec('t', 'numeric') y = ParamSpec('y', 'numeric', depends_on=['x', 't']) # Make a dataset (_, run_id, _) = mut_queries.create_run(experiment.conn, experiment.exp_id, name='testrun', guid=generate_guid(), parameters=[x, t, y]) deps = mut_queries._get_dependents(experiment.conn, run_id) layout_id = mut_queries._get_layout_id(experiment.conn, 'y', run_id) assert deps == [layout_id] # more parameters, more complicated dependencies x_raw = ParamSpec('x_raw', 'numeric') x_cooked = ParamSpec('x_cooked', 'numeric', inferred_from=['x_raw']) z = ParamSpec('z', 'numeric', depends_on=['x_cooked']) (_, run_id, _) = mut_queries.create_run(experiment.conn, experiment.exp_id, name='testrun', guid=generate_guid(), parameters=[x, t, x_raw, x_cooked, y, z]) deps = mut_queries._get_dependents(experiment.conn, run_id) expected_deps = [ mut_queries._get_layout_id(experiment.conn, 'y', run_id), mut_queries._get_layout_id(experiment.conn, 'z', run_id) ] assert deps == expected_deps
def make_ps(n): ps = ParamSpec(f'p{n}', label=f'Parameter {n}', unit=f'unit {n}', paramtype='numeric') return ps
def get_paramspec(conn: ConnectionPlus, run_id: int, param_name: str) -> ParamSpec: """ Get the ParamSpec object for the given parameter name in the given run Args: conn: Connection to the database run_id: The run id param_name: The name of the parameter """ # get table name sql = f""" SELECT result_table_name FROM runs WHERE run_id = {run_id} """ c = conn.execute(sql) result_table_name = one(c, 'result_table_name') # get the data type sql = f""" PRAGMA TABLE_INFO("{result_table_name}") """ c = conn.execute(sql) for row in c.fetchall(): if row['name'] == param_name: param_type = row['type'] break # get everything else sql = f""" SELECT * FROM layouts WHERE parameter="{param_name}" and run_id={run_id} """ c = conn.execute(sql) resp = many(c, 'layout_id', 'run_id', 'parameter', 'label', 'unit', 'inferred_from') (layout_id, _, _, label, unit, inferred_from_string) = resp if inferred_from_string: inferred_from = inferred_from_string.split(', ') else: inferred_from = [] deps = get_dependencies(conn, layout_id) depends_on: Optional[List[str]] if len(deps) == 0: depends_on = None else: dps: List[int] = [dp[0] for dp in deps] ax_nums: List[int] = [dp[1] for dp in deps] depends_on = [] for _, dp in sorted(zip(ax_nums, dps)): sql = f""" SELECT parameter FROM layouts WHERE layout_id = {dp} """ c = conn.execute(sql) depends_on.append(one(c, 'parameter')) parspec = ParamSpec(param_name, param_type, label, unit, inferred_from, depends_on) return parspec
def test_from_dict(version_0_dicts, version_0_objects): for sdict, ps in zip(version_0_dicts, version_0_objects): deps = ParamSpec._from_dict(sdict) assert ps == deps