1 /*
2 * This file is part of the Yices SMT Solver.
3 * Copyright (C) 2017 SRI International.
4 *
5 * Yices is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * Yices is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with Yices. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 /*
20 * PARSE A QF_BV BENCHMARK IN SMT-LIB FORMAT THEN CONVERT IT TO CNF
21 */
22
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <stdbool.h>
26 #include <inttypes.h>
27
28 #include "api/smt_logic_codes.h"
29 #include "api/yices_globals.h"
30 #include "context/context.h"
31 #include "frontend/smt1/smt_lexer.h"
32 #include "frontend/smt1/smt_parser.h"
33 #include "frontend/smt1/smt_term_stack.h"
34 #include "solvers/bv/bvsolver.h"
35 #include "solvers/bv/dimacs_printer.h"
36 #include "utils/command_line.h"
37 #include "utils/cputime.h"
38 #include "utils/memsize.h"
39 #include "yices.h"
40 #include "yices_exit_codes.h"
41
42
43 static lexer_t lexer;
44 static parser_t parser;
45 static tstack_t stack;
46 static smt_benchmark_t bench;
47 static context_t context;
48
49
50 /*
51 * Parameters:
52 * - filename = name of the input file (in SMT format)
53 * if filenemae is NULL, we read stdin
54 * - out_file = name of the output file
55 * if out_file is NULL, we use 'yices2bblast.cnf' as default.
56 */
57 static char *filename;
58 static char *out_file;
59
60
61 /*
62 * Command-line options
63 */
64 enum {
65 out_option,
66 help_flag,
67 };
68
69 #define NUM_OPTIONS 2
70
71 static option_desc_t options[NUM_OPTIONS] = {
72 { "out", 'o', MANDATORY_STRING, out_option },
73 { "help", 'h', FLAG_OPTION, help_flag },
74 };
75
print_help(char * progname)76 static void print_help(char *progname) {
77 printf("Usage: %s [options] filename\n\n", progname);
78 printf("Options:\n"
79 " --help, -h Display this information\n"
80 " --out=<file> or -o <file> Set the output file (default = 'yices2bblast.cnf')\n"
81 "\n");
82 fflush(stdout);
83 }
84
print_usage(char * progname)85 static void print_usage(char *progname) {
86 fprintf(stderr, "Try %s --help for more information\n", progname);
87 }
88
89
90 /*
91 * Parse the command line:
92 * - set filename, dump, and dump_file
93 */
process_command_line(int argc,char * argv[])94 static void process_command_line(int argc, char *argv[]) {
95 cmdline_parser_t parser;
96 cmdline_elem_t elem;
97
98 // default options
99 filename = NULL;
100 out_file = NULL;
101
102 init_cmdline_parser(&parser, options, NUM_OPTIONS, argv, argc);
103 for (;;) {
104 cmdline_parse_element(&parser, &elem);
105 switch (elem.status) {
106 case cmdline_done:
107 goto done;
108
109 case cmdline_argument:
110 if (filename == NULL) {
111 filename = elem.arg;
112 } else {
113 fprintf(stderr, "%s: can't have several input files\n", parser.command_name);
114 goto bad_usage;
115 }
116 break;
117
118 case cmdline_option:
119 switch (elem.key) {
120 case out_option:
121 if (out_file == NULL) {
122 out_file = elem.s_value;
123 } else {
124 fprintf(stderr, "%s: can't have several output files\n", parser.command_name);
125 goto bad_usage;
126 }
127 break;
128
129 case help_flag:
130 print_help(parser.command_name);
131 goto quick_exit;
132
133 default:
134 assert(false);
135 break;
136 }
137 break;
138
139 case cmdline_error:
140 cmdline_print_error(&parser, &elem);
141 goto bad_usage;
142 }
143 }
144
145 done:
146 // check that dump_file and filename are different
147 if (filename != NULL && out_file != NULL && strcmp(filename, out_file) == 0) {
148 fprintf(stderr, "%s: can't use '%s' for both input and dump file\n", parser.command_name, out_file);
149 goto bad_usage;
150 }
151
152 return;
153
154 quick_exit:
155 exit(YICES_EXIT_SUCCESS);
156
157 bad_usage:
158 print_usage(parser.command_name);
159 exit(YICES_EXIT_USAGE);
160 }
161
162
163 /*
164 * Conversion of internalization code to an error message
165 */
166 static const char * const code2error[NUM_INTERNALIZATION_ERRORS] = {
167 "no error",
168 "internal error",
169 "type error",
170 "formula contains free variables",
171 "logic not supported",
172 "context does not support UF",
173 "context does not support scalar types",
174 "context does not support tuples",
175 "context does not support uninterpreted types",
176 "context does not support arithmetic",
177 "context does not support bitvectors",
178 "context does not support function equalities",
179 "context does not support quantifiers",
180 "context does not support lambdas",
181 "not an IDL formula",
182 "not an RDL formula",
183 "non-linear arithmetic not supported",
184 "too many variables for the arithmetic solver",
185 "too many atoms for the arithmetic solver",
186 "arithmetic solver exception",
187 "bitvector solver exception",
188 };
189
190
191 /*
192 * Print the translation code returned by assert_formulas
193 */
print_internalization_code(int32_t code)194 static void print_internalization_code(int32_t code) {
195 assert(-NUM_INTERNALIZATION_ERRORS < code && code <= TRIVIALLY_UNSAT);
196 if (code == TRIVIALLY_UNSAT) {
197 printf("Internalization OK\n");
198 printf("Assertions simplify to false\n\n");
199 printf("unsat\n");
200 } else if (code == CTX_NO_ERROR) {
201 printf("Internalization OK\n\n");
202 printf("unknown\n");
203 } else {
204 assert(code < 0);
205 code = - code;
206 printf("Internalization error: %s\n\n", code2error[code]);
207 printf("unknown\n");
208 }
209
210 fflush(stdout);
211 }
212
213
214
215 /*
216 * Temporary test. Check whether one of the input assertion is reduced
217 * to false by simplification. This is checked independent of the
218 * logic label.
219 */
benchmark_reduced_to_false(smt_benchmark_t * bench)220 static bool benchmark_reduced_to_false(smt_benchmark_t *bench) {
221 uint32_t i, n;
222 term_t f;
223
224 n = bench->nformulas;
225 for (i=0; i<n; i++) {
226 f = bench->formulas[i];
227 assert(is_boolean_term(__yices_globals.terms, f));
228 if (f == false_term) {
229 return true;
230 }
231 }
232
233 return false;
234 }
235
236
237 /*
238 * Temporary test. Check whether the assertions are trivially true
239 * after internalization and variable elimination (i.e., vectors
240 * top_eqs, top_formulas, top_atoms, top_interns are all empty).
241 */
context_is_empty(context_t * ctx)242 static bool context_is_empty(context_t *ctx) {
243 return ctx->top_eqs.size == 0 && ctx->top_atoms.size == 0 &&
244 ctx->top_formulas.size == 0 && ctx->top_interns.size == 0;
245 }
246
247
248 #if 0
249
250 // NOT USED ANYMORE
251 /*
252 * Export the clauses of core into the given file:
253 * - use the DIMACs CNF format
254 * - don't export the learned clauses
255 * - return 0 if this works
256 * - return -1 if the file can't be opened
257 */
258 static int32_t smt_core_export(smt_core_t *core, const char *filename) {
259 FILE *f;
260
261 f = fopen(filename, "w");
262 if (f == NULL) return -1;
263 dimacs_print_core(f, core);
264 fclose(f);
265
266 return 0;
267 }
268
269 #endif
270
271 /*
272 * Export the context
273 */
export_context(context_t * ctx,const char * filename)274 static int32_t export_context(context_t *ctx, const char *filename) {
275 FILE *f;
276
277 f = fopen(filename, "w");
278 if (f == NULL) return -1;
279 dimacs_print_bvcontext(f, ctx);
280 fclose(f);
281
282 return 0;
283 }
284
285
286 /*
287 * Test bitblasting
288 */
test_blaster(smt_benchmark_t * bench)289 static void test_blaster(smt_benchmark_t *bench) {
290 int32_t code;
291 smt_logic_t logic;
292
293 /*
294 * Check the benchmark logic
295 */
296 if (bench->logic_name == NULL) {
297 printf("No logic specified\n\nunknown\n");
298 return;
299 }
300
301 logic = smt_logic_code(bench->logic_name);
302 assert(AUFLIA <= logic && logic <= SMT_UNKNOWN);
303 if (logic != QF_BV && logic != QF_UFBV) {
304 printf("Input is not a bit-vector problem (logic= %s)\n", bench->logic_name);
305 return;
306 }
307
308 init_context(&context, __yices_globals.terms, logic, CTX_MODE_ONECHECK, CTX_ARCH_BV, false);
309 enable_lax_mode(&context); // FOR TESTING
310 enable_variable_elimination(&context);
311 enable_eq_abstraction(&context);
312 enable_bvarith_elimination(&context);
313
314 code = assert_formulas(&context, bench->nformulas, bench->formulas);
315 if (code != CTX_NO_ERROR) {
316 print_internalization_code(code);
317 } else if (context_is_empty(&context)) {
318 printf("Reduced to the empty context\n\nsat\n");
319 } else {
320 assert(context_has_bv_solver(&context));
321 // test bit-blasting
322 if (bv_solver_bitblast(context.bv_solver)) {
323 printf("Bitblasting OK\n");
324 fflush(stdout);
325 print_internalization_code(code);
326 fflush(stdout);
327 // one round of smt_process to handle the lemmas
328 smt_process(context.core);
329
330 if (out_file == NULL) {
331 out_file = "yices2bblast.cnf";
332 }
333 if (export_context(&context, out_file) == 0) {
334 printf("Exported to %s\n", out_file);
335 } else {
336 perror(out_file);
337 }
338
339 } else {
340 printf("Bitbasting detected an inconsistency\n\nunsat\n");
341 }
342
343 }
344
345 delete_context(&context);
346 }
347
348
349 /*
350 * Test: parse and internalize an SMT benchmark
351 */
main(int argc,char * argv[])352 int main(int argc, char *argv[]) {
353 int32_t code;
354 double time, mem_used;
355
356 process_command_line(argc, argv);
357
358 if (filename != NULL) {
359 // read from file
360 if (init_smt_file_lexer(&lexer, filename) < 0) {
361 perror(filename);
362 exit(YICES_EXIT_FILE_NOT_FOUND);
363 }
364 } else {
365 // read from stdin
366 init_smt_stdin_lexer(&lexer);
367 }
368
369 yices_init();
370 init_smt_tstack(&stack);
371 init_parser(&parser, &lexer, &stack);
372 init_benchmark(&bench);
373 code = parse_smt_benchmark(&parser, &bench);
374 if (code == 0) {
375 printf("No syntax error found\n");
376 printf("term table: %"PRIu32" elements\n", __yices_globals.terms->nelems);
377 fflush(stdout);
378 } else {
379 exit(YICES_EXIT_SYNTAX_ERROR);
380 }
381
382 if (benchmark_reduced_to_false(&bench)) {
383 printf("Reduced to false\n\nunsat\n");
384 } else {
385 test_blaster(&bench);
386 }
387 fflush(stdout);
388
389 time = get_cpu_time();
390 mem_used = mem_size() / (1024 * 1024);
391 printf("\nConstruction time: %.4f s\n", time);
392 printf("Memory used: %.2f MB\n\n", mem_used);
393 fflush(stdout);
394
395 delete_benchmark(&bench);
396 delete_parser(&parser);
397 close_lexer(&lexer);
398 delete_tstack(&stack);
399 yices_exit();
400
401 return YICES_EXIT_SUCCESS;
402 }
403
404