1 /*
2  * Copyright 2017-2018 Tom van Dijk, Johannes Kepler University Linz
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <cstring> // for memset
18 #include <iomanip>
19 
20 #include "sspm.hpp"
21 
22 #define ODDFIRST 1
23 
24 namespace pg {
25 
SSPMSolver(Oink * oink,Game * game)26 SSPMSolver::SSPMSolver(Oink *oink, Game *game) : Solver(oink, game)
27 {
28 }
29 
~SSPMSolver()30 SSPMSolver::~SSPMSolver()
31 {
32 }
33 
34 void
to_tmp(int idx)35 SSPMSolver::to_tmp(int idx)
36 {
37     int base = idx*l;
38     for (int i=0; i<l; i++) tmp_b[i] = pm_b[base+i];
39     for (int i=0; i<l; i++) tmp_d[i] = pm_d[base+i];
40 }
41 
42 void
from_tmp(int idx)43 SSPMSolver::from_tmp(int idx)
44 {
45     int base = idx*l;
46     for (int i=0; i<l; i++) pm_b[base+i] = tmp_b[i];
47     for (int i=0; i<l; i++) pm_d[base+i] = tmp_d[i];
48 }
49 
50 void
to_best(int idx)51 SSPMSolver::to_best(int idx)
52 {
53     int base = idx*l;
54     for (int i=0; i<l; i++) best_b[i] = pm_b[base+i];
55     for (int i=0; i<l; i++) best_d[i] = pm_d[base+i];
56 }
57 
58 void
from_best(int idx)59 SSPMSolver::from_best(int idx)
60 {
61     int base = idx*l;
62     for (int i=0; i<l; i++) pm_b[base+i] = best_b[i];
63     for (int i=0; i<l; i++) pm_d[base+i] = best_d[i];
64 }
65 
66 void
tmp_to_best()67 SSPMSolver::tmp_to_best()
68 {
69     best_b = tmp_b;
70     memcpy(best_d, tmp_d, sizeof(int[l]));
71 }
72 
73 void
tmp_to_test()74 SSPMSolver::tmp_to_test()
75 {
76     test_b = tmp_b;
77     memcpy(test_d, tmp_d, sizeof(int[l]));
78 }
79 
80 /**
81  * Set tmp := min { m | m ==_p tmp }
82  */
83 void
trunc_tmp(int pindex)84 SSPMSolver::trunc_tmp(int pindex)
85 {
86     if (tmp_d[0] == -1) return; // already Top
87     // compute the lowest pindex >= p
88     // [pindex],.,...,.. => [pindex],000
89     // if pindex is the bottom, then this simply "buries" the remainder
90     for (int i=l-1; i>=0 and tmp_d[i] > pindex; i--) {
91         tmp_b[i] = 0;
92         tmp_d[i] = pindex+1;
93     }
94 }
95 
96 /*
97 void
98 SSPMSolver::prog_cap_tmp(int pindex)
99 {
100     // Start at the bottom, keep checking caps while going up
101     if (tmp_d[0] == -1) return; // nothing to do
102 
103     // compute value
104     int i=0;
105     for (int d=0; d<=pindex and i<l; d++) {
106         int val = 0, _i = i;
107 
108         for (; i<l; i++) {
109             if (tmp_d[i] != d) {
110                 // e found
111                 val |= ((1 << (l-i)) - 1);
112                 break;
113             }
114 
115             if (tmp_b[i]) val |= (1 << (l-i));
116         }
117 
118         if (val > cap[d]) {
119             if (d == 0) {
120                 // go to Top
121                 tmp_d[0] = -1;
122                 tmp_b.reset();
123             } else {
124                 for (int j=_i; j<l; j++) {
125                     tmp_d[j] = d-1;
126                     tmp_b[j] = 0;
127                 }
128                 tmp_b[_i] = 1;
129                 prog_cap_tmp(pindex); // again
130             }
131             return;
132         }
133     }
134 }
135 */
136 
137 /**
138  * Set tmp := min { m | m >_p tmp }
139  */
140 void
prog_tmp(int pindex,int h)141 SSPMSolver::prog_tmp(int pindex, int h)
142 {
143     // Simple case 1: Top >_p Top
144     if (tmp_d[0] == -1) return; // already Top
145 
146     // Simple case 2: Some bits below [pindex], ergo [pindex] can go from ..e to ..10*
147     if (tmp_d[l-1] > pindex) {
148         int i;
149         for (i=l-1; i>=0 and tmp_d[i] > pindex; i--) {
150             tmp_b[i] = 0;
151             tmp_d[i] = pindex;
152         }
153         tmp_b[i+1] = 1;
154         return;
155     }
156 
157     // Case 3: no bits below [pindex], so analyze lowest nonempty level
158     // * If lowest contains 0: 3a or 3b
159     // * Else if lowest level is root: 3c
160     // * Else append 100000000... to next higher level (3d, 3e, 3f)
161     //
162     // 3a: ,..011*  => ,..100*  (if lowest nonempty is the bottom)
163     // 3b: ,..011*, => ,..,000* (if lowest nonempty is not the bottom)
164     // 3c: 1111111  => Top      (if root contains only 1s)
165     // 3d: ,1111111 => 100*     (if non-root contains only 1s)
166     // 3e: ..,111*  => ..100*
167     // 3f: ,e,111*  => ,100*
168 
169     // no bits below pindex, so just find smallest increase
170     for (int i=l-1; i>=0; i--) {
171         if (tmp_b[i] == 0) {
172             if (tmp_d[i] == h) {
173                 // 3a: we found a 0 on the bottom, increase to 100...
174                 // ..011 => 00100
175                 tmp_b[i] = 1;
176                 break;
177             } else {
178                 // 3b: we found a 0 (not bottom), increase to [eps] and 0s on leaf
179                 // ..011 => ..,000
180                 tmp_b[i] = 0;
181                 int new_d = tmp_d[i]+1;
182                 for (int k=i; k<l; k++) tmp_d[k] = new_d;
183                 break;
184             }
185         } else if (i == 0) {
186             // we have only seen 1s and only 1 element
187             if (tmp_d[0] == 0) {
188                 // 3c: we are already the highest, so go to top
189                 tmp_b[0] = 0;
190                 tmp_d[0] = -1;
191                 break;
192             } else {
193                 // 3d: increase 1 higher...
194                 // ,e,111   => ,100
195                 int new_d = tmp_d[0]-1;
196                 tmp_b[0] = 1;
197                 for (int k=0; k<l; k++) tmp_d[k] = new_d;
198                 break;
199             }
200         } else if (tmp_d[i-1] != tmp_d[i]) {
201             // 3e, 3f: next is different
202             // two cases
203             //  .,111   => .100
204             // ,e,111   => ,100
205             int new_d = tmp_d[i]-1;
206             tmp_b[i] = 1;
207             for (int k=i; k<l; k++) tmp_d[k] = new_d;
208             break;
209         } else {
210             // next is same
211             tmp_b[i] = 0;
212         }
213     }
214 }
215 
216 /**
217  * Write pm to ostream.
218  */
219 void
stream_pm(std::ostream & out,int idx)220 SSPMSolver::stream_pm(std::ostream &out, int idx)
221 {
222     int base = idx*l;
223     if (pm_d[base] == -1) {
224         out << " \033[1;33mTop\033[m";
225     } else {
226         out << " { ";
227         int j=0;
228         for (int i=0; i<h; i++) {
229             if (i>0) out << ",";
230             int c=0;
231             while (j<l and pm_d[base+j] == i) {
232                 c++;
233                 out << pm_b[base+j];
234                 j++;
235             }
236             if (c == 0) out << "ε";
237         }
238         out << " }";
239     }
240 }
241 
242 /**
243  * Write tmp to ostream.
244  */
245 void
stream_tmp(std::ostream & out,int h)246 SSPMSolver::stream_tmp(std::ostream &out, int h)
247 {
248     if (tmp_d[0] == -1) {
249         out << " \033[1;33mTop\033[m";
250     } else {
251         out << " { ";
252         int j=0;
253         for (int i=0; i<h; i++) {
254             if (i>0) out << ",";
255             int c=0;
256             while (j<l and tmp_d[j] == i) {
257                 c++;
258                 out << tmp_b[j];
259                 j++;
260             }
261             if (c == 0) out << "ε";
262         }
263         out << " }";
264 
265         out << " {";
266 
267         // compute value
268         int i=0;
269         for (int d=0; d<h; d++) {
270             int val = 0;
271 
272             for (; i<l; i++) {
273                 if (tmp_d[i] != d) {
274                     // e found
275                     val |= ((1 << (l-i)) - 1);
276                     break;
277                 }
278 
279                 if (tmp_b[i]) val |= (1 << (l-i));
280             }
281 
282             logger << " " << val;
283         }
284 
285         out << " }";
286     }
287 }
288 
289 /**
290  * Write best to ostream.
291  */
292 void
stream_best(std::ostream & out,int h)293 SSPMSolver::stream_best(std::ostream &out, int h)
294 {
295     if (best_d[0] == -1) {
296         out << " \033[1;33mTop\033[m";
297     } else {
298         out << " { ";
299         int j=0;
300         for (int i=0; i<h; i++) {
301             if (i>0) out << ",";
302             int c=0;
303             while (j<l and best_d[j] == i) {
304                 c++;
305                 out << best_b[j];
306                 j++;
307             }
308             if (c == 0) out << "ε";
309         }
310         out << " }";
311     }
312 }
313 
314 /**
315  * Compare tmp and best
316  * res := -1 :: tmp < best
317  * res := 0  :: tmp = best
318  * res := 1  :: tmp > best
319  */
320 int
compare(int pindex)321 SSPMSolver::compare(int pindex)
322 {
323     // cases involving Top
324     if (tmp_d[0] == -1 and best_d[0] == -1) return 0;
325     if (tmp_d[0] == -1) return 1;
326     if (best_d[0] == -1) return -1;
327     for (int i=0; i<l; i++) {
328         if (tmp_d[i] > pindex and best_d[i] > pindex) {
329             // equal until pindex, return 0
330             return 0;
331         } else if (tmp_d[i] < best_d[i]) {
332             // equal until best has [eps]
333             if (tmp_b[i] == 0) return -1;
334             else return 1;
335         } else if (tmp_d[i] > best_d[i]) {
336             // equal until tmp has [eps]
337             if (best_b[i] == 0) return 1;
338             else return -1;
339         } else if (tmp_b[i] < best_b[i]) {
340             // equal until tmp<best
341             return -1;
342         } else if (tmp_b[i] > best_b[i]) {
343             // equal until tmp>best
344             return 1;
345         }
346     }
347     return 0;
348 }
349 
350 /**
351  * Compare tmp and test
352  * res := -1 :: tmp < test
353  * res := 0  :: tmp = test
354  * res := 1  :: tmp > test
355  */
356 int
compare_test(int pindex)357 SSPMSolver::compare_test(int pindex)
358 {
359     // cases involving Top
360     if (tmp_d[0] == -1 and test_d[0] == -1) return 0;
361     if (tmp_d[0] == -1) return 1;
362     if (test_d[0] == -1) return -1;
363     for (int i=0; i<l; i++) {
364         if (tmp_d[i] > pindex and test_d[i] > pindex) {
365             // equal until pindex, return 0
366             return 0;
367         } else if (tmp_d[i] < test_d[i]) {
368             // equal until test has [eps]
369             if (tmp_b[i] == 0) return -1;
370             else return 1;
371         } else if (tmp_d[i] > test_d[i]) {
372             // equal until tmp has [eps]
373             if (test_b[i] == 0) return 1;
374             else return -1;
375         } else if (tmp_b[i] < test_b[i]) {
376             // equal until tmp<test
377             return -1;
378         } else if (tmp_b[i] > test_b[i]) {
379             // equal until tmp>test
380             return 1;
381         }
382     }
383     return 0;
384 }
385 
386 bool
lift(int v,int target,int & str,int pl)387 SSPMSolver::lift(int v, int target, int &str, int pl)
388 {
389     // check if already Top
390     if (pm_d[l*v] == -1) return false; // already Top
391 
392     const int pr = priority(v);
393     const int pindex = pl == 0 ? h-(pr+1)/2-1 : h-pr/2-1;
394 
395 #ifndef NDEBUG
396     if (trace >= 2) {
397         logger << "\033[37;1mupdating vertex " << label_vertex(v) << " (" << pr << " => " << pindex << ")" << (owner(v)?" (odd)":" (even)") << "\033[m with current measure";
398         stream_pm(logger, v);
399         logger << std::endl;
400     }
401 #endif
402 
403     // if even owns and target is set, just check if specific target is better
404     if (owner(v) == pl and target != -1) {
405         to_tmp(target);
406         if (pl == (pr&1)) prog_tmp(pindex, h);
407         else trunc_tmp(pindex);
408         to_best(v);
409         if (compare(pindex) > 0) {
410             from_tmp(v);
411 #ifndef NDEBUG
412             if (trace >= 2) {
413                 logger << "to successor " << label_vertex(target) << ":";
414                 stream_tmp(logger, h);
415                 logger << " =>";
416             }
417 #endif
418             if (pl == (pr&1)) prog_tmp(pindex, h);
419             else trunc_tmp(pindex);
420 #ifndef NDEBUG
421             if (trace >= 2) {
422                 stream_tmp(logger, h);
423                 logger << std::endl;
424             }
425 #endif
426 #ifndef NDEBUG
427             if (trace >= 1) {
428                 logger << "\033[32;1mnew measure\033[m of \033[36;1m" << label_vertex(v) << "\033[m:";
429                 stream_tmp(logger, h);
430                 logger << " (to " << label_vertex(target) << ")\n";
431             }
432 #endif
433             return true;
434         } else {
435             return false;
436         }
437     }
438 
439     // compute best measure
440     bool first = true;
441     for (auto curedge = outs(v); *curedge != -1; curedge++) {
442         int to = *curedge;
443         if (disabled[to]) continue;
444         to_tmp(to);
445 #ifndef NDEBUG
446         if (trace >= 2) {
447             logger << "to successor " << label_vertex(to) << " from";
448             stream_tmp(logger, h);
449             logger << " =>";
450         }
451 #endif
452         if (pl == (pr&1)) prog_tmp(pindex, h);
453         else trunc_tmp(pindex);
454 #ifndef NDEBUG
455         if (trace >= 2) {
456             stream_tmp(logger, h);
457             logger << std::endl;
458         }
459 #endif
460         if (first) {
461             tmp_to_best();
462             str = to;
463         } else if (owner(v) == pl) {
464             // we want the max!
465             if (compare(pindex) > 0) {
466                 tmp_to_best();
467                 str = to;
468             }
469         } else {
470             // we want the min!
471             if (compare(pindex) < 0) {
472                 tmp_to_best();
473                 str = to;
474             }
475         }
476         first = false;
477     }
478 
479     // set best to pm if higher
480     to_tmp(v);
481     if (compare(pindex) < 0) {
482 #ifndef NDEBUG
483         if (trace >= 1) {
484             logger << "\033[1;32mnew measure\033[m of \033[36;1m" << label_vertex(v) << "\033[m:";
485             stream_best(logger, h);
486             logger << " (to " << label_vertex(str) << ")\n";
487         }
488 #endif
489         from_best(v);
490         return true;
491     } else {
492         return false;
493     }
494 }
495 
496 static int
ceil_log2(unsigned long long x)497 ceil_log2(unsigned long long x)
498 {
499     static const unsigned long long t[6] = {
500         0xFFFFFFFF00000000ull,
501         0x00000000FFFF0000ull,
502         0x000000000000FF00ull,
503         0x00000000000000F0ull,
504         0x000000000000000Cull,
505         0x0000000000000002ull
506     };
507 
508     int y = (((x & (x - 1)) == 0) ? 0 : 1);
509     int j = 32;
510     int i;
511 
512     for (i = 0; i < 6; i++) {
513         int k = (((x & t[i]) == 0) ? 0 : j);
514         y += k;
515         x >>= k;
516         j >>= 1;
517     }
518 
519     return y;
520 }
521 
522 void
run(int n_bits,int depth,int player)523 SSPMSolver::run(int n_bits, int depth, int player)
524 {
525     l = n_bits;
526     h = depth;
527 
528     pm_b.resize(l*nodecount());
529     pm_d = new int[l*nodecount()];
530 
531     tmp_b.resize(l);
532     tmp_d = new int[l];
533 
534     best_b.resize(l);
535     best_d = new int[l];
536 
537     test_b.resize(l);
538     test_d = new int[l];
539 
540     // initialize progress measures
541     // pm_b.reset(); // standard set to 0 already
542     memset(pm_d, 0, sizeof(int[l*nodecount()])); // every bit in the top ( = min )
543 
544     // lift_counters = new uint64_t[nodecount()];
545     // memset(lift_counters, 0, sizeof(uint64_t[nodecount()]));
546 
547     for (int n=nodecount()-1; n>=0; n--) {
548         if (disabled[n]) continue;
549         lift_attempt++;
550         int s;
551         if (lift(n, -1, s, player)) {
552             lift_count++;
553             // lift_counters[n]++;
554             for (auto curedge = ins(n); *curedge != -1; curedge++) {
555                 int from = *curedge;
556                 if (disabled[from]) continue;
557                 lift_attempt++;
558                 int s;
559                 if (lift(from, n, s, player)) {
560                     lift_count++;
561                     // lift_counters[from]++;
562                     todo_push(from);
563                 }
564             }
565         }
566     }
567 
568     while (!Q.empty()) {
569         int n = todo_pop();
570         for (auto curedge = ins(n); *curedge != -1; curedge++) {
571             int from = *curedge;
572             if (disabled[from]) continue;
573             lift_attempt++;
574             int s;
575             if (lift(from, n, s, player)) {
576                 lift_count++;
577                 // lift_counters[from]++;
578                 todo_push(from);
579             }
580         }
581     }
582 
583     /**
584      * Derive strategies.
585      */
586 
587     for (int v=0; v<nodecount(); v++) {
588         if (disabled[v]) continue;
589         if (pm_d[l*v] != -1) {
590             if (owner(v) != player) {
591                 if (lift(v, -1, game->strategy[v], player)) logger << "error: " << v << " is not progressive!" << std::endl;
592             }
593         }
594     }
595 
596     if (trace) {
597         for (int v=0; v<nodecount(); v++) {
598             if (disabled[v]) continue;
599 
600             logger << "\033[1m" << label_vertex(v) << (owner(v)?" (odd)":" (even)") << "\033[m:";
601             stream_pm(logger, v);
602 
603             if (pm_d[l*v] != -1) {
604                 if (owner(v) != player) {
605                     logger << " => " << label_vertex(game->strategy[v]);
606                 }
607             }
608 
609             logger << std::endl;
610         }
611     }
612 
613     /**
614      * Mark solved.
615      */
616 
617     for (int v=0; v<nodecount(); v++) {
618         if (disabled[v]) continue;
619         if (pm_d[l*v] != -1) oink->solve(v, 1-player, game->strategy[v]);
620     }
621 
622     oink->flush();
623 
624     /*
625     {
626         uint64_t hi_c = 0;
627         int hi_n = -1;
628         for (int i=0; i<nodecount(); i++) {
629             if (hi_c < lift_counters[i]) {
630                 hi_c = lift_counters[i];
631                 hi_n = i;
632             }
633         }
634         logger << "highest counter: " << hi_c << " lifts for vertex " << label_vertex(hi_n) << std::endl;
635     }
636     */
637 
638     delete[] pm_d;
639     delete[] tmp_d;
640     delete[] best_d;
641     delete[] test_d;
642 }
643 
644 void
run()645 SSPMSolver::run()
646 {
647     int max_prio = priority(nodecount()-1);
648 
649     // compute ml (max l) and the h for even/odd
650     int ml = ceil_log2(nodecount());
651     int h0 = (max_prio/2)+1;
652     int h1 = (max_prio+1)/2;
653 
654     // create datastructures
655     Q.resize(nodecount());
656     dirty.resize(nodecount());
657     unstable.resize(nodecount());
658 
659     logger << "even wants " << ml << "-bounded adaptive " << h0 << "-counters." << std::endl;
660     logger << "odd wants " << ml << "-bounded adaptive " << h1 << "-counters." << std::endl;
661 
662     // if running bounded sspm, start with 1-bounded adaptive counters
663     int i = bounded ? 1 : ml;
664 
665     for (; i<=ml; i++) {
666         int _l = lift_count, _a = lift_attempt;
667         uint64_t _c = game->count_unsolved(), c;
668 
669         if (ODDFIRST) {
670             // run odd counters
671             run(i, h1, 1);
672             c = game->count_unsolved();
673             logger << "after odd  with k=" << i << ", " << std::setw(9) << lift_count-_l << " lifts, " << std::setw(9) << lift_attempt-_a << " lift attempts, " << c << " unsolved left." << std::endl;
674 
675             // if now solved, no need to run odd counters
676             if (c == 0) break;
677 
678             // run even counters
679             run(i, h0, 0);
680             c = game->count_unsolved();
681             logger << "after even with k=" << i << ", " << std::setw(9) << lift_count-_l << " lifts, " << std::setw(9) << lift_attempt-_a << " lift attempts, " << c << " unsolved left." << std::endl;
682         } else {
683             // run even counters
684             run(i, h0, 0);
685             c = game->count_unsolved();
686             logger << "after even with k=" << i << ", " << std::setw(9) << lift_count-_l << " lifts, " << std::setw(9) << lift_attempt-_a << " lift attempts, " << c << " unsolved left." << std::endl;
687 
688             // if now solved, no need to run odd counters
689             if (c == 0) break;
690 
691             // run odd counters
692             run(i, h1, 1);
693             c = game->count_unsolved();
694             logger << "after odd  with k=" << i << ", " << std::setw(9) << lift_count-_l << " lifts, " << std::setw(9) << lift_attempt-_a << " lift attempts, " << c << " unsolved left." << std::endl;
695         }
696 
697         if (i != ml) {
698             // if i == ml then we are guaranteed to be done
699             // otherwise check if done
700             if (c == 0) break;
701             if (_c != c) i--; // do not increase i if we solved vertices with current i
702         } else {
703             break; // do not count higher pls
704         }
705     }
706 
707     logger << "solved with " << lift_count << " lifts, " << lift_attempt << " lift attempts, max l " << i << "." << std::endl;
708 }
709 
710 }
711