Skip to content

Commit 928a1e1

Browse files
authored
Update solution object to include the LP solver (#822)
This PR replaced `solved_by_pdlp` with `solved_by` in `optimization_problem_solution_t` and all associated objects, such that now it is possible to retrieve which method was used for solving the LP when running in concurrent mode. This also fix a typo in the `CUOPT_TERMINATION_STATUS` and updates the B&B logs to display the method used for solving the root relaxation. ## Issue Closes #787 Authors: - Nicolas L. Guidotti (https://github.com/nguidotti) - Ishika Roy (https://github.com/Iroy30) Approvers: - Ramakrishnap (https://github.com/rgsl888prabhu) - Nicolas Blin (https://github.com/Kh4ster) URL: #822
1 parent 93f4434 commit 928a1e1

40 files changed

Lines changed: 489 additions & 404 deletions

cpp/include/cuopt/linear_programming/constants.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@
130130
#define CUOPT_METHOD_PDLP 1
131131
#define CUOPT_METHOD_DUAL_SIMPLEX 2
132132
#define CUOPT_METHOD_BARRIER 3
133+
#define CUOPT_METHOD_UNSET 4
133134

134135
/* @brief PDLP precision mode constants */
135136
#define CUOPT_PDLP_DEFAULT_PRECISION -1

cpp/include/cuopt/linear_programming/cpu_optimization_problem_solution.hpp

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ class cpu_lp_solution_t : public lp_solution_interface_t<i_t, f_t> {
4646
l2_dual_residual_(std::numeric_limits<f_t>::signaling_NaN()),
4747
gap_(std::numeric_limits<f_t>::signaling_NaN()),
4848
num_iterations_(0),
49-
solved_by_pdlp_(false)
49+
solved_by_(Unset)
5050
{
5151
}
5252

@@ -65,7 +65,7 @@ class cpu_lp_solution_t : public lp_solution_interface_t<i_t, f_t> {
6565
f_t l2_dual_residual,
6666
f_t gap,
6767
i_t num_iterations,
68-
bool solved_by_pdlp)
68+
method_t solved_by)
6969
: primal_solution_(std::move(primal_solution)),
7070
dual_solution_(std::move(dual_solution)),
7171
reduced_cost_(std::move(reduced_cost)),
@@ -78,7 +78,7 @@ class cpu_lp_solution_t : public lp_solution_interface_t<i_t, f_t> {
7878
l2_dual_residual_(l2_dual_residual),
7979
gap_(gap),
8080
num_iterations_(num_iterations),
81-
solved_by_pdlp_(solved_by_pdlp)
81+
solved_by_(solved_by)
8282
{
8383
}
8484

@@ -97,7 +97,7 @@ class cpu_lp_solution_t : public lp_solution_interface_t<i_t, f_t> {
9797
f_t l2_dual_residual,
9898
f_t gap,
9999
i_t num_iterations,
100-
bool solved_by_pdlp,
100+
method_t solved_by,
101101
cpu_pdlp_warm_start_data_t<i_t, f_t>&& warmstart_data)
102102
: primal_solution_(std::move(primal_solution)),
103103
dual_solution_(std::move(dual_solution)),
@@ -111,7 +111,7 @@ class cpu_lp_solution_t : public lp_solution_interface_t<i_t, f_t> {
111111
l2_dual_residual_(l2_dual_residual),
112112
gap_(gap),
113113
num_iterations_(num_iterations),
114-
solved_by_pdlp_(solved_by_pdlp),
114+
solved_by_(solved_by),
115115
pdlp_warm_start_data_(std::move(warmstart_data))
116116
{
117117
}
@@ -149,7 +149,7 @@ class cpu_lp_solution_t : public lp_solution_interface_t<i_t, f_t> {
149149

150150
i_t get_num_iterations(i_t = 0) const override { return num_iterations_; }
151151

152-
bool is_solved_by_pdlp(i_t = 0) const override { return solved_by_pdlp_; }
152+
method_t solved_by(i_t = 0) const override { return solved_by_; }
153153

154154
const pdlp_warm_start_data_t<i_t, f_t>& get_pdlp_warm_start_data() const override
155155
{
@@ -266,7 +266,7 @@ class cpu_lp_solution_t : public lp_solution_interface_t<i_t, f_t> {
266266
f_t l2_dual_residual_;
267267
f_t gap_;
268268
i_t num_iterations_;
269-
bool solved_by_pdlp_;
269+
method_t solved_by_;
270270

271271
// PDLP warm start data (embedded struct, CPU-backed using std::vector)
272272
cpu_pdlp_warm_start_data_t<i_t, f_t> pdlp_warm_start_data_;

cpp/include/cuopt/linear_programming/optimization_problem_solution.hpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -122,9 +122,9 @@ class gpu_lp_solution_t : public lp_solution_interface_t<i_t, f_t> {
122122
return solution_.get_additional_termination_information(id).number_of_steps_taken;
123123
}
124124

125-
bool is_solved_by_pdlp(i_t id = 0) const override
125+
method_t solved_by(i_t id = 0) const override
126126
{
127-
return solution_.get_additional_termination_information(id).solved_by_pdlp;
127+
return solution_.get_additional_termination_information(id).solved_by;
128128
}
129129

130130
const pdlp_warm_start_data_t<i_t, f_t>& get_pdlp_warm_start_data() const override
@@ -338,7 +338,7 @@ class gpu_lp_solution_t : public lp_solution_interface_t<i_t, f_t> {
338338
get_l2_dual_residual(),
339339
get_gap(),
340340
get_num_iterations(),
341-
is_solved_by_pdlp(),
341+
solved_by(),
342342
std::move(cpu_ws));
343343
}
344344

@@ -353,7 +353,7 @@ class gpu_lp_solution_t : public lp_solution_interface_t<i_t, f_t> {
353353
get_l2_dual_residual(),
354354
get_gap(),
355355
get_num_iterations(),
356-
is_solved_by_pdlp());
356+
solved_by());
357357
}
358358

359359
/**

cpp/include/cuopt/linear_programming/optimization_problem_solution_interface.hpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -291,10 +291,10 @@ class lp_solution_interface_t : public optimization_problem_solution_interface_t
291291
virtual i_t get_num_iterations(i_t id = 0) const = 0;
292292

293293
/**
294-
* @brief Check if solved by PDLP
295-
* @return true if solved by PDLP
294+
* @brief Method used for solving the LP.
295+
* @return the method used for solving the LP.
296296
*/
297-
virtual bool is_solved_by_pdlp(i_t id = 0) const = 0;
297+
virtual method_t solved_by(i_t id = 0) const = 0;
298298

299299
/**
300300
* @brief Get PDLP warm start data (GPU solutions only)

cpp/include/cuopt/linear_programming/pdlp/solver_settings.hpp

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,19 +50,34 @@ enum pdlp_solver_mode_t : int {
5050
* @brief Enum representing the different methods that can be used to solve the
5151
* linear programming problem.
5252
*
53-
* Concurrent: Use both PDLP and DualSimplex in parallel.
53+
* Concurrent: Use PDLP, Barrier and DualSimplex in parallel.
5454
* PDLP: Use the PDLP method.
5555
* DualSimplex: Use the dual simplex method.
56+
* Barrier: Use the barrier method
57+
* Unset: The value was not set.
5658
*
5759
* @note Default method is Concurrent.
5860
*/
5961
enum method_t : int {
6062
Concurrent = CUOPT_METHOD_CONCURRENT,
6163
PDLP = CUOPT_METHOD_PDLP,
6264
DualSimplex = CUOPT_METHOD_DUAL_SIMPLEX,
63-
Barrier = CUOPT_METHOD_BARRIER
65+
Barrier = CUOPT_METHOD_BARRIER,
66+
Unset = CUOPT_METHOD_UNSET
6467
};
6568

69+
/// Returns the corresponding string from the enum `method_t`.
70+
inline std::string method_to_string(method_t method)
71+
{
72+
switch (method) {
73+
case method_t::DualSimplex: return "Dual Simplex";
74+
case method_t::PDLP: return "PDLP";
75+
case method_t::Barrier: return "Barrier";
76+
case method_t::Concurrent: return "Concurrent";
77+
default: return "Unset";
78+
}
79+
}
80+
6681
/**
6782
* @brief Enum representing the PDLP precision modes.
6883
*

cpp/include/cuopt/linear_programming/pdlp/solver_solution.hpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include <cuopt/linear_programming/constants.h>
1111
#include <cuopt/error.hpp>
1212
#include <cuopt/linear_programming/pdlp/pdlp_warm_start_data.hpp>
13+
#include <cuopt/linear_programming/pdlp/solver_settings.hpp>
1314
#include <cuopt/linear_programming/utilities/internals.hpp>
1415

1516
#include <rmm/cuda_stream_view.hpp>
@@ -89,8 +90,8 @@ class optimization_problem_solution_t : public base_solution_t {
8990
/** Solve time in seconds */
9091
double solve_time{std::numeric_limits<double>::signaling_NaN()};
9192

92-
/** Whether the problem was solved by PDLP or Dual Simplex */
93-
bool solved_by_pdlp{false};
93+
/** Whether the problem was solved by PDLP, Barrier or Dual Simplex */
94+
method_t solved_by = method_t::Unset;
9495
};
9596

9697
/**

cpp/include/cuopt/linear_programming/utilities/cython_types.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ struct linear_programming_ret_t {
8383
double gap_{};
8484
int nb_iterations_{};
8585
double solve_time_{};
86-
bool solved_by_pdlp_{};
86+
linear_programming::method_t solved_by_{};
8787

8888
bool is_gpu() const { return std::holds_alternative<gpu_solutions_t>(solutions_); }
8989
};

cpp/src/branch_and_bound/branch_and_bound.cpp

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1972,18 +1972,21 @@ lp_status_t branch_and_bound_t<i_t, f_t>::solve_root_relaxation(
19721972
set_uninitialized_steepest_edge_norms<i_t, f_t>(original_lp_, basic_list, edge_norms);
19731973
user_objective = root_crossover_soln_.user_objective;
19741974
iter = root_crossover_soln_.iterations;
1975-
solver_name = "Barrier/PDLP and Crossover";
1975+
solver_name = method_to_string(root_relax_solved_by);
1976+
19761977
} else {
1977-
root_status = root_status_future.get();
1978-
user_objective = root_relax_soln_.user_objective;
1979-
iter = root_relax_soln_.iterations;
1980-
solver_name = "Dual Simplex";
1978+
root_status = root_status_future.get();
1979+
user_objective = root_relax_soln_.user_objective;
1980+
iter = root_relax_soln_.iterations;
1981+
root_relax_solved_by = DualSimplex;
1982+
solver_name = "Dual Simplex";
19811983
}
19821984
} else {
1983-
root_status = root_status_future.get();
1984-
user_objective = root_relax_soln_.user_objective;
1985-
iter = root_relax_soln_.iterations;
1986-
solver_name = "Dual Simplex";
1985+
root_status = root_status_future.get();
1986+
user_objective = root_relax_soln_.user_objective;
1987+
iter = root_relax_soln_.iterations;
1988+
root_relax_solved_by = DualSimplex;
1989+
solver_name = "Dual Simplex";
19871990
}
19881991

19891992
settings_.log.printf("\n");

cpp/src/branch_and_bound/branch_and_bound.hpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@
3030
#include <utilities/work_limit_context.hpp>
3131
#include <utilities/work_unit_scheduler.hpp>
3232

33+
#include <cuopt/linear_programming/pdlp/solver_settings.hpp>
34+
3335
#include <omp.h>
3436

3537
#include <atomic>
@@ -89,7 +91,8 @@ class branch_and_bound_t {
8991
const std::vector<f_t>& reduced_costs,
9092
f_t objective,
9193
f_t user_objective,
92-
i_t iterations)
94+
i_t iterations,
95+
method_t method)
9396
{
9497
if (!is_root_solution_set) {
9598
root_crossover_soln_.x = primal;
@@ -99,6 +102,7 @@ class branch_and_bound_t {
99102
root_crossover_soln_.objective = objective;
100103
root_crossover_soln_.user_objective = user_objective;
101104
root_crossover_soln_.iterations = iterations;
105+
root_relax_solved_by = method;
102106
root_crossover_solution_set_.store(true, std::memory_order_release);
103107
}
104108
}
@@ -218,6 +222,7 @@ class branch_and_bound_t {
218222
f_t root_objective_;
219223
lp_solution_t<i_t, f_t> root_relax_soln_;
220224
lp_solution_t<i_t, f_t> root_crossover_soln_;
225+
method_t root_relax_solved_by{Unset};
221226
std::vector<f_t> edge_norms_;
222227
std::atomic<bool> root_crossover_solution_set_{false};
223228
omp_atomic_t<f_t> root_lp_current_lower_bound_;

cpp/src/dual_simplex/crossover.cpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -612,7 +612,7 @@ i_t dual_push(const lp_problem_t<i_t, f_t>& lp,
612612
return TIME_LIMIT_RETURN;
613613
}
614614
if (settings.concurrent_halt != nullptr && *settings.concurrent_halt == 1) {
615-
settings.log.printf("Concurrent halt\n");
615+
if (!settings.inside_mip) { settings.log.printf("Concurrent halt\n"); }
616616
return CONCURRENT_HALT_RETURN;
617617
}
618618
}
@@ -989,7 +989,7 @@ i_t primal_push(const lp_problem_t<i_t, f_t>& lp,
989989
return TIME_LIMIT_RETURN;
990990
}
991991
if (settings.concurrent_halt != nullptr && *settings.concurrent_halt == 1) {
992-
settings.log.printf("Concurrent halt\n");
992+
if (!settings.inside_mip) { settings.log.printf("Concurrent halt\n"); }
993993
return CONCURRENT_HALT_RETURN;
994994
}
995995
}
@@ -1353,7 +1353,7 @@ crossover_status_t crossover(const lp_problem_t<i_t, f_t>& lp,
13531353
return crossover_status_t::TIME_LIMIT;
13541354
}
13551355
if (settings.concurrent_halt != nullptr && *settings.concurrent_halt == 1) {
1356-
settings.log.printf("Concurrent halt\n");
1356+
if (!settings.inside_mip) { settings.log.printf("Concurrent halt\n"); }
13571357
return crossover_status_t::CONCURRENT_LIMIT;
13581358
}
13591359

@@ -1415,7 +1415,7 @@ crossover_status_t crossover(const lp_problem_t<i_t, f_t>& lp,
14151415
return crossover_status_t::TIME_LIMIT;
14161416
}
14171417
if (settings.concurrent_halt != nullptr && *settings.concurrent_halt == 1) {
1418-
settings.log.printf("Concurrent halt\n");
1418+
if (!settings.inside_mip) { settings.log.printf("Concurrent halt\n"); }
14191419
return crossover_status_t::CONCURRENT_LIMIT;
14201420
}
14211421
primal_infeas = primal_infeasibility(lp, settings, vstatus, solution.x);
@@ -1577,7 +1577,7 @@ crossover_status_t crossover(const lp_problem_t<i_t, f_t>& lp,
15771577
return crossover_status_t::TIME_LIMIT;
15781578
}
15791579
if (settings.concurrent_halt != nullptr && *settings.concurrent_halt == 1) {
1580-
settings.log.printf("Concurrent halt\n");
1580+
if (!settings.inside_mip) { settings.log.printf("Concurrent halt\n"); }
15811581
return crossover_status_t::CONCURRENT_LIMIT;
15821582
}
15831583
solution.iterations += iter;

0 commit comments

Comments
 (0)