/* * Copyright 2010, Intel Corporation * * This file is part of PowerTOP * * This program file is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; version 2 of the License. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * * You should have received a copy of the GNU General Public License * along with this program in a file named COPYING; if not, write to the * Free Software Foundation, Inc, * 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301 USA * or just google for it. * * Authors: * Arjan van de Ven */ #include "parameters.h" #include "../measurement/measurement.h" #include #include #include extern int debug_learning; double calculate_params(struct parameter_bundle *params) { unsigned int i; params->score = 0; for (i = 0; i < past_results.size(); i++) compute_bundle(params, past_results[i]); return params->score; } /* * gradual linear convergence of non-independent variables works better if once in a while * you make a wrong move.... */ static int random_disturb(int retry_left) { if (retry_left < 10) return 0; if ( (rand() % 500) == 7) return 1; return 0; } static int try_zero(double value) { if (value > 0.01) if ( (rand() % 100) == 1) return 1; if ( (rand() % 5) == 1) return 1; return 0; } static unsigned int previous_measurements; static void weed_empties(struct parameter_bundle *best_so_far) { double best_score; unsigned int i; best_score = best_so_far->score; for (i = 0; i < best_so_far->parameters.size(); i++) { double orgvalue; orgvalue = best_so_far->parameters[i]; best_so_far->parameters[i] = 0.0; calculate_params(best_so_far); if (best_so_far->score > best_score) { best_so_far->parameters[i] = orgvalue; } else { best_score = best_so_far->score; } } calculate_params(best_so_far); } /* leaks like a sieve */ void learn_parameters(int iterations, int do_base_power) { struct parameter_bundle *best_so_far; double best_score = 10000000000000000.0; int retry = iterations; int prevparam = -1; int locked = 0; static unsigned int bpi = 0; unsigned int i; time_t start; /* don't start fitting anything until we have at least 1 more measurement than we have parameters */ if (past_results.size() <= all_parameters.parameters.size()) return; // if (past_results.size() == previous_measurements) // return; precompute_valid(); previous_measurements = past_results.size(); double delta = 0.50; best_so_far = &all_parameters; if (!bpi) bpi = get_param_index("base power"); calculate_params(best_so_far); best_score = best_so_far->score; delta = 0.001 / pow(0.8, iterations / 2.0); if (iterations < 25) delta = 0.001 / pow(0.5, iterations / 2.0); if (delta > 0.2) delta = 0.2; if (1.0 * best_score / past_results.size() < 4 && delta > 0.05) delta = 0.05; if (debug_learning) printf("Delta starts at %5.3f\n", delta); if (best_so_far->parameters[bpi] > min_power * 0.9) best_so_far->parameters[bpi] = min_power * 0.9; /* We want to give up a little of base power, to give other parameters room to change; base power is the end post for everything after all */ if (do_base_power && !debug_learning) best_so_far->parameters[bpi] = best_so_far->parameters[bpi] * 0.9998; start = time(NULL); while (retry--) { int changed = 0; int bestparam; double newvalue = 0; double orgscore; double weight; bestparam = -1; if (time(NULL) - start > 1 && !debug_learning) retry = 0; calculate_params(best_so_far); orgscore = best_score = best_so_far->score; for (i = 1; i < best_so_far->parameters.size(); i++) { double value, orgvalue; weight = delta * best_so_far->weights[i]; orgvalue = value = best_so_far->parameters[i]; if (value <= 0.001) { value = 0.1; } else value = value * (1 + weight); if (i == bpi && value > min_power) value = min_power; if (i == bpi && orgvalue > min_power) orgvalue = min_power; if (value > 5000) value = 5000; // printf("Trying %s %4.2f -> %4.2f\n", param.c_str(), best_so_far->parameters[param], value); best_so_far->parameters[i] = value; calculate_params(best_so_far); if (best_so_far->score < best_score || random_disturb(retry)) { best_score = best_so_far->score; newvalue = value; bestparam = i; changed++; } value = orgvalue * 1 / (1 + weight); if (value < 0.0001) value = 0.0; if (try_zero(value)) value = 0.0; if (value > 5000) value = 5000; // printf("Trying %s %4.2f -> %4.2f\n", param.c_str(), orgvalue, value); if (orgvalue != value) { best_so_far->parameters[i] = value; calculate_params(best_so_far); if (best_so_far->score + 0.00001 < best_score || (random_disturb(retry) && value > 0.0)) { best_score = best_so_far->score; newvalue = value; bestparam = i; changed++; } } best_so_far->parameters[i] = orgvalue; } if (!changed) { double mult; if (!locked) { mult = 0.8; if (iterations < 25) mult = 0.5; delta = delta * mult; } locked = 0; prevparam = -1; } else { if (debug_learning) { printf("Retry is %i \n", retry); printf("delta is %5.4f\n", delta); printf("Best parameter is %i \n", bestparam); printf("Changing score from %4.3f to %4.3f\n", orgscore, best_score); printf("Changing value from %4.3f to %4.3f\n", best_so_far->parameters[bestparam], newvalue); } best_so_far->parameters[bestparam] = newvalue; if (prevparam == bestparam) delta = delta * 1.1; prevparam = bestparam; locked = 1; } if (delta < 0.001 && !locked) break; if (retry % 50 == 49) weed_empties(best_so_far); } /* now we weed out all parameters that don't have value */ if (iterations > 50) weed_empties(best_so_far); if (debug_learning) printf("Final score %4.2f (%i points)\n", best_so_far->score / past_results.size(), (int)past_results.size()); // dump_parameter_bundle(best_so_far); // dump_past_results(); }