Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
62d7a5f
pass global upper bound to strong branching. Added an estimation for …
nguidotti Mar 16, 2026
aa4d55c
Use a single dual simplex pivot to initialize pseudocost
chris-maes Mar 16, 2026
3289516
Merge branch 'fork/chris-maes/pseudo_cost_single_pivot_estimate' into…
nguidotti Mar 17, 2026
3ae3f22
reliability branching now rank the candidates based on the objective …
nguidotti Mar 17, 2026
2b2f94d
`single_pivot_objective_change_estimate` now returns the objective ch…
nguidotti Mar 18, 2026
925d36e
removed new cutoff in trial branching. adjusted reliability branching…
nguidotti Mar 19, 2026
c6b0e3c
small fixes
nguidotti Mar 19, 2026
94c3dc3
Merge branch 'release/26.04' into improve-reliable-branching
nguidotti Mar 20, 2026
703f30c
re-enable cutoff for trial branching
nguidotti Mar 21, 2026
bb71cde
fixed style
nguidotti Mar 23, 2026
a3193c9
fixed invalid symbols
nguidotti Mar 23, 2026
f535b49
removed debug code
nguidotti Mar 24, 2026
e8dea1e
enable by default the candidate ranking via simplex pivot
nguidotti Mar 24, 2026
839d562
remove additional debug code
nguidotti Mar 24, 2026
6c68a2b
small refactor
nguidotti Mar 25, 2026
b0cb041
Merge branch 'release/26.04' into improve-reliable-branching
nguidotti Mar 25, 2026
15604c7
small refactor
nguidotti Mar 26, 2026
7b5d429
Merge branch 'release/26.04' into improve-reliable-branching
nguidotti Mar 26, 2026
9c9d019
increasing time limit for `seymour1` instance for the determinism test
nguidotti Mar 30, 2026
b86145c
Fix issue with infinite lower bounds and try to bound free variables …
chris-maes Mar 30, 2026
5e16269
Merge branch 'release/26.04' into improve-reliable-branching
nguidotti Mar 31, 2026
bb20dd3
fixed incorrect cut off calculation. added pivot estimation for stron…
nguidotti Apr 1, 2026
c4f246e
Merge branch 'release/26.04' into improve-reliable-branching
nguidotti Apr 1, 2026
06ae755
Handle the Q matrix in no lower bound presolve
rg20 Apr 1, 2026
4432c27
Update presolve.hpp
chris-maes Apr 1, 2026
042eba2
added CLI parameter to control the maximum number of simplex iteratio…
nguidotti Apr 2, 2026
fa9e902
set simplex iteration limits to RINS and SubMIP
nguidotti Apr 2, 2026
9ea1248
Merged with release/26.04
nguidotti Apr 2, 2026
b18c472
Fixing broken links (#1028)
cwilkinson76 Apr 2, 2026
64d58f2
decreasing the work limit from 3 to 2 for `gmu-35-50` in the determin…
nguidotti Apr 2, 2026
cd852b5
Merge branch 'release/26.04' into improve-reliable-branching
nguidotti Apr 2, 2026
d8054d1
Fix a bug in complementarity residual. Finite bounds can only be so b…
chris-maes Apr 2, 2026
c7816fa
Only use a single implied bound to avoid an extra variable in barrier
chris-maes Apr 2, 2026
93f4434
Merge pull request #979 from nguidotti/improve-reliable-branching
anandhkb Apr 3, 2026
928a1e1
Update solution object to include the LP solver (#822)
nguidotti Apr 3, 2026
a290700
Race batch PDLP and dual simplex in strong branching / reliability br…
Kh4ster Apr 3, 2026
7a9a10d
Expose GPU heuristics tuning parameters via config files (#993)
aliceb-nv Apr 3, 2026
2335b85
Merge remote-tracking branch 'cuopt-nvidia/release/26.04' into barrie…
chris-maes Apr 3, 2026
db8b73a
Change compl termination. Add obj gap termination
chris-maes Apr 3, 2026
140c684
MIP Row and Objective Scaling (#906)
akifcorduk Apr 3, 2026
9133db2
Remove objective gap termination. Use user_obj and obj in relative compl
chris-maes Apr 3, 2026
545dfcf
Merge remote-tracking branch 'cuopt-nvidia/release/26.04' into barrie…
chris-maes Apr 4, 2026
178f934
Style fixes and remove obj constant print
chris-maes Apr 4, 2026
54a1822
Merge pull request #1001 from chris-maes/barrier_presolve_fixes
anandhkb Apr 4, 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
4 changes: 2 additions & 2 deletions ci/validate_wheel.sh
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,11 @@ PYDISTCHECK_ARGS=(
if [[ "${package_dir}" == "python/libcuopt" ]]; then
if [[ "${RAPIDS_CUDA_MAJOR}" == "12" ]]; then
PYDISTCHECK_ARGS+=(
--max-allowed-size-compressed '645Mi'
--max-allowed-size-compressed '650Mi'
)
else
PYDISTCHECK_ARGS+=(
--max-allowed-size-compressed '490Mi'
--max-allowed-size-compressed '495Mi'
)
fi
elif [[ "${package_dir}" != "python/cuopt" ]] && \
Expand Down
14 changes: 12 additions & 2 deletions cpp/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,8 @@ endif(BUILD_MSAN)
# infrastructure files. Those files include abseil headers, and abseil's shared library
# on conda-forge doesn't export Mutex::Dtor() in NDEBUG builds (abseil-cpp#1624).
# Keeping NDEBUG defined for gRPC files makes the header inline an empty Dtor(),
# avoiding the missing symbol at runtime.
# avoiding the missing symbol at runtime. Additionally, gRPC files are always
# compiled with -DNDEBUG (see below) so Debug builds also avoid the missing symbol.
if(DEFINE_ASSERT)
add_definitions(-DASSERT_MODE)
list(APPEND CUOPT_CUDA_FLAGS -UNDEBUG)
Expand Down Expand Up @@ -390,7 +391,7 @@ if(DEFINE_ASSERT)
endif()

# Add gRPC mapper files and generated protobuf sources
list(APPEND CUOPT_SRC_FILES
set(GRPC_INFRA_FILES
${PROTO_SRCS}
${GRPC_PROTO_SRCS}
${GRPC_SERVICE_SRCS}
Expand All @@ -401,6 +402,15 @@ list(APPEND CUOPT_SRC_FILES
src/grpc/client/grpc_client.cpp
src/grpc/client/solve_remote.cpp
)
list(APPEND CUOPT_SRC_FILES ${GRPC_INFRA_FILES})

# Always keep NDEBUG defined for gRPC infrastructure files so that abseil
# headers inline Mutex::Dtor() instead of emitting an external call.
# The conda-forge abseil shared library is built with NDEBUG and does not
# export that symbol (abseil-cpp#1624). Without this, Debug builds fail
# at runtime with "undefined symbol: absl::…::Mutex::Dtor".
set_property(SOURCE ${GRPC_INFRA_FILES} DIRECTORY ${CMAKE_SOURCE_DIR}
APPEND PROPERTY COMPILE_OPTIONS "-DNDEBUG")

add_library(cuopt SHARED
${CUOPT_SRC_FILES}
Expand Down
87 changes: 61 additions & 26 deletions cpp/cuopt_cli.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include <cuopt/linear_programming/solve.hpp>
#include <mps_parser/parser.hpp>
#include <utilities/logger.hpp>
#include <utilities/timer.hpp>

#include <raft/core/device_setter.hpp>
#include <raft/core/handle.hpp>
Expand Down Expand Up @@ -85,30 +86,22 @@ inline cuopt::init_logger_t dummy_logger(
* @brief Run a single file
* @param file_path Path to the MPS format input file containing the optimization problem
* @param initial_solution_file Path to initial solution file in SOL format
* @param settings_strings Map of solver parameters
* @param settings Merged solver settings (config file loaded in main, then CLI overrides applied)
*/
int run_single_file(const std::string& file_path,
const std::string& initial_solution_file,
bool solve_relaxation,
const std::map<std::string, std::string>& settings_strings)
cuopt::linear_programming::solver_settings_t<int, double>& settings)
{
cuopt::linear_programming::solver_settings_t<int, double> settings;

try {
for (auto& [key, val] : settings_strings) {
settings.set_parameter_from_string(key, val);
}
} catch (const std::exception& e) {
auto log = dummy_logger(settings);
CUOPT_LOG_ERROR("Error: %s", e.what());
return -1;
}
cuopt::init_logger_t log(settings.get_parameter<std::string>(CUOPT_LOG_FILE),
settings.get_parameter<bool>(CUOPT_LOG_TO_CONSOLE));

std::string base_filename = file_path.substr(file_path.find_last_of("/\\") + 1);

constexpr bool input_mps_strict = false;
cuopt::mps_parser::mps_data_model_t<int, double> mps_data_model;
bool parsing_failed = false;
auto timer = cuopt::timer_t(settings.get_parameter<double>(CUOPT_TIME_LIMIT));
{
CUOPT_LOG_INFO("Reading file %s", base_filename.c_str());
try {
Expand All @@ -123,6 +116,7 @@ int run_single_file(const std::string& file_path,
CUOPT_LOG_ERROR("Parsing MPS failed. Exiting!");
return -1;
}
CUOPT_LOG_INFO("Read file %s in %.2f seconds", base_filename.c_str(), timer.elapsed_time());

// Determine memory backend and create problem using interface
// Create handle only for GPU memory backend (avoid CUDA init on CPU-only hosts)
Expand Down Expand Up @@ -259,6 +253,21 @@ int set_cuda_module_loading(int argc, char* argv[])
*/
int main(int argc, char* argv[])
{
// Handle dump flags before argparse so no other args are required
for (int i = 1; i < argc; ++i) {
std::string arg = argv[i];
if (arg == "--dump-hyper-params") {
cuopt::linear_programming::solver_settings_t<int, double> settings;
settings.dump_parameters_to_file("/dev/stdout", true);
return 0;
}
if (arg == "--dump-params") {
cuopt::linear_programming::solver_settings_t<int, double> settings;
settings.dump_parameters_to_file("/dev/stdout", false);
return 0;
}
}

if (set_cuda_module_loading(argc, argv) != 0) { return 1; }

// Get the version string from the version_config.hpp file
Expand Down Expand Up @@ -287,6 +296,20 @@ int main(int argc, char* argv[])
.default_value(true)
.implicit_value(true);

program.add_argument("--params-file")
.help("path to parameter config file (key = value format, supports all parameters)")
.default_value(std::string(""));

program.add_argument("--dump-hyper-params")
.help("print hyper-parameters only in config file format and exit")
.default_value(false)
.implicit_value(true);

program.add_argument("--dump-params")
.help("print all parameters in config file format and exit")
.default_value(false)
.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
Expand All @@ -312,37 +335,39 @@ int main(int argc, char* argv[])
std::string arg_name = param_name_to_arg_name(param.param_name);
// handle duplicate parameters appearing in MIP and LP settings
if (arg_name_to_param_name.count(arg_name) == 0) {
program.add_argument(arg_name.c_str()).default_value(param.default_value);
auto& arg = program.add_argument(arg_name.c_str()).default_value(param.default_value);
if (param.param_name.find("hyper_") != std::string::npos) { arg.hidden(); }
arg_name_to_param_name[arg_name] = param.param_name;
}
}

for (auto& param : double_params) {
std::string arg_name = param_name_to_arg_name(param.param_name);
// handle duplicate parameters appearing in MIP and LP settings
if (arg_name_to_param_name.count(arg_name) == 0) {
program.add_argument(arg_name.c_str()).default_value(param.default_value);
auto& arg = program.add_argument(arg_name.c_str()).default_value(param.default_value);
if (param.param_name.find("hyper_") != std::string::npos) { arg.hidden(); }
arg_name_to_param_name[arg_name] = param.param_name;
}
}

for (auto& param : bool_params) {
std::string arg_name = param_name_to_arg_name(param.param_name);
if (arg_name_to_param_name.count(arg_name) == 0) {
program.add_argument(arg_name.c_str()).default_value(param.default_value);
auto& arg = program.add_argument(arg_name.c_str()).default_value(param.default_value);
if (param.param_name.find("hyper_") != std::string::npos) { arg.hidden(); }
arg_name_to_param_name[arg_name] = param.param_name;
}
}

for (auto& param : string_params) {
std::string arg_name = param_name_to_arg_name(param.param_name);
// handle duplicate parameters appearing in MIP and LP settings
if (arg_name_to_param_name.count(arg_name) == 0) {
program.add_argument(arg_name.c_str()).default_value(param.default_value);
auto& arg = program.add_argument(arg_name.c_str()).default_value(param.default_value);
if (param.param_name.find("hyper_") != std::string::npos) { arg.hidden(); }
arg_name_to_param_name[arg_name] = param.param_name;
}
} // done with solver settings
}
}
} // done with solver settings

// Parse arguments
try {
Expand Down Expand Up @@ -374,16 +399,26 @@ int main(int argc, char* argv[])

const auto initial_solution_file = program.get<std::string>("--initial-solution");
const auto solve_relaxation = program.get<bool>("--relaxation");
const auto params_file = program.get<std::string>("--params-file");

cuopt::linear_programming::solver_settings_t<int, double> settings;
try {
if (!params_file.empty()) { settings.load_parameters_from_file(params_file); }
for (auto& [key, val] : settings_strings) {
settings.set_parameter_from_string(key, val);
}
} catch (const std::exception& e) {
auto log = dummy_logger(settings);
CUOPT_LOG_ERROR("Error: %s", e.what());
return -1;
}

// Only initialize CUDA resources if using GPU memory backend (not remote execution)
auto memory_backend = cuopt::linear_programming::get_memory_backend_type();
std::vector<std::shared_ptr<rmm::mr::device_memory_resource>> memory_resources;

if (memory_backend == cuopt::linear_programming::memory_backend_t::GPU) {
// All arguments are parsed as string, default values are parsed as int if unused.
const auto num_gpus = program.is_used("--num-gpus")
? std::stoi(program.get<std::string>("--num-gpus"))
: program.get<int>("--num-gpus");
const int num_gpus = settings.get_parameter<int>(CUOPT_NUM_GPUS);

for (int i = 0; i < std::min(raft::device_setter::get_device_count(), num_gpus); ++i) {
RAFT_CUDA_TRY(cudaSetDevice(i));
Expand All @@ -393,5 +428,5 @@ int main(int argc, char* argv[])
RAFT_CUDA_TRY(cudaSetDevice(0));
}

return run_single_file(file_name, initial_solution_file, solve_relaxation, settings_strings);
return run_single_file(file_name, initial_solution_file, solve_relaxation, settings);
}
Loading
Loading