Skip to content
5 changes: 5 additions & 0 deletions cpp/src/grpc/client/grpc_client.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@ namespace cuopt::mathematical_optimization {
void grpc_test_inject_mock_stub(class grpc_client_t& client, std::shared_ptr<void> stub);
void grpc_test_mark_as_connected(class grpc_client_t& client);

// Implemented in solve_remote.cpp; declared here so unit tests can exercise the
// semi-continuous callback-disabling predicate without a live gRPC connection.
bool should_disable_semi_continuous_callbacks(const std::vector<var_t>& var_types,
bool has_callbacks);

/**
* @brief Configuration options for the gRPC client
*
Expand Down
18 changes: 13 additions & 5 deletions cpp/src/grpc/client/solve_remote.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include <utilities/logger.hpp>
#include "grpc_client.hpp"

#include <algorithm>
#include <cmath>
#include <cstdlib>
#include <fstream>
Expand All @@ -22,14 +23,23 @@
#include <sstream>
#include <stdexcept>

#include <thrust/count.h>

namespace cuopt::mathematical_optimization {

// Buffer added to the solver's time_limit to account for worker startup,
// GPU init, and result pipe transfer.
constexpr int kTimeoutBufferSeconds = 120;

// Pulled out of solve_mip_remote() so it can be unit-tested without a live gRPC
// connection: semi-continuous models are not supported together with remote MIP
// get/set callbacks, so callbacks are dropped rather than sent to the server.
bool should_disable_semi_continuous_callbacks(const std::vector<var_t>& var_types,
bool has_callbacks)
{
const bool has_sc_variables =
std::count(var_types.begin(), var_types.end(), var_t::SEMI_CONTINUOUS) > 0;
return has_sc_variables && has_callbacks;
}

// ============================================================================
// Helper function to get gRPC server address from environment variables
// ============================================================================
Expand Down Expand Up @@ -151,9 +161,7 @@ std::unique_ptr<mip_solution_interface_t<i_t, f_t>> solve_mip_remote(
// Check if user has set incumbent callbacks
auto mip_callbacks = settings.get_mip_callbacks();
const auto var_types = cpu_problem.get_variable_types_host();
const bool has_sc_variables =
thrust::count(var_types.begin(), var_types.end(), var_t::SEMI_CONTINUOUS) > 0;
if (has_sc_variables && !mip_callbacks.empty()) {
if (should_disable_semi_continuous_callbacks(var_types, !mip_callbacks.empty())) {
CUOPT_LOG_WARN(
"Disabling remote MIP get/set callbacks: semi-continuous models are not "
"supported with callbacks");
Expand Down
3 changes: 2 additions & 1 deletion cpp/src/math_optimization/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@

list(PREPEND
MATH_OPT_SRC_FILES
${CMAKE_CURRENT_SOURCE_DIR}/solver_settings.cu
${CMAKE_CURRENT_SOURCE_DIR}/solver_settings.cpp
${CMAKE_CURRENT_SOURCE_DIR}/solver_settings_gpu.cu
${CMAKE_CURRENT_SOURCE_DIR}/solution_reader.cu
${CMAKE_CURRENT_SOURCE_DIR}/solution_writer.cu
${CMAKE_CURRENT_SOURCE_DIR}/tic_toc.cpp
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -415,86 +415,6 @@ std::string solver_settings_t<i_t, f_t>::get_parameter_as_string(const std::stri
throw std::invalid_argument("Parameter " + name + " not found");
}

template <typename i_t, typename f_t>
void solver_settings_t<i_t, f_t>::set_initial_pdlp_primal_solution(const f_t* solution,
i_t size,
rmm::cuda_stream_view stream)
{
pdlp_settings.set_initial_primal_solution(solution, size, stream);
}

template <typename i_t, typename f_t>
void solver_settings_t<i_t, f_t>::set_initial_pdlp_dual_solution(const f_t* solution,
i_t size,
rmm::cuda_stream_view stream)
{
pdlp_settings.set_initial_dual_solution(solution, size, stream);
}

template <typename i_t, typename f_t>
void solver_settings_t<i_t, f_t>::set_pdlp_warm_start_data(
const f_t* current_primal_solution,
const f_t* current_dual_solution,
const f_t* initial_primal_average,
const f_t* initial_dual_average,
const f_t* current_ATY,
const f_t* sum_primal_solutions,
const f_t* sum_dual_solutions,
const f_t* last_restart_duality_gap_primal_solution,
const f_t* last_restart_duality_gap_dual_solution,
i_t primal_size,
i_t dual_size,
f_t initial_primal_weight,
f_t initial_step_size,
i_t total_pdlp_iterations,
i_t total_pdhg_iterations,
f_t last_candidate_kkt_score,
f_t last_restart_kkt_score,
f_t sum_solution_weight,
i_t iterations_since_last_restart)
{
pdlp_settings.set_pdlp_warm_start_data(current_primal_solution,
current_dual_solution,
initial_primal_average,
initial_dual_average,
current_ATY,
sum_primal_solutions,
sum_dual_solutions,
last_restart_duality_gap_primal_solution,
last_restart_duality_gap_dual_solution,
primal_size,
dual_size,
initial_primal_weight,
initial_step_size,
total_pdlp_iterations,
total_pdhg_iterations,
last_candidate_kkt_score,
last_restart_kkt_score,
sum_solution_weight,
iterations_since_last_restart);
}

template <typename i_t, typename f_t>
const rmm::device_uvector<f_t>& solver_settings_t<i_t, f_t>::get_initial_pdlp_primal_solution()
const
{
return pdlp_settings.get_initial_primal_solution();
}

template <typename i_t, typename f_t>
const rmm::device_uvector<f_t>& solver_settings_t<i_t, f_t>::get_initial_pdlp_dual_solution() const
{
return pdlp_settings.get_initial_dual_solution();
}

template <typename i_t, typename f_t>
void solver_settings_t<i_t, f_t>::add_initial_mip_solution(const f_t* solution,
i_t size,
rmm::cuda_stream_view stream)
{
mip_settings.add_initial_solution(solution, size, stream);
}

template <typename i_t, typename f_t>
void solver_settings_t<i_t, f_t>::set_mip_callback(internals::base_solution_callback_t* callback,
void* user_data)
Expand Down
181 changes: 181 additions & 0 deletions cpp/src/math_optimization/solver_settings_gpu.cu
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
/* clang-format off */
/*
* SPDX-FileCopyrightText: Copyright (c) 2024-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
/* clang-format on */

// Device-facing members of solver_settings_t, split out of solver_settings.cu.
//
// Everything else in that class is host-only parameter handling, so the remainder now
// builds as solver_settings.cpp into the CUDA-free cuopt_client library. Only these
// members take an rmm::cuda_stream_view or hand back a device_uvector, so they are the
// only ones that must stay in a CUDA TU inside libcuopt.
//
// The `template class` instantiation in solver_settings.cpp cannot emit these members
// (their definitions are not visible there), so they are instantiated explicitly below.

#include <cuopt/mathematical_optimization/solver_settings.hpp>

#include <rmm/cuda_stream_view.hpp>
#include <rmm/device_uvector.hpp>

#include <mip_heuristics/mip_constants.hpp>

namespace cuopt {
namespace CUOPT_EXPORT mathematical_optimization {

template <typename i_t, typename f_t>
void solver_settings_t<i_t, f_t>::set_initial_pdlp_primal_solution(const f_t* solution,
i_t size,
rmm::cuda_stream_view stream)
{
pdlp_settings.set_initial_primal_solution(solution, size, stream);
}

template <typename i_t, typename f_t>
void solver_settings_t<i_t, f_t>::set_initial_pdlp_dual_solution(const f_t* solution,
i_t size,
rmm::cuda_stream_view stream)
{
pdlp_settings.set_initial_dual_solution(solution, size, stream);
}

template <typename i_t, typename f_t>
void solver_settings_t<i_t, f_t>::set_pdlp_warm_start_data(
const f_t* current_primal_solution,
const f_t* current_dual_solution,
const f_t* initial_primal_average,
const f_t* initial_dual_average,
const f_t* current_ATY,
const f_t* sum_primal_solutions,
const f_t* sum_dual_solutions,
const f_t* last_restart_duality_gap_primal_solution,
const f_t* last_restart_duality_gap_dual_solution,
i_t primal_size,
i_t dual_size,
f_t initial_primal_weight,
f_t initial_step_size,
i_t total_pdlp_iterations,
i_t total_pdhg_iterations,
f_t last_candidate_kkt_score,
f_t last_restart_kkt_score,
f_t sum_solution_weight,
i_t iterations_since_last_restart)
{
pdlp_settings.set_pdlp_warm_start_data(current_primal_solution,
current_dual_solution,
initial_primal_average,
initial_dual_average,
current_ATY,
sum_primal_solutions,
sum_dual_solutions,
last_restart_duality_gap_primal_solution,
last_restart_duality_gap_dual_solution,
primal_size,
dual_size,
initial_primal_weight,
initial_step_size,
total_pdlp_iterations,
total_pdhg_iterations,
last_candidate_kkt_score,
last_restart_kkt_score,
sum_solution_weight,
iterations_since_last_restart);
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}

template <typename i_t, typename f_t>
const rmm::device_uvector<f_t>& solver_settings_t<i_t, f_t>::get_initial_pdlp_primal_solution()
const
{
return pdlp_settings.get_initial_primal_solution();
}

template <typename i_t, typename f_t>
const rmm::device_uvector<f_t>& solver_settings_t<i_t, f_t>::get_initial_pdlp_dual_solution() const
{
return pdlp_settings.get_initial_dual_solution();
}

template <typename i_t, typename f_t>
void solver_settings_t<i_t, f_t>::add_initial_mip_solution(const f_t* solution,
i_t size,
rmm::cuda_stream_view stream)
{
mip_settings.add_initial_solution(solution, size, stream);
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}

#if MIP_INSTANTIATE_FLOAT
template CUOPT_EXPORT void solver_settings_t<int, float>::set_initial_pdlp_primal_solution(
const float*, int, rmm::cuda_stream_view);
template CUOPT_EXPORT void solver_settings_t<int, float>::set_initial_pdlp_dual_solution(
const float*, int, rmm::cuda_stream_view);
template CUOPT_EXPORT const rmm::device_uvector<float>&
solver_settings_t<int, float>::get_initial_pdlp_primal_solution() const;
template CUOPT_EXPORT const rmm::device_uvector<float>&
solver_settings_t<int, float>::get_initial_pdlp_dual_solution() const;
template CUOPT_EXPORT void solver_settings_t<int, float>::add_initial_mip_solution(
const float*, int, rmm::cuda_stream_view);
// The 19-argument host overload. It was moved into this TU with the rest of the block, but
// `template class` in solver_settings.cpp cannot emit it (definition not visible there), so
// without this line the symbol disappears -- and it is the one the Cython layer binds to,
// which takes down every Python test, docs-build and wheel-test job.
template CUOPT_EXPORT void solver_settings_t<int, float>::set_pdlp_warm_start_data(const float*,
const float*,
const float*,
const float*,
const float*,
const float*,
const float*,
const float*,
const float*,
int,
int,
float,
float,
int,
int,
float,
float,
float,
int);
#endif

#if MIP_INSTANTIATE_DOUBLE
template CUOPT_EXPORT void solver_settings_t<int, double>::set_initial_pdlp_primal_solution(
const double*, int, rmm::cuda_stream_view);
template CUOPT_EXPORT void solver_settings_t<int, double>::set_initial_pdlp_dual_solution(
const double*, int, rmm::cuda_stream_view);
template CUOPT_EXPORT const rmm::device_uvector<double>&
solver_settings_t<int, double>::get_initial_pdlp_primal_solution() const;
template CUOPT_EXPORT const rmm::device_uvector<double>&
solver_settings_t<int, double>::get_initial_pdlp_dual_solution() const;
template CUOPT_EXPORT void solver_settings_t<int, double>::add_initial_mip_solution(
const double*, int, rmm::cuda_stream_view);
// The 19-argument host overload. It was moved into this TU with the rest of the block, but
// `template class` in solver_settings.cpp cannot emit it (definition not visible there), so
// without this line the symbol disappears -- and it is the one the Cython layer binds to,
// which takes down every Python test, docs-build and wheel-test job.
template CUOPT_EXPORT void solver_settings_t<int, double>::set_pdlp_warm_start_data(const double*,
const double*,
const double*,
const double*,
const double*,
const double*,
const double*,
const double*,
const double*,
int,
int,
double,
double,
int,
int,
double,
double,
double,
int);
#endif

} // namespace CUOPT_EXPORT mathematical_optimization
} // namespace cuopt
1 change: 1 addition & 0 deletions cpp/src/mip_heuristics/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ set(MIP_LP_NECESSARY_FILES
${CMAKE_CURRENT_SOURCE_DIR}/problem/problem.cu
${CMAKE_CURRENT_SOURCE_DIR}/problem/presolve_data.cu
${CMAKE_CURRENT_SOURCE_DIR}/solver_settings.cu
${CMAKE_CURRENT_SOURCE_DIR}/solver_settings.cpp
${CMAKE_CURRENT_SOURCE_DIR}/solver_solution.cu
${CMAKE_CURRENT_SOURCE_DIR}/local_search/rounding/simple_rounding.cu
${CMAKE_CURRENT_SOURCE_DIR}/presolve/third_party_presolve.cpp
Expand Down
Loading
Loading