@@ -32,7 +32,6 @@ class create_cut {
32
32
unsigned m_inf_col; // a basis column which has to be an integer but has a non integral value
33
33
const row_strip<mpq>& m_row;
34
34
int_solver& lia;
35
- mpq m_lcm_den = { mpq (1 ) };
36
35
mpq m_f;
37
36
mpq m_one_minus_f;
38
37
mpq m_fj;
@@ -131,120 +130,7 @@ class create_cut {
131
130
// conflict 0 >= k where k is positive
132
131
return lia_move::conflict;
133
132
}
134
-
135
- void divd (mpq& r, mpq const & d) {
136
- r /= d;
137
- if (!r.is_int ())
138
- r = ceil (r);
139
- }
140
-
141
- bool can_divide_by (vector<std::pair<mpq, lpvar>> const & p, mpq const & d) {
142
- mpq lhs (0 ), rhs (m_k);
143
- mpq max_c (abs (m_k));
144
- for (auto const & [c, v] : p) {
145
- auto c1 = c;
146
- max_c = std::max (max_c, abs (c1));
147
- divd (c1, d);
148
- if (c1 == 0 )
149
- return false ;
150
- VERIFY (lia.get_value (v).y == 0 );
151
- lhs += c1 * lia.get_value (v).x ;
152
- }
153
- if (max_c == 1 )
154
- return false ;
155
- divd (rhs, d);
156
- return lhs < rhs;
157
- }
158
133
159
- void adjust_term_and_k_for_some_ints_case_gomory () {
160
- lp_assert (!m_t .is_empty ());
161
- // k = 1 + sum of m_t at bounds
162
- lar_term t = lia.lra .unfold_nested_subterms (m_t );
163
- auto pol = t.coeffs_as_vector ();
164
- m_t .clear ();
165
- if (pol.size () == 1 ) {
166
- TRACE (" gomory_cut_detail" , tout << " pol.size() is 1" << std::endl;);
167
- auto const & [a, v] = pol[0 ];
168
- lp_assert (is_int (v));
169
- if (a.is_pos ()) { // we have av >= k
170
- divd (m_k, a);
171
- m_t .add_monomial (mpq (1 ), v);
172
- }
173
- else {
174
- // av >= k
175
- // a/-a*v >= k / - a
176
- // -v >= k / - a
177
- // -v >= ceil(k / -a)
178
- divd (m_k, -a);
179
- m_t .add_monomial (-mpq (1 ), v);
180
- }
181
- }
182
- else {
183
- m_lcm_den = denominator (m_k);
184
- for (auto const & [c, v] : pol)
185
- m_lcm_den = lcm (m_lcm_den, denominator (c));
186
- lp_assert (m_lcm_den.is_pos ());
187
- TRACE (" gomory_cut_detail" , tout << " pol.size() > 1 den: " << m_lcm_den << std::endl;);
188
- if (!m_lcm_den.is_one ()) {
189
- // normalize coefficients of integer parameters to be integers.
190
- for (auto & [c,v]: pol) {
191
- c *= m_lcm_den;
192
- SASSERT (!is_int (v) || c.is_int ());
193
- }
194
- m_k *= m_lcm_den;
195
- }
196
- #if 0
197
- unsigned j = 0, i = 0;
198
- for (auto & [c, v] : pol) {
199
- if (lia.is_fixed(v)) {
200
- push_explanation(column_lower_bound_constraint(v));
201
- push_explanation(column_upper_bound_constraint(v));
202
- m_k -= c;
203
- IF_VERBOSE(0, verbose_stream() << "got fixed " << v << "\n");
204
- }
205
- else
206
- pol[j++] = pol[i];
207
- ++i;
208
- }
209
- pol.shrink(j);
210
- #endif
211
-
212
- // gcd reduction is loss-less:
213
- mpq g (1 );
214
- for (const auto & [c, v] : pol)
215
- g = gcd (g, c);
216
-
217
- if (g != 1 ) {
218
- for (auto & [c, v] : pol)
219
- c /= g;
220
- divd (m_k, g);
221
- }
222
-
223
- #if 0
224
- // TODO: create self-contained rounding mode to weaken cuts
225
- // whose cofficients are considered too large
226
- // (larger than bounds from the input)
227
- mpq min_c = abs(m_k);
228
- for (const auto & [c, v] : pol)
229
- min_c = std::min(min_c, abs(c));
230
-
231
- if (min_c > 1 && can_divide_by(pol, min_c)) {
232
- for (auto& [c, v] : pol)
233
- divd(c, min_c);
234
- divd(m_k, min_c);
235
- }
236
- #endif
237
-
238
- for (const auto & [c, v]: pol)
239
- m_t .add_monomial (c, v);
240
- VERIFY (m_t .size () > 0 );
241
- }
242
-
243
- TRACE (" gomory_cut_detail" , tout << " k = " << m_k << std::endl;);
244
- lp_assert (m_k.is_int ());
245
-
246
-
247
- }
248
134
249
135
std::string var_name (unsigned j) const {
250
136
return std::string (" x" ) + std::to_string (j);
@@ -359,12 +245,12 @@ class create_cut {
359
245
// gomory will be t >= k and the current solution has a property t < k
360
246
m_k = 1 ;
361
247
m_t .clear ();
362
- bool some_int_columns = false ;
363
248
mpq m_f = fractional_part (get_value (m_inf_col));
364
249
TRACE (" gomory_cut_detail" , tout << " m_f: " << m_f << " , " ;
365
250
tout << " 1 - m_f: " << 1 - m_f << " , get_value(m_inf_col).x - m_f = " << get_value (m_inf_col).x - m_f << " \n " ;);
366
251
lp_assert (m_f.is_pos () && (get_value (m_inf_col).x - m_f).is_int ());
367
252
253
+ bool some_int_columns = false ;
368
254
#if SMALL_CUTS
369
255
m_abs_max = 0 ;
370
256
for (const auto & p : m_row) {
@@ -408,13 +294,7 @@ class create_cut {
408
294
if (m_t .is_empty ())
409
295
return report_conflict_from_gomory_cut ();
410
296
if (some_int_columns)
411
- adjust_term_and_k_for_some_ints_case_gomory ();
412
- if (!lia.current_solution_is_inf_on_cut ()) {
413
- m_ex->clear ();
414
- m_t .clear ();
415
- m_k = 1 ;
416
- return lia_move::undef;
417
- }
297
+ simplify_inequality ();
418
298
lp_assert (lia.current_solution_is_inf_on_cut ()); // checks that indices are columns
419
299
TRACE (" gomory_cut" , print_linear_combination_of_column_indices_only (m_t .coeffs_as_vector (), tout << " gomory cut: " ); tout << " >= " << m_k << std::endl;);
420
300
TRACE (" gomory_cut_detail" , dump_cut_and_constraints_as_smt_lemma (tout);
@@ -423,6 +303,91 @@ class create_cut {
423
303
return lia_move::cut;
424
304
}
425
305
306
+ // TODO: use this also for HNF cuts?
307
+ mpq m_lcm_den = { mpq (1 ) };
308
+
309
+ void simplify_inequality () {
310
+
311
+ auto divd = [](mpq& r, mpq const & d) {
312
+ r /= d;
313
+ if (!r.is_int ())
314
+ r = ceil (r);
315
+ };
316
+ SASSERT (!lia.m_upper );
317
+ lp_assert (!m_t .is_empty ());
318
+ // k = 1 + sum of m_t at bounds
319
+ lar_term t = lia.lra .unfold_nested_subterms (m_t );
320
+ auto pol = t.coeffs_as_vector ();
321
+ m_t .clear ();
322
+ if (pol.size () == 1 ) {
323
+ TRACE (" gomory_cut_detail" , tout << " pol.size() is 1" << std::endl;);
324
+ auto const & [a, v] = pol[0 ];
325
+ lp_assert (is_int (v));
326
+ if (a.is_pos ()) { // we have av >= k
327
+ divd (m_k, a);
328
+ m_t .add_monomial (mpq (1 ), v);
329
+ }
330
+ else {
331
+ // av >= k
332
+ // a/-a*v >= k / - a
333
+ // -v >= k / - a
334
+ // -v >= ceil(k / -a)
335
+ divd (m_k, -a);
336
+ m_t .add_monomial (-mpq (1 ), v);
337
+ }
338
+ }
339
+ else {
340
+ m_lcm_den = denominator (m_k);
341
+ for (auto const & [c, v] : pol)
342
+ m_lcm_den = lcm (m_lcm_den, denominator (c));
343
+ lp_assert (m_lcm_den.is_pos ());
344
+ bool int_row = true ;
345
+ TRACE (" gomory_cut_detail" , tout << " pol.size() > 1 den: " << m_lcm_den << std::endl;);
346
+ if (!m_lcm_den.is_one ()) {
347
+ // normalize coefficients of integer parameters to be integers.
348
+ for (auto & [c,v]: pol) {
349
+ c *= m_lcm_den;
350
+ SASSERT (!is_int (v) || c.is_int ());
351
+ int_row &= is_int (v);
352
+ }
353
+ m_k *= m_lcm_den;
354
+ }
355
+ unsigned j = 0 , i = 0 ;
356
+ for (auto & [c, v] : pol) {
357
+ if (lia.is_fixed (v)) {
358
+ push_explanation (column_lower_bound_constraint (v));
359
+ push_explanation (column_upper_bound_constraint (v));
360
+ m_k -= c * lower_bound (v).x ;
361
+ }
362
+ else
363
+ pol[j++] = pol[i];
364
+ ++i;
365
+ }
366
+ pol.shrink (j);
367
+
368
+ // gcd reduction is loss-less:
369
+ mpq g (1 );
370
+ for (const auto & [c, v] : pol)
371
+ g = gcd (g, c);
372
+ if (!int_row)
373
+ g = gcd (g, m_k);
374
+
375
+ if (g != 1 ) {
376
+ for (auto & [c, v] : pol)
377
+ c /= g;
378
+ divd (m_k, g);
379
+ }
380
+
381
+ for (const auto & [c, v]: pol)
382
+ m_t .add_monomial (c, v);
383
+ VERIFY (m_t .size () > 0 );
384
+ }
385
+
386
+ TRACE (" gomory_cut_detail" , tout << " k = " << m_k << std::endl;);
387
+ lp_assert (m_k.is_int ());
388
+ }
389
+
390
+
426
391
create_cut (lar_term & t, mpq & k, explanation* ex, unsigned basic_inf_int_j, const row_strip<mpq>& row, int_solver& lia) :
427
392
m_t (t),
428
393
m_k (k),
0 commit comments