diff --git a/benchmarks/linear_programming/cuopt/mip_test_instances.hpp b/benchmarks/linear_programming/cuopt/mip_test_instances.hpp index ac7c673590..22be74dadc 100644 --- a/benchmarks/linear_programming/cuopt/mip_test_instances.hpp +++ b/benchmarks/linear_programming/cuopt/mip_test_instances.hpp @@ -17,175 +17,11 @@ #pragma once #include #include -std::vector instances = {"30n20b8.mps", - "50v-10.mps", - "CMS750_4.mps", - "academictimetablesmall.mps", - "air05.mps", - "app1-1.mps", - "app1-2.mps", - "assign1-5-8.mps", - "atlanta-ip.mps", - "bab2.mps", - "bab6.mps", - "beasleyC3.mps", - "binkar10_1.mps", - "blp-ar98.mps", - "blp-ic98.mps", - "bppc4-08.mps", - "brazil3.mps", - "cmflsp50-24-8-8.mps", - "co-100.mps", - "cod105.mps", - "comp07-2idx.mps", - "comp21-2idx.mps", - "csched007.mps", - "csched008.mps", - "cvs16r128-89.mps", - "dano3_3.mps", - "decomp2.mps", - "drayage-100-23.mps", - "drayage-25-23.mps", - "eil33-2.mps", - "eilA101-2.mps", - "exp-1-500-5-5.mps", - "fast0507.mps", - "fastxgemm-n2r6s0t2.mps", - "fiball.mps", - "gen-ip002.mps", - "germanrr.mps", - "glass4.mps", - "graph20-20-1rand.mps", - "graphdraw-domain.mps", - "h80x6320d.mps", - "highschool1-aigio.mps", - "hypothyroid-k1.mps", - "icir97_tension.mps", - "irish-electricity.mps", - "istanbul-no-cutoff.mps", - "k1mushroom.mps", - "lectsched-5-obj.mps", - "leo1.mps", - "leo2.mps", - "lotsize.mps", - "mad.mps", - "map10.mps", - "map16715-04.mps", - "markshare2.mps", - "markshare_4_0.mps", - "mas74.mps", - "mc11.mps", - "mcsched.mps", - "mik-250-20-75-4.mps", - "momentum1.mps", - "mushroom-best.mps", - "mzzv11.mps", - "mzzv42z.mps", - "n2seq36q.mps", - "n3div36.mps", - "neos-1171448.mps", - "neos-1171737.mps", - "neos-1354092.mps", - "neos-1445765.mps", - "neos-1456979.mps", - "neos-1582420.mps", - "neos-2657525-crna.mps", - "neos-2746589-doon.mps", - "neos-3024952-loue.mps", - "neos-3046615-murg.mps", - "neos-3216931-puriri.mps", - "neos-3402294-bobin.mps", - "neos-3656078-kumeu.mps", - "neos-3754480-nidda.mps", - "neos-4300652-rahue.mps", - "neos-4338804-snowy.mps", - "neos-4387871-tavua.mps", - "neos-4413714-turia.mps", - "neos-4532248-waihi.mps", - "neos-4722843-widden.mps", - "neos-4738912-atrato.mps", - "neos-4763324-toguru.mps", - "neos-4954672-berkel.mps", - "neos-5049753-cuanza.mps", - "neos-5093327-huahum.mps", - "neos-5107597-kakapo.mps", - "neos-5114902-kasavu.mps", - "neos-5188808-nattai.mps", - "neos-5195221-niemur.mps", - "neos-662469.mps", - "neos-787933.mps", - "neos-848589.mps", - "neos-860300.mps", - "neos-911970.mps", - "neos-933966.mps", - "neos-950242.mps", - "neos17.mps", - "neos5.mps", - "net12.mps", - "netdiversion.mps", - "nexp-150-20-8-5.mps", - "ns1644855.mps", - "ns1760995.mps", - "ns1830653.mps", - "nursesched-medium-hint03.mps", - "nursesched-sprint02.mps", - "opm2-z10-s4.mps", - "pg.mps", - "physiciansched3-3.mps", - "piperout-08.mps", - "piperout-27.mps", - "pk1.mps", - "qap10.mps", - "radiationm18-12-05.mps", - "radiationm40-10-02.mps", - "rail01.mps", - "rail02.mps", - "rail507.mps", - "ran14x18-disj-8.mps", - "rmatr100-p10.mps", - "rmatr200-p5.mps", - "rocI-4-11.mps", - "rocII-5-11.mps", - "rococoB10-011000.mps", - "rococoC10-001000.mps", - "roi2alpha3n4.mps", - "roi5alpha10n8.mps", - "roll3000.mps", - "s100.mps", - "s250r10.mps", - "satellites2-40.mps", - "satellites2-60-fs.mps", - "savsched1.mps", - "sct2.mps", - "seymour.mps", - "seymour1.mps", - "sing326.mps", - "sing44.mps", - "sorrell3.mps", - "sp97ar.mps", - "sp98ar.mps", - "splice1k1.mps", - "square41.mps", - "square47.mps", - "supportcase10.mps", - "supportcase12.mps", - "supportcase18.mps", - "supportcase26.mps", - "supportcase33.mps", - "supportcase40.mps", - "supportcase42.mps", - "supportcase6.mps", - "supportcase7.mps", - "swath1.mps", - "swath3.mps", - "tbfp-network.mps", - "thor50dday.mps", - "timtab1.mps", - "tr12-30.mps", - "traininstance2.mps", - "traininstance6.mps", - "trento1.mps", - "uccase12.mps", - "uct-subprob.mps", - "unitcal_7.mps", - "var-smallemery-m6j6.mps"}; +std::vector instances = {"supportcase26_presolved.mps", + "supportcase26_presolved_2.mps", + "supportcase26_presolved_3.mps", + "supportcase26_presolved_4.mps", + "supportcase26_presolved_5.mps", + "supportcase26_presolved_6.mps", + "supportcase26_presolved_7.mps", + "supportcase26_presolved_8.mps"}; diff --git a/benchmarks/linear_programming/cuopt/run_mip.cpp b/benchmarks/linear_programming/cuopt/run_mip.cpp index 41dbd9aa37..f6b30e72ce 100644 --- a/benchmarks/linear_programming/cuopt/run_mip.cpp +++ b/benchmarks/linear_programming/cuopt/run_mip.cpp @@ -210,6 +210,7 @@ int run_single_file(std::string file_path, settings.log_to_console = log_to_console; settings.tolerances.relative_tolerance = 1e-12; settings.tolerances.absolute_tolerance = 1e-6; + settings.presolve = true; cuopt::linear_programming::benchmark_info_t benchmark_info; settings.benchmark_info_ptr = &benchmark_info; auto start_run_solver = std::chrono::high_resolution_clock::now(); diff --git a/cpp/include/cuopt/linear_programming/mip/solver_settings.hpp b/cpp/include/cuopt/linear_programming/mip/solver_settings.hpp index 4d4d29eaf9..1750f2a03e 100644 --- a/cpp/include/cuopt/linear_programming/mip/solver_settings.hpp +++ b/cpp/include/cuopt/linear_programming/mip/solver_settings.hpp @@ -72,8 +72,8 @@ class mip_solver_settings_t { struct tolerances_t { f_t presolve_absolute_tolerance = 1.0e-6; - f_t absolute_tolerance = 1.0e-4; - f_t relative_tolerance = 1.0e-6; + f_t absolute_tolerance = 1.0e-6; + f_t relative_tolerance = 1.0e-12; f_t integrality_tolerance = 1.0e-5; f_t absolute_mip_gap = 1.0e-10; f_t relative_mip_gap = 1.0e-4; diff --git a/cpp/include/cuopt/logger.hpp b/cpp/include/cuopt/logger.hpp index 5fb42b62d3..f8f4e200e0 100644 --- a/cpp/include/cuopt/logger.hpp +++ b/cpp/include/cuopt/logger.hpp @@ -78,7 +78,7 @@ inline rapids_logger::logger& default_logger() logger_.set_pattern(default_pattern()); #endif logger_.set_level(default_level()); - logger_.flush_on(rapids_logger::level_enum::info); + logger_.flush_on(rapids_logger::level_enum::debug); return logger_; }(); @@ -100,7 +100,7 @@ inline void reset_default_logger() default_logger().set_pattern(default_pattern()); #endif default_logger().set_level(default_level()); - default_logger().flush_on(rapids_logger::level_enum::info); + default_logger().flush_on(rapids_logger::level_enum::debug); } } // namespace cuopt diff --git a/cpp/src/dual_simplex/bound_flipping_ratio_test.cpp b/cpp/src/dual_simplex/bound_flipping_ratio_test.cpp index 11753cbcb7..1a513f4934 100644 --- a/cpp/src/dual_simplex/bound_flipping_ratio_test.cpp +++ b/cpp/src/dual_simplex/bound_flipping_ratio_test.cpp @@ -93,6 +93,11 @@ i_t bound_flipping_ratio_test_t::single_pass(i_t start, } step_length = min_val; nonbasic_entering = candidate; + // this should be temporary, find root causes where the candidate is not filled + if (nonbasic_entering == -1) { + // -1,-2 and -3 are reserved for other things + return -4; + } const i_t j = entering_index = nonbasic_list_[nonbasic_entering]; constexpr bool verbose = false; @@ -137,6 +142,7 @@ i_t bound_flipping_ratio_test_t::compute_step_length(f_t& step_length, i_t k_idx = single_pass( 0, num_breakpoints, indicies, ratios, slope, step_length, nonbasic_entering, entering_index); + if (k_idx == -4) { return -4; } bool continue_search = k_idx >= 0 && num_breakpoints > 1 && slope > 0.0; if (!continue_search) { if constexpr (0) { diff --git a/cpp/src/dual_simplex/branch_and_bound.cpp b/cpp/src/dual_simplex/branch_and_bound.cpp index 898f0a85ef..2986de0184 100644 --- a/cpp/src/dual_simplex/branch_and_bound.cpp +++ b/cpp/src/dual_simplex/branch_and_bound.cpp @@ -409,9 +409,12 @@ mip_status_t branch_and_bound_t::solve(mip_solution_t& solut assert(root_vstatus.size() == original_lp.num_cols); if (root_status == lp_status_t::INFEASIBLE) { settings.log.printf("MIP Infeasible\n"); - if (settings.heuristic_preemption_callback != nullptr) { - settings.heuristic_preemption_callback(); - } + // FIXME: rarely dual simplex detects infeasible whereas it is feasible. + // to add a small safety net, check if there is a primal solution already. + // Uncomment this if the issue with cost266-UUE is resolved + // if (settings.heuristic_preemption_callback != nullptr) { + // settings.heuristic_preemption_callback(); + // } return mip_status_t::INFEASIBLE; } if (root_status == lp_status_t::UNBOUNDED) { @@ -434,8 +437,16 @@ mip_status_t branch_and_bound_t::solve(mip_solution_t& solut if (settings.set_simplex_solution_callback != nullptr) { std::vector original_x; uncrush_primal_solution(original_problem, original_lp, root_relax_soln.x, original_x); - settings.set_simplex_solution_callback(original_x, - compute_user_objective(original_lp, root_objective)); + std::vector original_dual; + std::vector original_z; + uncrush_dual_solution(original_problem, + original_lp, + root_relax_soln.y, + root_relax_soln.z, + original_dual, + original_z); + settings.set_simplex_solution_callback( + original_x, original_dual, compute_user_objective(original_lp, root_objective)); } mutex_lower.lock(); f_t lower_bound = lower_bound_ = root_objective; diff --git a/cpp/src/dual_simplex/phase2.cpp b/cpp/src/dual_simplex/phase2.cpp index 7383f42216..370badf332 100644 --- a/cpp/src/dual_simplex/phase2.cpp +++ b/cpp/src/dual_simplex/phase2.cpp @@ -1495,13 +1495,13 @@ void compute_delta_y(const basis_update_mpf_t& ft, } template -void update_dual_variables(const sparse_vector_t& delta_y_sparse, - const std::vector& delta_z_indices, - const std::vector& delta_z, - f_t step_length, - i_t leaving_index, - std::vector& y, - std::vector& z) +i_t update_dual_variables(const sparse_vector_t& delta_y_sparse, + const std::vector& delta_z_indices, + const std::vector& delta_z, + f_t step_length, + i_t leaving_index, + std::vector& y, + std::vector& z) { // Update dual variables // y <- y + steplength * delta_y @@ -1517,6 +1517,7 @@ void update_dual_variables(const sparse_vector_t& delta_y_sparse, z[j] += step_length * delta_z[j]; } z[leaving_index] += step_length * delta_z[leaving_index]; + return 0; } template @@ -2514,6 +2515,10 @@ dual::status_t dual_phase2(i_t phase, delta_z_indices, nonbasic_mark); entering_index = bfrt.compute_step_length(step_length, nonbasic_entering_index); + if (entering_index == -4) { + settings.log.printf("Numerical issues encountered in ratio test.\n"); + return dual::status_t::NUMERICAL; + } timers.bfrt_time += timers.stop_timer(); } else { entering_index = phase2::phase2_ratio_test( @@ -2663,8 +2668,12 @@ dual::status_t dual_phase2(i_t phase, // Update dual variables // y <- y + steplength * delta_y // z <- z + steplength * delta_z - phase2::update_dual_variables( + i_t update_dual_variables_status = phase2::update_dual_variables( delta_y_sparse, delta_z_indices, delta_z, step_length, leaving_index, y, z); + if (update_dual_variables_status == -1) { + settings.log.printf("Numerical issues encountered in update_dual_variables.\n"); + return dual::status_t::NUMERICAL; + } timers.vector_time += timers.stop_timer(); #ifdef COMPUTE_DUAL_RESIDUAL diff --git a/cpp/src/dual_simplex/presolve.cpp b/cpp/src/dual_simplex/presolve.cpp index 48d696bf96..806f171d7a 100644 --- a/cpp/src/dual_simplex/presolve.cpp +++ b/cpp/src/dual_simplex/presolve.cpp @@ -1134,6 +1134,7 @@ void uncrush_dual_solution(const user_problem_t& user_problem, std::vector& user_y, std::vector& user_z) { + user_y.resize(user_problem.num_rows); // Reduced costs are uncrushed just like the primal solution uncrush_primal_solution(user_problem, problem, z, user_z); diff --git a/cpp/src/dual_simplex/simplex_solver_settings.hpp b/cpp/src/dual_simplex/simplex_solver_settings.hpp index a51ed19bcf..5b7e8bf0fa 100644 --- a/cpp/src/dual_simplex/simplex_solver_settings.hpp +++ b/cpp/src/dual_simplex/simplex_solver_settings.hpp @@ -109,7 +109,7 @@ struct simplex_solver_settings_t { i_t inside_mip; // 0 if outside MIP, 1 if inside MIP at root node, 2 if inside MIP at leaf node std::function&, f_t)> solution_callback; std::function heuristic_preemption_callback; - std::function&, f_t)> set_simplex_solution_callback; + std::function&, std::vector&, f_t)> set_simplex_solution_callback; mutable logger_t log; std::atomic* concurrent_halt; // if nullptr ignored, if !nullptr, 0 if solver should // continue, 1 if solver should halt diff --git a/cpp/src/linear_programming/initial_scaling_strategy/initial_scaling.cu b/cpp/src/linear_programming/initial_scaling_strategy/initial_scaling.cu index 72931267ad..3eae936902 100644 --- a/cpp/src/linear_programming/initial_scaling_strategy/initial_scaling.cu +++ b/cpp/src/linear_programming/initial_scaling_strategy/initial_scaling.cu @@ -39,17 +39,17 @@ pdlp_initial_scaling_strategy_t::pdlp_initial_scaling_strategy_t( problem_t& op_problem_scaled, i_t number_of_ruiz_iterations, f_t alpha, - pdhg_solver_t& pdhg_solver, rmm::device_uvector& A_T, rmm::device_uvector& A_T_offsets, rmm::device_uvector& A_T_indices, + pdhg_solver_t* pdhg_solver_ptr, bool running_mip) : handle_ptr_(handle_ptr), stream_view_(handle_ptr_->get_stream()), primal_size_h_(op_problem_scaled.n_variables), dual_size_h_(op_problem_scaled.n_constraints), op_problem_scaled_(op_problem_scaled), - pdhg_solver_(pdhg_solver), + pdhg_solver_ptr_(pdhg_solver_ptr), A_T_(A_T), A_T_offsets_(A_T_offsets), A_T_indices_(A_T_indices), @@ -398,7 +398,7 @@ void pdlp_initial_scaling_strategy_t::scale_problem() op_problem_scaled_.is_scaled_ = true; if (!running_mip_) { - scale_solutions(pdhg_solver_.get_primal_solution(), pdhg_solver_.get_dual_solution()); + scale_solutions(pdhg_solver_ptr_->get_primal_solution(), pdhg_solver_ptr_->get_dual_solution()); } } diff --git a/cpp/src/linear_programming/initial_scaling_strategy/initial_scaling.cuh b/cpp/src/linear_programming/initial_scaling_strategy/initial_scaling.cuh index 368b12770f..0cd01aa9ab 100644 --- a/cpp/src/linear_programming/initial_scaling_strategy/initial_scaling.cuh +++ b/cpp/src/linear_programming/initial_scaling_strategy/initial_scaling.cuh @@ -55,10 +55,10 @@ class pdlp_initial_scaling_strategy_t { problem_t& op_problem_scaled, i_t number_of_ruiz_iterations, f_t alpha, - pdhg_solver_t& pdhg_solver, rmm::device_uvector& A_T, rmm::device_uvector& A_T_offsets, rmm::device_uvector& A_T_indices, + pdhg_solver_t* pdhg_solver_ptr, bool running_mip = false); void scale_problem(); @@ -98,7 +98,7 @@ class pdlp_initial_scaling_strategy_t { rmm::device_uvector cummulative_constraint_matrix_scaling_; rmm::device_uvector cummulative_variable_scaling_; - pdhg_solver_t& pdhg_solver_; + pdhg_solver_t* pdhg_solver_ptr_; rmm::device_uvector& A_T_; rmm::device_uvector& A_T_offsets_; rmm::device_uvector& A_T_indices_; diff --git a/cpp/src/linear_programming/pdlp.cu b/cpp/src/linear_programming/pdlp.cu index 9f2de16f00..45d0b8b9c0 100644 --- a/cpp/src/linear_programming/pdlp.cu +++ b/cpp/src/linear_programming/pdlp.cu @@ -75,10 +75,10 @@ pdlp_solver_t::pdlp_solver_t(problem_t& op_problem, op_problem_scaled_, pdlp_hyper_params::default_l_inf_ruiz_iterations, (f_t)pdlp_hyper_params::default_alpha_pock_chambolle_rescaling, - pdhg_solver_, op_problem_scaled_.reverse_coefficients, op_problem_scaled_.reverse_offsets, - op_problem_scaled_.reverse_constraints}, + op_problem_scaled_.reverse_constraints, + &pdhg_solver_}, average_op_problem_evaluation_cusparse_view_{handle_ptr_, op_problem, unscaled_primal_avg_solution_, diff --git a/cpp/src/linear_programming/utilities/logger_init.hpp b/cpp/src/linear_programming/utilities/logger_init.hpp index 2448373915..093c4ae1c2 100644 --- a/cpp/src/linear_programming/utilities/logger_init.hpp +++ b/cpp/src/linear_programming/utilities/logger_init.hpp @@ -42,7 +42,7 @@ class init_logger_t { #else cuopt::default_logger().set_pattern(cuopt::default_pattern()); #endif - cuopt::default_logger().flush_on(rapids_logger::level_enum::info); + cuopt::default_logger().flush_on(rapids_logger::level_enum::debug); } } ~init_logger_t() { cuopt::reset_default_logger(); } diff --git a/cpp/src/mip/diversity/assignment_hash_map.cu b/cpp/src/mip/diversity/assignment_hash_map.cu index 24d0051b37..5e696a6789 100644 --- a/cpp/src/mip/diversity/assignment_hash_map.cu +++ b/cpp/src/mip/diversity/assignment_hash_map.cu @@ -90,8 +90,7 @@ void assignment_hash_map_t::fill_integer_assignment(solution_t size_t assignment_hash_map_t::hash_solution(solution_t& solution) { - const int TPB = 1024; - + const int TPB = 256; fill_integer_assignment(solution); thrust::fill( solution.handle_ptr->get_thrust_policy(), reduction_buffer.begin(), reduction_buffer.end(), 0); diff --git a/cpp/src/mip/diversity/diversity_config.hpp b/cpp/src/mip/diversity/diversity_config.hpp index c38555ab90..6acac8fbb5 100644 --- a/cpp/src/mip/diversity/diversity_config.hpp +++ b/cpp/src/mip/diversity/diversity_config.hpp @@ -22,7 +22,7 @@ namespace cuopt::linear_programming::detail { struct diversity_config_t { double time_ratio_on_init_lp = 0.1; double max_time_on_lp = 30; - double time_ratio_of_probing_cache = 0.10; + double time_ratio_of_probing_cache = 0.04; double max_time_on_probing = 60; size_t max_iterations_without_improvement = 15; int max_var_diff = 256; @@ -38,7 +38,7 @@ struct diversity_config_t { double max_fast_sol_time = 10; double lp_run_time_if_feasible = 15.; double lp_run_time_if_infeasible = 1; - bool halve_population = true; + bool halve_population = false; }; } // namespace cuopt::linear_programming::detail diff --git a/cpp/src/mip/diversity/diversity_manager.cu b/cpp/src/mip/diversity/diversity_manager.cu index 726eb5b41a..0fb3f9fc08 100644 --- a/cpp/src/mip/diversity/diversity_manager.cu +++ b/cpp/src/mip/diversity/diversity_manager.cu @@ -27,7 +27,6 @@ constexpr bool from_dir = false; constexpr bool fj_only_run = false; -constexpr bool fp_only_run = false; namespace cuopt::linear_programming::detail { @@ -47,19 +46,27 @@ diversity_manager_t::diversity_manager_t(mip_solver_context_tn_constraints), lp_optimal_solution(context.problem_ptr->n_variables, context.problem_ptr->handle_ptr->get_stream()), + lp_dual_optimal_solution(context.problem_ptr->n_constraints, + context.problem_ptr->handle_ptr->get_stream()), ls(context, lp_optimal_solution), timer(diversity_config.default_time_limit), bound_prop_recombiner(context, context.problem_ptr->n_variables, ls.constraint_prop, context.problem_ptr->handle_ptr), - fp_recombiner( - context, context.problem_ptr->n_variables, ls.fp, context.problem_ptr->handle_ptr), + fp_recombiner(context, + context.problem_ptr->n_variables, + ls.fj, + ls.constraint_prop, + ls.line_segment_search, + lp_optimal_solution, + context.problem_ptr->handle_ptr), line_segment_recombiner(context, context.problem_ptr->n_variables, ls.line_segment_search, @@ -73,7 +80,7 @@ diversity_manager_t::diversity_manager_t(mip_solver_context_t::n_of_arms, cuopt::seed_generator::get_seed(), ls_alpha, "ls"), - assignment_hash_map(*context.problem_ptr) + ls_hash_map(*context.problem_ptr) { // Read configuration ID from environment variable int max_config = -1; @@ -110,9 +117,9 @@ bool diversity_manager_t::run_local_search(solution_t& solut { i_t ls_mab_option = mab_ls.select_mab_option(); mab_ls_config_t::get_local_search_and_lm_from_config(ls_mab_option, ls_config); - assignment_hash_map.insert(solution); + ls_hash_map.insert(solution); constexpr i_t skip_solutions_threshold = 3; - if (assignment_hash_map.check_skip_solution(solution, skip_solutions_threshold)) { return false; } + if (ls_hash_map.check_skip_solution(solution, skip_solutions_threshold)) { return false; } ls.run_local_search(solution, weights, timer, ls_config); return true; } @@ -172,30 +179,10 @@ solution_t diversity_manager_t::generate_solution(f_t time_l { solution_t sol(*problem_ptr); sol.compute_feasibility(); - ls.generate_solution(sol, random_start, population.early_exit_primal_generation, time_limit); + ls.generate_solution(sol, random_start, &population, time_limit); return sol; } -template -void diversity_manager_t::generate_add_solution( - std::vector>& initial_sol_vector, f_t time_limit, bool random_start) -{ - // TODO check weights here if they are all similar - // do a local search than add it searched solution as well - initial_sol_vector.emplace_back(generate_solution(time_limit, random_start)); -} - -template -void diversity_manager_t::average_fj_weights(i_t i) -{ - thrust::transform(problem_ptr->handle_ptr->get_thrust_policy(), - population.weights.cstr_weights.begin(), - population.weights.cstr_weights.end(), - ls.fj.cstr_weights.begin(), - population.weights.cstr_weights.begin(), - [i] __device__(f_t w1, f_t w2) { return (w1 * i + w2) / (i + 1); }); -} - template void diversity_manager_t::add_user_given_solutions( std::vector>& initial_sol_vector) @@ -227,74 +214,6 @@ void diversity_manager_t::add_user_given_solutions( } } -// if 60% of the time, exit -// if 20% of the time finishes and we generate 5 solutions -template -void diversity_manager_t::generate_initial_solutions() -{ - add_user_given_solutions(initial_sol_vector); - bool skip_initial_island_generation = - initial_sol_vector.size() > diversity_config.n_sol_for_skip_init_gen || from_dir; - // allocate maximum of 40% of the time to the initial island generation - // aim to generate at least 5 feasible solutions thus spending 8% of the time to generate a - // solution if we can generate faster generate up to 10 sols - const f_t generation_time_limit = - diversity_config.generation_time_limit_ratio * timer.get_time_limit(); - const f_t max_island_gen_time = diversity_config.max_island_gen_time; - f_t total_island_gen_time = std::min(generation_time_limit, max_island_gen_time); - timer_t gen_timer(total_island_gen_time); - f_t sol_time_limit = gen_timer.remaining_time(); - for (i_t i = 0; i < diversity_config.maximum_island_size && !skip_initial_island_generation; - ++i) { - if (check_b_b_preemption()) { return; } - if (i + population.get_external_solution_size() >= 5) { break; } - CUOPT_LOG_DEBUG("Generating sol %d", i); - bool is_first_sol = (i == 0); - if (i == 1) { - sol_time_limit = gen_timer.remaining_time() / (diversity_config.initial_island_size - 1); - } - // in first iteration, definitely generate feasible - if (is_first_sol) { - sol_time_limit = gen_timer.remaining_time(); - ls.fj.reset_weights(problem_ptr->handle_ptr->get_stream()); - } - // in other iterations(when there is at least one feasible) - else { - ls.fj.randomize_weights(problem_ptr->handle_ptr); - } - generate_add_solution(initial_sol_vector, sol_time_limit, !is_first_sol); - if (is_first_sol && initial_sol_vector.back().get_feasible()) { - CUOPT_LOG_DEBUG("First FP/FJ solution found at %f with objective %f", - timer.elapsed_time(), - initial_sol_vector.back().get_user_objective()); - } - population.run_solution_callbacks(initial_sol_vector.back()); - // run ls on the generated solutions - solution_t searched_sol(initial_sol_vector.back()); - ls_config_t ls_config; - run_local_search(searched_sol, population.weights, gen_timer, ls_config); - population.run_solution_callbacks(searched_sol); - initial_sol_vector.emplace_back(std::move(searched_sol)); - average_fj_weights(i); - // run ls on the solutions - // if at least initial_island_size solutions are generated and time limit is reached - if (i >= diversity_config.initial_island_size || gen_timer.check_time_limit()) { break; } - } - CUOPT_LOG_DEBUG("Initial unsearched solutions are generated!"); - i_t actual_island_size = initial_sol_vector.size(); - population.normalize_weights(); - // find diversity of the population - population.find_diversity(initial_sol_vector, diversity_config.use_avg_diversity); - population.add_solutions_from_vec(std::move(initial_sol_vector)); - population.update_qualities(); - CUOPT_LOG_DEBUG("Initial population generated, size %d var_threshold %d", - population.current_size(), - population.var_threshold); - population.print(); - auto new_sol_vector = population.get_external_solutions(); - if (!fj_only_run && !fp_only_run) { recombine_and_ls_with_all(new_sol_vector); } -} - template bool diversity_manager_t::run_presolve(f_t time_limit) { @@ -320,7 +239,13 @@ bool diversity_manager_t::run_presolve(f_t time_limit) } stats.presolve_time = presolve_timer.elapsed_time(); lp_optimal_solution.resize(problem_ptr->n_variables, problem_ptr->handle_ptr->get_stream()); + lp_dual_optimal_solution.resize(problem_ptr->n_constraints, + problem_ptr->handle_ptr->get_stream()); problem_ptr->handle_ptr->sync_stream(); + CUOPT_LOG_INFO("After trivial presolve #constraints %d #variables %d objective offset %f.", + problem_ptr->n_constraints, + problem_ptr->n_variables, + problem_ptr->presolve_data.objective_offset); return true; } @@ -356,7 +281,7 @@ void diversity_manager_t::generate_quick_feasible_solution() template bool diversity_manager_t::check_b_b_preemption() { - if (population.preempt_heuristic_solver_) { + if (population.preempt_heuristic_solver_.load()) { if (population.current_size() == 0) { population.allocate_solutions(); } auto new_sol_vector = population.get_external_solutions(); population.add_solutions_from_vec(std::move(new_sol_vector)); @@ -385,7 +310,7 @@ template void diversity_manager_t::run_fp_alone(solution_t& solution) { CUOPT_LOG_INFO("Running FP alone!"); - ls.run_fp(solution, timer, &population.weights, false); + ls.run_fp(solution, timer, &population); CUOPT_LOG_INFO("FP alone finished!"); } @@ -418,7 +343,7 @@ solution_t diversity_manager_t::run_solver() population.initialize_population(); if (check_b_b_preemption()) { return population.best_feasible(); } // before probing cache or LP, run FJ to generate initial primal feasible solution - if (!from_dir && !fp_only_run && !fj_only_run) { generate_quick_feasible_solution(); } + if (!from_dir && !fj_only_run) { generate_quick_feasible_solution(); } const f_t time_ratio_of_probing_cache = diversity_config.time_ratio_of_probing_cache; const f_t max_time_on_probing = diversity_config.max_time_on_probing; f_t time_for_probing_cache = @@ -433,11 +358,7 @@ solution_t diversity_manager_t::run_solver() lp_state_t& lp_state = problem_ptr->lp_state; // resize because some constructor might be called before the presolve lp_state.resize(*problem_ptr, problem_ptr->handle_ptr->get_stream()); - bool bb_thread_solution_exists = false; - { - std::lock_guard guard(relaxed_solution_mutex); - bb_thread_solution_exists = simplex_solution_exists; - } // Mutex is unlocked here + bool bb_thread_solution_exists = simplex_solution_exists.load(); if (bb_thread_solution_exists) { ls.lp_optimal_exists = true; } else if (!fj_only_run) { @@ -447,17 +368,28 @@ solution_t diversity_manager_t::run_solver() lp_settings.return_first_feasible = false; lp_settings.save_state = true; lp_settings.concurrent_halt = &global_concurrent_halt; + lp_settings.has_initial_primal = false; rmm::device_uvector lp_optimal_solution_copy(lp_optimal_solution.size(), problem_ptr->handle_ptr->get_stream()); auto lp_result = get_relaxed_lp_solution(*problem_ptr, lp_optimal_solution_copy, lp_state, lp_settings); { std::lock_guard guard(relaxed_solution_mutex); - if (!simplex_solution_exists) { + if (!simplex_solution_exists.load()) { raft::copy(lp_optimal_solution.data(), lp_optimal_solution_copy.data(), lp_optimal_solution.size(), problem_ptr->handle_ptr->get_stream()); + } else { + // copy the lp state + raft::copy(lp_state.prev_primal.data(), + lp_optimal_solution.data(), + lp_optimal_solution.size(), + problem_ptr->handle_ptr->get_stream()); + raft::copy(lp_state.prev_dual.data(), + lp_dual_optimal_solution.data(), + lp_dual_optimal_solution.size(), + problem_ptr->handle_ptr->get_stream()); } } problem_ptr->handle_ptr->sync_stream(); @@ -481,29 +413,35 @@ solution_t diversity_manager_t::run_solver() } population.allocate_solutions(); + population.add_solutions_from_vec(std::move(initial_sol_vector)); if (check_b_b_preemption()) { return population.best_feasible(); } - if (!fp_only_run) { - // generate a population with 5 solutions(FP+FJ) - generate_initial_solutions(); - } + if (context.settings.benchmark_info_ptr != nullptr) { context.settings.benchmark_info_ptr->objective_of_initial_population = population.best_feasible().get_user_objective(); } if (fj_only_run) { - run_fj_alone(population.best_feasible()); - return population.best_feasible(); - } - - if (fp_only_run) { - auto sol = generate_solution(timer.remaining_time(), false); - run_fp_alone(sol); + solution_t sol(*problem_ptr); + run_fj_alone(sol); return sol; } - if (timer.check_time_limit()) { return population.best_feasible(); } + auto sol = generate_solution(timer.remaining_time(), false); + population.add_solution(std::move(solution_t(sol))); + if (timer.check_time_limit()) { + auto new_sol_vector = population.get_external_solutions(); + population.add_solutions_from_vec(std::move(new_sol_vector)); + return population.best_feasible(); + } + run_fp_alone(sol); + population.update_weights(); + if (timer.check_time_limit()) { + auto new_sol_vector = population.get_external_solutions(); + population.add_solutions_from_vec(std::move(new_sol_vector)); + return population.best_feasible(); + } main_loop(); return population.best_feasible(); @@ -557,24 +495,33 @@ void diversity_manager_t::set_new_user_bound(f_t new_bound) } template -void diversity_manager_t::recombine_and_ls_with_all(solution_t& solution) +void diversity_manager_t::recombine_and_ls_with_all(solution_t& solution, + bool add_only_feasible) { raft::common::nvtx::range fun_scope("recombine_and_ls_with_all"); + if (population.population_hash_map.check_skip_solution(solution, 1)) { return; } auto population_vector = population.population_to_vector(); for (auto& curr_sol : population_vector) { - if (check_b_b_preemption()) { return; } - if (curr_sol.get_feasible()) { - auto [offspring, lp_offspring] = recombine_and_local_search(curr_sol, solution); - i_t inserted_pos_1 = population.add_solution(std::move(lp_offspring)); - i_t inserted_pos_2 = population.add_solution(std::move(offspring)); - if (timer.check_time_limit()) { return; } + for (const auto recombiner_type : recombiner_types) { + if (check_b_b_preemption()) { return; } + if (curr_sol.get_feasible()) { + auto [offspring, lp_offspring] = + recombine_and_local_search(curr_sol, solution, recombiner_type); + if (!add_only_feasible || lp_offspring.get_feasible()) { + population.add_solution(std::move(lp_offspring)); + } + if (!add_only_feasible || offspring.get_feasible()) { + population.add_solution(std::move(offspring)); + } + if (timer.check_time_limit()) { return; } + } } } } template void diversity_manager_t::recombine_and_ls_with_all( - std::vector>& solutions) + std::vector>& solutions, bool add_only_feasible) { raft::common::nvtx::range fun_scope("recombine_and_ls_with_all"); if (solutions.size() > 0) { @@ -593,10 +540,10 @@ void diversity_manager_t::recombine_and_ls_with_all( // TODO try if running LP with integers fixed makes it feasible if (ls_solution.get_feasible()) { CUOPT_LOG_DEBUG("External LS searched solution feasible, running recombiners!"); - recombine_and_ls_with_all(ls_solution); + recombine_and_ls_with_all(ls_solution, add_only_feasible); } else { CUOPT_LOG_DEBUG("External solution feasible, running recombiners!"); - recombine_and_ls_with_all(sol); + recombine_and_ls_with_all(sol, add_only_feasible); } } } @@ -607,6 +554,7 @@ void diversity_manager_t::main_loop() { population.start_threshold_adjustment(); recombine_stats.reset(); + population.print(); while (true) { if (check_b_b_preemption()) { break; } CUOPT_LOG_DEBUG("Running a new step"); @@ -636,13 +584,11 @@ void diversity_manager_t::main_loop() population.find_diversity(current_population, diversity_config.use_avg_diversity); // if the threshold is lower than the threshold we progress with time // set it to the higher threshold - // population.var_threshold = max(population.var_threshold, prev_threshold); population.add_solutions_from_vec(std::move(current_population)); } else { // increase the threshold/decrease the diversity population.adjust_threshold(timer); } - // population.add_solutions_from_vec(std::move(new_solutions)); // idea to try, we can average the weights of the new solutions population.update_weights(); population.print(); @@ -678,7 +624,8 @@ void diversity_manager_t::check_better_than_both(solution_t& template std::pair, solution_t> diversity_manager_t::recombine_and_local_search(solution_t& sol1, - solution_t& sol2) + solution_t& sol2, + recombiner_enum_t recombiner_type) { raft::common::nvtx::range fun_scope("recombine_and_local_search"); CUOPT_LOG_DEBUG("Recombining sol cost:feas %f : %d and %f : %d", @@ -689,7 +636,7 @@ diversity_manager_t::recombine_and_local_search(solution_t& double best_objective_of_parents = std::min(sol1.get_objective(), sol2.get_objective()); bool at_least_one_parent_feasible = sol1.get_feasible() || sol2.get_feasible(); // randomly choose among 3 recombiners - auto [offspring, success] = recombine(sol1, sol2); + auto [offspring, success] = recombine(sol1, sol2, recombiner_type); if (!success) { // add the attempt mab_recombiner.add_mab_reward(static_cast(recombine_stats.get_last_attempt()), @@ -761,11 +708,11 @@ diversity_manager_t::recombine_and_local_search(solution_t& population.best().get_quality(population.weights), offspring_qual, recombiner_work_normalized_reward_t(recombine_stats.get_last_recombiner_time())); - // mab_ls.add_mab_reward(mab_ls_config_t::last_ls_mab_option, - // best_quality_of_parents, - // population.best_feasible().get_quality(population.weights), - // offspring_qual, - // ls_work_normalized_reward_t(mab_ls_config_t::last_lm_config)); + mab_ls.add_mab_reward(mab_ls_config_t::last_ls_mab_option, + best_quality_of_parents, + population.best_feasible().get_quality(population.weights), + offspring_qual, + ls_work_normalized_reward_t(mab_ls_config_t::last_lm_config)); if (context.settings.benchmark_info_ptr != nullptr) { check_better_than_both(offspring, sol1, sol2); check_better_than_both(lp_offspring, sol1, sol2); @@ -775,7 +722,7 @@ diversity_manager_t::recombine_and_local_search(solution_t& template std::pair, bool> diversity_manager_t::recombine( - solution_t& a, solution_t& b) + solution_t& a, solution_t& b, recombiner_enum_t recombiner_type) { recombiner_enum_t recombiner; if (run_only_ls_recombiner) { @@ -787,7 +734,12 @@ std::pair, bool> diversity_manager_t::recombine( } else if (run_only_sub_mip_recombiner) { recombiner = recombiner_enum_t::SUB_MIP; } else { - recombiner = static_cast(mab_recombiner.select_mab_option()); + // only run the given recombiner unless it is defult + if (recombiner_type == recombiner_enum_t::SIZE) { + recombiner = static_cast(mab_recombiner.select_mab_option()); + } else { + recombiner = recombiner_type; + } } recombine_stats.add_attempt((recombiner_enum_t)recombiner); recombine_stats.start_recombiner_time(); @@ -828,14 +780,16 @@ std::pair, bool> diversity_manager_t::recombine( template void diversity_manager_t::set_simplex_solution(const std::vector& solution, + const std::vector& dual_solution, f_t objective) { CUOPT_LOG_DEBUG("Setting simplex solution with objective %f", objective); using sol_t = solution_t; + cudaSetDevice(context.handle_ptr->get_device()); context.handle_ptr->sync_stream(); - RAFT_CUDA_TRY(cudaSetDevice(context.handle_ptr->get_device())); cuopt_func_call(sol_t new_sol(*problem_ptr)); cuopt_assert(new_sol.assignment.size() == solution.size(), "Assignment size mismatch"); + cuopt_assert(problem_ptr->n_constraints == dual_solution.size(), "Dual assignment size mismatch"); cuopt_func_call(new_sol.copy_new_assignment(solution)); cuopt_func_call(new_sol.compute_feasibility()); cuopt_assert(integer_equal(new_sol.get_user_objective(), objective, 1e-3), "Objective mismatch"); @@ -846,6 +800,10 @@ void diversity_manager_t::set_simplex_solution(const std::vector& // the operations are ordered as long as they are on the same stream raft::copy( lp_optimal_solution.data(), solution.data(), solution.size(), context.handle_ptr->get_stream()); + raft::copy(lp_dual_optimal_solution.data(), + dual_solution.data(), + dual_solution.size(), + context.handle_ptr->get_stream()); set_new_user_bound(objective); context.handle_ptr->sync_stream(); } diff --git a/cpp/src/mip/diversity/diversity_manager.cuh b/cpp/src/mip/diversity/diversity_manager.cuh index 3ad538d33b..e65b5697f1 100644 --- a/cpp/src/mip/diversity/diversity_manager.cuh +++ b/cpp/src/mip/diversity/diversity_manager.cuh @@ -52,7 +52,9 @@ class diversity_manager_t { // main loop of diversity improvements void main_loop(); // randomly chooses a recombiner and returns the offspring - std::pair, bool> recombine(solution_t& a, solution_t& b); + std::pair, bool> recombine(solution_t& a, + solution_t& b, + recombiner_enum_t recombiner_type); bool regenerate_solutions(); void generate_add_solution(std::vector>& initial_sol_vector, f_t time_limit, @@ -62,10 +64,13 @@ class diversity_manager_t { std::vector> generate_more_solutions(); void add_user_given_solutions(std::vector>& initial_sol_vector); population_t* get_population_pointer() { return &population; } - void recombine_and_ls_with_all(std::vector>& solutions); - void recombine_and_ls_with_all(solution_t& solution); + void recombine_and_ls_with_all(std::vector>& solutions, + bool add_only_feasible = false); + void recombine_and_ls_with_all(solution_t& solution, bool add_only_feasible = false); std::pair, solution_t> recombine_and_local_search( - solution_t& a, solution_t& b); + solution_t& a, + solution_t& b, + recombiner_enum_t recombiner_type = recombiner_enum_t::SIZE); void set_new_user_bound(f_t new_user_bound); void generate_quick_feasible_solution(); bool check_b_b_preemption(); @@ -77,14 +82,17 @@ class diversity_manager_t { timer_t& timer, ls_config_t& ls_config); - void set_simplex_solution(const std::vector& solution, f_t objective); + void set_simplex_solution(const std::vector& solution, + const std::vector& dual_solution, + f_t objective); mip_solver_context_t& context; problem_t* problem_ptr; diversity_config_t diversity_config; population_t population; rmm::device_uvector lp_optimal_solution; - bool simplex_solution_exists{false}; + rmm::device_uvector lp_dual_optimal_solution; + std::atomic simplex_solution_exists{false}; local_search_t ls; cuopt::timer_t timer; bound_prop_recombiner_t bound_prop_recombiner; @@ -98,7 +106,7 @@ class diversity_manager_t { std::vector> initial_sol_vector; mab_t mab_recombiner; mab_t mab_ls; - assignment_hash_map_t assignment_hash_map; + assignment_hash_map_t ls_hash_map; // mutex for the simplex solution update std::mutex relaxed_solution_mutex; // atomic for signalling pdlp to stop diff --git a/cpp/src/mip/diversity/population.cu b/cpp/src/mip/diversity/population.cu index 6a42cb39a1..63f4b3c855 100644 --- a/cpp/src/mip/diversity/population.cu +++ b/cpp/src/mip/diversity/population.cu @@ -15,6 +15,7 @@ * limitations under the License. */ +#include "diversity_manager.cuh" #include "population.cuh" #include @@ -38,23 +39,32 @@ constexpr double halving_skip_ratio = 0.75; template population_t::population_t(std::string const& name_, mip_solver_context_t& context_, + diversity_manager_t& dm_, int var_threshold_, size_t max_solutions_, f_t infeasibility_weight_) : name(name_), context(context_), problem_ptr(context.problem_ptr), + dm(dm_), var_threshold(var_threshold_), max_solutions(max_solutions_), infeasibility_importance(infeasibility_weight_), weights(0, context.problem_ptr->handle_ptr), rng(cuopt::seed_generator::get_seed()), early_exit_primal_generation(false), + population_hash_map(*problem_ptr), timer(0) { best_feasible_objective = std::numeric_limits::max(); } +template +i_t get_max_var_threshold(i_t n_vars) +{ + return n_vars - sqrt(n_vars); +} + template void population_t::allocate_solutions() { @@ -67,8 +77,7 @@ void population_t::allocate_solutions() template void population_t::initialize_population() { - var_threshold = - std::max(problem_ptr->n_variables - var_threshold, (problem_ptr->n_variables / 10) * 8); + var_threshold = get_max_var_threshold(problem_ptr->n_integer_vars); solutions.reserve(max_solutions); indices.reserve(max_solutions); // indices[0] always points to solutions[0] - a special place for feasible solution @@ -308,6 +317,7 @@ template i_t population_t::add_solution(solution_t&& sol) { raft::common::nvtx::range fun_scope("add_solution"); + population_hash_map.insert(sol); double sol_cost = sol.get_quality(weights); CUOPT_LOG_TRACE("Adding solution with quality %f and objective %f n_integers %d!", sol_cost, @@ -576,21 +586,6 @@ std::vector> population_t::population_to_vector() return sol_vec; } -template -i_t get_max_var_threshold(i_t n_vars) -{ - if (n_vars < 50) { - return std::max(1, n_vars - 1); - } else if (n_vars < 80) { - return n_vars - 2; - } else if (n_vars < 200) { - return n_vars - 4; - } else if (n_vars < 1000) { - return n_vars - 8; - } - return n_vars - 10; -} - template void population_t::halve_the_population() { @@ -756,6 +751,14 @@ void population_t::print() CUOPT_LOG_DEBUG(" -------------- "); } +template +void population_t::run_all_recombiners(solution_t& sol) +{ + std::vector> sol_vec; + sol_vec.emplace_back(std::move(solution_t(sol))); + dm.recombine_and_ls_with_all(sol_vec, true); +} + #if MIP_INSTANTIATE_FLOAT template class population_t; #endif diff --git a/cpp/src/mip/diversity/population.cuh b/cpp/src/mip/diversity/population.cuh index 0f0176341c..532b9f24d4 100644 --- a/cpp/src/mip/diversity/population.cuh +++ b/cpp/src/mip/diversity/population.cuh @@ -17,6 +17,7 @@ #pragma once +#include "assignment_hash_map.cuh" #include "population.cuh" #include @@ -30,11 +31,16 @@ namespace cuopt::linear_programming::detail { +// forward declare +template +class diversity_manager_t; + template class population_t { public: population_t(std::string const& name, mip_solver_context_t& context, + diversity_manager_t& dm, int var_threshold_, size_t max_solutions_, f_t infeasibility_weight_); @@ -64,6 +70,7 @@ class population_t { // initializes the population lazily. after presolve and var removals void initialize_population(); bool is_better_than_best_feasible(solution_t& sol); + void run_all_recombiners(solution_t& sol); void allocate_solutions(); @@ -154,6 +161,7 @@ class population_t { std::string name; mip_solver_context_t& context; problem_t* problem_ptr; + diversity_manager_t& dm; i_t var_threshold; i_t initial_threshold; double population_start_time; @@ -168,9 +176,10 @@ class population_t { std::mt19937 rng; i_t update_iter = 0; std::mutex solution_mutex; - bool early_exit_primal_generation = false; - f_t best_feasible_objective = std::numeric_limits::max(); - bool preempt_heuristic_solver_ = false; + std::atomic early_exit_primal_generation = false; + std::atomic preempt_heuristic_solver_ = false; + f_t best_feasible_objective = std::numeric_limits::max(); + assignment_hash_map_t population_hash_map; cuopt::timer_t timer; }; diff --git a/cpp/src/mip/diversity/recombiners/fp_recombiner.cuh b/cpp/src/mip/diversity/recombiners/fp_recombiner.cuh index 5597e8e845..f3d4d73556 100644 --- a/cpp/src/mip/diversity/recombiners/fp_recombiner.cuh +++ b/cpp/src/mip/diversity/recombiners/fp_recombiner.cuh @@ -34,11 +34,14 @@ class fp_recombiner_t : public recombiner_t { public: fp_recombiner_t(mip_solver_context_t& context, i_t n_vars, - feasibility_pump_t& fp_, + fj_t& fj, + constraint_prop_t& constraint_prop, + line_segment_search_t& line_segment_search, + rmm::device_uvector& lp_optimal_solution, const raft::handle_t* handle_ptr) : recombiner_t(context, n_vars, handle_ptr), vars_to_fix(n_vars, handle_ptr->get_stream()), - fp(fp_) + fp(context, fj, constraint_prop, line_segment_search, lp_optimal_solution) { } @@ -146,7 +149,8 @@ class fp_recombiner_t : public recombiner_t { return std::make_pair(offspring, !same_as_parents); } rmm::device_uvector vars_to_fix; - feasibility_pump_t& fp; + // keep a copy of FP to prevent interference with generation FP + feasibility_pump_t fp; }; } // namespace cuopt::linear_programming::detail diff --git a/cpp/src/mip/diversity/recombiners/recombiner.cuh b/cpp/src/mip/diversity/recombiners/recombiner.cuh index 94ca34ea18..3b09f1956f 100644 --- a/cpp/src/mip/diversity/recombiners/recombiner.cuh +++ b/cpp/src/mip/diversity/recombiners/recombiner.cuh @@ -124,7 +124,8 @@ class recombiner_t { CUOPT_LOG_DEBUG("n_objective_vars in different vars %d n_objective_vars %d", objective_indices_in_subproblem.size(), objective_indices.size()); - if (objective_indices_in_subproblem.size() < 0.4 * remaining_variables) { + if (objective_indices.size() > 0 && + objective_indices_in_subproblem.size() < 0.4 * remaining_variables) { std::default_random_engine rng_host(cuopt::seed_generator::get_seed()); std::vector objective_indices_not_in_subproblem; std::set_difference(objective_indices.begin(), diff --git a/cpp/src/mip/diversity/recombiners/recombiner_stats.hpp b/cpp/src/mip/diversity/recombiners/recombiner_stats.hpp index 7e3e25164a..c6f99a4e1f 100644 --- a/cpp/src/mip/diversity/recombiners/recombiner_stats.hpp +++ b/cpp/src/mip/diversity/recombiners/recombiner_stats.hpp @@ -23,6 +23,11 @@ namespace cuopt::linear_programming::detail { enum class recombiner_enum_t : int { BOUND_PROP = 0, FP, LINE_SEGMENT, SUB_MIP, SIZE }; +constexpr std::array recombiner_types = {recombiner_enum_t::BOUND_PROP, + recombiner_enum_t::FP, + recombiner_enum_t::LINE_SEGMENT, + recombiner_enum_t::SUB_MIP}; + struct recombine_stats { int attempts; int success; diff --git a/cpp/src/mip/diversity/recombiners/sub_mip.cuh b/cpp/src/mip/diversity/recombiners/sub_mip.cuh index 381f5b1258..eb94353515 100644 --- a/cpp/src/mip/diversity/recombiners/sub_mip.cuh +++ b/cpp/src/mip/diversity/recombiners/sub_mip.cuh @@ -79,6 +79,17 @@ class sub_mip_recombiner_t : public recombiner_t { "n_vars_from_guiding %d n_vars_from_other %d", n_vars_from_guiding, n_vars_from_other); this->compute_vars_to_fix(offspring, vars_to_fix, n_vars_from_other, n_vars_from_guiding); auto [fixed_problem, fixed_assignment, variable_map] = offspring.fix_variables(vars_to_fix); + pdlp_initial_scaling_strategy_t scaling( + fixed_problem.handle_ptr, + fixed_problem, + pdlp_hyper_params::default_l_inf_ruiz_iterations, + (f_t)pdlp_hyper_params::default_alpha_pock_chambolle_rescaling, + fixed_problem.reverse_coefficients, + fixed_problem.reverse_offsets, + fixed_problem.reverse_constraints, + nullptr, + true); + scaling.scale_problem(); fixed_problem.presolve_data.reset_additional_vars(fixed_problem, offspring.handle_ptr); fixed_problem.presolve_data.initialize_var_mapping(fixed_problem, offspring.handle_ptr); trivial_presolve(fixed_problem); @@ -131,6 +142,8 @@ class sub_mip_recombiner_t : public recombiner_t { offspring.handle_ptr->sync_stream(); } if (solution_vector.size() > 0) { + rmm::device_uvector dummy(0, offspring.handle_ptr->get_stream()); + scaling.unscale_solutions(fixed_assignment, dummy); // unfix the assignment on given result no matter if it is feasible offspring.unfix_variables(fixed_assignment, variable_map); } else { @@ -159,6 +172,8 @@ class sub_mip_recombiner_t : public recombiner_t { solution.size(), offspring.handle_ptr->get_stream()); fixed_problem.post_process_assignment(fixed_assignment, false); + rmm::device_uvector dummy(0, offspring.handle_ptr->get_stream()); + scaling.unscale_solutions(fixed_assignment, dummy); sol.unfix_variables(fixed_assignment, variable_map); sol.compute_feasibility(); cuopt_func_call(sol.test_variable_bounds()); diff --git a/cpp/src/mip/feasibility_jump/feasibility_jump.cu b/cpp/src/mip/feasibility_jump/feasibility_jump.cu index 7520d1431e..a30a9b1824 100644 --- a/cpp/src/mip/feasibility_jump/feasibility_jump.cu +++ b/cpp/src/mip/feasibility_jump/feasibility_jump.cu @@ -75,6 +75,10 @@ fj_t::fj_t(mip_solver_context_t& context_, fj_settings_t in_ { setval_launch_dims = get_launch_dims_max_occupancy( (void*)update_assignment_kernel, TPB_setval, pb_ptr->handle_ptr); + update_changed_constraints_launch_dims = + get_launch_dims_max_occupancy((void*)update_changed_constraints_kernel, + TPB_update_changed_constraints, + pb_ptr->handle_ptr); resetmoves_launch_dims = get_launch_dims_max_occupancy( (void*)compute_mtm_moves_kernel, TPB_resetmoves, pb_ptr->handle_ptr); resetmoves_bin_launch_dims = @@ -643,7 +647,9 @@ void fj_t::run_step_device(const rmm::cuda_stream_view& climber_stream bool use_graph) { raft::common::nvtx::range scope("run_step_device"); - auto [grid_setval, blocks_setval] = setval_launch_dims; + auto [grid_setval, blocks_setval] = setval_launch_dims; + auto [grid_update_changed_constraints, blocks_update_changed_constraints] = + update_changed_constraints_launch_dims; auto [grid_resetmoves, blocks_resetmoves] = resetmoves_launch_dims; auto [grid_resetmoves_bin, blocks_resetmoves_bin] = resetmoves_bin_launch_dims; auto [grid_update_weights, blocks_update_weights] = update_weights_launch_dims; @@ -795,7 +801,7 @@ void fj_t::run_step_device(const rmm::cuda_stream_view& climber_stream climber_stream); cudaLaunchKernel((void*)update_changed_constraints_kernel, 1, - blocks_setval, + blocks_update_changed_constraints, kernel_args, 0, climber_stream); diff --git a/cpp/src/mip/feasibility_jump/feasibility_jump.cuh b/cpp/src/mip/feasibility_jump/feasibility_jump.cuh index 5d362a3d3d..e5e7c6d978 100644 --- a/cpp/src/mip/feasibility_jump/feasibility_jump.cuh +++ b/cpp/src/mip/feasibility_jump/feasibility_jump.cuh @@ -34,13 +34,14 @@ namespace cuopt::linear_programming::detail { -static constexpr int TPB_resetmoves = raft::WarpSize * 4; -static constexpr int TPB_heavyvars = raft::WarpSize * 16; -static constexpr int TPB_heavycstrs = raft::WarpSize * 4; -static constexpr int TPB_localmin = raft::WarpSize * 4; -static constexpr int TPB_setval = raft::WarpSize * 16; -static constexpr int TPB_liftmoves = raft::WarpSize * 4; -static constexpr int TPB_loadbalance = raft::WarpSize * 4; +static constexpr int TPB_resetmoves = raft::WarpSize * 4; +static constexpr int TPB_heavyvars = raft::WarpSize * 16; +static constexpr int TPB_heavycstrs = raft::WarpSize * 4; +static constexpr int TPB_localmin = raft::WarpSize * 4; +static constexpr int TPB_setval = raft::WarpSize * 16; +static constexpr int TPB_update_changed_constraints = raft::WarpSize * 4; +static constexpr int TPB_liftmoves = raft::WarpSize * 4; +static constexpr int TPB_loadbalance = raft::WarpSize * 4; struct fj_hyper_parameters_t { // The number of moves to evaluate, if there are many positive-score @@ -253,6 +254,7 @@ class fj_t { // kernel launch dimensions, computed once inside the constructor std::pair setval_launch_dims; + std::pair update_changed_constraints_launch_dims; std::pair resetmoves_launch_dims; std::pair resetmoves_bin_launch_dims; std::pair update_weights_launch_dims; diff --git a/cpp/src/mip/local_search/feasibility_pump/feasibility_pump.cu b/cpp/src/mip/local_search/feasibility_pump/feasibility_pump.cu index f8b7d34fc5..d291efa4cf 100644 --- a/cpp/src/mip/local_search/feasibility_pump/feasibility_pump.cu +++ b/cpp/src/mip/local_search/feasibility_pump/feasibility_pump.cu @@ -59,8 +59,6 @@ feasibility_pump_t::feasibility_pump_t( context.problem_ptr->handle_ptr->get_stream()), orig_variable_types(context.problem_ptr->n_variables, context.problem_ptr->handle_ptr->get_stream()), - best_excess_solution(context.problem_ptr->n_variables, - context.problem_ptr->handle_ptr->get_stream()), lp_optimal_solution(lp_optimal_solution_), rng(cuopt::seed_generator::get_seed()), timer(20.) @@ -257,9 +255,16 @@ bool feasibility_pump_t::round(solution_t& solution) { bool result; CUOPT_LOG_DEBUG("Rounding the point"); - timer_t bounds_prop_timer(std::min(2., timer.remaining_time())); - const f_t lp_run_time_after_feasible = std::min(3., timer.remaining_time() / 20.); + timer_t bounds_prop_timer(std::min(0.5, timer.remaining_time())); + const f_t lp_run_time_after_feasible = 0.; + bool old_var = constraint_prop.round_all_vars; + f_t old_time = constraint_prop.max_time_for_bounds_prop; + constraint_prop.round_all_vars = false; + constraint_prop.max_time_for_bounds_prop = 0.7; result = constraint_prop.apply_round(solution, lp_run_time_after_feasible, bounds_prop_timer); + constraint_prop.round_all_vars = old_var; + constraint_prop.max_time_for_bounds_prop = old_time; + // result = solution.round_nearest(); cuopt_func_call(solution.test_variable_bounds(true)); // copy the last rounding raft::copy(last_rounding.data(), @@ -389,7 +394,6 @@ void feasibility_pump_t::resize_vectors(problem_t& problem, { last_rounding.resize(problem.n_variables, handle_ptr->get_stream()); last_projection.resize(problem.n_variables, handle_ptr->get_stream()); - best_excess_solution.resize(problem.n_variables, handle_ptr->get_stream()); } template @@ -419,20 +423,6 @@ bool feasibility_pump_t::check_distance_cycle(solution_t& so return is_cycle; } -template -void feasibility_pump_t::save_best_excess_solution(solution_t& solution) -{ - f_t sol_excess = solution.get_total_excess(); - if (sol_excess < best_excess) { - CUOPT_LOG_DEBUG("FP: updating excess from %f to %f", best_excess, sol_excess); - best_excess = sol_excess; - raft::copy(best_excess_solution.data(), - solution.assignment.data(), - solution.assignment.size(), - solution.handle_ptr->get_stream()); - } -} - template void feasibility_pump_t::relax_general_integers(solution_t& solution) { @@ -512,7 +502,6 @@ bool feasibility_pump_t::run_single_fp_descent(solution_t& s is_cycle = check_distance_cycle(solution); if (is_cycle) { is_feasible = round(solution); - save_best_excess_solution(solution); cuopt_func_call(solution.test_variable_bounds(true)); if (is_feasible) { bool res = solution.compute_feasibility(); @@ -530,7 +519,6 @@ bool feasibility_pump_t::run_single_fp_descent(solution_t& s if (n_integers == solution.problem_ptr->n_integer_vars) { if (is_feasible) { CUOPT_LOG_DEBUG("Feasible solution found after LP with relative tolerance"); - save_best_excess_solution(solution); return true; } // if the solution is almost on polytope @@ -550,8 +538,7 @@ bool feasibility_pump_t::run_single_fp_descent(solution_t& s is_feasible = solution.get_feasible(); n_integers = solution.compute_number_of_integers(); if (is_feasible && n_integers == solution.problem_ptr->n_integer_vars) { - CUOPT_LOG_DEBUG("Feasible solution verified with lower precision!"); - save_best_excess_solution(solution); + CUOPT_LOG_DEBUG("Feasible solution verified with LP!"); return true; } } @@ -564,7 +551,6 @@ bool feasibility_pump_t::run_single_fp_descent(solution_t& s const f_t time_ratio = 0.2; is_feasible = test_fj_feasible(solution, time_ratio * proj_and_round_time); } - save_best_excess_solution(solution); if (timer.check_time_limit()) { CUOPT_LOG_DEBUG("FP time limit reached!"); return false; diff --git a/cpp/src/mip/local_search/feasibility_pump/feasibility_pump.cuh b/cpp/src/mip/local_search/feasibility_pump/feasibility_pump.cuh index 2013e80f51..1806573f0a 100644 --- a/cpp/src/mip/local_search/feasibility_pump/feasibility_pump.cuh +++ b/cpp/src/mip/local_search/feasibility_pump/feasibility_pump.cuh @@ -137,7 +137,6 @@ class feasibility_pump_t { bool check_distance_cycle(solution_t& solution); void reset(); void resize_vectors(problem_t& problem, const raft::handle_t* handle_ptr); - void save_best_excess_solution(solution_t& solution); bool random_round_with_fj(solution_t& solution, timer_t& round_timer); bool round_multiple_points(solution_t& solution); void relax_general_integers(solution_t& solution); @@ -156,7 +155,6 @@ class feasibility_pump_t { rmm::device_uvector last_projection; rmm::device_uvector orig_variable_types; f_t best_excess; - rmm::device_uvector best_excess_solution; rmm::device_uvector& lp_optimal_solution; std::mt19937 rng; std::deque last_distances; diff --git a/cpp/src/mip/local_search/local_search.cu b/cpp/src/mip/local_search/local_search.cu index ad444568fd..f0497d3827 100644 --- a/cpp/src/mip/local_search/local_search.cu +++ b/cpp/src/mip/local_search/local_search.cu @@ -105,21 +105,17 @@ bool local_search_t::run_local_search(solution_t& solution, fj_settings.feasibility_run = false; fj.set_fj_settings(fj_settings); bool is_feas = false; - ls_method_t rd = static_cast(std::uniform_int_distribution( - static_cast(ls_method_t::FJ_LINE_SEGMENT), static_cast(ls_method_t::FP_SEARCH))(rng)); + ls_method_t rd = static_cast( + std::uniform_int_distribution(static_cast(ls_method_t::FJ_ANNEALING), + static_cast(ls_method_t::FJ_LINE_SEGMENT))(rng)); if (ls_config.ls_method == ls_method_t::FJ_LINE_SEGMENT) { rd = ls_method_t::FJ_LINE_SEGMENT; } else if (ls_config.ls_method == ls_method_t::FJ_ANNEALING) { rd = ls_method_t::FJ_ANNEALING; - } else if (ls_config.ls_method == ls_method_t::FP_SEARCH) { - rd = ls_method_t::FP_SEARCH; } if (rd == ls_method_t::FJ_LINE_SEGMENT && lp_optimal_exists) { fj.copy_weights(weights, solution.handle_ptr); is_feas = run_fj_line_segment(solution, timer, ls_config); - } else if (rd == ls_method_t::FP_SEARCH) { - timer = timer_t(std::min(3., timer.remaining_time())); - is_feas = run_fp(solution, timer, &weights, false); } else { fj.copy_weights(weights, solution.handle_ptr); is_feas = run_fj_annealing(solution, timer, ls_config); @@ -201,6 +197,7 @@ bool local_search_t::check_fj_on_lp_optimal(solution_t& solu lp_optimal_solution.data(), solution.assignment.size(), solution.handle_ptr->get_stream()); + cuopt_func_call(solution.test_variable_bounds(false)); } if (perturb) { CUOPT_LOG_DEBUG("Perturbating solution on initial fj on optimal run!"); @@ -252,8 +249,9 @@ bool local_search_t::run_fj_on_zero(solution_t& solution, ti template bool local_search_t::run_staged_fp(solution_t& solution, timer_t timer, - bool& early_exit) + population_t* population_ptr) { + cuopt_assert(population_ptr != nullptr, "Population pointer must not be null"); auto n_vars = solution.problem_ptr->n_variables; auto n_binary_vars = solution.problem_ptr->get_n_binary_variables(); auto n_integer_vars = solution.problem_ptr->n_integer_vars; @@ -262,22 +260,27 @@ bool local_search_t::run_staged_fp(solution_t& solution, auto integer_only = (n_binary_vars == 0); bool is_feasible = false; - // TODO return the best solution instead of the last if (binary_only || integer_only) { - return run_fp(solution, timer); + return run_fp(solution, timer, population_ptr); } else { const i_t n_fp_iterations = 1000000; fp.cycle_queue.reset(solution); fp.reset(); fp.resize_vectors(*solution.problem_ptr, solution.handle_ptr); for (i_t i = 0; i < n_fp_iterations && !timer.check_time_limit(); ++i) { - if (early_exit) { return false; } + if (population_ptr->preempt_heuristic_solver_.load()) { + CUOPT_LOG_DEBUG("Preempting heuristic solver!"); + return false; + } CUOPT_LOG_DEBUG("Running staged FP from beginning it %d", i); fp.relax_general_integers(solution); timer_t binary_timer(timer.remaining_time() / 3); i_t binary_it_counter = 0; for (; binary_it_counter < 100; ++binary_it_counter) { - if (early_exit) { return false; } + if (population_ptr->preempt_heuristic_solver_.load()) { + CUOPT_LOG_DEBUG("Preempting heuristic solver!"); + return false; + } CUOPT_LOG_DEBUG( "Running binary problem from it %d large_restart_it %d", binary_it_counter, i); is_feasible = fp.run_single_fp_descent(solution); @@ -331,83 +334,134 @@ void local_search_t::resize_vectors(problem_t& problem, } template -void save_best_fp_solution(solution_t& solution, - rmm::device_uvector& best_solution, - f_t& best_objective, - bool feasibility_run) +void local_search_t::save_solution_and_add_cutting_plane( + solution_t& solution, rmm::device_uvector& best_solution, f_t& best_objective) { - if (feasibility_run || solution.get_objective() < best_objective) { - CUOPT_LOG_DEBUG("Found better feasible in FP with obj %f. Continue with FJ!", - solution.get_objective()); - best_objective = solution.get_objective(); + if (solution.get_objective() < best_objective) { raft::copy(best_solution.data(), solution.assignment.data(), solution.assignment.size(), solution.handle_ptr->get_stream()); - solution.problem_ptr->add_cutting_plane_at_objective(solution.get_objective() - - OBJECTIVE_EPSILON); + best_objective = solution.get_objective(); + f_t objective_cut = + best_objective - std::max(std::abs(0.001 * best_objective), OBJECTIVE_EPSILON); + problem_with_objective_cut.add_cutting_plane_at_objective(objective_cut); } } template -void local_search_t::save_solution_and_add_cutting_plane( - solution_t& solution, rmm::device_uvector& best_solution, f_t& best_objective) +void local_search_t::resize_to_new_problem() { - if (solution.get_objective() < best_objective) { - raft::copy(best_solution.data(), - solution.assignment.data(), - solution.assignment.size(), - solution.handle_ptr->get_stream()); - best_objective = solution.get_objective(); - solution.problem_ptr->add_cutting_plane_at_objective(solution.get_objective() - - OBJECTIVE_EPSILON); + resize_vectors(problem_with_objective_cut, problem_with_objective_cut.handle_ptr); + // hint for next PR in case load balanced is reintroduced + // lb_constraint_prop.temp_problem.setup(problem_with_objective_cut); + // lb_constraint_prop.bounds_update.setup(lb_constraint_prop.temp_problem); + constraint_prop.bounds_update.resize(problem_with_objective_cut); +} + +template +void local_search_t::resize_to_old_problem(problem_t* old_problem_ptr) +{ + resize_vectors(*old_problem_ptr, old_problem_ptr->handle_ptr); + // hint for next PR in case load balanced is reintroduced + // lb_constraint_prop.temp_problem.setup(*old_problem_ptr); + // lb_constraint_prop.bounds_update.setup(lb_constraint_prop.temp_problem); + constraint_prop.bounds_update.resize(*old_problem_ptr); +} + +template +void local_search_t::reset_alpha_and_run_recombiners( + solution_t& solution, + problem_t* old_problem_ptr, + population_t* population_ptr, + i_t i, + i_t last_unimproved_iteration, + rmm::device_uvector& best_solution, + f_t& best_objective) +{ + fp.config.alpha = default_alpha; + solution_t solution_copy(solution); + solution_copy.problem_ptr = old_problem_ptr; + solution_copy.resize_to_problem(); + population_ptr->add_solution(std::move(solution_copy)); + constexpr i_t iterations_for_stagnation = 3; + if (population_ptr->current_size() > 1 && + i - last_unimproved_iteration > iterations_for_stagnation) { + solution_t best_feasible_copy(population_ptr->best_feasible()); + population_ptr->run_all_recombiners(best_feasible_copy); + } + auto new_sol_vector = population_ptr->get_external_solutions(); + population_ptr->add_solutions_from_vec(std::move(new_sol_vector)); + if (!cutting_plane_added_for_active_run) { + fj.copy_weights( + population_ptr->weights, solution.handle_ptr, problem_with_objective_cut.n_constraints); + solution.problem_ptr = &problem_with_objective_cut; + solution.resize_to_problem(); + resize_to_new_problem(); + cutting_plane_added_for_active_run = true; } + save_solution_and_add_cutting_plane( + population_ptr->best_feasible(), best_solution, best_objective); } template bool local_search_t::run_fp(solution_t& solution, timer_t timer, - const weight_t* weights, - bool feasibility_run) + population_t* population_ptr) { - const i_t n_fp_iterations = 1000000; - bool is_feasible = solution.compute_feasibility(); - double best_objective = solution.get_objective(); + cuopt_assert(population_ptr != nullptr, "Population pointer must not be null"); + const i_t n_fp_iterations = 1000000; + constexpr i_t n_sol_in_population_for_exit = 4; + bool is_feasible = solution.compute_feasibility(); + cutting_plane_added_for_active_run = is_feasible; + double best_objective = + is_feasible ? solution.get_objective() : std::numeric_limits::max(); rmm::device_uvector best_solution(solution.assignment, solution.handle_ptr->get_stream()); problem_t* old_problem_ptr = solution.problem_ptr; fp.timer = timer_t(timer.remaining_time()); - if (!feasibility_run) { - // if it has not been initialized yet, create a new problem and move it to the cut problem - if (!problem_with_objective_cut.cutting_plane_added) { - problem_with_objective_cut = std::move(problem_t(*old_problem_ptr)); - } - problem_with_objective_cut.add_cutting_plane_at_objective(solution.get_objective() - - OBJECTIVE_EPSILON); + // if it has not been initialized yet, create a new problem and move it to the cut problem + if (!problem_with_objective_cut.cutting_plane_added) { + problem_with_objective_cut = std::move(problem_t(*old_problem_ptr)); + } + if (is_feasible) { + f_t objective_cut = + best_objective - std::max(std::abs(0.001 * best_objective), OBJECTIVE_EPSILON); + problem_with_objective_cut.add_cutting_plane_at_objective(objective_cut); // Do the copy here for proper handling of the added constraints weight - fj.copy_weights(*weights, solution.handle_ptr, problem_with_objective_cut.n_constraints); + fj.copy_weights( + population_ptr->weights, solution.handle_ptr, problem_with_objective_cut.n_constraints); solution.problem_ptr = &problem_with_objective_cut; solution.resize_to_problem(); - resize_vectors(problem_with_objective_cut, solution.handle_ptr); - constraint_prop.bounds_update.resize(problem_with_objective_cut); + resize_to_new_problem(); } + i_t last_unimproved_iteration = 0; for (i_t i = 0; i < n_fp_iterations && !timer.check_time_limit(); ++i) { if (timer.check_time_limit()) { is_feasible = false; break; } CUOPT_LOG_DEBUG("fp_loop it %d", i); + if (population_ptr->preempt_heuristic_solver_.load()) { + CUOPT_LOG_DEBUG("Preempting heuristic solver!"); + break; + } is_feasible = fp.run_single_fp_descent(solution); + if (population_ptr->preempt_heuristic_solver_.load()) { + CUOPT_LOG_DEBUG("Preempting heuristic solver!"); + break; + } // if feasible return true if (is_feasible) { - if (feasibility_run) { - is_feasible = true; - break; - } else { - CUOPT_LOG_DEBUG("Found feasible in FP with obj %f. Continue with FJ!", - solution.get_objective()); - save_solution_and_add_cutting_plane(solution, best_solution, best_objective); - fp.config.alpha = default_alpha; - } + CUOPT_LOG_DEBUG("Found feasible in FP with obj %f. Continue with FJ!", + solution.get_objective()); + reset_alpha_and_run_recombiners(solution, + old_problem_ptr, + population_ptr, + i, + last_unimproved_iteration, + best_solution, + best_objective); + if (population_ptr->current_size() >= n_sol_in_population_for_exit) { break; } } // if not feasible, it means it is a cycle else { @@ -416,41 +470,45 @@ bool local_search_t::run_fp(solution_t& solution, break; } is_feasible = fp.restart_fp(solution); + if (population_ptr->preempt_heuristic_solver_.load()) { + CUOPT_LOG_DEBUG("Preempting heuristic solver!"); + break; + } if (is_feasible) { - if (feasibility_run) { - is_feasible = true; - break; - } else { - CUOPT_LOG_DEBUG("Found feasible in FP with obj %f. Continue with FJ!", - solution.get_objective()); - save_solution_and_add_cutting_plane(solution, best_solution, best_objective); - fp.config.alpha = default_alpha; - } + CUOPT_LOG_DEBUG("Found feasible during restart with obj %f. Continue with FJ!", + solution.get_objective()); + reset_alpha_and_run_recombiners(solution, + old_problem_ptr, + population_ptr, + i, + last_unimproved_iteration, + best_solution, + best_objective); + if (population_ptr->current_size() >= n_sol_in_population_for_exit) { break; } + } else { + last_unimproved_iteration = i; } } } - if (!feasibility_run) { - raft::copy(solution.assignment.data(), - best_solution.data(), - solution.assignment.size(), - solution.handle_ptr->get_stream()); - solution.problem_ptr = old_problem_ptr; - solution.resize_to_problem(); - resize_vectors(*old_problem_ptr, solution.handle_ptr); - constraint_prop.bounds_update.resize(*old_problem_ptr); - solution.handle_ptr->sync_stream(); - } + raft::copy(solution.assignment.data(), + best_solution.data(), + solution.assignment.size(), + solution.handle_ptr->get_stream()); + solution.problem_ptr = old_problem_ptr; + solution.resize_to_problem(); + resize_to_old_problem(old_problem_ptr); + solution.handle_ptr->sync_stream(); return is_feasible; } template bool local_search_t::generate_solution(solution_t& solution, bool perturb, - bool& early_exit, + population_t* population_ptr, f_t time_limit) { raft::common::nvtx::range fun_scope("LS FP Loop"); - + cuopt_assert(population_ptr != nullptr, "Population pointer must not be null"); timer_t timer(time_limit); auto n_vars = solution.problem_ptr->n_variables; auto n_binary_vars = solution.problem_ptr->get_n_binary_variables(); @@ -460,6 +518,10 @@ bool local_search_t::generate_solution(solution_t& solution, CUOPT_LOG_DEBUG("Solution generated with FJ on LP optimal: is_feasible %d", is_feasible); return true; } + if (population_ptr->preempt_heuristic_solver_.load()) { + CUOPT_LOG_DEBUG("Preempting heuristic solver!"); + return is_feasible; + } if (!perturb) { raft::copy(fj_sol_on_lp_opt.data(), solution.assignment.data(), @@ -476,12 +538,16 @@ bool local_search_t::generate_solution(solution_t& solution, solution.assignment.size(), solution.handle_ptr->get_stream()); } + if (population_ptr->preempt_heuristic_solver_.load()) { + CUOPT_LOG_DEBUG("Preempting heuristic solver!"); + return is_feasible; + } fp.timer = timer; // continue with the solution with fj on lp optimal fp.cycle_queue.reset(solution); fp.reset(); fp.resize_vectors(*solution.problem_ptr, solution.handle_ptr); - is_feasible = run_staged_fp(solution, timer, early_exit); + is_feasible = run_staged_fp(solution, timer, population_ptr); // is_feasible = run_fp(solution, timer); CUOPT_LOG_DEBUG("Solution generated with FP: is_feasible %d", is_feasible); return is_feasible; diff --git a/cpp/src/mip/local_search/local_search.cuh b/cpp/src/mip/local_search/local_search.cuh index bb95a8dc55..d878b4b558 100644 --- a/cpp/src/mip/local_search/local_search.cuh +++ b/cpp/src/mip/local_search/local_search.cuh @@ -17,6 +17,7 @@ #pragma once +#include #include #include #include @@ -29,7 +30,6 @@ namespace cuopt::linear_programming::detail { enum class ls_method_t : int { FJ_ANNEALING = 0, FJ_LINE_SEGMENT, - FP_SEARCH, RANDOM, LS_METHODS_SIZE = RANDOM }; @@ -55,7 +55,7 @@ class local_search_t { void generate_fast_solution(solution_t& solution, timer_t timer); bool generate_solution(solution_t& solution, bool perturb, - bool& early_exit, + population_t* population_ptr, f_t time_limit = 300.); bool run_fj_until_timer(solution_t& solution, const weight_t& weights, @@ -72,15 +72,25 @@ class local_search_t { const ls_config_t& ls_config); bool run_fj_on_zero(solution_t& solution, timer_t timer); bool check_fj_on_lp_optimal(solution_t& solution, bool perturb, timer_t timer); - bool run_staged_fp(solution_t& solution, timer_t timer, bool& early_exit); + bool run_staged_fp(solution_t& solution, + timer_t timer, + population_t* population_ptr); bool run_fp(solution_t& solution, timer_t timer, - const weight_t* weights = nullptr, - bool feasibility_run = true); + population_t* population_ptr = nullptr); void resize_vectors(problem_t& problem, const raft::handle_t* handle_ptr); void save_solution_and_add_cutting_plane(solution_t& solution, rmm::device_uvector& best_solution, f_t& best_objective); + void resize_to_new_problem(); + void resize_to_old_problem(problem_t* old_problem_ptr); + void reset_alpha_and_run_recombiners(solution_t& solution, + problem_t* old_problem_ptr, + population_t* population_ptr, + i_t i, + i_t last_unimproved_iteration, + rmm::device_uvector& best_solution, + f_t& best_objective); mip_solver_context_t& context; rmm::device_uvector& lp_optimal_solution; @@ -93,6 +103,7 @@ class local_search_t { feasibility_pump_t fp; std::mt19937 rng; problem_t problem_with_objective_cut; + bool cutting_plane_added_for_active_run{false}; }; } // namespace cuopt::linear_programming::detail diff --git a/cpp/src/mip/local_search/rounding/constraint_prop.cu b/cpp/src/mip/local_search/rounding/constraint_prop.cu index 61e8e08675..4dfd1b216b 100644 --- a/cpp/src/mip/local_search/rounding/constraint_prop.cu +++ b/cpp/src/mip/local_search/rounding/constraint_prop.cu @@ -17,6 +17,7 @@ #include #include +#include #include #include #include "constraint_prop.cuh" @@ -331,21 +332,16 @@ template struct find_unset_int_t { // This functor should be called only on integer variables f_t eps; - raft::device_span var_lb; - raft::device_span var_ub; raft::device_span assignment; - find_unset_int_t(f_t eps_, - raft::device_span lb_, - raft::device_span ub_, - raft::device_span assignment_) - : eps(eps_), var_lb(lb_), var_ub(ub_), assignment(assignment_) + find_unset_int_t(f_t eps_, raft::device_span assignment_) + : eps(eps_), assignment(assignment_) { } HDI bool operator()(i_t idx) { auto var_val = assignment[idx]; - bool is_set = is_integer(var_val); + bool is_set = is_integer(var_val, eps); return !is_set; } }; @@ -826,6 +822,21 @@ bool constraint_prop_t::run_repair_procedure(problem_t& prob return true; } +template +void constraint_prop_t::find_unset_integer_vars(solution_t& sol, + rmm::device_uvector& unset_vars) +{ + unset_vars.resize(sol.problem_ptr->n_integer_vars, sol.handle_ptr->get_stream()); + auto iter = + thrust::copy_if(sol.handle_ptr->get_thrust_policy(), + sol.problem_ptr->integer_indices.begin(), + sol.problem_ptr->integer_indices.end(), + unset_vars.begin(), + find_unset_int_t{sol.problem_ptr->tolerances.integrality_tolerance, + make_span(sol.assignment)}); + unset_vars.resize(iter - unset_vars.begin(), sol.handle_ptr->get_stream()); +} + template bool constraint_prop_t::is_problem_ii(problem_t& problem) { @@ -865,10 +876,35 @@ bool constraint_prop_t::find_integer( cuopt_func_call(orig_sol.test_variable_bounds()); return orig_sol.compute_feasibility(); } - raft::copy(unset_integer_vars.data(), - sol.problem_ptr->integer_indices.data(), - sol.problem_ptr->n_integer_vars, - sol.handle_ptr->get_stream()); + if (round_all_vars) { + raft::copy(unset_integer_vars.data(), + sol.problem_ptr->integer_indices.data(), + sol.problem_ptr->n_integer_vars, + sol.handle_ptr->get_stream()); + } else { + find_unset_integer_vars(sol, unset_integer_vars); + sort_by_frac(sol, make_span(unset_integer_vars)); + // round first unset_integer_vars.size() - 50, leave last 50 to be rounded by the algo + i_t n_to_round = std::max(unset_integer_vars.size() - 50, 0lu); + if (n_to_round > 0) { + thrust::for_each( + sol.handle_ptr->get_thrust_policy(), + unset_integer_vars.begin(), + unset_integer_vars.begin() + n_to_round, + [sol = sol.view(), seed = cuopt::seed_generator::get_seed()] __device__(i_t var_idx) { + raft::random::PCGenerator rng(seed, var_idx, 0); + auto var_bnd = sol.problem.variable_bounds[var_idx]; + sol.assignment[var_idx] = round_nearest(sol.assignment[var_idx], + get_lower(var_bnd), + get_upper(var_bnd), + sol.problem.tolerances.integrality_tolerance, + rng); + }); + find_unset_integer_vars(sol, unset_integer_vars); + } + set_bounds_on_fixed_vars(sol); + } + CUOPT_LOG_DEBUG("Bounds propagation rounding: unset vars %lu", unset_integer_vars.size()); if (unset_integer_vars.size() == 0) { CUOPT_LOG_ERROR("No integer variables provided in the bounds prop rounding"); @@ -1009,7 +1045,7 @@ bool constraint_prop_t::find_integer( // if the constraint is not ii, run LP if ((multi_probe.infeas_constraints_count_0 == 0 || multi_probe.infeas_constraints_count_1 == 0) && - !timeout_happened) { + !timeout_happened && lp_run_time_after_feasible > 0) { relaxed_lp_settings_t lp_settings; lp_settings.time_limit = lp_run_time_after_feasible; lp_settings.tolerance = orig_sol.problem_ptr->tolerances.absolute_tolerance; @@ -1034,10 +1070,7 @@ bool constraint_prop_t::apply_round( std::optional>> probing_config) { raft::common::nvtx::range fun_scope("constraint prop round"); - - // this is second timer that can continue but without recovery mode - const f_t max_time_for_bounds_prop = 5.; - max_timer = timer_t{max_time_for_bounds_prop}; + max_timer = timer_t{max_time_for_bounds_prop}; if (check_brute_force_rounding(sol)) { return true; } recovery_mode = false; rounding_ii = false; diff --git a/cpp/src/mip/local_search/rounding/constraint_prop.cuh b/cpp/src/mip/local_search/rounding/constraint_prop.cuh index 3b01da2749..591f25f36b 100644 --- a/cpp/src/mip/local_search/rounding/constraint_prop.cuh +++ b/cpp/src/mip/local_search/rounding/constraint_prop.cuh @@ -69,7 +69,7 @@ struct constraint_prop_t { std::optional>> probing_config = std::nullopt); void find_set_integer_vars(solution_t& sol, rmm::device_uvector& set_vars); - void find_unset_integer_vars(solution_t& sol, rmm::device_uvector& set_vars); + void find_unset_integer_vars(solution_t& sol, rmm::device_uvector& unset_vars); thrust::pair generate_double_probing_pair( const solution_t& sol, const solution_t& orig_sol, @@ -162,6 +162,9 @@ struct constraint_prop_t { bool use_probing_cache = true; static repair_stats_t repair_stats; bool single_rounding_only = false; + bool round_all_vars = true; + // this is second timer that can continue but without recovery mode + f_t max_time_for_bounds_prop = 5.; }; } // namespace cuopt::linear_programming::detail diff --git a/cpp/src/mip/presolve/load_balanced_bounds_presolve_helpers.cuh b/cpp/src/mip/presolve/load_balanced_bounds_presolve_helpers.cuh index f3e5e2ee0e..078169f7c6 100644 --- a/cpp/src/mip/presolve/load_balanced_bounds_presolve_helpers.cuh +++ b/cpp/src/mip/presolve/load_balanced_bounds_presolve_helpers.cuh @@ -126,7 +126,7 @@ i_t create_heavy_item_block_segments(rmm::cuda_stream_view stream, thrust::for_each( rmm::exec_policy(stream), thrust::make_counting_iterator(0), - thrust::make_counting_iterator(item_block_segments.size()), + thrust::make_counting_iterator(item_block_segments.size() - 1), heavy_vertex_meta_t{ make_span(item_block_segments), make_span(vertex_id), make_span(pseudo_block_id)}); thrust::inclusive_scan(rmm::exec_policy(stream), diff --git a/cpp/src/mip/presolve/probing_cache.cu b/cpp/src/mip/presolve/probing_cache.cu index 7389ec206f..9e586a220e 100644 --- a/cpp/src/mip/presolve/probing_cache.cu +++ b/cpp/src/mip/presolve/probing_cache.cu @@ -478,7 +478,7 @@ void compute_probing_cache(bound_presolve_t& bound_presolve, bound_presolve.settings.time_limit = timer.remaining_time(); // Set the number of threads - const size_t max_threads = 10; + const size_t max_threads = 8; omp_set_num_threads(max_threads); // Create a vector of multi_probe_t objects diff --git a/cpp/src/mip/presolve/trivial_presolve.cuh b/cpp/src/mip/presolve/trivial_presolve.cuh index 803bfca800..d7fe142336 100644 --- a/cpp/src/mip/presolve/trivial_presolve.cuh +++ b/cpp/src/mip/presolve/trivial_presolve.cuh @@ -265,10 +265,10 @@ void update_from_csr(problem_t& pb) pb.n_constraints = updated_n_cnst; pb.n_variables = updated_n_vars; - CUOPT_LOG_INFO("After trivial presolve updated %d constraints %d variables. Objective offset %f", - updated_n_cnst, - updated_n_vars, - pb.presolve_data.objective_offset); + CUOPT_LOG_DEBUG("After trivial presolve #constraints %d #variables %d. Objective offset %f", + updated_n_cnst, + updated_n_vars, + pb.presolve_data.objective_offset); // check successive cnst in coo increases by atmost 1 // update csr offset pb.offsets.resize(pb.n_constraints + 1, handle_ptr->get_stream()); diff --git a/cpp/src/mip/problem/problem.cu b/cpp/src/mip/problem/problem.cu index 0e5cf510c4..e4ab0835de 100644 --- a/cpp/src/mip/problem/problem.cu +++ b/cpp/src/mip/problem/problem.cu @@ -1578,7 +1578,7 @@ void problem_t::compute_vars_with_objective_coeffs() template void problem_t::add_cutting_plane_at_objective(f_t objective) { - CUOPT_LOG_INFO("Adding cutting plane at objective %f", objective); + CUOPT_LOG_DEBUG("Adding cutting plane at objective %f", objective); if (cutting_plane_added) { // modify the RHS i_t last_constraint = n_constraints - 1; diff --git a/cpp/src/mip/relaxed_lp/lp_state.cuh b/cpp/src/mip/relaxed_lp/lp_state.cuh index 0961a537f8..3bfa00955c 100644 --- a/cpp/src/mip/relaxed_lp/lp_state.cuh +++ b/cpp/src/mip/relaxed_lp/lp_state.cuh @@ -31,14 +31,10 @@ class lp_state_t { lp_state_t(problem_t& problem, rmm::cuda_stream_view stream) : prev_primal(problem.n_variables, stream), prev_dual(problem.n_constraints, stream) { - thrust::fill(problem.handle_ptr->get_thrust_policy(), - prev_primal.data(), - prev_primal.data() + problem.n_variables, - 0); - thrust::fill(problem.handle_ptr->get_thrust_policy(), - prev_dual.data(), - prev_dual.data() + problem.n_constraints, - 0); + thrust::fill( + rmm::exec_policy(stream), prev_primal.data(), prev_primal.data() + problem.n_variables, 0); + thrust::fill( + rmm::exec_policy(stream), prev_dual.data(), prev_dual.data() + problem.n_constraints, 0); } lp_state_t(problem_t& problem) : lp_state_t(problem, problem.handle_ptr->get_stream()) diff --git a/cpp/src/mip/relaxed_lp/relaxed_lp.cu b/cpp/src/mip/relaxed_lp/relaxed_lp.cu index 790c50b179..d28ad7fbb3 100644 --- a/cpp/src/mip/relaxed_lp/relaxed_lp.cu +++ b/cpp/src/mip/relaxed_lp/relaxed_lp.cu @@ -56,10 +56,10 @@ optimization_problem_solution_t get_relaxed_lp_solution( pdlp_settings.tolerances.relative_dual_tolerance = settings.tolerance / 100.; pdlp_settings.time_limit = settings.time_limit; pdlp_settings.concurrent_halt = settings.concurrent_halt; - if (settings.return_first_feasible) { pdlp_settings.per_constraint_residual = true; } - pdlp_settings.first_primal_feasible = settings.return_first_feasible; + pdlp_settings.per_constraint_residual = settings.per_constraint_residual; + pdlp_settings.first_primal_feasible = settings.return_first_feasible; pdlp_solver_t lp_solver(op_problem, pdlp_settings); - if (settings.save_state) { + if (settings.has_initial_primal) { i_t prev_size = lp_state.prev_dual.size(); CUOPT_LOG_DEBUG( "setting initial primal solution of size %d dual size %d problem vars %d cstrs %d", diff --git a/cpp/src/mip/relaxed_lp/relaxed_lp.cuh b/cpp/src/mip/relaxed_lp/relaxed_lp.cuh index a5fe23adb8..0e44a1a555 100644 --- a/cpp/src/mip/relaxed_lp/relaxed_lp.cuh +++ b/cpp/src/mip/relaxed_lp/relaxed_lp.cuh @@ -32,7 +32,8 @@ struct relaxed_lp_settings_t { bool check_infeasibility = true; bool return_first_feasible = false; bool save_state = true; - bool per_constraint_residual = false; + bool per_constraint_residual = true; + bool has_initial_primal = true; std::atomic* concurrent_halt = nullptr; }; diff --git a/cpp/src/mip/solve.cu b/cpp/src/mip/solve.cu index 09da4b376d..edff4a6939 100644 --- a/cpp/src/mip/solve.cu +++ b/cpp/src/mip/solve.cu @@ -106,16 +106,15 @@ mip_solution_t run_mip(detail::problem_t& problem, "Size mismatch"); cuopt_assert(problem.original_problem_ptr->get_n_constraints() == scaled_problem.n_constraints, "Size mismatch"); - detail::pdhg_solver_t pdhg_solver(scaled_problem.handle_ptr, scaled_problem); detail::pdlp_initial_scaling_strategy_t scaling( scaled_problem.handle_ptr, scaled_problem, pdlp_hyper_params::default_l_inf_ruiz_iterations, (f_t)pdlp_hyper_params::default_alpha_pock_chambolle_rescaling, - pdhg_solver, scaled_problem.reverse_coefficients, scaled_problem.reverse_offsets, scaled_problem.reverse_constraints, + nullptr, running_mip); cuopt_func_call(auto saved_problem = scaled_problem); @@ -157,12 +156,13 @@ mip_solution_t solve_mip(optimization_problem_t& op_problem, { try { constexpr f_t max_time_limit = 1000000000; - const f_t time_limit = settings.time_limit == 0 ? max_time_limit : settings.time_limit; - if (settings.heuristics_only && time_limit == std::numeric_limits::max()) { - CUOPT_LOG_ERROR("Time limit cannot be infinity when heuristics only is set"); - cuopt_expects(false, - error_type_t::RuntimeError, - "Time limit cannot be infinity when heuristics only is set"); + f_t time_limit = + (settings.time_limit == 0 || settings.time_limit == std::numeric_limits::infinity()) + ? max_time_limit + : settings.time_limit; + if (settings.heuristics_only && (time_limit == std::numeric_limits::max() || + time_limit == std::numeric_limits::infinity())) { + time_limit = max_time_limit; } // Create log stream for file logging and add it to default logger diff --git a/cpp/src/mip/solver.cu b/cpp/src/mip/solver.cu index 5e27a65eaf..ea32724191 100644 --- a/cpp/src/mip/solver.cu +++ b/cpp/src/mip/solver.cu @@ -76,9 +76,11 @@ struct branch_and_bound_solution_helper_t { dm->population.add_external_solution(solution, objective); } - void set_simplex_solution(std::vector& solution, f_t objective) + void set_simplex_solution(std::vector& solution, + std::vector& dual_solution, + f_t objective) { - dm->set_simplex_solution(solution, objective); + dm->set_simplex_solution(solution, dual_solution, objective); } void preempt_heuristic_solver() { dm->population.preempt_heuristic_solver(); } @@ -187,7 +189,8 @@ solution_t mip_solver_t::run_solver() std::bind(&branch_and_bound_solution_helper_t::set_simplex_solution, &solution_helper, std::placeholders::_1, - std::placeholders::_2); + std::placeholders::_2, + std::placeholders::_3); // Create the branch and bound object branch_and_bound = std::make_unique>( diff --git a/cpp/tests/mip/bounds_standardization_test.cu b/cpp/tests/mip/bounds_standardization_test.cu index 77b4acfd7b..14aa271cd7 100644 --- a/cpp/tests/mip/bounds_standardization_test.cu +++ b/cpp/tests/mip/bounds_standardization_test.cu @@ -78,8 +78,9 @@ void test_bounds_standardization_test(std::string test_instance) mip_solver_settings_t default_settings{}; detail::relaxed_lp_settings_t lp_settings; - lp_settings.time_limit = 120.; - lp_settings.tolerance = default_settings.tolerances.absolute_tolerance; + lp_settings.time_limit = 120.; + lp_settings.tolerance = default_settings.tolerances.absolute_tolerance; + lp_settings.per_constraint_residual = false; // run the problem through pdlp auto result_1 = detail::get_relaxed_lp_solution(standardized_problem, solution_1, lp_settings); diff --git a/cpp/tests/mip/elim_var_remap_test.cu b/cpp/tests/mip/elim_var_remap_test.cu index c486d98c81..e6aa6ec17f 100644 --- a/cpp/tests/mip/elim_var_remap_test.cu +++ b/cpp/tests/mip/elim_var_remap_test.cu @@ -163,8 +163,9 @@ void test_elim_var_solution(std::string test_instance) detail::solution_t solution_1(standardized_problem); detail::relaxed_lp_settings_t lp_settings; - lp_settings.time_limit = 120.; - lp_settings.tolerance = default_settings.tolerances.absolute_tolerance; + lp_settings.time_limit = 120.; + lp_settings.tolerance = default_settings.tolerances.absolute_tolerance; + lp_settings.per_constraint_residual = false; // run the problem through pdlp auto result_1 = detail::get_relaxed_lp_solution(standardized_problem, solution_1, lp_settings); solution_1.compute_feasibility(); @@ -192,8 +193,9 @@ void test_elim_var_solution(std::string test_instance) detail::solution_t solution_2(sub_problem); detail::relaxed_lp_settings_t lp_settings_2; - lp_settings_2.time_limit = 120.; - lp_settings_2.tolerance = default_settings.tolerances.absolute_tolerance; + lp_settings_2.time_limit = 120.; + lp_settings_2.tolerance = default_settings.tolerances.absolute_tolerance; + lp_settings_2.per_constraint_residual = false; // run the problem through pdlp auto result_2 = detail::get_relaxed_lp_solution(sub_problem, solution_2, lp_settings_2); solution_2.compute_feasibility(); diff --git a/cpp/tests/mip/load_balancing_test.cu b/cpp/tests/mip/load_balancing_test.cu index fb0d8b6e86..6d4762d301 100644 --- a/cpp/tests/mip/load_balancing_test.cu +++ b/cpp/tests/mip/load_balancing_test.cu @@ -141,10 +141,10 @@ void test_multi_probe(std::string path) problem, 10, 1.0, - pdhg_solver, problem.reverse_coefficients, problem.reverse_offsets, problem.reverse_constraints, + nullptr, true); detail::mip_solver_t solver(problem, default_settings, scaling, cuopt::timer_t(0)); detail::load_balanced_problem_t lb_problem(problem); diff --git a/cpp/tests/mip/multi_probe_test.cu b/cpp/tests/mip/multi_probe_test.cu index 1473c84bff..10d6bc7bce 100644 --- a/cpp/tests/mip/multi_probe_test.cu +++ b/cpp/tests/mip/multi_probe_test.cu @@ -158,15 +158,14 @@ void test_multi_probe(std::string path) problem_checking_t::check_problem_representation(op_problem); detail::problem_t problem(op_problem); mip_solver_settings_t default_settings{}; - detail::pdhg_solver_t pdhg_solver(problem.handle_ptr, problem); detail::pdlp_initial_scaling_strategy_t scaling(&handle_, problem, 10, 1.0, - pdhg_solver, problem.reverse_coefficients, problem.reverse_offsets, problem.reverse_constraints, + nullptr, true); detail::mip_solver_t solver(problem, default_settings, scaling, cuopt::timer_t(0)); detail::bound_presolve_t bnd_prb_0(solver.context); diff --git a/docs/cuopt/source/lp-milp-settings.rst b/docs/cuopt/source/lp-milp-settings.rst index bb9541f4ab..b97f31f168 100644 --- a/docs/cuopt/source/lp-milp-settings.rst +++ b/docs/cuopt/source/lp-milp-settings.rst @@ -280,14 +280,14 @@ Absolute Tolerance ``CUOPT_MIP_ABSOLUTE_TOLERANCE`` controls the MIP absolute tolerance. -Note: the default value is ``1e-4``. +Note: the default value is ``1e-6``. Relative Tolerance ^^^^^^^^^^^^^^^^^^ ``CUOPT_MIP_RELATIVE_TOLERANCE`` controls the MIP relative tolerance. -Note: the default value is ``1e-6``. +Note: the default value is ``1e-12``. Integrality Tolerance