@@ -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