diff --git a/cpp/src/mip_heuristics/presolve/bounds_presolve.cu b/cpp/src/mip_heuristics/presolve/bounds_presolve.cu index 0a7c9de41..81717dbec 100644 --- a/cpp/src/mip_heuristics/presolve/bounds_presolve.cu +++ b/cpp/src/mip_heuristics/presolve/bounds_presolve.cu @@ -118,6 +118,8 @@ bool bound_presolve_t::calculate_bounds_update(problem_t& pb constexpr i_t zero = 0; constexpr auto n_threads = 256; + upd.candidate_bound_scale = + pb.tolerances.absolute_tolerance / context.settings.semi_continuous_big_m; upd.bounds_changed.set_value_async(zero, pb.handle_ptr->get_stream()); update_bounds_kernel <<get_stream()>>>(pb.view(), upd.view()); diff --git a/cpp/src/mip_heuristics/presolve/bounds_update_data.cu b/cpp/src/mip_heuristics/presolve/bounds_update_data.cu index 487549aa4..7314cd5c2 100644 --- a/cpp/src/mip_heuristics/presolve/bounds_update_data.cu +++ b/cpp/src/mip_heuristics/presolve/bounds_update_data.cu @@ -21,7 +21,8 @@ bounds_update_data_t::bounds_update_data_t(problem_t& proble ub(problem.n_variables, problem.handle_ptr->get_stream()), changed_constraints(problem.n_constraints, problem.handle_ptr->get_stream()), next_changed_constraints(problem.n_constraints, problem.handle_ptr->get_stream()), - changed_variables(problem.n_variables, problem.handle_ptr->get_stream()) + changed_variables(problem.n_variables, problem.handle_ptr->get_stream()), + candidate_bound_scale(f_t(0)) { } @@ -49,6 +50,7 @@ typename bounds_update_data_t::view_t bounds_update_data_t:: v.changed_constraints = make_span(changed_constraints); v.next_changed_constraints = make_span(next_changed_constraints); v.changed_variables = make_span(changed_variables); + v.candidate_bound_scale = candidate_bound_scale; return v; } diff --git a/cpp/src/mip_heuristics/presolve/bounds_update_data.cuh b/cpp/src/mip_heuristics/presolve/bounds_update_data.cuh index e8b5e8586..8d1eac478 100644 --- a/cpp/src/mip_heuristics/presolve/bounds_update_data.cuh +++ b/cpp/src/mip_heuristics/presolve/bounds_update_data.cuh @@ -24,6 +24,7 @@ struct bounds_update_data_t { rmm::device_uvector changed_constraints; rmm::device_uvector next_changed_constraints; rmm::device_uvector changed_variables; + f_t candidate_bound_scale; struct view_t { i_t* bounds_changed; @@ -34,6 +35,7 @@ struct bounds_update_data_t { raft::device_span changed_constraints; raft::device_span next_changed_constraints; raft::device_span changed_variables; + f_t candidate_bound_scale; }; bounds_update_data_t(problem_t& pb); diff --git a/cpp/src/mip_heuristics/presolve/bounds_update_helpers.cuh b/cpp/src/mip_heuristics/presolve/bounds_update_helpers.cuh index b3f2ac16c..074cd17a6 100644 --- a/cpp/src/mip_heuristics/presolve/bounds_update_helpers.cuh +++ b/cpp/src/mip_heuristics/presolve/bounds_update_helpers.cuh @@ -40,6 +40,14 @@ inline __device__ f_t update_ub(f_t curr_ub, f_t coeff, f_t delta_min_act, f_t d return min(curr_ub, comp_bnd); } +template +inline __device__ bool accept_candidate_bound_update(f_t bound, + f_t abs_tol, + f_t candidate_bound_scale) +{ + return abs(bound) * candidate_bound_scale <= abs_tol; +} + template __global__ void calc_activity_kernel(typename problem_t::view_t pb, typename bounds_update_data_t::view_t upd_0, @@ -185,10 +193,19 @@ inline __device__ thrust::pair update_bounds_per_cnst( } min_a -= (coeff < 0) ? coeff * thrust::get<1>(old_bnd) : coeff * thrust::get<0>(old_bnd); max_a -= (coeff > 0) ? coeff * thrust::get<1>(old_bnd) : coeff * thrust::get<0>(old_bnd); - auto delta_min_act = cnst_ub - min_a; - auto delta_max_act = cnst_lb - max_a; - thrust::get<0>(bnd) = update_lb(thrust::get<0>(bnd), coeff, delta_min_act, delta_max_act); - thrust::get<1>(bnd) = update_ub(thrust::get<1>(bnd), coeff, delta_min_act, delta_max_act); + auto delta_min_act = cnst_ub - min_a; + auto delta_max_act = cnst_lb - max_a; + auto new_lb = update_lb(thrust::get<0>(bnd), coeff, delta_min_act, delta_max_act); + auto new_ub = update_ub(thrust::get<1>(bnd), coeff, delta_min_act, delta_max_act); + + if (accept_candidate_bound_update( + new_lb, pb.tolerances.absolute_tolerance, upd.candidate_bound_scale)) { + thrust::get<0>(bnd) = new_lb; + } + if (accept_candidate_bound_update( + new_ub, pb.tolerances.absolute_tolerance, upd.candidate_bound_scale)) { + thrust::get<1>(bnd) = new_ub; + } return bnd; } diff --git a/cpp/src/mip_heuristics/presolve/multi_probe.cu b/cpp/src/mip_heuristics/presolve/multi_probe.cu index f798957e1..9ffed054e 100644 --- a/cpp/src/mip_heuristics/presolve/multi_probe.cu +++ b/cpp/src/mip_heuristics/presolve/multi_probe.cu @@ -141,6 +141,10 @@ bool multi_probe_t::calculate_bounds_update(problem_t& pb, constexpr i_t zero = 0; constexpr auto n_threads = 256; + upd_0.candidate_bound_scale = + pb.tolerances.absolute_tolerance / context.settings.semi_continuous_big_m; + upd_1.candidate_bound_scale = + pb.tolerances.absolute_tolerance / context.settings.semi_continuous_big_m; if (skip_0 && skip_1) { return false; } else if (skip_0) { diff --git a/cpp/src/mip_heuristics/solve.cu b/cpp/src/mip_heuristics/solve.cu index 2b64a7c68..ef474e7c1 100644 --- a/cpp/src/mip_heuristics/solve.cu +++ b/cpp/src/mip_heuristics/solve.cu @@ -336,6 +336,14 @@ mip_solution_t solve_mip_helper(optimization_problem_t& op_p { try { mip_solver_settings_t settings(settings_const); + cuopt_expects(std::isfinite(settings.tolerances.absolute_tolerance) && + settings.tolerances.absolute_tolerance > f_t(0) && + std::isfinite(settings.semi_continuous_big_m) && + settings.semi_continuous_big_m > settings.tolerances.absolute_tolerance, + error_type_t::ValidationError, + "mip_absolute_tolerance must be finite and strictly positive, and " + "mip_semi_continuous_big_m must be finite and greater than " + "mip_absolute_tolerance"); if (settings.presolver == presolver_t::Default || settings.presolver == presolver_t::PSLP) { if (settings.presolver == presolver_t::PSLP) { CUOPT_LOG_INFO(