Skip to content

Commit d61b196

Browse files
authored
Fix variable bound violation in CPUFJ moves (#930)
## Issue Authors: - Alice Boucher (https://github.com/aliceb-nv) Approvers: - Rajesh Gandham (https://github.com/rg20) URL: #930
1 parent 0aa1b31 commit d61b196

1 file changed

Lines changed: 24 additions & 12 deletions

File tree

  • cpp/src/mip_heuristics/feasibility_jump

cpp/src/mip_heuristics/feasibility_jump/fj_cpu.cu

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -702,6 +702,21 @@ static void apply_move(fj_cpu_climber_t<i_t, f_t>& fj_cpu,
702702
raft::random::PCGenerator rng(fj_cpu.settings.seed + fj_cpu.iterations, 0, 0);
703703

704704
cuopt_assert(var_idx < fj_cpu.view.pb.n_variables, "variable index out of bounds");
705+
f_t old_val = fj_cpu.h_assignment[var_idx];
706+
f_t new_val = old_val + delta;
707+
if (is_integer_var<i_t, f_t>(fj_cpu, var_idx)) {
708+
cuopt_assert(fj_cpu.view.pb.integer_equal(new_val, round(new_val)), "new_val is not integer");
709+
new_val = round(new_val);
710+
}
711+
// clamp to var bounds
712+
new_val = std::min(std::max(new_val, get_lower(fj_cpu.h_var_bounds[var_idx].get())),
713+
get_upper(fj_cpu.h_var_bounds[var_idx].get()));
714+
delta = new_val - old_val;
715+
cuopt_assert(isfinite(new_val), "assignment is not finite");
716+
cuopt_assert(isfinite(delta), "applied delta is not finite");
717+
cuopt_assert((check_variable_within_bounds<i_t, f_t>(fj_cpu, var_idx, new_val)),
718+
"assignment not within bounds");
719+
705720
// Update the LHSs of all involved constraints.
706721
auto [offset_begin, offset_end] = reverse_range_for_var<i_t, f_t>(fj_cpu, var_idx);
707722

@@ -761,17 +776,7 @@ static void apply_move(fj_cpu_climber_t<i_t, f_t>& fj_cpu,
761776
}
762777

763778
// update the assignment and objective proper
764-
f_t new_val = fj_cpu.h_assignment[var_idx] + delta;
765-
if (is_integer_var<i_t, f_t>(fj_cpu, var_idx)) {
766-
cuopt_assert(fj_cpu.view.pb.integer_equal(new_val, round(new_val)), "new_val is not integer");
767-
new_val = round(new_val);
768-
}
769779
fj_cpu.h_assignment[var_idx] = new_val;
770-
771-
cuopt_assert((check_variable_within_bounds<i_t, f_t>(fj_cpu, var_idx, new_val)),
772-
"assignment not within bounds");
773-
cuopt_assert(isfinite(new_val), "assignment is not finite");
774-
775780
fj_cpu.h_incumbent_objective += fj_cpu.h_obj_coeffs[var_idx] * delta;
776781
if (fj_cpu.h_incumbent_objective < fj_cpu.h_best_objective &&
777782
fj_cpu.violated_constraints.empty()) {
@@ -786,11 +791,11 @@ static void apply_move(fj_cpu_climber_t<i_t, f_t>& fj_cpu,
786791
fj_cpu.iterations_since_best = 0;
787792
CUOPT_LOG_TRACE("%sCPUFJ: new best objective: %g",
788793
fj_cpu.log_prefix.c_str(),
789-
fj_cpu.pb_ptr->get_user_obj_from_solver_obj(fj_cpu.h_best_objective));
794+
fj_cpu.pb_ptr->get_user_obj_from_solver_obj(fj_cpu.h_incumbent_objective));
790795
if (fj_cpu.improvement_callback) {
791796
double current_work_units = fj_cpu.work_units_elapsed.load(std::memory_order_acquire);
792797
fj_cpu.improvement_callback(
793-
fj_cpu.h_best_objective, fj_cpu.h_assignment, current_work_units);
798+
fj_cpu.h_incumbent_objective, fj_cpu.h_assignment, current_work_units);
794799
}
795800
fj_cpu.feasible_found = true;
796801
}
@@ -1021,6 +1026,13 @@ static void recompute_lhs(fj_cpu_climber_t<i_t, f_t>& fj_cpu)
10211026
CPUFJ_NVTX_RANGE("CPUFJ::recompute_lhs");
10221027
cuopt_assert(fj_cpu.h_lhs.size() == fj_cpu.view.pb.n_constraints, "h_lhs size mismatch");
10231028

1029+
// clamp to var bounds - defensive; apply_move should already have clamped appropriately
1030+
for (i_t var_idx = 0; var_idx < fj_cpu.view.pb.n_variables; ++var_idx) {
1031+
fj_cpu.h_assignment[var_idx] = std::min(
1032+
std::max(fj_cpu.h_assignment[var_idx].get(), get_lower(fj_cpu.h_var_bounds[var_idx].get())),
1033+
get_upper(fj_cpu.h_var_bounds[var_idx].get()));
1034+
}
1035+
10241036
fj_cpu.violated_constraints.clear();
10251037
fj_cpu.satisfied_constraints.clear();
10261038
fj_cpu.total_violations = 0;

0 commit comments

Comments
 (0)