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