Skip to content

Commit 83ec311

Browse files
authored
Print scaling information about the user model (#488)
Print scaling information and issue a warning if the range of coefficients is greater than 10^6. This PR also prints out problem size information before scaling information. It also changes 'Running' to 'Reading' so that users understand that we are still reading the file. Authors: - Chris Maes (https://github.com/chris-maes) Approvers: - Hugo Linsenmaier (https://github.com/hlinsen) URL: #488
1 parent 6d11a00 commit 83ec311

File tree

6 files changed

+116
-13
lines changed

6 files changed

+116
-13
lines changed

cpp/cuopt_cli.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ int run_single_file(const std::string& file_path,
104104
cuopt::mps_parser::mps_data_model_t<int, double> mps_data_model;
105105
bool parsing_failed = false;
106106
{
107-
CUOPT_LOG_INFO("Running file %s", base_filename.c_str());
107+
CUOPT_LOG_INFO("Reading file %s", base_filename.c_str());
108108
try {
109109
mps_data_model = cuopt::mps_parser::parse_mps<int, double>(file_path, input_mps_strict);
110110
} catch (const std::logic_error& e) {

cpp/include/cuopt/linear_programming/optimization_problem.hpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,9 +311,13 @@ class optimization_problem_t {
311311
*/
312312
void write_to_mps(const std::string& mps_file_path);
313313

314+
/* Print scaling information */
315+
void print_scaling_information() const;
316+
314317
i_t get_n_variables() const;
315318
i_t get_n_constraints() const;
316319
i_t get_nnz() const;
320+
i_t get_n_integers() const;
317321
raft::handle_t const* get_handle_ptr() const noexcept;
318322
const rmm::device_uvector<f_t>& get_constraint_matrix_values() const;
319323
rmm::device_uvector<f_t>& get_constraint_matrix_values();

cpp/src/linear_programming/optimization_problem.cu

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
*/
1717

1818
#include <cuopt/error.hpp>
19+
#include <cuopt/logger.hpp>
1920
#include <mps_parser/writer.hpp>
2021

2122
#include <cuopt/linear_programming/optimization_problem.hpp>
@@ -273,6 +274,20 @@ i_t optimization_problem_t<i_t, f_t>::get_nnz() const
273274
return A_.size();
274275
}
275276

277+
template <typename i_t, typename f_t>
278+
i_t optimization_problem_t<i_t, f_t>::get_n_integers() const
279+
{
280+
i_t n_integers = 0;
281+
if (get_n_variables() != 0) {
282+
auto enum_variable_types = cuopt::host_copy(get_variable_types());
283+
284+
for (size_t i = 0; i < enum_variable_types.size(); ++i) {
285+
if (enum_variable_types[i] == var_t::INTEGER) { n_integers++; }
286+
}
287+
}
288+
return n_integers;
289+
}
290+
276291
template <typename i_t, typename f_t>
277292
raft::handle_t const* optimization_problem_t<i_t, f_t>::get_handle_ptr() const noexcept
278293
{
@@ -583,6 +598,84 @@ void optimization_problem_t<i_t, f_t>::write_to_mps(const std::string& mps_file_
583598
cuopt::mps_parser::write_mps(data_model_view, mps_file_path);
584599
}
585600

601+
template <typename i_t, typename f_t>
602+
void optimization_problem_t<i_t, f_t>::print_scaling_information() const
603+
{
604+
std::vector<f_t> constraint_matrix_values = cuopt::host_copy(get_constraint_matrix_values());
605+
std::vector<f_t> constraint_rhs = cuopt::host_copy(get_constraint_bounds());
606+
std::vector<f_t> objective_coefficients = cuopt::host_copy(get_objective_coefficients());
607+
std::vector<f_t> variable_lower_bounds = cuopt::host_copy(get_variable_lower_bounds());
608+
std::vector<f_t> variable_upper_bounds = cuopt::host_copy(get_variable_upper_bounds());
609+
std::vector<f_t> constraint_lower_bounds = cuopt::host_copy(get_constraint_lower_bounds());
610+
std::vector<f_t> constraint_upper_bounds = cuopt::host_copy(get_constraint_upper_bounds());
611+
612+
auto findMaxAbs = [](const std::vector<f_t>& vec) -> f_t {
613+
if (vec.empty()) { return 0.0; }
614+
const f_t inf = std::numeric_limits<f_t>::infinity();
615+
616+
const size_t sz = vec.size();
617+
f_t max_abs_val = 0.0;
618+
for (size_t i = 0; i < sz; ++i) {
619+
const f_t val = std::abs(vec[i]);
620+
if (val < inf) { max_abs_val = std::max(max_abs_val, val); }
621+
}
622+
return max_abs_val;
623+
};
624+
625+
auto findMinAbs = [](const std::vector<f_t>& vec) -> f_t {
626+
if (vec.empty()) { return 0.0; }
627+
const size_t sz = vec.size();
628+
const f_t inf = std::numeric_limits<f_t>::infinity();
629+
f_t min_abs_val = inf;
630+
for (size_t i = 0; i < sz; ++i) {
631+
const f_t val = std::abs(vec[i]);
632+
if (val > 0.0) { min_abs_val = std::min(min_abs_val, val); }
633+
}
634+
return min_abs_val < inf ? min_abs_val : 0.0;
635+
};
636+
637+
f_t A_max = findMaxAbs(constraint_matrix_values);
638+
f_t A_min = findMinAbs(constraint_matrix_values);
639+
f_t b_max = findMaxAbs(constraint_rhs);
640+
f_t b_min = findMinAbs(constraint_rhs);
641+
f_t c_max = findMaxAbs(objective_coefficients);
642+
f_t c_min = findMinAbs(objective_coefficients);
643+
f_t x_lower_max = findMaxAbs(variable_lower_bounds);
644+
f_t x_lower_min = findMinAbs(variable_lower_bounds);
645+
f_t x_upper_max = findMaxAbs(variable_upper_bounds);
646+
f_t x_upper_min = findMinAbs(variable_upper_bounds);
647+
f_t cstr_lower_max = findMaxAbs(constraint_lower_bounds);
648+
f_t cstr_lower_min = findMinAbs(constraint_lower_bounds);
649+
f_t cstr_upper_max = findMaxAbs(constraint_upper_bounds);
650+
f_t cstr_upper_min = findMinAbs(constraint_upper_bounds);
651+
652+
f_t rhs_max = std::max(b_max, std::max(cstr_lower_max, cstr_upper_max));
653+
f_t rhs_min = std::min(b_min, std::min(cstr_lower_min, cstr_upper_min));
654+
655+
f_t bound_max = std::max(x_upper_max, x_lower_max);
656+
f_t bound_min = std::min(x_upper_min, x_lower_min);
657+
658+
CUOPT_LOG_INFO("Problem scaling:");
659+
CUOPT_LOG_INFO("Objective coefficents range: [%.0e, %.0e]", c_min, c_max);
660+
CUOPT_LOG_INFO("Constraint matrix coefficients range: [%.0e, %.0e]", A_min, A_max);
661+
CUOPT_LOG_INFO("Constraint rhs / bounds range: [%.0e, %.0e]", rhs_min, rhs_max);
662+
CUOPT_LOG_INFO("Variable bounds range: [%.0e, %.0e]", bound_min, bound_max);
663+
664+
auto safelog10 = [](f_t x) { return x > 0 ? std::log10(x) : 0.0; };
665+
666+
f_t obj_range = safelog10(c_max) - safelog10(c_min);
667+
f_t A_range = safelog10(A_max) - safelog10(A_min);
668+
f_t rhs_range = safelog10(rhs_max) - safelog10(rhs_min);
669+
f_t bound_range = safelog10(bound_max) - safelog10(bound_min);
670+
671+
if (obj_range >= 6.0 || A_range >= 6.0 || rhs_range >= 6.0 || bound_range >= 6.0) {
672+
CUOPT_LOG_INFO(
673+
"Warning: input problem contains a large range of coefficients: consider reformulating to "
674+
"avoid numerical difficulties.");
675+
}
676+
CUOPT_LOG_INFO("");
677+
}
678+
586679
// NOTE: Explicitly instantiate all types here in order to avoid linker error
587680
#if MIP_INSTANTIATE_FLOAT
588681
template class optimization_problem_t<int, float>;

cpp/src/linear_programming/solve.cu

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -813,6 +813,14 @@ optimization_problem_solution_t<i_t, f_t> solve_lp(optimization_problem_t<i_t, f
813813
problem_checking_t<i_t, f_t>::check_initial_solution_representation(op_problem, settings);
814814
}
815815

816+
CUOPT_LOG_INFO(
817+
"Solving a problem with %d constraints, %d variables (%d integers), and %d nonzeros",
818+
op_problem.get_n_constraints(),
819+
op_problem.get_n_variables(),
820+
0,
821+
op_problem.get_nnz());
822+
op_problem.print_scaling_information();
823+
816824
// Check for crossing bounds. Return infeasible if there are any
817825
if (problem_checking_t<i_t, f_t>::has_crossing_bounds(op_problem)) {
818826
return optimization_problem_solution_t<i_t, f_t>(pdlp_termination_status_t::PrimalInfeasible,
@@ -851,12 +859,6 @@ optimization_problem_solution_t<i_t, f_t> solve_lp(optimization_problem_t<i_t, f
851859
CUOPT_LOG_INFO("Papilo presolve time: %f", presolve_time);
852860
}
853861

854-
CUOPT_LOG_INFO(
855-
"Solving a problem with %d constraints %d variables (%d integers) and %d nonzeros",
856-
problem.n_constraints,
857-
problem.n_variables,
858-
problem.n_integer_vars,
859-
problem.nnz);
860862
CUOPT_LOG_INFO("Objective offset %f scaling_factor %f",
861863
problem.presolve_data.objective_offset,
862864
problem.presolve_data.objective_scaling_factor);

cpp/src/mip/presolve/third_party_presolve.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -402,7 +402,7 @@ std::pair<optimization_problem_t<i_t, f_t>, bool> third_party_presolve_t<i_t, f_
402402
{
403403
papilo::Problem<f_t> papilo_problem = build_papilo_problem(op_problem, category);
404404

405-
CUOPT_LOG_INFO("Unpresolved problem: %d constraints, %d variables, %d nonzeros",
405+
CUOPT_LOG_INFO("Original problem: %d constraints, %d variables, %d nonzeros",
406406
papilo_problem.getNRows(),
407407
papilo_problem.getNCols(),
408408
papilo_problem.getConstraintMatrix().getNnz());

cpp/src/mip/solve.cu

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -96,11 +96,7 @@ mip_solution_t<i_t, f_t> run_mip(detail::problem_t<i_t, f_t>& problem,
9696
}
9797
// problem contains unpreprocessed data
9898
detail::problem_t<i_t, f_t> scaled_problem(problem);
99-
CUOPT_LOG_INFO("Solving a problem with %d constraints %d variables (%d integers) and %d nonzeros",
100-
problem.n_constraints,
101-
problem.n_variables,
102-
problem.n_integer_vars,
103-
problem.nnz);
99+
104100
CUOPT_LOG_INFO("Objective offset %f scaling_factor %f",
105101
problem.presolve_data.objective_offset,
106102
problem.presolve_data.objective_scaling_factor);
@@ -181,6 +177,14 @@ mip_solution_t<i_t, f_t> solve_mip(optimization_problem_t<i_t, f_t>& op_problem,
181177
problem_checking_t<i_t, f_t>::check_problem_representation(op_problem);
182178
problem_checking_t<i_t, f_t>::check_initial_solution_representation(op_problem, settings);
183179

180+
CUOPT_LOG_INFO(
181+
"Solving a problem with %d constraints, %d variables (%d integers), and %d nonzeros",
182+
op_problem.get_n_constraints(),
183+
op_problem.get_n_variables(),
184+
op_problem.get_n_integers(),
185+
op_problem.get_nnz());
186+
op_problem.print_scaling_information();
187+
184188
// Check for crossing bounds. Return infeasible if there are any
185189
if (problem_checking_t<i_t, f_t>::has_crossing_bounds(op_problem)) {
186190
return mip_solution_t<i_t, f_t>(mip_termination_status_t::Infeasible,

0 commit comments

Comments
 (0)