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