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