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
5 changes: 3 additions & 2 deletions cpp/include/cuopt/linear_programming/constants.h
Original file line number Diff line number Diff line change
Expand Up @@ -137,8 +137,9 @@
#define CUOPT_EQUAL 'E'

/* @brief The variable type constants */
#define CUOPT_CONTINUOUS 'C'
#define CUOPT_INTEGER 'I'
#define CUOPT_CONTINUOUS 'C'
#define CUOPT_INTEGER 'I'
#define CUOPT_SEMI_CONTINUOUS 'S'

/* @brief The infinity constant */
#ifdef __cplusplus
Expand Down
9 changes: 5 additions & 4 deletions cpp/include/cuopt/linear_programming/cuopt_c.h
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,8 @@ cuopt_int_t cuOptWriteProblem(cuOptOptimizationProblem problem,
* @param[in] upper_bounds A pointer to an array of type cuopt_float_t of size num_variables
* containing the upper bounds of the variables
* @param[in] variable_types A pointer to an array of type char of size num_variables
* containing the types of the variables (CUOPT_CONTINUOUS or CUOPT_INTEGER)
* containing the types of the variables (CUOPT_CONTINUOUS, CUOPT_INTEGER, or
* CUOPT_SEMI_CONTINUOUS)
* @param[out] problem_ptr Pointer to store the created optimization problem
* @return CUOPT_SUCCESS if successful, CUOPT_ERROR otherwise
*/
Expand Down Expand Up @@ -229,8 +230,8 @@ cuopt_int_t cuOptCreateProblem(cuopt_int_t num_constraints,
* cuopt_float_t of size num_variables containing the upper bounds of the variables.
*
* @param[in] variable_types - A pointer to an array of type char of size
* num_variables containing the types of the variables (CUOPT_CONTINUOUS or
* CUOPT_INTEGER).
* num_variables containing the types of the variables (CUOPT_CONTINUOUS,
* CUOPT_INTEGER, or CUOPT_SEMI_CONTINUOUS).
*
* @param[out] problem_ptr - A pointer to a cuOptOptimizationProblem.
* On output the problem will be created and initialized with the provided data.
Expand Down Expand Up @@ -585,7 +586,7 @@ cuopt_int_t cuOptGetVariableUpperBounds(cuOptOptimizationProblem problem,
*
* @param[out] variable_types_ptr - A pointer to an array of type char of size
* num_variables that on output will contain the types of the variables
* (CUOPT_CONTINUOUS or CUOPT_INTEGER).
* (CUOPT_CONTINUOUS, CUOPT_INTEGER, or CUOPT_SEMI_CONTINUOUS).
*
* @return A status code indicating success or failure.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,25 @@ namespace cuopt::linear_programming {

namespace detail {

inline constexpr bool is_valid_public_var_type_code(char variable_type)
{
return variable_type == 'C' || variable_type == 'I' || variable_type == 'S';
}

inline constexpr var_t char_to_var_type(char variable_type)
{
if (variable_type == 'I' || variable_type == 'B') { return var_t::INTEGER; }
if (variable_type == 'S') { return var_t::SEMI_CONTINUOUS; }
return var_t::CONTINUOUS;
}

inline constexpr char var_type_to_char(var_t variable_type)
{
if (variable_type == var_t::INTEGER) { return 'I'; }
if (variable_type == var_t::SEMI_CONTINUOUS) { return 'S'; }
return 'C';
}

} // namespace detail

/**
Expand Down
2 changes: 1 addition & 1 deletion cpp/src/io/mps_parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,7 @@ BoundType convert(std::string_view str)
return LowerBoundIntegerVariable;
} else if (str == "UI") {
return UpperBoundIntegerVariable;
} else if (str == "SC" || str == "LC") {
} else if (str == "SC") {
Copy link
Copy Markdown
Contributor

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?

Copy link
Copy Markdown
Contributor Author

@hlinsen hlinsen May 15, 2026

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.

return SemiContinuousVariable;
} else {
mps_parser_expects(false,
Expand Down
31 changes: 31 additions & 0 deletions cpp/src/mip_heuristics/presolve/semi_continuous.cu
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand All @@ -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
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

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
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cpp/src/mip_heuristics/presolve/semi_continuous.cu` around lines 42 - 50, The
current guard in ensure_constraint_bounds_populated returns if either
get_constraint_lower_bounds() OR get_constraint_upper_bounds() is non-empty,
which leaves the other side potentially empty and breaks later rebuild logic;
change the logic so it only returns when BOTH lower and upper are populated, and
if one side is missing populate the missing bounds to the correct size (using
get_constraint_bounds() when available or sizing from get_row_types()) so that
get_constraint_lower_bounds() and get_constraint_upper_bounds() are both fully
populated before proceeding; update code in ensure_constraint_bounds_populated
to check both sides, and when one is empty create/fill it with appropriate
default or copied values consistent with existing get_constraint_bounds().

}
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,
Expand Down Expand Up @@ -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.
Expand All @@ -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();
Expand Down
14 changes: 9 additions & 5 deletions cpp/src/pdlp/cpu_optimization_problem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include <cuopt/linear_programming/cpu_optimization_problem.hpp>
#include <cuopt/linear_programming/csr_matrix_utils.hpp>
#include <cuopt/linear_programming/optimization_problem.hpp>
#include <cuopt/linear_programming/optimization_problem_utils.hpp>
#include <cuopt/linear_programming/solve_remote.hpp>

#include <cuopt/linear_programming/io/writer.hpp>
Expand Down Expand Up @@ -176,13 +177,16 @@ void cpu_optimization_problem_t<i_t, f_t>::set_variable_types(const var_t* varia
std::copy(variable_types, variable_types + size, variable_types_.begin());

// Auto-detect problem category based on variable types (matching original optimization_problem_t)
i_t n_integer = std::count_if(
variable_types_.begin(), variable_types_.end(), [](auto val) { return val == var_t::INTEGER; });
i_t n_discrete = std::count_if(variable_types_.begin(), variable_types_.end(), [](auto val) {
return val == var_t::INTEGER || val == var_t::SEMI_CONTINUOUS;
});
// By default it is LP
if (n_integer == size) {
if (n_discrete == size) {
problem_category_ = problem_category_t::IP;
} else if (n_integer > 0) {
} else if (n_discrete > 0) {
problem_category_ = problem_category_t::MIP;
} else {
problem_category_ = problem_category_t::LP;
}
}

Expand Down Expand Up @@ -749,7 +753,7 @@ void cpu_optimization_problem_t<i_t, f_t>::write_to_mps(const std::string& mps_f
var_types_char.resize(variable_types_.size());

for (size_t i = 0; i < var_types_char.size(); ++i) {
var_types_char[i] = (variable_types_[i] == var_t::INTEGER) ? 'I' : 'C';
var_types_char[i] = detail::var_type_to_char(variable_types_[i]);
}

data_model_view.set_variable_types(var_types_char.data(), var_types_char.size());
Expand Down
21 changes: 15 additions & 6 deletions cpp/src/pdlp/cuopt_c.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Add non-negative dimension guards before using sizes/indices.

num_constraints and num_variables are not validated for negativity. With negative inputs, Line 188 / Line 254 index constraint_matrix_row_offsets[num_constraints], and vector sizing with num_variables can become unsafe.

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
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cpp/src/pdlp/cuopt_c.cpp` around lines 175 - 179, Add non-negative dimension
guards for num_constraints and num_variables before they are used: validate that
num_constraints >= 0 and num_variables >= 0 at the start of the API function
(before the loop that checks variable_types and before any access to
constraint_matrix_row_offsets or sizing of vectors); if either is negative
return CUOPT_INVALID_ARGUMENT. Specifically, add checks that guard uses of
variable_types[num_variables], constraint_matrix_row_offsets[num_constraints],
and any vector/reserve calls that take num_variables or num_constraints,
ensuring early return on invalid sizes.


problem_and_stream_view_t* problem_and_stream =
new problem_and_stream_view_t(get_memory_backend_type());
Expand All @@ -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]);
Comment thread
hlinsen marked this conversation as resolved.
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
problem->set_variable_types(variable_types_host.data(), num_variables);

Expand Down Expand Up @@ -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());
Expand All @@ -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
Expand Down Expand Up @@ -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;
}
Expand Down
3 changes: 2 additions & 1 deletion cpp/src/pdlp/optimization_problem.cu
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

#include <cuopt/linear_programming/cpu_optimization_problem.hpp>
#include <cuopt/linear_programming/optimization_problem.hpp>
#include <cuopt/linear_programming/optimization_problem_utils.hpp>
#include <cuopt/linear_programming/solve_remote.hpp>

#include <cuopt/error.hpp>
Expand Down Expand Up @@ -847,7 +848,7 @@ void optimization_problem_t<i_t, f_t>::write_to_mps(const std::string& mps_file_

// Convert enum types to char types
for (size_t i = 0; i < variable_types.size(); ++i) {
variable_types[i] = (enum_variable_types[i] == var_t::INTEGER) ? 'I' : 'C';
variable_types[i] = detail::var_type_to_char(enum_variable_types[i]);
}

data_model_view.set_variable_types(variable_types.data(), variable_types.size());
Expand Down
111 changes: 111 additions & 0 deletions cpp/tests/linear_programming/c_api_tests/c_api_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -1041,6 +1041,117 @@ cuopt_int_t test_ranged_problem(cuopt_int_t* termination_status_ptr, cuopt_float
return status;
}

cuopt_int_t test_semi_continuous_problem(cuopt_int_t* termination_status_ptr,
cuopt_float_t* objective_ptr,
cuopt_float_t* solution_values)
{
cuOptOptimizationProblem problem = NULL;
cuOptSolverSettings settings = NULL;
cuOptSolution solution = NULL;

/* Minimize x with x semi-continuous and x + y = 1.
Since x is either 0 or in [5, 10], the optimum is x = 0, y = 1.
If CUOPT_SEMI_CONTINUOUS is treated as integer, this model is infeasible. */
cuopt_int_t num_variables = 2;
cuopt_int_t num_constraints = 1;
cuopt_int_t row_offsets[] = {0, 2};
cuopt_int_t column_indices[] = {0, 1};
cuopt_float_t values[] = {1.0, 1.0};
char constraint_sense[] = {CUOPT_EQUAL};
cuopt_float_t rhs[] = {1.0};
cuopt_float_t lower_bounds[] = {5.0, 0.0};
cuopt_float_t upper_bounds[] = {10.0, 1.0};
char variable_types[] = {CUOPT_SEMI_CONTINUOUS, CUOPT_CONTINUOUS};
cuopt_float_t objective_coefficients[] = {1.0, 0.0};
char check_variable_types[2];
cuopt_int_t is_mip;
cuopt_int_t status;

status = cuOptCreateProblem(num_constraints,
num_variables,
CUOPT_MINIMIZE,
0.0,
objective_coefficients,
row_offsets,
column_indices,
values,
constraint_sense,
rhs,
lower_bounds,
upper_bounds,
variable_types,
&problem);
if (status != CUOPT_SUCCESS) {
printf("Error creating semi-continuous problem\n");
goto DONE;
}

status = cuOptGetVariableTypes(problem, check_variable_types);
if (status != CUOPT_SUCCESS) {
printf("Error getting variable types for semi-continuous problem\n");
goto DONE;
}
if (check_variable_types[0] != CUOPT_SEMI_CONTINUOUS ||
check_variable_types[1] != CUOPT_CONTINUOUS) {
printf("Error: semi-continuous variable types were not preserved\n");
status = -1;
goto DONE;
}

status = cuOptIsMIP(problem, &is_mip);
if (status != CUOPT_SUCCESS) {
printf("Error getting MIP flag for semi-continuous problem\n");
goto DONE;
}
if (!is_mip) {
printf("Error: semi-continuous problem was not detected as a MIP\n");
status = -1;
goto DONE;
}

status = cuOptCreateSolverSettings(&settings);
if (status != CUOPT_SUCCESS) {
printf("Error creating solver settings\n");
goto DONE;
}
status = cuOptSetFloatParameter(settings, CUOPT_TIME_LIMIT, 10.0);
if (status != CUOPT_SUCCESS) {
printf("Error setting time limit\n");
goto DONE;
}

status = cuOptSolve(problem, settings, &solution);
if (status != CUOPT_SUCCESS) {
printf("Error solving semi-continuous problem\n");
goto DONE;
}

status = cuOptGetTerminationStatus(solution, termination_status_ptr);
if (status != CUOPT_SUCCESS) {
printf("Error getting termination status\n");
goto DONE;
}

status = cuOptGetObjectiveValue(solution, objective_ptr);
if (status != CUOPT_SUCCESS) {
printf("Error getting objective value\n");
goto DONE;
}

status = cuOptGetPrimalSolution(solution, solution_values);
if (status != CUOPT_SUCCESS) {
printf("Error getting primal solution\n");
goto DONE;
}

DONE:
cuOptDestroyProblem(&problem);
cuOptDestroySolverSettings(&settings);
cuOptDestroySolution(&solution);

return status;
}

// Test invalid bounds scenario (what MOI wrapper was producing)
cuopt_int_t test_invalid_bounds(cuopt_int_t test_mip)
{
Expand Down
13 changes: 13 additions & 0 deletions cpp/tests/linear_programming/c_api_tests/c_api_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,19 @@ TEST(c_api, test_ranged_problem)
EXPECT_NEAR(objective, 32.0, 1e-3);
}

TEST(c_api, test_semi_continuous_problem)
{
cuopt_int_t termination_status = CUOPT_TERMINATION_STATUS_NO_TERMINATION;
cuopt_float_t objective = 0.0;
cuopt_float_t solution_values[2] = {0.0, 0.0};
ASSERT_EQ(test_semi_continuous_problem(&termination_status, &objective, solution_values),
CUOPT_SUCCESS);
EXPECT_EQ(termination_status, CUOPT_TERMINATION_STATUS_OPTIMAL);
EXPECT_NEAR(objective, 0.0, 1e-6);
EXPECT_NEAR(solution_values[0], 0.0, 1e-6);
EXPECT_NEAR(solution_values[1], 1.0, 1e-6);
}

TEST(c_api, test_invalid_bounds)
{
// Test LP codepath
Expand Down
3 changes: 3 additions & 0 deletions cpp/tests/linear_programming/c_api_tests/c_api_tests.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ cuopt_int_t test_bad_parameter_name();
cuopt_int_t test_mip_get_callbacks_only();
cuopt_int_t test_mip_get_set_callbacks();
cuopt_int_t test_ranged_problem(cuopt_int_t* termination_status_ptr, cuopt_float_t* objective_ptr);
cuopt_int_t test_semi_continuous_problem(cuopt_int_t* termination_status_ptr,
cuopt_float_t* objective_ptr,
cuopt_float_t* solution_values);
cuopt_int_t test_invalid_bounds(cuopt_int_t test_mip);
cuopt_int_t test_quadratic_problem(cuopt_int_t* termination_status_ptr,
cuopt_float_t* objective_ptr);
Expand Down
Loading
Loading