diff --git a/cpp/src/mip_heuristics/feasibility_jump/fj_cpu.cu b/cpp/src/mip_heuristics/feasibility_jump/fj_cpu.cu index 4d567c9ecb..b3bc0d688e 100644 --- a/cpp/src/mip_heuristics/feasibility_jump/fj_cpu.cu +++ b/cpp/src/mip_heuristics/feasibility_jump/fj_cpu.cu @@ -702,6 +702,21 @@ static void apply_move(fj_cpu_climber_t& fj_cpu, raft::random::PCGenerator rng(fj_cpu.settings.seed + fj_cpu.iterations, 0, 0); cuopt_assert(var_idx < fj_cpu.view.pb.n_variables, "variable index out of bounds"); + f_t old_val = fj_cpu.h_assignment[var_idx]; + f_t new_val = old_val + delta; + if (is_integer_var(fj_cpu, var_idx)) { + cuopt_assert(fj_cpu.view.pb.integer_equal(new_val, round(new_val)), "new_val is not integer"); + new_val = round(new_val); + } + // clamp to var bounds + new_val = std::min(std::max(new_val, get_lower(fj_cpu.h_var_bounds[var_idx].get())), + get_upper(fj_cpu.h_var_bounds[var_idx].get())); + delta = new_val - old_val; + cuopt_assert(isfinite(new_val), "assignment is not finite"); + cuopt_assert(isfinite(delta), "applied delta is not finite"); + cuopt_assert((check_variable_within_bounds(fj_cpu, var_idx, new_val)), + "assignment not within bounds"); + // Update the LHSs of all involved constraints. auto [offset_begin, offset_end] = reverse_range_for_var(fj_cpu, var_idx); @@ -761,17 +776,7 @@ static void apply_move(fj_cpu_climber_t& fj_cpu, } // update the assignment and objective proper - f_t new_val = fj_cpu.h_assignment[var_idx] + delta; - if (is_integer_var(fj_cpu, var_idx)) { - cuopt_assert(fj_cpu.view.pb.integer_equal(new_val, round(new_val)), "new_val is not integer"); - new_val = round(new_val); - } fj_cpu.h_assignment[var_idx] = new_val; - - cuopt_assert((check_variable_within_bounds(fj_cpu, var_idx, new_val)), - "assignment not within bounds"); - cuopt_assert(isfinite(new_val), "assignment is not finite"); - fj_cpu.h_incumbent_objective += fj_cpu.h_obj_coeffs[var_idx] * delta; if (fj_cpu.h_incumbent_objective < fj_cpu.h_best_objective && fj_cpu.violated_constraints.empty()) { @@ -786,11 +791,11 @@ static void apply_move(fj_cpu_climber_t& fj_cpu, fj_cpu.iterations_since_best = 0; CUOPT_LOG_TRACE("%sCPUFJ: new best objective: %g", fj_cpu.log_prefix.c_str(), - fj_cpu.pb_ptr->get_user_obj_from_solver_obj(fj_cpu.h_best_objective)); + fj_cpu.pb_ptr->get_user_obj_from_solver_obj(fj_cpu.h_incumbent_objective)); if (fj_cpu.improvement_callback) { double current_work_units = fj_cpu.work_units_elapsed.load(std::memory_order_acquire); fj_cpu.improvement_callback( - fj_cpu.h_best_objective, fj_cpu.h_assignment, current_work_units); + fj_cpu.h_incumbent_objective, fj_cpu.h_assignment, current_work_units); } fj_cpu.feasible_found = true; } @@ -1021,6 +1026,13 @@ static void recompute_lhs(fj_cpu_climber_t& fj_cpu) CPUFJ_NVTX_RANGE("CPUFJ::recompute_lhs"); cuopt_assert(fj_cpu.h_lhs.size() == fj_cpu.view.pb.n_constraints, "h_lhs size mismatch"); + // clamp to var bounds - defensive; apply_move should already have clamped appropriately + for (i_t var_idx = 0; var_idx < fj_cpu.view.pb.n_variables; ++var_idx) { + fj_cpu.h_assignment[var_idx] = std::min( + std::max(fj_cpu.h_assignment[var_idx].get(), get_lower(fj_cpu.h_var_bounds[var_idx].get())), + get_upper(fj_cpu.h_var_bounds[var_idx].get())); + } + fj_cpu.violated_constraints.clear(); fj_cpu.satisfied_constraints.clear(); fj_cpu.total_violations = 0;