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 #include "nra_plugin_explain.h"
20 #include "nra_plugin_internal.h"
21 #include "poly_constraint.h"
22 #include "libpoly_utils.h"
23 
24 #include "utils/int_hash_map.h"
25 #include "utils/pointer_vectors.h"
26 #include "mcsat/tracing.h"
27 #include "terms/term_manager.h"
28 #include "terms/rba_buffer_terms.h"
29 #include "terms/terms.h"
30 
31 #include <stdlib.h>
32 #include <stdio.h>
33 
34 #include <poly/poly.h>
35 #include <poly/polynomial_hash_set.h>
36 #include <poly/polynomial_vector.h>
37 #include <poly/variable_db.h>
38 #include <poly/variable_list.h>
39 #include <poly/variable_order.h>
40 #include <poly/polynomial.h>
41 #include <poly/interval.h>
42 
43 static
polynomial_buffer_ensure_size(lp_polynomial_t *** buffer,uint32_t * buffer_size,uint32_t size,const lp_polynomial_context_t * ctx)44 void polynomial_buffer_ensure_size(lp_polynomial_t*** buffer, uint32_t* buffer_size, uint32_t size, const lp_polynomial_context_t* ctx) {
45   if (*buffer_size < size) {
46     uint32_t new_size = *buffer_size;
47     while (new_size < size) {
48       new_size = new_size + new_size / 2 + 10;
49     }
50     *buffer = safe_realloc(*buffer, new_size*sizeof(lp_polynomial_t*));
51     uint32_t i;
52     for (i = *buffer_size; i < new_size; ++ i) {
53       (*buffer)[i] = lp_polynomial_new(ctx);
54     }
55     *buffer_size = new_size;
56   }
57 }
58 
59 static
psc_buffer_delete(lp_polynomial_t ** psc_buffer,uint32_t psc_buffer_size)60 void psc_buffer_delete(lp_polynomial_t** psc_buffer, uint32_t psc_buffer_size) {
61   uint32_t i;
62   for (i = 0; i < psc_buffer_size; ++ i) {
63     lp_polynomial_delete(psc_buffer[i]);
64   }
65   safe_free(psc_buffer);
66 }
67 
68 
69 struct lp_projection_map_struct {
70 
71   /** All polynomials added already */
72   lp_polynomial_hash_set_t all_polynomials;
73 
74   /** The sets we're maintaining */
75   lp_polynomial_hash_set_t* data;
76 
77   /** Size of the data */
78   size_t data_size;
79 
80   /** Size of the data */
81   size_t data_capacity;
82 
83   /** Map from indices to the projection sets where it is the top variable */
84   int_hmap_t var_to_index_map;
85 
86   /** List of all variables added */
87   lp_variable_list_t all_vars;
88 
89   /** List of all yet unprojected variables */
90   lp_variable_list_t unprojected_vars;
91 
92   /** The polynomial context */
93   const lp_polynomial_context_t* ctx;
94 
95   /** The variable database */
96   const lp_variable_db_t* var_db;
97 
98   /** The variable order */
99   const lp_variable_order_t* order;
100 
101   /** The assignment */
102   const lp_assignment_t* m;
103 
104   /** The nra plugin */
105   nra_plugin_t* nra;
106 };
107 
108 typedef struct lp_projection_map_struct lp_projection_map_t;
109 
110 #define LP_PROJECTION_MAP_DEFAULT_SIZE 10
111 
lp_projection_map_construct(lp_projection_map_t * map,nra_plugin_t * nra)112 void lp_projection_map_construct(lp_projection_map_t* map, nra_plugin_t* nra) {
113   map->data_size = 0;
114   map->data_capacity = LP_PROJECTION_MAP_DEFAULT_SIZE;
115   map->data = safe_malloc(sizeof(lp_polynomial_hash_set_t)*map->data_capacity);
116   map->ctx = nra->lp_data.lp_ctx;
117   map->var_db = nra->lp_data.lp_var_db;
118   map->order = nra->lp_data.lp_var_order;
119   map->m = nra->lp_data.lp_assignment;
120   map->nra = nra;
121 
122   lp_polynomial_hash_set_construct(&map->all_polynomials);
123   init_int_hmap(&map->var_to_index_map, 0);
124   lp_variable_list_construct(&map->all_vars);
125   lp_variable_list_construct(&map->unprojected_vars);
126 }
127 
lp_projection_map_destruct(lp_projection_map_t * map)128 void lp_projection_map_destruct(lp_projection_map_t* map) {
129   size_t i;
130   for (i = 0; i < map->data_size; ++ i) {
131     lp_polynomial_hash_set_destruct(map->data + i);
132   }
133   free(map->data);
134   lp_polynomial_hash_set_destruct(&map->all_polynomials);
135   delete_int_hmap(&map->var_to_index_map);
136   lp_variable_list_destruct(&map->all_vars);
137   lp_variable_list_destruct(&map->unprojected_vars);
138 }
139 
lp_projection_map_get_set_of(lp_projection_map_t * map,lp_variable_t var)140 lp_polynomial_hash_set_t* lp_projection_map_get_set_of(lp_projection_map_t* map, lp_variable_t var) {
141 
142   assert(var != variable_null);
143 
144   size_t var_index = 0;
145 
146   // Check if already in the map
147   int_hmap_pair_t* find = int_hmap_find(&map->var_to_index_map, var);
148   if (find == NULL) {
149     // Ensure we can add new
150     if (map->data_size == map->data_capacity) {
151       map->data_capacity = map->data_capacity + map->data_capacity/2 + 10;
152       map->data = safe_realloc(map->data, sizeof(lp_polynomial_hash_set_t)*map->data_capacity);
153     }
154     // Add new
155     var_index = map->data_size;
156     lp_polynomial_hash_set_construct(map->data + var_index);
157     map->data_size ++;
158     assert(map->data_size <= map->data_capacity);
159     int_hmap_add(&map->var_to_index_map, var, var_index);
160     // Add to the list of variables
161     if (!lp_variable_list_contains(&map->all_vars, var)) {
162       lp_variable_list_push(&map->all_vars, var);
163     }
164     if (!lp_variable_list_contains(&map->unprojected_vars, var)) {
165       lp_variable_list_push(&map->unprojected_vars, var);
166     }
167   } else {
168     // Already have it
169     var_index = find->val;
170   }
171 
172   return map->data + var_index;
173 }
174 
175 void lp_projection_map_reduce(lp_projection_map_t* map, lp_variable_t x, const lp_polynomial_t* p, lp_polynomial_t* p_r);
176 
lp_projection_map_add_if_not_there(lp_projection_map_t * map,const lp_polynomial_t * p)177 void lp_projection_map_add_if_not_there(lp_projection_map_t* map, const lp_polynomial_t* p) {
178   if (!lp_polynomial_hash_set_contains(&map->all_polynomials, p)) {
179     lp_variable_t x = lp_polynomial_top_variable(p);
180     lp_polynomial_hash_set_t* x_set = lp_projection_map_get_set_of(map, x);
181     assert(!lp_polynomial_hash_set_contains(x_set, p));
182     lp_polynomial_hash_set_insert(x_set, p);
183     lp_polynomial_hash_set_insert(&map->all_polynomials, p);
184   }
185 }
186 
lp_projection_map_add(lp_projection_map_t * map,const lp_polynomial_t * p)187 void lp_projection_map_add(lp_projection_map_t* map, const lp_polynomial_t* p) {
188 
189   // Don't add constants or things already there
190   if (lp_polynomial_is_constant(p) || lp_polynomial_hash_set_contains(&map->all_polynomials, p)) {
191     return;
192   }
193 
194   // Reduce the polynomials and add all the vanishing coefficients
195   lp_variable_t x = lp_polynomial_top_variable(p);
196   lp_polynomial_t* p_r = lp_polynomial_new(map->ctx);
197   lp_projection_map_reduce(map, x, p, p_r);
198 
199   // Don't add constants or things already there
200   if (lp_polynomial_is_constant(p_r) || lp_polynomial_hash_set_contains(&map->all_polynomials, p_r)) {
201     lp_polynomial_delete(p_r);
202     return;
203   }
204 
205   // If the variable has changed, it was added in reduce
206   if (lp_polynomial_top_variable(p_r) != x) {
207     lp_polynomial_delete(p_r);
208     return;
209   }
210 
211   // p_r leading coefficient doesn't vanish and it is primitive
212   // all the assumptions of this are put in the map
213 
214   // Factor the polynomial. Since it's primitive, all factors are in x,
215   // their leading coefficients don't vanish
216   lp_polynomial_t** p_r_factors = 0;
217   size_t* p_r_factors_multiplicities = 0;
218   size_t p_r_factors_size = 0;
219   lp_polynomial_factor_square_free(p_r, &p_r_factors, &p_r_factors_multiplicities, &p_r_factors_size);
220 
221   uint32_t i;
222 
223   lp_polynomial_t* p_r_zero = NULL;
224   // If x is assigned, check if any of the factors evaluates to 0
225   if (lp_assignment_get_value(map->m, x)->type != LP_VALUE_NONE) {
226     for (i = 0; i < p_r_factors_size; ++ i) {
227       // Get the sign of the polynomials
228       int sgn = lp_polynomial_sgn(p_r_factors[i], map->m);
229       if (sgn == 0) {
230         if (p_r_zero == NULL) {
231           p_r_zero = p_r_factors[i];
232         } else {
233           int cmp = lp_polynomial_cmp(p_r_factors[i], p_r_zero);
234           if (cmp < 0) {
235             p_r_zero = p_r_factors[i];
236           }
237         }
238       }
239     }
240   }
241 
242   // If we have a 0 factor, we just add that one
243   if (p_r_zero != NULL) {
244     assert(!lp_polynomial_is_constant(p_r_zero));
245     assert(x == lp_polynomial_top_variable(p_r_zero));
246     lp_projection_map_add_if_not_there(map, p_r_zero);
247   }
248 
249   // Add factors, if not zero, and delete them
250   for (i = 0; i < p_r_factors_size; ++i) {
251     if (p_r_zero == NULL && !lp_polynomial_is_constant(p_r_factors[i])) {
252       assert(x == lp_polynomial_top_variable(p_r_factors[i]));
253       lp_projection_map_add_if_not_there(map, p_r_factors[i]);
254     }
255     lp_polynomial_delete(p_r_factors[i]);
256   }
257 
258   // Hash the inputs
259   lp_polynomial_hash_set_insert(&map->all_polynomials, p);
260   lp_polynomial_hash_set_insert(&map->all_polynomials, p_r);
261 
262   // Remove other temps
263   free(p_r_factors);
264   free(p_r_factors_multiplicities);
265   lp_polynomial_delete(p_r);
266 }
267 
268 static
269 const lp_variable_order_t* lp_projection_map_variable_cmp_order = 0;
270 
lp_projection_map_variable_cmp(const void * x,const void * y)271 int lp_projection_map_variable_cmp(const void* x, const void* y) {
272   lp_variable_t x_var = *(lp_variable_t*)x;
273   lp_variable_t y_var = *(lp_variable_t*)y;
274   return lp_variable_order_cmp(lp_projection_map_variable_cmp_order, x_var, y_var);
275 }
276 
lp_projection_map_order_vars(lp_projection_map_t * map)277 void lp_projection_map_order_vars(lp_projection_map_t* map) {
278   lp_variable_list_order(&map->all_vars, map->order);
279   lp_variable_list_order(&map->unprojected_vars, map->order);
280 }
281 
lp_projection_map_pop_top_unprojected_var(lp_projection_map_t * map)282 lp_variable_t lp_projection_map_pop_top_unprojected_var(lp_projection_map_t* map) {
283   if (lp_variable_list_size(&map->unprojected_vars) > 0) {
284     // Sort all unprojected variable based on order
285     lp_variable_list_order(&map->unprojected_vars, map->order);
286     lp_variable_t top = lp_variable_list_top(&map->unprojected_vars);
287     lp_variable_list_pop(&map->unprojected_vars);
288     return top;
289   } else {
290     return lp_variable_null;
291   }
292 }
293 
lp_projection_map_print(const lp_projection_map_t * map,FILE * out)294 int lp_projection_map_print(const lp_projection_map_t* map, FILE* out) {
295   int ret = 0;
296   size_t i = 0;
297   for (i = 0; i < map->all_vars.list_size; ++ i) {
298     lp_variable_t x = map->all_vars.list[i];
299     ret += fprintf(out, "%s : ", lp_variable_db_get_name(map->var_db, x));
300     int_hmap_pair_t* find = int_hmap_find((int_hmap_t*) &map->var_to_index_map, x);
301     assert(find != NULL);
302     const lp_polynomial_hash_set_t* x_set = map->data + find->val;
303     ret += lp_polynomial_hash_set_print(x_set, out);
304     ret += fprintf(out, "\n");
305   }
306   return ret;
307 }
308 
lp_projection_map_mk_root_atom(lp_projection_map_t * map,lp_variable_t x,size_t root_index,const lp_polynomial_t * p,root_atom_rel_t r)309 term_t lp_projection_map_mk_root_atom(lp_projection_map_t* map, lp_variable_t x, size_t root_index, const lp_polynomial_t* p, root_atom_rel_t r) {
310   assert(lp_polynomial_top_variable(p) == x);
311   assert(lp_polynomial_lc_sgn(p) > 0);
312 
313   term_t root_atom = NULL_TERM;
314   term_manager_t* tm = map->nra->ctx->tm;
315 
316   size_t p_deg = lp_polynomial_degree(p);
317   if (p_deg == 1 && lp_polynomial_lc_is_constant(p)) {
318     // Linear
319     // x r root(ax + b)
320     // x r -b/a  [ a is positive ]
321     // ax + b r 0
322 
323     term_t p_term = lp_polynomial_to_yices_term(map->nra, p);
324 
325     if (ctx_trace_enabled(map->nra->ctx, "nra::explain::projection")) {
326       ctx_trace_printf(map->nra->ctx, "p_term = "); ctx_trace_term(map->nra->ctx, p_term);
327     }
328 
329     switch (r) {
330     case ROOT_ATOM_LT:
331       root_atom = mk_arith_term_lt0(tm, p_term);
332       break;
333     case ROOT_ATOM_LEQ:
334       root_atom = mk_arith_term_leq0(tm, p_term);
335       break;
336     case ROOT_ATOM_EQ: {
337       root_atom = mk_arith_term_eq0(tm, p_term);
338       break;
339     }
340     case ROOT_ATOM_NEQ:
341       root_atom = mk_arith_term_neq0(tm, p_term);
342       break;
343     case ROOT_ATOM_GEQ:
344       root_atom = mk_arith_term_geq0(tm, p_term);
345       break;
346     case ROOT_ATOM_GT:
347       root_atom = mk_arith_term_gt0(tm, p_term);
348       break;
349     default:
350       assert(false);
351     }
352   } else {
353     // Regular root atom
354     variable_t x_var = nra_plugin_get_variable_from_lp_variable(map->nra, x);
355     term_t x_term = variable_db_get_term(map->nra->ctx->var_db, x_var);
356     term_t p_term = lp_polynomial_to_yices_term(map->nra, p);
357     root_atom = mk_arith_root_atom(tm, root_index, x_term, p_term, r);
358   }
359 
360   assert(term_kind(tm->terms, root_atom) != CONSTANT_TERM);
361 
362   return root_atom;
363 }
364 
365 #ifndef NDEBUG
366 static
ensure_true(plugin_context_t * ctx,term_t literal)367 bool ensure_true(plugin_context_t* ctx, term_t literal) {
368   term_t atom = unsigned_term(literal);
369   variable_t atom_var = variable_db_get_variable_if_exists(ctx->var_db, atom);
370   bool ok = true;
371   if (atom_var != variable_null) {
372     if (trail_has_value(ctx->trail, atom_var)) {
373       const mcsat_value_t* atom_value = trail_get_value(ctx->trail, atom_var);
374       if (atom_value->type != VALUE_BOOLEAN) {
375         fprintf(stderr, "Value not Boolean\n");
376         ok = false;
377       } else if (atom_value->b == (literal != atom)){
378         fprintf(stderr, "Value is false (should be true)\n");
379         ok = false;
380       }
381     }
382   }
383   if (!ok) {
384     fprintf(stderr, "var = %d\n", atom_var);
385   }
386 
387   return ok;
388 }
389 
390 #endif
391 
392 /**
393  * Compare two polynomials by degree. Otherwise, go for the leading coefficients
394  */
polynomial_cmp(const void * p1_void,const void * p2_void)395 int polynomial_cmp(const void* p1_void, const void* p2_void) {
396   const lp_polynomial_t* p1 = *((const lp_polynomial_t**) p1_void);
397   const lp_polynomial_t* p2 = *((const lp_polynomial_t**) p2_void);
398   return lp_polynomial_cmp(p1, p2);
399 }
400 
401 /**
402  * Simplify 0-polynomials with the GCD.
403  */
gcd_simplify_zero(const lp_polynomial_context_t * ctx,lp_polynomial_t ** polys,size_t * size,const lp_assignment_t * m)404 void gcd_simplify_zero(const lp_polynomial_context_t* ctx, lp_polynomial_t** polys, size_t* size, const lp_assignment_t* m) {
405   // Temp for GCD computation
406   lp_polynomial_t* gcd = lp_polynomial_new(ctx);
407 
408   uint32_t i, j, to_keep = 0;
409   for (i = 0; i < *size; ++ i) {
410     const lp_polynomial_t* p = polys[i];
411     if (lp_polynomial_sgn(p, m) == 0) {
412       for (j = 0; j < to_keep; ++ j) {
413         const lp_polynomial_t* q = polys[j];
414         if (lp_polynomial_sgn(q, m) == 0) {
415           lp_polynomial_gcd(gcd, p, q);
416           if (!lp_polynomial_is_constant(gcd)) {
417             lp_polynomial_swap(polys[j], gcd);
418             break;
419           }
420         }
421       }
422       if (j >= to_keep) {
423         // Didn't embed it in any previous ones, keep it
424         polys[to_keep++] = polys[i];
425       } else {
426         // Not keeping it, have to remove it
427         lp_polynomial_delete(polys[i]);
428       }
429     } else {
430       // Keep it, it's non-zero
431       polys[to_keep++] = polys[i];
432     }
433   }
434 
435   // Resized
436   *size = to_keep;
437 
438   // Delete temp
439   lp_polynomial_delete(gcd);
440 }
441 
442 /**
443  * Isolate the roots of the projection polynomials of x. Then construct a cell
444  * assertions and add to out. Return the bound polynomials in x_cell_a_p and x_cell_b_p.
445  */
lp_projection_map_construct_cell(lp_projection_map_t * map,lp_variable_t x,ivector_t * out,const lp_polynomial_t ** x_cell_a_p,const lp_polynomial_t ** x_cell_b_p)446 void lp_projection_map_construct_cell(lp_projection_map_t* map, lp_variable_t x, ivector_t* out,
447     const lp_polynomial_t** x_cell_a_p,
448     const lp_polynomial_t** x_cell_b_p
449 ) {
450 
451   plugin_context_t* ctx = map->nra->ctx;
452 
453   // Get the set to make sign invariant
454   lp_polynomial_hash_set_t* x_set = lp_projection_map_get_set_of(map, x);
455   lp_polynomial_hash_set_close(x_set);
456 
457   if (ctx_trace_enabled(ctx, "nra::explain::projection")) {
458     ctx_trace_printf(ctx, "x_set = "); lp_polynomial_hash_set_print(x_set, ctx_trace_out(ctx)); ctx_trace_printf(ctx, "\n");
459   }
460 
461   // Simplify the polynomials based on gcd:
462   //   * If two polynomials evaluate to 0, they should be mutually prime
463   //   * We just check: if both 0 and gcd, then we keep the gcd reducing the size
464   gcd_simplify_zero(map->ctx, x_set->data, &x_set->size, map->m);
465 
466   // Sort the polynomials by degree
467   qsort(x_set->data, x_set->size, sizeof(lp_polynomial_t*), polynomial_cmp);
468 
469   // The cell we're constructing
470   lp_interval_t x_cell;
471   lp_interval_construct_full(&x_cell);
472 
473   // Lower bound polynomial and root index
474   (*x_cell_a_p) = NULL;
475   size_t x_cell_a_root_index = 0;
476 
477   // Upper bound polynomial and root index
478   (*x_cell_b_p) = NULL;
479   size_t x_cell_b_root_index = 0;
480 
481   size_t p_i;
482   bool done = false;
483   for (p_i = 0; !done && p_i < x_set->size; ++ p_i) {
484 
485     assert(x_cell.a_open && x_cell.b_open);
486 
487     // Polynomial and it's degree
488     const lp_polynomial_t* p = x_set->data[p_i];
489     assert(lp_polynomial_top_variable(p) == x);
490     size_t p_deg = lp_polynomial_degree(p);
491 
492     if (ctx_trace_enabled(ctx, "nra::explain::projection")) {
493       ctx_trace_printf(ctx, "x_cell = "); lp_interval_print(&x_cell, ctx_trace_out(ctx)); ctx_trace_printf(ctx, "\n");
494       ctx_trace_printf(ctx, "x_cell_a_p = "); if (*x_cell_a_p != NULL) lp_polynomial_print((*x_cell_a_p), ctx_trace_out(ctx)); ctx_trace_printf(ctx, "\n");
495       ctx_trace_printf(ctx, "x_cell_a_root_index = %zu\n", x_cell_a_root_index);
496       ctx_trace_printf(ctx, "x_cell_b_p = "); if (*x_cell_b_p != NULL) lp_polynomial_print((*x_cell_b_p), ctx_trace_out(ctx)); ctx_trace_printf(ctx, "\n");
497       ctx_trace_printf(ctx, "x_cell_b_root_index = %zu\n", x_cell_b_root_index);
498       ctx_trace_printf(ctx, "p = "); lp_polynomial_print(p, ctx_trace_out(ctx)); ctx_trace_printf(ctx, "\n");
499       ctx_trace_printf(ctx, "p_deg = %zu\n", p_deg);
500     }
501 
502     // Isolate the roots
503     assert(p_deg > 0);
504     lp_value_t* p_roots = safe_malloc(sizeof(lp_value_t)*p_deg);
505     size_t p_roots_size = 0;
506     lp_polynomial_roots_isolate(p, map->m, p_roots, &p_roots_size);
507 
508     if (ctx_trace_enabled(ctx, "nra::explain::projection")) {
509       ctx_trace_printf(ctx, "roots = ");
510       size_t p_roots_i;
511       for (p_roots_i = 0; p_roots_i < p_roots_size; ++ p_roots_i) {
512         if (p_roots_i) {
513           ctx_trace_printf(ctx, ", ");
514         }
515         lp_value_print(p_roots + p_roots_i, ctx_trace_out(ctx));
516       }
517       ctx_trace_printf(ctx, "\n");
518     }
519 
520     // Binary search for the current value x_v
521     const lp_value_t* x_v = lp_assignment_get_value(map->m, x);
522     if (ctx_trace_enabled(ctx, "nra::explain::projection")) {
523       ctx_trace_printf(ctx, "x_v = ");
524       lp_value_print(x_v, ctx_trace_out(ctx));
525       ctx_trace_printf(ctx, "\n");
526     }
527     if (p_roots_size > 0) {
528       int m; // midpoint and where to insert
529       int m_cmp;
530       int lb = 0;
531       int ub = p_roots_size - 1;
532 
533       for(;;) {
534         m = (lb + ub) / 2;
535         m_cmp = lp_value_cmp(p_roots + m, x_v);
536 
537         if (ctx_trace_enabled(ctx, "nra::explain::projection")) {
538           ctx_trace_printf(ctx, "m = %d\n", m);
539           ctx_trace_printf(ctx, "m_cmp = %d\n", m_cmp);
540           ctx_trace_printf(ctx, "lb = %d\n", lb);
541           ctx_trace_printf(ctx, "ub = %d\n", ub);
542         }
543 
544         if (m_cmp == 0) {
545           // found
546           break;
547         } else if (m_cmp < 0) {
548           lb = m + 1;
549           if (lb > ub) {
550             // it's in m, m+1
551             break;
552           }
553         } else  {
554           ub = m - 1;
555           if (lb > ub) {
556             // it's in m-1, m
557             m --;
558             break;
559           }
560         }
561       }
562 
563       if (m_cmp == 0) {
564         // found it at m, so we take [roots[m], roots[m]] as the final one
565         // no need for more cell division
566         lp_interval_collapse_to(&x_cell, x_v);
567         (*x_cell_a_p) = p;
568         (*x_cell_b_p) = NULL;
569         x_cell_a_root_index = m;
570         // We use the first one, sort should do it
571         done = true;
572       } else {
573         // Divide cells
574         if (m < 0) {
575           // in (-inf, p_roots[0]) so
576           if (lp_interval_contains(&x_cell, p_roots)) {
577             lp_interval_set_b(&x_cell, p_roots, 1);
578             (*x_cell_b_p) = p;
579             x_cell_b_root_index = 0;
580           }
581         } else if (m + 1 == p_roots_size) {
582           // in (p_roots[m], +inf)
583           if (lp_interval_contains(&x_cell, p_roots + m)) {
584             lp_interval_set_a(&x_cell, p_roots + m, 1);
585             (*x_cell_a_p) = p;
586             x_cell_a_root_index = m;
587           }
588         } else {
589           // in (p_roots[m], p_roots[m+1])
590           if (lp_interval_contains(&x_cell, p_roots + m)) {
591             lp_interval_set_a(&x_cell, p_roots + m, 1);
592             (*x_cell_a_p) = p;
593             x_cell_a_root_index = m;
594           }
595           if (lp_interval_contains(&x_cell, p_roots + m + 1)) {
596             lp_interval_set_b(&x_cell, p_roots + m + 1, 1);
597             (*x_cell_b_p) = p;
598             x_cell_b_root_index = m + 1;
599           }
600         }
601       }
602     }
603 
604     if (ctx_trace_enabled(ctx, "nra::explain::projection")) {
605       ctx_trace_printf(ctx, "roots = ");
606       size_t p_roots_i;
607       for (p_roots_i = 0; p_roots_i < p_roots_size; ++ p_roots_i) {
608         if (p_roots_i) {
609           ctx_trace_printf(ctx, ", ");
610         }
611         lp_value_print(p_roots + p_roots_i, ctx_trace_out(ctx));
612       }
613       ctx_trace_printf(ctx, "\n");
614     }
615 
616     // Remove the roots
617     size_t p_roots_i;
618     for (p_roots_i = 0; p_roots_i < p_roots_size; ++ p_roots_i) {
619       lp_value_destruct(p_roots + p_roots_i);
620     }
621     safe_free(p_roots);
622   }
623 
624   if (ctx_trace_enabled(ctx, "nra::explain::projection")) {
625     ctx_trace_printf(ctx, "x_cell = "); lp_interval_print(&x_cell, ctx_trace_out(ctx)); ctx_trace_printf(ctx, "\n");
626     ctx_trace_printf(ctx, "x_cell_a_p = "); if (*x_cell_a_p != NULL) lp_polynomial_print((*x_cell_a_p), ctx_trace_out(ctx)); ctx_trace_printf(ctx, "\n");
627     ctx_trace_printf(ctx, "x_cell_a_root_index = %zu\n", x_cell_a_root_index);
628     ctx_trace_printf(ctx, "x_cell_b_p = "); if (*x_cell_b_p != NULL) lp_polynomial_print((*x_cell_b_p), ctx_trace_out(ctx)); ctx_trace_printf(ctx, "\n");
629     ctx_trace_printf(ctx, "x_cell_b_root_index = %zu\n", x_cell_b_root_index);
630   }
631 
632   // Add the cell constraint
633   if (lp_interval_is_point(&x_cell)) {
634     term_t eq_root_atom = lp_projection_map_mk_root_atom(map, x, x_cell_a_root_index, (*x_cell_a_p), ROOT_ATOM_EQ);
635     ivector_push(out, eq_root_atom);
636     if (ctx_trace_enabled(ctx, "nra::explain::projection")) {
637       ctx_trace_printf(ctx, "eq_root_atom = "); ctx_trace_term(ctx, eq_root_atom);
638     }
639     assert(ensure_true(ctx, eq_root_atom));
640   } else {
641 
642     const lp_value_t* x_cell_lb = lp_interval_get_lower_bound(&x_cell);
643     const lp_value_t* x_cell_ub = lp_interval_get_upper_bound(&x_cell);
644 
645     assert(lp_value_cmp(x_cell_lb, x_cell_ub) < 0);
646 
647     if (x_cell_lb->type != LP_VALUE_MINUS_INFINITY) {
648       term_t lb_root_atom = lp_projection_map_mk_root_atom(map, x, x_cell_a_root_index, (*x_cell_a_p), ROOT_ATOM_GT);
649       ivector_push(out, lb_root_atom);
650       if (ctx_trace_enabled(ctx, "nra::explain::projection")) {
651         ctx_trace_printf(ctx, "lb_root_atom = "); ctx_trace_term(ctx, lb_root_atom);
652       }
653       assert(ensure_true(ctx, lb_root_atom));
654     }
655     if (x_cell_ub->type != LP_VALUE_PLUS_INFINITY) {
656       term_t ub_root_atom = lp_projection_map_mk_root_atom(map, x, x_cell_b_root_index, (*x_cell_b_p), ROOT_ATOM_LT);
657       ivector_push(out, ub_root_atom);
658       if (ctx_trace_enabled(ctx, "nra::explain::projection")) {
659         ctx_trace_printf(ctx, "ub_root_atom = "); ctx_trace_term(ctx, ub_root_atom);
660       }
661       assert(ensure_true(ctx, ub_root_atom));
662     }
663   }
664 
665   // Destruct the cell
666   lp_interval_destruct(&x_cell);
667 }
668 
669 /** Add the model based PSC of the two polynomials to the projection map */
lp_projection_map_add_psc(lp_projection_map_t * map,lp_polynomial_t *** polynomial_buffer,uint32_t * polynomial_buffer_size,lp_variable_t x,const lp_polynomial_t * p,const lp_polynomial_t * q)670 void lp_projection_map_add_psc(lp_projection_map_t* map, lp_polynomial_t*** polynomial_buffer, uint32_t* polynomial_buffer_size, lp_variable_t x, const lp_polynomial_t* p, const lp_polynomial_t* q) {
671   // Ensure buffer size min(deg(p_r_d), deg(p_r)) + 1 = p_r_deg
672   assert(lp_polynomial_top_variable(p) == x);
673   assert(lp_polynomial_top_variable(q) == x);
674 
675   size_t p_deg = lp_polynomial_degree(p);
676   size_t q_deg = lp_polynomial_degree(q);
677 
678   uint32_t psc_size = p_deg > q_deg ? q_deg + 1 : p_deg + 1;
679   polynomial_buffer_ensure_size(polynomial_buffer, polynomial_buffer_size, psc_size, map->ctx);
680 
681   // Get the psc
682   lp_polynomial_psc(*polynomial_buffer, p, q);
683   // Add the initial sequence of the psc
684   uint32_t psc_i;
685   for (psc_i = 0; psc_i < psc_size; ++ psc_i) {
686     // Add it
687     lp_projection_map_add(map, (*polynomial_buffer)[psc_i]);
688     // If it doesn't vanish we're done
689     if (lp_polynomial_sgn((*polynomial_buffer)[psc_i], map->m)) {
690       break;
691     }
692   }
693 }
694 
695 /** Add the model-based gcd of the two polynomials to the projection map */
lp_projection_map_add_mgcd(lp_projection_map_t * map,lp_variable_t x,const lp_polynomial_t * p,const lp_polynomial_t * q)696 void lp_projection_map_add_mgcd(lp_projection_map_t* map, lp_variable_t x, const lp_polynomial_t* p, const lp_polynomial_t* q) {
697   // Ensure buffer size min(deg(p_r_d), deg(p_r)) + 1 = p_r_deg
698   assert(lp_polynomial_top_variable(p) == x);
699   assert(lp_polynomial_top_variable(q) == x);
700 
701   // Compute the gcd
702   if (ctx_trace_enabled(map->nra->ctx, "nra::explain::mgcd")) {
703     ctx_trace_printf(map->nra->ctx, "p = "); lp_polynomial_print(p, ctx_trace_out(map->nra->ctx)); ctx_trace_printf(map->nra->ctx, "\n");
704     ctx_trace_printf(map->nra->ctx, "q = "); lp_polynomial_print(q, ctx_trace_out(map->nra->ctx)); ctx_trace_printf(map->nra->ctx, "\n");
705 
706     lp_variable_list_t vars;
707     lp_variable_list_construct(&vars);
708     lp_polynomial_get_variables(p, &vars);
709     lp_polynomial_get_variables(q, &vars);
710     lp_variable_list_order(&vars, map->nra->lp_data.lp_var_order);
711 
712     uint32_t i;
713     for (i = 0; i < vars.list_size; ++ i) {
714       lp_variable_t var = vars.list[i];
715       const lp_value_t* v = lp_assignment_get_value(map->m, var);
716       if (v->type != LP_VALUE_NONE) {
717         ctx_trace_printf(map->nra->ctx, "%s -> ", lp_variable_db_get_name(map->nra->lp_data.lp_var_db, var));
718         lp_value_print(v, ctx_trace_out(map->nra->ctx));
719         ctx_trace_printf(map->nra->ctx, "\n");
720       }
721     }
722 
723     lp_variable_list_destruct(&vars);
724   }
725 
726   lp_polynomial_vector_t* assumptions = lp_polynomial_mgcd(p, q, map->m);
727 
728   if (ctx_trace_enabled(map->nra->ctx, "nra::explain::mgcd")) {
729     ctx_trace_printf(map->nra->ctx, "mgcd done: \n");
730   }
731 
732   // Add the initial sequence of the psc
733   uint32_t assumptions_i;
734   uint32_t assumptions_size = lp_polynomial_vector_size(assumptions);
735   for (assumptions_i = 0; assumptions_i < assumptions_size; ++ assumptions_i) {
736     // Add it
737     lp_polynomial_t* assumption = lp_polynomial_vector_at(assumptions, assumptions_i);
738     lp_projection_map_add(map, assumption);
739     lp_polynomial_delete(assumption);
740   }
741 
742   lp_polynomial_vector_delete(assumptions);
743 }
744 
lp_projection_map_reduce(lp_projection_map_t * map,lp_variable_t x,const lp_polynomial_t * p,lp_polynomial_t * p_r)745 void lp_projection_map_reduce(lp_projection_map_t* map, lp_variable_t x, const lp_polynomial_t* p, lp_polynomial_t* p_r) {
746 
747   assert(p != p_r);
748   assert(lp_polynomial_top_variable(p) == x);
749 
750   lp_polynomial_t* p_coeff = lp_polynomial_new(map->ctx);
751 
752   uint32_t p_deg = lp_polynomial_degree(p);
753 
754   lp_polynomial_reductum_m(p_r, p, map->m);
755   uint32_t p_r_deg = lp_polynomial_top_variable(p_r) == x ? lp_polynomial_degree(p_r) : 0;
756 
757   // Add the vanishing initial coefficients (this includes the top reduced, hence the content)
758   uint32_t deg;
759   for (deg = p_r_deg; deg <= p_deg; ++ deg) {
760     // Add the coefficient
761     lp_polynomial_get_coefficient(p_coeff, p,  deg);
762     lp_projection_map_add(map, p_coeff);
763   }
764 
765   // Get the primitive part
766   lp_polynomial_pp(p_r, p_r);
767 
768   lp_polynomial_delete(p_coeff);
769 }
770 
771 /**
772  * Project the content of the map downwards until done. All the projection
773  * sets will be closed, so that iteration is possible.
774  */
lp_projection_map_project(lp_projection_map_t * map,ivector_t * out)775 void lp_projection_map_project(lp_projection_map_t* map, ivector_t* out) {
776 
777   // Temps
778   const lp_polynomial_t* p = 0;
779   const lp_polynomial_t* q = 0;
780   lp_polynomial_t* p_r = lp_polynomial_new(map->ctx);
781   lp_polynomial_t* q_r = lp_polynomial_new(map->ctx);
782   lp_polynomial_t* p_r_d = lp_polynomial_new(map->ctx);
783 
784   // PSC buffer
785   lp_polynomial_t** polynomial_buffer = 0;
786   uint32_t polynomial_buffer_size = 0;
787 
788   const lp_polynomial_t* x_cell_a_p = NULL;
789   const lp_polynomial_t* x_cell_b_p = NULL;
790   lp_polynomial_t* x_cell_a_p_r = lp_polynomial_new(map->ctx);
791   lp_polynomial_t* x_cell_b_p_r = lp_polynomial_new(map->ctx);
792 
793   // Project
794   for (;;) {
795 
796     if (ctx_trace_enabled(map->nra->ctx, "nra::explain::projection")) {
797       ctx_trace_printf(map->nra->ctx, "current projection:\n");
798       lp_projection_map_print(map, ctx_trace_out(map->nra->ctx));
799     }
800 
801     // Get the top variable not projected yet
802     lp_variable_t x = lp_projection_map_pop_top_unprojected_var(map);
803     // If all projected, we're done
804     if (x == lp_variable_null) {
805       break;
806     }
807 
808     if (ctx_trace_enabled(map->nra->ctx, "nra::explain::projection")) {
809       ctx_trace_printf(map->nra->ctx, "x = %s\n", lp_variable_db_get_name(map->var_db, x));
810     }
811 
812     // Get the set of polynomials to project
813     lp_polynomial_hash_set_close(lp_projection_map_get_set_of(map, x)); // We don't add again
814 
815     // If we are at the top variable we project all polynomials.
816     // At the lower levels we:
817     // * Isolate the roots, find the two (or one) roots that enclose the current
818     //   model (the cell, polynomials l, u).
819     // * L: polynomials that have roots below the cell
820     // * U: polynomials that have roots above the cell
821     // * The projection is then
822     //   - all p: fix degree, and number of roots, i.e. red(p), and psc/gcd(p,p')
823     //   - relationship between p in L, and l
824     //   - relationship between p in U, and u
825     //   - relationship between l and u
826     bool top = lp_assignment_get_value(map->m, x)->type == LP_VALUE_NONE;
827 
828     if (!top) {
829       // Generate the cell, and get the bounding polynomials
830       x_cell_a_p = NULL;
831       x_cell_b_p = NULL;
832       lp_projection_map_construct_cell(map, x, out, &x_cell_a_p, &x_cell_b_p);
833       // Reduce the polynomials
834       if (x_cell_a_p != NULL) {
835         lp_projection_map_reduce(map, x, x_cell_a_p, x_cell_a_p_r);
836       }
837       if (x_cell_b_p != NULL) {
838         lp_projection_map_reduce(map, x, x_cell_b_p, x_cell_b_p_r);
839       }
840     }
841 
842     // Go through the polynomials and project
843     uint32_t x_set_i;
844     for (x_set_i = 0; x_set_i < lp_projection_map_get_set_of(map, x)->size; ++ x_set_i) {
845 
846       // Current polynomial
847       p = lp_projection_map_get_set_of(map, x)->data[x_set_i];
848       assert(lp_polynomial_top_variable(p) == x);
849       uint32_t p_deg = lp_polynomial_degree(p);
850 
851       if (ctx_trace_enabled(map->nra->ctx, "nra::explain::projection")) {
852         ctx_trace_printf(map->nra->ctx, "p = "); lp_polynomial_print(p, ctx_trace_out(map->nra->ctx)); ctx_trace_printf(map->nra->ctx, "\n");
853         ctx_trace_printf(map->nra->ctx, "p_deg = %u\n", p_deg);
854       }
855 
856       // Reduce p modulo the model, and add assumptions
857       lp_projection_map_reduce(map, x, p, p_r);
858       uint32_t p_r_deg = lp_polynomial_top_variable(p_r) == x ? lp_polynomial_degree(p_r) : 0;
859 
860       // Is p_r univariate?
861       bool p_r_univariate = lp_polynomial_is_univariate(p_r);
862 
863       // Add the vanishing psc of p_r, p_r' (don't do univariate, they go to constants)
864       if (p_r_deg > 1 && !p_r_univariate) {
865         // Get the derivative
866         lp_polynomial_derivative(p_r_d, p_r);
867         // p_r is reduced, but the derivative might not be (the numberical constants)
868         lp_polynomial_pp(p_r_d, p_r_d);
869         // Add the projection
870         if (map->nra->ctx->options->nra_mgcd) {
871           lp_projection_map_add_mgcd(map, x, p_r, p_r_d);
872         } else {
873           lp_projection_map_add_psc(map, &polynomial_buffer, &polynomial_buffer_size, x, p_r, p_r_d);
874         }
875       }
876 
877       if (p_r_deg > 0) {
878         // Now combine with other reductums
879         if (!map->nra->ctx->options->nra_nlsat && !top) {
880           // Compare with lower bound polynomial
881           if (p != x_cell_a_p && x_cell_b_p_r != NULL) {
882             uint32_t x_cell_a_p_deg = lp_polynomial_top_variable(x_cell_a_p_r) == x ? lp_polynomial_degree(x_cell_a_p_r) : 0;
883             if ((!p_r_univariate || !lp_polynomial_is_univariate(x_cell_a_p_r)) && x_cell_a_p_deg > 0) {
884               // Add the psc
885               if (map->nra->ctx->options->nra_mgcd) {
886                 lp_projection_map_add_mgcd(map, x, p_r, x_cell_a_p_r);
887               } else {
888                 lp_projection_map_add_psc(map, &polynomial_buffer, &polynomial_buffer_size, x, p_r, x_cell_a_p_r);
889               }
890             }
891           }
892           // Compare with upper bound polynomial
893           if (p != x_cell_b_p_r && x_cell_b_p_r != NULL) {
894             uint32_t x_cell_b_p_r_deg = lp_polynomial_top_variable(x_cell_b_p_r) == x ? lp_polynomial_degree(x_cell_b_p_r) : 0;
895             if ((!p_r_univariate || !lp_polynomial_is_univariate(x_cell_b_p_r)) && x_cell_b_p_r_deg > 0) {
896               // Add the psc
897               if (map->nra->ctx->options->nra_mgcd) {
898                 lp_projection_map_add_mgcd(map, x, p_r, x_cell_b_p_r);
899               } else {
900                 lp_projection_map_add_psc(map, &polynomial_buffer, &polynomial_buffer_size, x, p_r, x_cell_b_p_r);
901               }
902             }
903           }
904         } else {
905           // Top level, project with all
906           uint32_t x_set_j;
907           for (x_set_j = x_set_i + 1; x_set_j < lp_projection_map_get_set_of(map, x)->size; ++ x_set_j) {
908 
909             // The other polynomial
910             q = lp_projection_map_get_set_of(map, x)->data[x_set_j];
911             assert(lp_polynomial_top_variable(p) == x);
912 
913             if (ctx_trace_enabled(map->nra->ctx, "nra::explain::projection")) {
914               ctx_trace_printf(map->nra->ctx, "q = "); lp_polynomial_print(q, ctx_trace_out(map->nra->ctx)); ctx_trace_printf(map->nra->ctx, "\n");
915             }
916 
917             // Reductum
918             lp_polynomial_reductum_m(q_r, q, map->m);
919             uint32_t q_r_deg = lp_polynomial_top_variable(q_r) == x ? lp_polynomial_degree(q_r) : 0;
920 
921             // No need to work on univariate ones
922             if (p_r_univariate && lp_polynomial_is_univariate(q_r)) {
923                continue;
924             }
925 
926             if (ctx_trace_enabled(map->nra->ctx, "nra::explain::projection")) {
927               ctx_trace_printf(map->nra->ctx, "q_r = "); lp_polynomial_print(q_r, ctx_trace_out(map->nra->ctx)); ctx_trace_printf(map->nra->ctx, "\n");
928               ctx_trace_printf(map->nra->ctx, "q_r_deg = %u\n", q_r_deg);
929             }
930 
931             if (q_r_deg > 0) {
932               // Add the psc
933               if (map->nra->ctx->options->nra_mgcd) {
934                 lp_projection_map_add_mgcd(map, x, p_r, q_r);
935               } else {
936                 lp_projection_map_add_psc(map, &polynomial_buffer, &polynomial_buffer_size, x, p_r, q_r);
937               }
938             }
939           }
940         }
941       }
942     }
943   }
944 
945   // Free the temps
946   lp_polynomial_delete(p_r);
947   lp_polynomial_delete(q_r);
948   lp_polynomial_delete(p_r_d);
949   if (x_cell_a_p_r != NULL) {
950     lp_polynomial_delete(x_cell_a_p_r);
951   }
952   if (x_cell_b_p_r != NULL) {
953     lp_polynomial_delete(x_cell_b_p_r);
954   }
955   psc_buffer_delete(polynomial_buffer, polynomial_buffer_size);
956 }
957 
958 #ifndef NDEBUG
959 static
constraint_has_value(const mcsat_trail_t * trail,const int_mset_t * pos,const int_mset_t * neg,variable_t constraint)960 bool constraint_has_value(const mcsat_trail_t* trail, const int_mset_t* pos, const int_mset_t* neg, variable_t constraint) {
961   if (trail_has_value(trail, constraint)) {
962     return true;
963   }
964   if (int_mset_contains(pos, constraint)) {
965     return true;
966   }
967   if (int_mset_contains(neg, constraint)) {
968     return true;
969   }
970   return false;
971 }
972 #endif
973 
974 static
constraint_get_value(const mcsat_trail_t * trail,const int_mset_t * pos,const int_mset_t * neg,variable_t constraint)975 bool constraint_get_value(const mcsat_trail_t* trail, const int_mset_t* pos, const int_mset_t* neg, variable_t constraint) {
976   if (trail_has_value(trail, constraint)) {
977     return trail_get_boolean_value(trail, constraint);
978   }
979   if (int_mset_contains(pos, constraint)) {
980     return true;
981   }
982   if (int_mset_contains(neg, constraint)) {
983     return false;
984   }
985   assert(false);
986   return false;
987 }
988 
nra_plugin_explain_conflict(nra_plugin_t * nra,const int_mset_t * pos,const int_mset_t * neg,const ivector_t * core,const ivector_t * lemma_reasons,ivector_t * conflict)989 void nra_plugin_explain_conflict(nra_plugin_t* nra, const int_mset_t* pos, const int_mset_t* neg,
990     const ivector_t* core, const ivector_t* lemma_reasons, ivector_t* conflict) {
991 
992   if (ctx_trace_enabled(nra->ctx, "nra::explain")) {
993     ctx_trace_printf(nra->ctx, "nra_plugin_explain_conflict()\n");
994     uint32_t i;
995     int_mset_t variables;
996     int_mset_construct(&variables, variable_null);
997     for (i = 0; i < core->size; ++ i) {
998       term_t core_i_t = variable_db_get_term(nra->ctx->var_db, core->data[i]);
999       nra_plugin_get_constraint_variables(nra, core_i_t, &variables);
1000       ctx_trace_printf(nra->ctx, "core[%u] = ", i);
1001       ctx_trace_term(nra->ctx, core_i_t);
1002     }
1003     ivector_t* variables_list = int_mset_get_list(&variables);
1004     for (i = 0; i < variables_list->size; ++ i) {
1005       variable_t var = variables_list->data[i];
1006       if (trail_has_value(nra->ctx->trail, var)) {
1007         const mcsat_value_t* v = trail_get_value(nra->ctx->trail, var);
1008         variable_db_print_variable(nra->ctx->var_db, var, ctx_trace_out(nra->ctx));
1009         ctx_trace_printf(nra->ctx, " -> ");
1010         mcsat_value_print(v, ctx_trace_out(nra->ctx));
1011         ctx_trace_printf(nra->ctx, "\n");
1012       }
1013     }
1014     int_mset_destruct(&variables);
1015   }
1016 
1017   // Create the map from variables to
1018   lp_projection_map_t projection_map;
1019   lp_projection_map_construct(&projection_map, nra);
1020 
1021   // Add all the polynomials
1022   uint32_t core_i;
1023   for (core_i = 0; core_i < core->size; ++ core_i) {
1024     variable_t constraint_var = core->data[core_i];
1025     assert(constraint_has_value(nra->ctx->trail, pos, neg, constraint_var));
1026     const poly_constraint_t* constraint = poly_constraint_db_get(nra->constraint_db, constraint_var);
1027     const lp_polynomial_t* p = poly_constraint_get_polynomial(constraint);
1028     lp_projection_map_add(&projection_map, p);
1029   }
1030 
1031   // Add all the top-level reasons for the conflict variable
1032   uint32_t lemma_reasons_i;
1033   for (lemma_reasons_i = 0; lemma_reasons_i < lemma_reasons->size; ++ lemma_reasons_i) {
1034     variable_t constraint_var = lemma_reasons->data[lemma_reasons_i];
1035     const poly_constraint_t* constraint = poly_constraint_db_get(nra->constraint_db, constraint_var);
1036     const lp_polynomial_t* p = poly_constraint_get_polynomial(constraint);
1037     lp_projection_map_add(&projection_map, p);
1038   }
1039 
1040   // Project
1041   lp_projection_map_project(&projection_map, conflict);
1042 
1043   // Add the core to the conflict
1044   for (core_i = 0; core_i < core->size; ++ core_i) {
1045     variable_t constraint_var = core->data[core_i];
1046     term_t constraint_term = variable_db_get_term(nra->ctx->var_db, constraint_var);
1047     assert(constraint_has_value(nra->ctx->trail, pos, neg, constraint_var));
1048     bool constraint_value = constraint_get_value(nra->ctx->trail, pos, neg, constraint_var);
1049     if (!constraint_value) {
1050       constraint_term = opposite_term(constraint_term);
1051     }
1052     ivector_push(conflict, constraint_term);
1053   }
1054 
1055   // Remove the projection map
1056   lp_projection_map_destruct(&projection_map);
1057 }
1058