Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
0f5ae25
refactor: split host-only members out of CUDA translation units
ramakrishnap-nv Aug 25, 2026
386b883
refactor: make to_optimization_problem a free function
ramakrishnap-nv Aug 25, 2026
5f80e9e
Merge branch 'main' into split/1-host-device-tus
ramakrishnap-nv Aug 28, 2026
7e5737f
test: add gtest coverage for the host/device solver-settings and CPU …
ramakrishnap-nv Aug 28, 2026
e55ebec
test: cover the <int, float> solver_settings_t GPU-facing instantiations
ramakrishnap-nv Aug 28, 2026
c92008a
fix: unbreak conda-cpp-build after macro-comma break in solver_settin…
ramakrishnap-nv Aug 28, 2026
4cd9ff1
fix: revert <int, float> solver_settings_t tests, float is never inst…
ramakrishnap-nv Aug 28, 2026
c77ffde
Merge remote-tracking branch 'origin/main' into pr-1801
ramakrishnap-nv Aug 28, 2026
fd9e28d
Merge remote-tracking branch 'origin/main' into split/1-host-device-tus
ramakrishnap-nv Aug 31, 2026
020cf6f
refactor: move the unsupported-feature predicate out of the client's …
ramakrishnap-nv Aug 31, 2026
e10b5b3
Merge remote-tracking branch 'origin/main' into split/1-host-device-tus
ramakrishnap-nv Aug 31, 2026
7de9056
refactor: pass the problem and settings into should_disable_unsupported
ramakrishnap-nv Aug 31, 2026
781c544
Merge remote-tracking branch 'origin/split/1-host-device-tus' into HEAD
ramakrishnap-nv Sep 1, 2026
9745294
Merge remote-tracking branch 'origin/main' into HEAD
ramakrishnap-nv Sep 2, 2026
63172d0
docs: trim redundant comments and drop the orphaned doxygen block
ramakrishnap-nv Sep 2, 2026
c4b4f01
Merge branch 'main' into split/2-devirtualize-to-optimization-problem
ramakrishnap-nv Sep 2, 2026
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
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ class mps_data_model_t;
// Forward declarations
template <typename i_t, typename f_t>
class optimization_problem_t;

template <typename i_t, typename f_t>
class pdlp_solver_settings_t;
template <typename i_t, typename f_t>
Expand Down Expand Up @@ -166,17 +167,6 @@ class cpu_optimization_problem_t : public optimization_problem_interface_t<i_t,
std::vector<char> get_row_types_host() const override;
std::vector<var_t> get_variable_types_host() const override;

/**
* @brief Convert this CPU optimization problem to an optimization_problem_t
* by copying CPU data to GPU (requires GPU memory transfer).
*
* @param handle_ptr RAFT handle with CUDA resources for GPU memory allocation.
* @return unique_ptr to new optimization_problem_t with all data copied to GPU
* @throws std::runtime_error if handle_ptr is null
*/
std::unique_ptr<optimization_problem_t<i_t, f_t>> to_optimization_problem(
raft::handle_t const* handle_ptr = nullptr) override;

/**
* @brief Write the optimization problem to an MPS file.
* @param[in] mps_file_path Path to the output MPS file
Expand Down Expand Up @@ -207,6 +197,12 @@ class cpu_optimization_problem_t : public optimization_problem_interface_t<i_t,
void copy_variable_types_to_host(var_t* output, i_t size) const override;

private:
// Reads this class's host-side storage directly. Callers include optimization_problem.hpp,
// where it is declared; this friend declaration alone is not visible to ordinary lookup.
template <typename I, typename F>
friend std::unique_ptr<optimization_problem_t<I, F>> to_optimization_problem(
optimization_problem_interface_t<I, F>&, raft::handle_t const*);

problem_category_t problem_category_ = problem_category_t::LP;
bool maximize_{false};
i_t n_vars_{0};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -352,13 +352,6 @@ class optimization_problem_t : public optimization_problem_interface_t<i_t, f_t>
template <typename other_f_t>
optimization_problem_t<i_t, other_f_t> convert_to_other_prec(rmm::cuda_stream_view stream) const;

/**
* @brief Returns nullptr since this is already a GPU problem.
* @return nullptr
*/
std::unique_ptr<optimization_problem_t<i_t, f_t>> to_optimization_problem(
raft::handle_t const* handle_ptr = nullptr) override;

// ============================================================================
// C API support: Copy to host (polymorphic)
// ============================================================================
Expand Down Expand Up @@ -427,5 +420,26 @@ class optimization_problem_t : public optimization_problem_interface_t<i_t, f_t>
std::vector<std::string> row_names_{};
};

