Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
20 changes: 20 additions & 0 deletions check/TestModelProperties.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -141,3 +141,23 @@ TEST_CASE("afiro-ill-conditioning", "[highs_model_properties]") {
highs.getIllConditioning(ill_conditioning, constraint);
highs.getIllConditioning(ill_conditioning, !constraint);
}

TEST_CASE("infinite-bounds", "[highs_model_properties]") {
Highs h;
h.setOptionValue("output_flag", dev_run);
HighsLp lp;
// clang-format off
lp.col_lower_ = {-1e25, 0, -1e12, 0, -1e25, -1e12, -1e25, 0, -1e12, 0, -1e25, -1e12};
lp.col_upper_ = { 0, 1e25, 0, 1e12, 1e25, 1e12, 0, 1e25, 0, 1e12, 1e25, 1e12};
lp.num_col_ = static_cast<HighsInt>(lp.col_lower_.size());
lp.num_row_ = 0;
lp.col_cost_.assign(lp.num_col_, 0);
lp.integrality_ = {HighsVarType::kContinuous, HighsVarType::kContinuous,
HighsVarType::kInteger, HighsVarType::kInteger,
HighsVarType::kContinuous, HighsVarType::kInteger,
HighsVarType::kSemiContinuous, HighsVarType::kSemiContinuous,
HighsVarType::kSemiInteger, HighsVarType::kSemiInteger,
HighsVarType::kSemiContinuous, HighsVarType::kSemiInteger};
// clang-format on
REQUIRE(h.passModel(lp) == HighsStatus::kOk);
}
2 changes: 1 addition & 1 deletion docs/src/options/definitions.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
- Default: 1e+20

