Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
6e309df
working fp32 support for PDLP no presolve no crossover
Kh4ster Feb 4, 2026
b9ac363
updade run_pdlp to allow for fp32
Kh4ster Feb 4, 2026
c7f6a24
Merge branch 'main' into pdlp_float32
Kh4ster Feb 6, 2026
3d11bfd
Merge branch 'main' into pdlp_float32
Kh4ster Feb 11, 2026
c4e778e
support fp32 with presolve
Kh4ster Feb 11, 2026
6a72668
implement and toggle mixed precision
Kh4ster Feb 11, 2026
c134104
Merge branch 'main' into mixed_precision_on
Kh4ster Feb 25, 2026
76eef99
Merge branch 'main' into mixed_precision_on
Kh4ster Feb 27, 2026
ea37dd2
cleanup and doc
Kh4ster Feb 27, 2026
f8f673a
update doc
Kh4ster Feb 27, 2026
64c5457
style cleanup
Kh4ster Feb 27, 2026
342ba32
use || for all pdlp float instanciation
Kh4ster Feb 27, 2026
366fd6a
address PR comments
Kh4ster Feb 27, 2026
abf13f3
add forgotten parameter
Kh4ster Feb 27, 2026
2cb9ce5
Merge branch 'main' into pdlp_fp32_and_mixed_precision_support
Kh4ster Feb 27, 2026
db6b2a3
handle cuda version not supporting mixed precision
Kh4ster Mar 2, 2026
410ec3c
Merge branch 'main' into pdlp_fp32_and_mixed_precision_support
Kh4ster Mar 2, 2026
a3dd383
fix compilation issue following the recent main merge
Kh4ster Mar 3, 2026
14f9052
fix cuda version guard to check cusparse version dynamically
Kh4ster Mar 3, 2026
ab9e8eb
fix doc
Kh4ster Mar 3, 2026
081a164
handle fp32 inside pdlp to allow exposing it at the c and Python laye…
Kh4ster Mar 5, 2026
297df88
Merge branch 'main' into pdlp_fp32_and_mixed_precision_support
Kh4ster Mar 5, 2026
f61ca92
address PR comments
Kh4ster Mar 6, 2026
7ef14d4
fix style
Kh4ster Mar 6, 2026
e8bd9b6
add stream parameter and fix c api to handle if mixed is not supported
Kh4ster Mar 6, 2026
1227b35
fix style
Kh4ster Mar 6, 2026
973346d
fix compile issue
Kh4ster Mar 6, 2026
647191d
modify c mixed precision test so that it can catch the issue both loc…
Kh4ster Mar 6, 2026
2c70fff
style
Kh4ster Mar 6, 2026
22e7571
removed mixed precision test from python test as it's inconvient to c…
Kh4ster Mar 6, 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
73 changes: 46 additions & 27 deletions benchmarks/linear_programming/cuopt/run_pdlp.cu
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,13 @@ static void parse_arguments(argparse::ArgumentParser& program)
.choices("None", "Papilo", "PSLP", "Default");

program.add_argument("--solution-path").help("Path where solution file will be generated");

program.add_argument("--pdlp-precision")
.help(
"PDLP precision mode. default: native type, single: FP32 internally, "
"double: FP64 explicitly, mixed: mixed-precision SpMV (FP32 matrix, FP64 vectors).")
.default_value(std::string("default"))
.choices("default", "single", "double", "mixed");
}

static cuopt::linear_programming::presolver_t string_to_presolver(const std::string& presolver)
Expand All @@ -87,6 +94,15 @@ static cuopt::linear_programming::presolver_t string_to_presolver(const std::str
return cuopt::linear_programming::presolver_t::Default;
}

static cuopt::linear_programming::pdlp_precision_t string_to_pdlp_precision(
const std::string& precision)
{
if (precision == "single") return cuopt::linear_programming::pdlp_precision_t::SinglePrecision;
if (precision == "double") return cuopt::linear_programming::pdlp_precision_t::DoublePrecision;
if (precision == "mixed") return cuopt::linear_programming::pdlp_precision_t::MixedPrecision;
return cuopt::linear_programming::pdlp_precision_t::DefaultPrecision;
}

