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