Skip to content

Commit 4824781

Browse files
authored
Fix several bugs appeared in unit testing of JuMP interface (#149)
While testing the JuMP interface, there were several corner cases showed up. Especially for tiny problems and problems without constraints. This PR has several such fixes. In addition, following things are added: - Implement `cuOptGetVersion()` in C-API - Change the max allowed absolute gap tolerance to INFINITY ## Issue closes #148 closes #147 closes #166 Authors: - Rajesh Gandham (https://github.com/rg20) Approvers: - Nicolas Blin (https://github.com/Kh4ster) - Ramakrishnap (https://github.com/rgsl888prabhu) - Chris Maes (https://github.com/chris-maes) - Akif ÇÖRDÜK (https://github.com/akifcorduk) URL: #149
1 parent d204b1d commit 4824781

26 files changed

Lines changed: 273 additions & 73 deletions

cpp/include/cuopt/linear_programming/cuopt_c.h

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,22 @@ int8_t cuOptGetFloatSize();
9393
*/
9494
int8_t cuOptGetIntSize();
9595

96+
/**
97+
* @brief Get the version of the library.
98+
*
99+
* @param[out] version_major - A pointer to a cuopt_int_t that will contain the major version
100+
* number.
101+
* @param[out] version_minor - A pointer to a cuopt_int_t that will contain the minor version
102+
* number.
103+
* @param[out] version_patch - A pointer to a cuopt_int_t that will contain the patch version
104+
* number.
105+
*
106+
* @return A status code indicating success or failure.
107+
*/
108+
cuopt_int_t cuOptGetVersion(cuopt_int_t* version_major,
109+
cuopt_int_t* version_minor,
110+
cuopt_int_t* version_patch);
111+
96112
/**
97113
* @brief Read an optimization problem from an MPS file.
98114
*
@@ -656,6 +672,18 @@ cuopt_int_t cuOptGetSolutionBound(cuOptSolution solution, cuopt_float_t* solutio
656672
*/
657673
cuopt_int_t cuOptGetDualSolution(cuOptSolution solution, cuopt_float_t* dual_solution_ptr);
658674

675+
/** @brief Get the dual objective value of an optimization problem.
676+
*
677+
* @param[in] solution - The solution object.
678+
*
679+
* @param[in, out] dual_objective_value_ptr - A pointer to a cuopt_float_t that will contain the
680+
* dual objective value.
681+
*
682+
* @return A status code indicating success or failure.
683+
*/
684+
cuopt_int_t cuOptGetDualObjectiveValue(cuOptSolution solution,
685+
cuopt_float_t* dual_objective_value_ptr);
686+
659687
/** @brief Get the reduced costs of an optimization problem.
660688
*
661689
* @param[in] solution - The solution object.

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,12 @@ class optimization_problem_solution_t : public base_solution_t {
214214
*/
215215
f_t get_objective_value() const;
216216

217+
/**
218+
* @brief Returns the dual objective value of the solution as a `f_t`.
219+
* @return objective value of the dual problem
220+
*/
221+
f_t get_dual_objective_value() const;
222+
217223
/**
218224
* @brief Returns the solution for the values of the primal variables as a vector of `f_t`.
219225
*

cpp/src/dual_simplex/solve.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,14 +96,14 @@ template <typename i_t, typename f_t>
9696
f_t compute_user_objective(const lp_problem_t<i_t, f_t>& lp, const std::vector<f_t>& x)
9797
{
9898
const f_t obj = compute_objective(lp, x);
99-
const f_t user_obj = obj * lp.obj_scale + lp.obj_constant;
99+
const f_t user_obj = compute_user_objective(lp, obj);
100100
return user_obj;
101101
}
102102

103103
template <typename i_t, typename f_t>
104104
f_t compute_user_objective(const lp_problem_t<i_t, f_t>& lp, f_t obj)
105105
{
106-
const f_t user_obj = obj * lp.obj_scale + lp.obj_constant;
106+
const f_t user_obj = lp.obj_scale * (obj + lp.obj_constant);
107107
return user_obj;
108108
}
109109

cpp/src/linear_programming/cuopt_c.cpp

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424

2525
#include <mps_parser/parser.hpp>
2626

27+
#include <cuopt/version_config.hpp>
28+
2729
#include <memory>
2830
#include <string>
2931

@@ -59,6 +61,19 @@ int8_t cuOptGetFloatSize() { return sizeof(cuopt_float_t); }
5961

6062
int8_t cuOptGetIntSize() { return sizeof(cuopt_int_t); }
6163

64+
cuopt_int_t cuOptGetVersion(cuopt_int_t* version_major,
65+
cuopt_int_t* version_minor,
66+
cuopt_int_t* version_patch)
67+
{
68+
if (version_major == nullptr || version_minor == nullptr || version_patch == nullptr) {
69+
return CUOPT_INVALID_ARGUMENT;
70+
}
71+
*version_major = CUOPT_VERSION_MAJOR;
72+
*version_minor = CUOPT_VERSION_MINOR;
73+
*version_patch = CUOPT_VERSION_PATCH;
74+
return CUOPT_SUCCESS;
75+
}
76+
6277
cuopt_int_t cuOptReadProblem(const char* filename, cuOptOptimizationProblem* problem_ptr)
6378
{
6479
problem_and_stream_view_t* problem_and_stream = new problem_and_stream_view_t();
@@ -825,6 +840,24 @@ cuopt_int_t cuOptGetDualSolution(cuOptSolution solution, cuopt_float_t* dual_sol
825840
}
826841
}
827842

843+
cuopt_int_t cuOptGetDualObjectiveValue(cuOptSolution solution,
844+
cuopt_float_t* dual_objective_value_ptr)
845+
{
846+
if (solution == nullptr) { return CUOPT_INVALID_ARGUMENT; }
847+
if (dual_objective_value_ptr == nullptr) { return CUOPT_INVALID_ARGUMENT; }
848+
solution_and_stream_view_t* solution_and_stream_view =
849+
static_cast<solution_and_stream_view_t*>(solution);
850+
if (solution_and_stream_view->is_mip) {
851+
return CUOPT_INVALID_ARGUMENT;
852+
} else {
853+
optimization_problem_solution_t<cuopt_int_t, cuopt_float_t>* optimization_problem_solution =
854+
static_cast<optimization_problem_solution_t<cuopt_int_t, cuopt_float_t>*>(
855+
solution_and_stream_view->lp_solution_ptr);
856+
*dual_objective_value_ptr = optimization_problem_solution->get_dual_objective_value();
857+
return CUOPT_SUCCESS;
858+
}
859+
}
860+
828861
cuopt_int_t cuOptGetReducedCosts(cuOptSolution solution, cuopt_float_t* reduced_cost_ptr)
829862
{
830863
if (solution == nullptr) { return CUOPT_INVALID_ARGUMENT; }

cpp/src/linear_programming/solver_solution.cu

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,12 @@ f_t optimization_problem_solution_t<i_t, f_t>::get_objective_value() const
309309
return termination_stats_.primal_objective;
310310
}
311311

312+
template <typename i_t, typename f_t>
313+
f_t optimization_problem_solution_t<i_t, f_t>::get_dual_objective_value() const
314+
{
315+
return termination_stats_.dual_objective;
316+
}
317+
312318
template <typename i_t, typename f_t>
313319
rmm::device_uvector<f_t>& optimization_problem_solution_t<i_t, f_t>::get_primal_solution()
314320
{

cpp/src/linear_programming/termination_strategy/convergence_information.cu

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,7 @@ __global__ void apply_objective_scaling_and_offset(f_t* objective,
254254
{
255255
if (threadIdx.x + blockIdx.x * blockDim.x > 0) { return; }
256256

257-
*objective = (objective_scaling_factor * *objective) + objective_offset;
257+
*objective = objective_scaling_factor * (*objective + objective_offset);
258258
}
259259

260260
template <typename i_t, typename f_t>

cpp/src/math_optimization/solver_settings.cu

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ solver_settings_t<i_t, f_t>::solver_settings_t() : pdlp_settings(), mip_settings
7979
{CUOPT_MIP_ABSOLUTE_TOLERANCE, &mip_settings.tolerances.absolute_tolerance, 0.0, 1e-1, 1e-4},
8080
{CUOPT_MIP_RELATIVE_TOLERANCE, &mip_settings.tolerances.relative_tolerance, 0.0, 1e-1, 1e-4},
8181
{CUOPT_MIP_INTEGRALITY_TOLERANCE, &mip_settings.tolerances.integrality_tolerance, 0.0, 1e-1, 1e-5},
82-
{CUOPT_MIP_ABSOLUTE_GAP, &mip_settings.tolerances.absolute_mip_gap, 0.0, 1e-1, 1e-10},
82+
{CUOPT_MIP_ABSOLUTE_GAP, &mip_settings.tolerances.absolute_mip_gap, 0.0, CUOPT_INFINITY, 1e-10},
8383
{CUOPT_MIP_RELATIVE_GAP, &mip_settings.tolerances.relative_mip_gap, 0.0, 1e-1, 1e-4},
8484
{CUOPT_PRIMAL_INFEASIBLE_TOLERANCE, &pdlp_settings.tolerances.primal_infeasible_tolerance, 0.0, 1e-1, 1e-8},
8585
{CUOPT_DUAL_INFEASIBLE_TOLERANCE, &pdlp_settings.tolerances.dual_infeasible_tolerance, 0.0, 1e-1, 1e-8}

cpp/src/mip/presolve/trivial_presolve.cuh

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,6 @@ void update_from_csr(problem_t<i_t, f_t>& pb)
234234

235235
// update objective_offset
236236
pb.presolve_data.objective_offset +=
237-
pb.presolve_data.objective_scaling_factor *
238237
thrust::transform_reduce(handle_ptr->get_thrust_policy(),
239238
thrust::counting_iterator<i_t>(0),
240239
thrust::counting_iterator<i_t>(pb.n_variables),

cpp/src/mip/problem/problem.cu

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -67,19 +67,26 @@ void problem_t<i_t, f_t>::op_problem_cstr_body(const optimization_problem_t<i_t,
6767

6868
// Set variables bounds to default if not set and constraints bounds if user has set a row type
6969
set_bounds_if_not_set(*this);
70+
71+
const bool is_mip = original_problem_ptr->get_problem_category() != problem_category_t::LP;
72+
if (is_mip) {
73+
variable_types =
74+
rmm::device_uvector<var_t>(problem_.get_variable_types(), handle_ptr->get_stream());
75+
// round bounds to integer for integer variables, note: do this before checking sanity
76+
round_bounds(*this);
77+
}
78+
7079
// check bounds sanity before, so that we can throw exceptions before going into asserts
7180
check_bounds_sanity(*this);
81+
7282
// Check before any modifications
7383
check_problem_representation(false, false);
7484
// If maximization problem, convert the problem
7585
if (maximize) convert_to_maximization_problem(*this);
7686

77-
const bool is_mip = original_problem_ptr->get_problem_category() != problem_category_t::LP;
7887
if (is_mip) {
7988
// Resize what is needed for MIP
8089
raft::common::nvtx::range scope("trivial_presolve");
81-
variable_types =
82-
rmm::device_uvector<var_t>(problem_.get_variable_types(), handle_ptr->get_stream());
8390
integer_indices.resize(n_variables, handle_ptr->get_stream());
8491
is_binary_variable.resize(n_variables, handle_ptr->get_stream());
8592
compute_n_integer_vars();
@@ -93,7 +100,9 @@ void problem_t<i_t, f_t>::op_problem_cstr_body(const optimization_problem_t<i_t,
93100
}
94101

95102
template <typename i_t, typename f_t>
96-
problem_t<i_t, f_t>::problem_t(const optimization_problem_t<i_t, f_t>& problem_)
103+
problem_t<i_t, f_t>::problem_t(
104+
const optimization_problem_t<i_t, f_t>& problem_,
105+
const typename mip_solver_settings_t<i_t, f_t>::tolerances_t tolerances_)
97106
: original_problem_ptr(&problem_),
98107
handle_ptr(problem_.get_handle_ptr()),
99108
n_variables(problem_.get_n_variables()),
@@ -129,7 +138,8 @@ problem_t<i_t, f_t>::problem_t(const optimization_problem_t<i_t, f_t>& problem_)
129138
related_variables_offsets(n_variables, problem_.get_handle_ptr()->get_stream()),
130139
var_names(problem_.get_variable_names()),
131140
row_names(problem_.get_row_names()),
132-
objective_name(problem_.get_objective_name())
141+
objective_name(problem_.get_objective_name()),
142+
tolerances(tolerances_)
133143
{
134144
op_problem_cstr_body(problem_);
135145
branch_and_bound_callback = nullptr;
@@ -1430,7 +1440,7 @@ void problem_t<i_t, f_t>::get_host_user_problem(
14301440
template <typename i_t, typename f_t>
14311441
f_t problem_t<i_t, f_t>::get_user_obj_from_solver_obj(f_t solver_obj)
14321442
{
1433-
return solver_obj * presolve_data.objective_scaling_factor + presolve_data.objective_offset;
1443+
return presolve_data.objective_scaling_factor * (solver_obj + presolve_data.objective_offset);
14341444
}
14351445
14361446
#if MIP_INSTANTIATE_FLOAT

cpp/src/mip/problem/problem.cuh

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,8 @@ constexpr bool USE_REL_TOLERANCE = true;
5454
template <typename i_t, typename f_t>
5555
class problem_t {
5656
public:
57-
problem_t(const optimization_problem_t<i_t, f_t>& problem);
57+
problem_t(const optimization_problem_t<i_t, f_t>& problem,
58+
const typename mip_solver_settings_t<i_t, f_t>::tolerances_t tolerances_ = {});
5859
problem_t() = delete;
5960
// copy constructor
6061
problem_t(const problem_t<i_t, f_t>& problem);

0 commit comments

Comments
 (0)