diff options
| author | Daniel Friesel <derf@finalrewind.org> | 2018-02-06 14:03:57 +0100 | 
|---|---|---|
| committer | Daniel Friesel <derf@finalrewind.org> | 2018-02-06 14:03:57 +0100 | 
| commit | 5ddb53dbc414ebb441cc44ac00a91b9ec3d40285 (patch) | |
| tree | 3baebf0a1976d39debf6584bd398eaed11a7c8bd /lib | |
| parent | f0c6496c821a618a9f6bc442c0f6c72f03b54ec8 (diff) | |
determine most likely analytic representation of a parameter influence
Diffstat (limited to 'lib')
| -rwxr-xr-x | lib/dfatool.py | 157 | 
1 files changed, 157 insertions, 0 deletions
| diff --git a/lib/dfatool.py b/lib/dfatool.py index 470c52e..aaf44bf 100755 --- a/lib/dfatool.py +++ b/lib/dfatool.py @@ -6,6 +6,7 @@ import io  import json  import numpy as np  import os +from scipy import optimize  from scipy.cluster.vq import kmeans2  import struct  import sys @@ -350,6 +351,84 @@ def _param_slice_eq(a, b, index):          return True      return False +class ParamFunction: + +    def __init__(self, param_function, validation_function, num_vars): +        self._param_function = param_function +        self._validation_function = validation_function +        self._num_variables = num_vars + +    def is_valid(self, arg): +        return self._validation_function(arg) + +    def eval(self, param, args): +        return self._param_function(param, args) + +    def error_function(self, P, X, y): +        return self._param_function(P, X) - y + +class analytic: +    _num0_8 = np.vectorize(lambda x: 8 - bin(int(x)).count("1")) +    _num0_16 = np.vectorize(lambda x: 16 - bin(int(x)).count("1")) +    _num1 = np.vectorize(lambda x: bin(int(x)).count("1")) + +    def functions(): +        functions = { +            'linear' : ParamFunction( +                lambda param, arg: param[0] + param[1] * arg, +                lambda arg: True, +                2 +            ), +            'logarithmic' : ParamFunction( +                lambda param, arg: param[0] + param[1] * np.log(arg), +                lambda arg: arg > 0, +                2 +            ), +            'logarithmic1' : ParamFunction( +                lambda param, arg: param[0] + param[1] * np.log(arg + 1), +                lambda arg: arg > -1, +                2 +            ), +            'exponential' : ParamFunction( +                lambda param, arg: param[0] + param[1] * np.exp(arg), +                lambda arg: arg <= 64, +                2 +            ), +            #'polynomial' : lambda param, arg: param[0] + param[1] * arg + param[2] * arg ** 2, +            'square' : ParamFunction( +                lambda param, arg: param[0] + param[1] * arg ** 2, +                lambda arg: True, +                2 +            ), +            'fractional' : ParamFunction( +                lambda param, arg: param[0] + param[1] / arg, +                lambda arg: arg != 0, +                2 +            ), +            'sqrt' : ParamFunction( +                lambda param, arg: param[0] + param[1] * np.sqrt(arg), +                lambda arg: arg >= 0, +                2 +            ), +            'num0_8' : ParamFunction( +                lambda param, arg: param[0] + param[1] * analytic._num0_8(arg), +                lambda arg: True, +                2 +            ), +            'num0_16' : ParamFunction( +                lambda param, arg: param[0] + param[1] * analytic._num0_16(arg), +                lambda arg: True, +                2 +            ), +            'num1' : ParamFunction( +                lambda param, arg: param[0] + param[1] * analytic._num1(arg), +                lambda arg: True, +                2 +            ), +        } + +        return functions +  class EnergyModel:      def __init__(self, preprocessed_data): @@ -372,6 +451,67 @@ class EnergyModel:                  if key in self.by_name[state_or_trans]:                      self._compute_param_statistics(state_or_trans, key) +    def _try_fits(self, state_or_tran, model_attribute, param_index): +        functions = analytic.functions() + + +        for param_key in filter(lambda x: x[0] == state_or_tran, self.by_param.keys()): +            # We might remove elements from 'functions' while iterating over +            # its keys. A generator will not allow this, so we need to +            # convert to a list. +            function_names = list(functions.keys()) +            for function_name in function_names: +                function_object = functions[function_name] +                if is_numeric(param_key[1][param_index]) and not function_object.is_valid(param_key[1][param_index]): +                    functions.pop(function_name, None) + +        raw_results = {} +        results = {} + +        for param_key in filter(lambda x: x[0] == state_or_tran, self.by_param.keys()): +            X = [] +            Y = [] +            num_valid = 0 +            num_total = 0 +            for k, v in self.by_param.items(): +                if _param_slice_eq(k, param_key, param_index): +                    num_total += 1 +                    if is_numeric(k[1][param_index]): +                        num_valid += 1 +                        X.extend([float(k[1][param_index])] * len(v[model_attribute])) +                        Y.extend(v[model_attribute]) + +            if num_valid > 2: +                X = np.array(X) +                Y = np.array(Y) +                for function_name, param_function in functions.items(): +                    raw_results[function_name] = {} +                    error_function = param_function.error_function +                    res = optimize.least_squares(error_function, [0, 1], args=(X, Y), xtol=2e-15) +                    measures = regression_measures(param_function.eval(res.x, X), Y) +                    for measure, error_rate in measures.items(): +                        if not measure in raw_results[function_name]: +                            raw_results[function_name][measure] = [] +                        raw_results[function_name][measure].append(error_rate) +                    #print(function_name, res, measures) + +        best_fit_val = np.inf +        best_fit_name = None +        for function_name, result in raw_results.items(): +            if len(result) > 0: +                results[function_name] = {} +                for measure in result.keys(): +                    results[function_name][measure] = np.mean(result[measure]) +                rmsd = results[function_name]['rmsd'] +                if rmsd < best_fit_val: +                    best_fit_val = rmsd +                    best_fit_name = function_name + +        return { +            'best' : best_fit_name, +            'results' : results +        } +      def _aggregate_to_ndarray(self, aggregate):          for elem in aggregate.values():              for key in ['power', 'power_std', 'energy', 'duration', 'timeout', 'rel_energy_prev', 'rel_energy_next']: @@ -485,6 +625,23 @@ class EnergyModel:          return lut_median_getter +    def get_param_analytic(self): +        static_model = self._get_model_from_dict(self.by_name, np.median) + +    def get_fitted(self): +        for state_or_tran in self.by_name.keys(): +            if self.by_name[state_or_tran]['isa'] == 'state': +                attributes = ['power'] +            else: +                attributes = ['energy', 'duration', 'timeout', 'rel_energy_prev', 'rel_energy_next'] +            for model_attribute in attributes: +                for parameter_index, parameter_name in enumerate(self._parameter_names): +                    if self.param_dependence_ratio(state_or_tran, model_attribute, parameter_name) > 0.5: +                        fit_results = self._try_fits(state_or_tran, model_attribute, parameter_index) +                        print('{} is {}'.format(parameter_name, fit_results['best'])) +        pass + +      def states(self):          return sorted(list(filter(lambda k: self.by_name[k]['isa'] == 'state', self.by_name.keys()))) | 
