diff options
Diffstat (limited to 'lib/parameters.py')
-rw-r--r-- | lib/parameters.py | 450 |
1 files changed, 320 insertions, 130 deletions
diff --git a/lib/parameters.py b/lib/parameters.py index 41e312a..8b562b6 100644 --- a/lib/parameters.py +++ b/lib/parameters.py @@ -21,8 +21,10 @@ def distinct_param_values(by_name, state_or_tran): write() or similar has not been called yet. Other parameters should always be initialized when leaving UNINITIALIZED. """ - distinct_values = [OrderedDict() for i in range(len(by_name[state_or_tran]['param'][0]))] - for param_tuple in by_name[state_or_tran]['param']: + distinct_values = [ + OrderedDict() for i in range(len(by_name[state_or_tran]["param"][0])) + ] + for param_tuple in by_name[state_or_tran]["param"]: for i in range(len(param_tuple)): distinct_values[i][param_tuple[i]] = True @@ -30,8 +32,9 @@ def distinct_param_values(by_name, state_or_tran): distinct_values = list(map(lambda x: list(x.keys()), distinct_values)) return distinct_values + def _depends_on_param(corr_param, std_param, std_lut): - #if self.use_corrcoef: + # if self.use_corrcoef: if False: return corr_param > 0.1 elif std_param == 0: @@ -40,6 +43,7 @@ def _depends_on_param(corr_param, std_param, std_lut): return False return std_lut / std_param < 0.5 + def _reduce_param_matrix(matrix: np.ndarray, parameter_names: list) -> list: """ :param matrix: parameter dependence matrix, M[(...)] == 1 iff (model attribute) is influenced by (parameter) for other parameter value indxe == (...) @@ -53,7 +57,7 @@ def _reduce_param_matrix(matrix: np.ndarray, parameter_names: list) -> list: # Diese Abbruchbedingung scheint noch nicht so schlau zu sein... # Mit wird zu viel rausgefiltert (z.B. auto_ack! -> max_retry_count in "bin/analyze-timing.py ../data/20190815_122531_nRF24_no-rx.json" nicht erkannt) # Ohne wird zu wenig rausgefiltert (auch ganz viele Abhängigkeiten erkannt, bei denen eine Parameter-Abhängigketi immer unabhängig vom Wert der anderen Parameter besteht) - #if not is_power_of_two(np.count_nonzero(matrix)): + # if not is_power_of_two(np.count_nonzero(matrix)): # # cannot be reliably reduced to a list of parameters # return list() @@ -65,20 +69,23 @@ def _reduce_param_matrix(matrix: np.ndarray, parameter_names: list) -> list: return influential_parameters for axis in range(matrix.ndim): - candidate = _reduce_param_matrix(np.all(matrix, axis=axis), remove_index_from_tuple(parameter_names, axis)) + candidate = _reduce_param_matrix( + np.all(matrix, axis=axis), remove_index_from_tuple(parameter_names, axis) + ) if len(candidate): return candidate return list() + def _codependent_parameters(param, lut_by_param_values, std_by_param_values): """ Return list of parameters which affect whether a parameter affects a model attribute or not. """ return list() - safe_div = np.vectorize(lambda x,y: 0. if x == 0 else 1 - x/y) + safe_div = np.vectorize(lambda x, y: 0.0 if x == 0 else 1 - x / y) ratio_by_value = safe_div(lut_by_param_values, std_by_param_values) - err_mode = np.seterr('ignore') + err_mode = np.seterr("ignore") dep_by_value = ratio_by_value > 0.5 np.seterr(**err_mode) @@ -86,7 +93,10 @@ def _codependent_parameters(param, lut_by_param_values, std_by_param_values): influencer_parameters = _reduce_param_matrix(dep_by_value, other_param_list) return influencer_parameters -def _std_by_param(by_param, all_param_values, state_or_tran, attribute, param_index, verbose = False): + +def _std_by_param( + by_param, all_param_values, state_or_tran, attribute, param_index, verbose=False +): u""" Calculate standard deviations for a static model where all parameters but `param_index` are constant. @@ -130,7 +140,10 @@ def _std_by_param(by_param, all_param_values, state_or_tran, attribute, param_in param_partition = list() std_list = list() for k, v in by_param.items(): - if k[0] == state_or_tran and (*k[1][:param_index], *k[1][param_index+1:]) == param_value: + if ( + k[0] == state_or_tran + and (*k[1][:param_index], *k[1][param_index + 1 :]) == param_value + ): param_partition.extend(v[attribute]) std_list.append(np.std(v[attribute])) @@ -143,17 +156,26 @@ def _std_by_param(by_param, all_param_values, state_or_tran, attribute, param_in lut_matrix[matrix_index] = np.mean(std_list) # This can (and will) happen in normal operation, e.g. when a transition's # arguments are combined using 'zip' rather than 'cartesian'. - #elif len(param_partition) == 1: + # elif len(param_partition) == 1: # vprint(verbose, '[W] parameter value partition for {} contains only one element -- skipping'.format(param_value)) - #else: + # else: # vprint(verbose, '[W] parameter value partition for {} is empty'.format(param_value)) if np.all(np.isnan(stddev_matrix)): - print('[W] {}/{} parameter #{} has no data partitions -- how did this even happen?'.format(state_or_tran, attribute, param_index)) - print('stddev_matrix = {}'.format(stddev_matrix)) - return stddev_matrix, 0. + print( + "[W] {}/{} parameter #{} has no data partitions -- how did this even happen?".format( + state_or_tran, attribute, param_index + ) + ) + print("stddev_matrix = {}".format(stddev_matrix)) + return stddev_matrix, 0.0 + + return ( + stddev_matrix, + np.nanmean(stddev_matrix), + lut_matrix, + ) # np.mean([np.std(partition) for partition in partitions]) - return stddev_matrix, np.nanmean(stddev_matrix), lut_matrix #np.mean([np.std(partition) for partition in partitions]) def _corr_by_param(by_name, state_or_trans, attribute, param_index): """ @@ -169,22 +191,46 @@ def _corr_by_param(by_name, state_or_trans, attribute, param_index): :param param_index: index of parameter in `by_name[state_or_trans]['param']` """ if _all_params_are_numeric(by_name[state_or_trans], param_index): - param_values = np.array(list((map(lambda x: x[param_index], by_name[state_or_trans]['param'])))) + param_values = np.array( + list((map(lambda x: x[param_index], by_name[state_or_trans]["param"]))) + ) try: return np.corrcoef(by_name[state_or_trans][attribute], param_values)[0, 1] except FloatingPointError: # Typically happens when all parameter values are identical. # Building a correlation coefficient is pointless in this case # -> assume no correlation - return 0. + return 0.0 except ValueError: - print('[!] Exception in _corr_by_param(by_name, state_or_trans={}, attribute={}, param_index={})'.format(state_or_trans, attribute, param_index)) - print('[!] while executing np.corrcoef(by_name[{}][{}]={}, {}))'.format(state_or_trans, attribute, by_name[state_or_trans][attribute], param_values)) + print( + "[!] Exception in _corr_by_param(by_name, state_or_trans={}, attribute={}, param_index={})".format( + state_or_trans, attribute, param_index + ) + ) + print( + "[!] while executing np.corrcoef(by_name[{}][{}]={}, {}))".format( + state_or_trans, + attribute, + by_name[state_or_trans][attribute], + param_values, + ) + ) raise else: - return 0. - -def _compute_param_statistics(by_name, by_param, parameter_names, arg_count, state_or_trans, attribute, distinct_values, distinct_values_by_param_index, verbose = False): + return 0.0 + + +def _compute_param_statistics( + by_name, + by_param, + parameter_names, + arg_count, + state_or_trans, + attribute, + distinct_values, + distinct_values_by_param_index, + verbose=False, +): """ Compute standard deviation and correlation coefficient for various data partitions. @@ -223,87 +269,140 @@ def _compute_param_statistics(by_name, by_param, parameter_names, arg_count, sta Only set if state_or_trans appears in arg_count, empty dict otherwise. """ ret = { - 'std_static' : np.std(by_name[state_or_trans][attribute]), - 'std_param_lut' : np.mean([np.std(by_param[x][attribute]) for x in by_param.keys() if x[0] == state_or_trans]), - 'std_by_param' : {}, - 'std_by_param_values' : {}, - 'lut_by_param_values' : {}, - 'std_by_arg' : [], - 'std_by_arg_values' : [], - 'lut_by_arg_values' : [], - 'corr_by_param' : {}, - 'corr_by_arg' : [], - 'depends_on_param' : {}, - 'depends_on_arg' : [], - 'param_data' : {}, + "std_static": np.std(by_name[state_or_trans][attribute]), + "std_param_lut": np.mean( + [ + np.std(by_param[x][attribute]) + for x in by_param.keys() + if x[0] == state_or_trans + ] + ), + "std_by_param": {}, + "std_by_param_values": {}, + "lut_by_param_values": {}, + "std_by_arg": [], + "std_by_arg_values": [], + "lut_by_arg_values": [], + "corr_by_param": {}, + "corr_by_arg": [], + "depends_on_param": {}, + "depends_on_arg": [], + "param_data": {}, } - np.seterr('raise') + np.seterr("raise") for param_idx, param in enumerate(parameter_names): - std_matrix, mean_std, lut_matrix = _std_by_param(by_param, distinct_values_by_param_index, state_or_trans, attribute, param_idx, verbose) - ret['std_by_param'][param] = mean_std - ret['std_by_param_values'][param] = std_matrix - ret['lut_by_param_values'][param] = lut_matrix - ret['corr_by_param'][param] = _corr_by_param(by_name, state_or_trans, attribute, param_idx) - - ret['depends_on_param'][param] = _depends_on_param(ret['corr_by_param'][param], ret['std_by_param'][param], ret['std_param_lut']) - - if ret['depends_on_param'][param]: - ret['param_data'][param] = { - 'codependent_parameters': _codependent_parameters(param, lut_matrix, std_matrix), - 'depends_for_codependent_value': dict() + std_matrix, mean_std, lut_matrix = _std_by_param( + by_param, + distinct_values_by_param_index, + state_or_trans, + attribute, + param_idx, + verbose, + ) + ret["std_by_param"][param] = mean_std + ret["std_by_param_values"][param] = std_matrix + ret["lut_by_param_values"][param] = lut_matrix + ret["corr_by_param"][param] = _corr_by_param( + by_name, state_or_trans, attribute, param_idx + ) + + ret["depends_on_param"][param] = _depends_on_param( + ret["corr_by_param"][param], + ret["std_by_param"][param], + ret["std_param_lut"], + ) + + if ret["depends_on_param"][param]: + ret["param_data"][param] = { + "codependent_parameters": _codependent_parameters( + param, lut_matrix, std_matrix + ), + "depends_for_codependent_value": dict(), } # calculate parameter dependence for individual values of codependent parameters codependent_param_values = list() - for codependent_param in ret['param_data'][param]['codependent_parameters']: + for codependent_param in ret["param_data"][param]["codependent_parameters"]: codependent_param_values.append(distinct_values[codependent_param]) for combi in itertools.product(*codependent_param_values): by_name_part = deepcopy(by_name) - filter_list = list(zip(ret['param_data'][param]['codependent_parameters'], combi)) + filter_list = list( + zip(ret["param_data"][param]["codependent_parameters"], combi) + ) filter_aggregate_by_param(by_name_part, parameter_names, filter_list) by_param_part = by_name_to_by_param(by_name_part) # there may be no data for this specific parameter value combination if state_or_trans in by_name_part: - part_corr = _corr_by_param(by_name_part, state_or_trans, attribute, param_idx) - part_std_lut = np.mean([np.std(by_param_part[x][attribute]) for x in by_param_part.keys() if x[0] == state_or_trans]) - _, part_std_param, _ = _std_by_param(by_param_part, distinct_values_by_param_index, state_or_trans, attribute, param_idx, verbose) - ret['param_data'][param]['depends_for_codependent_value'][combi] = _depends_on_param(part_corr, part_std_param, part_std_lut) + part_corr = _corr_by_param( + by_name_part, state_or_trans, attribute, param_idx + ) + part_std_lut = np.mean( + [ + np.std(by_param_part[x][attribute]) + for x in by_param_part.keys() + if x[0] == state_or_trans + ] + ) + _, part_std_param, _ = _std_by_param( + by_param_part, + distinct_values_by_param_index, + state_or_trans, + attribute, + param_idx, + verbose, + ) + ret["param_data"][param]["depends_for_codependent_value"][ + combi + ] = _depends_on_param(part_corr, part_std_param, part_std_lut) if state_or_trans in arg_count: for arg_index in range(arg_count[state_or_trans]): - std_matrix, mean_std, lut_matrix = _std_by_param(by_param, distinct_values_by_param_index, state_or_trans, attribute, len(parameter_names) + arg_index, verbose) - ret['std_by_arg'].append(mean_std) - ret['std_by_arg_values'].append(std_matrix) - ret['lut_by_arg_values'].append(lut_matrix) - ret['corr_by_arg'].append(_corr_by_param(by_name, state_or_trans, attribute, len(parameter_names) + arg_index)) + std_matrix, mean_std, lut_matrix = _std_by_param( + by_param, + distinct_values_by_param_index, + state_or_trans, + attribute, + len(parameter_names) + arg_index, + verbose, + ) + ret["std_by_arg"].append(mean_std) + ret["std_by_arg_values"].append(std_matrix) + ret["lut_by_arg_values"].append(lut_matrix) + ret["corr_by_arg"].append( + _corr_by_param( + by_name, state_or_trans, attribute, len(parameter_names) + arg_index + ) + ) if False: - ret['depends_on_arg'].append(ret['corr_by_arg'][arg_index] > 0.1) - elif ret['std_by_arg'][arg_index] == 0: + ret["depends_on_arg"].append(ret["corr_by_arg"][arg_index] > 0.1) + elif ret["std_by_arg"][arg_index] == 0: # In general, std_param_lut < std_by_arg. So, if std_by_arg == 0, std_param_lut == 0 follows. # This means that the variation of arg does not affect the model quality -> no influence - ret['depends_on_arg'].append(False) + ret["depends_on_arg"].append(False) else: - ret['depends_on_arg'].append(ret['std_param_lut'] / ret['std_by_arg'][arg_index] < 0.5) + ret["depends_on_arg"].append( + ret["std_param_lut"] / ret["std_by_arg"][arg_index] < 0.5 + ) return ret + def _compute_param_statistics_parallel(arg): - return { - 'key' : arg['key'], - 'result': _compute_param_statistics(*arg['args']) - } + return {"key": arg["key"], "result": _compute_param_statistics(*arg["args"])} + def _all_params_are_numeric(data, param_idx): """Check if all `data['param'][*][param_idx]` elements are numeric, as reported by `utils.is_numeric`.""" - param_values = list(map(lambda x: x[param_idx], data['param'])) + param_values = list(map(lambda x: x[param_idx], data["param"])) if len(list(filter(is_numeric, param_values))) == len(param_values): return True return False -def prune_dependent_parameters(by_name, parameter_names, correlation_threshold = 0.5): + +def prune_dependent_parameters(by_name, parameter_names, correlation_threshold=0.5): """ Remove dependent parameters from aggregate. @@ -320,15 +419,17 @@ def prune_dependent_parameters(by_name, parameter_names, correlation_threshold = """ parameter_indices_to_remove = list() - for parameter_combination in itertools.product(range(len(parameter_names)), range(len(parameter_names))): + for parameter_combination in itertools.product( + range(len(parameter_names)), range(len(parameter_names)) + ): index_1, index_2 = parameter_combination if index_1 >= index_2: continue - parameter_values = [list(), list()] # both parameters have a value - parameter_values_1 = list() # parameter 1 has a value - parameter_values_2 = list() # parameter 2 has a value + parameter_values = [list(), list()] # both parameters have a value + parameter_values_1 = list() # parameter 1 has a value + parameter_values_2 = list() # parameter 2 has a value for name in by_name: - for measurement in by_name[name]['param']: + for measurement in by_name[name]["param"]: value_1 = measurement[index_1] value_2 = measurement[index_2] if is_numeric(value_1): @@ -342,16 +443,30 @@ def prune_dependent_parameters(by_name, parameter_names, correlation_threshold = # Calculating the correlation coefficient only makes sense when neither value is constant if np.std(parameter_values_1) != 0 and np.std(parameter_values_2) != 0: correlation = np.corrcoef(parameter_values)[0][1] - if correlation != np.nan and np.abs(correlation) > correlation_threshold: - print('[!] Parameters {} <-> {} are correlated with coefficcient {}'.format(parameter_names[index_1], parameter_names[index_2], correlation)) + if ( + correlation != np.nan + and np.abs(correlation) > correlation_threshold + ): + print( + "[!] Parameters {} <-> {} are correlated with coefficcient {}".format( + parameter_names[index_1], + parameter_names[index_2], + correlation, + ) + ) if len(parameter_values_1) < len(parameter_values_2): index_to_remove = index_1 else: index_to_remove = index_2 - print(' Removing parameter {}'.format(parameter_names[index_to_remove])) + print( + " Removing parameter {}".format( + parameter_names[index_to_remove] + ) + ) parameter_indices_to_remove.append(index_to_remove) remove_parameters_by_indices(by_name, parameter_names, parameter_indices_to_remove) + def remove_parameters_by_indices(by_name, parameter_names, parameter_indices_to_remove): """ Remove parameters listed in `parameter_indices` from aggregate `by_name` and `parameter_names`. @@ -365,12 +480,13 @@ def remove_parameters_by_indices(by_name, parameter_names, parameter_indices_to_ """ # Start removal from the end of the list to avoid renumbering of list elemenets - for parameter_index in sorted(parameter_indices_to_remove, reverse = True): + for parameter_index in sorted(parameter_indices_to_remove, reverse=True): for name in by_name: - for measurement in by_name[name]['param']: + for measurement in by_name[name]["param"]: measurement.pop(parameter_index) parameter_names.pop(parameter_index) + class ParamStats: """ :param stats: `stats[state_or_tran][attribute]` = std_static, std_param_lut, ... (see `compute_param_statistics`) @@ -378,7 +494,15 @@ class ParamStats: :param distinct_values_by_param_index: `distinct_values[state_or_tran][i]` = [distinct values in aggregate] """ - def __init__(self, by_name, by_param, parameter_names, arg_count, use_corrcoef = False, verbose = False): + def __init__( + self, + by_name, + by_param, + parameter_names, + arg_count, + use_corrcoef=False, + verbose=False, + ): """ Compute standard deviation and correlation coefficient on parameterized data partitions. @@ -411,24 +535,40 @@ class ParamStats: for state_or_tran in by_name.keys(): self.stats[state_or_tran] = dict() - self.distinct_values_by_param_index[state_or_tran] = distinct_param_values(by_name, state_or_tran) + self.distinct_values_by_param_index[state_or_tran] = distinct_param_values( + by_name, state_or_tran + ) self.distinct_values[state_or_tran] = dict() for i, param in enumerate(parameter_names): - self.distinct_values[state_or_tran][param] = self.distinct_values_by_param_index[state_or_tran][i] - for attribute in by_name[state_or_tran]['attributes']: - stats_queue.append({ - 'key': [state_or_tran, attribute], - 'args': [by_name, by_param, parameter_names, arg_count, state_or_tran, attribute, self.distinct_values[state_or_tran], self.distinct_values_by_param_index[state_or_tran], verbose], - }) + self.distinct_values[state_or_tran][ + param + ] = self.distinct_values_by_param_index[state_or_tran][i] + for attribute in by_name[state_or_tran]["attributes"]: + stats_queue.append( + { + "key": [state_or_tran, attribute], + "args": [ + by_name, + by_param, + parameter_names, + arg_count, + state_or_tran, + attribute, + self.distinct_values[state_or_tran], + self.distinct_values_by_param_index[state_or_tran], + verbose, + ], + } + ) with Pool() as pool: stats_results = pool.map(_compute_param_statistics_parallel, stats_queue) for stats in stats_results: - state_or_tran, attribute = stats['key'] - self.stats[state_or_tran][attribute] = stats['result'] + state_or_tran, attribute = stats["key"] + self.stats[state_or_tran][attribute] = stats["result"] - def can_be_fitted(self, state_or_tran = None) -> bool: + def can_be_fitted(self, state_or_tran=None) -> bool: """ Return whether a sufficient amount of distinct numeric parameter values is available, allowing a parameter-aware model to be generated. @@ -441,8 +581,27 @@ class ParamStats: for key in keys: for param in self._parameter_names: - if len(list(filter(lambda n: is_numeric(n), self.distinct_values[key][param]))) > 2: - print(key, param, list(filter(lambda n: is_numeric(n), self.distinct_values[key][param]))) + if ( + len( + list( + filter( + lambda n: is_numeric(n), + self.distinct_values[key][param], + ) + ) + ) + > 2 + ): + print( + key, + param, + list( + filter( + lambda n: is_numeric(n), + self.distinct_values[key][param], + ) + ), + ) return True return False @@ -456,7 +615,9 @@ class ParamStats: # TODO pass - def has_codependent_parameters(self, state_or_tran: str, attribute: str, param: str) -> bool: + def has_codependent_parameters( + self, state_or_tran: str, attribute: str, param: str + ) -> bool: """ Return whether there are parameters which determine whether `param` influences `state_or_tran` `attribute` or not. @@ -468,7 +629,9 @@ class ParamStats: return True return False - def codependent_parameters(self, state_or_tran: str, attribute: str, param: str) -> list: + def codependent_parameters( + self, state_or_tran: str, attribute: str, param: str + ) -> list: """ Return list of parameters which determine whether `param` influences `state_or_tran` `attribute` or not. @@ -476,12 +639,15 @@ class ParamStats: :param attribute: model attribute :param param: parameter name """ - if self.stats[state_or_tran][attribute]['depends_on_param'][param]: - return self.stats[state_or_tran][attribute]['param_data'][param]['codependent_parameters'] + if self.stats[state_or_tran][attribute]["depends_on_param"][param]: + return self.stats[state_or_tran][attribute]["param_data"][param][ + "codependent_parameters" + ] return list() - - def has_codependent_parameters_union(self, state_or_tran: str, attribute: str) -> bool: + def has_codependent_parameters_union( + self, state_or_tran: str, attribute: str + ) -> bool: """ Return whether there is a subset of parameters which decides whether `state_or_tran` `attribute` is static or parameter-dependent @@ -490,11 +656,14 @@ class ParamStats: """ depends_on_a_parameter = False for param in self._parameter_names: - if self.stats[state_or_tran][attribute]['depends_on_param'][param]: - print('{}/{} depends on {}'.format(state_or_tran, attribute, param)) + if self.stats[state_or_tran][attribute]["depends_on_param"][param]: + print("{}/{} depends on {}".format(state_or_tran, attribute, param)) depends_on_a_parameter = True - if len(self.codependent_parameters(state_or_tran, attribute, param)) == 0: - print('has no codependent parameters') + if ( + len(self.codependent_parameters(state_or_tran, attribute, param)) + == 0 + ): + print("has no codependent parameters") # Always depends on this parameter, regardless of other parameters' values return False return depends_on_a_parameter @@ -508,14 +677,21 @@ class ParamStats: """ codependent_parameters = set() for param in self._parameter_names: - if self.stats[state_or_tran][attribute]['depends_on_param'][param]: - if len(self.codependent_parameters(state_or_tran, attribute, param)) == 0: + if self.stats[state_or_tran][attribute]["depends_on_param"][param]: + if ( + len(self.codependent_parameters(state_or_tran, attribute, param)) + == 0 + ): return list(self._parameter_names) - for codependent_param in self.codependent_parameters(state_or_tran, attribute, param): + for codependent_param in self.codependent_parameters( + state_or_tran, attribute, param + ): codependent_parameters.add(codependent_param) return sorted(codependent_parameters) - def codependence_by_codependent_param_values(self, state_or_tran: str, attribute: str, param: str) -> dict: + def codependence_by_codependent_param_values( + self, state_or_tran: str, attribute: str, param: str + ) -> dict: """ Return dict mapping codependent parameter values to a boolean indicating whether `param` influences `state_or_tran` `attribute`. @@ -525,11 +701,15 @@ class ParamStats: :param attribute: model attribute :param param: parameter name """ - if self.stats[state_or_tran][attribute]['depends_on_param'][param]: - return self.stats[state_or_tran][attribute]['param_data'][param]['depends_for_codependent_value'] + if self.stats[state_or_tran][attribute]["depends_on_param"][param]: + return self.stats[state_or_tran][attribute]["param_data"][param][ + "depends_for_codependent_value" + ] return dict() - def codependent_parameter_value_dicts(self, state_or_tran: str, attribute: str, param: str, kind='dynamic'): + def codependent_parameter_value_dicts( + self, state_or_tran: str, attribute: str, param: str, kind="dynamic" + ): """ Return dicts of codependent parameter key-value mappings for which `param` influences (or does not influence) `state_or_tran` `attribute`. @@ -538,16 +718,21 @@ class ParamStats: :param param: parameter name: :param kind: 'static' or 'dynamic'. If 'dynamic' (the default), returns codependent parameter values for which `param` influences `attribute`. If 'static', returns codependent parameter values for which `param` does not influence `attribute` """ - codependent_parameters = self.stats[state_or_tran][attribute]['param_data'][param]['codependent_parameters'] - codependence_info = self.stats[state_or_tran][attribute]['param_data'][param]['depends_for_codependent_value'] + codependent_parameters = self.stats[state_or_tran][attribute]["param_data"][ + param + ]["codependent_parameters"] + codependence_info = self.stats[state_or_tran][attribute]["param_data"][param][ + "depends_for_codependent_value" + ] if len(codependent_parameters) == 0: return else: for param_values, is_dynamic in codependence_info.items(): - if (is_dynamic and kind == 'dynamic') or (not is_dynamic and kind == 'static'): + if (is_dynamic and kind == "dynamic") or ( + not is_dynamic and kind == "static" + ): yield dict(zip(codependent_parameters, param_values)) - def _generic_param_independence_ratio(self, state_or_trans, attribute): """ Return the heuristic ratio of parameter independence for state_or_trans and attribute. @@ -559,9 +744,9 @@ class ParamStats: if self.use_corrcoef: # not supported raise ValueError - if statistics['std_static'] == 0: + if statistics["std_static"] == 0: return 0 - return statistics['std_param_lut'] / statistics['std_static'] + return statistics["std_param_lut"] / statistics["std_static"] def generic_param_dependence_ratio(self, state_or_trans, attribute): """ @@ -572,7 +757,9 @@ class ParamStats: """ return 1 - self._generic_param_independence_ratio(state_or_trans, attribute) - def _param_independence_ratio(self, state_or_trans: str, attribute: str, param: str) -> float: + def _param_independence_ratio( + self, state_or_trans: str, attribute: str, param: str + ) -> float: """ Return the heuristic ratio of parameter independence for state_or_trans, attribute, and param. @@ -580,17 +767,19 @@ class ParamStats: """ statistics = self.stats[state_or_trans][attribute] if self.use_corrcoef: - return 1 - np.abs(statistics['corr_by_param'][param]) - if statistics['std_by_param'][param] == 0: - if statistics['std_param_lut'] != 0: + return 1 - np.abs(statistics["corr_by_param"][param]) + if statistics["std_by_param"][param] == 0: + if statistics["std_param_lut"] != 0: raise RuntimeError("wat") # In general, std_param_lut < std_by_param. So, if std_by_param == 0, std_param_lut == 0 follows. # This means that the variation of param does not affect the model quality -> no influence, return 1 - return 1. + return 1.0 - return statistics['std_param_lut'] / statistics['std_by_param'][param] + return statistics["std_param_lut"] / statistics["std_by_param"][param] - def param_dependence_ratio(self, state_or_trans: str, attribute: str, param: str) -> float: + def param_dependence_ratio( + self, state_or_trans: str, attribute: str, param: str + ) -> float: """ Return the heuristic ratio of parameter dependence for state_or_trans, attribute, and param. @@ -607,16 +796,18 @@ class ParamStats: def _arg_independence_ratio(self, state_or_trans, attribute, arg_index): statistics = self.stats[state_or_trans][attribute] if self.use_corrcoef: - return 1 - np.abs(statistics['corr_by_arg'][arg_index]) - if statistics['std_by_arg'][arg_index] == 0: - if statistics['std_param_lut'] != 0: + return 1 - np.abs(statistics["corr_by_arg"][arg_index]) + if statistics["std_by_arg"][arg_index] == 0: + if statistics["std_param_lut"] != 0: raise RuntimeError("wat") # In general, std_param_lut < std_by_arg. So, if std_by_arg == 0, std_param_lut == 0 follows. # This means that the variation of arg does not affect the model quality -> no influence, return 1 return 1 - return statistics['std_param_lut'] / statistics['std_by_arg'][arg_index] + return statistics["std_param_lut"] / statistics["std_by_arg"][arg_index] - def arg_dependence_ratio(self, state_or_trans: str, attribute: str, arg_index: int) -> float: + def arg_dependence_ratio( + self, state_or_trans: str, attribute: str, arg_index: int + ) -> float: return 1 - self._arg_independence_ratio(state_or_trans, attribute, arg_index) # This heuristic is very similar to the "function is not much better than @@ -625,10 +816,9 @@ class ParamStats: # --df, 2018-04-18 def depends_on_param(self, state_or_trans, attribute, param): """Return whether attribute of state_or_trans depens on param.""" - return self.stats[state_or_trans][attribute]['depends_on_param'][param] + return self.stats[state_or_trans][attribute]["depends_on_param"][param] # See notes on depends_on_param def depends_on_arg(self, state_or_trans, attribute, arg_index): """Return whether attribute of state_or_trans depens on arg_index.""" - return self.stats[state_or_trans][attribute]['depends_on_arg'][arg_index] - + return self.stats[state_or_trans][attribute]["depends_on_arg"][arg_index] |