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 * SUPPORT FOR EXISTS/FORALL SOLVING
21 */
22
23 #if defined(CYGWIN) || defined(MINGW)
24 #ifndef __YICES_DLLSPEC__
25 #define __YICES_DLLSPEC__ __declspec(dllexport)
26 #endif
27 #endif
28
29 #include <inttypes.h>
30
31 #include "context/context.h"
32 #include "exists_forall/efsolver.h"
33 #include "model/literal_collector.h" //get_implicant (pre qf normalization)
34 #include "model/projection.h" //project_literals (quantifier elimination)
35 #include "terms/term_substitution.h"
36 #include "utils/index_vectors.h"
37
38 #include "yices.h"
39
40 #define EF_VERBOSE 0
41
42
43 /*
44 * PRINT STUFF
45 */
46
47 /*
48 * Print solution found by the ef solver
49 */
print_ef_solution(FILE * f,ef_solver_t * solver)50 void print_ef_solution(FILE *f, ef_solver_t *solver) {
51 ef_prob_t *prob;
52 uint32_t i, n;
53
54 prob = solver->prob;
55 n = ef_prob_num_evars(prob);
56
57 for (i=0; i<n; i++) {
58 fprintf(f, "%s := ", yices_get_term_name(prob->all_evars[i]));
59 yices_pp_term(f, solver->evalue[i], 100, 1, 10);
60 }
61 }
62
63
64 /*
65 * Show witness found for constraint i
66 */
print_forall_witness(FILE * f,ef_solver_t * solver,uint32_t i)67 void print_forall_witness(FILE *f, ef_solver_t *solver, uint32_t i) {
68 ef_prob_t *prob;
69 ef_cnstr_t *cnstr;
70 uint32_t j, n;
71
72 prob = solver->prob;
73 assert(i < ef_prob_num_constraints(prob));
74 cnstr = prob->cnstr + i;
75
76 n = ef_constraint_num_uvars(cnstr);
77 for (j=0; j<n; j++) {
78 fprintf(f, "%s := ", yices_get_term_name(cnstr->uvars[j]));
79 yices_pp_term(f, solver->uvalue_aux.data[j], 100, 1, 10);
80 }
81 }
82
83
84 /*
85 * Print the full map:
86 * - the variables are in solver->all_vars
87 * - their values are in solver->all_values
88 */
print_full_map(FILE * f,ef_solver_t * solver)89 void print_full_map(FILE *f, ef_solver_t *solver) {
90 uint32_t i, n;
91
92 n = solver->all_vars.size;
93 assert(n == solver->all_values.size);
94 for (i=0; i<n; i++) {
95 fprintf(f, "%s := ", yices_get_term_name(solver->all_vars.data[i]));
96 yices_pp_term(f, solver->all_values.data[i], 100, 1, 10);
97 }
98 fprintf(f, "(%"PRIu32" variables)\n", n);
99 }
100
101
102 /*
103 * GLOBAL PROCEDURES
104 */
105
106 /*
107 * Initialize solver:
108 * - prob = problem descriptor
109 * - logic/arch/parameters = for context initialization and check
110 */
init_ef_solver(ef_solver_t * solver,ef_prob_t * prob,smt_logic_t logic,context_arch_t arch)111 void init_ef_solver(ef_solver_t *solver, ef_prob_t *prob, smt_logic_t logic, context_arch_t arch) {
112 uint32_t n;
113
114 solver->prob = prob;
115 solver->logic = logic;
116 solver->arch = arch;
117 solver->status = EF_STATUS_IDLE;
118 solver->error_code = 0;
119
120 solver->parameters = NULL;
121 solver->option = EF_GEN_BY_SUBST_OPTION;
122 solver->max_samples = 0;
123 solver->max_iters = 0;
124 solver->scan_idx = 0;
125
126 solver->exists_context = NULL;
127 solver->forall_context = NULL;
128 solver->exists_model = NULL;
129
130 n = ef_prob_num_evars(prob);
131 assert(n <= UINT32_MAX/sizeof(term_t));
132 solver->evalue = (term_t *) safe_malloc(n * sizeof(term_t));
133
134 n = ef_prob_num_uvars(prob);
135 assert(n <= UINT32_MAX/sizeof(term_t));
136 solver->uvalue = (term_t *) safe_malloc(n * sizeof(term_t));
137
138
139 solver->full_model = NULL;
140 init_ivector(&solver->implicant, 20);
141 init_ivector(&solver->projection, 20);
142
143 init_ivector(&solver->evalue_aux, 64);
144 init_ivector(&solver->uvalue_aux, 64);
145 init_ivector(&solver->all_vars, 64);
146 init_ivector(&solver->all_values, 64);
147
148 solver->trace = NULL;
149 }
150
151
152 /*
153 * Delete the whole thing
154 */
delete_ef_solver(ef_solver_t * solver)155 void delete_ef_solver(ef_solver_t *solver) {
156 if (solver->exists_context != NULL) {
157 delete_context(solver->exists_context);
158 safe_free(solver->exists_context);
159 solver->exists_context = NULL;
160 }
161 if (solver->forall_context != NULL) {
162 delete_context(solver->forall_context);
163 safe_free(solver->forall_context);
164 solver->forall_context = NULL;
165 }
166 if (solver->exists_model != NULL) {
167 yices_free_model(solver->exists_model);
168 solver->exists_model = NULL;
169 }
170
171 safe_free(solver->evalue);
172 safe_free(solver->uvalue);
173 solver->evalue = NULL;
174 solver->uvalue = NULL;
175
176 if (solver->full_model != NULL) {
177 yices_free_model(solver->full_model);
178 solver->full_model = NULL;
179 }
180 delete_ivector(&solver->implicant);
181 delete_ivector(&solver->projection);
182
183 delete_ivector(&solver->evalue_aux);
184 delete_ivector(&solver->uvalue_aux);
185 delete_ivector(&solver->all_vars);
186 delete_ivector(&solver->all_values);
187 }
188
189
190
191 /*
192 * Set the trace:
193 * - the current tracer must be NULL.
194 */
ef_solver_set_trace(ef_solver_t * solver,tracer_t * trace)195 void ef_solver_set_trace(ef_solver_t *solver, tracer_t *trace) {
196 assert(solver->trace == NULL);
197 solver->trace = trace;
198 }
199
200
201
202
203 /*
204 * OPERATIONS ON THE EXISTS CONTEXT
205 */
206
207 /*
208 * Allocate and initialize the exists context
209 */
init_exists_context(ef_solver_t * solver)210 static void init_exists_context(ef_solver_t *solver) {
211 context_t *ctx;
212
213 assert(solver->exists_context == NULL);
214 ctx = (context_t *) safe_malloc(sizeof(context_t));
215 init_context(ctx, solver->prob->terms, solver->logic, CTX_MODE_MULTICHECKS, solver->arch, false); // qflag is false
216 solver->exists_context = ctx;
217 if (solver->trace != NULL) {
218 context_set_trace(ctx, solver->trace);
219 }
220 }
221
222 /*
223 * Assert all the conditions from prob into ctx
224 * - return the internalization code
225 */
exists_context_assert_conditions(ef_solver_t * solver)226 static int32_t exists_context_assert_conditions(ef_solver_t *solver) {
227 context_t *ctx;
228 uint32_t n;
229
230 ctx = solver->exists_context;
231
232 assert(ctx != NULL && context_status(ctx) == STATUS_IDLE);
233
234 n = ef_prob_num_conditions(solver->prob);
235 return assert_formulas(ctx, n, solver->prob->conditions);
236 }
237
238
239 /*
240 * Add assertion f to the exists context
241 * - return the internalization code
242 * - the exists context must not be UNSAT
243 */
update_exists_context(ef_solver_t * solver,term_t f)244 static int32_t update_exists_context(ef_solver_t *solver, term_t f) {
245 context_t *ctx;
246 smt_status_t status;
247 int32_t code;
248
249 ctx = solver->exists_context;
250
251 assert(ctx != NULL && is_boolean_term(ctx->terms, f));
252
253 status = context_status(ctx);
254 switch (status) {
255 case STATUS_SAT:
256 case STATUS_UNKNOWN:
257 context_clear(ctx);
258 assert(context_status(ctx) == STATUS_IDLE);
259 case STATUS_IDLE:
260 code = assert_formula(ctx, f);
261 break;
262
263 default:
264 code = INTERNAL_ERROR; // should not happen
265 break;
266 }
267
268 return code;
269 }
270
271
272 /*
273 * SUBSTITUTION
274 */
275
276 /*
277 * Apply the substitution defined by var and value to term t
278 * - n = size of arrays var and value
279 * - return code < 0 means that an error occurred during the substitution
280 * (cf. apply_term_subst in term_substitution.h).
281 */
ef_substitution(ef_prob_t * prob,term_t * var,term_t * value,uint32_t n,term_t t)282 static term_t ef_substitution(ef_prob_t *prob, term_t *var, term_t *value, uint32_t n, term_t t) {
283 term_subst_t subst;
284 term_t g;
285
286 init_term_subst(&subst, prob->manager, n, var, value);
287 g = apply_term_subst(&subst, t);
288 delete_term_subst(&subst);
289
290 return g;
291 }
292
293
294
295
296
297 /*
298 * FORALL CONTEXT
299 */
300
301 /*
302 * Allocate and initialize it (prior to sampling)
303 */
init_forall_context(ef_solver_t * solver)304 static void init_forall_context(ef_solver_t *solver) {
305 context_t *ctx;
306
307 assert(solver->forall_context == NULL);
308 ctx = (context_t *) safe_malloc(sizeof(context_t));
309 init_context(ctx, solver->prob->terms, solver->logic, CTX_MODE_MULTICHECKS, solver->arch, false);
310 solver->forall_context = ctx;
311 if (solver->trace != NULL) {
312 context_set_trace(ctx, solver->trace);
313 }
314 }
315
316
317 /*
318 * Assert (B and not C) in ctx
319 * - return the assertion code
320 */
forall_context_assert(ef_solver_t * solver,term_t b,term_t c)321 static int32_t forall_context_assert(ef_solver_t *solver, term_t b, term_t c) {
322 context_t *ctx;
323 term_t assertions[2];
324
325 ctx = solver->forall_context;
326
327 assert(ctx != NULL && context_status(ctx) == STATUS_IDLE);
328 assert(is_boolean_term(ctx->terms, b) && is_boolean_term(ctx->terms, c));
329
330 assertions[0] = b;
331 assertions[1] = opposite_term(c);
332 return assert_formulas(ctx, 2, assertions);
333 }
334
335
336 /*
337 * Free or reset the forall_context:
338 * - if flag del is true: delete
339 * - otherwise, just reset
340 */
clear_forall_context(ef_solver_t * solver,bool del)341 static void clear_forall_context(ef_solver_t *solver, bool del) {
342 assert(solver->forall_context != NULL);
343 if (del) {
344 delete_context(solver->forall_context);
345 safe_free(solver->forall_context);
346 solver->forall_context = NULL;
347 } else {
348 reset_context(solver->forall_context);
349 }
350 }
351
352
353 /*
354 * Get the forall_context: return it if it's not NULL
355 * allocate and initialize it otherwise.
356 */
get_forall_context(ef_solver_t * solver)357 static context_t *get_forall_context(ef_solver_t *solver) {
358 if (solver->forall_context == NULL) {
359 init_forall_context(solver);
360 }
361 return solver->forall_context;
362 }
363
364
365
366
367
368 /*
369 * SAT SOLVING
370 */
371
372 /*
373 * Check satisfiability and get a model
374 * - ctx = the context
375 * - parameters = heuristic settings (if parameters is NULL, the defaults are used)
376 * - var = array of n uninterpreted terms
377 * - n = size of array evar and value
378 * Output parameters;
379 * - value = array of n terms (to receive the value of each var)
380 * - model = to export the model (if model is NULL, nothing is exported)
381 *
382 * The return code is as in check_context:
383 * 1) if code = STATUS_SAT then the context is satisfiable
384 * and a model is stored in value[0 ... n-1]
385 * - value[i] = a constant term mapped to evar[i] in the model
386 * 2) code = STATUS_UNSAT: not satisfiable
387 *
388 * 3) other codes report an error of some kind or STATUS_INTERRUPTED
389 */
satisfy_context(context_t * ctx,const param_t * parameters,term_t * var,uint32_t n,term_t * value,model_t ** model)390 static smt_status_t satisfy_context(context_t *ctx, const param_t *parameters, term_t *var, uint32_t n, term_t *value, model_t **model) {
391 smt_status_t stat;
392 model_t *mdl;
393 int32_t code;
394
395 assert(context_status(ctx) == STATUS_IDLE);
396
397 stat = check_context(ctx, parameters);
398 switch (stat) {
399 case STATUS_SAT:
400 case STATUS_UNKNOWN:
401 mdl = yices_get_model(ctx, true);
402 code = yices_term_array_value(mdl, n, var, value);
403 if (model != NULL) {
404 *model = mdl;
405 } else {
406 yices_free_model(mdl);
407 }
408
409 if (code < 0) {
410 // can't convert the model to constant terms
411 stat = STATUS_ERROR;
412 }
413 break;
414
415 default:
416 // can't build a model
417 break;
418 }
419
420 return stat;
421 }
422
423
424
425 /*
426 * Check satisfiability of the exists_context
427 * - first free the exists_model if non-NULL
428 * - if SAT build a model (in solver->evalue) and store the exists_model
429 */
ef_solver_check_exists(ef_solver_t * solver)430 static smt_status_t ef_solver_check_exists(ef_solver_t *solver) {
431 term_t *evar;
432 uint32_t n;
433
434 if (solver->exists_model != NULL) {
435 yices_free_model(solver->exists_model);
436 solver->exists_model = NULL;
437 }
438
439 evar = solver->prob->all_evars;
440 n = iv_len(evar);
441 return satisfy_context(solver->exists_context, solver->parameters, evar, n, solver->evalue, &solver->exists_model);
442 }
443
444
445
446 /*
447 * Test the current exists model using universal constraint i
448 * - i must be a valid index (i.e., 0 <= i < solver->prob->num_cnstr)
449 * - this checks the assertion B_i and not C_i after replacing existential
450 * variables by their values (stored in evalue)
451 * - return code:
452 * if STATUS_SAT (or STATUS_UNKNOWN): a model of (B_i and not C_i)
453 * is found and stored in uvalue_aux
454 * if STATUS_UNSAT: no model found (current exists model is good as
455 * far as constraint i is concerned)
456 * anything else: an error or interruption
457 *
458 * - if we get an error or interruption, solver->status is updated
459 * otherwise, it is kept as is (should be EF_STATUS_SEARCHING)
460 */
ef_solver_test_exists_model(ef_solver_t * solver,uint32_t i)461 static smt_status_t ef_solver_test_exists_model(ef_solver_t *solver, uint32_t i) {
462 context_t *forall_ctx;
463 ef_cnstr_t *cnstr;
464 term_t *value;
465 term_t g;
466 uint32_t n;
467 int32_t code;
468 smt_status_t status;
469
470 assert(i < ef_prob_num_constraints(solver->prob));
471 cnstr = solver->prob->cnstr + i;
472
473 n = ef_prob_num_evars(solver->prob);
474 g = ef_substitution(solver->prob, solver->prob->all_evars, solver->evalue, n, cnstr->guarantee);
475 if (g < 0) {
476 // error in substitution
477 solver->status = EF_STATUS_SUBST_ERROR;
478 solver->error_code = g;
479 return STATUS_ERROR;
480 }
481
482 /*
483 * make uvalue_aux large enough
484 */
485 n = ef_constraint_num_uvars(cnstr);
486 resize_ivector(&solver->uvalue_aux, n);
487 solver->uvalue_aux.size = n;
488 value = solver->uvalue_aux.data;
489
490 forall_ctx = get_forall_context(solver);
491
492 code = forall_context_assert(solver, cnstr->assumption, g); // assert B_i(Y_i) and not g(Y_i)
493 if (code == CTX_NO_ERROR) {
494 status = satisfy_context(forall_ctx, solver->parameters, cnstr->uvars, n, value, NULL);
495 switch (status) {
496 case STATUS_SAT:
497 case STATUS_UNKNOWN:
498 case STATUS_UNSAT:
499 break;
500
501 case STATUS_INTERRUPTED:
502 solver->status = EF_STATUS_INTERRUPTED;
503 break;
504
505 default:
506 solver->status = EF_STATUS_CHECK_ERROR;
507 solver->error_code = status;
508 break;
509 }
510
511 } else if (code == TRIVIALLY_UNSAT) {
512 assert(context_status(forall_ctx) == STATUS_UNSAT);
513 status = STATUS_UNSAT;
514
515 } else {
516 // error in assertion
517 solver->status = EF_STATUS_ASSERT_ERROR;
518 solver->error_code = code;
519 status = STATUS_ERROR;
520 }
521
522 clear_forall_context(solver, true);
523
524 return status;
525 }
526
527
528
529 /*
530 * PROJECTION
531 */
532
533 /*
534 * Search for x in sorted array v
535 */
find_elem(int32_t * v,int32_t x)536 static int32_t find_elem(int32_t *v, int32_t x) {
537 uint32_t i, j, k;
538
539 i = 0;
540 j = iv_len(v);
541 while (i < j) {
542 k = (i + j) >> 1;
543 assert(i <= k && k < j);
544 if (v[k] == x) return k;
545 if (v[k] < x) {
546 i = k+1;
547 } else {
548 j = k;
549 }
550 }
551
552 return -1; // not found
553 }
554
555
556 /*
557 * Restrict a model defined by var and value to subvar
558 * - the result is stored in subvalue
559 * - n = size of arrays subvar and subvalue
560 * - preconditions:
561 * var is a sorted index vector
562 * every element of subvar occurs in var
563 * value has the same size as var
564 */
project_model(int32_t * var,term_t * value,term_t * subvar,term_t * subvalue,uint32_t n)565 static void project_model(int32_t *var, term_t *value, term_t *subvar, term_t *subvalue, uint32_t n) {
566 uint32_t i;
567 int32_t k;
568
569 for (i=0; i<n; i++) {
570 k = find_elem(var, subvar[i]);
571 assert(k >= 0 && subvar[i] == var[k]);
572 subvalue[i] = value[k];
573 }
574 }
575
576
577 /*
578 * Project model onto a subset of the existential variables
579 * - value[i] = exist model = array of constant values
580 * - evar = an array of n existential variables: every element of evar occurs in ef->all_evars
581 * - then this function builds the model restricted to evar into array eval
582 *
583 * Assumption:
584 * - value[i] = value mapped to ef->all_evars[i] for i=0 ... num_evars-1
585 * - every x in sub_var occurs somewhere in ef->all_evars
586 * - then if evar[i] = x and x is equal to all_evar[k] the function copies
587 * value[k] into eval[i]
588 */
ef_project_exists_model(ef_prob_t * prob,term_t * value,term_t * evar,term_t * eval,uint32_t n)589 static void ef_project_exists_model(ef_prob_t *prob, term_t *value, term_t *evar, term_t *eval, uint32_t n) {
590 project_model(prob->all_evars, value, evar, eval, n);
591 }
592
593
594
595
596 /*
597 * PRE SAMPLING
598 */
599
600 /*
601 * Sampling for the i-th constraint in solver->prob
602 * - search for at most max_samples y's that satisfy the assumption of constraint i in prob
603 * - for each such y, add a new constraint to the exist context ctx
604 * - max_samples = bound on the number of samples to take (must be positive)
605 *
606 * Constraint i is of the form
607 * (FORALL Y_i: B_i(Y_i) => C(X_i, Y_i))
608 * - every sample is a model y_i that satisfies B_i.
609 * - for each sample, we learn that any good candidate X_i must satisfy C(X_i, y_i).
610 * So we add the constraint C(X_i, y_i) to ctx.
611 *
612 * Update the solver->status to
613 * - EF_STATUS_UNSAT if the exits context is trivially unsat
614 * - EF_STATUS_..._ERROR if something goes wrong
615 * - EF_STATUS_INTERRUPTED if a call to check/recheck context is interrupted
616 * keep it unchanged otherwise (should be EF_STATUS_SEARCHING).
617 */
ef_sample_constraint(ef_solver_t * solver,uint32_t i)618 static void ef_sample_constraint(ef_solver_t *solver, uint32_t i) {
619 context_t *sampling_ctx;
620 ef_cnstr_t *cnstr;
621 term_t *value;
622 uint32_t nvars, samples;
623 int32_t ucode, ecode;
624 smt_status_t status;
625 term_t cnd;
626
627 assert(i < ef_prob_num_constraints(solver->prob) && solver->max_samples > 0);
628
629 cnstr = solver->prob->cnstr + i;
630
631 /*
632 * make uvalue_aux large enough
633 * its size = number of universal variables in constraint i
634 */
635 nvars = ef_constraint_num_uvars(cnstr);
636 resize_ivector(&solver->uvalue_aux, nvars);
637 solver->uvalue_aux.size = nvars;
638 value = solver->uvalue_aux.data;
639
640 samples = solver->max_samples;
641
642 /*
643 * assert the assumption in the forall context
644 */
645 sampling_ctx = get_forall_context(solver);
646 ucode = assert_formula(sampling_ctx, cnstr->assumption);
647 while (ucode == CTX_NO_ERROR) {
648 trace_printf(solver->trace, 4, "(EF: start: sampling universal variables)\n");
649 status = satisfy_context(sampling_ctx, solver->parameters, cnstr->uvars, nvars, value, NULL);
650 switch (status) {
651 case STATUS_SAT:
652 case STATUS_UNKNOWN:
653 // learned condition on X:
654 cnd = ef_substitution(solver->prob, cnstr->uvars, value, nvars, cnstr->guarantee);
655 if (cnd < 0) {
656 solver->status = EF_STATUS_SUBST_ERROR;
657 solver->error_code = cnd;
658 goto done;
659 }
660 ecode = update_exists_context(solver, cnd);
661 if (ecode < 0) {
662 solver->status = EF_STATUS_ASSERT_ERROR;
663 solver->error_code = ecode;
664 goto done;
665 }
666 if (ecode == TRIVIALLY_UNSAT) {
667 solver->status = EF_STATUS_UNSAT;
668 goto done;
669 }
670 break;
671
672 case STATUS_UNSAT:
673 // no more samples for this constraints
674 goto done;
675
676 case STATUS_INTERRUPTED:
677 solver->status = EF_STATUS_INTERRUPTED;
678 goto done;
679
680 default:
681 solver->status = EF_STATUS_CHECK_ERROR;
682 solver->error_code = status;
683 goto done;
684 }
685
686 samples --;
687 if (samples == 0) goto done;
688
689 ucode = assert_blocking_clause(sampling_ctx);
690 }
691
692 /*
693 * if ucode < 0, something went wrong in assert_formula
694 * or in assert_blocking_clause
695 */
696 if (ucode < 0) {
697 solver->status = EF_STATUS_ASSERT_ERROR;
698 solver->error_code = ucode;
699 }
700
701 done:
702 clear_forall_context(solver, true);
703 }
704
705
706
707
708 /*
709 * FIRST EXISTS MODELS
710 */
711
712 /*
713 * Initialize the exists context
714 * - asserts all conditions from solver->prob
715 * - if max_samples is positive, also apply pre-sampling to all the universal constraints
716 *
717 * - solver->status is set as follows
718 * EF_STATUS_SEARCHING: means not solved yet
719 * EF_STATUS_UNSAT: if the exists context is trivially UNSAT
720 * EF_STATUS_..._ERROR: if something goes wrong
721 * EF_STATUS_INTERRUPTED if the pre-sampling is interrupted
722 */
ef_solver_start(ef_solver_t * solver)723 static void ef_solver_start(ef_solver_t *solver) {
724 int32_t code;
725 uint32_t i, n;
726
727 assert(solver->status == EF_STATUS_IDLE);
728
729 solver->status = EF_STATUS_SEARCHING;
730
731 init_exists_context(solver);
732 code = exists_context_assert_conditions(solver); // add all conditions
733 if (code < 0) {
734 // assertion error
735 solver->status = EF_STATUS_ASSERT_ERROR;
736 solver->error_code = code;
737 } else if (code == TRIVIALLY_UNSAT) {
738 solver->status = EF_STATUS_UNSAT;
739 } else if (solver->max_samples > 0) {
740 /*
741 * run the pre-sampling stuff
742 */
743 n = ef_prob_num_constraints(solver->prob);
744 for (i=0; i<n; i++) {
745 ef_sample_constraint(solver, i);
746 if (solver->status != EF_STATUS_SEARCHING) break;
747 }
748 }
749 }
750
751
752
753 /*
754 * IMPLICANT CONSTRUCTION
755 */
756
757 /*
758 * Merge the exists and forall variables of constraint i
759 * - store the variables in solver->all_vars
760 * - store their values in solver->all_values
761 */
ef_build_full_map(ef_solver_t * solver,uint32_t i)762 static void ef_build_full_map(ef_solver_t *solver, uint32_t i) {
763 ef_cnstr_t *cnstr;
764 ivector_t *v;
765 uint32_t n;
766
767 assert(i < ef_prob_num_constraints(solver->prob));
768 cnstr = solver->prob->cnstr + i;
769
770 // collect all variables of cnstr in solver->all_vars
771 v = &solver->all_vars;
772 ivector_reset(v);
773 n = ef_constraint_num_evars(cnstr);
774 ivector_add(v, cnstr->evars, n);
775 n = ef_constraint_num_uvars(cnstr);
776 ivector_add(v, cnstr->uvars, n);
777
778 // project the evalues onto the exists variable of constraint i
779 // store the results in all_values
780 v = &solver->all_values;
781 n = ef_constraint_num_evars(cnstr);
782 resize_ivector(v, n);
783 v->size = n;
784 ef_project_exists_model(solver->prob, solver->evalue, cnstr->evars, v->data, n);
785
786 // copy the uvalues for constraint i (from uvalue_aux to v)
787 n = ef_constraint_num_uvars(cnstr);
788 assert(n == solver->uvalue_aux.size);
789 ivector_add(v, solver->uvalue_aux.data, n);
790
791 #if EF_VERBOSE
792 printf("Full map\n");
793 print_full_map(stdout, solver);
794 fflush(stdout);
795 #endif
796 }
797
798
799
800
801 /*
802 * GENERALIZATION FROM UNIVERSAL MODELS
803 */
804
805 /*
806 * Option 1: don't generalize:
807 * - build the formula (or (/= var[0] value[0]) ... (/= var[n-1] value[n-1]))
808 */
ef_generalize1(ef_prob_t * prob,term_t * var,term_t * value,uint32_t n)809 static term_t ef_generalize1(ef_prob_t *prob, term_t *var, term_t *value, uint32_t n) {
810 return mk_array_neq(prob->manager, n, var, value);
811 }
812
813
814 /*
815 * Option 2: generalize by substitution
816 * - return (prob->cnstr[i].guarantee with y := value)
817 */
ef_generalize2(ef_prob_t * prob,uint32_t i,term_t * value)818 static term_t ef_generalize2(ef_prob_t *prob, uint32_t i, term_t *value) {
819 ef_cnstr_t *cnstr;
820 uint32_t n;
821 term_t g;
822
823 assert(i < ef_prob_num_constraints(prob));
824 cnstr = prob->cnstr + i;
825 n = ef_constraint_num_uvars(cnstr);
826 g = ef_substitution(prob, cnstr->uvars, value, n, cnstr->guarantee);
827 return g;
828 }
829
830
831 /*
832 * Option 3: generalize by computing an implicant then
833 * applying projection.
834 */
ef_generalize3(ef_solver_t * solver,uint32_t i)835 static term_t ef_generalize3(ef_solver_t *solver, uint32_t i) {
836 model_t *mdl;
837 ef_cnstr_t *cnstr;
838 ivector_t *v, *w;
839 term_t a[2];
840 uint32_t n;
841 int32_t code;
842 proj_flag_t pflag;
843 term_t result;
844
845 assert(i < ef_prob_num_constraints(solver->prob));
846
847 // free the current full model if any
848 if (solver->full_model != NULL) {
849 yices_free_model(solver->full_model);
850 solver->full_model = NULL;
851 }
852
853 // build the full_map and the corresponding model.
854 ef_build_full_map(solver, i);
855 n = solver->all_vars.size;
856 assert(n == solver->all_values.size);
857 mdl = yices_model_from_map(n, solver->all_vars.data, solver->all_values.data);
858 if (mdl == NULL) {
859 // error in the model construction
860 solver->status = EF_STATUS_MDL_ERROR;
861 solver->error_code = yices_error_code();
862 return NULL_TERM;
863 }
864 solver->full_model = mdl;
865
866
867 // Constraint
868 cnstr = solver->prob->cnstr + i;
869 a[0] = cnstr->assumption; // B(y)
870 a[1] = opposite_term(cnstr->guarantee); // not C(x, y)
871
872
873 #if EF_VERBOSE
874 printf("Constraint:\n");
875 yices_pp_term_array(stdout, 2, a, 120, UINT32_MAX, 0, 0);
876 printf("(%"PRIu32" literals)\n", 2);
877 #endif
878
879 // Compute the implicant
880 v = &solver->implicant;
881 ivector_reset(v);
882 code = get_implicant(mdl, solver->prob->manager, LIT_COLLECTOR_ALL_OPTIONS, 2, a, v);
883 if (code < 0) {
884 solver->status = EF_STATUS_IMPLICANT_ERROR;
885 solver->error_code = code;
886 return NULL_TERM;
887 }
888
889 #if EF_VERBOSE
890 printf("Implicant:\n");
891 yices_pp_term_array(stdout, v->size, v->data, 120, UINT32_MAX, 0, 0);
892 printf("(%"PRIu32" literals)\n", v->size);
893 #endif
894
895 // Projection
896 w = &solver->projection;
897 ivector_reset(w);
898 n = ef_constraint_num_uvars(cnstr);
899
900 #if EF_VERBOSE
901 printf("(%"PRIu32" universals)\n", n);
902 yices_pp_term_array(stdout, n, cnstr->uvars, 120, UINT32_MAX, 0, 0);
903 #endif
904
905
906 pflag = project_literals(mdl, solver->prob->manager, v->size, v->data, n, cnstr->uvars, w);
907
908 if (pflag != PROJ_NO_ERROR) {
909 solver->status = EF_STATUS_PROJECTION_ERROR;
910 solver->error_code = pflag;
911 return NULL_TERM;
912 }
913
914 #if EF_VERBOSE
915 printf("Projection:\n");
916 yices_pp_term_array(stdout, w->size, w->data, 120, UINT32_MAX, 0, 0);
917 printf("(%"PRIu32" literals)\n", w->size);
918 #endif
919
920 switch (w->size) {
921 case 0:
922 result = true_term;
923 break;
924
925 case 1:
926 result = w->data[0];
927 break;
928
929 default:
930 result = mk_and(solver->prob->manager, w->size, w->data);
931 break;
932 }
933
934 return opposite_term(result);
935 }
936
937
938 /*
939 * Learn a constraint for the exists variable based on the
940 * existing forall witness for constraint i:
941 * - the witness is defined by the uvars of constraint i
942 * and the values stored in uvalue_aux.
943 *
944 * This function adds a constraint to the exists_context that will remove the
945 * current exists model:
946 * - if solver->option is EF_NOGEN_OPTION, the new constraint is
947 * of the form (or (/= var[0] eval[k0]) ... (/= var[k-1] eval[k-1]))
948 * where var[0 ... k-1] are the exist variables of constraint i
949 * - if solver->option is EF_GEN_BY_SUBST_OPTION, we build a new
950 * constraint by substitution (option 2)
951 *
952 * If something goes wrong, then solver->status is updated to EF_STATUS_ERROR.
953 * If the new learned assertion makes the exist context trivially unsat
954 * then context->status is set to EF_STATUS_UNSAT.
955 * Otherwise context->status is kept as is.
956 */
ef_solver_learn(ef_solver_t * solver,uint32_t i)957 static void ef_solver_learn(ef_solver_t *solver, uint32_t i) {
958 ef_cnstr_t *cnstr;
959 term_t *val;
960 term_t new_constraint;
961 uint32_t n;
962 int32_t code;
963
964 assert(i < ef_prob_num_constraints(solver->prob));
965 cnstr = solver->prob->cnstr + i;
966
967 switch (solver->option) {
968 case EF_NOGEN_OPTION:
969 /*
970 * project solver->evalues on the existential
971 * variables that occur in constraint i.
972 * then build (or (/= evar[0] val[0]) ... (/= evar[n-1] val[n-1]))
973 */
974 n = ef_constraint_num_evars(cnstr);
975 resize_ivector(&solver->evalue_aux, n);
976 solver->evalue_aux.size = n;
977 val = solver->evalue_aux.data;
978 ef_project_exists_model(solver->prob, solver->evalue, cnstr->evars, val, n);
979 new_constraint = ef_generalize1(solver->prob, cnstr->evars, val, n);
980 break;
981
982 case EF_GEN_BY_SUBST_OPTION:
983 val = solver->uvalue_aux.data;
984 new_constraint = ef_generalize2(solver->prob, i, val);
985 if (new_constraint < 0) {
986 // error in substitution
987 solver->status = EF_STATUS_SUBST_ERROR;
988 solver->error_code = new_constraint;
989 return;
990 }
991 break;
992
993 case EF_GEN_BY_PROJ_OPTION:
994 default: // added this to prevent bogus GCC warning
995 new_constraint = ef_generalize3(solver, i);
996 if (new_constraint < 0) {
997 return;
998 }
999 break;
1000 }
1001
1002 // add the new constraint to the exists context
1003 code = update_exists_context(solver, new_constraint);
1004 if (code == TRIVIALLY_UNSAT) {
1005 solver->status = EF_STATUS_UNSAT;
1006 } else if (code < 0) {
1007 solver->status = EF_STATUS_ASSERT_ERROR;
1008 solver->error_code = code;
1009 }
1010 }
1011
1012
1013
1014
1015 /*
1016 * EF SOLVER: INNER LOOP
1017 */
1018
1019 /*
1020 * Trace: result of testing candidate
1021 * - i = constraint id
1022 */
trace_candidate_check(ef_solver_t * solver,uint32_t i,smt_status_t status)1023 static void trace_candidate_check(ef_solver_t *solver, uint32_t i, smt_status_t status) {
1024 switch (status) {
1025 case STATUS_SAT:
1026 case STATUS_UNKNOWN:
1027 trace_printf(solver->trace, 4, "(EF: candidate rejected: failed constraint %"PRIu32")\n", i);
1028 break;
1029
1030 case STATUS_UNSAT:
1031 trace_printf(solver->trace, 4, "(EF: candidate passed constraint %"PRIu32")\n", i);
1032 break;
1033
1034 case STATUS_INTERRUPTED:
1035 trace_printf(solver->trace, 4, "(EF: candidate check was interrupted)\n");
1036 break;
1037
1038 default:
1039 trace_printf(solver->trace, 4, "(EF: error in candidate check for constraint %"PRIu32")\n", i);
1040 break;
1041 }
1042 }
1043
1044 /*
1045 * Check whether the current exists_model can be falsified by one
1046 * of the universal constraints.
1047 * - scan the universal constraints starting from solver->scan_idx
1048 * - the current exists model is defined by
1049 * the mapping from solver->prob->evars to solver->evalues
1050 *
1051 * Update the solver->status as follows:
1052 * - if no constraint falsifies the model, solver->status = EF_STATUS_SAT
1053 * - if some constraint falsifies the model, then solver->status may be
1054 * updated to EF_STATUS_UNSAT (trivially unsat after learning)
1055 * - if something goes wrong, solver->status = EF_STATUS_ERROR
1056 *
1057 * If constraint i falsifies the model then solver->scan_idx is
1058 * set to (i+1) modulo num_constraints.
1059 */
ef_solver_check_exists_model(ef_solver_t * solver)1060 static void ef_solver_check_exists_model(ef_solver_t *solver) {
1061 smt_status_t status;
1062 uint32_t i, n;
1063
1064 n = ef_prob_num_constraints(solver->prob);
1065
1066 /*
1067 * Special case: if there are no universal constraints
1068 * we return SAT immediately.
1069 */
1070 if (n == 0) {
1071 solver->status = EF_STATUS_SAT;
1072 return;
1073 }
1074
1075 i = solver->scan_idx;
1076 do {
1077 trace_printf(solver->trace, 4, "(EF: testing candidate against constraint %"PRIu32")\n", i);
1078 status = ef_solver_test_exists_model(solver, i);
1079 trace_candidate_check(solver, i, status);
1080
1081 switch (status) {
1082 case STATUS_SAT:
1083 case STATUS_UNKNOWN:
1084 #if EF_VERBOSE
1085 printf("Counterexample for constraint[%"PRIu32"]\n", i);
1086 print_forall_witness(stdout, solver, i);
1087 printf("\n");
1088 fflush(stdout);
1089 #endif
1090 ef_solver_learn(solver, i);
1091
1092 default:
1093 i ++;
1094 if (i == n) {
1095 i = 0;
1096 }
1097 break;
1098 }
1099
1100 } while (status == STATUS_UNSAT && i != solver->scan_idx);
1101
1102 solver->scan_idx = i; // prepare for the next call
1103 if (status == STATUS_UNSAT) {
1104 // done a full scan
1105 solver->status = EF_STATUS_SAT;
1106 }
1107 }
1108
1109
1110
1111 /*
1112 * EF SOLVER: OUTER LOOP
1113 */
1114
1115 /*
1116 * Sample exists models
1117 * - stop when we find one that's not refuted by the forall constraints
1118 * - or when we reach the iteration bound
1119 *
1120 * Result:
1121 * - solver->status = EF_STATUS_ERROR if something goes wrong
1122 * - solver->status = EF_STATUS_INTERRUPTED if one of the calls to
1123 * check_context is interrupted
1124 * - solver->status = EF_STATUS_UNSAT if all efmodels have been tried and none
1125 * of them worked
1126 * - solver->status = EF_STATUS_UNKNOWN if the iteration limit is reached
1127 * - solver->status = EF_STATUS_SAT if a good model is found
1128 *
1129 * In the later case,
1130 * - the model is stored in solver->exists_model
1131 * - also it's available as a mapping form solver->prob->evars to solver->evalues
1132 *
1133 * Also solver->iters stores the number of iterations used.
1134 */
ef_solver_search(ef_solver_t * solver)1135 static void ef_solver_search(ef_solver_t *solver) {
1136 smt_status_t stat;
1137 uint32_t i, max;
1138
1139 max = solver->max_iters;
1140 i = 0;
1141
1142 assert(max > 0);
1143
1144 trace_printf(solver->trace, 2,
1145 "(EF search: %"PRIu32" constraints, %"PRIu32" exists vars, %"PRIu32" forall vars)\n",
1146 ef_prob_num_constraints(solver->prob),
1147 ef_prob_num_evars(solver->prob),
1148 ef_prob_num_uvars(solver->prob));
1149 #if EF_VERBOSE
1150 printf("\nConditions on the exists variables:\n");
1151 yices_pp_term_array(stdout, ef_prob_num_conditions(solver->prob), solver->prob->conditions, 120, UINT32_MAX, 0, 0);
1152 #endif
1153
1154 ef_solver_start(solver);
1155 while (solver->status == EF_STATUS_SEARCHING && i < max) {
1156
1157 trace_printf(solver->trace, 3, "(EF Iteration %"PRIu32", scan_idx = %"PRIu32")\n", i, solver->scan_idx);
1158
1159 stat = ef_solver_check_exists(solver);
1160 switch (stat) {
1161 case STATUS_SAT:
1162 case STATUS_UNKNOWN:
1163 // we have a candidate exists model
1164 // check it and learn what we can
1165
1166 #if EF_VERBOSE
1167 // FOR DEBUGGING
1168 printf("Candidate exists model:\n");
1169 print_ef_solution(stdout, solver);
1170 printf("\n");
1171 #endif
1172 trace_puts(solver->trace, 4, "(EF: Found candidate model)\n");
1173 ef_solver_check_exists_model(solver);
1174 break;
1175
1176 case STATUS_UNSAT:
1177 trace_puts(solver->trace, 4, "(EF: No candidate model)\n");
1178 solver->status = EF_STATUS_UNSAT;
1179 break;
1180
1181 case STATUS_INTERRUPTED:
1182 trace_puts(solver->trace, 4, "(EF: Interrupted)\n");
1183 solver->status = EF_STATUS_INTERRUPTED;
1184 break;
1185
1186 default:
1187 solver->status = EF_STATUS_CHECK_ERROR;
1188 solver->error_code = stat;
1189 break;
1190 }
1191
1192 i ++;
1193 }
1194
1195 /*
1196 * Cleanup and set status if i == max
1197 */
1198 if (solver->status != EF_STATUS_SAT && solver->exists_model != NULL) {
1199 yices_free_model(solver->exists_model);
1200 solver->exists_model = NULL;
1201 if (solver->status == EF_STATUS_SEARCHING) {
1202 assert(i == max);
1203 solver->status = EF_STATUS_UNKNOWN;
1204 }
1205 }
1206
1207 solver->iters = i;
1208
1209 trace_puts(solver->trace, 3, "(EF: done)\n\n");
1210 }
1211
1212
1213 /*
1214 * Check satisfiability: the result is stored in solver->status
1215 * - if solver->status is EF_STATUS_SAT then the model is in solver->exists_model
1216 * (as in ef_solver_search).
1217 */
ef_solver_check(ef_solver_t * solver,const param_t * parameters,ef_gen_option_t gen_mode,uint32_t max_samples,uint32_t max_iters)1218 void ef_solver_check(ef_solver_t *solver, const param_t *parameters,
1219 ef_gen_option_t gen_mode, uint32_t max_samples, uint32_t max_iters) {
1220 solver->parameters = parameters;
1221 solver->option = gen_mode;
1222 solver->max_samples = max_samples;
1223 solver->max_iters = max_iters;
1224 solver->scan_idx = 0;
1225
1226 // adjust mode
1227 if (gen_mode == EF_GEN_AUTO_OPTION) {
1228 solver->option = EF_GEN_BY_SUBST_OPTION;
1229 if (ef_prob_has_arithmetic_uvars(solver->prob)) {
1230 solver->option = EF_GEN_BY_PROJ_OPTION;
1231 }
1232 }
1233
1234 assert(solver->exists_context == NULL &&
1235 solver->forall_context == NULL &&
1236 solver->exists_model == NULL);
1237
1238 ef_solver_search(solver);
1239 }
1240
1241
1242