Skip to content

Conversation

Kh4ster
Copy link
Contributor

@Kh4ster Kh4ster commented Jul 1, 2025

This PR aims at multiple improvements in MIP:

  • Allow for concurrent solve (PDLP and Dual Simplex) in get_relaxed_lp which should improve FP
    • This is done by adding a solve LP interface that takes as input the MIP problem representation (instead of the LP one)
    • This interface is now used everywhere. If we need to solve an LP with the LP problem representation, the problem will be converted (which was already done eventually in any case)
    • Added handling of the "inside_mip" flag for both PDLP and Dual Simplex
  • Proper handling of PDLP with warm start data through an additional field. This is in case previous problem was solved by Dual Simplex because of a concurent solve
    • Test added and also comments
  • Added a few missing NVTX ranges

Eventually we want to add plug warm start in FP for both PDLP and Dual Simplex.

@Kh4ster Kh4ster requested review from a team as code owners July 1, 2025 14:16
@Kh4ster Kh4ster requested review from tmckayus, hlinsen, aliceb-nv, akifcorduk, Iroy30 and chris-maes and removed request for hlinsen and aliceb-nv July 1, 2025 14:16
@Kh4ster Kh4ster self-assigned this Jul 1, 2025
@Kh4ster Kh4ster added non-breaking Introduces a non-breaking change improvement Improves an existing functionality mip labels Jul 1, 2025
* @return optimization_problem_solution_t<i_t, f_t> owning container for the solver solution
*/
template <typename i_t, typename f_t>
optimization_problem_solution_t<i_t, f_t> solve_lp(
Copy link
Contributor

Choose a reason for hiding this comment

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

If this is used purely in MIP context. It is cleaner to rename this to solve_relaxed_lp or solve_lp_relaxation

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I agree that for now this is just used in the context of MIP but some people might want to solve an LP using the detail::problem representation instead of the optimization_problem one. What do you think?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@akifcorduk do you have an opinion there?

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 renaming it kind of pins it to MIP use-cases, I agree with Nicolas that it is just another LP interface that accepts problem_t. In the MIP context, the naming doesn't matter too much because we have other functions like run_lp_with_vars_fixed

std::tuple<dual_simplex::lp_solution_t<i_t, f_t>, dual_simplex::lp_status_t, f_t, f_t, f_t>
run_dual_simplex(dual_simplex::user_problem_t<i_t, f_t>& user_problem,
pdlp_solver_settings_t<i_t, f_t> const& settings)
pdlp_solver_settings_t<i_t, f_t> const& settings,
Copy link
Contributor

Choose a reason for hiding this comment

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

We should consider changing pdlp_solver_settings to lp_settings

}

template <typename i_t, typename f_t>
optimization_problem_solution_t<i_t, f_t> solve_lp(optimization_problem_t<i_t, f_t>& op_problem,
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this for pure LP?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Same comment as above, yes currently this used in the context LP as its using the optimization_problem_t representation.

detail::problem_t<i_t, f_t>& problem,
pdlp_solver_settings_t<i_t, f_t> const& settings)
pdlp_solver_settings_t<i_t, f_t> const& settings,
bool inside_mip)
Copy link
Contributor

Choose a reason for hiding this comment

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

Is inside_mip currently used anywhere? or is it for future purposes?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes I'm passing it to both PDLP and Dual Simplex

