[docs]defcombine(*parameters:Parameter,name:str,label:str|None=None,unit:str|None=None,units:str|None=None,aggregator:Callable[...,Any]|None=None,)->CombinedParameter:""" Combine parameters into one sweepable parameter A combined parameter sets all the combined parameters at every point of the sweep. The sets are called in the same order the parameters are, and sequentially. Args: *parameters: The parameters to combine. name: The name of the paramter. label: The label of the combined parameter. unit: The unit of the combined parameter. units: Deprecated argument left for backwards compatibility. Do not use. aggregator: A function to aggregate the set values into one. """my_parameters=list(parameters)multi_par=CombinedParameter(my_parameters,name,label,unit,units,aggregator)returnmulti_par
[docs]classCombinedParameter(Metadatable):""" A combined parameter. It sets all the combined parameters at every point of the sweep. The sets are called in the same order the parameters are, and sequentially. Args: *parameters: The parameters to combine. name: The name of the parameter label: The label of the combined parameter unit: The unit of the combined parameter units: Deprecated argument left for backwards compatibility. Do not use. aggregator: A function to aggregate the set values into one """def__init__(self,parameters:Sequence[Parameter],name:str,label:str|None=None,unit:str|None=None,units:str|None=None,aggregator:Callable[...,Any]|None=None,)->None:super().__init__()# TODO(giulioungaretti)temporary hack# starthack# this is a dummy parameter# that mimicks the api that a normal parameter hasifnotname.isidentifier():raiseValueError(f"Parameter name must be a valid identifier "f"got {name} which is not. Parameter names "f"cannot start with a number and "f"must not contain spaces or special characters")self.parameter=lambda:None# mypy will complain that a callable does not have these attributes# but you can still create them here.self.parameter.full_name=name# type: ignore[attr-defined]self.parameter.name=name# type: ignore[attr-defined]self.parameter.label=label# type: ignore[attr-defined]ifunitsisnotNone:_LOG.warning(f"`units` is deprecated for the "f"`CombinedParameter` class, use `unit` instead. {self!r}")ifunitisNone:unit=unitsself.parameter.unit=unit# type: ignore[attr-defined]self.setpoints:list[Any]=[]# endhackself.parameters=parametersself.sets=[parameter.setforparameterinself.parameters]self.dimensionality=len(self.sets)ifaggregator:self.f=aggregatorsetattr(self,"aggregate",self._aggregate)
[docs]defset(self,index:int)->list[Any]:""" Set multiple parameters. Args: index: the index of the setpoints one wants to set Returns: list of values that where actually set """values=self.setpoints[index]forsetFunction,valueinzip(self.sets,values):setFunction(value)returnvalues
[docs]defsweep(self,*array:np.ndarray)->CombinedParameter:""" Creates a new combined parameter to be iterated over. One can sweep over either: - n array of length m - one nxm array where n is the number of combined parameters and m is the number of setpoints Args: *array: Array(s) of setpoints. Returns: combined parameter """# if it's a list of arrays, convert to one arrayiflen(array)>1:dim={len(a)forainarray}iflen(dim)!=1:raiseValueError("Arrays have different number of setpoints")nparray=np.array(array).transpose()eliflen(array)==1:# cast to array in case users# decide to not read docstring# and pass a 2d listnparray=np.array(array[0])else:raiseValueError("Need at least one array to sweep over.")new=copy(self)_error_msg=""" Dimensionality of array does not match\ the number of parameter combined. Expected a \{} dimensional array, got a {} dimensional array. \ """try:ifnparray.shape[1]!=self.dimensionality:raiseValueError(_error_msg.format(self.dimensionality,nparray.shape[1]))exceptKeyError:# this means the array is 1draiseValueError(_error_msg.format(self.dimensionality,1))# type safety. Since the dtype is not specified in this method# anything can be the dtype of the array which is not allowed# the user is responsible for calling this method with a# dtype that makes sensenew.setpoints=nparray.tolist()# type: ignore[assignment]returnnew
def_aggregate(self,*vals:Any)->Any:# check f argsreturnself.f(*vals)def__iter__(self)->Iterator[int]:returniter(range(len(self.setpoints)))def__len__(self)->int:# dimension of the sweep_values# i.e. how many setpointreturnnp.shape(self.setpoints)[0]
[docs]defsnapshot_base(self,update:bool|None=False,params_to_skip_update:Sequence[str]|None=None,)->dict[Any,Any]:""" State of the combined parameter as a JSON-compatible dict (everything that the custom JSON encoder class :class:`.NumpyJSONEncoder` supports). Args: update: ``True`` or ``False``. params_to_skip_update: Unused in this subclass. Returns: dict: Base snapshot. """meta_data:dict[str,Any]=collections.OrderedDict()meta_data["__class__"]=full_class(self)param=self.parametermeta_data["unit"]=param.unit# type: ignore[attr-defined]meta_data["label"]=param.label# type: ignore[attr-defined]meta_data["full_name"]=param.full_name# type: ignore[attr-defined]meta_data["aggregator"]=repr(getattr(self,"f",None))forparameterinself.parameters:meta_data[str(parameter)]=parameter.snapshot()returnmeta_data