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  * Support for constructing Mixed-integer Gomory cuts.
21  */
22 
23 #include "solvers/simplex/gomory_cuts.h"
24 #include "utils/memalloc.h"
25 
26 
27 /*
28  * Initialization: use the default size
29  * - nelem is 0
30  */
init_gomory_vector(gomory_vector_t * v)31 void init_gomory_vector(gomory_vector_t *v) {
32   uint32_t n;
33 
34   n = DEF_GOMORY_VECTOR_SIZE;
35   assert(n <= MAX_GOMORY_VECTOR_SIZE);
36 
37   v->tag = (uint8_t *) safe_malloc(n * sizeof(uint8_t));
38   v->var = (int32_t *) safe_malloc(n * sizeof(int32_t));
39   v->coeff = (rational_t *) safe_malloc(n * sizeof(rational_t));
40   v->bound = (rational_t *) safe_malloc(n * sizeof(rational_t));
41   v->nelems = 0;
42   v->size = n;
43 
44   q_init(&v->sum);
45   q_init(&v->fraction);
46   q_init(&v->ext);
47   q_init(&v->aux_fraction);
48   q_init(&v->aux_coeff);
49   q_init(&v->aux);
50 }
51 
52 
53 /*
54  * Reset: empty the vector, also reset all the rationals to 0
55  */
reset_gomory_vector(gomory_vector_t * v)56 void reset_gomory_vector(gomory_vector_t *v) {
57   uint32_t i, n;
58 
59   n = v->nelems;
60   for (i=0; i<n; i++) {
61     q_clear(v->coeff + i);
62     q_clear(v->bound + i);
63   }
64   v->nelems = 0;
65 
66   q_clear(&v->sum);
67   q_clear(&v->fraction);
68   q_clear(&v->ext);
69   q_clear(&v->aux_fraction);
70   q_clear(&v->aux_coeff);
71   q_clear(&v->aux);
72 }
73 
74 
75 /*
76  * Delete: free memory
77  */
delete_gomory_vector(gomory_vector_t * v)78 void delete_gomory_vector(gomory_vector_t *v) {
79   reset_gomory_vector(v);
80   safe_free(v->tag);
81   safe_free(v->var);
82   safe_free(v->coeff);
83   safe_free(v->bound);
84   v->tag = NULL;
85   v->var = NULL;
86   v->coeff = NULL;
87   v->bound = NULL;
88 }
89 
90 
91 /*
92  * Make the vector 50% larger
93  */
extend_gomory_vector(gomory_vector_t * v)94 static void extend_gomory_vector(gomory_vector_t *v) {
95   uint32_t n;
96 
97   n = v->size;
98   if (n >= MAX_GOMORY_VECTOR_SIZE) {
99     out_of_memory();
100   }
101   n += n>>1;
102   assert(n > v->size);
103 
104   v->tag = (uint8_t *) safe_realloc(v->tag, n * sizeof(uint8_t));
105   v->var = (int32_t *) safe_realloc(v->var, n * sizeof(int32_t));
106   v->coeff = (rational_t *) safe_realloc(v->coeff, n * sizeof(rational_t));
107   v->bound = (rational_t *) safe_realloc(v->bound, n * sizeof(rational_t));
108   v->size = n;
109 }
110 
111 
112 /*
113  * Add an element to the vector:
114  * - x = variable
115  * - a = coefficient
116  * - b = bound
117  * - is_int: true if x is an integer variable
118  * - is_lb:  true if b is a lower bound (i.e., x >= b)
119  */
gomory_vector_add_elem(gomory_vector_t * v,int32_t x,rational_t * a,rational_t * b,bool is_int,bool is_lb)120 void gomory_vector_add_elem(gomory_vector_t *v, int32_t x, rational_t *a, rational_t *b,
121 			    bool is_int, bool is_lb) {
122   uint32_t i;
123 
124   assert(!is_int || q_is_integer(b)); // if x is an integer, b must be too
125 
126   i = v->nelems;
127   if (i == v->size) {
128     extend_gomory_vector(v);
129   }
130   assert(i < v->size);
131 
132   v->tag[i] = gomory_tag(is_int, is_lb);
133   v->var[i] = x;
134   q_init(v->coeff + i);
135   q_set(v->coeff + i, a); // make a copy
136   q_init(v->bound + i);
137   q_set(v->bound + i, b);
138   v->nelems = i+1;
139 }
140 
141 
142 /*
143  * GOMORY CUT
144  */
145 
146 /*
147  * Store the fractional part of a into f
148  */
get_fraction(rational_t * f,const rational_t * a)149 static void get_fraction(rational_t *f, const rational_t *a) {
150   q_set(f, a);
151   q_floor(f);
152   q_sub(f, a);   // f is floor(a) - a
153   q_neg(f);
154 }
155 
156 /*
157  * Compute the sum of a_i * bound_i
158  * - store the result if v->sum
159  * - store the fractional part in v->fraction
160  * - store 1 - fractional part in v->ext
161  */
gomory_add_bounds(gomory_vector_t * v)162 static void gomory_add_bounds(gomory_vector_t *v) {
163   uint32_t i, n;
164 
165   q_clear(&v->sum);
166   n = v->nelems;
167   for (i=0; i<n; i++) {
168     q_addmul(&v->sum, v->coeff + i, v->bound + i);
169   }
170   get_fraction(&v->fraction, &v->sum);
171   q_set_one(&v->ext);
172   q_sub(&v->ext, &v->fraction);
173 }
174 
175 
176 /*
177  * Build the Gomory cut from vector v:
178  * - the cut is of the form (poly >= bound)
179  * - this function stores poly in buffer and bound in *bound
180  */
make_gomory_cut(gomory_vector_t * v,poly_buffer_t * buffer)181 bool make_gomory_cut(gomory_vector_t *v, poly_buffer_t *buffer) {
182   uint32_t i, n;
183   rational_t *f, *e, *f_i, *c_i;
184 
185   gomory_add_bounds(v);
186 
187   f = &v->fraction;
188   e = &v->ext;          // e is 1 - f
189   if (q_is_zero(f)) {
190     return false;
191   }
192 
193   f_i = &v->aux_fraction;
194   c_i = &v->aux_coeff;
195 
196   reset_poly_buffer(buffer);
197 
198   n = v->nelems;
199   for (i=0; i<n; i++) {
200     /*
201      * Coefficient for variable x_i
202      * - if x_i is an integer, that's either f_i or f_i - 1
203      * - otherwise, that's a_i.
204      */
205     if (gomory_var_is_int(v, i)) {
206       get_fraction(f_i, v->coeff + i);
207       q_set(c_i, f_i);
208       // if x_i has a lower bound and f_i > 1 - f then c_i := f_i - 1
209       // if x_i has an upper bound and f_i > f then c_i := f_i - 1
210       if ((gomory_bound_is_lb(v, i) && q_gt(f_i, e))  ||
211 	  (gomory_bound_is_ub(v, i) && q_gt(f_i, f))) {
212 	q_sub_one(c_i);
213       }
214     } else {
215       q_set(c_i, v->coeff + i);
216     }
217 
218     if (gomory_bound_is_lb(v, i)) {
219       // c_i * (x_i - l_i) with x_i >= l_i
220       if (q_is_pos(c_i)) {
221 	q_div(c_i, e);   // c_i/(1-f)
222       } else {
223 	q_neg(c_i);
224 	q_div(c_i, f);  // -c_i/f
225       }
226       assert(q_is_pos(c_i));
227     } else {
228       // c_i * (x_i - u_i) with x_i <= u_i
229       if (q_is_pos(c_i)) {
230 	q_neg(c_i);
231 	q_div(c_i, f);   // -c_i/f
232       } else {
233 	q_div(c_i, e);   //  c_i/(1-f)
234       }
235       assert(q_is_neg(c_i));
236     }
237 
238     /*
239      * Add c_i * (x_i - bound_i) to buffer
240      */
241     poly_buffer_add_monomial(buffer, v->var[i], c_i);
242     q_mul(c_i, v->bound + i);
243     poly_buffer_sub_const(buffer, c_i);
244   }
245 
246   poly_buffer_sub_one(buffer);
247   normalize_poly_buffer(buffer);
248 
249   return true;
250 }
251 
252 
253 
254