Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions cpp/src/linear_programming/solve.cu
Original file line number Diff line number Diff line change
Expand Up @@ -467,7 +467,7 @@ void run_dual_simplex_thread(

template <typename i_t, typename f_t>
optimization_problem_solution_t<i_t, f_t> run_concurrent(
optimization_problem_t<i_t, f_t>& op_problem,
const optimization_problem_t<i_t, f_t>& op_problem,
detail::problem_t<i_t, f_t>& problem,
pdlp_solver_settings_t<i_t, f_t> const& settings,
bool is_batch_mode)
Expand Down Expand Up @@ -540,7 +540,7 @@ optimization_problem_solution_t<i_t, f_t> run_concurrent(

template <typename i_t, typename f_t>
optimization_problem_solution_t<i_t, f_t> solve_lp_with_method(
optimization_problem_t<i_t, f_t>& op_problem,
const optimization_problem_t<i_t, f_t>& op_problem,
detail::problem_t<i_t, f_t>& problem,
pdlp_solver_settings_t<i_t, f_t> const& settings,
bool is_batch_mode)
Expand Down Expand Up @@ -714,6 +714,12 @@ optimization_problem_solution_t<i_t, f_t> solve_lp(
bool problem_checking, \
bool use_pdlp_solver_mode); \
\
template optimization_problem_solution_t<int, F_TYPE> solve_lp_with_method( \
const optimization_problem_t<int, F_TYPE>& op_problem, \
detail::problem_t<int, F_TYPE>& problem, \
pdlp_solver_settings_t<int, F_TYPE> const& settings, \
bool is_batch_mode = false); \
\
template optimization_problem_t<int, F_TYPE> mps_data_model_to_optimization_problem( \
raft::handle_t const* handle_ptr, \
const cuopt::mps_parser::mps_data_model_t<int, F_TYPE>& data_model);
Expand Down
7 changes: 7 additions & 0 deletions cpp/src/linear_programming/solve.cuh
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,11 @@ cuopt::linear_programming::optimization_problem_t<i_t, f_t> mps_data_model_to_op
raft::handle_t const* handle_ptr,
const cuopt::mps_parser::mps_data_model_t<i_t, f_t>& data_model);

template <typename i_t, typename f_t>
cuopt::linear_programming::optimization_problem_solution_t<i_t, f_t> solve_lp_with_method(
const optimization_problem_t<i_t, f_t>& op_problem,
detail::problem_t<i_t, f_t>& problem,
pdlp_solver_settings_t<i_t, f_t> const& settings,
bool is_batch_mode = false);

} // namespace cuopt::linear_programming
10 changes: 6 additions & 4 deletions cpp/src/mip/solve.cu
Original file line number Diff line number Diff line change
Expand Up @@ -125,10 +125,12 @@ mip_solution_t<i_t, f_t> run_mip(detail::problem_t<i_t, f_t>& problem,
running_mip);

cuopt_func_call(auto saved_problem = scaled_problem);
if (settings.mip_scaling) { scaling.scale_problem(); }
if (settings.initial_solutions.size() > 0) {
for (const auto& initial_solution : settings.initial_solutions) {
scaling.scale_primal(*initial_solution);
if (settings.mip_scaling) {
scaling.scale_problem();
if (settings.initial_solutions.size() > 0) {
for (const auto& initial_solution : settings.initial_solutions) {
scaling.scale_primal(*initial_solution);
}
}
}
// only call preprocess on scaled problem, so we can compute feasibility on the original problem
Expand Down
24 changes: 24 additions & 0 deletions cpp/src/mip/solver.cu
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@
#include "local_search/rounding/simple_rounding.cuh"
#include "solver.cuh"

#include <linear_programming/pdlp.cuh>
#include <linear_programming/solve.cuh>

#include <dual_simplex/branch_and_bound.hpp>
#include <dual_simplex/simplex_solver_settings.hpp>
#include <dual_simplex/solve.hpp>
Expand Down Expand Up @@ -124,6 +127,27 @@ solution_t<i_t, f_t> mip_solver_t<i_t, f_t>::run_solver()
return sol;
}

// if the problem was reduced to a LP: run concurrent LP
if (context.problem_ptr->n_integer_vars == 0) {
CUOPT_LOG_INFO("Problem reduced to a LP, running concurrent LP");
pdlp_solver_settings_t<i_t, f_t> settings{};
settings.time_limit = timer_.remaining_time();
settings.method = method_t::Concurrent;

auto opt_sol = solve_lp_with_method<i_t, f_t>(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Calling LP with solve_lp_with_method instead of solve_lp means we would not get the initial LP problem checks and presolve being added in #234.
Should we use it instead? the rest looks good to me!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also think we should be calling solve_lp and pass down the corresponding settings to use concurrent unless I missed something

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Problem checks should be superfluous as they're already performed on the presolved problem. Additionally, the checks in solve_lp are only done on the original problem structure (which has already been checked at the time of calling solve_mip)
solve_lp requires passing either a mps_data_model or the original optimization_problem_t instead of the presolved problem, I used solve_lp_with_method to avoid making changes to solve_lp. Do you think I should add an overload instead?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think another overload would over complexify things at this point. I prefer sticking with this approach. What we could do is move the try/catch and presolve in solve_lp_with_method in some other PR.

*context.problem_ptr->original_problem_ptr, *context.problem_ptr, settings);

solution_t<i_t, f_t> sol(*context.problem_ptr);
sol.copy_new_assignment(host_copy(opt_sol.get_primal_solution()));
if (opt_sol.get_termination_status() == pdlp_termination_status_t::Optimal ||
opt_sol.get_termination_status() == pdlp_termination_status_t::PrimalInfeasible ||
opt_sol.get_termination_status() == pdlp_termination_status_t::DualInfeasible) {
sol.set_problem_fully_reduced();
}
context.problem_ptr->post_process_solution(sol);
return sol;
}

namespace dual_simplex = cuopt::linear_programming::dual_simplex;
std::future<dual_simplex::mip_status_t> branch_and_bound_status_future;
dual_simplex::user_problem_t<i_t, f_t> branch_and_bound_problem;
Expand Down
7 changes: 7 additions & 0 deletions cpp/tests/mip/empty_fixed_problems_test.cu
Original file line number Diff line number Diff line change
Expand Up @@ -78,4 +78,11 @@ TEST(mip_solve, empty_max_problem_with_objective_test)
EXPECT_NEAR(obj_val, 11, 1e-5);
}

TEST(mip_solve, mip_presolved_to_lp)
{
auto [termination_status, obj_val, lb] = test_mps_file("mip/mip-presolved-to-lp.mps", 5, false);
EXPECT_EQ(termination_status, mip_termination_status_t::Optimal);
EXPECT_NEAR(obj_val, 0, 1e-5);
}

} // namespace cuopt::linear_programming::test
32 changes: 32 additions & 0 deletions datasets/mip/mip-presolved-to-lp.mps
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
NAME LP_PROBLEM
ROWS
N OBJ
E R001
E R002
E R003
L R004
L R005
L R006
L R007
COLUMNS
X001 OBJ 1.000000
X001 R004 -1.000000
X001 R006 -1.000000
X002 OBJ 1.000000
X002 R005 -1.000000
X002 R007 -1.000000
X003 R001 1.000000
X003 R004 1.000000
X003 R006 -1.000000
X004 R002 1.000000
X004 R005 1.000000
X004 R007 -1.000000
X005 R001 -1.000000
X005 R002 -1.000000
X005 R003 1.000000
RHS
BOUNDS
LO BND1 X005 0.000000
UP BND1 X005 1.000000
BV BND1 X005
ENDATA