You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

933 lines
29 KiB

3 months ago
  1. #include <argp.h>
  2. #include <inttypes.h>
  3. #include <locale.h>
  4. #include <stdio.h>
  5. #include <stdlib.h>
  6. #include <string.h>
  7. #include <sys/time.h>
  8. #ifdef HAVE_PROFILER
  9. #include <gperftools/profiler.h>
  10. #endif
  11. #include <getrss.h>
  12. #include <sylvan.h>
  13. #include <sylvan_int.h>
  14. /* Configuration (via argp) */
  15. static int report_levels = 0; // report states at end of every level
  16. static int report_table = 0; // report table size at end of every level
  17. static int report_nodes = 0; // report number of nodes of BDDs
  18. static int strategy = 2; // 0 = BFS, 1 = PAR, 2 = SAT, 3 = CHAINING
  19. static int check_deadlocks = 0; // set to 1 to check for deadlocks on-the-fly (only bfs/par)
  20. static int merge_relations = 0; // merge relations to 1 relation
  21. static int print_transition_matrix = 0; // print transition relation matrix
  22. static int workers = 0; // autodetect
  23. static char* model_filename = NULL; // filename of model
  24. #ifdef HAVE_PROFILER
  25. static char* profile_filename = NULL; // filename for profiling
  26. #endif
  27. /* argp configuration */
  28. static struct argp_option options[] =
  29. {
  30. {"workers", 'w', "<workers>", 0, "Number of workers (default=0: autodetect)", 0},
  31. {"strategy", 's', "<bfs|par|sat|chaining>", 0, "Strategy for reachability (default=sat)", 0},
  32. #ifdef HAVE_PROFILER
  33. {"profiler", 'p', "<filename>", 0, "Filename for profiling", 0},
  34. #endif
  35. {"deadlocks", 3, 0, 0, "Check for deadlocks", 1},
  36. {"count-nodes", 5, 0, 0, "Report #nodes for BDDs", 1},
  37. {"count-states", 1, 0, 0, "Report #states at each level", 1},
  38. {"count-table", 2, 0, 0, "Report table usage at each level", 1},
  39. {"merge-relations", 6, 0, 0, "Merge transition relations into one transition relation", 1},
  40. {"print-matrix", 4, 0, 0, "Print transition matrix", 1},
  41. {0, 0, 0, 0, 0, 0}
  42. };
  43. static error_t
  44. parse_opt(int key, char *arg, struct argp_state *state)
  45. {
  46. switch (key) {
  47. case 'w':
  48. workers = atoi(arg);
  49. break;
  50. case 's':
  51. if (strcmp(arg, "bfs")==0) strategy = 0;
  52. else if (strcmp(arg, "par")==0) strategy = 1;
  53. else if (strcmp(arg, "sat")==0) strategy = 2;
  54. else if (strcmp(arg, "chaining")==0) strategy = 3;
  55. else argp_usage(state);
  56. break;
  57. case 4:
  58. print_transition_matrix = 1;
  59. break;
  60. case 3:
  61. check_deadlocks = 1;
  62. break;
  63. case 1:
  64. report_levels = 1;
  65. break;
  66. case 2:
  67. report_table = 1;
  68. break;
  69. case 5:
  70. report_nodes = 1;
  71. break;
  72. case 6:
  73. merge_relations = 1;
  74. break;
  75. #ifdef HAVE_PROFILER
  76. case 'p':
  77. profile_filename = arg;
  78. break;
  79. #endif
  80. case ARGP_KEY_ARG:
  81. if (state->arg_num >= 1) argp_usage(state);
  82. model_filename = arg;
  83. break;
  84. case ARGP_KEY_END:
  85. if (state->arg_num < 1) argp_usage(state);
  86. break;
  87. default:
  88. return ARGP_ERR_UNKNOWN;
  89. }
  90. return 0;
  91. }
  92. static struct argp argp = { options, parse_opt, "<model>", 0, 0, 0, 0 };
  93. /**
  94. * Types (set and relation)
  95. */
  96. typedef struct set
  97. {
  98. BDD bdd;
  99. BDD variables; // all variables in the set (used by satcount)
  100. } *set_t;
  101. typedef struct relation
  102. {
  103. BDD bdd;
  104. BDD variables; // all variables in the relation (used by relprod)
  105. int r_k, w_k, *r_proj, *w_proj;
  106. } *rel_t;
  107. static int vectorsize; // size of vector in integers
  108. static int *statebits; // number of bits for each state integer
  109. static int actionbits; // number of bits for action label
  110. static int totalbits; // total number of bits
  111. static int next_count; // number of partitions of the transition relation
  112. static rel_t *next; // each partition of the transition relation
  113. /**
  114. * Obtain current wallclock time
  115. */
  116. static double
  117. wctime()
  118. {
  119. struct timeval tv;
  120. gettimeofday(&tv, NULL);
  121. return (tv.tv_sec + 1E-6 * tv.tv_usec);
  122. }
  123. static double t_start;
  124. #define INFO(s, ...) fprintf(stdout, "[% 8.2f] " s, wctime()-t_start, ##__VA_ARGS__)
  125. #define Abort(...) { fprintf(stderr, __VA_ARGS__); fprintf(stderr, "Abort at line %d!\n", __LINE__); exit(-1); }
  126. static char*
  127. to_h(double size, char *buf)
  128. {
  129. const char* units[] = {"B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"};
  130. int i = 0;
  131. for (;size>1024;size/=1024) i++;
  132. sprintf(buf, "%.*f %s", i, size, units[i]);
  133. return buf;
  134. }
  135. static void
  136. print_memory_usage(void)
  137. {
  138. char buf[32];
  139. to_h(getCurrentRSS(), buf);
  140. INFO("Memory usage: %s\n", buf);
  141. }
  142. /**
  143. * Load a set from file
  144. * The expected binary format:
  145. * - int k : projection size, or -1 for full state
  146. * - int[k] proj : k integers specifying the variables of the projection
  147. * - MTBDD[1] BDD (mtbdd binary format)
  148. */
  149. #define set_load(f) CALL(set_load, f)
  150. TASK_1(set_t, set_load, FILE*, f)
  151. {
  152. // allocate set
  153. set_t set = (set_t)malloc(sizeof(struct set));
  154. set->bdd = sylvan_false;
  155. set->variables = sylvan_true;
  156. sylvan_protect(&set->bdd);
  157. sylvan_protect(&set->variables);
  158. // read k
  159. int k;
  160. if (fread(&k, sizeof(int), 1, f) != 1) Abort("Invalid input file!\n");
  161. if (k == -1) {
  162. // create variables for a full state vector
  163. uint32_t vars[totalbits];
  164. for (int i=0; i<totalbits; i++) vars[i] = 2*i;
  165. set->variables = sylvan_set_fromarray(vars, totalbits);
  166. } else {
  167. // read proj
  168. int proj[k];
  169. if (fread(proj, sizeof(int), k, f) != (size_t)k) Abort("Invalid input file!\n");
  170. // create variables for a short/projected state vector
  171. uint32_t vars[totalbits];
  172. uint32_t cv = 0;
  173. int j = 0, n = 0;
  174. for (int i=0; i<vectorsize && j<k; i++) {
  175. if (i == proj[j]) {
  176. for (int x=0; x<statebits[i]; x++) vars[n++] = (cv += 2) - 2;
  177. j++;
  178. } else {
  179. cv += 2 * statebits[i];
  180. }
  181. }
  182. set->variables = sylvan_set_fromarray(vars, n);
  183. }
  184. // read bdd
  185. if (mtbdd_reader_frombinary(f, &set->bdd, 1) != 0) Abort("Invalid input file!\n");
  186. return set;
  187. }
  188. /**
  189. * Load a relation from file
  190. * This part just reads the r_k, w_k, r_proj and w_proj variables.
  191. */
  192. #define rel_load_proj(f) CALL(rel_load_proj, f)
  193. TASK_1(rel_t, rel_load_proj, FILE*, f)
  194. {
  195. rel_t rel = (rel_t)malloc(sizeof(struct relation));
  196. int r_k, w_k;
  197. if (fread(&r_k, sizeof(int), 1, f) != 1) Abort("Invalid file format.");
  198. if (fread(&w_k, sizeof(int), 1, f) != 1) Abort("Invalid file format.");
  199. rel->r_k = r_k;
  200. rel->w_k = w_k;
  201. int *r_proj = (int*)malloc(sizeof(int[r_k]));
  202. int *w_proj = (int*)malloc(sizeof(int[w_k]));
  203. if (fread(r_proj, sizeof(int), r_k, f) != (size_t)r_k) Abort("Invalid file format.");
  204. if (fread(w_proj, sizeof(int), w_k, f) != (size_t)w_k) Abort("Invalid file format.");
  205. rel->r_proj = r_proj;
  206. rel->w_proj = w_proj;
  207. rel->bdd = sylvan_false;
  208. sylvan_protect(&rel->bdd);
  209. /* Compute a_proj the union of r_proj and w_proj, and a_k the length of a_proj */
  210. int a_proj[r_k+w_k];
  211. int r_i = 0, w_i = 0, a_i = 0;
  212. for (;r_i < r_k || w_i < w_k;) {
  213. if (r_i < r_k && w_i < w_k) {
  214. if (r_proj[r_i] < w_proj[w_i]) {
  215. a_proj[a_i++] = r_proj[r_i++];
  216. } else if (r_proj[r_i] > w_proj[w_i]) {
  217. a_proj[a_i++] = w_proj[w_i++];
  218. } else /* r_proj[r_i] == w_proj[w_i] */ {
  219. a_proj[a_i++] = w_proj[w_i++];
  220. r_i++;
  221. }
  222. } else if (r_i < r_k) {
  223. a_proj[a_i++] = r_proj[r_i++];
  224. } else if (w_i < w_k) {
  225. a_proj[a_i++] = w_proj[w_i++];
  226. }
  227. }
  228. const int a_k = a_i;
  229. /* Compute all_variables, which are all variables the transition relation is defined on */
  230. uint32_t all_vars[totalbits * 2];
  231. uint32_t curvar = 0; // start with variable 0
  232. int i=0, j=0, n=0;
  233. for (; i<vectorsize && j<a_k; i++) {
  234. if (i == a_proj[j]) {
  235. for (int k=0; k<statebits[i]; k++) {
  236. all_vars[n++] = curvar;
  237. all_vars[n++] = curvar + 1;
  238. curvar += 2;
  239. }
  240. j++;
  241. } else {
  242. curvar += 2 * statebits[i];
  243. }
  244. }
  245. rel->variables = sylvan_set_fromarray(all_vars, n);
  246. sylvan_protect(&rel->variables);
  247. return rel;
  248. }
  249. /**
  250. * Load a relation from file
  251. * This part just reads the bdd of the relation
  252. */
  253. #define rel_load(rel, f) CALL(rel_load, rel, f)
  254. VOID_TASK_2(rel_load, rel_t, rel, FILE*, f)
  255. {
  256. if (mtbdd_reader_frombinary(f, &rel->bdd, 1) != 0) Abort("Invalid file format!\n");
  257. }
  258. /**
  259. * Print a single example of a set to stdout
  260. * Assumption: the example is a full vector and variables contains all state variables...
  261. */
  262. #define print_example(example, variables) CALL(print_example, example, variables)
  263. VOID_TASK_2(print_example, BDD, example, BDDSET, variables)
  264. {
  265. uint8_t str[totalbits];
  266. if (example != sylvan_false) {
  267. sylvan_sat_one(example, variables, str);
  268. int x=0;
  269. printf("[");
  270. for (int i=0; i<vectorsize; i++) {
  271. uint32_t res = 0;
  272. for (int j=0; j<statebits[i]; j++) {
  273. if (str[x++] == 1) res++;
  274. res <<= 1;
  275. }
  276. if (i>0) printf(",");
  277. printf("%" PRIu32, res);
  278. }
  279. printf("]");
  280. }
  281. }
  282. /**
  283. * Implementation of (parallel) saturation
  284. * (assumes relations are ordered on first variable)
  285. */
  286. TASK_2(BDD, go_sat, BDD, set, int, idx)
  287. {
  288. /* Terminal cases */
  289. if (set == sylvan_false) return sylvan_false;
  290. if (idx == next_count) return set;
  291. /* Consult the cache */
  292. BDD result;
  293. const BDD _set = set;
  294. if (cache_get3(200LL<<40, _set, idx, 0, &result)) return result;
  295. mtbdd_refs_pushptr(&_set);
  296. /**
  297. * Possible improvement: cache more things (like intermediate results?)
  298. * and chain-apply more of the current level before going deeper?
  299. */
  300. /* Check if the relation should be applied */
  301. const uint32_t var = sylvan_var(next[idx]->variables);
  302. if (set == sylvan_true || var <= sylvan_var(set)) {
  303. /* Count the number of relations starting here */
  304. int count = idx+1;
  305. while (count < next_count && var == sylvan_var(next[count]->variables)) count++;
  306. count -= idx;
  307. /*
  308. * Compute until fixpoint:
  309. * - SAT deeper
  310. * - chain-apply all current level once
  311. */
  312. BDD prev = sylvan_false;
  313. BDD step = sylvan_false;
  314. mtbdd_refs_pushptr(&set);
  315. mtbdd_refs_pushptr(&prev);
  316. mtbdd_refs_pushptr(&step);
  317. while (prev != set) {
  318. prev = set;
  319. // SAT deeper
  320. set = CALL(go_sat, set, idx+count);
  321. // chain-apply all current level once
  322. for (int i=0;i<count;i++) {
  323. step = sylvan_relnext(set, next[idx+i]->bdd, next[idx+i]->variables);
  324. set = sylvan_or(set, step);
  325. step = sylvan_false; // unset, for gc
  326. }
  327. }
  328. mtbdd_refs_popptr(3);
  329. result = set;
  330. } else {
  331. /* Recursive computation */
  332. mtbdd_refs_spawn(SPAWN(go_sat, sylvan_low(set), idx));
  333. BDD high = mtbdd_refs_push(CALL(go_sat, sylvan_high(set), idx));
  334. BDD low = mtbdd_refs_sync(SYNC(go_sat));
  335. mtbdd_refs_pop(1);
  336. result = sylvan_makenode(sylvan_var(set), low, high);
  337. }
  338. /* Store in cache */
  339. cache_put3(200LL<<40, _set, idx, 0, result);
  340. mtbdd_refs_popptr(1);
  341. return result;
  342. }
  343. /**
  344. * Wrapper for the Saturation strategy
  345. */
  346. VOID_TASK_1(sat, set_t, set)
  347. {
  348. set->bdd = CALL(go_sat, set->bdd, 0);
  349. }
  350. /**
  351. * Implement parallel strategy (that performs the relnext operations in parallel)
  352. * This function does one level...
  353. */
  354. TASK_5(BDD, go_par, BDD, cur, BDD, visited, size_t, from, size_t, len, BDD*, deadlocks)
  355. {
  356. if (len == 1) {
  357. // Calculate NEW successors (not in visited)
  358. BDD succ = sylvan_relnext(cur, next[from]->bdd, next[from]->variables);
  359. bdd_refs_push(succ);
  360. if (deadlocks) {
  361. // check which BDDs in deadlocks do not have a successor in this relation
  362. BDD anc = sylvan_relprev(next[from]->bdd, succ, next[from]->variables);
  363. bdd_refs_push(anc);
  364. *deadlocks = sylvan_diff(*deadlocks, anc);
  365. bdd_refs_pop(1);
  366. }
  367. BDD result = sylvan_diff(succ, visited);
  368. bdd_refs_pop(1);
  369. return result;
  370. } else {
  371. BDD deadlocks_left;
  372. BDD deadlocks_right;
  373. if (deadlocks) {
  374. deadlocks_left = *deadlocks;
  375. deadlocks_right = *deadlocks;
  376. sylvan_protect(&deadlocks_left);
  377. sylvan_protect(&deadlocks_right);
  378. }
  379. // Recursively calculate left+right
  380. bdd_refs_spawn(SPAWN(go_par, cur, visited, from, (len+1)/2, deadlocks ? &deadlocks_left: NULL));
  381. BDD right = bdd_refs_push(CALL(go_par, cur, visited, from+(len+1)/2, len/2, deadlocks ? &deadlocks_right : NULL));
  382. BDD left = bdd_refs_push(bdd_refs_sync(SYNC(go_par)));
  383. // Merge results of left+right
  384. BDD result = sylvan_or(left, right);
  385. bdd_refs_pop(2);
  386. if (deadlocks) {
  387. bdd_refs_push(result);
  388. *deadlocks = sylvan_and(deadlocks_left, deadlocks_right);
  389. sylvan_unprotect(&deadlocks_left);
  390. sylvan_unprotect(&deadlocks_right);
  391. bdd_refs_pop(1);
  392. }
  393. return result;
  394. }
  395. }
  396. /**
  397. * Implementation of the PAR strategy
  398. */
  399. VOID_TASK_1(par, set_t, set)
  400. {
  401. BDD visited = set->bdd;
  402. BDD next_level = visited;
  403. BDD cur_level = sylvan_false;
  404. BDD deadlocks = sylvan_false;
  405. sylvan_protect(&visited);
  406. sylvan_protect(&next_level);
  407. sylvan_protect(&cur_level);
  408. sylvan_protect(&deadlocks);
  409. int iteration = 1;
  410. do {
  411. // calculate successors in parallel
  412. cur_level = next_level;
  413. deadlocks = cur_level;
  414. next_level = CALL(go_par, cur_level, visited, 0, next_count, check_deadlocks ? &deadlocks : NULL);
  415. if (check_deadlocks && deadlocks != sylvan_false) {
  416. INFO("Found %'0.0f deadlock states... ", sylvan_satcount(deadlocks, set->variables));
  417. if (deadlocks != sylvan_false) {
  418. printf("example: ");
  419. print_example(deadlocks, set->variables);
  420. check_deadlocks = 0;
  421. }
  422. printf("\n");
  423. }
  424. // visited = visited + new
  425. visited = sylvan_or(visited, next_level);
  426. if (report_table && report_levels) {
  427. size_t filled, total;
  428. sylvan_table_usage(&filled, &total);
  429. INFO("Level %d done, %'0.0f states explored, table: %0.1f%% full (%'zu nodes)\n",
  430. iteration, sylvan_satcount(visited, set->variables),
  431. 100.0*(double)filled/total, filled);
  432. } else if (report_table) {
  433. size_t filled, total;
  434. sylvan_table_usage(&filled, &total);
  435. INFO("Level %d done, table: %0.1f%% full (%'zu nodes)\n",
  436. iteration,
  437. 100.0*(double)filled/total, filled);
  438. } else if (report_levels) {
  439. INFO("Level %d done, %'0.0f states explored\n", iteration, sylvan_satcount(visited, set->variables));
  440. } else {
  441. INFO("Level %d done\n", iteration);
  442. }
  443. iteration++;
  444. } while (next_level != sylvan_false);
  445. set->bdd = visited;
  446. sylvan_unprotect(&visited);
  447. sylvan_unprotect(&next_level);
  448. sylvan_unprotect(&cur_level);
  449. sylvan_unprotect(&deadlocks);
  450. }
  451. /**
  452. * Implement sequential strategy (that performs the relnext operations one by one)
  453. * This function does one level...
  454. */
  455. TASK_5(BDD, go_bfs, BDD, cur, BDD, visited, size_t, from, size_t, len, BDD*, deadlocks)
  456. {
  457. if (len == 1) {
  458. // Calculate NEW successors (not in visited)
  459. BDD succ = sylvan_relnext(cur, next[from]->bdd, next[from]->variables);
  460. bdd_refs_push(succ);
  461. if (deadlocks) {
  462. // check which BDDs in deadlocks do not have a successor in this relation
  463. BDD anc = sylvan_relprev(next[from]->bdd, succ, next[from]->variables);
  464. bdd_refs_push(anc);
  465. *deadlocks = sylvan_diff(*deadlocks, anc);
  466. bdd_refs_pop(1);
  467. }
  468. BDD result = sylvan_diff(succ, visited);
  469. bdd_refs_pop(1);
  470. return result;
  471. } else {
  472. BDD deadlocks_left;
  473. BDD deadlocks_right;
  474. if (deadlocks) {
  475. deadlocks_left = *deadlocks;
  476. deadlocks_right = *deadlocks;
  477. sylvan_protect(&deadlocks_left);
  478. sylvan_protect(&deadlocks_right);
  479. }
  480. // Recursively calculate left+right
  481. BDD left = CALL(go_bfs, cur, visited, from, (len+1)/2, deadlocks ? &deadlocks_left : NULL);
  482. bdd_refs_push(left);
  483. BDD right = CALL(go_bfs, cur, visited, from+(len+1)/2, len/2, deadlocks ? &deadlocks_right : NULL);
  484. bdd_refs_push(right);
  485. // Merge results of left+right
  486. BDD result = sylvan_or(left, right);
  487. bdd_refs_pop(2);
  488. if (deadlocks) {
  489. bdd_refs_push(result);
  490. *deadlocks = sylvan_and(deadlocks_left, deadlocks_right);
  491. sylvan_unprotect(&deadlocks_left);
  492. sylvan_unprotect(&deadlocks_right);
  493. bdd_refs_pop(1);
  494. }
  495. return result;
  496. }
  497. }
  498. /**
  499. * Implementation of the BFS strategy
  500. */
  501. VOID_TASK_1(bfs, set_t, set)
  502. {
  503. BDD visited = set->bdd;
  504. BDD next_level = visited;
  505. BDD cur_level = sylvan_false;
  506. BDD deadlocks = sylvan_false;
  507. sylvan_protect(&visited);
  508. sylvan_protect(&next_level);
  509. sylvan_protect(&cur_level);
  510. sylvan_protect(&deadlocks);
  511. int iteration = 1;
  512. do {
  513. // calculate successors in parallel
  514. cur_level = next_level;
  515. deadlocks = cur_level;
  516. next_level = CALL(go_bfs, cur_level, visited, 0, next_count, check_deadlocks ? &deadlocks : NULL);
  517. if (check_deadlocks && deadlocks != sylvan_false) {
  518. INFO("Found %'0.0f deadlock states... ", sylvan_satcount(deadlocks, set->variables));
  519. if (deadlocks != sylvan_false) {
  520. printf("example: ");
  521. print_example(deadlocks, set->variables);
  522. check_deadlocks = 0;
  523. }
  524. printf("\n");
  525. }
  526. // visited = visited + new
  527. visited = sylvan_or(visited, next_level);
  528. if (report_table && report_levels) {
  529. size_t filled, total;
  530. sylvan_table_usage(&filled, &total);
  531. INFO("Level %d done, %'0.0f states explored, table: %0.1f%% full (%'zu nodes)\n",
  532. iteration, sylvan_satcount(visited, set->variables),
  533. 100.0*(double)filled/total, filled);
  534. } else if (report_table) {
  535. size_t filled, total;
  536. sylvan_table_usage(&filled, &total);
  537. INFO("Level %d done, table: %0.1f%% full (%'zu nodes)\n",
  538. iteration,
  539. 100.0*(double)filled/total, filled);
  540. } else if (report_levels) {
  541. INFO("Level %d done, %'0.0f states explored\n", iteration, sylvan_satcount(visited, set->variables));
  542. } else {
  543. INFO("Level %d done\n", iteration);
  544. }
  545. iteration++;
  546. } while (next_level != sylvan_false);
  547. set->bdd = visited;
  548. sylvan_unprotect(&visited);
  549. sylvan_unprotect(&next_level);
  550. sylvan_unprotect(&cur_level);
  551. sylvan_unprotect(&deadlocks);
  552. }
  553. /**
  554. * Implementation of the Chaining strategy (does not support deadlock detection)
  555. */
  556. VOID_TASK_1(chaining, set_t, set)
  557. {
  558. BDD visited = set->bdd;
  559. BDD next_level = visited;
  560. BDD succ = sylvan_false;
  561. bdd_refs_pushptr(&visited);
  562. bdd_refs_pushptr(&next_level);
  563. bdd_refs_pushptr(&succ);
  564. int iteration = 1;
  565. do {
  566. // calculate successors in parallel
  567. for (int i=0; i<next_count; i++) {
  568. succ = sylvan_relnext(next_level, next[i]->bdd, next[i]->variables);
  569. next_level = sylvan_or(next_level, succ);
  570. succ = sylvan_false; // reset, for gc
  571. }
  572. // new = new - visited
  573. // visited = visited + new
  574. next_level = sylvan_diff(next_level, visited);
  575. visited = sylvan_or(visited, next_level);
  576. if (report_table && report_levels) {
  577. size_t filled, total;
  578. sylvan_table_usage(&filled, &total);
  579. INFO("Level %d done, %'0.0f states explored, table: %0.1f%% full (%'zu nodes)\n",
  580. iteration, sylvan_satcount(visited, set->variables),
  581. 100.0*(double)filled/total, filled);
  582. } else if (report_table) {
  583. size_t filled, total;
  584. sylvan_table_usage(&filled, &total);
  585. INFO("Level %d done, table: %0.1f%% full (%'zu nodes)\n",
  586. iteration,
  587. 100.0*(double)filled/total, filled);
  588. } else if (report_levels) {
  589. INFO("Level %d done, %'0.0f states explored\n", iteration, sylvan_satcount(visited, set->variables));
  590. } else {
  591. INFO("Level %d done\n", iteration);
  592. }
  593. iteration++;
  594. } while (next_level != sylvan_false);
  595. set->bdd = visited;
  596. bdd_refs_popptr(3);
  597. }
  598. /**
  599. * Extend a transition relation to a larger domain (using s=s')
  600. */
  601. #define extend_relation(rel, vars) CALL(extend_relation, rel, vars)
  602. TASK_2(BDD, extend_relation, MTBDD, relation, MTBDD, variables)
  603. {
  604. /* first determine which state BDD variables are in rel */
  605. int has[totalbits];
  606. for (int i=0; i<totalbits; i++) has[i] = 0;
  607. MTBDD s = variables;
  608. while (!sylvan_set_isempty(s)) {
  609. uint32_t v = sylvan_set_first(s);
  610. if (v/2 >= (unsigned)totalbits) break; // action labels
  611. has[v/2] = 1;
  612. s = sylvan_set_next(s);
  613. }
  614. /* create "s=s'" for all variables not in rel */
  615. BDD eq = sylvan_true;
  616. for (int i=totalbits-1; i>=0; i--) {
  617. if (has[i]) continue;
  618. BDD low = sylvan_makenode(2*i+1, eq, sylvan_false);
  619. bdd_refs_push(low);
  620. BDD high = sylvan_makenode(2*i+1, sylvan_false, eq);
  621. bdd_refs_pop(1);
  622. eq = sylvan_makenode(2*i, low, high);
  623. }
  624. bdd_refs_push(eq);
  625. BDD result = sylvan_and(relation, eq);
  626. bdd_refs_pop(1);
  627. return result;
  628. }
  629. /**
  630. * Compute \BigUnion ( sets[i] )
  631. */
  632. #define big_union(first, count) CALL(big_union, first, count)
  633. TASK_2(BDD, big_union, int, first, int, count)
  634. {
  635. if (count == 1) return next[first]->bdd;
  636. bdd_refs_spawn(SPAWN(big_union, first, count/2));
  637. BDD right = bdd_refs_push(CALL(big_union, first+count/2, count-count/2));
  638. BDD left = bdd_refs_push(bdd_refs_sync(SYNC(big_union)));
  639. BDD result = sylvan_or(left, right);
  640. bdd_refs_pop(2);
  641. return result;
  642. }
  643. /**
  644. * Print one row of the transition matrix (for vars)
  645. */
  646. static void
  647. print_matrix_row(rel_t rel)
  648. {
  649. int r_i = 0, w_i = 0;
  650. for (int i=0; i<vectorsize; i++) {
  651. int s = 0;
  652. if (r_i < rel->r_k && rel->r_proj[r_i] == i) {
  653. s |= 1;
  654. r_i++;
  655. }
  656. if (w_i < rel->w_k && rel->w_proj[w_i] == i) {
  657. s |= 2;
  658. w_i++;
  659. }
  660. if (s == 0) fprintf(stdout, "-");
  661. else if (s == 1) fprintf(stdout, "r");
  662. else if (s == 2) fprintf(stdout, "w");
  663. else if (s == 3) fprintf(stdout, "+");
  664. }
  665. }
  666. VOID_TASK_0(gc_start)
  667. {
  668. char buf[32];
  669. to_h(getCurrentRSS(), buf);
  670. INFO("(GC) Starting garbage collection... (rss: %s)\n", buf);
  671. }
  672. VOID_TASK_0(gc_end)
  673. {
  674. char buf[32];
  675. to_h(getCurrentRSS(), buf);
  676. INFO("(GC) Garbage collection done. (rss: %s)\n", buf);
  677. }
  678. int
  679. main(int argc, char **argv)
  680. {
  681. /**
  682. * Parse command line, set locale, set startup time for INFO messages.
  683. */
  684. argp_parse(&argp, argc, argv, 0, 0, 0);
  685. setlocale(LC_NUMERIC, "en_US.utf-8");
  686. t_start = wctime();
  687. /**
  688. * Initialize Lace.
  689. *
  690. * First: setup with given number of workers (0 for autodetect) and some large size task queue.
  691. * Second: start all worker threads with default settings.
  692. * Third: setup local variables using the LACE_ME macro.
  693. */
  694. lace_init(workers, 1000000);
  695. lace_startup(0, NULL, NULL);
  696. LACE_ME;
  697. /**
  698. * Initialize Sylvan.
  699. *
  700. * First: set memory limits
  701. * - 2 GB memory, nodes table twice as big as cache, initial size halved 6x
  702. * (that means it takes 6 garbage collections to get to the maximum nodes&cache size)
  703. * Second: initialize package and subpackages
  704. * Third: add hooks to report garbage collection
  705. */
  706. sylvan_set_limits(2LL<<30, 1, 6);
  707. sylvan_init_package();
  708. sylvan_init_bdd();
  709. sylvan_gc_hook_pregc(TASK(gc_start));
  710. sylvan_gc_hook_postgc(TASK(gc_end));
  711. /**
  712. * Read the model from file
  713. */
  714. /* Open the file */
  715. FILE *f = fopen(model_filename, "r");
  716. if (f == NULL) Abort("Cannot open file '%s'!\n", model_filename);
  717. /* Read domain data */
  718. if (fread(&vectorsize, sizeof(int), 1, f) != 1) Abort("Invalid input file!\n");
  719. statebits = (int*)malloc(sizeof(int[vectorsize]));
  720. if (fread(statebits, sizeof(int), vectorsize, f) != (size_t)vectorsize) Abort("Invalid input file!\n");
  721. if (fread(&actionbits, sizeof(int), 1, f) != 1) Abort("Invalid input file!\n");
  722. totalbits = 0;
  723. for (int i=0; i<vectorsize; i++) totalbits += statebits[i];
  724. /* Read initial state */
  725. set_t states = set_load(f);
  726. /* Read number of transition relations */
  727. if (fread(&next_count, sizeof(int), 1, f) != 1) Abort("Invalid input file!\n");
  728. next = (rel_t*)malloc(sizeof(rel_t) * next_count);
  729. /* Read transition relations */
  730. for (int i=0; i<next_count; i++) next[i] = rel_load_proj(f);
  731. for (int i=0; i<next_count; i++) rel_load(next[i], f);
  732. /* We ignore the reachable states and action labels that are stored after the relations */
  733. /* Close the file */
  734. fclose(f);
  735. /**
  736. * Pre-processing and some statistics reporting
  737. */
  738. if (strategy == 2 || strategy == 3) {
  739. // for SAT and CHAINING, sort the transition relations (gnome sort because I like gnomes)
  740. int i = 1, j = 2;
  741. rel_t t;
  742. while (i < next_count) {
  743. rel_t *p = &next[i], *q = p-1;
  744. if (sylvan_var((*q)->variables) > sylvan_var((*p)->variables)) {
  745. t = *q;
  746. *q = *p;
  747. *p = t;
  748. if (--i) continue;
  749. }
  750. i = j++;
  751. }
  752. }
  753. INFO("Read file '%s'\n", model_filename);
  754. INFO("%d integers per state, %d bits per state, %d transition groups\n", vectorsize, totalbits, next_count);
  755. /* if requested, print the transition matrix */
  756. if (print_transition_matrix) {
  757. for (int i=0; i<next_count; i++) {
  758. INFO(""); // print time prefix
  759. print_matrix_row(next[i]); // print row
  760. fprintf(stdout, "\n"); // print newline
  761. }
  762. }
  763. /* merge all relations to one big transition relation if requested */
  764. if (merge_relations) {
  765. BDD newvars = sylvan_set_empty();
  766. bdd_refs_pushptr(&newvars);
  767. for (int i=totalbits-1; i>=0; i--) {
  768. newvars = sylvan_set_add(newvars, i*2+1);
  769. newvars = sylvan_set_add(newvars, i*2);
  770. }
  771. INFO("Extending transition relations to full domain.\n");
  772. for (int i=0; i<next_count; i++) {
  773. next[i]->bdd = extend_relation(next[i]->bdd, next[i]->variables);
  774. next[i]->variables = newvars;
  775. }
  776. bdd_refs_popptr(1);
  777. INFO("Taking union of all transition relations.\n");
  778. next[0]->bdd = big_union(0, next_count);
  779. for (int i=1; i<next_count; i++) {
  780. next[i]->bdd = sylvan_false;
  781. next[i]->variables = sylvan_true;
  782. }
  783. next_count = 1;
  784. }
  785. if (report_nodes) {
  786. INFO("BDD nodes:\n");
  787. INFO("Initial states: %zu BDD nodes\n", sylvan_nodecount(states->bdd));
  788. for (int i=0; i<next_count; i++) {
  789. INFO("Transition %d: %zu BDD nodes\n", i, sylvan_nodecount(next[i]->bdd));
  790. }
  791. }
  792. print_memory_usage();
  793. #ifdef HAVE_PROFILER
  794. if (profile_filename != NULL) ProfilerStart(profile_filename);
  795. #endif
  796. if (strategy == 0) {
  797. double t1 = wctime();
  798. CALL(bfs, states);
  799. double t2 = wctime();
  800. INFO("BFS Time: %f\n", t2-t1);
  801. } else if (strategy == 1) {
  802. double t1 = wctime();
  803. CALL(par, states);
  804. double t2 = wctime();
  805. INFO("PAR Time: %f\n", t2-t1);
  806. } else if (strategy == 2) {
  807. double t1 = wctime();
  808. CALL(sat, states);
  809. double t2 = wctime();
  810. INFO("SAT Time: %f\n", t2-t1);
  811. } else if (strategy == 3) {
  812. double t1 = wctime();
  813. CALL(chaining, states);
  814. double t2 = wctime();
  815. INFO("CHAINING Time: %f\n", t2-t1);
  816. } else {
  817. Abort("Invalid strategy set?!\n");
  818. }
  819. #ifdef HAVE_PROFILER
  820. if (profile_filename != NULL) ProfilerStop();
  821. #endif
  822. // Now we just have states
  823. INFO("Final states: %'0.0f states\n", sylvan_satcount(states->bdd, states->variables));
  824. if (report_nodes) {
  825. INFO("Final states: %'zu BDD nodes\n", sylvan_nodecount(states->bdd));
  826. }
  827. print_memory_usage();
  828. sylvan_stats_report(stdout);
  829. return 0;
  830. }