/**
* @brief Convert a problem to a GPU-backed optimization_problem_t.
*
* For optimization_problem_t (GPU): returns nullptr (already is one).
* For cpu_optimization_problem_t: creates a new GPU problem, copies data, returns it.
*
* Usage pattern:
* auto temp = to_optimization_problem(problem, &handle);
* optimization_problem_t& op = temp ? *temp : static_cast<optimization_problem_t&>(problem);
*
* A free function rather than a virtual member so that cpu_optimization_problem_t's vtable
* carries no GPU-defined entry; see optimization_problem_interface.hpp.
*
* @param problem The problem to convert.
* @param handle_ptr RAFT handle with CUDA resources. Required for CPU->GPU conversion.
* @return unique_ptr to a new GPU problem, or nullptr if it already is one.
*/
template <typename i_t, typename f_t>
std::unique_ptr<optimization_problem_t<i_t, f_t>> to_optimization_problem(
optimization_problem_interface_t<i_t, f_t>& problem, raft::handle_t const* handle_ptr = nullptr);
Comment thread
coderabbitai[bot] marked this conversation as resolved.

} // namespace CUOPT_EXPORT mathematical_optimization
} // namespace cuopt
Original file line number Diff line number Diff line change
Expand Up @@ -478,22 +478,13 @@ class optimization_problem_interface_t {
// Conversion
// ============================================================================

/**
* @brief Convert to a GPU-backed optimization_problem_t.
*
* For optimization_problem_t (GPU): returns nullptr (already is one).
* For cpu_optimization_problem_t: creates new GPU problem, copies data, returns owned pointer.
*
* Usage pattern:
* auto temp = problem_interface->to_optimization_problem(&handle);
* optimization_problem_t& op = temp ? *temp : static_cast<optimization_problem_t&>(*this);
*
* @param handle_ptr RAFT handle with CUDA resources for GPU memory allocation.
* Required for CPU->GPU conversion. Ignored for GPU problems.
* @return unique_ptr to new GPU problem, or nullptr if already a GPU problem
*/
virtual std::unique_ptr<optimization_problem_t<i_t, f_t>> to_optimization_problem(
raft::handle_t const* handle_ptr = nullptr) = 0;
// NOTE: CPU -> GPU conversion is deliberately NOT a virtual member here.
//
// As a virtual, it occupied a slot in cpu_optimization_problem_t's vtable, and vtable
// relocations are resolved eagerly at load time. That made every library containing
// the vtable -- including the CUDA-free cuopt_client -- unable to load without
// libcuopt.so present. It is now the free function to_optimization_problem() declared
// in optimization_problem.hpp, which lives in libcuopt where the GPU types do.
};

} // namespace cuopt::mathematical_optimization
4 changes: 2 additions & 2 deletions cpp/src/grpc/server/grpc_worker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -425,7 +425,7 @@ static SolveResult run_mip_solve(DeserializedJob& dj,
}

SERVER_LOG_INFO("[Worker] Converting CPU problem to GPU problem...");
auto gpu_problem = dj.problem.to_optimization_problem(&handle);
auto gpu_problem = to_optimization_problem(dj.problem, &handle);