static cuopt::linear_programming::pdlp_solver_mode_t string_to_pdlp_solver_mode(
const std::string& mode)
{
Expand All @@ -105,38 +121,24 @@ static cuopt::linear_programming::pdlp_solver_mode_t string_to_pdlp_solver_mode(
static cuopt::linear_programming::pdlp_solver_settings_t<int, double> create_solver_settings(
const argparse::ArgumentParser& program)
{
cuopt::linear_programming::pdlp_solver_settings_t<int, double> settings =
cuopt::linear_programming::pdlp_solver_settings_t<int, double>{};
cuopt::linear_programming::pdlp_solver_settings_t<int, double> settings{};

settings.time_limit = program.get<double>("--time-limit");
settings.iteration_limit = program.get<int>("--iteration-limit");
settings.set_optimality_tolerance(program.get<double>("--optimality-tolerance"));
settings.pdlp_solver_mode =
string_to_pdlp_solver_mode(program.get<std::string>("--pdlp-solver-mode"));
settings.method = static_cast<cuopt::linear_programming::method_t>(program.get<int>("--method"));
settings.crossover = program.get<int>("--crossover");
settings.presolver = string_to_presolver(program.get<std::string>("--presolver"));
settings.crossover = program.get<int>("--crossover");
settings.presolver = string_to_presolver(program.get<std::string>("--presolver"));
settings.pdlp_precision = string_to_pdlp_precision(program.get<std::string>("--pdlp-precision"));

return settings;
}

int main(int argc, char* argv[])
static int run_solver(const argparse::ArgumentParser& program, const raft::handle_t& handle_)
{
// Parse binary arguments
argparse::ArgumentParser program("solve_LP");
parse_arguments(program);

try {
program.parse_args(argc, argv);
} catch (const std::runtime_error& err) {
std::cerr << err.what() << std::endl;
std::cerr << program;
return 1;
}

// Initialize solver settings from binary arguments
cuopt::linear_programming::pdlp_solver_settings_t<int, double> settings =
create_solver_settings(program);
auto settings = create_solver_settings(program);

bool use_pdlp_solver_mode = true;
if (program.is_used("--pdlp-hyper-params-path")) {
Expand All @@ -145,13 +147,6 @@ int main(int argc, char* argv[])
use_pdlp_solver_mode = false;
}

// Setup up RMM memory pool
auto memory_resource = make_pool();
rmm::mr::set_current_device_resource(memory_resource.get());

// Initialize raft handle and running stream
const raft::handle_t handle_{};

// Parse MPS file
cuopt::mps_parser::mps_data_model_t<int, double> op_problem =
cuopt::mps_parser::parse_mps<int, double>(program.get<std::string>("--path"));
Expand All @@ -168,3 +163,27 @@ int main(int argc, char* argv[])

return 0;
}

int main(int argc, char* argv[])
{
// Parse binary arguments
argparse::ArgumentParser program("solve_LP");
parse_arguments(program);

try {
program.parse_args(argc, argv);
} catch (const std::runtime_error& err) {
std::cerr << err.what() << std::endl;
std::cerr << program;
return 1;
}

// Setup up RMM memory pool
auto memory_resource = make_pool();
rmm::mr::set_current_device_resource(memory_resource.get());

// Initialize raft handle and running stream
const raft::handle_t handle_{};

return run_solver(program, handle_);
}
26 changes: 23 additions & 3 deletions cpp/cuopt_cli.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,8 @@ inline auto make_async() { return std::make_shared<rmm::mr::cuda_async_memory_re
inline cuopt::init_logger_t dummy_logger(
const cuopt::linear_programming::solver_settings_t<int, double>& settings)
{
return cuopt::init_logger_t(settings.get_parameter<std::string>(CUOPT_LOG_FILE),
settings.get_parameter<bool>(CUOPT_LOG_TO_CONSOLE));
return cuopt::init_logger_t(settings.template get_parameter<std::string>(CUOPT_LOG_FILE),
settings.template get_parameter<bool>(CUOPT_LOG_TO_CONSOLE));
}

/**
Expand Down Expand Up @@ -287,6 +287,17 @@ int main(int argc, char* argv[])
.implicit_value(true);

std::map<std::string, std::string> arg_name_to_param_name;

// Register --pdlp-precision with string-to-int mapping so that it flows
// through the settings_strings map like other settings.
program.add_argument("--pdlp-precision")
.help(
"PDLP precision mode. default: native type, single: FP32 internally, "
"double: FP64 explicitly, mixed: mixed-precision SpMV (FP32 matrix, FP64 vectors).")
.default_value(std::string("-1"))
.choices("default", "single", "double", "mixed", "-1", "0", "1", "2");
arg_name_to_param_name["--pdlp-precision"] = CUOPT_PDLP_PRECISION;

{
// Add all solver settings as arguments
cuopt::linear_programming::solver_settings_t<int, double> dummy_settings;
Expand Down Expand Up @@ -341,11 +352,20 @@ int main(int argc, char* argv[])
return 1;
}

// Map symbolic pdlp-precision names to integer values
static const std::map<std::string, std::string> precision_name_to_value = {
{"default", "-1"}, {"single", "0"}, {"double", "1"}, {"mixed", "2"}};

// Read everything as a string
std::map<std::string, std::string> settings_strings;
for (auto& [arg_name, param_name] : arg_name_to_param_name) {
if (program.is_used(arg_name.c_str())) {
settings_strings[param_name] = program.get<std::string>(arg_name.c_str());
auto val = program.get<std::string>(arg_name.c_str());
if (param_name == CUOPT_PDLP_PRECISION) {
auto it = precision_name_to_value.find(val);
if (it != precision_name_to_value.end()) { val = it->second; }
}
settings_strings[param_name] = val;
}
}
// Get the values
Expand Down
7 changes: 7 additions & 0 deletions cpp/include/cuopt/linear_programming/constants.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@
#define CUOPT_NUM_GPUS "num_gpus"
#define CUOPT_USER_PROBLEM_FILE "user_problem_file"
#define CUOPT_RANDOM_SEED "random_seed"
#define CUOPT_PDLP_PRECISION "pdlp_precision"

/* @brief MIP determinism mode constants */
#define CUOPT_MODE_OPPORTUNISTIC 0
Expand Down Expand Up @@ -125,6 +126,12 @@
#define CUOPT_METHOD_DUAL_SIMPLEX 2
#define CUOPT_METHOD_BARRIER 3

/* @brief PDLP precision mode constants */
#define CUOPT_PDLP_DEFAULT_PRECISION -1
#define CUOPT_PDLP_SINGLE_PRECISION 0
#define CUOPT_PDLP_DOUBLE_PRECISION 1
#define CUOPT_PDLP_MIXED_PRECISION 2

/* @brief File format constants for problem I/O */
#define CUOPT_FILE_FORMAT_MPS 0

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,14 @@ class optimization_problem_t : public optimization_problem_interface_t<i_t, f_t>
// Conversion
// ============================================================================

/**
* @brief Convert this problem to a different floating-point precision.
*
* @tparam other_f_t Target floating-point type (e.g. float when this is double)
*/
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
Expand Down
18 changes: 17 additions & 1 deletion cpp/include/cuopt/linear_programming/pdlp/solver_settings.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,21 @@ enum method_t : int {
Barrier = CUOPT_METHOD_BARRIER
};

/**
* @brief Enum representing the PDLP precision modes.
*
* DefaultPrecision: Use the type of the problem (FP64 for double problems).
* SinglePrecision: Run PDLP internally in FP32, converting inputs and outputs.
* DoublePrecision: Explicitly run in FP64 (same as default for double problems).
* MixedPrecision: Use mixed precision SpMV (FP32 matrix with FP64 vectors/compute).
*/
enum pdlp_precision_t : int {
DefaultPrecision = CUOPT_PDLP_DEFAULT_PRECISION,
SinglePrecision = CUOPT_PDLP_SINGLE_PRECISION,
DoublePrecision = CUOPT_PDLP_DOUBLE_PRECISION,
MixedPrecision = CUOPT_PDLP_MIXED_PRECISION
};

template <typename i_t, typename f_t>
class pdlp_solver_settings_t {
public:
Expand Down Expand Up @@ -224,7 +239,7 @@ class pdlp_solver_settings_t {
bool detect_infeasibility{false};
bool strict_infeasibility{false};
i_t iteration_limit{std::numeric_limits<i_t>::max()};
double time_limit{std::numeric_limits<double>::infinity()};
f_t time_limit{std::numeric_limits<f_t>::infinity()};
pdlp_solver_mode_t pdlp_solver_mode{pdlp_solver_mode_t::Stable3};
bool log_to_console{true};
std::string log_file{""};
Expand All @@ -239,6 +254,7 @@ class pdlp_solver_settings_t {
i_t ordering{-1};
i_t barrier_dual_initial_point{-1};
bool eliminate_dense_columns{true};
pdlp_precision_t pdlp_precision{pdlp_precision_t::DefaultPrecision};
bool save_best_primal_so_far{false};
bool first_primal_feasible{false};
presolver_t presolver{presolver_t::Default};
Expand Down
7 changes: 7 additions & 0 deletions cpp/src/dual_simplex/sparse_matrix.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include <dual_simplex/sparse_vector.hpp>

#include <dual_simplex/types.hpp>
#include <mip_heuristics/mip_constants.hpp>

// #include <thrust/for_each.h>
// #include <thrust/iterator/counting_iterator.h>
Expand Down Expand Up @@ -938,6 +939,12 @@ f_t sparse_dot(const std::vector<i_t>& xind,
return dot;
}

#if MIP_INSTANTIATE_FLOAT || PDLP_INSTANTIATE_FLOAT
// Minimal float instantiation for LP usage
template class csc_matrix_t<int, float>;
template class csr_matrix_t<int, float>;
#endif

#ifdef DUAL_SIMPLEX_INSTANTIATE_DOUBLE
template class csc_matrix_t<int, double>;

Expand Down
27 changes: 24 additions & 3 deletions cpp/src/math_optimization/solution_writer.cu
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,18 @@
#include <utilities/logger.hpp>
#include "solution_writer.hpp"

#include <mip_heuristics/mip_constants.hpp>

#include <fstream>

namespace cuopt::linear_programming {

template <typename f_t>
void solution_writer_t::write_solution_to_sol_file(const std::string& filename,
const std::string& status,
const double objective_value,
const f_t objective_value,
const std::vector<std::string>& variable_names,
const std::vector<double>& variable_values)
const std::vector<f_t>& variable_values)
{
raft::common::nvtx::range fun_scope("write final solution to .sol file");
std::ofstream file(filename.data());
Expand All @@ -27,7 +30,7 @@ void solution_writer_t::write_solution_to_sol_file(const std::string& filename,
return;
}

file.precision(std::numeric_limits<double>::max_digits10 + 1);
file.precision(std::numeric_limits<f_t>::max_digits10 + 1);

file << "# Status: " << status << std::endl;

Expand All @@ -39,4 +42,22 @@ void solution_writer_t::write_solution_to_sol_file(const std::string& filename,
}
}

#if MIP_INSTANTIATE_FLOAT || PDLP_INSTANTIATE_FLOAT
template void solution_writer_t::write_solution_to_sol_file<float>(
const std::string& filename,
const std::string& status,
const float objective_value,
const std::vector<std::string>& variable_names,
const std::vector<float>& variable_values);
#endif

#if MIP_INSTANTIATE_DOUBLE
template void solution_writer_t::write_solution_to_sol_file<double>(
const std::string& filename,
const std::string& status,
const double objective_value,
const std::vector<std::string>& variable_names,
const std::vector<double>& variable_values);
#endif

} // namespace cuopt::linear_programming
7 changes: 4 additions & 3 deletions cpp/src/math_optimization/solution_writer.hpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* clang-format off */
/*
* SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
* SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
/* clang-format on */
Expand All @@ -23,10 +23,11 @@ namespace cuopt::linear_programming {
*/
class solution_writer_t {
public:
template <typename f_t>
static void write_solution_to_sol_file(const std::string& sol_file_path,
const std::string& status,
const double objective_value,
const f_t objective_value,
const std::vector<std::string>& variable_names,
const std::vector<double>& variable_values);
const std::vector<f_t>& variable_values);
};
} // namespace cuopt::linear_programming
Loading