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