Skip to content

Commit 716502c

Browse files
authored
Fix inversion of constraint bounds in conditional bounds presolve (#75)
Updating the constraint bounds based on minimal and maximal activity can sometimes cause the update constraint lower bounds to be greater than constraint upper bounds even if the constraint is feasible within tolerance. This PR fixes the constraint bounds to the same value if this happens. Authors: - Kumar Aatish (https://github.com/kaatish) Approvers: - Chris Maes (https://github.com/chris-maes) URL: #75
1 parent 057b27d commit 716502c

File tree

5 files changed

+54
-36
lines changed

5 files changed

+54
-36
lines changed

cpp/include/cuopt/linear_programming/mip/solver_settings.hpp

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -70,11 +70,12 @@ class mip_solver_settings_t {
7070
bool has_initial_solution() const;
7171

7272
struct tolerances_t {
73-
f_t absolute_tolerance = 1.0e-4;
74-
f_t relative_tolerance = 1.0e-6;
75-
f_t integrality_tolerance = 1.0e-5;
76-
f_t absolute_mip_gap = 1.0e-10;
77-
f_t relative_mip_gap = 1.0e-4;
73+
f_t presolve_absolute_tolerance = 1.0e-6;
74+
f_t absolute_tolerance = 1.0e-4;
75+
f_t relative_tolerance = 1.0e-6;
76+
f_t integrality_tolerance = 1.0e-5;
77+
f_t absolute_mip_gap = 1.0e-10;
78+
f_t relative_mip_gap = 1.0e-4;
7879
};
7980

8081
/**

cpp/src/mip/diversity/diversity_manager.cu

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -246,15 +246,15 @@ bool diversity_manager_t<i_t, f_t>::run_presolve(f_t time_limit)
246246
if (termination_criterion_t::NO_UPDATE != term_crit) {
247247
ls.constraint_prop.bounds_update.set_updated_bounds(*problem_ptr);
248248
trivial_presolve(*problem_ptr);
249-
if (!problem_ptr->empty) { check_bounds_sanity(*problem_ptr); }
249+
if (!problem_ptr->empty && !check_bounds_sanity(*problem_ptr)) { return false; }
250250
}
251251
if (!problem_ptr->empty) {
252252
// do the resizing no-matter what, bounds presolve might not change the bounds but initial
253253
// trivial presolve might have
254254
ls.constraint_prop.bounds_update.resize(*problem_ptr);
255255
ls.constraint_prop.conditional_bounds_update.update_constraint_bounds(
256256
*problem_ptr, ls.constraint_prop.bounds_update);
257-
check_bounds_sanity(*problem_ptr);
257+
if (!check_bounds_sanity(*problem_ptr)) { return false; }
258258
}
259259
stats.presolve_time = presolve_timer.elapsed_time();
260260
return true;

cpp/src/mip/presolve/bounds_presolve.cu

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@
2020

2121
#include <thrust/count.h>
2222
#include <thrust/extrema.h>
23+
#include <thrust/functional.h>
2324
#include <thrust/iterator/zip_iterator.h>
25+
#include <thrust/transform_reduce.h>
2426
#include <thrust/tuple.h>
2527
#include <utilities/copy_helpers.hpp>
2628
#include <utilities/device_utils.cuh>
@@ -350,18 +352,29 @@ void bound_presolve_t<i_t, f_t>::calc_and_set_updated_constraint_bounds(problem_
350352
{
351353
calculate_activity_on_problem_bounds(pb);
352354
353-
thrust::transform(pb.handle_ptr->get_thrust_policy(),
354-
upd.max_activity.begin(),
355-
upd.max_activity.end(),
356-
pb.constraint_upper_bounds.begin(),
357-
pb.constraint_upper_bounds.begin(),
358-
thrust::minimum<f_t>());
359-
thrust::transform(pb.handle_ptr->get_thrust_policy(),
360-
upd.min_activity.begin(),
361-
upd.min_activity.end(),
362-
pb.constraint_lower_bounds.begin(),
363-
pb.constraint_lower_bounds.begin(),
364-
thrust::maximum<f_t>());
355+
thrust::for_each(pb.handle_ptr->get_thrust_policy(),
356+
thrust::make_counting_iterator(0),
357+
thrust::make_counting_iterator(pb.n_constraints),
358+
[pb = pb.view(),
359+
min_act = make_span(upd.min_activity),
360+
max_act = make_span(upd.max_activity),
361+
cnst_lb = make_span(pb.constraint_lower_bounds),
362+
cnst_ub = make_span(pb.constraint_upper_bounds)] __device__(i_t idx) -> i_t {
363+
auto min_a = min_act[idx];
364+
auto max_a = max_act[idx];
365+
auto c_lb = cnst_lb[idx];
366+
auto c_ub = cnst_ub[idx];
367+
auto new_c_lb = max(c_lb, min_a);
368+
auto new_c_ub = min(c_ub, max_a);
369+
i_t infeas = check_infeasibility<i_t, f_t>(
370+
min_a, max_a, new_c_lb, new_c_ub, pb.tolerances.presolve_absolute_tolerance);
371+
if (!infeas && (new_c_lb > new_c_ub)) {
372+
new_c_lb = (new_c_lb + new_c_ub) / 2;
373+
new_c_ub = new_c_lb;
374+
}
375+
cnst_lb[idx] = new_c_lb;
376+
cnst_ub[idx] = new_c_ub;
377+
});
365378
}
366379
367380
#if MIP_INSTANTIATE_FLOAT

cpp/src/mip/presolve/bounds_update_helpers.cuh

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,12 @@ __global__ void calc_activity_kernel(typename problem_t<i_t, f_t>::view_t pb,
142142

143143
// Update bounds
144144

145+
template <typename i_t, typename f_t>
146+
inline __device__ bool check_infeasibility(f_t min_a, f_t max_a, f_t cnst_lb, f_t cnst_ub, f_t eps)
147+
{
148+
return (min_a > cnst_ub + eps) || (max_a < cnst_lb - eps);
149+
}
150+
145151
template <typename i_t, typename f_t>
146152
inline __device__ bool check_infeasibility(
147153
f_t min_a, f_t max_a, f_t cnst_lb, f_t cnst_ub, f_t abs_tol, f_t rel_tol)

cpp/src/mip/problem/problem_helpers.cuh

Lines changed: 15 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -248,42 +248,40 @@ static void check_csr_representation([[maybe_unused]] const rmm::device_uvector<
248248
}
249249
250250
template <typename i_t, typename f_t>
251-
static void check_var_bounds_sanity(const detail::problem_t<i_t, f_t>& problem)
251+
static bool check_var_bounds_sanity(const detail::problem_t<i_t, f_t>& problem)
252252
{
253253
bool crossing_bounds_detected =
254254
thrust::any_of(problem.handle_ptr->get_thrust_policy(),
255255
thrust::counting_iterator(0),
256256
thrust::counting_iterator((i_t)problem.variable_lower_bounds.size()),
257-
[lb = make_span(problem.variable_lower_bounds),
258-
ub = make_span(problem.variable_upper_bounds)] __device__(i_t index) {
259-
return lb[index] > ub[index];
257+
[tolerance = problem.tolerances.presolve_absolute_tolerance,
258+
lb = make_span(problem.variable_lower_bounds),
259+
ub = make_span(problem.variable_upper_bounds)] __device__(i_t index) {
260+
return (lb[index] > ub[index] + tolerance);
260261
});
261-
cuopt_expects(!crossing_bounds_detected,
262-
error_type_t::ValidationError,
263-
"There shouldn't be any crossing bounds in variable bounds.");
262+
return !crossing_bounds_detected;
264263
}
265264
266265
template <typename i_t, typename f_t>
267-
static void check_constraint_bounds_sanity(const detail::problem_t<i_t, f_t>& problem)
266+
static bool check_constraint_bounds_sanity(const detail::problem_t<i_t, f_t>& problem)
268267
{
269268
bool crossing_bounds_detected =
270269
thrust::any_of(problem.handle_ptr->get_thrust_policy(),
271270
thrust::counting_iterator(0),
272271
thrust::counting_iterator((i_t)problem.constraint_lower_bounds.size()),
273-
[lb = make_span(problem.constraint_lower_bounds),
274-
ub = make_span(problem.constraint_upper_bounds)] __device__(i_t index) {
275-
return lb[index] > ub[index];
272+
[tolerance = problem.tolerances.presolve_absolute_tolerance,
273+
lb = make_span(problem.constraint_lower_bounds),
274+
ub = make_span(problem.constraint_upper_bounds)] __device__(i_t index) {
275+
return (lb[index] > ub[index] + tolerance);
276276
});
277-
cuopt_expects(!crossing_bounds_detected,
278-
error_type_t::ValidationError,
279-
"There shouldn't be any crossing bounds in constraints bounds.");
277+
return !crossing_bounds_detected;
280278
}
281279
282280
template <typename i_t, typename f_t>
283-
static void check_bounds_sanity(const detail::problem_t<i_t, f_t>& problem)
281+
static bool check_bounds_sanity(const detail::problem_t<i_t, f_t>& problem)
284282
{
285-
check_var_bounds_sanity<i_t, f_t>(problem);
286-
check_constraint_bounds_sanity<i_t, f_t>(problem);
283+
return check_var_bounds_sanity<i_t, f_t>(problem) &&
284+
check_constraint_bounds_sanity<i_t, f_t>(problem);
287285
}
288286
289287
} // namespace cuopt::linear_programming::detail

0 commit comments

Comments
 (0)