99 * @brief Class-independent utilities for HiGHS
1010 */
1111
12+ #include < sstream>
13+
1214#include " ipm/IpxWrapper.h"
1315#include " lp_data/HighsSolutionDebug.h"
1416#include " pdlp/CupdlpWrapper.h"
@@ -347,6 +349,21 @@ HighsStatus solveUnconstrainedLp(const HighsOptions& options, const HighsLp& lp,
347349void assessExcessiveBoundCost (const HighsLogOptions log_options,
348350 const HighsModel& model,
349351 const HighsUserScaleData& user_scale_data) {
352+ const bool user_cost_or_bound_scale = user_scale_data.user_cost_scale || user_scale_data.user_bound_scale ;
353+ std::stringstream message;
354+ if (user_cost_or_bound_scale) {
355+ if (user_scale_data.user_cost_scale )
356+ message << highsFormatToString (" user_cost_scale option value of %d" ,
357+ user_scale_data.user_cost_scale );
358+ if (user_scale_data.user_bound_scale ) {
359+ if (user_scale_data.user_cost_scale ) message << " and" ;
360+ message << highsFormatToString (" user_bound_scale option value of %d" ,
361+ user_scale_data.user_bound_scale );
362+ }
363+ highsLogUser (log_options, HighsLogType::kInfo ,
364+ " Assessing excessive costs or bounds after applying%s\n " , message.str ().c_str ());
365+ }
366+ // Lambda for assessing a finite nonzero
350367 auto assessFiniteNonzero = [&](const double value, double & min_value,
351368 double & max_value) {
352369 double abs_value = std::abs (value);
@@ -439,43 +456,7 @@ void assessExcessiveBoundCost(const HighsLogOptions log_options,
439456 if (lp.num_row_ )
440457 highsLogUser (log_options, HighsLogType::kInfo , " RHS [%5.0e, %5.0e]\n " ,
441458 min_row_bound, max_row_bound);
442-
443- // Determine recommended user scaling values
444- HighsInt recommend_user_bound_scale_setting = 0 ;
445- HighsInt recommend_user_cost_scale_setting = 0 ;
446- double ratio = 1 ;
447- if (min_scalable_bound > kExcessivelyLargeBoundValue ) {
448- // All scalable bounds are excessively large, so obviously suggest
449- // scaling them down
450- ratio = kExcessivelyLargeBoundValue / min_scalable_bound;
451- } else if (0 < max_scalable_bound && max_scalable_bound < kExcessivelySmallBoundValue ) {
452- // All scalable bounds are excessively small, so obviously suggest
453- // scaling them up
454- ratio = kExcessivelySmallBoundValue / min_scalable_bound;
455- } else {
456- if (max_scalable_bound > kExcessivelyLargeBoundValue ) {
457- // Max bound is excessively large, so look to scale it down
458- ratio = kExcessivelyLargeBoundValue / max_scalable_bound;
459- if (0 < min_scalable_bound && min_scalable_bound < kExcessivelySmallBoundValue ) {
460- // Min nonzero bound is excessively small: look for balance
461-
462- } else {
463- // Ensure that scaling down large bounds doesn't lead to min
464- // bound being too small
465- }
466- } else if (0 < min_scalable_bound && min_scalable_bound < kExcessivelySmallBoundValue ) {
467- // Min bound is excessively small, so look to scale it up,
468- // ensuring that it doesn't lead to max bounds being too large
469- }
470- }
471- const double user_cost_scale_value =
472- std::pow (2 , user_scale_data.user_cost_scale );
473- const double user_bound_scale_value =
474- std::pow (2 , user_scale_data.user_bound_scale );
475- const std::string problem =
476- (user_scale_data.user_cost_scale || user_scale_data.user_bound_scale )
477- ? " User-scaled problem"
478- : " Problem" ;
459+
479460 // LPs with no columns or no finite nonzero costs will have
480461 // max_col_cost = 0
481462 assert (max_col_cost >= 0 );
@@ -485,121 +466,160 @@ void assessExcessiveBoundCost(const HighsLogOptions log_options,
485466 // LPs with no rows or no finite nonzero bounds will have
486467 // max_row_bound = 0
487468 assert (max_row_bound >= 0 );
488-
489- if (max_col_cost > kExcessivelyLargeCostValue ) {
490- // Warn that costs are excessively large, and suggest scaling
491- double ratio =
492- kExcessivelyLargeCostValue / (max_col_cost / user_cost_scale_value);
493- HighsInt suggested_user_cost_scale_setting = std::floor (std::log2 (ratio));
494- HighsInt suggested_cost_scale_exponent = std::floor (std::log10 (ratio));
495- highsLogUser (
496- log_options, HighsLogType::kWarning ,
497- " %s has excessively large costs: consider scaling the costs "
498- " by 1e%+1d or less, or setting option user_cost_scale to %d or less\n " ,
499- problem.c_str (), int (-suggested_cost_scale_exponent),
500- int (suggested_user_cost_scale_setting));
501- }
502- if (max_col_bound > kExcessivelyLargeBoundValue ) {
503- // Warn that bounds are excessively large, and suggest scaling
504- double ratio =
505- kExcessivelyLargeBoundValue / (max_col_bound / user_bound_scale_value);
506- HighsInt suggested_user_bound_scale = std::floor (std::log2 (ratio));
507- HighsInt suggested_bound_scale_exponent = std::floor (std::log10 (ratio));
508- if (lp.isMip ()) {
509- highsLogUser (
510- log_options, HighsLogType::kWarning ,
511- " %s has excessively large bounds: consider scaling the bounds "
512- " by 1e%+1d or less\n " ,
513- problem.c_str (), int (-suggested_bound_scale_exponent));
514- } else {
515- highsLogUser (
516- log_options, HighsLogType::kWarning ,
517- " %s has excessively large bounds: consider scaling the bounds "
518- " by 1e%+1d or less, "
519- " or setting option user_bound_scale to %d or less\n " ,
520- problem.c_str (), int (-suggested_bound_scale_exponent),
521- int (suggested_user_bound_scale));
522- }
523- }
524- if (max_row_bound > kExcessivelyLargeBoundValue ) {
525- // Warn that bounds are excessively large, and suggest scaling
526- double ratio =
527- kExcessivelyLargeBoundValue / (max_row_bound / user_bound_scale_value);
528- HighsInt suggested_user_bound_scale = std::floor (std::log2 (ratio));
529- HighsInt suggested_bound_scale_exponent = std::floor (std::log10 (ratio));
530- if (lp.isMip ()) {
531- highsLogUser (
532- log_options, HighsLogType::kWarning ,
533- " %s has excessively large bounds: consider scaling the bounds "
534- " by 1e%+1d or less\n " ,
535- problem.c_str (), int (-suggested_bound_scale_exponent));
469+
470+ const std::string problem = user_cost_or_bound_scale ? " User-scaled problem" : " Problem" ;
471+
472+ if (0 < min_col_cost && min_col_cost < kExcessivelySmallCostValue )
473+ highsLogUser (log_options, HighsLogType::kWarning ,
474+ " %s has excessively small costs\n " , problem.c_str ());
475+ if (max_col_cost > kExcessivelyLargeCostValue )
476+ highsLogUser (log_options, HighsLogType::kWarning ,
477+ " %s has excessively large costs\n " , problem.c_str ());
478+
479+ if (0 < min_col_bound && min_col_bound < kExcessivelySmallBoundValue )
480+ highsLogUser (log_options, HighsLogType::kWarning ,
481+ " %s has excessively small column bounds\n " , problem.c_str ());
482+ if (max_col_bound > kExcessivelyLargeBoundValue )
483+ highsLogUser (log_options, HighsLogType::kWarning ,
484+ " %s has excessively large column bounds\n " , problem.c_str ());
485+
486+ if (0 < min_row_bound && min_row_bound < kExcessivelySmallBoundValue )
487+ highsLogUser (log_options, HighsLogType::kWarning ,
488+ " %s has excessively small row bounds\n " , problem.c_str ());
489+ if (max_row_bound > kExcessivelyLargeBoundValue )
490+ highsLogUser (log_options, HighsLogType::kWarning ,
491+ " %s has excessively large row bounds\n " , problem.c_str ());
492+
493+ // Determine recommended user scaling values
494+ auto suggestScaling = [&](double min_value, double max_value, double small_value, double large_value) {
495+ double ratio = 1 ;
496+ if (min_value > large_value) {
497+ // All scalable values are excessively large, so obviously suggest
498+ // scaling them down
499+ ratio = 1.0 / min_value;
500+ assert (0 == 11 );
501+ } else if (0 < max_value && max_value < small_value) {
502+ // All scalable values are excessively small, so obviously suggest
503+ // scaling them up
504+ ratio = 1.0 / max_value;
536505 } else {
537- highsLogUser (
538- log_options, HighsLogType::kWarning ,
539- " %s has excessively large bounds: consider scaling the bounds "
540- " by 1e%+1d or less, "
541- " or setting option user_bound_scale to %d or less\n " ,
542- problem.c_str (), int (-suggested_bound_scale_exponent),
543- int (suggested_user_bound_scale));
506+ if (max_value > large_value) {
507+ // Max value is excessively large, so look to scale it down
508+ if (0 < min_value) {
509+ // Chance that small values may be scaled down below
510+ // small_value, so look for balance
511+ ratio = 1.0 / max_value;
512+ if (ratio * min_value < small_value) {
513+ ratio = std::sqrt (1.0 / (max_value * min_value));
514+ assert (0 == 325 );
515+ }
516+ assert (0 == 33 );
517+ } else {
518+ ratio = 1.0 / max_value;
519+ assert (0 == 44 );
520+ }
521+ } else if (0 < min_value && min_value < small_value) {
522+ // Min value is excessively small, so look to scale it up,
523+ if (0 < max_value) {
524+ // Chance that large values may be scaled up above
525+ // large_value, so look for balance
526+ ratio = 1.0 / min_value;
527+ if (ratio * max_value > large_value) {
528+ ratio = std::sqrt (1.0 / (max_value * min_value));
529+ assert (0 == 545 );
530+ }
531+ assert (0 == 55 );
532+ } else {
533+ ratio = 1.0 / min_value;
534+ assert (0 == 66 );
535+ }
536+ }
544537 }
545- }
546- // Now consider warning relating to small maximum costs and bounds
547- if (max_col_cost > 0 && max_col_cost < kExcessivelySmallCostValue ) {
548- // Warn that costs are excessively small, and suggest scaling
549- double ratio =
550- kExcessivelySmallCostValue / (max_col_cost / user_cost_scale_value);
551- HighsInt suggested_user_cost_scale_setting = std::ceil (std::log2 (ratio));
552- HighsInt suggested_cost_scale_exponent = std::ceil (std::log10 (ratio));
553- highsLogUser (
554- log_options, HighsLogType::kWarning ,
555- " %s has excessively small costs: consider scaling the costs up "
556- " by 1e%+1d or more, "
557- " or setting option user_cost_scale to %d or more\n " ,
558- problem.c_str (), int (suggested_cost_scale_exponent),
559- int (suggested_user_cost_scale_setting));
560- }
561- if (max_col_bound > 0 && max_col_bound < kExcessivelySmallBoundValue ) {
562- // Warn that bounds are excessively small, and suggest scaling
563- double ratio =
564- kExcessivelySmallBoundValue / (max_col_bound / user_bound_scale_value);
565- HighsInt suggested_user_bound_scale = std::ceil (std::log2 (ratio));
566- HighsInt suggested_bound_scale_exponent = std::ceil (std::log10 (ratio));
567- if (lp.isMip ()) {
568- highsLogUser (
569- log_options, HighsLogType::kWarning ,
570- " %s has excessively small bounds: consider scaling the bounds "
571- " by 1e%+1d or more\n " ,
572- problem.c_str (), int (suggested_bound_scale_exponent));
538+ assert (ratio);
539+ return ratio;
540+ };
541+
542+ double suggested_bound_scaling = suggestScaling (min_scalable_bound,
543+ max_scalable_bound,
544+ kExcessivelySmallBoundValue ,
545+ kExcessivelyLargeBoundValue );
546+ // Determine the suggested (new) value for user_bound_scale,
547+ // allowing for the fact that the current value has been applied
548+ HighsInt suggested_user_bound_scale =
549+ user_scale_data.user_bound_scale +
550+ std::ceil (std::log2 (suggested_bound_scaling));
551+ // Determine the order of magnitude of the suggested bound scaling -
552+ // just for logging
553+ HighsInt suggested_bound_scale_order_of_magnitude =
554+ std::ceil (std::log10 (suggested_bound_scaling));
555+ // Applying the suggested bound scaling requires the costs and
556+ // matrix columns of non-continuous variables to be scaled
557+ //
558+ // Determine the corresponding extreme non-continuous costs and
559+ // update the extreme costs so that cost scalign can be suggested
560+ double suggested_user_bound_scale_value = pow (2.0 , suggested_user_bound_scale);
561+ min_noncontinuous_col_cost *= suggested_user_bound_scale_value;
562+ max_noncontinuous_col_cost *= suggested_user_bound_scale_value;
563+ min_col_cost =
564+ std::min (min_continuous_col_cost, min_noncontinuous_col_cost);
565+ max_col_cost =
566+ std::max (max_continuous_col_cost, max_noncontinuous_col_cost);
567+ if (min_col_cost == kHighsInf ) min_col_cost = 0 ;
568+ if (max_col_cost == -kHighsInf ) max_col_cost = 0 ;
569+
570+ double suggested_cost_scaling = suggestScaling (min_col_cost,
571+ max_col_cost,
572+ kExcessivelySmallCostValue ,
573+ kExcessivelyLargeCostValue );
574+ // Determine the suggested (new) value for user_cost_scale,
575+ // allowing for the fact that the current value has been applied
576+ HighsInt suggested_user_cost_scale =
577+ user_scale_data.user_cost_scale +
578+ std::ceil (std::log2 (suggested_cost_scaling));
579+ // Determine the order of magnitude of the suggested cost scaling -
580+ // just for logging
581+ HighsInt suggested_cost_scale_order_of_magnitude =
582+ std::ceil (std::log10 (suggested_cost_scaling));
583+
584+ // Only report the order of magnitude scaling if there is no user
585+ // scaling
586+ bool order_of_magnitude_message =
587+ suggested_cost_scale_order_of_magnitude &&
588+ !user_scale_data.user_cost_scale ;
589+ message.str (std::string ());
590+ if (order_of_magnitude_message)
591+ message << highsFormatToString (" Consider scaling the costs by 1e%+1d" ,
592+ int (suggested_cost_scale_order_of_magnitude));
593+ if (suggested_user_cost_scale) {
594+ if (!order_of_magnitude_message) {
595+ message << " Consider" ;
573596 } else {
574- highsLogUser (
575- log_options, HighsLogType::kWarning ,
576- " %s has excessively small bounds: consider scaling the bounds "
577- " by 1e%+1d or more, "
578- " or setting option user_bound_scale to %d or more\n " ,
579- problem.c_str (), int (suggested_bound_scale_exponent),
580- int (suggested_user_bound_scale));
597+ message << " , or" ;
581598 }
599+ message << highsFormatToString (" setting the user_cost_scale option to %d\n " ,
600+ int (suggested_user_cost_scale));
582601 }
583- if (max_row_bound > 0 && max_row_bound < kExcessivelySmallBoundValue ) {
584- // Warn that bounds are excessively small, and suggest scaling
585- double ratio =
586- kExcessivelySmallBoundValue / (max_row_bound / user_bound_scale_value);
587- HighsInt suggested_user_bound_scale = std::ceil (std::log2 (ratio));
588- HighsInt suggested_bound_scale_exponent = std::ceil (std::log10 (ratio));
589- if (lp.isMip ()) {
590- highsLogUser (
591- log_options, HighsLogType::kWarning ,
592- " %s has excessively small bounds: consider scaling the bounds "
593- " by 1e%+1d or more\n " ,
594- problem.c_str (), int (suggested_bound_scale_exponent));
602+ if (order_of_magnitude_message || suggested_user_cost_scale)
603+ highsLogUser (log_options, HighsLogType::kWarning , " %s\n " , message.str ().c_str ());
604+
605+ message.str (std::string ());
606+ order_of_magnitude_message =
607+ suggested_bound_scale_order_of_magnitude &&
608+ !user_scale_data.user_bound_scale ;
609+ message.str (std::string ());
610+ if (order_of_magnitude_message)
611+ message << highsFormatToString (" Consider scaling the bounds by 1e%+1d" ,
612+ int (suggested_bound_scale_order_of_magnitude));
613+ if (suggested_user_bound_scale) {
614+ if (!order_of_magnitude_message) {
615+ message << " Consider" ;
595616 } else {
596- highsLogUser (
597- log_options, HighsLogType::kWarning ,
598- " %s has excessively small bounds: consider scaling the bounds "
599- " by 1e%+1d or more, "
600- " or setting option user_bound_scale to %d or more\n " ,
601- problem.c_str (), int (suggested_bound_scale_exponent),
602- int (suggested_user_bound_scale));
617+ message << " , or" ;
603618 }
619+ message << highsFormatToString (" setting the user_bound_scale option to %d\n " ,
620+ int (suggested_user_bound_scale));
604621 }
622+ if (order_of_magnitude_message || suggested_user_bound_scale)
623+ highsLogUser (log_options, HighsLogType::kWarning , " %s\n " , message.str ().c_str ());
624+
605625}
0 commit comments