The source code and dockerfile for the GSW2024 AI Lab.
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.
This repo is archived. You can view files and clone it, but cannot push or open issues/pull-requests.

757 lines
24 KiB

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