## [infinite\_bound](@id option-infinite-bound)
- Limit on |constraint bound|: values greater than or equal to this will be treated as infinite
- Limit on |variable/constraint bound|: values greater than or equal to this will be treated as infinite
- Type: double
- Range: [1e+15, inf]
- Default: 1e+20
Expand Down
67 changes: 55 additions & 12 deletions highs/lp_data/HighsLpUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -386,9 +386,15 @@ HighsStatus assessBounds(const HighsOptions& options, const std::string& type,

HighsInt num_infinite_lower_bound = 0;
HighsInt num_infinite_upper_bound = 0;
HighsInt num_infinite_integer_lower_bound = 0;
HighsInt num_infinite_integer_upper_bound = 0;
HighsInt local_ix;
HighsInt ml_ix;
HighsInt usr_ix = -1;
// Any bounds on integer or semi-integer variables that are not less
// than infinite_integer_bound are set to kHighsInf
const double infinite_integer_bound =
1e9; // Approx static_cast<double>(kHighsIInf/2);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Would it be worth having a separate option infinite_integer_bound (instead of hard-coding this)?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I don't think so. There's no way we'd allow a larger value, and we don't need it to be any less for the original purpose.

The reason for the infinite_bound option is that some folk will not know the value to use for an "infinite" bound, so just put something large. To be honest, allowing them to vary the value of the infinite_bound option from its default value of 1e20 (with no upper limit!) gives the misleading impression that they can have meaningful bounds greater than 1e20. If it were not for the prospect of having some internal problem scaling facility, I'd be in favour of reducing the default value to 1e12, and only allowing users to reduce it - with the only lower limit being zero.

for (HighsInt k = from_k; k < to_k + 1; k++) {
if (index_collection.is_interval_ || index_collection.is_mask_) {
local_ix = k;
Expand All @@ -404,20 +410,33 @@ HighsStatus assessBounds(const HighsOptions& options, const std::string& type,
if (index_collection.is_mask_ && !index_collection.mask_[local_ix])
continue;

// Record whether the continuous or integer infinite bound
// criterion should be used for this variable
const bool integer_for_bounds =
integrality && (integrality[usr_ix] == HighsVarType::kInteger ||
integrality[usr_ix] == HighsVarType::kSemiInteger);
const double local_infinite_bound =
integer_for_bounds ? infinite_integer_bound : infinite_bound;
if (!highs_isInfinity(-lower[usr_ix])) {
// Check whether a finite lower bound will be treated as -Infinity
bool infinite_lower_bound = lower[usr_ix] <= -infinite_bound;
bool infinite_lower_bound = lower[usr_ix] <= -local_infinite_bound;
if (infinite_lower_bound) {
lower[usr_ix] = -kHighsInf;
num_infinite_lower_bound++;
if (integer_for_bounds)
num_infinite_integer_lower_bound++;
else
num_infinite_lower_bound++;
}
}
if (!highs_isInfinity(upper[usr_ix])) {
// Check whether a finite upper bound will be treated as Infinity
bool infinite_upper_bound = upper[usr_ix] >= infinite_bound;
bool infinite_upper_bound = upper[usr_ix] >= local_infinite_bound;
if (infinite_upper_bound) {
upper[usr_ix] = kHighsInf;
num_infinite_upper_bound++;
if (integer_for_bounds)
num_infinite_integer_upper_bound++;
else
num_infinite_upper_bound++;
}
}
// Check that the lower bound does not exceed the upper bound
Expand All @@ -438,39 +457,63 @@ HighsStatus assessBounds(const HighsOptions& options, const std::string& type,
warning_found = true;
}
// Check that the lower bound is not as much as +Infinity
bool legalLowerBound = lower[usr_ix] < infinite_bound;
bool legalLowerBound = lower[usr_ix] < local_infinite_bound;
if (!legalLowerBound) {
highsLogUser(options.log_options, HighsLogType::kError,
"%3s %12" HIGHSINT_FORMAT
"%s has lower bound of %12g >= %12g\n",
type.c_str(), ml_ix, possible_name(ml_ix).c_str(),
lower[usr_ix], infinite_bound);
lower[usr_ix], local_infinite_bound);
error_found = true;
}
// Check that the upper bound is not as little as -Infinity
bool legalUpperBound = upper[usr_ix] > -infinite_bound;
bool legalUpperBound = upper[usr_ix] > -local_infinite_bound;
if (!legalUpperBound) {
highsLogUser(options.log_options, HighsLogType::kError,
"%3s %12" HIGHSINT_FORMAT
"%s has upper bound of %12g <= %12g\n",
type.c_str(), ml_ix, possible_name(ml_ix).c_str(),
upper[usr_ix], -infinite_bound);
upper[usr_ix], -local_infinite_bound);
error_found = true;
}
}
if (num_infinite_lower_bound) {
highsLogUser(options.log_options, HighsLogType::kInfo,
"%3ss:%12" HIGHSINT_FORMAT
" lower bounds less than or equal to %12g are treated as "
" lower bound%s less than or equal to %12g are treated as "
"-Infinity\n",
type.c_str(), num_infinite_lower_bound, -infinite_bound);
type.c_str(), num_infinite_lower_bound,
highsIntToPlural(num_infinite_lower_bound).c_str(),
-infinite_bound);
}
if (num_infinite_upper_bound) {
highsLogUser(options.log_options, HighsLogType::kInfo,
"%3ss:%12" HIGHSINT_FORMAT
" upper bounds greater than or equal to %12g are treated as "
" upper bound%s greater than or equal to %12g are treated as "
"+Infinity\n",
type.c_str(), num_infinite_upper_bound, infinite_bound);
type.c_str(), num_infinite_upper_bound,
highsIntToPlural(num_infinite_upper_bound).c_str(),
infinite_bound);
}
if (num_infinite_integer_lower_bound) {
highsLogUser(options.log_options, HighsLogType::kInfo,
"%3ss:%12" HIGHSINT_FORMAT
" lower bound%s less than or equal to %12g on integer "
"variables are treated as "
"-Infinity\n",
type.c_str(), num_infinite_integer_lower_bound,
highsIntToPlural(num_infinite_integer_lower_bound).c_str(),
-infinite_integer_bound);
}
if (num_infinite_integer_upper_bound) {
highsLogUser(options.log_options, HighsLogType::kInfo,
"%3ss:%12" HIGHSINT_FORMAT
" upper bound%s greater than or equal to %12g on integer "
"variables are treated as "
"+Infinity\n",
type.c_str(), num_infinite_integer_upper_bound,
highsIntToPlural(num_infinite_integer_upper_bound).c_str(),
infinite_integer_bound);
}

if (error_found)
Expand Down
2 changes: 1 addition & 1 deletion highs/lp_data/HighsOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -812,7 +812,7 @@ class HighsOptions : public HighsOptionsStruct {

record_double = new OptionRecordDouble(
"infinite_bound",
"Limit on |constraint bound|: values greater than or equal to "
"Limit on |variable/constraint bound|: values greater than or equal to "
"this will be treated as infinite",
advanced, &infinite_bound, 1e15, 1e20, kHighsInf);
records.push_back(record_double);
Expand Down
Loading