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.

777 lines
25 KiB

  1. #include <argp.h>
  2. #include <assert.h>
  3. #include <inttypes.h>
  4. #include <stdio.h>
  5. #include <stdlib.h>
  6. #include <string.h>
  7. #include <sys/time.h>
  8. #include <sylvan_int.h>
  9. /* Configuration */
  10. static int workers = 0; // autodetect
  11. static int verbose = 0;
  12. static char* model_filename = NULL; // filename of model
  13. static char* bdd_filename = NULL; // filename of output BDD
  14. static char* sizes = "22,27,21,26"; // default sizes
  15. static int check_results = 0;
  16. /* argp configuration */
  17. static struct argp_option options[] =
  18. {
  19. {"workers", 'w', "<workers>", 0, "Number of workers (default=0: autodetect)", 0},
  20. {"table-sizes", 1, "<tablesize>,<tablemax>,<cachesize>,<cachemax>", 0, "Sizes of nodes table and operation cache as powers of 2", 0},
  21. {"check-results", 2, 0, 0, "Check new transition relations ", 0},
  22. {"verbose", 'v', 0, 0, "Set verbose", 0},
  23. {0, 0, 0, 0, 0, 0}
  24. };
  25. static error_t
  26. parse_opt(int key, char *arg, struct argp_state *state)
  27. {
  28. switch (key) {
  29. case 'w':
  30. workers = atoi(arg);
  31. break;
  32. case 'v':
  33. verbose = 1;
  34. break;
  35. case 1:
  36. sizes = arg;
  37. break;
  38. case 2:
  39. check_results = 1;
  40. break;
  41. case ARGP_KEY_ARG:
  42. if (state->arg_num == 0) model_filename = arg;
  43. if (state->arg_num == 1) bdd_filename = arg;
  44. if (state->arg_num >= 2) argp_usage(state);
  45. break;
  46. case ARGP_KEY_END:
  47. if (state->arg_num < 2) argp_usage(state);
  48. break;
  49. default:
  50. return ARGP_ERR_UNKNOWN;
  51. }
  52. return 0;
  53. }
  54. static struct argp argp = { options, parse_opt, "<model> [<output-bdd>]", 0, 0, 0, 0 };
  55. /* Globals */
  56. typedef struct set
  57. {
  58. MDD mdd;
  59. MDD proj;
  60. } *set_t;
  61. typedef struct relation
  62. {
  63. MDD mdd;
  64. MDD meta;
  65. } *rel_t;
  66. static size_t vector_size; // size of vector
  67. static int next_count; // number of partitions of the transition relation
  68. static rel_t *next; // each partition of the transition relation
  69. static int actionbits = 0;
  70. static int has_actions = 0;
  71. #define Abort(...) { fprintf(stderr, __VA_ARGS__); exit(-1); }
  72. /* Load a set from file */
  73. #define set_load(f) CALL(set_load, f)
  74. TASK_1(set_t, set_load, FILE*, f)
  75. {
  76. lddmc_serialize_fromfile(f);
  77. size_t mdd;
  78. size_t proj;
  79. int size;
  80. if (fread(&mdd, sizeof(size_t), 1, f) != 1) Abort("Invalid input file!\n");
  81. if (fread(&proj, sizeof(size_t), 1, f) != 1) Abort("Invalid input file!\n");
  82. if (fread(&size, sizeof(int), 1, f) != 1) Abort("Invalid input file!\n");
  83. set_t set = (set_t)malloc(sizeof(struct set));
  84. set->mdd = lddmc_ref(lddmc_serialize_get_reversed(mdd));
  85. set->proj = lddmc_ref(lddmc_serialize_get_reversed(proj));
  86. return set;
  87. }
  88. /* Load a relation from file */
  89. #define rel_load(f) CALL(rel_load, f)
  90. TASK_1(rel_t, rel_load, FILE*, f)
  91. {
  92. lddmc_serialize_fromfile(f);
  93. size_t mdd;
  94. size_t meta;
  95. if (fread(&mdd, sizeof(size_t), 1, f) != 1) Abort("Invalid input file!\n");
  96. if (fread(&meta, sizeof(size_t), 1, f) != 1) Abort("Invalid input file!\n");
  97. rel_t rel = (rel_t)malloc(sizeof(struct relation));
  98. rel->mdd = lddmc_ref(lddmc_serialize_get_reversed(mdd));
  99. rel->meta = lddmc_ref(lddmc_serialize_get_reversed(meta));
  100. return rel;
  101. }
  102. /**
  103. * Compute the highest value for each variable level.
  104. * This method is called for the set of reachable states.
  105. */
  106. static uint64_t compute_highest_id;
  107. #define compute_highest(dd, arr) CALL(compute_highest, dd, arr)
  108. VOID_TASK_2(compute_highest, MDD, dd, uint32_t*, arr)
  109. {
  110. if (dd == lddmc_true || dd == lddmc_false) return;
  111. uint64_t result = 1;
  112. if (cache_get3(compute_highest_id, dd, 0, 0, &result)) return;
  113. cache_put3(compute_highest_id, dd, 0, 0, result);
  114. mddnode_t n = LDD_GETNODE(dd);
  115. SPAWN(compute_highest, mddnode_getright(n), arr);
  116. CALL(compute_highest, mddnode_getdown(n), arr+1);
  117. SYNC(compute_highest);
  118. if (!mddnode_getcopy(n)) {
  119. const uint32_t v = mddnode_getvalue(n);
  120. while (1) {
  121. const uint32_t cur = *(volatile uint32_t*)arr;
  122. if (v <= cur) break;
  123. if (__sync_bool_compare_and_swap(arr, cur, v)) break;
  124. }
  125. }
  126. }
  127. /**
  128. * Compute the highest value for the action label.
  129. * This method is called for each transition relation.
  130. */
  131. static uint64_t compute_highest_action_id;
  132. #define compute_highest_action(dd, meta, arr) CALL(compute_highest_action, dd, meta, arr)
  133. VOID_TASK_3(compute_highest_action, MDD, dd, MDD, meta, uint32_t*, target)
  134. {
  135. if (dd == lddmc_true || dd == lddmc_false) return;
  136. if (meta == lddmc_true) return;
  137. uint64_t result = 1;
  138. if (cache_get3(compute_highest_action_id, dd, meta, 0, &result)) return;
  139. cache_put3(compute_highest_action_id, dd, meta, 0, result);
  140. /* meta:
  141. * 0 is skip
  142. * 1 is read
  143. * 2 is write
  144. * 3 is only-read
  145. * 4 is only-write
  146. * 5 is action label (at end, before -1)
  147. * -1 is end
  148. */
  149. const mddnode_t n = LDD_GETNODE(dd);
  150. const mddnode_t nmeta = LDD_GETNODE(meta);
  151. const uint32_t vmeta = mddnode_getvalue(nmeta);
  152. if (vmeta == (uint32_t)-1) return;
  153. SPAWN(compute_highest_action, mddnode_getright(n), meta, target);
  154. CALL(compute_highest_action, mddnode_getdown(n), mddnode_getdown(nmeta), target);
  155. SYNC(compute_highest_action);
  156. if (vmeta == 5) {
  157. has_actions = 1;
  158. const uint32_t v = mddnode_getvalue(n);
  159. while (1) {
  160. const uint32_t cur = *(volatile uint32_t*)target;
  161. if (v <= cur) break;
  162. if (__sync_bool_compare_and_swap(target, cur, v)) break;
  163. }
  164. }
  165. }
  166. /**
  167. * Compute the BDD equivalent of the LDD of a set of states.
  168. */
  169. static uint64_t bdd_from_ldd_id;
  170. #define bdd_from_ldd(dd, bits, firstvar) CALL(bdd_from_ldd, dd, bits, firstvar)
  171. TASK_3(MTBDD, bdd_from_ldd, MDD, dd, MDD, bits_mdd, uint32_t, firstvar)
  172. {
  173. /* simple for leaves */
  174. if (dd == lddmc_false) return mtbdd_false;
  175. if (dd == lddmc_true) return mtbdd_true;
  176. MTBDD result;
  177. /* get from cache */
  178. /* note: some assumptions about the encoding... */
  179. if (cache_get3(bdd_from_ldd_id, dd, bits_mdd, firstvar, &result)) return result;
  180. mddnode_t n = LDD_GETNODE(dd);
  181. mddnode_t nbits = LDD_GETNODE(bits_mdd);
  182. int bits = (int)mddnode_getvalue(nbits);
  183. /* spawn right, same bits_mdd and firstvar */
  184. mtbdd_refs_spawn(SPAWN(bdd_from_ldd, mddnode_getright(n), bits_mdd, firstvar));
  185. /* call down, with next bits_mdd and firstvar */
  186. MTBDD down = CALL(bdd_from_ldd, mddnode_getdown(n), mddnode_getdown(nbits), firstvar + 2*bits);
  187. /* encode current value */
  188. uint32_t val = mddnode_getvalue(n);
  189. for (int i=0; i<bits; i++) {
  190. /* encode with high bit first */
  191. int bit = bits-i-1;
  192. if (val & (1LL<<i)) down = mtbdd_makenode(firstvar + 2*bit, mtbdd_false, down);
  193. else down = mtbdd_makenode(firstvar + 2*bit, down, mtbdd_false);
  194. }
  195. /* sync right */
  196. mtbdd_refs_push(down);
  197. MTBDD right = mtbdd_refs_sync(SYNC(bdd_from_ldd));
  198. /* take union of current and right */
  199. mtbdd_refs_push(right);
  200. result = sylvan_or(down, right);
  201. mtbdd_refs_pop(2);
  202. /* put in cache */
  203. cache_put3(bdd_from_ldd_id, dd, bits_mdd, firstvar, result);
  204. return result;
  205. }
  206. /**
  207. * Compute the BDD equivalent of an LDD transition relation.
  208. */
  209. static uint64_t bdd_from_ldd_rel_id;
  210. #define bdd_from_ldd_rel(dd, bits, firstvar, meta) CALL(bdd_from_ldd_rel, dd, bits, firstvar, meta)
  211. TASK_4(MTBDD, bdd_from_ldd_rel, MDD, dd, MDD, bits_mdd, uint32_t, firstvar, MDD, meta)
  212. {
  213. if (dd == lddmc_false) return mtbdd_false;
  214. if (dd == lddmc_true) return mtbdd_true;
  215. assert(meta != lddmc_false && meta != lddmc_true);
  216. /* meta:
  217. * -1 is end
  218. * 0 is skip
  219. * 1 is read
  220. * 2 is write
  221. * 3 is only-read
  222. * 4 is only-write
  223. */
  224. MTBDD result;
  225. /* note: assumptions */
  226. if (cache_get4(bdd_from_ldd_rel_id, dd, bits_mdd, firstvar, meta, &result)) return result;
  227. const mddnode_t n = LDD_GETNODE(dd);
  228. const mddnode_t nmeta = LDD_GETNODE(meta);
  229. const mddnode_t nbits = LDD_GETNODE(bits_mdd);
  230. const int bits = (int)mddnode_getvalue(nbits);
  231. const uint32_t vmeta = mddnode_getvalue(nmeta);
  232. assert(vmeta != (uint32_t)-1);
  233. if (vmeta == 0) {
  234. /* skip level */
  235. result = bdd_from_ldd_rel(dd, mddnode_getdown(nbits), firstvar + 2*bits, mddnode_getdown(nmeta));
  236. } else if (vmeta == 1) {
  237. /* read level */
  238. assert(!mddnode_getcopy(n)); // do not process read copy nodes for now
  239. assert(mddnode_getright(n) != mtbdd_true);
  240. /* spawn right */
  241. mtbdd_refs_spawn(SPAWN(bdd_from_ldd_rel, mddnode_getright(n), bits_mdd, firstvar, meta));
  242. /* compute down with same bits / firstvar */
  243. MTBDD down = bdd_from_ldd_rel(mddnode_getdown(n), bits_mdd, firstvar, mddnode_getdown(nmeta));
  244. mtbdd_refs_push(down);
  245. /* encode read value */
  246. uint32_t val = mddnode_getvalue(n);
  247. MTBDD part = mtbdd_true;
  248. for (int i=0; i<bits; i++) {
  249. /* encode with high bit first */
  250. int bit = bits-i-1;
  251. if (val & (1LL<<i)) part = mtbdd_makenode(firstvar + 2*bit, mtbdd_false, part);
  252. else part = mtbdd_makenode(firstvar + 2*bit, part, mtbdd_false);
  253. }
  254. /* intersect read value with down result */
  255. mtbdd_refs_push(part);
  256. down = sylvan_and(part, down);
  257. mtbdd_refs_pop(2);
  258. /* sync right */
  259. mtbdd_refs_push(down);
  260. MTBDD right = mtbdd_refs_sync(SYNC(bdd_from_ldd_rel));
  261. /* take union of current and right */
  262. mtbdd_refs_push(right);
  263. result = sylvan_or(down, right);
  264. mtbdd_refs_pop(2);
  265. } else if (vmeta == 2 || vmeta == 4) {
  266. /* write or only-write level */
  267. /* spawn right */
  268. assert(mddnode_getright(n) != mtbdd_true);
  269. mtbdd_refs_spawn(SPAWN(bdd_from_ldd_rel, mddnode_getright(n), bits_mdd, firstvar, meta));
  270. /* get recursive result */
  271. MTBDD down = CALL(bdd_from_ldd_rel, mddnode_getdown(n), mddnode_getdown(nbits), firstvar + 2*bits, mddnode_getdown(nmeta));
  272. if (mddnode_getcopy(n)) {
  273. /* encode a copy node */
  274. for (int i=0; i<bits; i++) {
  275. int bit = bits-i-1;
  276. MTBDD low = mtbdd_makenode(firstvar + 2*bit + 1, down, mtbdd_false);
  277. mtbdd_refs_push(low);
  278. MTBDD high = mtbdd_makenode(firstvar + 2*bit + 1, mtbdd_false, down);
  279. mtbdd_refs_pop(1);
  280. down = mtbdd_makenode(firstvar + 2*bit, low, high);
  281. }
  282. } else {
  283. /* encode written value */
  284. uint32_t val = mddnode_getvalue(n);
  285. for (int i=0; i<bits; i++) {
  286. /* encode with high bit first */
  287. int bit = bits-i-1;
  288. if (val & (1LL<<i)) down = mtbdd_makenode(firstvar + 2*bit + 1, mtbdd_false, down);
  289. else down = mtbdd_makenode(firstvar + 2*bit + 1, down, mtbdd_false);
  290. }
  291. }
  292. /* sync right */
  293. mtbdd_refs_push(down);
  294. MTBDD right = mtbdd_refs_sync(SYNC(bdd_from_ldd_rel));
  295. /* take union of current and right */
  296. mtbdd_refs_push(right);
  297. result = sylvan_or(down, right);
  298. mtbdd_refs_pop(2);
  299. } else if (vmeta == 3) {
  300. /* only-read level */
  301. assert(!mddnode_getcopy(n)); // do not process read copy nodes
  302. /* spawn right */
  303. mtbdd_refs_spawn(SPAWN(bdd_from_ldd_rel, mddnode_getright(n), bits_mdd, firstvar, meta));
  304. /* get recursive result */
  305. MTBDD down = CALL(bdd_from_ldd_rel, mddnode_getdown(n), mddnode_getdown(nbits), firstvar + 2*bits, mddnode_getdown(nmeta));
  306. /* encode read value */
  307. uint32_t val = mddnode_getvalue(n);
  308. for (int i=0; i<bits; i++) {
  309. /* encode with high bit first */
  310. int bit = bits-i-1;
  311. /* only-read, so write same value */
  312. if (val & (1LL<<i)) down = mtbdd_makenode(firstvar + 2*bit + 1, mtbdd_false, down);
  313. else down = mtbdd_makenode(firstvar + 2*bit + 1, down, mtbdd_false);
  314. if (val & (1LL<<i)) down = mtbdd_makenode(firstvar + 2*bit, mtbdd_false, down);
  315. else down = mtbdd_makenode(firstvar + 2*bit, down, mtbdd_false);
  316. }
  317. /* sync right */
  318. mtbdd_refs_push(down);
  319. MTBDD right = mtbdd_refs_sync(SYNC(bdd_from_ldd_rel));
  320. /* take union of current and right */
  321. mtbdd_refs_push(right);
  322. result = sylvan_or(down, right);
  323. mtbdd_refs_pop(2);
  324. } else if (vmeta == 5) {
  325. assert(!mddnode_getcopy(n)); // not allowed!
  326. /* we assume this is the last value */
  327. result = mtbdd_true;
  328. /* encode action value */
  329. uint32_t val = mddnode_getvalue(n);
  330. for (int i=0; i<actionbits; i++) {
  331. /* encode with high bit first */
  332. int bit = actionbits-i-1;
  333. /* only-read, so write same value */
  334. if (val & (1LL<<i)) result = mtbdd_makenode(1000000 + bit, mtbdd_false, result);
  335. else result = mtbdd_makenode(1000000 + bit, result, mtbdd_false);
  336. }
  337. } else {
  338. assert(vmeta <= 5);
  339. }
  340. cache_put4(bdd_from_ldd_rel_id, dd, bits_mdd, firstvar, meta, result);
  341. return result;
  342. }
  343. /**
  344. * Compute the BDD equivalent of the meta variable (to a variables cube)
  345. */
  346. MTBDD
  347. meta_to_bdd(MDD meta, MDD bits_mdd, uint32_t firstvar)
  348. {
  349. if (meta == lddmc_false || meta == lddmc_true) return mtbdd_true;
  350. /* meta:
  351. * -1 is end
  352. * 0 is skip (no variables)
  353. * 1 is read (variables added by write)
  354. * 2 is write
  355. * 3 is only-read
  356. * 4 is only-write
  357. */
  358. const mddnode_t nmeta = LDD_GETNODE(meta);
  359. const uint32_t vmeta = mddnode_getvalue(nmeta);
  360. if (vmeta == (uint32_t)-1) return mtbdd_true;
  361. if (vmeta == 1) {
  362. /* return recursive result, don't go down on bits */
  363. return meta_to_bdd(mddnode_getdown(nmeta), bits_mdd, firstvar);
  364. }
  365. const mddnode_t nbits = LDD_GETNODE(bits_mdd);
  366. const int bits = (int)mddnode_getvalue(nbits);
  367. /* compute recursive result */
  368. MTBDD res = meta_to_bdd(mddnode_getdown(nmeta), mddnode_getdown(nbits), firstvar + 2*bits);
  369. /* add our variables if meta is 2,3,4 */
  370. if (vmeta != 0 && vmeta != 5) {
  371. for (int i=0; i<bits; i++) {
  372. res = mtbdd_makenode(firstvar + 2*(bits-i-1) + 1, mtbdd_false, res);
  373. res = mtbdd_makenode(firstvar + 2*(bits-i-1), mtbdd_false, res);
  374. }
  375. }
  376. return res;
  377. }
  378. static char*
  379. to_h(double size, char *buf)
  380. {
  381. const char* units[] = {"B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"};
  382. int i = 0;
  383. for (;size>1024;size/=1024) i++;
  384. sprintf(buf, "%.*f %s", i, size, units[i]);
  385. return buf;
  386. }
  387. VOID_TASK_0(gc_start)
  388. {
  389. printf("Starting garbage collection\n");
  390. }
  391. VOID_TASK_0(gc_end)
  392. {
  393. printf("Garbage collection done\n");
  394. }
  395. int
  396. main(int argc, char **argv)
  397. {
  398. argp_parse(&argp, argc, argv, 0, 0, 0);
  399. // Parse table sizes
  400. int tablesize, maxtablesize, cachesize, maxcachesize;
  401. if (sscanf(sizes, "%d,%d,%d,%d", &tablesize, &maxtablesize, &cachesize, &maxcachesize) != 4) {
  402. Abort("Invalid string for --table-sizes, try e.g. --table-sizes=23,28,22,27");
  403. }
  404. if (tablesize < 10 || maxtablesize < 10 || cachesize < 10 || maxcachesize < 10 ||
  405. tablesize > 40 || maxtablesize > 40 || cachesize > 40 || maxcachesize > 40) {
  406. Abort("Invalid string for --table-sizes, must be between 10 and 40");
  407. }
  408. if (tablesize > maxtablesize) {
  409. Abort("Invalid string for --table-sizes, tablesize is larger than maxtablesize");
  410. }
  411. if (cachesize > maxcachesize) {
  412. Abort("Invalid string for --table-sizes, cachesize is larger than maxcachesize");
  413. }
  414. // Report table sizes
  415. char buf[32];
  416. to_h((1ULL<<maxtablesize)*24+(1ULL<<maxcachesize)*36, buf);
  417. printf("Sylvan allocates %s virtual memory for nodes table and operation cache.\n", buf);
  418. to_h((1ULL<<tablesize)*24+(1ULL<<cachesize)*36, buf);
  419. printf("Initial nodes table and operation cache requires %s.\n", buf);
  420. // Init Lace
  421. lace_init(workers, 1000000); // auto-detect number of workers, use a 1,000,000 size task queue
  422. lace_startup(0, NULL, NULL); // auto-detect program stack, do not use a callback for startup
  423. LACE_ME;
  424. // Init Sylvan
  425. sylvan_set_sizes(1LL<<21, 1LL<<27, 1LL<<20, 1LL<<26);
  426. sylvan_init_package();
  427. sylvan_init_ldd();
  428. sylvan_init_mtbdd();
  429. sylvan_gc_hook_pregc(TASK(gc_start));
  430. sylvan_gc_hook_postgc(TASK(gc_end));
  431. // Obtain operation ids for the operation cache
  432. compute_highest_id = cache_next_opid();
  433. compute_highest_action_id = cache_next_opid();
  434. bdd_from_ldd_id = cache_next_opid();
  435. bdd_from_ldd_rel_id = cache_next_opid();
  436. // Open file
  437. FILE *f = fopen(model_filename, "r");
  438. if (f == NULL) Abort("Cannot open file '%s'!\n", model_filename);
  439. // Read integers per vector
  440. if (fread(&vector_size, sizeof(size_t), 1, f) != 1) Abort("Invalid input file!\n");
  441. // Read initial state
  442. if (verbose) {
  443. printf("Loading initial state... ");
  444. fflush(stdout);
  445. }
  446. set_t initial = set_load(f);
  447. if (verbose) printf("done.\n");
  448. // Read number of transitions
  449. if (fread(&next_count, sizeof(int), 1, f) != 1) Abort("Invalid input file!\n");
  450. next = (rel_t*)malloc(sizeof(rel_t) * next_count);
  451. // Read transitions
  452. if (verbose) {
  453. printf("Loading transition relations... ");
  454. fflush(stdout);
  455. }
  456. int i;
  457. for (i=0; i<next_count; i++) {
  458. next[i] = rel_load(f);
  459. if (verbose) {
  460. printf("%d, ", i);
  461. fflush(stdout);
  462. }
  463. }
  464. if (verbose) printf("done.\n");
  465. // Read whether reachable states are stored
  466. int has_reachable = 0;
  467. if (fread(&has_reachable, sizeof(int), 1, f) != 1) Abort("Input file missing reachable states!\n");
  468. if (has_reachable == 0) Abort("Input file missing reachable states!\n");
  469. // Read reachable states
  470. if (verbose) {
  471. printf("Loading reachable states... ");
  472. fflush(stdout);
  473. }
  474. set_t states = set_load(f);
  475. if (verbose) printf("done.\n");
  476. // Read number of action labels
  477. int action_labels_count = 0;
  478. if (fread(&action_labels_count, sizeof(int), 1, f) != 1) Abort("Input file missing action label count!\n");
  479. // Read action labels
  480. char *action_labels[action_labels_count];
  481. for (int i=0; i<action_labels_count; i++) {
  482. uint32_t len;
  483. if (fread(&len, sizeof(uint32_t), 1, f) != 1) Abort("Invalid input file!\n");
  484. action_labels[i] = (char*)malloc(sizeof(char[len+1]));
  485. if (fread(action_labels[i], sizeof(char), len, f) != len) Abort("Invalid input file!\n");
  486. action_labels[i][len] = 0;
  487. }
  488. // Close file
  489. fclose(f);
  490. // Report that we have read the input file
  491. printf("Read file %s.\n", argv[1]);
  492. // Report statistics
  493. if (verbose) {
  494. printf("%zu integers per state, %d transition groups\n", vector_size, next_count);
  495. printf("LDD nodes:\n");
  496. printf("Initial states: %zu LDD nodes\n", lddmc_nodecount(initial->mdd));
  497. for (i=0; i<next_count; i++) {
  498. printf("Transition %d: %zu LDD nodes\n", i, lddmc_nodecount(next[i]->mdd));
  499. }
  500. }
  501. // Report that we prepare BDD conversion
  502. if (verbose) printf("Preparing conversion to BDD...\n");
  503. // Compute highest value at each level (from reachable states)
  504. uint32_t highest[vector_size];
  505. for (size_t i=0; i<vector_size; i++) highest[i] = 0;
  506. compute_highest(states->mdd, highest);
  507. // Compute highest action label value (from transition relations)
  508. uint32_t highest_action = 0;
  509. for (int i=0; i<next_count; i++) {
  510. compute_highest_action(next[i]->mdd, next[i]->meta, &highest_action);
  511. }
  512. // Report highest integers
  513. /*
  514. printf("Highest integer per level: ");
  515. for (size_t i=0; i<vector_size; i++) {
  516. if (i>0) printf(", ");
  517. printf("%u", highest[i]);
  518. }
  519. printf("\n");
  520. */
  521. // Compute number of bits for each level
  522. int bits[vector_size];
  523. for (size_t i=0; i<vector_size; i++) {
  524. bits[i] = 0;
  525. while (highest[i] != 0) {
  526. bits[i]++;
  527. highest[i]>>=1;
  528. }
  529. if (bits[i] == 0) bits[i] = 1;
  530. }
  531. // Compute number of bits for action label
  532. actionbits = 0;
  533. while (highest_action != 0) {
  534. actionbits++;
  535. highest_action>>=1;
  536. }
  537. if (actionbits == 0 && has_actions) actionbits = 1;
  538. // Report number of bits
  539. if (verbose) {
  540. printf("Bits per level: ");
  541. for (size_t i=0; i<vector_size; i++) {
  542. if (i>0) printf(", ");
  543. printf("%d", bits[i]);
  544. }
  545. printf("\n");
  546. printf("Action bits: %d.\n", actionbits);
  547. }
  548. // Compute bits MDD
  549. MDD bits_mdd = lddmc_true;
  550. for (size_t i=0; i<vector_size; i++) {
  551. bits_mdd = lddmc_makenode(bits[vector_size-i-1], bits_mdd, lddmc_false);
  552. }
  553. lddmc_ref(bits_mdd);
  554. // Compute total number of bits
  555. int totalbits = 0;
  556. for (size_t i=0; i<vector_size; i++) {
  557. totalbits += bits[i];
  558. }
  559. // Compute state variables
  560. MTBDD state_vars = mtbdd_true;
  561. for (int i=0; i<totalbits; i++) {
  562. state_vars = mtbdd_makenode(2*(totalbits-i-1), mtbdd_false, state_vars);
  563. }
  564. mtbdd_protect(&state_vars);
  565. // Report that we begin the actual conversion
  566. if (verbose) printf("Converting to BDD...\n");
  567. // Create BDD file
  568. f = fopen(bdd_filename, "w");
  569. if (f == NULL) Abort("Cannot open file '%s'!\n", bdd_filename);
  570. // Write domain...
  571. int vector_size = 1;
  572. fwrite(&totalbits, sizeof(int), 1, f); // use number of bits as vector size
  573. fwrite(&vector_size, sizeof(int), 1, f); // set each to 1
  574. fwrite(&actionbits, sizeof(int), 1, f);
  575. // Write initial state...
  576. MTBDD new_initial = bdd_from_ldd(initial->mdd, bits_mdd, 0);
  577. assert((size_t)mtbdd_satcount(new_initial, totalbits) == (size_t)lddmc_satcount_cached(initial->mdd));
  578. mtbdd_refs_push(new_initial);
  579. {
  580. size_t a = sylvan_serialize_add(new_initial);
  581. size_t b = sylvan_serialize_add(state_vars);
  582. size_t s = totalbits;
  583. sylvan_serialize_tofile(f);
  584. fwrite(&a, sizeof(size_t), 1, f);
  585. fwrite(&s, sizeof(size_t), 1, f);
  586. fwrite(&b, sizeof(size_t), 1, f);
  587. }
  588. // Custom operation that converts to BDD given number of bits for each level
  589. MTBDD new_states = bdd_from_ldd(states->mdd, bits_mdd, 0);
  590. assert((size_t)mtbdd_satcount(new_states, totalbits) == (size_t)lddmc_satcount_cached(states->mdd));
  591. mtbdd_refs_push(new_states);
  592. // Report size of BDD
  593. if (verbose) {
  594. printf("Initial states: %zu BDD nodes\n", mtbdd_nodecount(new_initial));
  595. printf("Reachable states: %zu BDD nodes\n", mtbdd_nodecount(new_states));
  596. }
  597. // Write number of transitions
  598. fwrite(&next_count, sizeof(int), 1, f);
  599. // Write transitions
  600. for (int i=0; i<next_count; i++) {
  601. // Compute new transition relation
  602. MTBDD new_rel = bdd_from_ldd_rel(next[i]->mdd, bits_mdd, 0, next[i]->meta);
  603. mtbdd_refs_push(new_rel);
  604. // Compute new <variables> for the current transition relation
  605. MTBDD new_vars = meta_to_bdd(next[i]->meta, bits_mdd, 0);
  606. mtbdd_refs_push(new_vars);
  607. if (check_results) {
  608. // Test if the transition is correctly converted
  609. MTBDD test = sylvan_relnext(new_states, new_rel, new_vars);
  610. mtbdd_refs_push(test);
  611. MDD succ = lddmc_relprod(states->mdd, next[i]->mdd, next[i]->meta);
  612. lddmc_refs_push(succ);
  613. MTBDD test2 = bdd_from_ldd(succ, bits_mdd, 0);
  614. if (test != test2) Abort("Conversion error!\n");
  615. mtbdd_refs_pop(1);
  616. lddmc_refs_pop(1);
  617. }
  618. // Report number of nodes
  619. if (verbose) printf("Transition %d: %zu BDD nodes\n", i, mtbdd_nodecount(new_rel));
  620. size_t a = sylvan_serialize_add(new_rel);
  621. size_t b = sylvan_serialize_add(new_vars);
  622. sylvan_serialize_tofile(f);
  623. fwrite(&a, sizeof(size_t), 1, f);
  624. fwrite(&b, sizeof(size_t), 1, f);
  625. }
  626. // Write reachable states
  627. has_reachable = 1;
  628. fwrite(&has_reachable, sizeof(int), 1, f);
  629. {
  630. size_t a = sylvan_serialize_add(new_states);
  631. size_t b = sylvan_serialize_add(state_vars);
  632. size_t s = totalbits;
  633. sylvan_serialize_tofile(f);
  634. fwrite(&a, sizeof(size_t), 1, f);
  635. fwrite(&s, sizeof(size_t), 1, f);
  636. fwrite(&b, sizeof(size_t), 1, f);
  637. }
  638. // Write action labels
  639. fwrite(&action_labels_count, sizeof(int), 1, f);
  640. for (int i=0; i<action_labels_count; i++) {
  641. uint32_t len = strlen(action_labels[i]);
  642. fwrite(&len, sizeof(uint32_t), 1, f);
  643. fwrite(action_labels[i], sizeof(char), len, f);
  644. }
  645. // Close the file
  646. fclose(f);
  647. // Report to the user
  648. printf("Written file %s.\n", bdd_filename);
  649. // Report Sylvan statistics (if SYLVAN_STATS is set)
  650. if (verbose) sylvan_stats_report(stdout);
  651. return 0;
  652. }