Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
a7f5121
logger_t now can support formatting strings based on `std::format`
nguidotti May 27, 2026
27f2973
moved search_tree object to a separated file. added a depth-first cle…
nguidotti May 27, 2026
501220b
track search progress using tree the weight metric. migrate the B&B l…
nguidotti May 27, 2026
9672194
simplified log lines construction in B&B
nguidotti May 29, 2026
8367299
switched to std::format for compile-time checks
nguidotti May 29, 2026
b865ae9
migrate node_id from int to uint64_t. bug fixes
nguidotti May 29, 2026
6d7f0f5
revert changes to node_id. added exception handling to the destructor.
nguidotti Jun 1, 2026
e5ea499
address coderabbit
nguidotti Jun 1, 2026
da1015f
revert log changes
nguidotti Jun 1, 2026
87cdf92
Merge branch 'main' into tree-progress
nguidotti Jun 2, 2026
6c04ed4
Merge branch 'main' into tree-progress
nguidotti Jun 3, 2026
f79029f
Merge remote-tracking branch 'origin/tree-progress' into tree-progress
nguidotti Jun 3, 2026
ffdc469
removed search progress from the logs. It is too inaccurate, especial…
nguidotti Jun 8, 2026
e47c61f
removed try-catch block in the destructor.
nguidotti Jun 8, 2026
5eb9730
removed changes to the search tree
nguidotti Jun 8, 2026
739aab2
fixed compilation
nguidotti Jun 8, 2026
3b9f99c
use constexpr constants to determine column width
nguidotti Jun 8, 2026
ec577e6
now the MIP solver gives a summary at the end
nguidotti Jun 8, 2026
2e9059e
clean up the MIP logs
nguidotti Jun 9, 2026
4b3a6b6
revert column width to use constants
nguidotti Jun 9, 2026
e7523fd
fix string_view for format variants in the logger_t
nguidotti Jun 9, 2026
ae178fb
address coderabbit
nguidotti Jun 9, 2026
9e4e2a3
update last log line
nguidotti Jun 10, 2026
1d30f7f
fixed log message when concurrent root solve is disabled
nguidotti Jun 10, 2026
c542637
Merge branch 'main' into last-log-cleanup
nguidotti Jun 10, 2026
3fbee38
fixes parsing for remote execution test
nguidotti Jun 10, 2026
1e992c0
Fix test_cli.sh to match renamed MIP summary log line
nguidotti Jun 11, 2026
651e2ba
update logs based on Miles comments
nguidotti Jun 18, 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 @@ -9,7 +9,6 @@
#include <atomic>
#include <limits>
namespace cuopt::linear_programming {

template <typename i_t, typename f_t>
struct solver_stats_t {
// Direction-neutral placeholder; solver_context initializes based on maximize/minimize.
Expand Down
232 changes: 105 additions & 127 deletions cpp/src/branch_and_bound/branch_and_bound.cpp

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions cpp/src/branch_and_bound/branch_and_bound.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,7 @@ class branch_and_bound_t {
omp_atomic_t<f_t> lower_bound_numerical_;
std::function<void(f_t)> user_bound_callback_;

void print_table_header();
void report_heuristic(f_t obj);
void report(char symbol,
f_t obj,
Expand Down
2 changes: 2 additions & 0 deletions cpp/src/branch_and_bound/symmetry.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -684,6 +684,8 @@ std::unique_ptr<mip_symmetry_t<i_t, f_t>> detect_symmetry(
const simplex_solver_settings_t<i_t, f_t>& settings,
bool& has_symmetry)
{
settings.log.printf("\nRunning symmetry detection...\n");

has_symmetry = false;

f_t start_time = tic();
Expand Down
6 changes: 4 additions & 2 deletions cpp/src/cuts/cuts.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3592,6 +3592,8 @@ variable_bounds_t<i_t, f_t>::variable_bounds_t(const lp_problem_t<i_t, f_t>& lp,
}
f_t start_time = tic();

settings.log.debug("Computing variable bounds...");

std::vector<i_t> num_integer_in_row(lp.num_rows, 0);

// Construct the slack map
Expand Down Expand Up @@ -3763,7 +3765,7 @@ variable_bounds_t<i_t, f_t>::variable_bounds_t(const lp_problem_t<i_t, f_t>& lp,
}
}
upper_offsets[lp.num_cols] = upper_edges;
settings.log.printf("%d variable upper bounds in %.2f seconds\n", upper_edges, toc(start_time));
settings.log.debug("%d variable upper bounds in %.2f seconds\n", upper_edges, toc(start_time));

// Now go through all continuous variables and use the activiites to get lower variable bounds
i_t lower_edges = 0;
Expand Down Expand Up @@ -3854,7 +3856,7 @@ variable_bounds_t<i_t, f_t>::variable_bounds_t(const lp_problem_t<i_t, f_t>& lp,
}
}
lower_offsets[lp.num_cols] = lower_edges;
settings.log.printf("%d variable lower bounds in %.2f seconds\n", lower_edges, toc(start_time));
settings.log.debug("%d variable lower bounds in %.2f seconds\n", lower_edges, toc(start_time));
}

template <typename i_t, typename f_t>
Expand Down
48 changes: 48 additions & 0 deletions cpp/src/dual_simplex/logger.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@
#include <cstdarg>
#include <cstdio>
#include <cstring>
#include <format>
#include <string_view>
#include <utility>
Comment thread
coderabbitai[bot] marked this conversation as resolved.

namespace cuopt::linear_programming::dual_simplex {

Expand Down Expand Up @@ -84,6 +87,29 @@ class logger_t {
}
}

template <typename... Args>
void print_format(std::format_string<Args...> fmt, Args&&... args)
{
if (log) {
std::string msg = std::format(fmt, std::forward<Args>(args)...);
if (log_to_console) {
#ifdef CUOPT_LOG_ACTIVE_LEVEL
std::string msg_no_newline = msg;
if (msg_no_newline.size() > 0 && msg.ends_with("\n")) { msg_no_newline.pop_back(); }

CUOPT_LOG_INFO("%s%s", log_prefix.c_str(), msg_no_newline.c_str());
#else
std::printf("%s", msg.c_str());
fflush(stdout);
#endif
}
if (log_to_file && log_file != nullptr) {
std::fprintf(log_file, "%s", msg.c_str());
fflush(log_file);
}
}
}

void debug([[maybe_unused]] const char* fmt, ...)
{
if (log) {
Expand Down Expand Up @@ -118,6 +144,28 @@ class logger_t {
}
}

template <typename... Args>
void debug_format(std::format_string<Args...> fmt, Args&&... args)
{
if (log) {
std::string msg = std::format(fmt, std::forward<Args>(args)...);
if (log_to_console) {
#ifdef CUOPT_LOG_DEBUG
std::string msg_no_newline = msg;
if (msg_no_newline.size() > 0 && msg.ends_with("\n")) { msg_no_newline.pop_back(); }
CUOPT_LOG_TRACE("%s%s", log_prefix.c_str(), msg_no_newline.c_str());
#else
std::printf("%s", msg.c_str());
fflush(stdout);
#endif
}
if (log_to_file && log_file != nullptr) {
std::fprintf(log_file, "%s", msg.c_str());
fflush(log_file);
}
}
}

bool log;
bool log_to_console;
std::string log_prefix;
Expand Down
2 changes: 1 addition & 1 deletion cpp/src/mip_heuristics/diversity/diversity_manager.cu
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ template <typename i_t, typename f_t>
bool diversity_manager_t<i_t, f_t>::run_presolve(f_t time_limit, timer_t global_timer)
{
raft::common::nvtx::range fun_scope("run_presolve");
CUOPT_LOG_INFO("Starting cuOpt presolve");
CUOPT_LOG_INFO("\nRunning cuOpt presolve");
timer_t presolve_timer(time_limit);

auto term_crit = ls.constraint_prop.bounds_update.solve(*problem_ptr);
Expand Down
10 changes: 5 additions & 5 deletions cpp/src/mip_heuristics/presolve/third_party_presolve.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -682,12 +682,12 @@ third_party_presolve_result_t<i_t, f_t> third_party_presolve_t<i_t, f_t>::apply(

papilo::Problem<f_t> papilo_problem = build_papilo_problem(op_problem, category, maximize_);

CUOPT_LOG_INFO("Original problem: %d constraints, %d variables, %d nonzeros",
papilo_problem.getNRows(),
papilo_problem.getNCols(),
papilo_problem.getConstraintMatrix().getNnz());
CUOPT_LOG_DEBUG("Original problem: %d constraints, %d variables, %d nonzeros",

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

We already log the dimensions of the problems in another place. This is duplicated info

papilo_problem.getNRows(),
papilo_problem.getNCols(),
papilo_problem.getConstraintMatrix().getNnz());

CUOPT_LOG_INFO("Calling Papilo presolver (git hash %s)", PAPILO_GITHASH);
CUOPT_LOG_INFO("\nRunning Papilo presolve (git hash %s)", PAPILO_GITHASH);
if (category == problem_category_t::MIP) { dual_postsolve = false; }
papilo::Presolve<f_t> papilo_presolver;
set_presolve_methods(papilo_presolver, category, dual_postsolve);
Expand Down
5 changes: 1 addition & 4 deletions cpp/src/mip_heuristics/solve.cu
Original file line number Diff line number Diff line change
Expand Up @@ -735,10 +735,7 @@ mip_solution_t<i_t, f_t> solve_mip_helper(optimization_problem_t<i_t, f_t>& op_p
}
}

if (sol.get_termination_status() == mip_termination_status_t::FeasibleFound ||
sol.get_termination_status() == mip_termination_status_t::Optimal) {
sol.log_detailed_summary();
}
sol.log_detailed_summary();

if (settings.sol_file != "") {
CUOPT_LOG_INFO("Writing solution to file %s", settings.sol_file.c_str());
Expand Down
2 changes: 1 addition & 1 deletion cpp/src/mip_heuristics/solver.cu
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ void extract_probing_implied_bounds(
}
}

CUOPT_LOG_INFO("Probing implied bounds: %d zero entries, %d one entries", zero_nnz, one_nnz);
CUOPT_LOG_INFO("\nProbing implied bounds: %d zero entries, %d one entries", zero_nnz, one_nnz);
}

template <typename i_t, typename f_t>
Expand Down
48 changes: 33 additions & 15 deletions cpp/src/mip_heuristics/solver_solution.cu
Original file line number Diff line number Diff line change
Expand Up @@ -238,21 +238,39 @@ void mip_solution_t<i_t, f_t>::log_summary() const
template <typename i_t, typename f_t>
void mip_solution_t<i_t, f_t>::log_detailed_summary() const
{
CUOPT_LOG_INFO(
"Solution objective: %f , relative_mip_gap %f solution_bound %f presolve_time %f "
"total_solve_time %f "
"max constraint violation %f max int violation %f max var bounds violation %f "
"nodes %d simplex_iterations %d",
objective_,
mip_gap_,
stats_.get_solution_bound(),
stats_.presolve_time,
stats_.total_solve_time,
max_constraint_violation_,
max_int_violation_,
max_variable_bound_violation_,
stats_.num_nodes,
stats_.num_simplex_iterations);
switch (termination_status_) {
case mip_termination_status_t::Optimal:
case mip_termination_status_t::FeasibleFound:
CUOPT_LOG_INFO("%s\n",
std::format("Best objective {:.4g}, best bound {:.4g}, gap {:.2f}%.",
objective_,
stats_.get_solution_bound(),
mip_gap_ * 100)
.c_str());
break;

case mip_termination_status_t::Infeasible:
CUOPT_LOG_INFO("No integer feasible solution exists.\n");
break;

case mip_termination_status_t::TimeLimit:
CUOPT_LOG_INFO("No feasible solution was found within the time limit.\n");
break;

case mip_termination_status_t::WorkLimit:
CUOPT_LOG_INFO("No feasible solution was found within the work limit.\n");
break;

case mip_termination_status_t::Unbounded: CUOPT_LOG_INFO("The problem is unbounded.\n"); break;

case mip_termination_status_t::UnboundedOrInfeasible:
CUOPT_LOG_INFO("The problem is unbounded or infeasible.\n");
break;

case mip_termination_status_t::NoTermination:
CUOPT_LOG_INFO("Warning: The solver did not terminate successfully.\n");
break;
}
}

#if MIP_INSTANTIATE_FLOAT || PDLP_INSTANTIATE_FLOAT
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,9 +120,9 @@ def _parse_cli_output(output):
result["status"] = "Optimal"
continue

# MIP solution: "Solution objective: 2.000000 , ..."
# MIP solution: "Best objective 2.000000 , ..."
m = re.match(
r"Solution objective:\s*([+-]?\d+\.?\d*(?:[eE][+-]?\d+)?)",
r"Best objective \s*([+-]?\d+\.?\d*(?:[eE][+-]?\d+)?)",
stripped,
)
if m:
Expand Down
2 changes: 1 addition & 1 deletion python/libcuopt/libcuopt/tests/test_cli.sh
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,4 @@ cuopt_cli "${RAPIDS_DATASET_ROOT_DIR}"/linear_programming/good-mps-1.lp.bz2 | gr

# Add a for mixed integer programming test with options

cuopt_cli "${RAPIDS_DATASET_ROOT_DIR}"/mip/sample.mps --mip-absolute-gap 0.01 --time-limit 10 | grep -q "Solution objective" || (echo "Expected solution objective not found" && exit 1)
cuopt_cli "${RAPIDS_DATASET_ROOT_DIR}"/mip/sample.mps --mip-absolute-gap 0.01 --time-limit 10 | grep -q "Best objective" || (echo "Expected solution objective not found" && exit 1)