-
Notifications
You must be signed in to change notification settings - Fork 172
Implement python and C api for semi-continuous variables #1225
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
e04bf77
7f09eba
17b9147
33107e0
8e9199c
63c0c78
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -14,6 +14,7 @@ | |
| #include <dual_simplex/simplex_solver_settings.hpp> | ||
| #include <mip_heuristics/mip_constants.hpp> | ||
| #include <mip_heuristics/problem/problem.cuh> | ||
| #include <mip_heuristics/problem/problem_helpers.cuh> | ||
| #include <mip_heuristics/solver_context.cuh> | ||
| #include <pdlp/translate.hpp> | ||
| #include <utilities/copy_helpers.hpp> | ||
|
|
@@ -38,6 +39,32 @@ bool is_effectively_infinite_sc_upper_bound(f_t ub) | |
| return !std::isfinite(ub) || ub >= static_cast<f_t>(sc_infinity_threshold); | ||
| } | ||
|
|
||
| template <typename i_t, typename f_t> | ||
| void ensure_constraint_bounds_populated(optimization_problem_t<i_t, f_t>& op_problem) | ||
| { | ||
| if (!op_problem.get_constraint_lower_bounds().is_empty() || | ||
| !op_problem.get_constraint_upper_bounds().is_empty()) { | ||
| return; | ||
| } | ||
| if (op_problem.get_row_types().is_empty() || op_problem.get_constraint_bounds().is_empty()) { | ||
| return; | ||
|
Comment on lines
+42
to
+50
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Populate missing bounds when either side is absent (Line 45). The guard at Line 45 returns when either lower or upper bounds is present. If only one side exists, the other remains empty, but later rebuild logic assumes both are fully populated and can mis-size bound arrays. Proposed fix template <typename i_t, typename f_t>
void ensure_constraint_bounds_populated(optimization_problem_t<i_t, f_t>& op_problem)
{
- if (!op_problem.get_constraint_lower_bounds().is_empty() ||
- !op_problem.get_constraint_upper_bounds().is_empty()) {
+ if (!op_problem.get_constraint_lower_bounds().is_empty() &&
+ !op_problem.get_constraint_upper_bounds().is_empty()) {
return;
}
if (op_problem.get_row_types().is_empty() || op_problem.get_constraint_bounds().is_empty()) {
return;
}As per coding guidelines, "Flag missing input validation at library and server boundaries". 🤖 Prompt for AI Agents |
||
| } | ||
| const auto* handle_ptr = op_problem.get_handle_ptr(); | ||
| const auto stream = handle_ptr->get_stream(); | ||
| const i_t n = static_cast<i_t>(op_problem.get_row_types().size()); | ||
| rmm::device_uvector<f_t> clb(n, stream); | ||
| rmm::device_uvector<f_t> cub(n, stream); | ||
| auto in_first = thrust::make_zip_iterator(thrust::make_tuple( | ||
| op_problem.get_row_types().cbegin(), op_problem.get_constraint_bounds().cbegin())); | ||
| auto in_last = thrust::make_zip_iterator(thrust::make_tuple( | ||
| op_problem.get_row_types().cend(), op_problem.get_constraint_bounds().cend())); | ||
| auto out_first = thrust::make_zip_iterator(thrust::make_tuple(clb.begin(), cub.begin())); | ||
| thrust::transform( | ||
| handle_ptr->get_thrust_policy(), in_first, in_last, out_first, transform_bounds_functor<f_t>{}); | ||
| op_problem.set_constraint_lower_bounds(clb.data(), n); | ||
| op_problem.set_constraint_upper_bounds(cub.data(), n); | ||
| } | ||
|
|
||
| template <typename i_t, typename f_t> | ||
| std::vector<f_t> call_host_bounds_strengthening(const optimization_problem_t<i_t, f_t>& op_problem, | ||
| const mip_solver_settings_t<i_t, f_t>& settings, | ||
|
|
@@ -147,6 +174,8 @@ bool reformulate_semi_continuous(optimization_problem_t<i_t, f_t>& op_problem, | |
| op_relaxed.set_variable_types(relaxed_types.data(), n_orig); | ||
| op_relaxed.set_variable_lower_bounds(relaxed_lb.data(), n_orig); | ||
| op_relaxed.set_variable_upper_bounds(relaxed_ub.data(), n_orig); | ||
|
|
||
| ensure_constraint_bounds_populated(op_relaxed); | ||
| } | ||
|
|
||
| // 3. Run deterministic CPU bounds strengthening on the relaxed problem to tighten UBs. | ||
|
|
@@ -159,6 +188,8 @@ bool reformulate_semi_continuous(optimization_problem_t<i_t, f_t>& op_problem, | |
|
|
||
| // 4. Fetch all host arrays we need to extend with the new binary variables | ||
| // and linking constraints. | ||
| ensure_constraint_bounds_populated(op_problem); | ||
|
|
||
| auto obj_c = op_problem.get_objective_coefficients_host(); | ||
| auto A_vals = op_problem.get_constraint_matrix_values_host(); | ||
| auto A_idx = op_problem.get_constraint_matrix_indices_host(); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -172,6 +172,11 @@ cuopt_int_t cuOptCreateProblem(cuopt_int_t num_constraints, | |
| variable_types == nullptr) { | ||
| return CUOPT_INVALID_ARGUMENT; | ||
| } | ||
| for (int j = 0; j < num_variables; j++) { | ||
| if (!detail::is_valid_public_var_type_code(variable_types[j])) { | ||
| return CUOPT_INVALID_ARGUMENT; | ||
| } | ||
| } | ||
|
Comment on lines
+175
to
+179
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add non-negative dimension guards before using sizes/indices.
Proposed fix@@
- if (problem_ptr == nullptr || objective_coefficients == nullptr ||
+ if (problem_ptr == nullptr || objective_coefficients == nullptr ||
constraint_matrix_row_offsets == nullptr || constraint_matrix_column_indices == nullptr ||
constraint_matrix_coefficent_values == nullptr || constraint_sense == nullptr ||
rhs == nullptr || lower_bounds == nullptr || upper_bounds == nullptr ||
variable_types == nullptr) {
return CUOPT_INVALID_ARGUMENT;
}
+ if (num_constraints < 0 || num_variables < 0) { return CUOPT_INVALID_ARGUMENT; }
@@
- for (int j = 0; j < num_variables; j++) {
+ for (cuopt_int_t j = 0; j < num_variables; ++j) {
@@
- if (problem_ptr == nullptr || objective_coefficients == nullptr ||
+ if (problem_ptr == nullptr || objective_coefficients == nullptr ||
constraint_matrix_row_offsets == nullptr || constraint_matrix_column_indices == nullptr ||
constraint_matrix_coefficent_values == nullptr || constraint_lower_bounds == nullptr ||
constraint_upper_bounds == nullptr || variable_lower_bounds == nullptr ||
variable_upper_bounds == nullptr) {
return CUOPT_INVALID_ARGUMENT;
}
+ if (num_constraints < 0 || num_variables < 0) { return CUOPT_INVALID_ARGUMENT; }
@@
- for (int j = 0; j < num_variables; j++) {
+ for (cuopt_int_t j = 0; j < num_variables; ++j) {As per coding guidelines: "Flag missing input validation at library and server boundaries". Also applies to: 239-245 🤖 Prompt for AI Agents |
||
|
|
||
| problem_and_stream_view_t* problem_and_stream = | ||
| new problem_and_stream_view_t(get_memory_backend_type()); | ||
|
|
@@ -195,8 +200,7 @@ cuopt_int_t cuOptCreateProblem(cuopt_int_t num_constraints, | |
| // Set variable types (problem category is auto-detected) | ||
| std::vector<var_t> variable_types_host(num_variables); | ||
| for (int j = 0; j < num_variables; j++) { | ||
| variable_types_host[j] = | ||
| variable_types[j] == CUOPT_CONTINUOUS ? var_t::CONTINUOUS : var_t::INTEGER; | ||
| variable_types_host[j] = detail::char_to_var_type(variable_types[j]); | ||
|
hlinsen marked this conversation as resolved.
|
||
| } | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
| problem->set_variable_types(variable_types_host.data(), num_variables); | ||
|
|
||
|
|
@@ -232,6 +236,13 @@ cuopt_int_t cuOptCreateRangedProblem(cuopt_int_t num_constraints, | |
| variable_upper_bounds == nullptr) { | ||
| return CUOPT_INVALID_ARGUMENT; | ||
| } | ||
| if (variable_types != nullptr) { | ||
| for (int j = 0; j < num_variables; j++) { | ||
| if (!detail::is_valid_public_var_type_code(variable_types[j])) { | ||
| return CUOPT_INVALID_ARGUMENT; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| problem_and_stream_view_t* problem_and_stream = | ||
| new problem_and_stream_view_t(get_memory_backend_type()); | ||
|
|
@@ -257,8 +268,7 @@ cuopt_int_t cuOptCreateRangedProblem(cuopt_int_t num_constraints, | |
| std::vector<var_t> variable_types_host(num_variables); | ||
| if (variable_types != nullptr) { | ||
| for (int j = 0; j < num_variables; j++) { | ||
| variable_types_host[j] = | ||
| variable_types[j] == CUOPT_CONTINUOUS ? var_t::CONTINUOUS : var_t::INTEGER; | ||
| variable_types_host[j] = detail::char_to_var_type(variable_types[j]); | ||
| } | ||
| } else { | ||
| // Default to all continuous | ||
|
|
@@ -614,8 +624,7 @@ cuopt_int_t cuOptGetVariableTypes(cuOptOptimizationProblem problem, char* variab | |
|
|
||
| // Convert var_t enum to C API char values | ||
| for (size_t j = 0; j < variable_types_host.size(); j++) { | ||
| variable_types_ptr[j] = | ||
| variable_types_host[j] == var_t::INTEGER ? CUOPT_INTEGER : CUOPT_CONTINUOUS; | ||
| variable_types_ptr[j] = detail::var_type_to_char(variable_types_host[j]); | ||
| } | ||
| return CUOPT_SUCCESS; | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this change to mps_parser intentional?
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. The semi continuous variables is always defined with the upper bound SC which is mandatory. LC shouldn't have been added.