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