summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xbin/analyze-archive.py59
-rwxr-xr-xbin/test.py34
-rwxr-xr-xlib/dfatool.py138
3 files changed, 204 insertions, 27 deletions
diff --git a/bin/analyze-archive.py b/bin/analyze-archive.py
index e5f3783..81d6f54 100755
--- a/bin/analyze-archive.py
+++ b/bin/analyze-archive.py
@@ -10,31 +10,42 @@ if __name__ == '__main__':
preprocessed_data = raw_data.get_preprocessed_data()
model = EnergyModel(preprocessed_data)
- print('--- simple static model ---')
- static_model = model.get_static()
+ #print('--- simple static model ---')
+ #static_model = model.get_static()
+ #for state in model.states():
+ # print('{:10s}: {:.0f} µW ({:.2f})'.format(
+ # state,
+ # static_model(state, 'power'),
+ # model.generic_param_dependence_ratio(state, 'power')))
+ # for param in model.parameters():
+ # print('{:10s} dependence on {:15s}: {:.2f}'.format(
+ # '',
+ # param,
+ # model.param_dependence_ratio(state, 'power', param)))
+ #for trans in model.transitions():
+ # print('{:10s}: {:.0f} / {:.0f} / {:.0f} pJ ({:.2f} / {:.2f} / {:.2f})'.format(
+ # trans, static_model(trans, 'energy'),
+ # static_model(trans, 'rel_energy_prev'),
+ # static_model(trans, 'rel_energy_next'),
+ # model.generic_param_dependence_ratio(trans, 'energy'),
+ # model.generic_param_dependence_ratio(trans, 'rel_energy_prev'),
+ # model.generic_param_dependence_ratio(trans, 'rel_energy_next')))
+ # print('{:10s}: {:.0f} µs'.format(trans, static_model(trans, 'duration')))
+ #model.assess(static_model)
+
+ #print('--- LUT ---')
+ #lut_model = model.get_param_lut()
+ #model.assess(lut_model)
+
+ print('--- param model ---')
+ param_model, param_info = model.get_fitted()
for state in model.states():
- print('{:10s}: {:.0f} µW ({:.2f})'.format(
- state,
- static_model(state, 'power'),
- model.generic_param_dependence_ratio(state, 'power')))
- for param in model.parameters():
- print('{:10s} dependence on {:15s}: {:.2f}'.format(
- '',
- param,
- model.param_dependence_ratio(state, 'power', param)))
+ for attribute in ['power']:
+ if param_info(state, attribute):
+ print('{:10s}: {}'.format(state, param_info(state, attribute)['function']._model_str))
for trans in model.transitions():
- print('{:10s}: {:.0f} / {:.0f} / {:.0f} pJ ({:.2f} / {:.2f} / {:.2f})'.format(
- trans, static_model(trans, 'energy'),
- static_model(trans, 'rel_energy_prev'),
- static_model(trans, 'rel_energy_next'),
- model.generic_param_dependence_ratio(trans, 'energy'),
- model.generic_param_dependence_ratio(trans, 'rel_energy_prev'),
- model.generic_param_dependence_ratio(trans, 'rel_energy_next')))
- print('{:10s}: {:.0f} µs'.format(trans, static_model(trans, 'duration')))
- model.assess(static_model)
-
- print('--- LUT ---')
- lut_model = model.get_param_lut()
- model.assess(lut_model)
+ for attribute in ['energy', 'rel_energy_prev', 'rel_energy_next', 'duration', 'timeout']:
+ if param_info(trans, attribute):
+ print('{:10s}: {:10s}: {}'.format(trans, attribute, param_info(trans, attribute)['function']._model_str))
sys.exit(0)
diff --git a/bin/test.py b/bin/test.py
index 71c02bd..7a45ca9 100755
--- a/bin/test.py
+++ b/bin/test.py
@@ -43,6 +43,31 @@ class TestStaticModel(unittest.TestCase):
self.assertAlmostEqual(static_model('stopListening', 'duration'), 260, places=0)
self.assertAlmostEqual(static_model('write_nb', 'duration'), 510, places=0)
+ self.assertAlmostEqual(model.param_dependence_ratio('POWERDOWN', 'power', 'datarate'), 0, places=2)
+ self.assertAlmostEqual(model.param_dependence_ratio('POWERDOWN', 'power', 'txbytes'), 0, places=2)
+ self.assertAlmostEqual(model.param_dependence_ratio('POWERDOWN', 'power', 'txpower'), 0, places=2)
+ self.assertAlmostEqual(model.param_dependence_ratio('RX', 'power', 'datarate'), 0.99, places=2)
+ self.assertAlmostEqual(model.param_dependence_ratio('RX', 'power', 'txbytes'), 0, places=2)
+ self.assertAlmostEqual(model.param_dependence_ratio('RX', 'power', 'txpower'), 0.01, places=2)
+ self.assertAlmostEqual(model.param_dependence_ratio('STANDBY1', 'power', 'datarate'), 0.04, places=2)
+ self.assertAlmostEqual(model.param_dependence_ratio('STANDBY1', 'power', 'txbytes'), 0.35, places=2)
+ self.assertAlmostEqual(model.param_dependence_ratio('STANDBY1', 'power', 'txpower'), 0.32, places=2)
+ self.assertAlmostEqual(model.param_dependence_ratio('TX', 'power', 'datarate'), 1, places=2)
+ self.assertAlmostEqual(model.param_dependence_ratio('TX', 'power', 'txbytes'), 0.09, places=2)
+ self.assertAlmostEqual(model.param_dependence_ratio('TX', 'power', 'txpower'), 1, places=2)
+
+ param_model, param_info = model.get_fitted()
+ self.assertEqual(param_info('POWERDOWN', 'power'), None)
+ self.assertEqual(param_info('RX', 'power')['function']._model_str,
+ '0 + regression_arg(0) + regression_arg(1) * np.sqrt(parameter(datarate))')
+ self.assertEqual(param_info('STANDBY1', 'power'), None)
+ self.assertEqual(param_info('TX', 'power')['function']._model_str,
+ '0 + regression_arg(0) + regression_arg(1) * 1/(parameter(datarate)) + regression_arg(2) * parameter(txpower) + regression_arg(3) * 1/(parameter(datarate)) * parameter(txpower)')
+ self.assertEqual(param_info('epilogue', 'timeout')['function']._model_str,
+ '0 + regression_arg(0) + regression_arg(1) * 1/(parameter(datarate))')
+ self.assertEqual(param_info('stopListening', 'duration')['function']._model_str,
+ '0 + regression_arg(0) + regression_arg(1) * 1/(parameter(datarate))')
+
def test_model_singlefile_mmparam(self):
raw_data = RawData(['../data/20161221_123347_mmparam.tar'])
@@ -191,5 +216,14 @@ class TestStaticModel(unittest.TestCase):
self.assertAlmostEqual(static_model('sleep', 'energy'), 104445, places=0)
self.assertEqual(static_model('txDone', 'energy'), 0)
+ param_model, param_info = model.get_fitted()
+ self.assertEqual(param_info('IDLE', 'power'), None)
+ self.assertEqual(param_info('RX', 'power')['function']._model_str,
+ '0 + regression_arg(0) + regression_arg(1) * np.log(parameter(symbolrate) + 1)')
+ self.assertEqual(param_info('SLEEP', 'power'), None)
+ self.assertEqual(param_info('SLEEP_EWOR', 'power'), None)
+ self.assertEqual(param_info('SYNTH_ON', 'power'), None)
+ self.assertEqual(param_info('XOFF', 'power'), None)
+
if __name__ == '__main__':
unittest.main()
diff --git a/lib/dfatool.py b/lib/dfatool.py
index aaf44bf..e39f58f 100755
--- a/lib/dfatool.py
+++ b/lib/dfatool.py
@@ -367,11 +367,83 @@ class ParamFunction:
def error_function(self, P, X, y):
return self._param_function(P, X) - y
+class AnalyticFunction:
+
+ def __init__(self, function_str, num_vars, parameters):
+ self._parameter_names = parameters
+ self._model_str = function_str
+ rawfunction = function_str
+ self._dependson = [False] * len(parameters)
+
+ for i in range(len(parameters)):
+ if rawfunction.find('parameter({})'.format(parameters[i])) >= 0:
+ self._dependson[i] = True
+ rawfunction = rawfunction.replace('parameter({})'.format(parameters[i]), 'arg[{:d}]'.format(i))
+ if rawfunction.find('function_arg({})'.format(parameters[i])) >= 0:
+ self._dependson[i] = True
+ rawfunction = rawfunction.replace('function_arg({})'.format(parameters[i]), 'arg[{:d}]'.format(i))
+ for i in range(num_vars):
+ rawfunction = rawfunction.replace('regression_arg({:d})'.format(i), 'param[{:d}]'.format(i))
+ self._function_str = rawfunction
+ self._function = eval('lambda param, arg: ' + rawfunction);
+ self._regression_args = list(np.ones((num_vars)))
+
+ def _get_fit_data(self, by_param, state_or_tran, model_attribute):
+ X = [[] for i in range(len(self._parameter_names))]
+ Y = []
+
+ num_valid = 0
+ num_total = 0
+
+ for key, val in by_param.items():
+ if key[0] == state_or_tran and len(key[1]) == len(self._parameter_names):
+ valid = True
+ num_total += 1
+ for i in range(len(self._parameter_names)):
+ if self._dependson[i] and not is_numeric(key[1][i]):
+ valid = False
+ if valid:
+ num_valid += 1
+ Y.extend(val[model_attribute])
+ for i in range(len(self._parameter_names)):
+ if self._dependson[i]:
+ X[i].extend([float(key[1][i])] * len(val[model_attribute]))
+ else:
+ X[i].extend([np.nan] * len(val[model_attribute]))
+ for i in range(len(self._parameter_names)):
+ X[i] = np.array(X[i])
+ Y = np.array(Y)
+
+ return X, Y, num_valid, num_total
+
+ def fit(self, by_param, state_or_tran, model_attribute):
+ X, Y, num_valid, num_total = self._get_fit_data(by_param, state_or_tran, model_attribute)
+ if num_valid > 2:
+ error_function = lambda P, X, y: self._function(P, X) - y
+ try:
+ res = optimize.least_squares(error_function, self._regression_args, args=(X, Y), xtol=2e-15)
+ except ValueError as err:
+ return
+ self._regression_args = res.x
+
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"))
+ _function_map = {
+ 'linear' : lambda x: x,
+ 'logarithmic' : np.log,
+ 'logarithmic1' : lambda x: np.log(x + 1),
+ 'exponential' : np.exp,
+ 'square' : lambda x : x ** 2,
+ 'fractional' : lambda x : 1 / x,
+ 'sqrt' : np.sqrt,
+ 'num0_8' : _num0_8,
+ 'num0_16' : _num0_16,
+ 'num1' : _num1,
+ }
+
def functions():
functions = {
'linear' : ParamFunction(
@@ -429,6 +501,47 @@ class analytic:
return functions
+ def _fmap(reference_type, reference_name, function_type):
+ ref_str = '{}({})'.format(reference_type,reference_name)
+ if function_type == 'linear':
+ return ref_str
+ if function_type == 'logarithmic':
+ return 'np.log({})'.format(ref_str)
+ if function_type == 'logarithmic1':
+ return 'np.log({} + 1)'.format(ref_str)
+ if function_type == 'exponential':
+ return 'np.exp({})'.format(ref_str)
+ if function_type == 'exponential':
+ return 'np.exp({})'.format(ref_str)
+ if function_type == 'square':
+ return '({})**2'.format(ref_str)
+ if function_type == 'fractional':
+ return '1/({})'.format(ref_str)
+ if function_type == 'sqrt':
+ return 'np.sqrt({})'.format(ref_str)
+ return 'analytic._{}({})'.format(function_type, ref_str)
+
+ def function_powerset(function_descriptions, parameter_names):
+ buf = '0'
+ arg_idx = 0
+ for combination in powerset(function_descriptions.items()):
+ buf += ' + regression_arg({:d})'.format(arg_idx)
+ arg_idx += 1
+ for function_item in combination:
+ buf += ' * {}'.format(analytic._fmap('parameter', function_item[0], function_item[1]['best']))
+ return AnalyticFunction(buf, arg_idx, parameter_names)
+
+ #def function_powerset(function_descriptions):
+ # function_buffer = lambda param, arg: 0
+ # param_idx = 0
+ # for combination in powerset(function_descriptions):
+ # new_function = lambda param, arg: param[param_idx]
+ # param_idx += 1
+ # for function_name in combination:
+ # new_function = lambda param, arg: new_function(param, arg) * analytic._function_map[function_name](arg)
+ # new_function = lambda param, arg: param[param_idx] *
+ # function_buffer = lambda param, arg: function_buffer(param, arg) +
+
class EnergyModel:
def __init__(self, preprocessed_data):
@@ -629,17 +742,36 @@ class EnergyModel:
static_model = self._get_model_from_dict(self.by_name, np.median)
def get_fitted(self):
+ static_model = self._get_model_from_dict(self.by_name, np.mean)
+ param_model = dict([[state_or_tran, {}] for state_or_tran in self.by_name.keys()])
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:
+ fit_results = {}
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
+ fit_results[parameter_name] = self._try_fits(state_or_tran, model_attribute, parameter_index)
+ #print('{} {} is {}'.format(state_or_tran, parameter_name, fit_results[parameter_name]['best']))
+ if len(fit_results.keys()):
+ x = analytic.function_powerset(fit_results, self._parameter_names)
+ x.fit(self.by_param, state_or_tran, model_attribute)
+ param_model[state_or_tran][model_attribute] = {
+ 'fit_result': fit_results,
+ 'function' : x
+ }
+
+ def model_getter(name, key, **kwargs):
+ return static_model[name][key]
+
+ def info_getter(name, key):
+ if key in param_model[name]:
+ return param_model[name][key]
+ return None
+
+ return model_getter, info_getter
def states(self):