avoiding repetitive tuner
[satune.git] / src / Tuner / multituner.cc
1 #include "multituner.h"
2 #include "csolver.h"
3 #include "searchtuner.h"
4 #include <math.h>
5 #include <stdlib.h>
6 #include <float.h>
7 #include <string.h>
8 #include <iostream>
9 #include <fstream>
10 #include <limits>
11
12 #define UNSETVALUE -1
13
14 Problem::Problem(const char *_problem) :
15         problemnumber(-1),
16         result(UNSETVALUE),
17         besttime(LLONG_MAX)
18 {
19         uint len = strlen(_problem);
20         problem = (char *) ourmalloc(len + 1);
21         memcpy(problem, _problem, len + 1);
22 }
23
24 Problem::~Problem() {
25         ourfree(problem);
26 }
27
28 void TunerRecord::setTime(Problem *problem, long long time) {
29         timetaken.put(problem, time);
30 }
31
32 void TunerRecord::print(){
33         model_print("*************TUNER NUMBER=%d***********\n", tunernumber);
34         tuner->print();
35         model_print("&&&&&&&&&&&&&USED SETTINGS &&&&&&&&&&&&\n");
36         tuner->printUsed();
37         model_print("\n");
38 }
39
40 long long TunerRecord::getTime(Problem *problem) {
41         if (timetaken.contains(problem))
42                 return timetaken.get(problem);
43         else return -1;
44 }
45
46 TunerRecord *TunerRecord::changeTuner(SearchTuner *_newtuner) {
47         TunerRecord *tr = new TunerRecord(_newtuner);
48         for (uint i = 0; i < problems.getSize(); i++) {
49                 tr->problems.push(problems.get(i));
50         }
51         return tr;
52 }
53
54 MultiTuner::MultiTuner(uint _budget, uint _rounds, uint _timeout) :
55         budget(_budget), rounds(_rounds), timeout(_timeout), execnum(0) {
56 }
57
58 MultiTuner::~MultiTuner() {
59         for (uint i = 0; i < problems.getSize(); i++)
60                 ourfree(problems.get(i));
61         for (uint i = 0; i < allTuners.getSize(); i++)
62                 delete allTuners.get(i);
63 }
64
65 void MultiTuner::addProblem(const char *filename) {
66         Problem *p = new Problem(filename);
67         p->problemnumber = problems.getSize();
68         problems.push(p);
69 }
70
71 void MultiTuner::printData() {
72         model_print("*********** DATA DUMP ***********\n");
73         for (uint i = 0; i < allTuners.getSize(); i++) {
74                 TunerRecord *tuner = allTuners.get(i);
75                 SearchTuner *stun = tuner->getTuner();
76                 model_print("Tuner %u\n", i);
77                 stun->print();
78                 model_print("----------------------------------\n\n\n");
79                 for (uint j = 0; j < tuner->problems.getSize(); j++) {
80                         Problem *problem = tuner->problems.get(j);
81                         model_print("Problem %s\n", problem->getProblem());
82                         model_print("Time = %lld\n", tuner->getTime(problem));
83                 }
84         }
85 }
86
87 void MultiTuner::findBestThreeTuners() {
88         if (allTuners.getSize() < 3) {
89                 printData();
90                 return;
91         }
92         TunerRecord *bestTuners[3];
93         double score = DBL_MAX;
94         for (uint i = 0; i < allTuners.getSize() - 2; i++) {
95                 for (uint j = i + 1; j < allTuners.getSize() - 1; j++) {
96                         for (uint k = j + 1; k < allTuners.getSize(); k++) {
97                                 TunerRecord *tuner1 = allTuners.get(i);
98                                 TunerRecord *tuner2 = allTuners.get(j);
99                                 TunerRecord *tuner3 = allTuners.get(k);
100                                 double mintimes[problems.getSize()];
101                                 for (uint l = 0; l < problems.getSize(); l++) {
102                                         Problem *problem = problems.get(l);
103                                         mintimes[l] = pow(min(tuner1->getTime(problem), tuner2->getTime(problem), tuner3->getTime(problem)), (double)1 / problems.getSize());
104                                 }
105                                 double result = 1;
106                                 for (uint l = 0; l < problems.getSize(); l++) {
107                                         result *= mintimes[l];
108                                 }
109                                 if (result < score) {
110                                         score = result;
111                                         bestTuners[0] = tuner1;
112                                         bestTuners[1] = tuner2;
113                                         bestTuners[2] = tuner3;
114                                 }
115                         }
116                 }
117         }
118         model_print("Best 3 tuners:\n");
119         for (uint i = 0; i < 3; i++) {
120                 TunerRecord *tuner = bestTuners[i];
121                 SearchTuner *stun = tuner->getTuner();
122                 char buffer[512];
123                 snprintf(buffer, sizeof(buffer), "best%u.tuner", i);
124                 stun->serialize(buffer);
125                 model_print("Tuner %u\n", tuner->tunernumber);
126                 stun->print();
127                 for (uint j = 0; j < tuner->problems.getSize(); j++) {
128                         Problem *problem = tuner->problems.get(j);
129                         model_print("Problem %s\n", problem->getProblem());
130                         model_print("Time = %lld\n", tuner->getTime(problem));
131                 }
132                 model_print("----------------------------------\n\n\n");
133         }
134 }
135
136 void MultiTuner::addTuner(SearchTuner *tuner) {
137         TunerRecord *t = new TunerRecord(tuner);
138         tuners.push(t);
139         t->tunernumber = allTuners.getSize();
140         allTuners.push(t);
141 }
142
143
144 void MultiTuner::readData(uint numRuns) {
145         for (uint i = 0; i < numRuns; i++) {
146                 ifstream myfile;
147                 char buffer[512];
148                 uint tunernumber;
149                 snprintf(buffer, sizeof(buffer), "tunernum%u", i);
150                 myfile.open (buffer, ios::in);
151                 myfile >> tunernumber;
152                 myfile.close();
153                 if (allTuners.getSize() <= tunernumber)
154                         allTuners.setSize(tunernumber + 1);
155                 if (allTuners.get(tunernumber) == NULL) {
156                         snprintf(buffer, sizeof(buffer), "tuner%u", i);
157                         allTuners.set(tunernumber, new TunerRecord(new SearchTuner(buffer), tunernumber));
158                 }
159                 //Add any new used records
160                 snprintf(buffer, sizeof(buffer), "tuner%uused", i);
161                 TunerRecord *tuner = allTuners.get(tunernumber);
162                 tuner->getTuner()->addUsed(buffer);
163
164                 char problemname[512];
165                 uint problemnumber;
166                 snprintf(buffer, sizeof(buffer), "problem%u", i);
167                 myfile.open(buffer, ios::in);
168                 myfile.getline(problemname, sizeof(problemname));
169                 myfile >> problemnumber;
170                 myfile.close();
171                 if (problems.getSize() <= problemnumber)
172                         problems.setSize(problemnumber + 1);
173                 if (problems.get(problemnumber) == NULL)
174                         problems.set(problemnumber, new Problem(problemname));
175                 Problem *problem = problems.get(problemnumber);
176                 long long metric = -1;
177                 int sat = IS_INDETER;
178                 //Read data in from results file
179                 snprintf(buffer, sizeof(buffer), "result%u", i);
180
181                 myfile.open (buffer, ios::in);
182
183
184                 if (myfile.is_open()) {
185                         myfile >> metric;
186                         myfile >> sat;
187                         myfile.close();
188                 }
189                 if (problem->result == UNSETVALUE && sat != IS_INDETER) {
190                         problem->result = sat;
191                 } else if (problem->result != sat && sat != IS_INDETER) {
192                         model_print("******** Result has changed ********\n");
193                 }
194
195                 if (metric != -1) {
196                         if (tuner->getTime(problem) == -1)
197                                 tuner->problems.push(problem);
198                         tuner->setTime(problem, metric);
199                 }
200
201         }
202
203 }
204
205 long long MultiTuner::evaluate(Problem *problem, TunerRecord *tuner) {
206         char buffer[512];
207         {
208                 snprintf(buffer, sizeof(buffer), "problem%u", execnum);
209
210                 ofstream myfile;
211                 myfile.open (buffer, ios::out);
212
213
214                 if (myfile.is_open()) {
215                         myfile << problem->getProblem() << endl;
216                         myfile << problem->problemnumber << endl;
217                         myfile.close();
218                 }
219         }
220
221         {
222                 snprintf(buffer, sizeof(buffer), "tunernum%u", execnum);
223
224                 ofstream myfile;
225                 myfile.open (buffer, ios::out);
226
227
228                 if (myfile.is_open()) {
229                         myfile << tuner->tunernumber << endl;
230                         myfile.close();
231                 }
232         }
233
234         //Write out the tuner
235         snprintf(buffer, sizeof(buffer), "tuner%u", execnum);
236         tuner->getTuner()->serialize(buffer);
237
238         //compute timeout
239         uint timeinsecs = problem->besttime / NANOSEC;
240         uint adaptive = (timeinsecs > 30) ? timeinsecs * 5 : 150;
241         uint maxtime = (adaptive < timeout) ? adaptive : timeout;
242
243         //Do run
244         snprintf(buffer, sizeof(buffer), "./run.sh deserializerun %s %u tuner%u result%u > log%u", problem->getProblem(), maxtime, execnum, execnum, execnum);
245         int status = system(buffer);
246
247         long long metric = -1;
248         int sat = IS_INDETER;
249
250         if (status == 0) {
251                 //Read data in from results file
252                 snprintf(buffer, sizeof(buffer), "result%u", execnum);
253
254                 ifstream myfile;
255                 myfile.open (buffer, ios::in);
256
257
258                 if (myfile.is_open()) {
259                         myfile >> metric;
260                         myfile >> sat;
261                         myfile.close();
262                 }
263                 updateTimeout(problem, metric);
264                 snprintf(buffer, sizeof(buffer), "tuner%uused", execnum);
265                 tuner->getTuner()->addUsed(buffer);
266                 explored.push(tuner);
267         }
268         //Increment execution count
269         execnum++;
270
271         if (problem->result == UNSETVALUE && sat != IS_INDETER) {
272                 problem->result = sat;
273         } else if (problem->result != sat && sat != IS_INDETER) {
274                 model_print("******** Result has changed ********\n");
275         }
276         if (sat == IS_INDETER && metric != -1) {//The case when we have a timeout
277                 metric = -1;
278         }
279         return metric;
280 }
281
282 void MultiTuner::updateTimeout(Problem *problem, long long metric) {
283         if (metric < problem->besttime) {
284                 problem->besttime = metric;
285         }
286 }
287
288 void MultiTuner::tuneComp() {
289         Vector<TunerRecord *> *tunerV = new Vector<TunerRecord *>(&tuners);
290         for (uint b = 0; b < budget; b++) {
291                 model_print("Round %u of %u\n", b, budget);
292                 uint tSize = tunerV->getSize();
293                 for (uint i = 0; i < tSize; i++) {
294                         SearchTuner *tmpTuner = mutateTuner(tunerV->get(i)->getTuner(), b);
295                         TunerRecord *tmp = new TunerRecord(tmpTuner);
296                         tmp->tunernumber = allTuners.getSize();
297                         model_print("Mutated tuner %u to generate tuner %u\n", tunerV->get(i)->tunernumber, tmp->tunernumber);
298                         allTuners.push(tmp);
299                         tunerV->push(tmp);
300                 }
301
302                 Hashtable<TunerRecord *, int, uint64_t> scores;
303                 for (uint i = 0; i < problems.getSize(); i++) {
304                         Problem *problem = problems.get(i);
305                         Vector<TunerRecord *> places;
306                         for (uint j = 0; j < tunerV->getSize(); j++) {
307                                 TunerRecord *tuner = tunerV->get(j);
308                                 long long metric = tuner->getTime(problem);
309                                 if (metric == -1) {
310                                         metric = evaluate(problem, tuner);
311                                         if (tuner->getTime(problem) == -1) {
312                                                 tuner->problems.push(problem);
313                                         }
314                                         model_print("%u.Problem<%s>\tTuner<%p, %d>\tMetric<%lld>\n", i, problem->problem,tuner, tuner->tunernumber, metric);
315                                         model_print("*****************************\n");
316                                         if (metric != -1)
317                                                 tuner->setTime(problem, metric);
318                                         else
319                                                 tuner->setTime(problem, -2);
320                                 }
321                                 if (metric >= 0) {
322                                         uint k = 0;
323                                         for (; k < places.getSize(); k++) {
324                                                 if (metric < places.get(k)->getTime(problem))
325                                                         break;
326                                         }
327                                         model_print("place[%u]=Tuner<%p,%d>\n", k, tuner, tuner->tunernumber);
328                                         places.insertAt(k, tuner);
329                                 }
330                         }
331                         int points = 9;
332                         for (uint k = 0; k < places.getSize() && points; k++) {
333                                 TunerRecord *tuner = places.get(k);
334                                 int currScore = 0;
335                                 if (scores.contains(tuner))
336                                         currScore = scores.get(tuner);
337                                 currScore += points;
338                                 model_print("Problem<%s>\tTuner<%p,%d>\tmetric<%d>\n", problem->problem, tuner, tuner->tunernumber,  currScore);
339                                 model_print("**************************\n");
340                                 scores.put(tuner, currScore);
341                                 points = points / 3;
342                         }
343                 }
344                 Vector<TunerRecord *> ranking;
345                 for (uint i = 0; i < tunerV->getSize(); i++) {
346                         TunerRecord *tuner = tunerV->get(i);
347                         int score = 0;
348                         if (scores.contains(tuner))
349                                 score = scores.get(tuner);
350                         uint j = 0;
351                         for (; j < ranking.getSize(); j++) {
352                                 TunerRecord *t = ranking.get(j);
353                                 int tscore = 0;
354                                 if (scores.contains(t))
355                                         tscore = scores.get(t);
356                                 if (score > tscore)
357                                         break;
358                         }
359                         model_print("ranking[%u]=tuner<%p,%u>(Score=%d)\n", j, tuner, tuner->tunernumber, score);
360                         model_print("************************\n");
361                         ranking.insertAt(j, tuner);
362                 }
363                 model_print("tunerSize=%u\trankingSize=%u\ttunerVSize=%u\n", tuners.getSize(), ranking.getSize(), tunerV->getSize());
364                 for (uint i = tuners.getSize(); i < ranking.getSize(); i++) {
365                         TunerRecord *tuner = ranking.get(i);
366                         model_print("Removing tuner %u\n", tuner->tunernumber);
367                         for (uint j = 0; j < tunerV->getSize(); j++) {
368                                 if (tunerV->get(j) == tuner)
369                                         tunerV->removeAt(j);
370                         }
371                 }
372         }
373         printData();
374 }
375
376 void MultiTuner::mapProblemsToTuners(Vector<TunerRecord *> *tunerV) {
377         for (uint i = 0; i < problems.getSize(); i++) {
378                 Problem *problem = problems.get(i);
379                 TunerRecord *bestTuner = NULL;
380                 long long bestscore = 0;
381                 for (uint j = 0; j < tunerV->getSize(); j++) {
382                         TunerRecord *tuner = tunerV->get(j);
383                         long long metric = tuner->getTime(problem);
384                         if (metric == -1) {
385                                 metric = evaluate(problem, tuner);
386                                 if (metric != -1)
387                                         tuner->setTime(problem, metric);
388                                 else
389                                         tuner->setTime(problem, -2);
390                         }
391                         if ((bestTuner == NULL && metric >= 0) ||
392                                         (metric < bestscore && metric >= 0)) {
393                                 bestTuner = tuner;
394                                 bestscore = metric;
395                         }
396                 }
397                 if (bestTuner != NULL)
398                         bestTuner->problems.push(problem);
399         }
400 }
401
402 void clearVector(Vector<TunerRecord *> *tunerV) {
403         for (uint j = 0; j < tunerV->getSize(); j++) {
404                 TunerRecord *tuner = tunerV->get(j);
405                 tuner->problems.clear();
406         }
407 }
408
409 void MultiTuner::tuneK() {
410         Vector<TunerRecord *> *tunerV = new Vector<TunerRecord *>(&tuners);
411         for (uint i = 0; i < rounds; i++) {
412                 clearVector(tunerV);
413                 mapProblemsToTuners(tunerV);
414                 improveTuners(tunerV);
415         }
416         model_print("Best tuners\n");
417         for (uint j = 0; j < tunerV->getSize(); j++) {
418                 TunerRecord *tuner = tunerV->get(j);
419                 char buffer[256];
420                 sprintf(buffer, "tuner%u.conf", j);
421                 tuner->getTuner()->serialize(buffer);
422                 tuner->getTuner()->print();
423         }
424         delete tunerV;
425 }
426
427 void MultiTuner::improveTuners(Vector<TunerRecord *> *tunerV) {
428         for (uint j = 0; j < tunerV->getSize(); j++) {
429                 TunerRecord *tuner = tunerV->get(j);
430                 TunerRecord *newtuner = tune(tuner);
431                 tunerV->set(j, newtuner);
432         }
433 }
434
435 double MultiTuner::evaluateAll(TunerRecord *tuner) {
436         double product = 1;
437         for (uint i = 0; i < tuner->problems.getSize(); i++) {
438                 Problem *problem = tuner->problems.get(i);
439                 long long metric = tuner->getTime(problem);
440                 if (metric == -1) {
441                         metric = evaluate(problem, tuner);
442                         if (metric != -1)
443                                 tuner->setTime(problem, metric);
444                         else
445                                 tuner->setTime(problem, -2);
446                 }
447
448                 double score = metric;
449                 if (metric < 0)
450                         score = timeout;
451                 product *= score;
452         }
453         return pow(product, 1 / ((double)tuner->problems.getSize()));
454 }
455
456 SearchTuner *MultiTuner::mutateTuner(SearchTuner *oldTuner, uint k) {
457         SearchTuner *newTuner = oldTuner->copyUsed();
458         uint numSettings = oldTuner->getSize();
459         uint settingsToMutate = (uint)(AUTOTUNERFACTOR * (((double)numSettings) * (budget - k)) / (budget));
460         if (settingsToMutate < 1)
461                 settingsToMutate = 1;
462         model_print("Mutating %u settings\n", settingsToMutate);
463         while (settingsToMutate-- != 0) {
464                 newTuner->randomMutate();
465                 if(hasExplored(newTuner)){
466                         model_print("Note:A repetitive tuner has found\n");
467                         settingsToMutate++;
468                 }
469         }
470         return newTuner;
471 }
472
473 bool MultiTuner::hasExplored(SearchTuner *newTuner){
474         for (uint i=0; i< explored.getSize(); i++){
475                 SearchTuner *tuner = explored.get(i)->getTuner();
476                 if(tuner->isSubTunerof(newTuner)){
477                         return true;
478                 }
479         }
480         return false;
481 }
482
483 TunerRecord *MultiTuner::tune(TunerRecord *tuner) {
484         TunerRecord *bestTuner = NULL;
485         double bestScore = DBL_MAX;
486
487         TunerRecord *oldTuner = tuner;
488         double base_temperature = evaluateAll(oldTuner);
489         double oldScore = base_temperature;
490
491         for (uint i = 0; i < budget; i++) {
492                 SearchTuner *tmpTuner = mutateTuner(oldTuner->getTuner(), i);
493                 TunerRecord *newTuner = oldTuner->changeTuner(tmpTuner);
494                 newTuner->tunernumber = allTuners.getSize();
495                 allTuners.push(newTuner);
496                 double newScore = evaluateAll(newTuner);
497                 newTuner->tuner->printUsed();
498                 model_print("Received score %f\n", newScore);
499                 double scoreDiff = newScore - oldScore; //smaller is better
500                 if (newScore < bestScore) {
501                         bestScore = newScore;
502                         bestTuner = newTuner;
503                 }
504
505                 double acceptanceP;
506                 if (scoreDiff < 0) {
507                         acceptanceP = 1;
508                 } else {
509                         double currTemp = base_temperature * (((double)budget - i) / budget);
510                         acceptanceP = exp(-scoreDiff / currTemp);
511                 }
512                 double ran = ((double)random()) / RAND_MAX;
513                 if (ran <= acceptanceP) {
514                         oldScore = newScore;
515                         oldTuner = newTuner;
516                 }
517         }
518
519         return bestTuner;
520 }
521