Skip to content

Commit 380f7ee

Browse files
authored
Block Bounded Value Elimination presolving pass for binary problems (#1683)
## Issue Authors: - Alice Boucher (https://github.com/aliceb-nv) Approvers: - Ramakrishna Prabhu (https://github.com/ramakrishnap-nv) - Akif ÇÖRDÜK (https://github.com/akifcorduk) URL: #1683
1 parent c7c94d2 commit 380f7ee

38 files changed

Lines changed: 3822 additions & 198 deletions

ci/validate_wheel.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ PYDISTCHECK_ARGS=(
2222
if [[ "${package_dir}" == "python/libcuopt" ]]; then
2323
if [[ "${RAPIDS_CUDA_MAJOR}" == "12" ]]; then
2424
PYDISTCHECK_ARGS+=(
25-
--max-allowed-size-compressed '690Mi'
25+
--max-allowed-size-compressed '695Mi'
2626
)
2727
else
2828
PYDISTCHECK_ARGS+=(

cpp/include/cuopt/mathematical_optimization/constants.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,9 @@
146146
#define CUOPT_MIP_HYPER_SUBMIP_ITERATION_LIMIT_RATIO "mip_hyper_submip_iteration_limit_ratio"
147147
#define CUOPT_MIP_HYPER_SUBMIP_ENABLE_CPUFJ "mip_hyper_submip_enable_cpufj"
148148

149+
/* @brief Block bounded-variable-elimination step of cuOpt's internal MIP presolve */
150+
#define CUOPT_MIP_HYPER_BLOCK_BVE "mip_hyper_block_bve"
151+
149152
/* @brief QCQP (barrier) scaling hyper-parameters */
150153
#define CUOPT_QCQP_HYPER_RUIZ_EQUILIBRATION "qcqp_hyper_ruiz_equilibration"
151154

cpp/include/cuopt/mathematical_optimization/mip/solver_settings.hpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,15 @@ class mip_solver_settings_t {
162162
* When this is `false`, probing is skipped even if presolve is otherwise on.
163163
*/
164164
bool probing{true};
165+
/**
166+
* @brief Enable the block bounded-variable-elimination step of cuOpt's MIP presolve.
167+
*
168+
* Runs after trivial_presolve and eliminates blocks of functionally-determined binary auxiliary
169+
* variables discovered via the probing-cache implication closure, re-encoding each block's
170+
* projected relation as certified prime-implicate clauses. Requires the probing-cache step; a
171+
* no-op when no certified reduction exists.
172+
*/
173+
bool block_bve{true};
165174
/**
166175
* @brief Determinism mode for MIP solver.
167176
*

cpp/src/io/mps_writer.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -229,8 +229,8 @@ void mps_writer_t<i_t, f_t>::write(const std::string& mps_file_path)
229229
// save coefficients with full precision
230230
mps_file << std::setprecision(std::numeric_limits<f_t>::max_digits10);
231231

232-
// NAME section
233-
mps_file << "NAME " << problem_.get_problem_name() << "\n";
232+
const std::string& pname = problem_.get_problem_name();
233+
mps_file << "NAME " << (pname.empty() ? "cuopt" : pname) << "\n";
234234

235235
if (problem_.get_sense()) { mps_file << "OBJSENSE\n MAXIMIZE\n"; }
236236

cpp/src/math_optimization/solver_settings.cu

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,7 @@ solver_settings_t<i_t, f_t>::solver_settings_t() : pdlp_settings(), mip_settings
213213
{CUOPT_MIP_HYPER_DIVING_SHOW_TYPE, &mip_settings.diving_params.show_type, false, "log diving heuristic type when it finds a new incumbent"},
214214
// Recursive sub-MIP (RINS) hyper-parameters (hidden from default --help: name contains "hyper_")
215215
{CUOPT_MIP_HYPER_SUBMIP_ENABLE_CPUFJ, &mip_settings.submip_params.enable_cpufj, true, "run CPU FJ over the sub-MIP"},
216+
{CUOPT_MIP_HYPER_BLOCK_BVE, &mip_settings.block_bve, true, "eliminate blocks of binaries in cuOpt's MIP presolve (needs " CUOPT_MIP_PROBING ")"},
216217
};
217218
// String parameters
218219
string_parameters = {

cpp/src/mip_heuristics/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ set(MIP_NON_LP_FILES
3333
${CMAKE_CURRENT_SOURCE_DIR}/local_search/rounding/simple_rounding.cu
3434
${CMAKE_CURRENT_SOURCE_DIR}/local_search/feasibility_pump/feasibility_pump.cu
3535
${CMAKE_CURRENT_SOURCE_DIR}/local_search/line_segment_search/line_segment_search.cu
36+
${CMAKE_CURRENT_SOURCE_DIR}/presolve/block_bve.cu
3637
${CMAKE_CURRENT_SOURCE_DIR}/presolve/bounds_presolve.cu
3738
${CMAKE_CURRENT_SOURCE_DIR}/presolve/bounds_update_data.cu
3839
${CMAKE_CURRENT_SOURCE_DIR}/presolve/semi_continuous.cu

cpp/src/mip_heuristics/diversity/diversity_manager.cu

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,15 @@
1111
#include <mip_heuristics/mip_constants.hpp>
1212
#include <mip_heuristics/presolve/third_party_presolve.hpp>
1313

14+
#include <mip_heuristics/presolve/block_bve.cuh>
1415
#include <mip_heuristics/presolve/conflict_graph/clique_table.cuh>
1516
#include <mip_heuristics/presolve/probing_cache.cuh>
1617
#include <mip_heuristics/presolve/trivial_presolve.cuh>
1718
#include <mip_heuristics/problem/problem_helpers.cuh>
1819

1920
#include <pdlp/solve.cuh>
2021

22+
#include <utilities/copy_helpers.hpp>
2123
#include <utilities/scope_guard.hpp>
2224

2325
#include <chrono>
@@ -307,7 +309,10 @@ bool diversity_manager_t<i_t, f_t>::run_presolve(f_t time_limit, timer_t global_
307309
CUOPT_LOG_INFO("Probing-cache step disabled via %s=false", CUOPT_MIP_PROBING);
308310
run_probing_cache = false;
309311
}
310-
if (run_probing_cache) {
312+
const bool remap_cache_ids = true;
313+
problem_ptr->related_vars_time_limit = context.settings.heuristic_params.related_vars_time_limit;
314+
315+
if (run_probing_cache && !global_timer.check_time_limit() && !presolve_timer.check_time_limit()) {
311316
log_presolve_budget("PROBING", probing_features, probing_budget);
312317
f_t time_for_probing_cache = std::min(time_limit, (f_t)global_timer.remaining_time());
313318
timer_t probing_timer{time_for_probing_cache};
@@ -324,9 +329,17 @@ bool diversity_manager_t<i_t, f_t>::run_presolve(f_t time_limit, timer_t global_
324329
std::chrono::duration<double>(std::chrono::steady_clock::now() - probing_t0).count());
325330
if (problem_is_infeasible) { return false; }
326331
}
327-
const bool remap_cache_ids = true;
328-
problem_ptr->related_vars_time_limit = context.settings.heuristic_params.related_vars_time_limit;
332+
329333
if (!global_timer.check_time_limit()) { trivial_presolve(*problem_ptr, remap_cache_ids); }
334+
335+
if (context.settings.block_bve && run_probing_cache) {
336+
timer_t bve_deadline(std::min(global_timer.remaining_time(), presolve_timer.remaining_time()));
337+
if (!block_bve_phase(ls.constraint_prop.bounds_update, *problem_ptr, bve_deadline)) {
338+
stats.presolve_time = timer.elapsed_time();
339+
return false;
340+
}
341+
}
342+
330343
if (!problem_ptr->empty && !check_bounds_sanity(*problem_ptr)) { return false; }
331344
// if (!presolve_timer.check_time_limit() && !context.settings.heuristics_only &&
332345
// !problem_ptr->empty) {

cpp/src/mip_heuristics/diversity/population.cu

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,12 @@ std::vector<solution_t<i_t, f_t>> population_t<i_t, f_t>::get_external_solutions
233233
sol.compute_number_of_integers(),
234234
problem_ptr->n_integer_vars);
235235
}
236+
if (std::abs(sol.get_objective() - h_entry.objective) > OBJECTIVE_EPSILON) {
237+
CUOPT_LOG_DEBUG(
238+
"External solution objective mismatch: sol.get_objective() = %g, h_entry.objective = %g",
239+
sol.get_objective(),
240+
h_entry.objective);
241+
}
236242
sol.handle_ptr->sync_stream();
237243
return_vector.emplace_back(std::move(sol));
238244
counter++;

cpp/src/mip_heuristics/feasibility_jump/early_gpufj.cu

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
#include <mip_heuristics/solver_context.cuh>
1313
#include <utilities/logger.hpp>
1414

15-
#include <raft/core/error.hpp>
15+
#include <raft/core/device_setter.hpp>
1616

1717
#include <limits>
1818

@@ -63,7 +63,7 @@ void early_gpufj_t<i_t, f_t>::start()
6363
#pragma omp task default(none) shared(fj_ptr_) priority(CUOPT_DEFAULT_TASK_PRIORITY) \
6464
depend(out : *fj_ptr_)
6565
{
66-
RAFT_CUDA_TRY(cudaSetDevice(this->device_id_));
66+
raft::device_setter guard(this->device_id_);
6767
fj_ptr_->solve(*this->solution_ptr_);
6868
}
6969
}

0 commit comments

Comments
 (0)