-
Notifications
You must be signed in to change notification settings - Fork 173
Detect MIP symmetry using dejavu; Perform orbital fixing and lexical reduction #1103
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
Changes from 14 commits
4fe3f94
ada114b
6425c25
f447d4c
89ce17f
fcd28d7
96010da
7ea524f
426582a
07d421b
24acd6f
659a7c4
7bd9569
2aba39f
d4658ed
6cdc089
1dfa784
9c41f40
18cf2df
c607017
98cef5a
a422f86
e054968
e787ec1
a7126e4
c890f2f
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 |
|---|---|---|
|
|
@@ -8,6 +8,7 @@ | |
| #include <branch_and_bound/branch_and_bound.hpp> | ||
| #include <branch_and_bound/mip_node.hpp> | ||
| #include <branch_and_bound/pseudo_costs.hpp> | ||
| #include <branch_and_bound/symmetry.hpp> | ||
|
|
||
| #include <cuts/cuts.hpp> | ||
| #include <mip_heuristics/presolve/conflict_graph/clique_table.cuh> | ||
|
|
@@ -248,11 +249,13 @@ branch_and_bound_t<i_t, f_t>::branch_and_bound_t( | |
| const simplex_solver_settings_t<i_t, f_t>& solver_settings, | ||
| f_t start_time, | ||
| const probing_implied_bound_t<i_t, f_t>& probing_implied_bound, | ||
| std::shared_ptr<detail::clique_table_t<i_t, f_t>> clique_table) | ||
| std::shared_ptr<detail::clique_table_t<i_t, f_t>> clique_table, | ||
| mip_symmetry_t<i_t, f_t>* symmetry) | ||
| : original_problem_(user_problem), | ||
| settings_(solver_settings), | ||
| probing_implied_bound_(probing_implied_bound), | ||
| clique_table_(std::move(clique_table)), | ||
| symmetry_(symmetry), | ||
| original_lp_(user_problem.handle_ptr, 1, 1, 1), | ||
| Arow_(1, 1, 0), | ||
| incumbent_(1), | ||
|
|
@@ -299,10 +302,11 @@ branch_and_bound_t<i_t, f_t>::branch_and_bound_t( | |
| template <typename i_t, typename f_t> | ||
| f_t branch_and_bound_t<i_t, f_t>::get_lower_bound() | ||
| { | ||
| f_t lower_bound = lower_bound_ceiling_.load(); | ||
| f_t heap_lower_bound = node_queue_.get_lower_bound(); | ||
| lower_bound = std::min(heap_lower_bound, lower_bound); | ||
| lower_bound = std::min(worker_pool_.get_lower_bound(), lower_bound); | ||
| f_t lower_bound = lower_bound_ceiling_.load(); | ||
| f_t heap_lower_bound = node_queue_.get_lower_bound(); | ||
| f_t worker_lower_bound = worker_pool_.get_lower_bound(); | ||
| lower_bound = std::min(heap_lower_bound, lower_bound); | ||
| lower_bound = std::min(worker_lower_bound, lower_bound); | ||
|
|
||
| if (std::isfinite(lower_bound)) { | ||
| return lower_bound; | ||
|
|
@@ -729,6 +733,14 @@ void branch_and_bound_t<i_t, f_t>::set_final_solution(mip_solution_t<i_t, f_t>& | |
| settings_.log.printf("Explored %d nodes in %.2fs.\n", | ||
| exploration_stats_.nodes_explored, | ||
| toc(exploration_stats_.start_time)); | ||
| if (exploration_stats_.orbital_fixing_nodes.load() > 0 || | ||
| exploration_stats_.orbital_conflict_nodes.load() > 0) { | ||
| settings_.log.printf("Orbital fixing applied at %lld nodes, %lld total variable fixings, " | ||
| "%lld nodes with conflicting orbits\n", | ||
| (long long)exploration_stats_.orbital_fixing_nodes.load(), | ||
| (long long)exploration_stats_.orbital_fixings_applied.load(), | ||
| (long long)exploration_stats_.orbital_conflict_nodes.load()); | ||
| } | ||
| settings_.log.printf("Absolute Gap %e Objective %.16e %s Bound %.16e\n", | ||
| gap, | ||
| obj, | ||
|
|
@@ -1386,8 +1398,29 @@ dual::status_t branch_and_bound_t<i_t, f_t>::solve_node_lp( | |
| bool feasible = worker->set_lp_variable_bounds(node_ptr, settings_); | ||
| dual::status_t lp_status = dual::status_t::DUAL_UNBOUNDED; | ||
| worker->leaf_edge_norms = edge_norms_; | ||
| if (worker->recompute_bounds && worker->orbital_fixing && | ||
| worker->search_strategy == BEST_FIRST) { | ||
| worker->orbital_fixing->reset(symmetry_, node_ptr); | ||
| } | ||
|
|
||
| if (feasible) { | ||
|
|
||
| // Perform orbital fixing | ||
| auto* of = worker->orbital_fixing.get(); | ||
|
chris-maes marked this conversation as resolved.
Outdated
|
||
| if (of != nullptr && !of->disabled()) { | ||
| i_t prev_fix = node_ptr->orbital_fix_zero.size() + node_ptr->orbital_fix_one.size(); | ||
| i_t conflicts = of->orbital_fixing(symmetry_, settings_, node_ptr, worker->leaf_problem, | ||
| worker->start_lower, worker->start_upper); | ||
| i_t new_fix = node_ptr->orbital_fix_zero.size() + node_ptr->orbital_fix_one.size(); | ||
| if (new_fix > prev_fix) { | ||
| ++stats.orbital_fixing_nodes; | ||
| stats.orbital_fixings_applied += (new_fix - prev_fix); | ||
| } | ||
| if (conflicts > 0) { ++stats.orbital_conflict_nodes; } | ||
| } else if (of != nullptr) { | ||
| of->propagate_cumulative_fixings(node_ptr); | ||
| } | ||
|
|
||
| i_t node_iter = 0; | ||
| f_t lp_start_time = tic(); | ||
|
|
||
|
|
@@ -1437,6 +1470,7 @@ void branch_and_bound_t<i_t, f_t>::plunge_with(branch_and_bound_worker_t<i_t, f_ | |
| stack.push_front(worker->start_node); | ||
| worker->recompute_basis = true; | ||
| worker->recompute_bounds = true; | ||
| worker->ensure_orbital_fixing(); | ||
|
|
||
| f_t lower_bound = get_lower_bound(); | ||
| f_t upper_bound = upper_bound_; | ||
|
|
@@ -1562,6 +1596,7 @@ template <typename i_t, typename f_t> | |
| void branch_and_bound_t<i_t, f_t>::dive_with(branch_and_bound_worker_t<i_t, f_t>* worker) | ||
| { | ||
| raft::common::nvtx::range scope("BB::diving_thread"); | ||
| if (worker->orbital_fixing) { worker->orbital_fixing->disable(); } | ||
| logger_t log; | ||
| log.log = false; | ||
|
|
||
|
|
@@ -1658,7 +1693,7 @@ void branch_and_bound_t<i_t, f_t>::run_scheduler() | |
| std::array<i_t, num_search_strategies> max_num_workers_per_type = | ||
| get_max_workers(num_workers, strategies); | ||
|
|
||
| worker_pool_.init(num_workers, original_lp_, Arow_, var_types_, settings_); | ||
| worker_pool_.init(num_workers, original_lp_, Arow_, var_types_, symmetry_, settings_); | ||
| active_workers_per_strategy_.fill(0); | ||
|
|
||
| #ifdef CUOPT_LOG_DEBUG | ||
|
|
@@ -1800,15 +1835,15 @@ void branch_and_bound_t<i_t, f_t>::run_scheduler() | |
| template <typename i_t, typename f_t> | ||
| void branch_and_bound_t<i_t, f_t>::single_threaded_solve() | ||
| { | ||
| branch_and_bound_worker_t<i_t, f_t> worker(0, original_lp_, Arow_, var_types_, settings_); | ||
| worker_pool_.init(1, original_lp_, Arow_, var_types_, symmetry_, settings_); | ||
| branch_and_bound_worker_t<i_t, f_t>* worker = worker_pool_.get_idle_worker(); | ||
|
|
||
|
Comment on lines
+1889
to
1891
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. Single-thread worker can remain active with stale lower bound after plunge.
🔧 Proposed fix worker->init_best_first(start_node.value(), original_lp_);
plunge_with(worker);
+ worker->is_active = false; // prevent stale worker bound from affecting global lower boundAlso applies to: 1860-1861 🤖 Prompt for AI Agents |
||
| f_t lower_bound = get_lower_bound(); | ||
| f_t abs_gap = compute_user_abs_gap(original_lp_, upper_bound_.load(), lower_bound); | ||
| f_t rel_gap = user_relative_gap(original_lp_, upper_bound_.load(), lower_bound); | ||
|
|
||
| while (solver_status_ == mip_status_t::UNSET && abs_gap > settings_.absolute_mip_gap_tol && | ||
| rel_gap > settings_.relative_mip_gap_tol && node_queue_.best_first_queue_size() > 0) { | ||
| bool launched_any_task = false; | ||
| repair_heuristic_solutions(); | ||
|
|
||
| f_t now = toc(exploration_stats_.start_time); | ||
|
|
@@ -1844,8 +1879,8 @@ void branch_and_bound_t<i_t, f_t>::single_threaded_solve() | |
| continue; | ||
| } | ||
|
|
||
| worker.init_best_first(start_node.value(), original_lp_); | ||
| plunge_with(&worker); | ||
| worker->init_best_first(start_node.value(), original_lp_); | ||
| plunge_with(worker); | ||
|
|
||
| lower_bound = get_lower_bound(); | ||
| abs_gap = compute_user_abs_gap(original_lp_, upper_bound_.load(), lower_bound); | ||
|
|
@@ -2523,6 +2558,7 @@ mip_status_t branch_and_bound_t<i_t, f_t>::solve(mip_solution_t<i_t, f_t>& solut | |
| basic_list, | ||
| nonbasic_list, | ||
| basis_update, | ||
| symmetry_, | ||
| pc_); | ||
| } | ||
|
|
||
|
|
@@ -2592,6 +2628,17 @@ mip_status_t branch_and_bound_t<i_t, f_t>::solve(mip_solution_t<i_t, f_t>& solut | |
| node_queue_.push(search_tree_.root.get_down_child()); | ||
| node_queue_.push(search_tree_.root.get_up_child()); | ||
|
|
||
| if (symmetry_ != nullptr) { | ||
| i_t removed = symmetry_->generators.template prune_by_bounds<f_t>( | ||
|
chris-maes marked this conversation as resolved.
Outdated
|
||
| original_lp_.lower, original_lp_.upper); | ||
| if (removed > 0) { | ||
| symmetry_->num_generators = static_cast<int>(symmetry_->generators.num_generators()); | ||
| settings_.log.printf( | ||
| "Pruned %d generators invalidated by root-level bound tightening, %d remain\n", | ||
| removed, symmetry_->num_generators); | ||
| } | ||
| } | ||
|
|
||
| settings_.log.printf("Exploring the B&B tree using %d threads\n\n", settings_.num_threads); | ||
| node_concurrent_halt_ = 0; | ||
|
|
||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.