From fc0106b24ddf92c36c5f21370c9873fbdfdde026 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Wed, 4 Mar 2026 07:11:01 -0800 Subject: [PATCH 1/3] bump --- cpp/src/mip_heuristics/feasibility_jump/fj_cpu.cu | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/src/mip_heuristics/feasibility_jump/fj_cpu.cu b/cpp/src/mip_heuristics/feasibility_jump/fj_cpu.cu index 4d567c9ecb..168065d32a 100644 --- a/cpp/src/mip_heuristics/feasibility_jump/fj_cpu.cu +++ b/cpp/src/mip_heuristics/feasibility_jump/fj_cpu.cu @@ -769,7 +769,7 @@ static void apply_move(fj_cpu_climber_t& fj_cpu, fj_cpu.h_assignment[var_idx] = new_val; cuopt_assert((check_variable_within_bounds(fj_cpu, var_idx, new_val)), - "assignment not within bounds"); + "1assignment 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; From 42267ddcc14f2ceb20553ecf5260ede4dd460a87 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Wed, 4 Mar 2026 07:11:59 -0800 Subject: [PATCH 2/3] fix cpufj bug --- .../feasibility_jump/feasibility_jump_kernels.cu | 7 +++++-- .../mip_heuristics/feasibility_jump/fj_cpu.cu | 16 +++++++++++++--- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/cpp/src/mip_heuristics/feasibility_jump/feasibility_jump_kernels.cu b/cpp/src/mip_heuristics/feasibility_jump/feasibility_jump_kernels.cu index ebbb761277..8c15c94ccb 100644 --- a/cpp/src/mip_heuristics/feasibility_jump/feasibility_jump_kernels.cu +++ b/cpp/src/mip_heuristics/feasibility_jump/feasibility_jump_kernels.cu @@ -590,9 +590,13 @@ __global__ void update_assignment_kernel(typename fj_t::climber_data_t // update the assignment and objective proper if (FIRST_THREAD) { f_t new_val = fj.incumbent_assignment[var_idx] + fj.jump_move_delta[var_idx]; - cuopt_assert(fj.pb.check_variable_within_bounds(var_idx, new_val), "assignment not within bounds"); + // clamping to err on the safe size - assert catches this + auto bounds = fj.pb.variable_bounds[var_idx]; + f_t lb = get_lower(bounds); + f_t ub = get_upper(bounds); + new_val = min(max(new_val, lb), ub); cuopt_assert(isfinite(new_val), "assignment is not finite"); if (fj.pb.is_integer_var(var_idx)) { @@ -617,7 +621,6 @@ __global__ void update_assignment_kernel(typename fj_t::climber_data_t } } - auto bounds = fj.pb.variable_bounds[var_idx]; i_t var_range = get_upper(bounds) - get_lower(bounds); double delta_rel_err = fabs(fj.jump_move_delta[var_idx]) / var_range; if (delta_rel_err < fj.settings->parameters.small_move_tabu_threshold) { diff --git a/cpp/src/mip_heuristics/feasibility_jump/fj_cpu.cu b/cpp/src/mip_heuristics/feasibility_jump/fj_cpu.cu index 168065d32a..8bfb603400 100644 --- a/cpp/src/mip_heuristics/feasibility_jump/fj_cpu.cu +++ b/cpp/src/mip_heuristics/feasibility_jump/fj_cpu.cu @@ -766,10 +766,13 @@ static void apply_move(fj_cpu_climber_t& fj_cpu, 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())); fj_cpu.h_assignment[var_idx] = new_val; cuopt_assert((check_variable_within_bounds(fj_cpu, var_idx, new_val)), - "1assignment not within bounds"); + "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; @@ -786,11 +789,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 +1024,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; From d07e2ae140a27ec9ac118608c75153128609f9f9 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Wed, 4 Mar 2026 09:42:09 -0800 Subject: [PATCH 3/3] ai review --- .../feasibility_jump_kernels.cu | 7 ++--- .../mip_heuristics/feasibility_jump/fj_cpu.cu | 28 ++++++++++--------- 2 files changed, 17 insertions(+), 18 deletions(-) diff --git a/cpp/src/mip_heuristics/feasibility_jump/feasibility_jump_kernels.cu b/cpp/src/mip_heuristics/feasibility_jump/feasibility_jump_kernels.cu index 8c15c94ccb..ebbb761277 100644 --- a/cpp/src/mip_heuristics/feasibility_jump/feasibility_jump_kernels.cu +++ b/cpp/src/mip_heuristics/feasibility_jump/feasibility_jump_kernels.cu @@ -590,13 +590,9 @@ __global__ void update_assignment_kernel(typename fj_t::climber_data_t // update the assignment and objective proper if (FIRST_THREAD) { f_t new_val = fj.incumbent_assignment[var_idx] + fj.jump_move_delta[var_idx]; + cuopt_assert(fj.pb.check_variable_within_bounds(var_idx, new_val), "assignment not within bounds"); - // clamping to err on the safe size - assert catches this - auto bounds = fj.pb.variable_bounds[var_idx]; - f_t lb = get_lower(bounds); - f_t ub = get_upper(bounds); - new_val = min(max(new_val, lb), ub); cuopt_assert(isfinite(new_val), "assignment is not finite"); if (fj.pb.is_integer_var(var_idx)) { @@ -621,6 +617,7 @@ __global__ void update_assignment_kernel(typename fj_t::climber_data_t } } + auto bounds = fj.pb.variable_bounds[var_idx]; i_t var_range = get_upper(bounds) - get_lower(bounds); double delta_rel_err = fabs(fj.jump_move_delta[var_idx]) / var_range; if (delta_rel_err < fj.settings->parameters.small_move_tabu_threshold) { diff --git a/cpp/src/mip_heuristics/feasibility_jump/fj_cpu.cu b/cpp/src/mip_heuristics/feasibility_jump/fj_cpu.cu index 8bfb603400..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,20 +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); - } - // 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())); 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()) {