Skip to content
Draft
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
1 change: 1 addition & 0 deletions cmake/sources-python.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,7 @@ set(highs_sources_python
highs/lp_data/HighsModelUtils.cpp
highs/lp_data/HighsOptions.cpp
highs/lp_data/HighsRanging.cpp
highs/lp_data/HighsReformulation.cpp
highs/lp_data/HighsSolution.cpp
highs/lp_data/HighsSolutionDebug.cpp
highs/lp_data/HighsSolve.cpp
Expand Down
1 change: 1 addition & 0 deletions cmake/sources.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,7 @@ set(highs_sources
lp_data/HighsModelUtils.cpp
lp_data/HighsOptions.cpp
lp_data/HighsRanging.cpp
lp_data/HighsReformulation.cpp
lp_data/HighsSolution.cpp
lp_data/HighsSolutionDebug.cpp
lp_data/HighsSolve.cpp
Expand Down
9 changes: 7 additions & 2 deletions highs/Highs.h
Original file line number Diff line number Diff line change
Expand Up @@ -1771,8 +1771,6 @@ class Highs {
void callLpKktCheck(const HighsLp& lp, const std::string& message = "");
HighsStatus invertRequirementError(std::string method_name) const;

HighsStatus handleInfCost();
void restoreInfCost(HighsStatus& return_status);
HighsStatus optionChangeAction();

HighsStatus userScale(HighsUserScaleData& data);
Expand Down Expand Up @@ -1800,6 +1798,13 @@ class Highs {

bool tryPdlpCleanup(HighsInt& pdlp_cleanup_iteration_limit,
const HighsInfo& presolved_lp_info) const;

// Reformulation methods
HighsStatus doReformulation();
void undoReformulation(HighsStatus& optimize_status);

HighsStatus handleInfiniteCost();
void restoreInfiniteCost(HighsStatus& optimize_status);
};

// Start of deprecated methods not in the Highs class
Expand Down
2 changes: 2 additions & 0 deletions highs/lp_data/HStruct.h
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,8 @@ struct HighsLpMods {
std::vector<double> save_inf_cost_variable_upper;

void clear();
void clearInfiniteCostRecord();
void clearSemiVariableRecord();
bool isClear();
};

Expand Down
38 changes: 11 additions & 27 deletions highs/lp_data/Highs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -977,9 +977,16 @@ HighsStatus Highs::run() {
HighsStatus Highs::optimizeHighs() {
// Level 1 of Highs::run()
//
// Move the "mods" to here
return this->multi_linear_objective_.size() ? this->multiobjectiveSolve()
: this->optimizeModel();
HighsStatus optimize_status = doReformulation();
if (optimize_status == HighsStatus::kError) return optimize_status;

optimize_status = this->multi_linear_objective_.size()
? this->multiobjectiveSolve()
: this->optimizeModel();

undoReformulation(optimize_status);

return optimize_status;
}

HighsStatus Highs::optimizeLp() {
Expand Down Expand Up @@ -1088,26 +1095,7 @@ HighsStatus Highs::calledOptimizeModel() {
// Set undo_mods = false so that returnFromOptimizeModel() doesn't undo any
// mods that must be preserved - such as when solving a MIP node
bool undo_mods = false;
if (model_.lp_.has_infinite_cost_) {
// If the model has infinite costs, then try to remove them. The
// return_status indicates the success of this operation and, if
// it's unsuccessful, the model will not have been modified and
// optimizeModel() can simply return an error with model status
// HighsModelStatus::kUnknown
assert(model_.lp_.hasInfiniteCost(options_.infinite_cost));
HighsStatus return_status = handleInfCost();
if (return_status != HighsStatus::kOk) {
assert(return_status == HighsStatus::kError);
setHighsModelStatusAndClearSolutionAndBasis(HighsModelStatus::kUnknown);
return return_status;
}
// Modifications have been performed, so must be undone before
// this call to optimizeModel() returns
assert(!model_.lp_.has_infinite_cost_);
undo_mods = true;
} else {
assert(!model_.lp_.hasInfiniteCost(options_.infinite_cost));
}
assert(!model_.lp_.has_infinite_cost_);

// Ensure that all vectors in the model have exactly the right size
exactResizeModel();
Expand Down Expand Up @@ -4727,12 +4715,8 @@ HighsStatus Highs::returnFromOptimizeModel(const HighsStatus run_return_status,
called_return_from_optimize_model = true;

if (undo_mods) {
// Restore any infinite costs
this->restoreInfCost(return_status);

// Unapply any modifications that have not yet been unapplied
this->model_.lp_.unapplyMods();
// undo_mods = false;
}

// Unless solved as a MIP, report on the solution
Expand Down
127 changes: 0 additions & 127 deletions highs/lp_data/HighsInterface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2789,139 +2789,12 @@ HighsStatus Highs::invertRequirementError(std::string method_name) const {
return HighsStatus::kError;
}

HighsStatus Highs::handleInfCost() {
HighsLp& lp = this->model_.lp_;
if (!lp.has_infinite_cost_) return HighsStatus::kOk;
HighsLpMods& mods = lp.mods_;
double inf_cost = this->options_.infinite_cost;
for (HighsInt k = 0; k < 2; k++) {
// Pass twice: first checking that infinite costs can be handled,
// then handling them, so that model is unmodified if infinite
// costs cannot be handled
for (HighsInt iCol = 0; iCol < lp.num_col_; iCol++) {
double cost = lp.col_cost_[iCol];
if (cost > -inf_cost && cost < inf_cost) continue;
double lower = lp.col_lower_[iCol];
double upper = lp.col_upper_[iCol];
if (lp.isMip()) {
if (lp.integrality_[iCol] == HighsVarType::kInteger) {
lower = std::ceil(lower);
upper = std::floor(upper);
}
}
if (cost <= -inf_cost) {
if (lp.sense_ == ObjSense::kMinimize) {
// Minimizing with -inf cost so try to fix at upper bound
if (upper < kHighsInf) {
if (k) lp.col_lower_[iCol] = upper;
} else {
highsLogUser(options_.log_options, HighsLogType::kError,
"Cannot minimize with a cost on variable %d of %g and "
"upper bound of %g\n",
int(iCol), cost, upper);
return HighsStatus::kError;
}
} else {
// Maximizing with -inf cost so try to fix at lower bound
if (lower > -kHighsInf) {
if (k) lp.col_upper_[iCol] = lower;
} else {
highsLogUser(options_.log_options, HighsLogType::kError,
"Cannot maximize with a cost on variable %d of %g and "
"lower bound of %g\n",
int(iCol), cost, lower);
return HighsStatus::kError;
}
}
} else {
if (lp.sense_ == ObjSense::kMinimize) {
// Minimizing with inf cost so try to fix at lower bound
if (lower > -kHighsInf) {
if (k) lp.col_upper_[iCol] = lower;
} else {
highsLogUser(options_.log_options, HighsLogType::kError,
"Cannot minimize with a cost on variable %d of %g and "
"lower bound of %g\n",
int(iCol), cost, lower);
return HighsStatus::kError;
}
} else {
// Maximizing with inf cost so try to fix at upper bound
if (upper < kHighsInf) {
if (k) lp.col_lower_[iCol] = upper;
} else {
highsLogUser(options_.log_options, HighsLogType::kError,
"Cannot maximize with a cost on variable %d of %g and "
"upper bound of %g\n",
int(iCol), cost, upper);
return HighsStatus::kError;
}
}
}
if (k) {
mods.save_inf_cost_variable_index.push_back(iCol);
mods.save_inf_cost_variable_cost.push_back(cost);
mods.save_inf_cost_variable_lower.push_back(lower);
mods.save_inf_cost_variable_upper.push_back(upper);
lp.col_cost_[iCol] = 0;
}
}
}
// Infinite costs have been removed, but their presence in the
// original model is known from mods.save_inf_cost_variable_*, so
// set lp.has_infinite_cost_ to be false to avoid assert when run()
// is called using copy of model in MIP solver (See #1446)
lp.has_infinite_cost_ = false;

return HighsStatus::kOk;
}

HighsStatus Highs::optionChangeAction() {
if (this->iis_.valid_ && options_.iis_strategy != this->iis_.strategy_)
this->iis_.clear();
return HighsStatus::kOk;
}

void Highs::restoreInfCost(HighsStatus& return_status) {
HighsLp& lp = this->model_.lp_;
HighsBasis& basis = this->basis_;
HighsLpMods& mods = lp.mods_;
HighsInt num_inf_cost = mods.save_inf_cost_variable_index.size();
if (num_inf_cost <= 0) return;
assert(num_inf_cost);
for (HighsInt ix = 0; ix < num_inf_cost; ix++) {
HighsInt iCol = mods.save_inf_cost_variable_index[ix];
double cost = mods.save_inf_cost_variable_cost[ix];
double lower = mods.save_inf_cost_variable_lower[ix];
double upper = mods.save_inf_cost_variable_upper[ix];
double value = solution_.value_valid ? solution_.col_value[iCol] : 0;
if (basis.valid) {
assert(basis.col_status[iCol] != HighsBasisStatus::kBasic);
if (lp.col_lower_[iCol] == lower) {
basis.col_status[iCol] = HighsBasisStatus::kLower;
} else {
basis.col_status[iCol] = HighsBasisStatus::kUpper;
}
}
assert(lp.col_cost_[iCol] == 0);
if (value) this->info_.objective_function_value += value * cost;
lp.col_cost_[iCol] = cost;
lp.col_lower_[iCol] = lower;
lp.col_upper_[iCol] = upper;
}
// Infinite costs have been reintroduced, so reset to true the flag
// that was set false in Highs::handleInfCost() (See #1446)
lp.has_infinite_cost_ = true;

if (this->model_status_ == HighsModelStatus::kInfeasible) {
// Model is infeasible with the infinite cost variables fixed at
// appropriate values, so model status cannot be determined
this->model_status_ = HighsModelStatus::kUnknown;
setHighsModelStatusAndClearSolutionAndBasis(this->model_status_);
return_status = highsStatusFromHighsModelStatus(model_status_);
}
}

HighsStatus Highs::userScale(HighsUserScaleData& data) {
if (!options_.user_objective_scale && !options_.user_bound_scale)
return HighsStatus::kOk;
Expand Down
18 changes: 13 additions & 5 deletions highs/lp_data/HighsLp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -491,7 +491,14 @@ void HighsLp::unapplyMods() {
this->mods_.clear();
}

void HighsLpMods::clear() {
void HighsLpMods::clearInfiniteCostRecord() {
this->save_inf_cost_variable_index.clear();
this->save_inf_cost_variable_cost.clear();
this->save_inf_cost_variable_lower.clear();
this->save_inf_cost_variable_upper.clear();
}

void HighsLpMods::clearSemiVariableRecord() {
this->save_non_semi_variable_index.clear();
this->save_inconsistent_semi_variable_index.clear();
this->save_inconsistent_semi_variable_lower_bound_value.clear();
Expand All @@ -501,10 +508,11 @@ void HighsLpMods::clear() {
this->save_relaxed_semi_variable_lower_bound_value.clear();
this->save_tightened_semi_variable_upper_bound_index.clear();
this->save_tightened_semi_variable_upper_bound_value.clear();
this->save_inf_cost_variable_index.clear();
this->save_inf_cost_variable_cost.clear();
this->save_inf_cost_variable_lower.clear();
this->save_inf_cost_variable_upper.clear();
}

void HighsLpMods::clear() {
this->clearInfiniteCostRecord();
this->clearSemiVariableRecord();
}

bool HighsLpMods::isClear() {
Expand Down
Loading
Loading