* @return optimization_problem_solution_t<i_t, f_t> owning container for the solver solution
*/
template <typename i_t, typename f_t>
optimization_problem_solution_t<i_t, f_t> solve_lp(
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 renaming it kind of pins it to MIP use-cases, I agree with Nicolas that it is just another LP interface that accepts problem_t. In the MIP context, the naming doesn't matter too much because we have other functions like run_lp_with_vars_fixed

if (settings.has_pdlp_warm_start_data()) {
const auto& warm_start_data = settings.get_pdlp_warm_start_data();
if (!warm_start_data.solved_by_pdlp_) {
CUOPT_LOG_INFO(
Copy link
Contributor

Choose a reason for hiding this comment

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

I wouldn't put INFO logs here, it would bloat the regular solution logs. DEBUG would be more appropiate.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Agreed, changed

} else if (pdlp_hyper_params::restart_strategy ==
static_cast<int>(
pdlp_restart_strategy_t<i_t, f_t>::restart_strategy_t::TRUST_REGION_RESTART)) {
CUOPT_LOG_INFO(
Copy link
Contributor

Choose a reason for hiding this comment

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

Same: INFO is only for solution progress info to be conveyed to the user.

CUOPT_LOG_INFO("Solved with dual simplex");
sol_pdlp.copy_from(op_problem.get_handle_ptr(), sol_dual_simplex);
// TODO confirm with Akif that using the problem handle ptr is not a problem
sol_pdlp.copy_from(problem.handle_ptr, sol_dual_simplex);
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 using problem handle_ptr should be okay given that there are no races across host threads. Even a different handle is used, the thread is default_per_thread so it should be fine.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks, removed the comment

lp_solver.set_inside_mip(true);
auto solver_response = lp_solver.run_solver(start_time);
// TODO check that we do want to do problem checking here
auto solver_response = solve_lp(op_problem, settings, true, true, true);
Copy link
Contributor

Choose a reason for hiding this comment

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

I would name the boolean argument, so that it is easier to read.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I agree. But regarding the comment, should we do problem checking here? Maybe just when we are in assert/debug mode

Copy link
Contributor

Choose a reason for hiding this comment

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

At this point we don't need to do problem checks because we are not modifiying the problem. It is done in places where we modify the problem.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It did help me in the debug process, will add it in only assert/debug mode

});
lp_solver.set_initial_primal_solution(lp_state.prev_primal);
lp_solver.set_initial_dual_solution(lp_state.prev_dual);
settings.set_initial_primal_solution(lp_state.prev_primal.data(),
Copy link
Contributor

Choose a reason for hiding this comment

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

So currently, we are not using the full warm start data yet and this code is a base/skeleton for adding the future warm start data?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Excatly, because we could need to add the warm start data to the lp_state which is also tied to the mip problem

@anandhkb anandhkb added this to the 25.08 milestone Jul 2, 2025
Copy link
Member

@Iroy30 Iroy30 left a comment

Choose a reason for hiding this comment

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

Needs changes in the server warmstart population

problem_checking_t<i_t, f_t>::check_initial_solution_representation(
*problem.original_problem_ptr, settings);
} else {
problem.check_problem_representation(true, true);
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 this might be unnecessary as we are doing the problem check in the problem cstr. We could maybe convert it to an assert? I think we should do the runtime problem check only at the beginning from the user, later problem checks should be asserts.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I agree we should do it in as assert. But I believe we should keep the checks for two reasons:

  1. We also check the initial solutions which is not done in the problem constructor
  2. Even if we check in the problem constructor this is more for the case where the problem has been modified in an invalid way, we then need to check the problem again

Copy link
Contributor

@chris-maes chris-maes left a comment

Choose a reason for hiding this comment

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

I just looked at the changes that touched the code for starting dual simplex. They look fine. It is fine to add info about inside mip to the dual simplex solve. Right now this is only used for changing how optimality information is printed.

I did not review the other parts of this PR. (Sorry ran out of time.) I think ideally we want to use primal simplex warm starting inside FP. I'm not sure how this PR relates to that.

Approving so this can be merged while I am out of office.

@Kh4ster Kh4ster removed the request for review from tmckayus July 11, 2025 12:59
@Kh4ster Kh4ster removed this from the 25.08 milestone Jul 16, 2025
@Kh4ster Kh4ster marked this pull request as draft July 16, 2025 08:27
Copy link

copy-pr-bot bot commented Jul 16, 2025

Auto-sync is disabled for draft pull requests in this repository. Workflows must be run manually.

Contributors can view more details about this message here.

@tmckayus
Copy link
Contributor

Set to 25.10 milestone when it is created

@tmckayus tmckayus added this to the 25.10 milestone Jul 29, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
improvement Improves an existing functionality mip non-breaking Introduces a non-breaking change
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants