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  * Anything that includes "yices.h" requires these macros.
21  * Otherwise the code doesn't build on Windows or Cygwin.
22  */
23 #if defined(CYGWIN) || defined(MINGW)
24 #ifndef __YICES_DLLSPEC__
25 #define __YICES_DLLSPEC__ __declspec(dllexport)
26 #endif
27 #endif
28 
29 #include <poly/polynomial.h>
30 #include <poly/polynomial_context.h>
31 #include <poly/variable_db.h>
32 #include <poly/feasibility_set.h>
33 #include <poly/variable_order.h>
34 #include <poly/variable_list.h>
35 #include <poly/upolynomial.h>
36 
37 #include "mcsat/nra/nra_plugin.h"
38 #include "mcsat/nra/nra_plugin_internal.h"
39 #include "mcsat/nra/libpoly_utils.h"
40 #include "mcsat/tracing.h"
41 #include "mcsat/utils/scope_holder.h"
42 #include "mcsat/utils/int_mset.h"
43 #include "mcsat/watch_list_manager.h"
44 #include "mcsat/nra/poly_constraint.h"
45 #include "mcsat/nra/nra_plugin_explain.h"
46 
47 #include "terms/terms.h"
48 #include "utils/int_array_sort2.h"
49 #include "terms/term_explorer.h"
50 #include "utils/refcount_strings.h"
51 
52 #include "api/yices_api_lock_free.h"
53 
54 static inline
nra_plugin_has_assignment(const nra_plugin_t * nra,variable_t x)55 bool nra_plugin_has_assignment(const nra_plugin_t* nra, variable_t x) {
56   return trail_has_value(nra->ctx->trail, x) && trail_get_index(nra->ctx->trail, x) < nra->trail_i;
57 }
58 
59 static
nra_plugin_stats_init(nra_plugin_t * nra)60 void nra_plugin_stats_init(nra_plugin_t* nra) {
61   // Add statistics
62   nra->stats.propagations = statistics_new_int(nra->ctx->stats, "mcsat::nra::propagations");
63   nra->stats.conflicts = statistics_new_int(nra->ctx->stats, "mcsat::nra::conflicts");
64   nra->stats.conflicts_int = statistics_new_int(nra->ctx->stats, "mcsat::nra::conflicts_int");
65   nra->stats.constraints_attached = statistics_new_int(nra->ctx->stats, "mcsat::nra::constraints_attached");
66   nra->stats.evaluations = statistics_new_int(nra->ctx->stats, "mcsat::nra::evaluations");
67   nra->stats.constraint_regular = statistics_new_int(nra->ctx->stats, "mcsat::nra::constraints_regular");
68   nra->stats.constraint_root = statistics_new_int(nra->ctx->stats, "mcsat::nra::constraints_root");
69 }
70 
71 static
nra_plugin_heuristics_init(nra_plugin_t * nra)72 void nra_plugin_heuristics_init(nra_plugin_t* nra) {
73   // Initialize heuristic
74 }
75 
76 static
nra_plugin_construct(plugin_t * plugin,plugin_context_t * ctx)77 void nra_plugin_construct(plugin_t* plugin, plugin_context_t* ctx) {
78   nra_plugin_t* nra = (nra_plugin_t*) plugin;
79 
80   // OPTIONS NOT AVAILABLE IN CONSTRUCTOR, THEY ARE SETUP LATER
81 
82   nra->ctx = ctx;
83   nra->last_decided_and_unprocessed = variable_null;
84   nra->trail_i = 0;
85   nra->conflict_variable = variable_null;
86 
87   watch_list_manager_construct(&nra->wlm, ctx->var_db);
88   init_int_hmap(&nra->constraint_unit_info, 0);
89   init_int_hmap(&nra->constraint_unit_var, 0);
90 
91   init_ivector(&nra->processed_variables, 0);
92   nra->processed_variables_size = 0;
93 
94   scope_holder_construct(&nra->scope);
95 
96   init_int_hmap(&nra->lp_data.mcsat_to_lp_var_map, 0);
97   init_int_hmap(&nra->lp_data.lp_to_mcsat_var_map, 0);
98 
99   init_int_hmap(&nra->evaluation_value_cache, 0);
100   init_int_hmap(&nra->evaluation_timestamp_cache, 0);
101 
102   init_int_hmap(&nra->feasible_set_cache_top_var[0], 0);
103   init_int_hmap(&nra->feasible_set_cache_top_var[1], 0);
104   init_int_hmap(&nra->feasible_set_cache_timestamp[0], 0);
105   init_int_hmap(&nra->feasible_set_cache_timestamp[1], 0);
106   init_ptr_hmap(&nra->feasible_set_cache[0], 0);
107   init_ptr_hmap(&nra->feasible_set_cache[1], 0);
108 
109   // Constraint db
110   nra->constraint_db = poly_constraint_db_new(nra);
111 
112   // Feasible sets
113   nra->feasible_set_db = feasible_set_db_new(ctx);
114 
115   // lipoly init
116   nra->lp_data.lp_var_db = lp_variable_db_new();
117   nra->lp_data.lp_var_order = lp_variable_order_new();
118   nra->lp_data.lp_var_order_size = 0;
119   nra->lp_data.lp_ctx = lp_polynomial_context_new(lp_Z, nra->lp_data.lp_var_db, nra->lp_data.lp_var_order);
120   nra->lp_data.lp_assignment = lp_assignment_new(nra->lp_data.lp_var_db);
121 
122   // Tracing in libpoly
123   if (false) {
124     lp_trace_enable("coefficient");
125     lp_trace_enable("coefficient::roots");
126     lp_trace_enable("polynomial");
127     lp_trace_enable("polynomial::check_input");
128   }
129 
130   // Trace pscs
131   if (false) {
132     lp_trace_enable("polynomial::expensive");
133   }
134 
135   // Atoms
136   ctx->request_term_notification_by_kind(ctx, ARITH_EQ_ATOM);
137   ctx->request_term_notification_by_kind(ctx, ARITH_GE_ATOM);
138   ctx->request_term_notification_by_kind(ctx, ARITH_BINEQ_ATOM);
139   ctx->request_term_notification_by_kind(ctx, ARITH_ROOT_ATOM);
140   ctx->request_term_notification_by_kind(ctx, ARITH_MOD);
141   ctx->request_term_notification_by_kind(ctx, ARITH_IDIV);
142   ctx->request_term_notification_by_kind(ctx, ARITH_RDIV);
143   ctx->request_term_notification_by_kind(ctx, ARITH_CEIL);
144   ctx->request_term_notification_by_kind(ctx, ARITH_FLOOR);
145 
146   // Terms
147   ctx->request_term_notification_by_kind(ctx, ARITH_CONSTANT);
148   ctx->request_term_notification_by_kind(ctx, ARITH_POLY);
149   ctx->request_term_notification_by_kind(ctx, POWER_PRODUCT);
150 
151   // Types (we add INT because it's there for ITEs over int constants)
152   ctx->request_term_notification_by_type(ctx, REAL_TYPE);
153   ctx->request_term_notification_by_type(ctx, INT_TYPE);
154   ctx->request_decision_calls(ctx, REAL_TYPE);
155   ctx->request_decision_calls(ctx, INT_TYPE);
156 
157   init_rba_buffer(&nra->buffer, ctx->terms->pprods);
158 
159   nra->conflict_variable = variable_null;
160   nra->conflict_variable_int = variable_null;
161 
162   nra->global_bound_term = NULL_TERM;
163 
164   nra_plugin_stats_init(nra);
165   nra_plugin_heuristics_init(nra);
166 }
167 
168 static
nra_plugin_destruct(plugin_t * plugin)169 void nra_plugin_destruct(plugin_t* plugin) {
170   nra_plugin_t* nra = (nra_plugin_t*) plugin;
171 
172   watch_list_manager_destruct(&nra->wlm);
173   delete_int_hmap(&nra->constraint_unit_info);
174   delete_int_hmap(&nra->constraint_unit_var);
175   delete_ivector(&nra->processed_variables);
176   scope_holder_destruct(&nra->scope);
177 
178   delete_int_hmap(&nra->lp_data.mcsat_to_lp_var_map);
179   delete_int_hmap(&nra->lp_data.lp_to_mcsat_var_map);
180 
181   delete_int_hmap(&nra->evaluation_value_cache);
182   delete_int_hmap(&nra->evaluation_timestamp_cache);
183 
184   delete_int_hmap(&nra->feasible_set_cache_top_var[0]);
185   delete_int_hmap(&nra->feasible_set_cache_top_var[1]);
186   delete_int_hmap(&nra->feasible_set_cache_timestamp[0]);
187   delete_int_hmap(&nra->feasible_set_cache_timestamp[1]);
188 
189   // delete feasibility objects
190   ptr_hmap_pair_t* it = ptr_hmap_first_record(&nra->feasible_set_cache[0]);
191   for (; it != NULL; it = ptr_hmap_next_record(&nra->feasible_set_cache[0], it)) {
192     lp_feasibility_set_delete(it->val);
193   }
194   delete_ptr_hmap(&nra->feasible_set_cache[0]);
195   it = ptr_hmap_first_record(&nra->feasible_set_cache[1]);
196   for (; it != NULL; it = ptr_hmap_next_record(&nra->feasible_set_cache[1], it)) {
197     lp_feasibility_set_delete(it->val);
198   }
199   delete_ptr_hmap(&nra->feasible_set_cache[1]);
200 
201   poly_constraint_db_delete(nra->constraint_db);
202 
203   feasible_set_db_delete(nra->feasible_set_db);
204 
205   lp_polynomial_context_detach(nra->lp_data.lp_ctx);
206   lp_variable_order_detach(nra->lp_data.lp_var_order);
207   lp_variable_db_detach(nra->lp_data.lp_var_db);
208   lp_assignment_delete(nra->lp_data.lp_assignment);
209 
210   delete_rba_buffer(&nra->buffer);
211 }
212 
213 static
nra_plugin_trail_variable_compare(void * data,variable_t t1,variable_t t2)214 bool nra_plugin_trail_variable_compare(void *data, variable_t t1, variable_t t2) {
215   const mcsat_trail_t* trail;
216   bool t1_has_value, t2_has_value;
217   uint32_t t1_index, t2_index;
218 
219   trail = data;
220 
221   // We compare variables based on the trail level, unassigned to the front,
222   // then assigned ones by decreasing level
223 
224   // Literals with no value
225   t1_has_value = trail_has_value(trail, t1);
226   t2_has_value = trail_has_value(trail, t2);
227   if (!t1_has_value && !t2_has_value) {
228     // Both have no value, just order by variable
229     return t1 < t2;
230   }
231 
232   // At least one has a value
233   if (!t1_has_value) {
234     // t1 < t2, goes to front
235     return true;
236   }
237   if (!t2_has_value) {
238     // t2 < t1, goes to front
239     return false;
240   }
241 
242   // Both literals have a value, sort by decreasing level
243   t1_index = trail_get_index(trail, t1);
244   t2_index = trail_get_index(trail, t2);
245   if (t1_index != t2_index) {
246     // t1 > t2 goes to front
247     return t1_index > t2_index;
248   } else {
249     return t1 < t2;
250   }
251 }
252 
253 static
nra_plugin_get_feasible_set(nra_plugin_t * nra,variable_t cstr_var,variable_t cstr_top_var,bool is_negated)254 lp_feasibility_set_t* nra_plugin_get_feasible_set(nra_plugin_t* nra, variable_t cstr_var, variable_t cstr_top_var, bool is_negated) {
255   // TODO:
256   // 1. cache
257   // 2. negation
258   // 3. only compute if current value doesn't satisfy
259 
260   // Check if it is a valid constraints
261   const poly_constraint_t* cstr = poly_constraint_db_get(nra->constraint_db, cstr_var);
262 
263   // Constraint var list (we only cache if in use)
264   if (!watch_list_manager_has_constraint(&nra->wlm, cstr_var)) {
265     return poly_constraint_get_feasible_set(cstr, nra->lp_data.lp_assignment, is_negated);
266   }
267   variable_list_ref_t var_list_ref = watch_list_manager_get_list_of(&nra->wlm, cstr_var);
268   const variable_t* var_list = watch_list_manager_get_list(&nra->wlm, var_list_ref);
269 
270   // Get the timestamp and level
271   uint32_t cstr_timestamp = 0;
272   const mcsat_trail_t* trail = nra->ctx->trail;
273   const variable_t* var_i = var_list;
274   while (*var_i != variable_null) {
275     if (nra_plugin_has_assignment(nra, *var_i)) {
276       assert(*var_i != cstr_top_var);
277       uint32_t timestamp_i = trail_get_value_timestamp(trail, *var_i);
278       assert(timestamp_i > 0);
279       if (cstr_timestamp < timestamp_i) {
280         cstr_timestamp = timestamp_i;
281       }
282     } else {
283       assert(*var_i == cstr_top_var);
284     }
285     var_i ++;
286   }
287 
288   // Cached top variable
289   int_hmap_t* cache_top_var = &nra->feasible_set_cache_top_var[is_negated];\
290   int_hmap_pair_t* find_top_var = int_hmap_get(cache_top_var, cstr_var);
291   // Cached timestamp
292   int_hmap_t* cache_timestamp = &nra->feasible_set_cache_timestamp[is_negated];
293   int_hmap_pair_t* find_timestamp = int_hmap_get(cache_timestamp, cstr_var);
294   // Cached feasible set
295   ptr_hmap_t* cache = &nra->feasible_set_cache[is_negated];
296   ptr_hmap_pair_t* find = ptr_hmap_get(cache, cstr_var);
297 
298   // Check if we can use the cached value
299   if (find->val != NULL) {
300     if (find_top_var->val == cstr_top_var && find_timestamp->val == cstr_timestamp) {
301       return lp_feasibility_set_new_copy(find->val);
302     }
303   }
304 
305   // Compute
306   lp_feasibility_set_t* feasible = poly_constraint_get_feasible_set(cstr, nra->lp_data.lp_assignment, is_negated);
307 
308   // Remember cache
309   find_top_var->val = cstr_top_var;
310   find_timestamp->val = cstr_timestamp;
311   if (find->val != NULL) {
312     lp_feasibility_set_delete(find->val);
313   }
314   find->val = feasible;
315 
316   // Done
317   return lp_feasibility_set_new_copy(feasible);
318 }
319 
320 static
nra_plugin_constraint_evaluate(nra_plugin_t * nra,variable_t cstr_var,uint32_t * cstr_level)321 const mcsat_value_t* nra_plugin_constraint_evaluate(nra_plugin_t* nra, variable_t cstr_var, uint32_t* cstr_level) {
322 
323   assert(!trail_has_value(nra->ctx->trail, cstr_var));
324 
325   if (ctx_trace_enabled(nra->ctx, "nra::evaluate")) {
326     trail_print(nra->ctx->trail, ctx_trace_out(nra->ctx));
327   }
328 
329   // Check if it is a valid constraints
330   const poly_constraint_t* cstr = poly_constraint_db_get(nra->constraint_db, cstr_var);
331   if (!poly_constraint_is_valid(cstr)) {
332     return NULL;
333   }
334 
335   // Constraint var list
336   variable_list_ref_t var_list_ref = watch_list_manager_get_list_of(&nra->wlm, cstr_var);
337   const variable_t* var_list = watch_list_manager_get_list(&nra->wlm, var_list_ref);
338 
339   // Get the timestamp and level
340   uint32_t cstr_timestamp = 0;
341   *cstr_level = nra->ctx->trail->decision_level_base;
342   const mcsat_trail_t* trail = nra->ctx->trail;
343   const variable_t* var_i = var_list;
344   while (*var_i != variable_null) {
345     if (nra_plugin_has_assignment(nra, *var_i)) {
346       uint32_t timestamp_i = trail_get_value_timestamp(trail, *var_i);
347       assert(timestamp_i > 0);
348       if (cstr_timestamp < timestamp_i) {
349         cstr_timestamp = timestamp_i;
350       }
351       uint32_t level_i = trail_get_level(trail, *var_i);
352       if (level_i > *cstr_level) {
353         *cstr_level = level_i;
354       }
355     } else {
356       // Doesn't evaluate
357       return NULL;
358     }
359     var_i ++;
360   }
361 
362   bool cstr_value = false;
363 
364   // Check the cache
365   int_hmap_pair_t* find_value = int_hmap_find(&nra->evaluation_value_cache, cstr_var);
366   int_hmap_pair_t* find_timestamp = NULL;
367   if (find_value != NULL) {
368     find_timestamp = int_hmap_find(&nra->evaluation_timestamp_cache, cstr_var);
369     assert(find_timestamp != NULL);
370     if (find_timestamp->val == cstr_timestamp) {
371       // Can use the cached value;
372       cstr_value = find_value->val;
373       return cstr_value ? &mcsat_value_true : &mcsat_value_false;
374     }
375   }
376 
377   // NOTE: with/without caching can change search. Some poly constraints
378   // do not evaluate (see ok below, but we can evaluate them in the cache)
379 
380   // Compute the evaluation
381   bool ok = poly_constraint_evaluate(cstr, nra, &cstr_value);
382   (void) ok;
383   assert(ok);
384   (*nra->stats.evaluations) ++;
385 
386   // Set the cache
387   if (find_value != NULL) {
388     find_value->val = cstr_value;
389     find_timestamp->val = cstr_timestamp;
390   } else {
391     int_hmap_add(&nra->evaluation_value_cache, cstr_var, cstr_value);
392     int_hmap_add(&nra->evaluation_timestamp_cache, cstr_var, cstr_timestamp);
393   }
394 
395   return cstr_value ? &mcsat_value_true : &mcsat_value_false;
396 }
397 
398 static
nra_plugin_process_fully_assigned_constraint(nra_plugin_t * nra,trail_token_t * prop,variable_t cstr_var)399 void nra_plugin_process_fully_assigned_constraint(nra_plugin_t* nra, trail_token_t* prop, variable_t cstr_var) {
400 
401   uint32_t cstr_level = 0;
402   const mcsat_value_t* cstr_value = NULL;
403 
404   assert(!trail_has_value(nra->ctx->trail, cstr_var));
405 
406   if (ctx_trace_enabled(nra->ctx, "nra::evaluate")) {
407     trail_print(nra->ctx->trail, ctx_trace_out(nra->ctx));
408   }
409 
410   // Compute the evaluation timestamp
411   cstr_value = nra_plugin_constraint_evaluate(nra, cstr_var, &cstr_level);
412 
413   // Propagate
414   if (cstr_value) {
415     bool ok = prop->add_at_level(prop, cstr_var, cstr_value, cstr_level);
416     (void)ok;
417     assert(ok);
418   }
419 
420   if (ctx_trace_enabled(nra->ctx, "nra::evaluate")) {
421     trail_print(nra->ctx->trail, ctx_trace_out(nra->ctx));
422   }
423 }
424 
425 static
nra_plugin_new_term_notify(plugin_t * plugin,term_t t,trail_token_t * prop)426 void nra_plugin_new_term_notify(plugin_t* plugin, term_t t, trail_token_t* prop) {
427 
428   uint32_t i;
429   nra_plugin_t* nra = (nra_plugin_t*) plugin;
430   term_table_t* terms = nra->ctx->terms;
431 
432   if (ctx_trace_enabled(nra->ctx, "mcsat::new_term")) {
433     ctx_trace_printf(nra->ctx, "nra_plugin_new_term_notify: ");
434     ctx_trace_term(nra->ctx, t);
435   }
436 
437   assert(!is_neg_term(t));
438 
439   term_kind_t t_kind = term_kind(terms, t);
440 
441   // Only process power terms if they are real ones
442   if (t_kind == POWER_PRODUCT && !is_arithmetic_term(terms, t)) {
443     return;
444   }
445 
446   // The variable
447   variable_t t_var = variable_db_get_variable(nra->ctx->var_db, t);
448 
449   // Check for div and mod
450   if (t_kind == ARITH_MOD) {
451     // Just make sure that the div is registered
452     composite_term_t* mod = arith_mod_term_desc(terms, t);
453     term_t a = mod->arg[0];
454     term_t b = mod->arg[1];
455     term_t t_div = arith_idiv(terms, a, b);
456     variable_db_get_variable(nra->ctx->var_db, t_div);
457     return;
458   }
459   if (t_kind == ARITH_IDIV) {
460     // Make sure that mod has a variable
461     composite_term_t* div = arith_idiv_term_desc(terms, t);
462     term_t q = t;
463     term_t m = div->arg[0];
464     term_t n = div->arg[1];
465     term_t r = arith_mod(terms, m, n);
466     variable_db_get_variable(nra->ctx->var_db, r);
467 
468     // Add a lemma (assuming non-zero):
469     // (div m n)
470     // (and (= m (+ (* n q) r)) (<= 0 r (- (abs n) 1))))))
471     term_t guard = opposite_term(_o_yices_arith_eq0_atom(n));
472     term_t c1 = _o_yices_eq(m, _o_yices_add(_o_yices_mul(n, q), r));
473     term_t c2 = arith_geq_atom(terms, r);
474     // r < (abs n) same as not (r - abs) >= 0
475     term_t abs_n = _o_yices_ite(_o_yices_arith_geq0_atom(n), n, _o_yices_neg(n));
476     term_t c3 = opposite_term(arith_geq_atom(terms, _o_yices_sub(r, abs_n)));
477 
478     prop->lemma(prop, _o_yices_implies(guard, c1));
479     prop->lemma(prop, _o_yices_implies(guard, c2));
480     prop->lemma(prop, _o_yices_implies(guard, c3));
481     return;
482   }
483   if (t_kind == ARITH_RDIV) {
484     composite_term_t* div = arith_rdiv_term_desc(terms, t);
485     term_t q = t;
486     term_t m = div->arg[0];
487     term_t n = div->arg[1];
488 
489     // Add a lemma (assuming non-zero):
490     // (n != 0) => m = n*q
491     // (and (= m (+ (* n q) r)) (<= 0 r (- (abs n) 1))))))
492     term_t guard = opposite_term(_o_yices_arith_eq0_atom(n));
493     term_t c = _o_yices_eq(m, _o_yices_mul(n, q));
494     prop->lemma(prop, _o_yices_implies(guard, c));
495     return;
496   }
497 
498   // Check for floor, ceil
499   if (t_kind == ARITH_FLOOR) {
500     term_t arg = arith_floor_arg(terms, t);
501 
502     // t <= arg < t+1: t is int so it should be fine
503     term_t ineq1 = _o_yices_arith_geq_atom(arg, t);
504     term_t t_1 = _o_yices_add(t, _o_yices_rational32(1, 1));
505     term_t ineq2 = _o_yices_arith_gt_atom(t_1, arg);
506 
507     prop->lemma(prop, ineq1);
508     prop->lemma(prop, ineq2);
509     return;
510   }
511 
512   // Check for floor, ceil
513   if (t_kind == ARITH_CEIL) {
514     term_t arg = arith_ceil_arg(terms, t);
515 
516     // t-1 < arg <= t: t is int so it should be fine
517     term_t t_1 = _o_yices_sub(t, _o_yices_rational32(1, 1));
518     term_t ineq1 = _o_yices_arith_gt_atom(arg, t_1);
519     term_t ineq2 = _o_yices_arith_geq_atom(t, arg);
520 
521     prop->lemma(prop, ineq1);
522     prop->lemma(prop, ineq2);
523     return;
524   }
525 
526   // The vector to collect variables
527   int_mset_t t_variables;
528   int_mset_construct(&t_variables, variable_null);
529   nra_plugin_get_constraint_variables(nra, t, &t_variables);
530 
531   // Examples:
532   // - x: t_variables = [x]
533   // - 5: t_variables = [5]
534   // - x+1: t_variables = [x+1, x]
535   // - (x = 0): t_variables = [x]
536   // We're a constraint if
537   bool is_constraint = t_variables.element_list.size != 1 || t_variables.element_list.data[0] != t_var;
538 
539   // Setup the constraint
540   if (is_constraint) {
541 
542     // Get the list of variables
543     ivector_t* t_variables_list = int_mset_get_list(&t_variables);
544 
545     assert(t_variables_list->size > 0);
546 
547     // Register all the variables to libpoly (these are mcsat_variables)
548     for (i = 0; i < t_variables_list->size; ++ i) {
549       if (!nra_plugin_variable_has_lp_variable(nra, t_variables_list->data[i])) {
550         nra_plugin_add_lp_variable(nra, t_variables_list->data[i]);
551       }
552     }
553 
554     // Bump variables
555     for (i = 0; i < t_variables_list->size; ++ i) {
556       variable_t x = t_variables_list->data[i];
557       uint32_t deg = int_mset_contains(&t_variables, x);
558       nra->ctx->bump_variable_n(nra->ctx, x, deg);
559     }
560 
561     // Sort variables by trail index
562     int_array_sort2(t_variables_list->data, t_variables_list->size, (void*) nra->ctx->trail, nra_plugin_trail_variable_compare);
563 
564     if (ctx_trace_enabled(nra->ctx, "mcsat::new_term")) {
565       ctx_trace_printf(nra->ctx, "nra_plugin_new_term_notify: vars: \n");
566       for (i = 0; i < t_variables_list->size; ++ i) {
567         ctx_trace_term(nra->ctx, variable_db_get_term(nra->ctx->var_db, t_variables_list->data[i]));
568       }
569     }
570 
571     // Make the variable list
572     variable_list_ref_t var_list = watch_list_manager_new_list(&nra->wlm, t_variables_list->data, t_variables_list->size, t_var);
573 
574     // Add first variable to watch list
575     watch_list_manager_add_to_watch(&nra->wlm, var_list, t_variables_list->data[0]);
576 
577     // Add second variable to watch list
578     if (t_variables_list->size > 1) {
579       watch_list_manager_add_to_watch(&nra->wlm, var_list, t_variables_list->data[1]);
580     }
581 
582     // Check the current status of the constraint
583     variable_t top_var = t_variables_list->data[0];
584     constraint_unit_info_t unit_status = CONSTRAINT_UNKNOWN;
585     if (nra_plugin_has_assignment(nra, top_var)) {
586       // All variables assigned,
587       unit_status = CONSTRAINT_FULLY_ASSIGNED;
588     } else {
589       if (t_variables_list->size == 1) {
590         // Single variable, unassigned => unit
591         unit_status = CONSTRAINT_UNIT;
592       } else if (nra_plugin_has_assignment(nra, t_variables_list->data[1])) {
593         // Second one is assigned and processed, so we're unit
594         unit_status = CONSTRAINT_UNIT;
595       }
596     }
597 
598     // Set the status of the constraint
599     nra_plugin_set_unit_info(nra, t_var, unit_status == CONSTRAINT_UNIT ? top_var : variable_null, unit_status);
600 
601     // Add the constraint to the database
602     poly_constraint_db_add(nra->constraint_db, t_var);
603 
604     // Propagate if fully assigned
605     if (unit_status == CONSTRAINT_FULLY_ASSIGNED) {
606       nra_plugin_process_fully_assigned_constraint(nra, prop, t_var);
607     }
608 
609     // Stats
610     (*nra->stats.constraints_attached) ++;
611   } else {
612     // Add the variable to libpoly if not a constant
613     if (t_kind != ARITH_CONSTANT) {
614       if (!nra_plugin_term_has_lp_variable(nra, t)) {
615         nra_plugin_add_lp_variable_from_term(nra, t);
616       }
617 
618       if (nra->ctx->options->nra_bound) {
619 
620         if (nra->global_bound_term == NULL_TERM) {
621           term_table_t* terms = nra->ctx->terms;
622           type_t reals = int_type(terms->types);
623           nra->global_bound_term = new_uninterpreted_term(terms, reals);
624           set_term_name(terms, nra->global_bound_term, clone_string("__mcsat_B"));
625         }
626 
627         // Add bound lemma b - t >= 0 && t + b >= 0
628         term_t ub_term = _o_yices_sub(nra->global_bound_term, t);
629         term_t lb_term = _o_yices_add(nra->global_bound_term, t);
630         term_t ub = _o_yices_arith_geq0_atom(ub_term);
631         term_t lb = _o_yices_arith_geq0_atom(lb_term);
632         prop->lemma(prop, ub);
633         prop->lemma(prop, lb);
634 
635         // If we are at bound variable, set it as main decision
636         if (t == nra->global_bound_term) {
637           nra->ctx->request_top_decision(nra->ctx, t_var);
638           if (nra->ctx->options->nra_bound_min >= 0) {
639             rational_t q;
640             q_init(&q);
641             q_set32(&q, nra->ctx->options->nra_bound_min);
642             term_t min = mk_arith_constant(nra->ctx->tm, &q);
643             term_t min_bound = _o_yices_arith_geq_atom(nra->global_bound_term, min);
644             prop->lemma(prop, min_bound);
645             q_clear(&q);
646           }
647           if (nra->ctx->options->nra_bound_max >= 0) {
648             rational_t q;
649             q_init(&q);
650             q_set32(&q, nra->ctx->options->nra_bound_max);
651             term_t max = mk_arith_constant(nra->ctx->tm, &q);
652             term_t max_bound = _o_yices_arith_leq_atom(nra->global_bound_term, max);
653             prop->lemma(prop, max_bound);
654             q_clear(&q);
655           }
656         }
657       }
658 
659     } else {
660       // Propagate constant value
661       lp_rational_t rat_value;
662       lp_rational_construct(&rat_value);
663       q_get_mpq(rational_term_desc(terms, t), &rat_value);
664       lp_value_t lp_value;
665       lp_value_construct(&lp_value, LP_VALUE_RATIONAL, &rat_value);
666       mcsat_value_t mcsat_value;
667       mcsat_value_construct_lp_value(&mcsat_value, &lp_value);
668       prop->add_at_level(prop, t_var, &mcsat_value, nra->ctx->trail->decision_level_base);
669       mcsat_value_destruct(&mcsat_value);
670       lp_value_destruct(&lp_value);
671       lp_rational_destruct(&rat_value);
672     }
673   }
674 
675   // Remove the variables vector
676   int_mset_destruct(&t_variables);
677 }
678 
679 static
nra_plugin_process_unit_constraint(nra_plugin_t * nra,trail_token_t * prop,variable_t constraint_var)680 void nra_plugin_process_unit_constraint(nra_plugin_t* nra, trail_token_t* prop, variable_t constraint_var) {
681 
682   bool feasible;
683 
684   if (ctx_trace_enabled(nra->ctx, "nra::propagate")) {
685     ctx_trace_printf(nra->ctx, "nra: processing unit constraint :\n");
686     ctx_trace_term(nra->ctx, variable_db_get_term(nra->ctx->var_db, constraint_var));
687   }
688 
689   // Process if constraint is assigned, or an evaluation constraint
690   bool is_eval_constraint = !variable_db_is_boolean(nra->ctx->var_db, constraint_var);
691   if (is_eval_constraint || trail_has_value(nra->ctx->trail, constraint_var)) {
692     // Get the constraint value
693     bool constraint_value = is_eval_constraint || trail_get_value(nra->ctx->trail, constraint_var)->b;
694 
695     // Get the constraint
696     const poly_constraint_t* constraint = poly_constraint_db_get(nra->constraint_db, constraint_var);
697     if (!poly_constraint_is_valid(constraint)) {
698       return;
699     }
700 
701     // Variable of the constraint
702     int_hmap_pair_t* x_find = int_hmap_find(&nra->constraint_unit_var, constraint_var);
703     variable_t x = x_find->val;
704 
705     lp_feasibility_set_t* constraint_feasible = nra_plugin_get_feasible_set(nra, constraint_var, x, !constraint_value);
706 
707     if (ctx_trace_enabled(nra->ctx, "nra::propagate")) {
708       ctx_trace_printf(nra->ctx, "nra: constraint_feasible = ");
709       lp_feasibility_set_print(constraint_feasible, ctx_trace_out(nra->ctx));
710       ctx_trace_printf(nra->ctx, "\n");
711     }
712 
713     // Update the infeasible intervals
714     feasible = feasible_set_db_update(nra->feasible_set_db, x, constraint_feasible, &constraint_var, 1);
715 
716     if (ctx_trace_enabled(nra->ctx, "nra::propagate")) {
717       ctx_trace_printf(nra->ctx, "nra: new feasible = ");
718       lp_feasibility_set_print(feasible_set_db_get(nra->feasible_set_db, x), ctx_trace_out(nra->ctx));
719       ctx_trace_printf(nra->ctx, "\n");
720     }
721 
722     // If the intervals are empty, we have a conflict
723     if (!feasible) {
724       nra_plugin_report_conflict(nra, prop, x);
725     } else {
726       // If the variable is integer, check that is has an integer solution
727       if (nra->conflict_variable_int == variable_null && variable_db_is_int(nra->ctx->var_db, x)) {
728         // Check if there is an integer value
729         lp_value_t v;
730         lp_value_construct_none(&v);
731         lp_feasibility_set_pick_value(feasible_set_db_get(nra->feasible_set_db, x), &v);
732         if (!lp_value_is_integer(&v)) {
733           nra->conflict_variable_int = x;
734         }
735         lp_value_destruct(&v);
736       }
737       // If the value is implied at zero level, propagate it
738       if (!trail_has_value(nra->ctx->trail, x) && trail_is_at_base_level(nra->ctx->trail)) {
739         const lp_feasibility_set_t* feasible = feasible_set_db_get(nra->feasible_set_db, x);
740         if (lp_feasibility_set_is_point(feasible)) {
741           lp_value_t x_value;
742           lp_value_construct_none(&x_value);
743           lp_feasibility_set_pick_value(feasible, &x_value);
744           mcsat_value_t value;
745           mcsat_value_construct_lp_value(&value, &x_value);
746           prop->add_at_level(prop, x, &value, nra->ctx->trail->decision_level_base);
747           mcsat_value_destruct(&value);
748           lp_value_destruct(&x_value);
749         }
750       }
751     }
752   }
753 }
754 
755 /**
756  * The variable has been assigned, look for constraints that become unit.
757  */
758 static
nra_plugin_process_variable_assignment(nra_plugin_t * nra,trail_token_t * prop,variable_t var)759 void nra_plugin_process_variable_assignment(nra_plugin_t* nra, trail_token_t* prop, variable_t var) {
760 
761   remove_iterator_t it;
762   variable_list_ref_t var_list_ref;
763   variable_t* var_list;
764   variable_t* var_list_it;
765 
766   // The trail
767   const mcsat_trail_t* trail = nra->ctx->trail;
768 
769   assert(trail_is_consistent(trail));
770 
771   // Mark the variable as processed
772   ivector_push(&nra->processed_variables, var);
773   nra->processed_variables_size ++;
774 
775   // Check if we have processed our last decision
776   if (var == nra->last_decided_and_unprocessed) {
777     nra->last_decided_and_unprocessed = variable_null;
778   }
779 
780   if (ctx_trace_enabled(nra->ctx, "nra::propagate")) {
781     ctx_trace_printf(nra->ctx, "nra: processing var assignment of :\n");
782     ctx_trace_term(nra->ctx, variable_db_get_term(nra->ctx->var_db, var));
783   }
784 
785   // If it's constant, just skip it
786   if (!nra_plugin_variable_has_lp_variable(nra, var)) {
787     return;
788   }
789 
790   // Add to the lp model and context
791   lp_variable_t lp_var = nra_plugin_get_lp_variable(nra, var);
792   lp_assignment_set_value(nra->lp_data.lp_assignment, lp_var, &trail_get_value(trail, var)->lp_value);
793   lp_variable_order_push(nra->lp_data.lp_var_order, lp_var);
794   nra->lp_data.lp_var_order_size ++;
795 
796   if (ctx_trace_enabled(nra->ctx, "nra::propagate")) {
797     ctx_trace_printf(nra->ctx, "nra: var order :");
798     lp_variable_order_print(nra->lp_data.lp_var_order, nra->lp_data.lp_var_db, ctx_trace_out(nra->ctx));
799     ctx_trace_printf(nra->ctx, "\n");
800   }
801 
802   if (ctx_trace_enabled(nra->ctx, "nra::wlm")) {
803     watch_list_manager_print(&nra->wlm, ctx_trace_out(nra->ctx));
804   }
805 
806   // Get the watch-list
807   remove_iterator_construct(&it, &nra->wlm, var);
808 
809   // Go through all constraints where this variable appears
810   while (!remove_iterator_done(&it)) {
811 
812     // Get the variables of the constraint
813     var_list_ref = remove_iterator_get_list_ref(&it);
814     var_list = watch_list_manager_get_list(&nra->wlm, var_list_ref);
815 
816     // Constraint variable
817     variable_t constraint_var = watch_list_manager_get_constraint(&nra->wlm, var_list_ref);
818 
819     if (ctx_trace_enabled(nra->ctx, "nra::propagate")) {
820       ctx_trace_printf(nra->ctx, "nra: processing constraint :");
821       ctx_trace_term(nra->ctx, variable_db_get_term(nra->ctx->var_db, constraint_var));
822 
823       ctx_trace_printf(nra->ctx, "nra: var_list :");
824       var_list_it = var_list;
825       while (*var_list_it != variable_null) {
826         ctx_trace_term(nra->ctx, variable_db_get_term(nra->ctx->var_db, *var_list_it));
827         var_list_it ++;
828       }
829     }
830 
831     // Put the variable to [1] so that [0] is the unit one
832     if (var_list[0] == var && var_list[1] != variable_null) {
833       var_list[0] = var_list[1];
834       var_list[1] = var;
835     }
836 
837     // Find a new watch (start from [2], increase in for loop again!)
838     var_list_it = var_list + 1;
839     if (*var_list_it != variable_null) {
840       for (++ var_list_it; *var_list_it != variable_null; ++ var_list_it) {
841         if (!nra_plugin_has_assignment(nra, *var_list_it)) {
842           // Swap with var_list[1]
843           var_list[1] = *var_list_it;
844           *var_list_it = var;
845           // Add to new watch
846           watch_list_manager_add_to_watch(&nra->wlm, var_list_ref, var_list[1]);
847           // Don't watch this one
848           remove_iterator_next_and_remove(&it);
849           break;
850         }
851       }
852     }
853 
854     if (*var_list_it == variable_null) {
855       if (ctx_trace_enabled(nra->ctx, "nra::propagate")) {
856         ctx_trace_printf(nra->ctx, "no watch found\n");
857       }
858       if (!nra_plugin_has_assignment(nra, *var_list)) {
859         // We're unit
860         nra_plugin_set_unit_info(nra, constraint_var, *var_list, CONSTRAINT_UNIT);
861         // Process the constraint
862         if (trail_is_consistent(trail)) {
863           nra_plugin_process_unit_constraint(nra, prop, constraint_var);
864         }
865       } else {
866         // Fully assigned
867         nra_plugin_set_unit_info(nra, constraint_var, variable_null, CONSTRAINT_FULLY_ASSIGNED);
868         // Evaluate the constraint and propagate (if not assigned already)
869         if (trail_is_consistent(trail) && !trail_has_value(trail, constraint_var)) {
870           nra_plugin_process_fully_assigned_constraint(nra, prop, constraint_var);
871         }
872       }
873       // Keep the watch
874       remove_iterator_next_and_keep(&it);
875     }
876   }
877 
878   // Done, destruct the iterator
879   remove_iterator_destruct(&it);
880 }
881 
882 
883 static
nra_plugin_check_assignment(nra_plugin_t * nra)884 void nra_plugin_check_assignment(nra_plugin_t* nra) {
885 
886   const mcsat_trail_t* trail = nra->ctx->trail;
887 
888   // Go through the trail and check if all assigned are in lp_assignment
889   uint32_t i;
890   for (i = 0; i < trail->elements.size; ++ i) {
891     variable_t x = trail->elements.data[i];
892     if (x == nra->last_decided_and_unprocessed) {
893       continue;
894     }
895     const mcsat_value_t* value = trail_get_value(trail, x);
896     if (value->type == VALUE_LIBPOLY && nra_plugin_has_assignment(nra, x)) {
897       lp_variable_t x_lp = nra_plugin_get_lp_variable(nra, x);
898       const lp_value_t* value_lp = lp_assignment_get_value(nra->lp_data.lp_assignment, x_lp);
899       int cmp = lp_value_cmp(&value->lp_value, value_lp);
900       (void)cmp;
901       assert(cmp == 0);
902     }
903   }
904 
905   // Go through lp_assignment and check if they are assigned in trail
906   const lp_variable_list_t* order = lp_variable_order_get_list(nra->lp_data.lp_var_order);
907   for (i = 0; i < order->list_size; ++ i) {
908     lp_variable_t x_lp = order->list[i];
909     variable_t x = nra_plugin_get_variable_from_lp_variable(nra, x_lp);
910     const mcsat_value_t* value = trail_get_value(trail, x);
911     const lp_value_t* value_lp = lp_assignment_get_value(nra->lp_data.lp_assignment, x_lp);
912     int cmp = lp_value_cmp(&value->lp_value, value_lp);
913     (void)cmp;
914     assert(cmp == 0);
915   }
916 }
917 
918 
919 /**
920  * Propagates the trail with BCP. When done, either the trail is fully
921  * propagated, or the trail is in an inconsistent state.
922  */
923 static
nra_plugin_propagate(plugin_t * plugin,trail_token_t * prop)924 void nra_plugin_propagate(plugin_t* plugin, trail_token_t* prop) {
925   nra_plugin_t* nra = (nra_plugin_t*) plugin;
926 
927   variable_t var;
928 
929   if (ctx_trace_enabled(nra->ctx, "nra::check_assignment")) {
930     nra_plugin_check_assignment(nra);
931   }
932 
933   // Context
934   const mcsat_trail_t* trail = nra->ctx->trail;
935   variable_db_t* var_db = nra->ctx->var_db;
936 
937   if (ctx_trace_enabled(nra->ctx, "nra::propagate")) {
938     ctx_trace_printf(nra->ctx, "trail:\n");
939     trail_print(trail, nra->ctx->tracer->file);
940   }
941 
942   // Propagate
943   while(trail_is_consistent(trail) && nra->trail_i < trail_size(trail)) {
944     // Current trail element
945     var = trail_at(trail, nra->trail_i);
946     nra->trail_i ++;
947     if (variable_db_is_real(var_db, var) || variable_db_is_int(var_db, var)) {
948       // Real variables, detect if the constraint is unit
949       nra_plugin_process_variable_assignment(nra, prop, var);
950     }
951     if (nra_plugin_get_unit_info(nra, var) == CONSTRAINT_UNIT) {
952       // Process any unit constraints
953       nra_plugin_process_unit_constraint(nra, prop, var);
954     }
955   }
956 
957   if (trail_is_consistent(trail) && nra->conflict_variable_int != variable_null) {
958     nra_plugin_report_int_conflict(nra, prop, nra->conflict_variable_int);
959   }
960 
961   if (ctx_trace_enabled(nra->ctx, "nra::check_assignment")) {
962     nra_plugin_check_assignment(nra);
963   }
964 }
965 
966 static
nra_plugin_decide(plugin_t * plugin,variable_t x,trail_token_t * decide_token,bool must)967 void nra_plugin_decide(plugin_t* plugin, variable_t x, trail_token_t* decide_token, bool must) {
968   nra_plugin_t* nra = (nra_plugin_t*) plugin;
969 
970   assert(variable_db_is_real(nra->ctx->var_db, x) || variable_db_is_int(nra->ctx->var_db, x));
971 
972   // Get the feasibility set
973   lp_feasibility_set_t* feasible = feasible_set_db_get(nra->feasible_set_db, x);
974 
975   if (ctx_trace_enabled(nra->ctx, "nra::decide")) {
976     ctx_trace_printf(nra->ctx, "decide on ");
977     variable_db_print_variable(nra->ctx->var_db, x, ctx_trace_out(nra->ctx));
978     ctx_trace_printf(nra->ctx, "[%d] at level %d\n", x, nra->ctx->trail->decision_level);
979     if (feasible) {
980       ctx_trace_printf(nra->ctx, "feasible :");
981       feasible_set_db_print_var(nra->feasible_set_db, x, ctx_trace_out(nra->ctx));
982       ctx_trace_printf(nra->ctx, "\n");
983     } else {
984       ctx_trace_printf(nra->ctx, "feasible : ALL\n");
985     }
986   }
987 
988   // Pick a value from the set
989   lp_value_t x_new_lpvalue;
990   lp_rational_t x_value_default;
991   lp_rational_construct_from_int(&x_value_default, 0, 1);
992   lp_value_construct(&x_new_lpvalue, LP_VALUE_RATIONAL, &x_value_default);
993   lp_rational_destruct(&x_value_default);
994 
995   // See if the cached value fits
996   bool using_cached = false;
997   const mcsat_value_t* x_cached_value = NULL;
998   if (trail_has_cached_value(nra->ctx->trail, x)) {
999     x_cached_value = trail_get_cached_value(nra->ctx->trail, x);
1000     if (feasible == NULL || lp_feasibility_set_contains(feasible, &x_cached_value->lp_value)) {
1001       using_cached = true;
1002     }
1003   }
1004 
1005   // If the set is 0, we can pick any value, including 0
1006   if (!using_cached && feasible != NULL) {
1007     // Otherwise pick from the set
1008     lp_feasibility_set_pick_value(feasible, &x_new_lpvalue);
1009   }
1010 
1011   // Decide if not too complex of a rational number
1012   bool decide = true;
1013   if (!must) {
1014     if (!lp_value_is_rational(&x_new_lpvalue)) {
1015       if (lp_upolynomial_degree(x_new_lpvalue.value.a.f) > 2) {
1016         decide = false;
1017       }
1018     }
1019   }
1020 
1021   if (decide) {
1022 
1023     if (ctx_trace_enabled(nra->ctx, "nra::decide")) {
1024       ctx_trace_printf(nra->ctx, "decided on ");
1025       variable_db_print_variable(nra->ctx->var_db, x, ctx_trace_out(nra->ctx));
1026       ctx_trace_printf(nra->ctx, "[%d]: ", x);
1027       if (using_cached) {
1028         mcsat_value_print(x_cached_value, ctx_trace_out(nra->ctx));
1029       } else {
1030         lp_value_print(&x_new_lpvalue, ctx_trace_out(nra->ctx));
1031       }
1032       ctx_trace_printf(nra->ctx, "\n");
1033     }
1034 
1035     // Make an mcsat value
1036     mcsat_value_t to_decide_value;
1037     if (!using_cached) {
1038       mcsat_value_construct_lp_value(&to_decide_value, &x_new_lpvalue);
1039       if (variable_db_is_int(nra->ctx->var_db, x)) {
1040         assert(lp_value_is_integer(&x_new_lpvalue));
1041       }
1042     }
1043 
1044     // Decide the to_decide_value
1045     const mcsat_value_t* to_decide_value_ptr = using_cached ? x_cached_value : &to_decide_value;
1046     decide_token->add(decide_token, x, to_decide_value_ptr);
1047 
1048     // Remember that we've decided this guy
1049     nra->last_decided_and_unprocessed = x;
1050 
1051     if (!using_cached) {
1052       mcsat_value_destruct(&to_decide_value);
1053     }
1054   }
1055 
1056   lp_value_destruct(&x_new_lpvalue);
1057 }
1058 
1059 /**
1060  * Check conflicts in core (set of variables).
1061  */
1062 static
nra_plugin_check_conflict(nra_plugin_t * nra,ivector_t * core)1063 void nra_plugin_check_conflict(nra_plugin_t* nra, ivector_t* core) {
1064   uint32_t i;
1065 
1066   static uint32_t count = 0;
1067   count ++;
1068 
1069   fprintf(stderr, "CONFLICT %u\n", count);
1070 
1071   char filename[100];
1072   sprintf(filename, "conflict_%u.m", count);
1073 
1074   FILE* out = fopen(filename, "w");
1075   if (out == NULL) {
1076     fprintf(stderr, "Can't open %s.", filename);
1077     return;
1078   }
1079 
1080   // Variables in the conflict
1081   int_mset_t core_vars;
1082   int_mset_construct(&core_vars, variable_null);
1083 
1084   // The trail
1085   const mcsat_trail_t* trail = nra->ctx->trail;
1086 
1087   // Variable database
1088   const variable_db_t* var_db = nra->ctx->var_db;
1089 
1090   // Get the constraint variables
1091   for (i = 0; i < core->size; ++ i) {
1092     variable_t constraint_var = core->data[i];
1093     term_t constraint_term = variable_db_get_term(var_db, constraint_var);
1094     nra_plugin_get_constraint_variables(nra, constraint_term, &core_vars);
1095   }
1096 
1097   // Variables in the assignment
1098   ivector_t* core_vars_list = int_mset_get_list(&core_vars);
1099 
1100   lp_variable_t free_var = lp_variable_null;
1101 
1102   // Variable assignment
1103   for (i = 0; i < core_vars_list->size; ++ i) {
1104     variable_t x = core_vars_list->data[i];
1105     // Don't consider ones we didn't process yet
1106     if (x == nra->last_decided_and_unprocessed) {
1107       continue;
1108     }
1109     lp_variable_t x_lp = nra_plugin_get_lp_variable(nra, x);
1110     // Ignore unassigned too
1111     if (!trail_has_value(trail, x)) {
1112       assert(free_var == lp_variable_null);
1113       free_var = x_lp;
1114       continue;
1115     }
1116     const mcsat_value_t* x_value = trail_get_value(trail, x);
1117     switch (x_value->type) {
1118     case VALUE_NONE:
1119       assert(free_var == lp_variable_null);
1120       free_var = x_lp;
1121       continue;
1122       break;
1123     case VALUE_LIBPOLY: {
1124       fprintf(out, "x%zu = ", x_lp);
1125       const lp_value_t* x_value_lp = &x_value->lp_value;
1126 #ifndef NDEBUG
1127       const lp_value_t* x_value_lp_in_assignment = lp_assignment_get_value(nra->lp_data.lp_assignment, x_lp);
1128       assert(lp_value_cmp(x_value_lp, x_value_lp_in_assignment) == 0);
1129 #endif
1130       if (lp_value_is_rational(x_value_lp)) {
1131         lp_rational_t q;
1132         lp_rational_construct(&q);
1133         lp_value_get_rational(x_value_lp, &q);
1134         lp_rational_print(&q, out);
1135         lp_rational_destruct(&q);
1136       } else {
1137         lp_value_print(x_value_lp, out);
1138       }
1139       fprintf(out, "\n");
1140       break;
1141     }
1142     default:
1143       assert(false);
1144     }
1145   }
1146 
1147   fprintf(out, "check = ");
1148   if (free_var != lp_variable_null) {
1149     fprintf(out, "Resolve[Exists[{x%zu},", free_var);
1150   }
1151 
1152   // Constraints
1153   for (i = 0; i < core->size; ++ i) {
1154     fprintf(out, "\\\n\t");
1155     if (i) {
1156       fprintf(out, "&& ");
1157     }
1158     variable_t constraint_var = core->data[i];
1159     const poly_constraint_t* constraint = poly_constraint_db_get(nra->constraint_db, constraint_var);
1160     bool constraint_value = trail_get_boolean_value(trail, constraint_var);
1161     poly_constraint_print_mathematica(constraint, !constraint_value, out);
1162   }
1163 
1164   if (free_var != lp_variable_null) {
1165     fprintf(out, "]]\n");
1166   } else {
1167     fprintf(out, "\n");
1168   }
1169 
1170   fclose(out);
1171   int_mset_destruct(&core_vars);
1172 }
1173 
1174 static
nra_plugin_get_real_conflict(nra_plugin_t * nra,const int_mset_t * pos,const int_mset_t * neg,variable_t x,ivector_t * conflict)1175 void nra_plugin_get_real_conflict(nra_plugin_t* nra, const int_mset_t* pos, const int_mset_t* neg,
1176     variable_t x, ivector_t* conflict) {
1177   size_t i;
1178 
1179   if (ctx_trace_enabled(nra->ctx, "nra::conflict")) {
1180     ctx_trace_printf(nra->ctx, "nra_plugin_get_conflict(): ");
1181     ctx_trace_term(nra->ctx, variable_db_get_term(nra->ctx->var_db, x));
1182   }
1183 
1184   // The assertions on x that are in conflict (as constraint variables)
1185   ivector_t core, lemma_reasons;
1186   init_ivector(&core, 0);
1187   init_ivector(&lemma_reasons, 0);
1188   feasible_set_db_get_conflict_reasons(nra->feasible_set_db, nra, x, &core, &lemma_reasons);
1189 
1190   if (ctx_trace_enabled(nra->ctx, "nra::conflict")) {
1191     ctx_trace_printf(nra->ctx, "nra_plugin_get_conflict(): core:\n");
1192     for (i = 0; i < core.size; ++ i) {
1193       ctx_trace_printf(nra->ctx, "[%zu] (", i);
1194       if (trail_has_value(nra->ctx->trail, core.data[i])) {
1195         mcsat_value_print(trail_get_value(nra->ctx->trail, core.data[i]), ctx_trace_out(nra->ctx));
1196       }
1197       ctx_trace_printf(nra->ctx, "): ");
1198       ctx_trace_term(nra->ctx, variable_db_get_term(nra->ctx->var_db, core.data[i]));
1199     }
1200   }
1201 
1202   if (ctx_trace_enabled(nra->ctx, "nra::check_conflict")) {
1203     nra_plugin_check_conflict(nra, &core);
1204   }
1205 
1206   // Project
1207   nra_plugin_explain_conflict(nra, pos, neg, &core, &lemma_reasons, conflict);
1208 
1209   if (ctx_trace_enabled(nra->ctx, "nra::conflict")) {
1210     ctx_trace_printf(nra->ctx, "nra_plugin_get_conflict(): conflict:\n");
1211     for (i = 0; i < conflict->size; ++ i) {
1212       ctx_trace_printf(nra->ctx, "[%zu]: ", i);
1213       ctx_trace_term(nra->ctx, conflict->data[i]);
1214     }
1215   }
1216 
1217   // Remove temps
1218   delete_ivector(&core);
1219   delete_ivector(&lemma_reasons);
1220 
1221 }
1222 
1223 /**
1224  * Check consistency of given constraint. If inconsistent it returns the conflict.
1225  */
1226 static
nra_plugin_speculate_constraint(nra_plugin_t * nra,int_mset_t * pos,int_mset_t * neg,variable_t x,term_t constraint,ivector_t * conflict)1227 bool nra_plugin_speculate_constraint(nra_plugin_t* nra, int_mset_t* pos, int_mset_t* neg,
1228     variable_t x, term_t constraint, ivector_t* conflict) {
1229 
1230   term_t constraint_atom = unsigned_term(constraint);
1231   bool negated = constraint != constraint_atom;
1232   variable_t constraint_var = variable_db_get_variable(nra->ctx->var_db, constraint_atom);
1233   poly_constraint_db_add(nra->constraint_db, constraint_var);
1234 
1235   // Check if the constraint is in Boolean conflict
1236   if (trail_has_value(nra->ctx->trail, constraint_var)) {
1237     if (trail_get_boolean_value(nra->ctx->trail, constraint_var) == negated) {
1238       // Constraint value is false, so we just add the Boolean conflict
1239       ivector_push(conflict, constraint);
1240       ivector_push(conflict, opposite_term(constraint));
1241       return false;
1242     }
1243   }
1244 
1245   // Compute the feasible set
1246   lp_feasibility_set_t* constraint_feasible = nra_plugin_get_feasible_set(nra, constraint_var, x, negated);
1247 
1248   // Update the infeasible intervals
1249   bool feasible = feasible_set_db_update(nra->feasible_set_db, x, constraint_feasible, &constraint_var, 1);
1250 
1251   // Add to assumptions
1252   if (negated) {
1253     int_mset_add(neg, constraint_var);
1254   } else {
1255     int_mset_add(pos, constraint_var);
1256   }
1257 
1258   // If not feasible, get the conflict
1259   if (!feasible) {
1260     // Get the real conflict
1261     ivector_t constraint_conflict;
1262     init_ivector(&constraint_conflict, 0);
1263     nra_plugin_get_real_conflict(nra, pos, neg, x, &constraint_conflict);
1264 
1265     // Copy the conflict (except the assumption into the conflict)
1266     uint32_t i;
1267     for (i = 0; i < constraint_conflict.size; ++ i) {
1268       ivector_push(conflict, constraint_conflict.data[i]);
1269     }
1270     delete_ivector(&constraint_conflict);
1271   }
1272 
1273   // Remove from assumptions not feasible
1274   if (!feasible) {
1275     if (negated) {
1276       int_mset_remove_one(neg, constraint_var);
1277     } else {
1278       int_mset_remove_one(pos, constraint_var);
1279     }
1280   }
1281 
1282   return feasible;
1283 }
1284 
1285 static
nra_plugin_get_int_conflict(nra_plugin_t * nra,int_mset_t * pos,int_mset_t * neg,variable_t x,ivector_t * conflict)1286 void nra_plugin_get_int_conflict(nra_plugin_t* nra, int_mset_t* pos, int_mset_t* neg,
1287     variable_t x, ivector_t* conflict) {
1288 
1289   // We have an int conflict on x
1290 
1291   // That means that the feasible set is of the form
1292   //   I1    I2   I3 I4  I5
1293   //   ( )   ()   ( )()  ()
1294   //
1295   // Where each Ik doesn't allow an integer value.
1296   //
1297   // We do the splits manually and collect the explanation
1298 
1299   lp_value_t v;
1300   lp_value_construct_none(&v);
1301 
1302   term_t x_term = variable_db_get_term(nra->ctx->var_db, x);
1303 
1304   // We'll be making changes, remember state
1305   feasible_set_db_push(nra->feasible_set_db);
1306 
1307   // Set of constraints that we resolve using Boolean resolution
1308   int_mset_t to_resolve;
1309   int_mset_construct(&to_resolve, NULL_TERM);
1310 
1311   for (;;) {
1312 
1313     // Get the first value from the left
1314     const lp_feasibility_set_t* x_feasible = feasible_set_db_get(nra->feasible_set_db, x);
1315     if (ctx_trace_enabled(nra->ctx, "nia")) {
1316       ctx_trace_printf(nra->ctx, "x = ");
1317       variable_db_print_variable(nra->ctx->var_db, x, ctx_trace_out(nra->ctx));
1318       ctx_trace_printf(nra->ctx, "\nx_feasible = ");
1319       lp_feasibility_set_print(x_feasible, ctx_trace_out(nra->ctx));
1320       ctx_trace_printf(nra->ctx, "\n");
1321     }
1322     lp_feasibility_set_pick_first_value(x_feasible, &v);
1323     if (ctx_trace_enabled(nra->ctx, "nia")) {
1324       ctx_trace_printf(nra->ctx, "v = ");
1325       lp_value_print(&v, ctx_trace_out(nra->ctx));
1326       ctx_trace_printf(nra->ctx, "\n");
1327     }
1328     assert(!lp_value_is_integer(&v));
1329 
1330     // Get the floor
1331     lp_integer_t v_floor;
1332     lp_integer_construct(&v_floor);
1333     lp_value_floor(&v, &v_floor);
1334 
1335     // Yices versions of the floor
1336     rational_t v_floor_rat;
1337     rational_construct_from_lp_integer(&v_floor_rat, &v_floor);
1338     term_t v_floor_term = mk_arith_constant(nra->ctx->tm, &v_floor_rat);
1339 
1340     // Remove temp
1341     lp_integer_destruct(&v_floor);
1342     q_clear(&v_floor_rat);
1343 
1344     // The constraint
1345     term_t x_leq_floor = mk_arith_leq(nra->ctx->tm, x_term, v_floor_term);
1346     int_mset_add(&to_resolve, x_leq_floor);
1347 
1348     // Get the conflict
1349     feasible_set_db_push(nra->feasible_set_db);
1350     bool feasible = nra_plugin_speculate_constraint(nra, pos, neg, x, x_leq_floor, conflict);
1351     assert(!feasible);
1352     feasible_set_db_pop(nra->feasible_set_db);
1353 
1354     // Get the ceiling
1355     lp_integer_t v_ceil;
1356     lp_integer_construct(&v_ceil);
1357     lp_value_ceiling(&v, &v_ceil);
1358 
1359     // Yices versions of the ceiling
1360     rational_t v_ceil_rat;
1361     rational_construct_from_lp_integer(&v_ceil_rat, &v_ceil);
1362     term_t v_ceil_term = mk_arith_constant(nra->ctx->tm, &v_ceil_rat);
1363 
1364     // Remove temp
1365     lp_integer_destruct(&v_ceil);
1366     q_clear(&v_ceil_rat);
1367 
1368     // The constraint
1369     term_t x_geq_ceil = mk_arith_geq(nra->ctx->tm, x_term, v_ceil_term);
1370     int_mset_add(&to_resolve, x_geq_ceil);
1371 
1372     // Try it out
1373     feasible = nra_plugin_speculate_constraint(nra, pos, neg, x, x_geq_ceil, conflict);
1374 
1375     if (ctx_trace_enabled(nra->ctx, "nia")) {
1376       ctx_trace_printf(nra->ctx, "int_conflict: current conflict:\n");
1377       uint32_t i;
1378       for (i = 0; i < conflict->size; ++ i) {
1379         ctx_trace_printf(nra->ctx, "  ");
1380         ctx_trace_term(nra->ctx, conflict->data[i]);
1381       }
1382     }
1383 
1384     // If not feasible, we're done
1385     if (!feasible) {
1386       break;
1387     }
1388   }
1389 
1390   // Undo feasibility changes
1391   feasible_set_db_pop(nra->feasible_set_db);
1392 
1393   // Remove resolved literals
1394   uint32_t i = 0, to_keep;
1395   for (i = 0, to_keep = 0; i < conflict->size; ++ i) {
1396     if (!int_mset_contains(&to_resolve, conflict->data[i])) {
1397       conflict->data[to_keep ++] = conflict->data[i];
1398     }
1399   }
1400   ivector_shrink(conflict, to_keep);
1401 
1402   if (ctx_trace_enabled(nra->ctx, "nia")) {
1403     ctx_trace_printf(nra->ctx, "int_conflict: final conflict:\n");
1404     uint32_t i;
1405     for (i = 0; i < conflict->size; ++ i) {
1406       ctx_trace_printf(nra->ctx, "  ");
1407       ctx_trace_term(nra->ctx, conflict->data[i]);
1408     }
1409   }
1410 
1411   // Remove tempss
1412   int_mset_destruct(&to_resolve);
1413   lp_value_destruct(&v);
1414 }
1415 
1416 static
nra_plugin_get_conflict(plugin_t * plugin,ivector_t * conflict)1417 void nra_plugin_get_conflict(plugin_t* plugin, ivector_t* conflict) {
1418   nra_plugin_t* nra = (nra_plugin_t*) plugin;
1419 
1420   if (ctx_trace_enabled(nra->ctx, "nra::conflict")) {
1421     ctx_trace_printf(nra->ctx, "nra_plugin_get_conflict(): START\n");
1422   }
1423 
1424   int_mset_t pos, neg;
1425   int_mset_construct(&pos, variable_null);
1426   int_mset_construct(&neg, variable_null);
1427 
1428   if (nra->conflict_variable != variable_null) {
1429     nra_plugin_get_real_conflict(nra, &pos, &neg, nra->conflict_variable, conflict);
1430   } else if (nra->conflict_variable_int != variable_null) {
1431     nra_plugin_get_int_conflict(nra, &pos, &neg, nra->conflict_variable_int, conflict);
1432   } else {
1433     assert(false);
1434   }
1435 
1436   int_mset_destruct(&pos);
1437   int_mset_destruct(&neg);
1438 
1439   if (ctx_trace_enabled(nra->ctx, "nra::conflict")) {
1440     uint32_t i;
1441     ctx_trace_printf(nra->ctx, "nra_plugin_get_conflict(): END\n");
1442     for (i = 0; i < conflict->size; ++ i) {
1443       ctx_trace_term(nra->ctx, conflict->data[i]);
1444     }
1445   }
1446 }
1447 
1448 static
nra_plugin_explain_propagation(plugin_t * plugin,variable_t var,ivector_t * reasons)1449 term_t nra_plugin_explain_propagation(plugin_t* plugin, variable_t var, ivector_t* reasons) {
1450   nra_plugin_t* nra = (nra_plugin_t*) plugin;
1451 
1452   // We only propagate evaluations, and we explain them using the literal itself
1453   term_t atom = variable_db_get_term(nra->ctx->var_db, var);
1454   if (ctx_trace_enabled(nra->ctx, "nra::conflict")) {
1455     ctx_trace_printf(nra->ctx, "nra_plugin_explain_propagation():\n");
1456     ctx_trace_term(nra->ctx, atom);
1457   }
1458   bool value = trail_get_boolean_value(nra->ctx->trail, var);
1459   if (ctx_trace_enabled(nra->ctx, "nra::conflict")) {
1460     ctx_trace_printf(nra->ctx, "assigned to %s\n", value ? "true" : "false");
1461   }
1462 
1463   if (value) {
1464     // atom => atom = true
1465     ivector_push(reasons, atom);
1466     return bool2term(true);
1467   } else {
1468     // neg atom => atom = false
1469     ivector_push(reasons, opposite_term(atom));
1470     return bool2term(false);
1471   }
1472 }
1473 
1474 static
nra_plugin_explain_evaluation(plugin_t * plugin,term_t t,int_mset_t * vars,mcsat_value_t * value)1475 bool nra_plugin_explain_evaluation(plugin_t* plugin, term_t t, int_mset_t* vars, mcsat_value_t* value) {
1476   nra_plugin_t* nra = (nra_plugin_t*) plugin;
1477 
1478   // Get all the variables and make sure they are all assigned.
1479   nra_plugin_get_constraint_variables(nra, t, vars);
1480 
1481   // Check if the variables are assigned
1482   ivector_t* var_list = int_mset_get_list(vars);
1483   size_t i = 0;
1484   for (i = 0; i < var_list->size; ++ i) {
1485     if (!trail_has_value(nra->ctx->trail, var_list->data[i])) {
1486       int_mset_clear(vars);
1487       return false;
1488     }
1489   }
1490 
1491   // All variables assigned
1492   return true;
1493 }
1494 
1495 static
nra_plugin_push(plugin_t * plugin)1496 void nra_plugin_push(plugin_t* plugin) {
1497   nra_plugin_t* nra = (nra_plugin_t*) plugin;
1498   scope_holder_push(&nra->scope,
1499       &nra->trail_i,
1500       &nra->processed_variables_size,
1501       &nra->lp_data.lp_var_order_size,
1502       NULL);
1503 
1504   feasible_set_db_push(nra->feasible_set_db);
1505 }
1506 
1507 static
nra_plugin_pop(plugin_t * plugin)1508 void nra_plugin_pop(plugin_t* plugin) {
1509 
1510   nra_plugin_t* nra = (nra_plugin_t*) plugin;
1511   const mcsat_trail_t* trail = nra->ctx->trail;
1512 
1513   if (ctx_trace_enabled(nra->ctx, "nra::conflict")) {
1514     trail_print(trail, ctx_trace_out(nra->ctx));
1515   }
1516 
1517   // Pop the scoped variables
1518   scope_holder_pop(&nra->scope,
1519       &nra->trail_i,
1520       &nra->processed_variables_size,
1521       &nra->lp_data.lp_var_order_size,
1522       NULL);
1523 
1524   // Undo the processed variables
1525   while (nra->processed_variables.size > nra->processed_variables_size) {
1526     // The variable to undo
1527     variable_t x = ivector_last(&nra->processed_variables);
1528     ivector_pop(&nra->processed_variables);
1529     assert(variable_db_is_real(nra->ctx->var_db, x) || variable_db_is_int(nra->ctx->var_db, x));
1530     // Go through the watch and mark the constraints
1531     remove_iterator_t it;
1532     remove_iterator_construct(&it, &nra->wlm, x);
1533     while (!remove_iterator_done(&it)) {
1534       variable_t constraint_var = remove_iterator_get_constraint(&it);
1535       constraint_unit_info_t unit_info = nra_plugin_get_unit_info(nra, constraint_var);
1536       switch (unit_info) {
1537       case CONSTRAINT_UNKNOWN:
1538         // Nothing to do
1539         break;
1540       case CONSTRAINT_UNIT:
1541         // If it was unit it becomes not unit
1542         nra_plugin_set_unit_info(nra, constraint_var, variable_null, CONSTRAINT_UNKNOWN);
1543         break;
1544       case CONSTRAINT_FULLY_ASSIGNED:
1545         // It is unit now
1546         nra_plugin_set_unit_info(nra, constraint_var, x, CONSTRAINT_UNIT);
1547         break;
1548       }
1549       remove_iterator_next_and_keep(&it);
1550     }
1551     remove_iterator_destruct(&it);
1552   }
1553 
1554   // Pop the variable order and the lp model
1555   lp_variable_order_t* order = nra->lp_data.lp_var_order;
1556   lp_assignment_t* assignment = nra->lp_data.lp_assignment;
1557   while (lp_variable_order_size(order) > nra->lp_data.lp_var_order_size) {
1558     lp_variable_t lp_var = lp_variable_order_top(order);
1559     lp_variable_order_pop(order);
1560     lp_assignment_set_value(assignment, lp_var, 0);
1561     variable_t var = nra_plugin_get_variable_from_lp_variable(nra, lp_var);
1562     (void)var;
1563   }
1564 
1565   if (ctx_trace_enabled(nra->ctx, "nra::check_assignment")) {
1566     nra_plugin_check_assignment(nra);
1567   }
1568 
1569   // Pop the feasibility
1570   feasible_set_db_pop(nra->feasible_set_db);
1571 
1572   // Unset the conflict
1573   nra->conflict_variable = variable_null;
1574   nra->conflict_variable_int = variable_null;
1575 
1576   // We undid last decision, so we're back to normal
1577   nra->last_decided_and_unprocessed = variable_null;
1578 }
1579 
1580 static
nra_plugin_gc_mark(plugin_t * plugin,gc_info_t * gc_vars)1581 void nra_plugin_gc_mark(plugin_t* plugin, gc_info_t* gc_vars) {
1582   nra_plugin_t* nra = (nra_plugin_t*) plugin;
1583   // The NRA plugin doesn't really need to keep much. The only things we'd
1584   // like to keep are the lemmas that restrict top level feasibility sets.
1585   feasible_set_db_gc_mark(nra->feasible_set_db, gc_vars);
1586   // We also need to mark all the real variables that are in use
1587   poly_constraint_db_gc_mark(nra->constraint_db, gc_vars);
1588 }
1589 
1590 static
nra_plugin_gc_sweep(plugin_t * plugin,const gc_info_t * gc_vars)1591 void nra_plugin_gc_sweep(plugin_t* plugin, const gc_info_t* gc_vars) {
1592   nra_plugin_t* nra = (nra_plugin_t*) plugin;
1593 
1594   // The feasibility sets keep everything, we just gc the constraints,
1595   // the watchlists and the unit information.
1596 
1597   // The constraint database
1598   poly_constraint_db_gc_sweep(nra->constraint_db, gc_vars);
1599 
1600   // The lp_data mappings:
1601   // - lpdata.lp_to_mcsat_var_map (values)
1602   // - lpdata.mcsat_to_lp_var_map (keys)
1603   gc_info_sweep_int_hmap_values(gc_vars, &nra->lp_data.lp_to_mcsat_var_map);
1604   gc_info_sweep_int_hmap_keys(gc_vars, &nra->lp_data.mcsat_to_lp_var_map);
1605 
1606   // Evaluation cache
1607   gc_info_sweep_int_hmap_keys(gc_vars, &nra->evaluation_value_cache);
1608   gc_info_sweep_int_hmap_keys(gc_vars, &nra->evaluation_timestamp_cache);
1609 
1610   // Feasible set cache
1611   gc_info_sweep_int_hmap_keys(gc_vars, &nra->feasible_set_cache_top_var[0]);
1612   gc_info_sweep_int_hmap_values(gc_vars, &nra->feasible_set_cache_top_var[0]);
1613   gc_info_sweep_int_hmap_keys(gc_vars, &nra->feasible_set_cache_top_var[1]);
1614   gc_info_sweep_int_hmap_values(gc_vars, &nra->feasible_set_cache_top_var[1]);
1615   gc_info_sweep_int_hmap_keys(gc_vars, &nra->feasible_set_cache_timestamp[0]);
1616   gc_info_sweep_int_hmap_keys(gc_vars, &nra->feasible_set_cache_timestamp[1]);
1617   gc_info_sweep_ptr_hmap_keys(gc_vars, &nra->feasible_set_cache[0], (ptr_hmap_ptr_delete) &lp_feasibility_set_delete);
1618   gc_info_sweep_ptr_hmap_keys(gc_vars, &nra->feasible_set_cache[0], (ptr_hmap_ptr_delete) &lp_feasibility_set_delete);
1619 
1620   // Unit information (constraint_unit_info, constraint_unit_var)
1621   gc_info_sweep_int_hmap_keys(gc_vars, &nra->constraint_unit_info);
1622   gc_info_sweep_int_hmap_keys(gc_vars, &nra->constraint_unit_var);
1623 
1624   // Watch list manager
1625   watch_list_manager_gc_sweep_lists(&nra->wlm, gc_vars);
1626 }
1627 
1628 static
nra_plugin_event_notify(plugin_t * plugin,plugin_notify_kind_t kind)1629 void nra_plugin_event_notify(plugin_t* plugin, plugin_notify_kind_t kind) {
1630   nra_plugin_t* nra = (nra_plugin_t*) plugin;
1631   (void)nra;
1632 
1633   switch (kind) {
1634   case MCSAT_SOLVER_START:
1635     // Re-initialize the heuristics
1636     break;
1637   case MCSAT_SOLVER_RESTART:
1638     // Check if clause compaction needed
1639     break;
1640   case MCSAT_SOLVER_CONFLICT:
1641     // Decay the scores each conflict
1642     break;
1643   case MCSAT_SOLVER_POP:
1644     // Not much to do
1645     break;
1646   default:
1647     assert(false);
1648   }
1649 }
1650 
1651 static
nra_plugin_new_lemma_notify(plugin_t * plugin,ivector_t * lemma,trail_token_t * prop)1652 void nra_plugin_new_lemma_notify(plugin_t* plugin, ivector_t* lemma, trail_token_t* prop) {
1653   nra_plugin_t* nra = (nra_plugin_t*) plugin;
1654 
1655   uint32_t i;
1656 
1657   // Check if unit in single variable
1658   bool unit = true;
1659   variable_t unit_var = variable_null;
1660   for (i = 0; unit && i < lemma->size; ++ i) {
1661 
1662     if (ctx_trace_enabled(nra->ctx, "nra::lemma")) {
1663       ctx_trace_printf(nra->ctx, "lemma[%u] = ", i); ctx_trace_term(nra->ctx, lemma->data[i]); ctx_trace_printf(nra->ctx, "\n");
1664     }
1665 
1666     term_t literal = lemma->data[i];
1667     term_t atom = unsigned_term(literal);
1668     variable_t atom_var = variable_db_get_variable_if_exists(nra->ctx->var_db, atom);
1669     assert(atom_var != variable_null);
1670     if (nra_plugin_get_unit_info(nra, atom_var) != CONSTRAINT_UNIT) {
1671       // Not unit
1672       unit = false;
1673     } else {
1674       // Unit, check if same variable
1675       variable_t atom_unit_var = nra_plugin_get_unit_var(nra, atom_var);
1676       if (unit_var == variable_null) {
1677         unit_var = atom_unit_var;
1678       } else if (unit_var != atom_unit_var) {
1679         unit = false;
1680       }
1681     }
1682   }
1683 
1684   if (unit && nra->ctx->trail->decision_level == 0) {
1685 
1686     // Get the feasible set
1687     lp_feasibility_set_t* lemma_feasible = lp_feasibility_set_new_empty();
1688 
1689     // Add all the literal sets
1690     for (i = 0; i < lemma->size; ++ i) {
1691 
1692       if (ctx_trace_enabled(nra->ctx, "nra::lemma")) {
1693         ctx_trace_printf(nra->ctx, "nra: lemma_feasible = ");
1694         lp_feasibility_set_print(lemma_feasible, ctx_trace_out(nra->ctx));
1695         ctx_trace_printf(nra->ctx, "\n");
1696       }
1697 
1698       term_t literal_term = lemma->data[i];
1699       term_t constraint_term = unsigned_term(literal_term);
1700       variable_t constraint_var = variable_db_get_variable_if_exists(nra->ctx->var_db, constraint_term);
1701       assert(constraint_var != variable_null);
1702 
1703       // Is it negated
1704       bool negated = constraint_term != literal_term;
1705 
1706       // Compute and add the feasible set
1707       lp_feasibility_set_t* constraint_feasible = nra_plugin_get_feasible_set(nra, constraint_var, unit_var, negated);
1708 
1709       if (ctx_trace_enabled(nra->ctx, "nra::lemma")) {
1710         ctx_trace_printf(nra->ctx, "nra: constraint_feasible = ");
1711         lp_feasibility_set_print(constraint_feasible, ctx_trace_out(nra->ctx));
1712         ctx_trace_printf(nra->ctx, "\n");
1713       }
1714 
1715       lp_feasibility_set_add(lemma_feasible, constraint_feasible);
1716       lp_feasibility_set_delete(constraint_feasible);
1717     }
1718 
1719     if (ctx_trace_enabled(nra->ctx, "nra::lemma")) {
1720       ctx_trace_printf(nra->ctx, "nra: lemma_feasible = ");
1721       lp_feasibility_set_print(lemma_feasible, ctx_trace_out(nra->ctx));
1722       ctx_trace_printf(nra->ctx, "\n");
1723     }
1724 
1725     // Since we need to communicate conflicts to bool theory, vacuous constraints
1726     // can happen, i.e. the following is not a valid assertion
1727     // assert(!lp_feasibility_set_is_full(lemma_feasible));
1728     // We can also learn infeasible lemmas, we just have to report a conflict
1729     if (!lp_feasibility_set_is_full(lemma_feasible)) {
1730 
1731       if (ctx_trace_enabled(nra->ctx, "nra::lemma")) {
1732         const lp_feasibility_set_t* current_feasible = feasible_set_db_get(nra->feasible_set_db, unit_var);
1733         if (current_feasible) {
1734           ctx_trace_printf(nra->ctx, "nra: current feasible = ");
1735           lp_feasibility_set_print(current_feasible, ctx_trace_out(nra->ctx));
1736           ctx_trace_printf(nra->ctx, "\n");
1737         }
1738       }
1739 
1740       // Collect the literals
1741       ivector_t lemma_reasons;
1742       init_ivector(&lemma_reasons, 0);
1743       for (i = 0; i < lemma->size; ++ i) {
1744         term_t literal_term = lemma->data[i];
1745         term_t constraint_term = unsigned_term(literal_term);
1746         variable_t constraint_var = variable_db_get_variable_if_exists(nra->ctx->var_db, constraint_term);
1747         assert(constraint_var != variable_null);
1748         ivector_push(&lemma_reasons, constraint_var);
1749       }
1750 
1751       // Update
1752       bool feasible = feasible_set_db_update(nra->feasible_set_db, unit_var, lemma_feasible, lemma_reasons.data, lemma_reasons.size);
1753       if (ctx_trace_enabled(nra->ctx, "nra::lemma")) {
1754         ctx_trace_printf(nra->ctx, "nra: new feasible = ");
1755         const lp_feasibility_set_t* current_feasible = feasible_set_db_get(nra->feasible_set_db, unit_var);
1756         lp_feasibility_set_print(current_feasible, ctx_trace_out(nra->ctx));
1757         ctx_trace_printf(nra->ctx, "\n");
1758       }
1759 
1760       // If infeasible report conflict
1761       if (!feasible) {
1762         nra_plugin_report_conflict(nra, prop, unit_var);
1763       } else if (variable_db_is_int(nra->ctx->var_db, unit_var)) {
1764         // Check if there is an integer value
1765         lp_value_t v;
1766         lp_value_construct_none(&v);
1767         lp_feasibility_set_pick_value(feasible_set_db_get(nra->feasible_set_db, unit_var), &v);
1768         if (!lp_value_is_integer(&v)) {
1769           nra->conflict_variable_int = unit_var;
1770           nra_plugin_report_conflict(nra, prop, unit_var);
1771         }
1772         lp_value_destruct(&v);
1773       }
1774 
1775       delete_ivector(&lemma_reasons);
1776     } else {
1777       lp_feasibility_set_delete(lemma_feasible);
1778     }
1779   }
1780 }
1781 
1782 static
nra_plugin_set_exception_handler(plugin_t * plugin,jmp_buf * handler)1783 void nra_plugin_set_exception_handler(plugin_t* plugin, jmp_buf* handler) {
1784   nra_plugin_t* nra = (nra_plugin_t*) plugin;
1785   nra->exception = handler;
1786 }
1787 
nra_plugin_allocator(void)1788 plugin_t* nra_plugin_allocator(void) {
1789   nra_plugin_t* plugin = safe_malloc(sizeof(nra_plugin_t));
1790   plugin_construct((plugin_t*) plugin);
1791   plugin->plugin_interface.construct           = nra_plugin_construct;
1792   plugin->plugin_interface.destruct            = nra_plugin_destruct;
1793   plugin->plugin_interface.new_term_notify     = nra_plugin_new_term_notify;
1794   plugin->plugin_interface.new_lemma_notify    = nra_plugin_new_lemma_notify;
1795   plugin->plugin_interface.event_notify        = nra_plugin_event_notify;
1796   plugin->plugin_interface.propagate           = nra_plugin_propagate;
1797   plugin->plugin_interface.decide              = nra_plugin_decide;
1798   plugin->plugin_interface.get_conflict        = nra_plugin_get_conflict;
1799   plugin->plugin_interface.explain_propagation = nra_plugin_explain_propagation;
1800   plugin->plugin_interface.explain_evaluation  = nra_plugin_explain_evaluation;
1801   plugin->plugin_interface.push                = nra_plugin_push;
1802   plugin->plugin_interface.pop                 = nra_plugin_pop;
1803   plugin->plugin_interface.gc_mark             = nra_plugin_gc_mark;
1804   plugin->plugin_interface.gc_sweep            = nra_plugin_gc_sweep;
1805   plugin->plugin_interface.set_exception_handler = nra_plugin_set_exception_handler;
1806 
1807   return (plugin_t*) plugin;
1808 }
1809