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 * Term partitions for equality abstract domain
21 */
22
23 #include <assert.h>
24
25 #include "context/eq_abstraction.h"
26 #include "utils/memalloc.h"
27
28
29 /*
30 * Allocate a partition descriptor for n classes and m terms
31 */
alloc_epartition(uint32_t n,uint32_t m)32 static epartition_t *alloc_epartition(uint32_t n, uint32_t m) {
33 uint32_t size;
34 epartition_t *tmp;
35
36 size = n + m;
37 if (size >= EPARTITION_MAX_SIZE) {
38 out_of_memory();
39 }
40 tmp = (epartition_t *) safe_malloc(sizeof(epartition_t) + size * sizeof(term_t));
41 tmp->nclasses = n;
42 tmp->size = size;
43
44 return tmp;
45 }
46
47
48
49 /*
50 * Create a basic partition: two elements x and y in a single class
51 * - x and y must be distinct
52 */
basic_epartition(term_t x,term_t y)53 epartition_t *basic_epartition(term_t x, term_t y) {
54 epartition_t *tmp;
55
56 assert(x != y);
57 tmp = alloc_epartition(1, 2);
58 tmp->data[0] = x;
59 tmp->data[1] = y;
60 tmp->data[2] = NULL_TERM;
61
62 return tmp;
63 }
64
65
66
67 /*
68 * Initialize manager m
69 */
init_epartition_manager(epartition_manager_t * m)70 void init_epartition_manager(epartition_manager_t *m) {
71 uint32_t i, n;
72
73 n = EQABS_DEF_ESIZE;
74 assert(n < EQABS_MAX_ESIZE);
75
76 m->e_size = n;
77 m->nterms = 0;
78 m->label = (int32_t *) safe_malloc(n * sizeof(int32_t));
79 m->next = (term_t *) safe_malloc(n * sizeof(term_t));
80
81 // nothing stored: labels are all -1
82 for (i=0; i<n; i++) {
83 m->label[i] = -1;
84 }
85
86 n = EQABS_DEF_CSIZE;
87 assert(n < EQABS_MAX_CSIZE);
88 m->c_size = n;
89 m->nclasses = 0;
90 m->order = 0;
91 m->root = (term_t *) safe_malloc(n * sizeof(term_t));
92
93 n = EQABS_DEF_SCSIZE;
94 assert(n < EQABS_MAX_SCSIZE);
95 m->sc_size = n;
96 m->subclass = (int32_t *) safe_malloc(n * sizeof(int32_t));
97
98 // subclass[i] must be -1 for all i
99 for (i=0; i<n; i++) {
100 m->subclass[i] = -1;
101 }
102
103 init_ivector(&m->buffer, EQABS_BUFFER_SIZE);
104 m->empty = alloc_epartition(0, 0);
105 }
106
107
108 /*
109 * Delete all allocated memory
110 */
delete_epartition_manager(epartition_manager_t * m)111 void delete_epartition_manager(epartition_manager_t *m) {
112 safe_free(m->label);
113 safe_free(m->next);
114 safe_free(m->root);
115 safe_free(m->subclass);
116 delete_ivector(&m->buffer);
117 safe_free(m->empty);
118 m->label = NULL;
119 m->next = NULL;
120 m->root = NULL;
121 m->subclass = NULL;
122 m->empty = NULL;
123 }
124
125
126 /*
127 * Allocate a new class index. Make root array larger if necessary
128 */
get_class_index(epartition_manager_t * m)129 static int32_t get_class_index(epartition_manager_t *m) {
130 int32_t i;
131 uint32_t n;
132
133 i = m->nclasses;
134 n = m->c_size;
135 if (i == n) {
136 n += n >> 1; // make it 50% larger
137 if (n >= EQABS_MAX_CSIZE) {
138 out_of_memory();
139 }
140 m->root = (term_t *) safe_realloc(m->root, n * sizeof(term_t));
141 m->c_size = n;
142 }
143 assert(i < m->c_size);
144 m->nclasses = i+1;
145 return i;
146 }
147
148
149 /*
150 * Extend the label and next array: make them large enough to store term t
151 * - no change if the arrays are large enough already
152 */
resize_term_arrays(epartition_manager_t * m,term_t t)153 static void resize_term_arrays(epartition_manager_t *m, term_t t) {
154 uint32_t i, n;
155
156 assert(t >= 0);
157
158 n = m->e_size;
159 if (t >= n) {
160 // new_size = max(t+1, n+n/2)
161 n += n>>1;
162 if (t >= n) n = t+1;
163
164 if (n >= EQABS_MAX_ESIZE) {
165 out_of_memory();
166 }
167 m->label = (int32_t *) safe_realloc(m->label, n * sizeof(int32_t));
168 m->next = (term_t *) safe_realloc(m->next, n * sizeof(term_t));
169
170 for (i=m->e_size; i<n; i++) {
171 m->label[i] = -1;
172 }
173 m->e_size = n;
174 }
175 assert(t < m->e_size);
176 }
177
178
179 /*
180 * Make sure the subclass array is large enough for n subclasses
181 */
resize_subclass_array(epartition_manager_t * m,uint32_t n)182 static void resize_subclass_array(epartition_manager_t *m, uint32_t n) {
183 uint32_t i, k;
184
185 k = m->sc_size;
186 if (k < n) {
187 // new_size = max(n, k+k/2)
188 k += k>>1;
189 if (k < n) k = n;
190
191 if (k >= EQABS_MAX_SCSIZE) {
192 out_of_memory();
193 }
194 m->subclass = (int32_t *) safe_realloc(m->subclass, k * sizeof(int32_t));
195
196 // initialize all subclasses to -1
197 for (i=m->sc_size; i<k; i++) {
198 m->subclass[i] = -1;
199 }
200 m->sc_size = k;
201 }
202 assert(n <= m->sc_size);
203 }
204
205
206
207
208
209 /*
210 * CONSTRUCTION OF PARTITION OBJECTS
211 */
212
213
214 /*
215 * Convert the partition stored in m into an epartition object
216 * If a class c is marked (m->root[c] < 0) then it's skipped
217 * Side-effect: reset m, but does not reset the labels.
218 */
get_epartition(epartition_manager_t * m)219 static epartition_t *get_epartition(epartition_manager_t *m) {
220 uint32_t i, j, n;
221 term_t t, r;
222 epartition_t *tmp;
223
224 if (m->order == 0) {
225 // empty partition
226 assert(m->nterms == 0);
227 tmp = m->empty;
228 } else {
229 // build a partition object
230 tmp = alloc_epartition(m->order, m->nterms);
231 n = m->nclasses;
232 j = 0;
233 for (i=0; i<n; i++) {
234 r = m->root[i];
235 if (r >= 0) {
236 t = r;
237 do {
238 assert(j < tmp->size);
239 tmp->data[j] = t;
240 j ++;
241 t = m->next[t];
242 } while (t != r);
243 assert(j < tmp->size);
244 tmp->data[j] = NULL_TERM; // end marker
245 j ++;
246 }
247 }
248 assert(j == tmp->size);
249 }
250
251 m->nclasses = 0;
252 m->order = 0;
253 m->nterms = 0;
254
255 return tmp;
256 }
257
258
259 /*
260 * Clear the labels of all terms in p
261 */
epartition_clear_labels(epartition_manager_t * m,epartition_t * p)262 static void epartition_clear_labels(epartition_manager_t *m, epartition_t *p) {
263 uint32_t i, n;
264 term_t *q, t;
265
266 q = p->data;
267 n = p->size;
268 for (i=0; i<n; i++) {
269 t = q[i];
270 if (t >= 0) {
271 assert(t < m->e_size);
272 m->label[t] = -1;
273 }
274 }
275 }
276
277
278
279
280
281 /*
282 * MERGE OPERATIONS
283 */
284
285 /*
286 * Meet/merge operations construct a partition in m by merging classes
287 * In this mode, the label array stores the class of terms in the partition
288 * being constructed.
289 */
290
291
292 #ifndef NDEBUG
293
294 /*
295 * For debugging: check whether i is a good class index
296 */
epartition_good_class(epartition_manager_t * m,int32_t i)297 static bool epartition_good_class(epartition_manager_t *m, int32_t i) {
298 term_t r;
299
300 if (i<0 || i>= m->nclasses) return false;
301 r = m->root[i];
302 return 0 <= r && r < m->e_size && m->label[r] == i;
303 }
304
305 #endif
306
307
308 /*
309 * Read the class id for term t
310 * -1 means t is not in any class
311 */
epartition_class_of_term(epartition_manager_t * m,term_t t)312 static int32_t epartition_class_of_term(epartition_manager_t *m, term_t t) {
313 assert(t >= 0);
314 return (t < m->e_size) ? m->label[t] : -1;
315 }
316
317 /*
318 * Allocate a new class with unique element t
319 * - t must not be in any other class
320 * - return the new class index
321 */
epartition_start_class(epartition_manager_t * m,term_t t)322 static int32_t epartition_start_class(epartition_manager_t *m, term_t t) {
323 int32_t i;
324
325 resize_term_arrays(m, t);
326 assert(m->label[t] == -1);
327 i = get_class_index(m);
328 m->root[i] = t;
329 m->label[t] = i;
330 m->next[t] = t;
331 m->nterms ++;
332 m->order ++;
333
334 return i;
335 }
336
337
338 /*
339 * Add term t to an existing class i
340 * - t must not be in any class yet and t must be >= 0
341 */
epartition_add_to_class(epartition_manager_t * m,term_t t,int32_t i)342 static void epartition_add_to_class(epartition_manager_t *m, term_t t, int32_t i) {
343 term_t r;
344
345 assert(epartition_good_class(m, i));
346
347 resize_term_arrays(m, t);
348 assert(m->label[t] == -1);
349
350 m->label[t] = i;
351 r = m->root[i];
352 m->next[t] = m->next[r];
353 m->next[r] = t;
354 m->nterms ++;
355 }
356
357
358 /*
359 * Merge classes i and j (into class i)
360 * - relabel all elements in class j
361 * - merge the lists
362 * - mark that class j does not exist anymore
363 *
364 * NOTE: this could be inefficient if class j is large
365 * (could use a union-find structure for this)
366 */
epartition_merge_classes(epartition_manager_t * m,int32_t i,int32_t j)367 static void epartition_merge_classes(epartition_manager_t *m, int32_t i, int32_t j) {
368 term_t t, r, s;
369
370 assert(i != j && epartition_good_class(m, i) && epartition_good_class(m, j));
371
372 // fix the labels
373 r = m->root[j];
374 t = r;
375 do {
376 m->label[t] = i;
377 t = m->next[t];
378 } while (t != r);
379
380 // merge the lists by swapping next[root[i]] and next[root[j]]
381 s = m->root[i];
382 t = m->next[r];
383 m->next[r] = m->next[s];
384 m->next[s] = t;
385
386 // mark class j
387 m->root[j] = NULL_TERM;
388 m->order --;
389 }
390
391
392 /*
393 * Copy partition p into m, in preparation for a meet operation
394 */
epartition_init_for_meet(epartition_manager_t * m,epartition_t * p)395 void epartition_init_for_meet(epartition_manager_t *m, epartition_t *p) {
396 term_t *q, t;
397 uint32_t i, n;
398 int32_t c;
399
400 assert(m->nclasses == 0 && m->nterms == 0 && m->order == 0);
401 q = p->data;
402 n = p->nclasses;
403 for (i=0; i<n; i++) {
404 t = *q ++;
405 c = epartition_start_class(m, t);
406 // each class in p should have at least two elements
407 t = *q ++;
408 do {
409 epartition_add_to_class(m, t, c);
410 t = *q ++;
411 } while (t >= 0);
412 }
413 }
414
415
416 /*
417 * Compute the meet of partition in m and p:
418 * - for all terms t u in a class of p,
419 * merge the class of t and the class of u in m
420 */
epartition_meet(epartition_manager_t * m,epartition_t * p)421 void epartition_meet(epartition_manager_t *m, epartition_t *p) {
422 term_t *q, t;
423 uint32_t i, n;
424 int32_t c, d;
425
426 q = p->data;
427 n = p->nclasses;
428 for (i=0; i<n; i++) {
429 t = *q ++;
430 // first term: create a new class if t is not in m
431 c = epartition_class_of_term(m, t);
432 if (c < 0) {
433 c = epartition_start_class(m, t);
434 }
435
436 // for every other term t: either merge the class of t and c or add t to c
437 t = *q ++;
438 do {
439 d = epartition_class_of_term(m, t);
440 if (d < 0) {
441 epartition_add_to_class(m, t, c);
442 } else if (d != c) {
443 epartition_merge_classes(m, d, c);
444 c = d;
445 }
446 t = *q ++;
447 } while (t >= 0);
448 }
449 }
450
451
452
453 /*
454 * Convert the current partition (in a meet operation)
455 * to a partition object
456 */
epartition_get_meet(epartition_manager_t * m)457 epartition_t *epartition_get_meet(epartition_manager_t *m) {
458 epartition_t *tmp;
459
460 tmp = get_epartition(m);
461 epartition_clear_labels(m, tmp);
462
463 return tmp;
464 }
465
466
467 /*
468 * SPLIT OPERATIONS
469 */
470
471 /*
472 * Split/join operations construct a partition in m by splitting classes of m
473 * into smaller subclasses. In this mode, terms are not labeled by their
474 * class id, but the label array is used internally by epartition_join
475 */
476
477 /*
478 * Start a new class with unique element t
479 * - t must not be in any class yet
480 * - return the new class index
481 * - update order and nterms
482 */
epartition_start_join_class(epartition_manager_t * m,term_t t)483 static int32_t epartition_start_join_class(epartition_manager_t *m, term_t t) {
484 int32_t i;
485
486 resize_term_arrays(m,t);
487 i = get_class_index(m);
488 m->root[i] = t;
489 m->next[t] = t;
490 m->nterms ++;
491 m->order ++;
492
493 return i;
494 }
495
496 /*
497 * Add t to an existing class i
498 * - t must not be in any class yet
499 * - update nterms
500 */
epartition_add_to_join_class(epartition_manager_t * m,term_t t,int32_t i)501 static void epartition_add_to_join_class(epartition_manager_t *m, term_t t, int32_t i) {
502 term_t r;
503
504 resize_term_arrays(m, t);
505 r = m->root[i];
506 m->next[t] = m->next[r];
507 m->next[r] = t;
508 m->nterms ++;
509 }
510
511
512 /*
513 * For each term in class i of p, set label[t] to i in m
514 */
epartition_set_labels(epartition_manager_t * m,epartition_t * p)515 static void epartition_set_labels(epartition_manager_t *m, epartition_t *p) {
516 uint32_t i, n;
517 term_t *q, t;
518
519 q = p->data;
520 n = p->nclasses;
521 for (i=0; i<n; i++) {
522 t = * q++;
523 resize_term_arrays(m, t);
524 m->label[t] = i;
525 t = * q++;
526 do {
527 resize_term_arrays(m, t);
528 m->label[t] = i;
529 t = *q ++;
530 } while (t >= 0);
531 }
532 }
533
534
535 /*
536 * Add element t to subclass[i]
537 * - create a new subclass if needed
538 */
epartition_add_to_subclass(epartition_manager_t * m,term_t t,int32_t i)539 static void epartition_add_to_subclass(epartition_manager_t *m, term_t t, int32_t i) {
540 int32_t c;
541 term_t r;
542
543 c = m->subclass[i];
544 if (c < 0) {
545 c = get_class_index(m);
546 m->subclass[i] = c;
547 m->order ++;
548 m->root[c] = t;
549 m->next[t] = t;
550 } else {
551 r = m->root[c];
552 m->next[t] = m->next[r];
553 m->next[r] = t;
554 }
555 }
556
557
558 /*
559 * Follow list of terms starting with r
560 * - move every term into a separate subclass, based on its label
561 * - subclass[i] = all terms t in the list such that label[t] == i
562 * - terms such that label[t] = -1 are removed
563 * Requirement:
564 * - subclass array must be large enough
565 * - for each i subclass[i] must be -1
566 */
refine_class(epartition_manager_t * m,term_t r)567 static void refine_class(epartition_manager_t *m, term_t r) {
568 uint32_t i, n;
569 term_t t, nxt;
570 int32_t j;
571
572 n = m->nclasses;
573 t = r;
574 do {
575 j = m->label[t];
576 nxt = m->next[t];
577 if (j < 0) {
578 m->nterms --;
579 } else {
580 epartition_add_to_subclass(m, t, j);
581 }
582 t = nxt;
583 } while (t != r);
584
585 // clear the subclasses
586 for (i=n; i<m->nclasses; i++) {
587 t = m->root[i];
588 j = m->label[t];
589 assert(j >= 0 && m->subclass[j] == i);
590 m->subclass[j] = -1;
591 }
592 }
593
594
595 /*
596 * Remove the singleton classes from m
597 * - each class that gets removed is marked by setting root[i] to NULL_TERM
598 */
epartition_remove_singletons(epartition_manager_t * m)599 static void epartition_remove_singletons(epartition_manager_t *m) {
600 uint32_t i, n;
601 term_t r;
602
603 n = m->nclasses;
604 for (i=0; i<n; i++) {
605 r = m->root[i];
606 if (r >= 0 && m->next[r] == r) {
607 assert(m->nterms > 0 && m->order > 0);
608 m->root[i] = NULL_TERM;
609 m->label[r] = -1;
610 m->nterms --;
611 m->order --;
612 }
613 }
614 }
615
616
617
618 /*
619 * Copy partition p into m, in preparation for join
620 */
epartition_init_for_join(epartition_manager_t * m,epartition_t * p)621 void epartition_init_for_join(epartition_manager_t *m, epartition_t *p) {
622 term_t *q, t;
623 uint32_t i, n;
624 int32_t c;
625
626 assert(m->nclasses == 0 && m->nterms == 0 && m->order == 0);
627 q = p->data;
628 n = p->nclasses;
629 for (i=0; i<n; i++) {
630 t = *q ++;
631 c = epartition_start_join_class(m, t);
632 t = *q ++;
633 do {
634 epartition_add_to_join_class(m, t, c);
635 t = *q ++;
636 } while (t >= 0);
637 }
638 }
639
640
641 /*
642 * Refine the partition stored in m:
643 * - (t == u) are in the same class in the result
644 * iff they are in the same class in m and in p
645 */
epartition_join(epartition_manager_t * m,epartition_t * p)646 void epartition_join(epartition_manager_t *m, epartition_t *p) {
647 uint32_t i, n;
648 ivector_t *v;
649 term_t t;
650
651 resize_subclass_array(m, p->nclasses);
652 epartition_set_labels(m, p);
653
654 // copy all classes in the internal buffer
655 v = &m->buffer;
656 assert(v->size == 0);
657 n = m->nclasses;
658 for (i=0; i<n; i++) {
659 t = m->root[i];
660 if (t >= 0) {
661 ivector_push(v, t);
662 }
663 }
664 m->nclasses = 0;
665 m->order = 0;
666
667 // refine all classes in the buffer
668 n = v->size;
669 for (i=0; i<n; i++) {
670 refine_class(m, v->data[i]);
671 }
672
673 // cleanup: remove singleton classes and clear labels
674 ivector_reset(v);
675 epartition_remove_singletons(m);
676 epartition_clear_labels(m, p);
677 }
678
679
680 /*
681 * Construct the partition p stored in m
682 */
epartition_get_join(epartition_manager_t * m)683 epartition_t *epartition_get_join(epartition_manager_t *m) {
684 return get_epartition(m);
685 }
686