-
Notifications
You must be signed in to change notification settings - Fork 222
Generate cuts before we have an optimal basic solution to the root relaxation #1822
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
e0fd56f
e57ba0f
969f6d4
e5477bb
cea4732
8a6fcc3
980780d
ae2e26b
d537e3b
15d0087
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -290,6 +290,7 @@ branch_and_bound_t<i_t, f_t>::branch_and_bound_t( | |
| solver_status_(mip_status_t::UNSET) | ||
| { | ||
| exploration_stats_.start_time = start_time; | ||
| clique_table_complete_.store(clique_table_ != nullptr, std::memory_order_relaxed); | ||
| #ifdef PRINT_CONSTRAINT_MATRIX | ||
| settings_.log.printf("A"); | ||
| original_problem_.A.print_matrix(); | ||
|
|
@@ -3033,9 +3034,20 @@ lp_status_t branch_and_bound_t<i_t, f_t>::solve_root_relaxation( | |
| basis_update_mpf_t<i_t, f_t>& basis_update, | ||
| std::vector<i_t>& basic_list, | ||
| std::vector<i_t>& nonbasic_list, | ||
| std::vector<f_t>& edge_norms) | ||
| std::vector<f_t>& edge_norms, | ||
| variable_bounds_t<i_t, f_t>& variable_bounds, | ||
| cut_pool_t<i_t, f_t>& cut_pool) | ||
| { | ||
| lp_status_t root_status; | ||
| i_t relaxation_cut_task_status = 0; | ||
| std::atomic<int> relaxation_cut_halt{0}; | ||
| std::atomic<bool> relaxation_cut_task_complete{false}; | ||
| bool relaxation_cut_task_started{false}; | ||
| f_t relaxation_cut_task_elapsed{0.0}; | ||
| method_t relaxation_cut_method{Unset}; | ||
| std::vector<f_t> relaxation_root_x; | ||
| std::vector<f_t> relaxation_root_y; | ||
| std::vector<f_t> relaxation_root_z; | ||
|
|
||
| // Launch a task for solving the root LP relaxation via dual simplex. | ||
| #pragma omp task default(shared) depend(out : root_status) priority(CUOPT_CRITICAL_TASK_PRIORITY) | ||
|
|
@@ -3085,6 +3097,57 @@ lp_status_t branch_and_bound_t<i_t, f_t>::solve_root_relaxation( | |
| root_crossover_soln_.y = crushed_root_y; | ||
| root_crossover_soln_.z = crushed_root_z; | ||
|
|
||
| if ((root_relax_solved_by == PDLP || root_relax_solved_by == Barrier) && | ||
| settings_.max_cut_passes > 0 && omp_get_num_threads() >= 3) { | ||
| relaxation_root_x = root_crossover_soln_.x; | ||
| relaxation_root_y = root_crossover_soln_.y; | ||
| relaxation_root_z = root_crossover_soln_.z; | ||
| relaxation_cut_method = root_relax_solved_by; | ||
| relaxation_cut_task_started = true; | ||
|
|
||
| #pragma omp task default(shared) depend(out : relaxation_cut_task_status) \ | ||
| priority(CUOPT_DEFAULT_TASK_PRIORITY) | ||
| { | ||
| const f_t cut_start_time = tic(); | ||
| auto cut_settings = settings_; | ||
| cut_settings.concurrent_halt = &relaxation_cut_halt; | ||
| // Consume a completed clique table without stopping or waiting for its producer. If it is | ||
| // still being built, leave it running and defer clique/zero-half cuts to the normal pass. | ||
| const bool clique_table_ready = clique_table_complete_.load(std::memory_order_acquire); | ||
| if (!clique_table_ready) { | ||
| cut_settings.clique_cuts = 0; | ||
| cut_settings.zero_half_cuts = 0; | ||
| } | ||
| cut_generation_t<i_t, f_t> relaxation_cut_generation( | ||
| cut_pool, | ||
| original_lp_, | ||
| cut_settings, | ||
| Arow_, | ||
| new_slacks_, | ||
| var_types_, | ||
| original_problem_, | ||
| probing_implied_bound_, | ||
| clique_table_ready ? clique_table_ : nullptr); | ||
| const bool feasible = | ||
| relaxation_cut_generation.generate_cuts(original_lp_, | ||
| cut_settings, | ||
| Arow_, | ||
| new_slacks_, | ||
| var_types_, | ||
| std::nullopt, | ||
| relaxation_root_x, | ||
| relaxation_root_y, | ||
| relaxation_root_z, | ||
| std::nullopt, | ||
| std::nullopt, | ||
| variable_bounds, | ||
| exploration_stats_.start_time); | ||
| relaxation_cut_task_elapsed = toc(cut_start_time); | ||
| relaxation_cut_task_status = feasible ? 1 : -1; | ||
| relaxation_cut_task_complete.store(true, std::memory_order_release); | ||
| } | ||
| } | ||
|
|
||
| // Call crossover on the crushed solution | ||
| auto root_crossover_settings = settings_; | ||
| root_crossover_settings.log.log = false; | ||
|
|
@@ -3098,6 +3161,7 @@ lp_status_t branch_and_bound_t<i_t, f_t>::solve_root_relaxation( | |
|
|
||
| // Check if crossover was stopped by dual simplex | ||
| if (crossover_status == crossover_status_t::OPTIMAL) { | ||
| if (relaxation_cut_task_started) { relaxation_cut_halt.store(1, std::memory_order_release); } | ||
| // Stop dual simplex and then wait it to finish | ||
| set_root_concurrent_halt(1); | ||
| #pragma omp taskwait depend(in : root_status) | ||
|
|
@@ -3157,16 +3221,32 @@ lp_status_t branch_and_bound_t<i_t, f_t>::solve_root_relaxation( | |
| } else { | ||
| // Wait for the dual simplex to finish (after telling PDLP/Barrier to stop) | ||
| #pragma omp taskwait depend(in : root_status) | ||
| if (relaxation_cut_task_started) { relaxation_cut_halt.store(1, std::memory_order_release); } | ||
| root_relax_solved_by = DualSimplex; | ||
| exploration_stats_.total_simplex_iters = root_relax_soln_.iterations; | ||
| } | ||
| } else { | ||
| // Wait for the dual simplex to finish (crossover do not produced a solution) | ||
| #pragma omp taskwait depend(in : root_status) | ||
| if (relaxation_cut_task_started) { relaxation_cut_halt.store(1, std::memory_order_release); } | ||
| root_relax_solved_by = DualSimplex; | ||
| exploration_stats_.total_simplex_iters = root_relax_soln_.iterations; | ||
| } | ||
|
|
||
| if (relaxation_cut_task_started) { | ||
| const bool relaxation_cut_task_interrupted = | ||
| !relaxation_cut_task_complete.load(std::memory_order_acquire); | ||
| #pragma omp taskwait depend(in : relaxation_cut_task_status) | ||
| const i_t generated_cuts = cut_pool.pool_size(); | ||
| settings_.log.printf( | ||
| "%s speculative root cut pass generated %d candidates in %.2f seconds%s%s\n", | ||
| method_to_string(relaxation_cut_method).c_str(), | ||
| generated_cuts, | ||
| relaxation_cut_task_elapsed, | ||
| relaxation_cut_task_interrupted ? " (stopped when the basis became available)" : "", | ||
| relaxation_cut_task_status < 0 ? " (separator reported infeasibility)" : ""); | ||
| } | ||
|
|
||
| is_root_solution_set = true; | ||
|
|
||
| return root_status; | ||
|
|
@@ -3191,7 +3271,8 @@ auto branch_and_bound_t<i_t, f_t>::do_cut_pass( | |
| f_t& last_objective, | ||
| f_t root_relax_objective, | ||
| i_t& cut_pool_size, | ||
| [[maybe_unused]] const std::vector<f_t>& saved_solution) -> cut_pass_action_t | ||
| [[maybe_unused]] const std::vector<f_t>& saved_solution, | ||
| cut_pass_mode_t mode) -> cut_pass_action_t | ||
| { | ||
| #ifdef PRINT_FRACTIONAL_INFO | ||
| settings_.log.printf("Found %d fractional variables on cut pass %d\n", num_fractional, cut_pass); | ||
|
|
@@ -3204,37 +3285,43 @@ auto branch_and_bound_t<i_t, f_t>::do_cut_pass( | |
| } | ||
| #endif | ||
|
|
||
| f_t cut_start_time = tic(); | ||
| bool problem_feasible = cut_generation.generate_cuts(original_lp_, | ||
| settings_, | ||
| Arow_, | ||
| new_slacks_, | ||
| var_types_, | ||
| basis_update, | ||
| root_relax_soln_.x, | ||
| root_relax_soln_.y, | ||
| root_relax_soln_.z, | ||
| basic_list, | ||
| nonbasic_list, | ||
| variable_bounds, | ||
| exploration_stats_.start_time); | ||
| if (!problem_feasible) { | ||
| if (settings_.heuristic_preemption_callback != nullptr) { | ||
| settings_.heuristic_preemption_callback(); | ||
| if (mode == cut_pass_mode_t::GENERATE_AND_APPLY) { | ||
| f_t cut_start_time = tic(); | ||
| bool problem_feasible = cut_generation.generate_cuts(original_lp_, | ||
| settings_, | ||
| Arow_, | ||
| new_slacks_, | ||
| var_types_, | ||
| std::ref(basis_update), | ||
| root_relax_soln_.x, | ||
| root_relax_soln_.y, | ||
| root_relax_soln_.z, | ||
| std::cref(basic_list), | ||
| std::cref(nonbasic_list), | ||
| variable_bounds, | ||
| exploration_stats_.start_time); | ||
| if (!problem_feasible) { | ||
| if (settings_.heuristic_preemption_callback != nullptr) { | ||
| settings_.heuristic_preemption_callback(); | ||
| } | ||
| solver_status_ = mip_status_t::INFEASIBLE; | ||
| return cut_pass_action_t::RETURN; | ||
| } | ||
| if (toc(exploration_stats_.start_time) >= settings_.time_limit) { | ||
| solver_status_ = mip_status_t::TIME_LIMIT; | ||
| set_final_solution(solution, root_objective_); | ||
| return cut_pass_action_t::RETURN; | ||
| } | ||
| f_t cut_generation_time = toc(cut_start_time); | ||
| if (cut_generation_time > 1.0) { | ||
| settings_.log.debug("Cut generation time %.2f seconds\n", cut_generation_time); | ||
| } | ||
|
|
||
| solver_status_ = mip_status_t::INFEASIBLE; | ||
| return cut_pass_action_t::RETURN; | ||
| } | ||
| if (toc(exploration_stats_.start_time) >= settings_.time_limit) { | ||
| solver_status_ = mip_status_t::TIME_LIMIT; | ||
| set_final_solution(solution, root_objective_); | ||
| return cut_pass_action_t::RETURN; | ||
| } | ||
| f_t cut_generation_time = toc(cut_start_time); | ||
| if (cut_generation_time > 1.0) { | ||
| settings_.log.debug("Cut generation time %.2f seconds\n", cut_generation_time); | ||
| } | ||
| // Score the cuts | ||
| f_t score_start_time = tic(); | ||
| cut_pool.score_cuts(root_relax_soln_.x); | ||
|
|
@@ -3245,7 +3332,14 @@ auto branch_and_bound_t<i_t, f_t>::do_cut_pass( | |
| std::vector<f_t> cut_rhs; | ||
| std::vector<cut_type_t> cut_types; | ||
| i_t num_cuts = cut_pool.get_best_cuts(cuts_to_add, cut_rhs, cut_types); | ||
| if (num_cuts == 0) { return cut_pass_action_t::BREAK; } | ||
| if (mode == cut_pass_mode_t::APPLY_EXISTING_POOL) { | ||
| settings_.log.printf("Retained and applying %d speculative root cuts to the basis solution\n", | ||
| num_cuts); | ||
| } | ||
| if (num_cuts == 0) { | ||
| if (mode == cut_pass_mode_t::APPLY_EXISTING_POOL) { cut_pool.clear(); } | ||
| return cut_pass_action_t::BREAK; | ||
| } | ||
| cut_info.record_cut_types(cut_types); | ||
| #ifdef PRINT_CUT_POOL_TYPES | ||
| cut_pool.print_cutpool_types(); | ||
|
|
@@ -3266,6 +3360,7 @@ auto branch_and_bound_t<i_t, f_t>::do_cut_pass( | |
| #ifdef CHECK_CUTS_AGAINST_SAVED_SOLUTION | ||
| verify_cuts_against_saved_solution(cuts_to_add, cut_rhs, saved_solution); | ||
| #endif | ||
| if (mode == cut_pass_mode_t::APPLY_EXISTING_POOL) { cut_pool.clear(); } | ||
| cut_pool_size = cut_pool.pool_size(); | ||
|
|
||
| // Resolve the LP with the new cuts | ||
|
|
@@ -3486,14 +3581,16 @@ auto branch_and_bound_t<i_t, f_t>::do_cut_pass( | |
| f_t change_in_objective = root_objective_ - last_objective; | ||
| const f_t factor = settings_.cut_change_threshold; | ||
| const f_t min_objective = 1e-3; | ||
| if (factor > 0.0 && | ||
| if (mode == cut_pass_mode_t::GENERATE_AND_APPLY && factor > 0.0 && | ||
| change_in_objective <= factor * std::max(min_objective, std::abs(root_relax_objective))) { | ||
| settings_.log.printf( | ||
| "Change in objective %.16e is less than 1e-3 of root relax objective %.16e\n", | ||
| change_in_objective, | ||
| root_relax_objective); | ||
| return cut_pass_action_t::BREAK; | ||
| } | ||
| // Pass 0 must update the baseline for the first ordinary cut pass, but it must not terminate the | ||
| // normal loop based on the speculative pass's objective movement. | ||
| last_objective = root_objective_; | ||
| return cut_pass_action_t::CONTINUE; | ||
| } | ||
|
|
@@ -3518,6 +3615,7 @@ mip_status_t branch_and_bound_t<i_t, f_t>::solve(mip_solution_t<i_t, f_t>& solut | |
|
|
||
| variable_bounds_t<i_t, f_t> variable_bounds( | ||
| original_lp_, settings_, var_types_, Arow_, new_slacks_); | ||
| cut_pool_t<i_t, f_t> cut_pool(original_lp_.num_cols, settings_); | ||
|
|
||
| if (guess_.size() != 0) { | ||
| raft::common::nvtx::range scope_guess("BB::check_initial_guess"); | ||
|
|
@@ -3547,6 +3645,7 @@ mip_status_t branch_and_bound_t<i_t, f_t>::solve(mip_solution_t<i_t, f_t>& solut | |
| if ((settings_.clique_cuts != 0 || settings_.zero_half_cuts != 0) && clique_table_ == nullptr && | ||
| omp_get_num_threads() >= CUOPT_MIP_CLIQUE_CUTS_REQUIRED_THREAD_COUNT) { | ||
| signal_extend_cliques_.store(false, std::memory_order_release); | ||
| clique_table_complete_.store(false, std::memory_order_release); | ||
| typename mip_solver_settings_t<i_t, f_t>::tolerances_t tolerances_for_clique{}; | ||
| tolerances_for_clique.presolve_absolute_tolerance = settings_.primal_tol; | ||
| tolerances_for_clique.absolute_tolerance = settings_.primal_tol; | ||
|
|
@@ -3560,8 +3659,12 @@ mip_status_t branch_and_bound_t<i_t, f_t>::solve(mip_solution_t<i_t, f_t>& solut | |
| { | ||
| user_problem_t<i_t, f_t> problem_copy = original_problem_; | ||
| timer_t timer(std::numeric_limits<double>::infinity()); | ||
| mip::find_initial_cliques( | ||
| problem_copy, tolerances_for_clique, clique_table_, timer, clique_signal); | ||
| mip::find_initial_cliques(problem_copy, | ||
| tolerances_for_clique, | ||
| clique_table_, | ||
| timer, | ||
| clique_signal, | ||
| &clique_table_complete_); | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -3609,7 +3712,9 @@ mip_status_t branch_and_bound_t<i_t, f_t>::solve(mip_solution_t<i_t, f_t>& solut | |
| basis_update, | ||
| basic_list, | ||
| nonbasic_list, | ||
| edge_norms_); | ||
| edge_norms_, | ||
| variable_bounds, | ||
| cut_pool); | ||
| } | ||
| settings_.log.printf("\n"); | ||
|
|
||
|
|
@@ -3710,7 +3815,6 @@ mip_status_t branch_and_bound_t<i_t, f_t>::solve(mip_solution_t<i_t, f_t>& solut | |
|
|
||
| if (num_fractional != 0 && settings_.max_cut_passes > 0) { print_table_header(); } | ||
|
|
||
| cut_pool_t<i_t, f_t> cut_pool(original_lp_.num_cols, settings_); | ||
| cut_generation_t<i_t, f_t> cut_generation(cut_pool, | ||
| original_lp_, | ||
| settings_, | ||
|
|
@@ -3719,8 +3823,9 @@ mip_status_t branch_and_bound_t<i_t, f_t>::solve(mip_solution_t<i_t, f_t>& solut | |
| var_types_, | ||
| original_problem_, | ||
| probing_implied_bound_, | ||
| clique_table_, | ||
| clique_signal); | ||
| nullptr, | ||
| clique_signal, | ||
| std::ref(clique_table_)); | ||
|
|
||
| std::vector<f_t> saved_solution; | ||
| #ifdef CHECK_CUTS_AGAINST_SAVED_SOLUTION | ||
|
|
@@ -3746,8 +3851,42 @@ mip_status_t branch_and_bound_t<i_t, f_t>::solve(mip_solution_t<i_t, f_t>& solut | |
| i_t cut_pool_size = 0; | ||
| lp_settings.concurrent_halt = settings_.concurrent_halt; | ||
| lp_settings.inside_mip = 2; | ||
| i_t first_normal_cut_pass = 0; | ||
|
|
||
| // Pass 0 consumes cuts completed from the PDLP/Barrier relaxation while the winning basis was | ||
| // being built. Score them against that basis solution and reoptimize before generating any | ||
| // basis-aware cuts. If cuts are applied, this replaces normal cut pass 0. | ||
| if (cut_pool.pool_size() > 0) { | ||
| cut_pass_action_t speculative_cut_action = do_cut_pass(-1, | ||
| solution, | ||
| num_fractional, | ||
| fractional, | ||
| cut_generation, | ||
| basis_update, | ||
| basic_list, | ||
| nonbasic_list, | ||
| variable_bounds, | ||
| cut_pool, | ||
| cut_info, | ||
| lp_settings, | ||
| original_rows, | ||
| last_upper_bound, | ||
| last_objective, | ||
| root_relax_objective, | ||
| cut_pool_size, | ||
| saved_solution, | ||
| cut_pass_mode_t::APPLY_EXISTING_POOL); | ||
| if (speculative_cut_action == cut_pass_action_t::RETURN) { | ||
| if (settings_.benchmark_info_ptr != nullptr) { | ||
| settings_.benchmark_info_ptr->cut_generation_time_sec = toc(cut_generation_start_time); | ||
| } | ||
| assert(solver_status_ != mip_status_t::UNSET); | ||
| return solver_status_; | ||
| } | ||
| if (speculative_cut_action == cut_pass_action_t::CONTINUE) { first_normal_cut_pass = 1; } | ||
| } | ||
|
Comment on lines
+3854
to
+3887
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win 🔎 Supported by static analysis🏁 Script executed: #!/bin/bash
# Description: Find every read of incumbent_.x / current_incumbent to check index provenance.
set -euo pipefail
echo "=== reads of incumbent_.x ==="
rg -n -C4 --type=cpp --type=cuda 'incumbent_\.x' cpp/src
echo "=== current_incumbent indexing ==="
rg -n -C6 --type=cpp 'current_incumbent\s*\[' cpp/src
echo "=== get_unfixed_integer_variables definition and callers ==="
ast-grep run --pattern 'void get_unfixed_integer_variables($$$) { $$$ }' --lang cpp cpp/src
rg -n -C6 --type=cpp 'get_unfixed_integer_variables\s*\(' cpp/src
echo "=== var_types_ resize sites (confirm CONTINUOUS for cut slacks) ==="
rg -n -C3 --type=cpp 'var_types_\.resize' cpp/srcRepository: NVIDIA/cuopt Length of output: 10088 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "=== cut-loop window ==="
sed -n '3840,3955p' cpp/src/branch_and_bound/branch_and_bound.cpp
echo "=== launch_root_heuristics and RINS path ==="
rg -n -C12 --type=cpp 'launch_root_heuristics|use_rins|current_incumbent' cpp/src/branch_and_bound/branch_and_bound.cpp
echo "=== integer-variable helper symbols ==="
rg -n -C8 --type=cpp 'get_unfixed|integer_list|current_sol\[j\]|current_incumbent\[j\]' cpp/src/branch_and_bound
echo "=== variable-type growth and slack initialization ==="
rg -n -C5 --type=cpp 'var_types_\.resize|new_slacks_|CONTINUOUS' cpp/src/branch_and_bound/branch_and_bound.cppRepository: NVIDIA/cuopt Length of output: 50368 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "=== root heuristic worker construction and recursive path ==="
sed -n '2960,3027p' cpp/src/branch_and_bound/branch_and_bound.cpp
sed -n '2654,2730p' cpp/src/branch_and_bound/branch_and_bound.cpp
sed -n '2368,2390p' cpp/src/branch_and_bound/branch_and_bound.cpp
echo "=== crush_primal_solution declarations and implementation ==="
rg -n -C8 --type=cpp --type=hpp --type=h 'crush_primal_solution\s*\(' cpp/src | head -160
echo "=== root heuristic worker dimensions ==="
rg -n -C8 --type=cpp 'create_submip_worker|leaf_solution\.x|leaf_problem\.lower|leaf_problem\.upper' cpp/src/branch_and_bound cpp/src | head -220Repository: NVIDIA/cuopt Length of output: 8076 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "=== crush_primal_solution definitions and call contracts ==="
rg -n -C12 'crush_primal_solution\s*\(' cpp/src | head -240
echo "=== sub-MIP worker initialization ==="
rg -n -C12 'create_submip_worker|leaf_solution\.x\s*=|leaf_problem\.lower\s*=|leaf_problem\.upper\s*=' cpp/src | head -240Repository: NVIDIA/cuopt Length of output: 36709 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "=== all crush_primal_solution bindings ==="
rg -n -C10 'crush_primal_solution' . --glob '!build/**' --glob '!dist/**' --glob '!node_modules/**' | head -320
echo "=== third-party presolver type and source files ==="
rg -n -C8 'class third_party_presolve_t|struct third_party_presolve_t|third_party_presolve_t' . --glob '!build/**' --glob '!dist/**' --glob '!node_modules/**' | head -240Repository: NVIDIA/cuopt Length of output: 50368 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "=== files defining third_party_presolve_t ==="
rg -l 'third_party_presolve_t' . --glob '!build/**' --glob '!dist/**' --glob '!node_modules/**'
echo "=== compact crush call sites outside branch_and_bound.cpp ==="
rg -n 'crush_primal_solution' cpp/include cpp/src cpp/tests 2>/dev/null | head -120Repository: NVIDIA/cuopt Length of output: 6440 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "=== third-party presolver crush implementations ==="
sed -n '1280,1385p' cpp/src/mip_heuristics/presolve/third_party_presolve.cpp
sed -n '125,160p' cpp/src/mip_heuristics/presolve/third_party_presolve.hpp
echo "=== sub-MIP presolve and initial-guess sequence ==="
sed -n '2305,2390p' cpp/src/branch_and_bound/branch_and_bound.cppRepository: NVIDIA/cuopt Length of output: 10992 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "=== third-party crush_primal_dual_solution implementation ==="
rg -n -C8 'crush_primal_dual_solution' cpp/src/mip_heuristics/presolve/third_party_presolve.cpp cpp/src/mip_heuristics/presolve/third_party_presolve.hpp
sed -n '1360,1465p' cpp/src/mip_heuristics/presolve/third_party_presolve.cppRepository: NVIDIA/cuopt Length of output: 12686 Re-crush the incumbent after the speculative pass. When the speculative pass adds columns, 🤖 Prompt for AI Agents |
||
|
|
||
| for (i_t cut_pass = 0; cut_pass < settings_.max_cut_passes; cut_pass++) { | ||
| for (i_t cut_pass = first_normal_cut_pass; cut_pass < settings_.max_cut_passes; cut_pass++) { | ||
| if (toc(exploration_stats_.start_time) >= settings_.time_limit) { | ||
| solver_status_ = mip_status_t::TIME_LIMIT; | ||
| set_final_solution(solution, root_objective_); | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Have you checked if other heuristics or diving is conflicting with this (i.e. thread count)?