@@ -401,24 +401,177 @@ class create_cut {
401
401
m_f (fractional_part(get_value(basic_inf_int_j).x)),
402
402
m_one_minus_f (1 - m_f) {}
403
403
404
- };
404
+ };
405
405
406
- lia_move gomory::cut (lar_term & t, mpq & k, explanation* ex, unsigned basic_inf_int_j, const row_strip<mpq>& row) {
407
- create_cut cc (t, k, ex, basic_inf_int_j, row, lia);
408
- return cc.cut ();
409
- }
406
+ lia_move gomory::cut (lar_term & t, mpq & k, explanation* ex, unsigned basic_inf_int_j, const row_strip<mpq>& row) {
407
+ create_cut cc (t, k, ex, basic_inf_int_j, row, lia);
408
+ return cc.cut ();
409
+ }
410
410
411
- lia_move gomory::get_cut (lpvar j) {
412
- unsigned r = lia.row_of_basic_column (j);
413
- const row_strip<mpq>& row = lra.get_row (r);
414
- SASSERT (lra.row_is_correct (r));
415
- SASSERT (lia.is_gomory_cut_target (j));
416
- lia.m_upper = false ;
417
- lia.m_cut_vars .push_back (j);
418
- return cut (lia.m_t , lia.m_k , lia.m_ex , j, row);
419
- }
411
+ bool gomory::is_gomory_cut_target (lpvar k) {
412
+ SASSERT (lia.is_base (k));
413
+ // All non base variables must be at their bounds and assigned to rationals (that is, infinitesimals are not allowed).
414
+ const row_strip<mpq>& row = lra.get_row (lia.row_of_basic_column (k));
415
+ unsigned j;
416
+ for (const auto & p : row) {
417
+ j = p.var ();
418
+ if ( k != j && (!lia.at_bound (j) || lia.get_value (j).y != 0 )) {
419
+ TRACE (" gomory_cut" , tout << " row is not gomory cut target:\n " ;
420
+ lia.display_column (tout, j);
421
+ tout << " infinitesimal: " << !(lia.get_value (j).y ==0 ) << " \n " ;);
422
+ return false ;
423
+ }
424
+ }
425
+ return true ;
426
+ }
427
+
428
+
429
+ lia_move gomory::get_cut (lpvar j) {
430
+ unsigned r = lia.row_of_basic_column (j);
431
+ const row_strip<mpq>& row = lra.get_row (r);
432
+ SASSERT (lra.row_is_correct (r));
433
+ SASSERT (is_gomory_cut_target (j));
434
+ lia.m_upper = false ;
435
+ return cut (lia.m_t , lia.m_k , lia.m_ex , j, row);
436
+ }
437
+
438
+ // return the minimal distance from the variable value to an integer
439
+ mpq get_gomory_score (const int_solver& lia, lpvar j) {
440
+ const mpq& val = lia.get_value (j).x ;
441
+ auto l = val - floor (val);
442
+ if (l <= mpq (1 , 2 ))
443
+ return l;
444
+ return mpq (1 ) - l;
445
+ }
446
+
447
+ unsigned_vector gomory::gomory_select_int_infeasible_vars (unsigned num_cuts) {
448
+ std::list<lpvar> sorted_vars;
449
+ std::unordered_map<lpvar, mpq> score;
450
+ for (lpvar j : lra.r_basis ()) {
451
+ if (!lia.column_is_int_inf (j) || !is_gomory_cut_target (j))
452
+ continue ;
453
+ SASSERT (!lia.is_fixed (j));
454
+ sorted_vars.push_back (j);
455
+ score[j] = get_gomory_score (lia, j);
456
+ }
457
+ // prefer the variables with the values close to integers
458
+ sorted_vars.sort ([&](lpvar j, lpvar k) {
459
+ auto diff = score[j] - score[k];
460
+ if (diff.is_neg ())
461
+ return true ;
462
+ if (diff.is_pos ())
463
+ return false ;
464
+ return lra.usage_in_terms (j) > lra.usage_in_terms (k);
465
+ });
466
+ unsigned_vector ret;
467
+ unsigned n = static_cast <unsigned >(sorted_vars.size ());
468
+
469
+ while (num_cuts-- && n > 0 ) {
470
+ unsigned k = lia.random () % n;
471
+
472
+ double k_ratio = k / (double ) n;
473
+ k_ratio *= k_ratio*k_ratio; // square k_ratio to make it smaller
474
+ k = static_cast <unsigned >(std::floor (k_ratio * n));
475
+ // these operations move k to the beginning of the indices range
476
+ SASSERT (0 <= k && k < n);
477
+ auto it = sorted_vars.begin ();
478
+ while (k--) it++;
479
+
480
+ ret.push_back (*it);
481
+ sorted_vars.erase (it);
482
+ n--;
483
+ }
484
+ return ret;
485
+ }
486
+
487
+
488
+ lia_move gomory::get_gomory_cuts (unsigned num_cuts) {
489
+ struct ex { explanation m_ex; lar_term m_term; mpq m_k; bool m_is_upper; };
490
+ unsigned_vector columns_for_cuts = gomory_select_int_infeasible_vars (num_cuts);
491
+
492
+ vector<ex> cuts;
493
+
494
+ for (unsigned j : columns_for_cuts) {
495
+ lia.m_ex ->clear ();
496
+ lia.m_t .clear ();
497
+ lia.m_k .reset ();
498
+ auto r = get_cut (j);
499
+ if (r != lia_move::cut)
500
+ continue ;
501
+ cuts.push_back ({ *lia.m_ex , lia.m_t , lia.m_k , lia.is_upper () });
502
+ if (lia.settings ().get_cancel_flag ())
503
+ return lia_move::undef;
504
+ }
420
505
506
+ auto is_small_cut = [&](ex const & cut) {
507
+ return all_of (cut.m_term , [&](auto ci) { return ci.coeff ().is_small (); });
508
+ };
509
+
510
+ auto add_cut = [&](ex const & cut) {
511
+ u_dependency* dep = nullptr ;
512
+ for (auto c : cut.m_ex )
513
+ dep = lra.join_deps (lra.dep_manager ().mk_leaf (c.ci ()), dep);
514
+ lp::lpvar term_index = lra.add_term (cut.m_term .coeffs_as_vector (), UINT_MAX);
515
+ term_index = lra.map_term_index_to_column_index (term_index);
516
+ lra.update_column_type_and_bound (term_index,
517
+ cut.m_is_upper ? lp::lconstraint_kind::LE : lp::lconstraint_kind::GE,
518
+ cut.m_k , dep);
519
+ };
520
+
521
+ auto _check_feasible = [&](void ) {
522
+ lra.find_feasible_solution ();
523
+ if (!lra.is_feasible () && !lia.settings ().get_cancel_flag ()) {
524
+ lra.get_infeasibility_explanation (*lia.m_ex );
525
+ return false ;
526
+ }
527
+ return true ;
528
+ };
421
529
422
- gomory::gomory (int_solver& lia): lia(lia), lra(lia.lra) { }
530
+ bool has_small = false , has_large = false ;
531
+
532
+ for (auto const & cut : cuts) {
533
+ if (!is_small_cut (cut)) {
534
+ has_large = true ;
535
+ continue ;
536
+ }
537
+ has_small = true ;
538
+ add_cut (cut);
539
+ }
540
+
541
+ if (has_large) {
542
+ lra.push ();
543
+
544
+ for (auto const & cut : cuts)
545
+ if (!is_small_cut (cut))
546
+ add_cut (cut);
423
547
548
+ bool feas = _check_feasible ();
549
+ lra.pop (1 );
550
+
551
+ if (lia.settings ().get_cancel_flag ())
552
+ return lia_move::undef;
553
+
554
+ if (!feas)
555
+ return lia_move::conflict;
556
+ }
557
+
558
+ if (!_check_feasible ())
559
+ return lia_move::conflict;
560
+
561
+
562
+ lia.m_ex ->clear ();
563
+ lia.m_t .clear ();
564
+ lia.m_k .reset ();
565
+ if (!lia.has_inf_int ())
566
+ return lia_move::sat;
567
+
568
+ if (has_small || has_large)
569
+ return lia_move::continue_with_check;
570
+
571
+ lra.move_non_basic_columns_to_bounds ();
572
+ return lia_move::undef;
573
+ }
574
+
575
+
576
+ gomory::gomory (int_solver& lia): lia(lia), lra(lia.lra) { }
424
577
}
0 commit comments