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  * TABLE OF ATOMS FOR ARITHMETIC SOLVERS
21  */
22 
23 /*
24  * The atoms can be of the form (x >= k) or (x <= k) or (x == k),
25  * where x is a variable in the arithmetic solver and k is a rational
26  * constant. Each atom is identified by an index in the table.
27  * The table uses hash consing and it supports removal of atoms for push/pop
28  * operations.
29  *
30  * The components of an atom are:
31  * - a 2bit tag to specify the atom type (>=, <=, or ==)
32  * - the variable x (30bits index)
33  * - the rational constant k
34  * - a boolean variable (mapped to the atom in the smt-core)
35  */
36 
37 #include "solvers/simplex/arith_atomtable.h"
38 #include "utils/memalloc.h"
39 
40 
41 /*
42  * Hash code for an atom, based on Jenkins's lookup3.c code
43  * - header = tag + variable index
44  * - bound = constant
45  */
46 #define rot(x,k) (((x)<<(k)) | ((x)>>(32-(k))))
47 
48 #define final(a,b,c)      \
49 {                         \
50   c ^= b; c -= rot(b,14); \
51   a ^= c; a -= rot(c,11); \
52   b ^= a; b -= rot(a,25); \
53   c ^= b; c -= rot(b,16); \
54   a ^= c; a -= rot(c,4);  \
55   b ^= a; b -= rot(a,14); \
56   c ^= b; c -= rot(b,24); \
57 }
58 
hash_arith_atom(uint32_t header,rational_t * bound)59 static uint32_t hash_arith_atom(uint32_t header, rational_t *bound) {
60   uint32_t a, b, c;
61 
62   q_hash_decompose(bound, &a, &b);
63   c = header + 0xdeadbeef;
64   final(a, b, c);
65   return c;
66 }
67 
68 
69 /*
70  * Initialize: use default sizes
71  */
init_arith_atomtable(arith_atomtable_t * table,smt_core_t * core)72 void init_arith_atomtable(arith_atomtable_t *table, smt_core_t *core) {
73   uint32_t n;
74 
75   n = DEF_ARITHATOMTABLE_SIZE;
76   assert(n < MAX_ARITHATOMTABLE_SIZE);
77 
78   table->size = n;
79   table->natoms = 0;
80   table->atoms = (arith_atom_t *) safe_malloc(n * sizeof(arith_atom_t));
81   table->mark = allocate_bitvector(n);
82 
83   table->core = core;
84   init_int_htbl(&table->htbl, 0);
85   q_init(&table->aux);
86 }
87 
88 
89 /*
90  * Make the table 50% larger
91  */
extend_arith_atomtable(arith_atomtable_t * table)92 static void extend_arith_atomtable(arith_atomtable_t *table) {
93   uint32_t n;
94 
95   n = table->size + 1;
96   n += n>>1;
97   if (n >= MAX_ARITHATOMTABLE_SIZE) {
98     out_of_memory();
99   }
100   assert(n > table->size);
101 
102   table->size = n;
103   table->atoms = (arith_atom_t *) safe_realloc(table->atoms, n * sizeof(arith_atom_t));
104   table->mark = extend_bitvector(table->mark, n);
105 }
106 
107 
108 
109 /*
110  * Create a new atom and attach it to a new boolean variable
111  * - header = atom header (var + tag)
112  * - bound = rational constant
113  */
new_arith_atom(arith_atomtable_t * table,uint32_t header,rational_t * bound)114 static int32_t new_arith_atom(arith_atomtable_t *table, uint32_t header, rational_t *bound) {
115   int32_t i;
116   bvar_t x;
117 
118   i = table->natoms;
119   if (i == table->size) {
120     extend_arith_atomtable(table);
121   }
122   assert(i < table->size);
123 
124   // new boolean variable
125   x = create_boolean_variable(table->core);
126   attach_atom_to_bvar(table->core, x, arithatom_idx2tagged_ptr(i));
127 
128   // initialize the atom descriptor
129   table->atoms[i].header = header;
130   table->atoms[i].boolvar = x;
131   q_init(&table->atoms[i].bound);
132   q_set(&table->atoms[i].bound, bound);
133 
134   // new atom is not assigned
135   clr_bit(table->mark, i);
136 
137   table->natoms ++;
138 
139   return i;
140 }
141 
142 
143 
144 /*
145  * Reset the table:
146  * - free all rationals
147  * - reset the hash table
148  */
reset_arith_atomtable(arith_atomtable_t * table)149 void reset_arith_atomtable(arith_atomtable_t *table) {
150   uint32_t i, n;
151 
152   n = table->natoms;
153   for (i=0; i<n; i++) {
154     q_clear(&table->atoms[i].bound);
155   }
156 
157   table->natoms = 0;
158   reset_int_htbl(&table->htbl);
159   q_clear(&table->aux);
160 }
161 
162 
163 /*
164  * Delete the table
165  */
delete_arith_atomtable(arith_atomtable_t * table)166 void delete_arith_atomtable(arith_atomtable_t *table) {
167   uint32_t i, n;
168 
169   n = table->natoms;
170   for (i=0; i<n; i++) {
171     q_clear(&table->atoms[i].bound);
172   }
173 
174   safe_free(table->atoms);
175   delete_bitvector(table->mark);
176 
177   table->atoms = NULL;
178   table->mark = NULL;
179 
180   delete_int_htbl(&table->htbl);
181   q_clear(&table->aux);
182 }
183 
184 
185 
186 /*
187  * Remove all atoms of index >= natoms
188  */
arith_atomtable_remove_atoms(arith_atomtable_t * table,uint32_t natoms)189 void arith_atomtable_remove_atoms(arith_atomtable_t *table, uint32_t natoms) {
190   uint32_t i, n, k;
191   arith_atom_t *a;
192 
193   assert(natoms <= table->natoms);
194 
195   a = table->atoms;
196   n = table->natoms;
197   for (i=natoms; i<n; i++) {
198     // remove i from the hash table
199     k = hash_arith_atom(a[i].header, &a[i].bound);
200     int_htbl_erase_record(&table->htbl, k, i);
201 
202     // free the rational constant
203     q_clear(&a[i].bound);
204   }
205 
206   table->natoms = natoms;
207 }
208 
209 
210 /*
211  * Get atom index for boolean var v
212  * - return -1 if there's no atom for v or the atom is not arithmetic
213  */
arith_atom_id_for_bvar(arith_atomtable_t * table,bvar_t v)214 int32_t arith_atom_id_for_bvar(arith_atomtable_t *table, bvar_t v) {
215   void *a;
216   int32_t id;
217 
218   a = bvar_atom(table->core, v);
219   if (a != NULL && atom_tag(a) == ARITH_ATM_TAG) {
220     id = arithatom_tagged_ptr2idx(a);
221     assert(boolvar_of_atom(arith_atom(table, id)) == v);
222     return id;
223   } else {
224     return -1;
225   }
226 }
227 
228 
229 /*
230  * Get atom descriptor for boolean variable v
231  * - return NULL if there's no atom for v or the atom is not arithmetic
232  */
arith_atom_for_bvar(arith_atomtable_t * table,bvar_t v)233 arith_atom_t *arith_atom_for_bvar(arith_atomtable_t *table, bvar_t v) {
234   void *a;
235   int32_t id;
236 
237   a = bvar_atom(table->core, v);
238   if (a != NULL && atom_tag(a) == ARITH_ATM_TAG) {
239     id = arithatom_tagged_ptr2idx(a);
240     assert(boolvar_of_atom(arith_atom(table, id)) == v);
241     return arith_atom(table, id);
242   } else {
243     return NULL;
244   }
245 }
246 
247 
248 
249 /*
250  * ATOM CONSTRUCTION
251  */
252 
253 /*
254  * Hobject for interfacing with int_hash_table
255  */
256 typedef struct arith_atom_hobj_s {
257   int_hobj_t m;
258   arith_atomtable_t *table;
259   rational_t *bound;
260   uint32_t header; // encodes var + tag
261 } arith_atom_hobj_t;
262 
263 
264 /*
265  * Methods for int_hobj_t
266  */
hash_arith_atm_hobj(arith_atom_hobj_t * o)267 static uint32_t hash_arith_atm_hobj(arith_atom_hobj_t *o) {
268   return hash_arith_atom(o->header, o->bound);
269 }
270 
eq_arith_atm_hobj(arith_atom_hobj_t * o,int32_t i)271 static bool eq_arith_atm_hobj(arith_atom_hobj_t *o, int32_t i) {
272   arith_atomtable_t *table;
273 
274   table = o->table;
275   assert(0 <= i && i < table->natoms);
276   return o->header == table->atoms[i].header && q_eq(o->bound, &table->atoms[i].bound);
277 }
278 
build_arith_atm_hobj(arith_atom_hobj_t * o)279 static int32_t build_arith_atm_hobj(arith_atom_hobj_t *o) {
280   return new_arith_atom(o->table, o->header, o->bound);
281 }
282 
283 /*
284  * EXPORTED FUNCTIONS
285  */
286 
287 /*
288  * Search for an atom (x op k) where op is one of GE_ATM, LE_ATM, EQ_ATM
289  * - return -1 if there's no such atom, otherwise, return the atom index
290  */
find_arith_atom(arith_atomtable_t * table,thvar_t x,arithatm_tag_t op,rational_t * k)291 int32_t find_arith_atom(arith_atomtable_t *table, thvar_t x, arithatm_tag_t op, rational_t *k) {
292   arith_atom_hobj_t arith_atom_hobj;
293 
294   arith_atom_hobj.m.hash = (hobj_hash_t) hash_arith_atm_hobj;
295   arith_atom_hobj.m.eq = (hobj_eq_t) eq_arith_atm_hobj;
296   arith_atom_hobj.m.build = (hobj_build_t) build_arith_atm_hobj;
297   arith_atom_hobj.table = table;
298   arith_atom_hobj.header = arithatom_mk_header(x, op);
299   arith_atom_hobj.bound = k;
300 
301   // int_htbl_find_obj returns -1 (NULL_VALUE) if the object is not found
302   return int_htbl_find_obj(&table->htbl, (int_hobj_t *) &arith_atom_hobj);
303 }
304 
305 
306 /*
307  * Search for the atom (x op k).  Create it if it's not already in the table.
308  */
get_arith_atom(arith_atomtable_t * table,thvar_t x,arithatm_tag_t op,rational_t * k,bool * new_atom)309 int32_t get_arith_atom(arith_atomtable_t *table, thvar_t x, arithatm_tag_t op, rational_t *k, bool *new_atom) {
310   uint32_t n;
311   int32_t i;
312   arith_atom_hobj_t arith_atom_hobj;
313 
314   arith_atom_hobj.m.hash = (hobj_hash_t) hash_arith_atm_hobj;
315   arith_atom_hobj.m.eq = (hobj_eq_t) eq_arith_atm_hobj;
316   arith_atom_hobj.m.build = (hobj_build_t) build_arith_atm_hobj;
317   arith_atom_hobj.table = table;
318   arith_atom_hobj.header = arithatom_mk_header(x, op);
319   arith_atom_hobj.bound = k;
320 
321   n = table->natoms;
322   i = int_htbl_get_obj(&table->htbl, (int_hobj_t *) &arith_atom_hobj);
323   *new_atom = table->natoms > n;
324 
325   return i;
326 }
327 
328 
329 
330 /*
331  * Variants: return a literal, create a new atom if needed
332  * - for ge_atom/le_atom, is_int indicates whether x is an integer variable:
333  *   if x is an integer, then k must also be an integer constant,
334  *   and we use the equivalence
335  *      (x <= k) <==> not (x >= k+1)
336  *   (so all integer atoms are of type GE_ATM)
337  *
338  * Returned atom index in *new_idx:
339  * - *new_idx = -1 if the atom was already present in the table
340  * - if a new atom is created, *new_idx is set to that atom's index
341  */
get_literal_for_eq_atom(arith_atomtable_t * table,thvar_t x,rational_t * k,int32_t * new_idx)342 literal_t get_literal_for_eq_atom(arith_atomtable_t *table, thvar_t x, rational_t *k, int32_t *new_idx) {
343   bool new_atom;
344   int32_t i;
345 
346   i = get_arith_atom(table, x, EQ_ATM, k, &new_atom);
347   if (new_atom) {
348     *new_idx = i;
349   } else {
350     *new_idx = -1;
351   }
352   return pos_lit(table->atoms[i].boolvar);
353 }
354 
get_literal_for_ge_atom(arith_atomtable_t * table,thvar_t x,bool is_int,rational_t * k,int32_t * new_idx)355 literal_t get_literal_for_ge_atom(arith_atomtable_t *table, thvar_t x, bool is_int, rational_t *k, int32_t *new_idx) {
356   bool new_atom;
357   int32_t i;
358 
359   assert(! is_int || q_is_integer(k));
360   i = get_arith_atom(table, x, GE_ATM, k, &new_atom);
361   if (new_atom) {
362     *new_idx = i;
363   } else {
364     *new_idx = -1;
365   }
366   return pos_lit(table->atoms[i].boolvar);
367 }
368 
get_literal_for_le_atom(arith_atomtable_t * table,thvar_t x,bool is_int,rational_t * k,int32_t * new_idx)369 literal_t get_literal_for_le_atom(arith_atomtable_t *table, thvar_t x, bool is_int, rational_t *k, int32_t *new_idx) {
370   bool new_atom;
371   int32_t i;
372 
373   if (is_int) {
374     assert(q_is_integer(k));
375 
376     // get atom (x >= k+1)
377     q_set(&table->aux, k);
378     q_add_one(&table->aux);
379     i = get_arith_atom(table, x, GE_ATM, &table->aux, &new_atom);
380     if (new_atom) {
381       *new_idx = i;
382     } else {
383       *new_idx = -1;
384     }
385     return neg_lit(table->atoms[i].boolvar);
386 
387   } else {
388     // get (x <= k)
389     i = get_arith_atom(table, x, LE_ATM, k, &new_atom);
390     if (new_atom) {
391       *new_idx = i;
392     } else {
393       *new_idx = -1;
394     }
395     return pos_lit(table->atoms[i].boolvar);
396   }
397 }
398