SERVER_LOG_INFO("[Worker] Calling solve_mip...");
auto gpu_solution = cuopt::mathematical_optimization::solve_mip(*gpu_problem, dj.mip_settings);
Expand Down Expand Up @@ -486,7 +486,7 @@ static SolveResult run_lp_solve(DeserializedJob& dj,
dj.lp_settings.log_to_console = config.log_to_console;

SERVER_LOG_INFO("[Worker] Converting CPU problem to GPU problem...");
auto gpu_problem = dj.problem.to_optimization_problem(&handle);
auto gpu_problem = to_optimization_problem(dj.problem, &handle);

SERVER_LOG_INFO("[Worker] Calling solve_lp...");
auto gpu_solution = cuopt::mathematical_optimization::solve_lp(*gpu_problem, dj.lp_settings);
Expand Down
2 changes: 1 addition & 1 deletion cpp/src/mip_heuristics/solve.cu
Original file line number Diff line number Diff line change
Expand Up @@ -936,7 +936,7 @@ std::unique_ptr<mip_solution_interface_t<i_t, f_t>> solve_mip(
raft::handle_t handle(stream);

// Convert CPU problem to GPU problem
auto gpu_problem = cpu_problem.to_optimization_problem(&handle);
auto gpu_problem = to_optimization_problem(cpu_problem, &handle);

// Synchronize before solving to ensure conversion is complete
stream.synchronize();
Expand Down
1 change: 1 addition & 0 deletions cpp/src/pdlp/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ set(LP_CORE_FILES
${CMAKE_CURRENT_SOURCE_DIR}/solver_settings.cu
${CMAKE_CURRENT_SOURCE_DIR}/optimization_problem.cu
${CMAKE_CURRENT_SOURCE_DIR}/cpu_optimization_problem.cpp
${CMAKE_CURRENT_SOURCE_DIR}/cpu_optimization_problem_to_gpu.cpp
${CMAKE_CURRENT_SOURCE_DIR}/backend_selection.cpp
${CMAKE_CURRENT_SOURCE_DIR}/utilities/problem_checking.cu
${CMAKE_CURRENT_SOURCE_DIR}/solve.cu
Expand Down
95 changes: 0 additions & 95 deletions cpp/src/pdlp/cpu_optimization_problem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
#include <cuopt/mathematical_optimization/cpu_optimization_problem.hpp>
#include <cuopt/mathematical_optimization/csr_matrix_utils.hpp>
#include <cuopt/mathematical_optimization/io/mps_data_model.hpp>
#include <cuopt/mathematical_optimization/optimization_problem.hpp>
#include <cuopt/mathematical_optimization/optimization_problem_utils.hpp>
#include <cuopt/mathematical_optimization/solve_remote.hpp>

Expand Down Expand Up @@ -634,100 +633,6 @@ std::vector<var_t> cpu_optimization_problem_t<i_t, f_t>::get_variable_types_host
return variable_types_;
}

// ==============================================================================
// Conversion to optimization_problem_t
// ==============================================================================

template <typename i_t, typename f_t>
std::unique_ptr<optimization_problem_t<i_t, f_t>>
cpu_optimization_problem_t<i_t, f_t>::to_optimization_problem(raft::handle_t const* handle_ptr)
{
if (handle_ptr == nullptr) {
throw std::runtime_error(
"cpu_optimization_problem_t::to_optimization_problem(): "
"handle_ptr is null. A RAFT handle with CUDA resources is required to convert "
"a CPU-backed problem to a GPU-backed optimization_problem_t.");
}

auto gpu_problem = std::make_unique<optimization_problem_t<i_t, f_t>>(handle_ptr);

// Set scalar values
gpu_problem->set_maximize(maximize_);
gpu_problem->set_objective_scaling_factor(objective_scaling_factor_);
gpu_problem->set_objective_offset(objective_offset_);
gpu_problem->set_problem_category(problem_category_);

// Set string values
if (!objective_name_.empty()) gpu_problem->set_objective_name(objective_name_);
if (!problem_name_.empty()) gpu_problem->set_problem_name(problem_name_);
if (!var_names_.empty()) gpu_problem->set_variable_names(var_names_);
if (!row_names_.empty()) gpu_problem->set_row_names(row_names_);

// Set CSR constraint matrix (data will be copied to GPU by optimization_problem_t setters)
// Use A_offsets_ presence as the guard: a valid CSR can have zero non-zeros but still
// needs row offsets to define the number of constraints.
if (!A_offsets_.empty()) {
gpu_problem->set_csr_constraint_matrix(A_.data(),
A_.size(),
A_indices_.data(),
A_indices_.size(),
A_offsets_.data(),
A_offsets_.size());
}

// Set constraint bounds
if (!b_.empty()) { gpu_problem->set_constraint_bounds(b_.data(), b_.size()); }

// Set objective coefficients
if (!c_.empty()) { gpu_problem->set_objective_coefficients(c_.data(), c_.size()); }

// Set quadratic objective if present (GPU setter symmetrizes once: H = Q + Q^T)
if (!Q_values_.empty()) {
gpu_problem->set_quadratic_objective_matrix(Q_values_.data(),
Q_values_.size(),
Q_indices_.data(),
Q_indices_.size(),
Q_offsets_.data(),
Q_offsets_.size());
}

if (!quadratic_constraints_.empty()) {
gpu_problem->set_quadratic_constraints(
std::vector<typename optimization_problem_interface_t<i_t, f_t>::quadratic_constraint_t>(
quadratic_constraints_));
}

// Set variable bounds
if (!variable_lower_bounds_.empty()) {
gpu_problem->set_variable_lower_bounds(variable_lower_bounds_.data(),
variable_lower_bounds_.size());
}
if (!variable_upper_bounds_.empty()) {
gpu_problem->set_variable_upper_bounds(variable_upper_bounds_.data(),
variable_upper_bounds_.size());
}

// Set variable types
if (!variable_types_.empty()) {
gpu_problem->set_variable_types(variable_types_.data(), variable_types_.size());
}

// Set constraint bounds
if (!constraint_lower_bounds_.empty()) {
gpu_problem->set_constraint_lower_bounds(constraint_lower_bounds_.data(),
constraint_lower_bounds_.size());
}
if (!constraint_upper_bounds_.empty()) {
gpu_problem->set_constraint_upper_bounds(constraint_upper_bounds_.data(),
constraint_upper_bounds_.size());
}

// Set row types
if (!row_types_.empty()) { gpu_problem->set_row_types(row_types_.data(), row_types_.size()); }

return gpu_problem;
}

// ==============================================================================
// File I/O
// ==============================================================================
Expand Down
Loading
Loading