Skip to content

Commit

Permalink
Merge pull request #863 from ERGO-Code/mathprog-sol
Browse files Browse the repository at this point in the history
Mathprog sol
  • Loading branch information
jajhall authored May 22, 2022
2 parents 693781b + 3c56286 commit e1dc76d
Show file tree
Hide file tree
Showing 20 changed files with 1,173 additions and 250 deletions.
16 changes: 7 additions & 9 deletions MODS.md
Original file line number Diff line number Diff line change
@@ -1,22 +1,20 @@
# Modifications for v1.3.0 since v1.2.2

* Added (partial) Python wrapper `highspy`

* Highs::setSolution can now be used to give a solution to the simplex solver (#775)

* Highs::addVar; Highs::addVars; Highs::deleteVars(Interval/set/mask) introduced for more natural modelling

* logHeader now written as first output, even when using libraries (#784)

* Highs::presolve and Highs::postsolve completed

* Highs::resetGlobalScheduler added to reset the global scheduler

# Planned modifications for v1.3.0

* write_solution_style option modified:
* "Old" HiGHS raw solution style now indicated by value `kSolutionStyleOldRaw = -1`
* Raw and pretty solution formats for Glpsol now indicated by values `kSolutionStyleGlpsolRaw = 2` and `kSolutionStyleGlpsolPretty = 3`
* Many minor fixes handling marginal LP fle behaviour
* Highs::crossover completed (#815)

* scaled_model_status_ removed from Highs (#814)
* Major revisions of CMake build system

# Planned modifications for v1.3.0

# Planned modifications beyond v1.3.0

Expand Down
9 changes: 6 additions & 3 deletions src/io/FilereaderMps.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ FilereaderRetcode FilereaderMps::readModelFromFile(const HighsOptions& options,
switch (result) {
case FreeFormatParserReturnCode::kSuccess:
lp.ensureColwise();
assert(model.lp_.objective_name_ != "");
return FilereaderRetcode::kOk;
case FreeFormatParserReturnCode::kParserError:
return FilereaderRetcode::kParserError;
Expand All @@ -63,13 +64,15 @@ FilereaderRetcode FilereaderMps::readModelFromFile(const HighsOptions& options,
readMps(options.log_options, filename, -1, -1, lp.num_row_, lp.num_col_,
lp.sense_, lp.offset_, lp.a_matrix_.start_, lp.a_matrix_.index_,
lp.a_matrix_.value_, lp.col_cost_, lp.col_lower_, lp.col_upper_,
lp.row_lower_, lp.row_upper_, lp.integrality_, lp.col_names_,
lp.row_names_, hessian.dim_, hessian.start_, hessian.index_,
hessian.value_, options.keep_n_rows);
lp.row_lower_, lp.row_upper_, lp.integrality_, lp.objective_name_,
lp.col_names_, lp.row_names_, hessian.dim_, hessian.start_,
hessian.index_, hessian.value_, lp.cost_row_location_,
options.keep_n_rows);
if (return_code == FilereaderRetcode::kOk) lp.ensureColwise();
// Comment on existence of names with spaces
hasNamesWithSpaces(options.log_options, lp.num_col_, lp.col_names_);
hasNamesWithSpaces(options.log_options, lp.num_row_, lp.row_names_);
assert(model.lp_.objective_name_ != "");
return return_code;
}

Expand Down
65 changes: 38 additions & 27 deletions src/io/HMPSIO.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,24 +34,25 @@ using std::map;
//
// Read file called filename. Returns 0 if OK and 1 if file can't be opened
//
FilereaderRetcode readMps(const HighsLogOptions& log_options,
const std::string filename, HighsInt mxNumRow,
HighsInt mxNumCol, HighsInt& numRow, HighsInt& numCol,
ObjSense& objSense, double& objOffset,
vector<HighsInt>& Astart, vector<HighsInt>& Aindex,
vector<double>& Avalue, vector<double>& colCost,
vector<double>& colLower, vector<double>& colUpper,
vector<double>& rowLower, vector<double>& rowUpper,
vector<HighsVarType>& integerColumn,
vector<string>& col_names, vector<string>& row_names,
HighsInt& Qdim, vector<HighsInt>& Qstart,
vector<HighsInt>& Qindex, vector<double>& Qvalue,
const HighsInt keep_n_rows) {
FilereaderRetcode readMps(
const HighsLogOptions& log_options, const std::string filename,
HighsInt mxNumRow, HighsInt mxNumCol, HighsInt& numRow, HighsInt& numCol,
ObjSense& objSense, double& objOffset, vector<HighsInt>& Astart,
vector<HighsInt>& Aindex, vector<double>& Avalue, vector<double>& colCost,
vector<double>& colLower, vector<double>& colUpper,
vector<double>& rowLower, vector<double>& rowUpper,
vector<HighsVarType>& integerColumn, std::string& objective_name,
vector<std::string>& col_names, vector<std::string>& row_names,
HighsInt& Qdim, vector<HighsInt>& Qstart, vector<HighsInt>& Qindex,
vector<double>& Qvalue, HighsInt& cost_row_location,
const HighsInt keep_n_rows) {
// MPS file buffer
numRow = 0;
numCol = 0;
cost_row_location = -1;
objOffset = 0;
objSense = ObjSense::kMinimize;
objective_name = "";

// Astart.clear() added since setting Astart.push_back(0) in
// setup_clearModel() messes up the MPS read
Expand Down Expand Up @@ -117,7 +118,12 @@ FilereaderRetcode readMps(const HighsLogOptions& log_options,
if (flag[0] == 'N' &&
(objName == 0 || keep_n_rows == kKeepNRowsDeleteRows)) {
// N-row: take the first as the objective and possibly ignore any others
if (objName == 0) objName = data[1];
if (objName == 0) {
objName = data[1];
std::string name(&line[4], &line[4] + 8);
objective_name = trim(name);
cost_row_location = numRow;
}
} else {
if (mxNumRow > 0 && numRow >= mxNumRow)
return FilereaderRetcode::kParserError;
Expand Down Expand Up @@ -540,7 +546,6 @@ HighsStatus writeModelAsMps(const HighsOptions& options,
std::vector<std::string> local_row_names;
local_col_names.resize(lp.num_col_);
local_row_names.resize(lp.num_row_);
//
// Initialise the local names to any existing names
if (have_col_names) local_col_names = lp.col_names_;
if (have_row_names) local_row_names = lp.row_names_;
Expand Down Expand Up @@ -576,17 +581,20 @@ HighsStatus writeModelAsMps(const HighsOptions& options,
warning_found = true;
}
}
// Set a local objective name, creating one if necessary
const std::string local_objective_name =
findModelObjectiveName(&lp, &hessian);
// If there is Hessian data to write out, writeMps assumes that hessian is
// triangular
if (hessian.dim_) assert(hessian.format_ == HessianFormat::kTriangular);

HighsStatus write_status =
writeMps(options.log_options, filename, lp.model_name_, lp.num_row_,
lp.num_col_, hessian.dim_, lp.sense_, lp.offset_, lp.col_cost_,
lp.col_lower_, lp.col_upper_, lp.row_lower_, lp.row_upper_,
lp.a_matrix_.start_, lp.a_matrix_.index_, lp.a_matrix_.value_,
hessian.start_, hessian.index_, hessian.value_, lp.integrality_,
local_col_names, local_row_names, use_free_format);
HighsStatus write_status = writeMps(
options.log_options, filename, lp.model_name_, lp.num_row_, lp.num_col_,
hessian.dim_, lp.sense_, lp.offset_, lp.col_cost_, lp.col_lower_,
lp.col_upper_, lp.row_lower_, lp.row_upper_, lp.a_matrix_.start_,
lp.a_matrix_.index_, lp.a_matrix_.value_, hessian.start_, hessian.index_,
hessian.value_, lp.integrality_, local_objective_name, local_col_names,
local_row_names, use_free_format);
if (write_status == HighsStatus::kOk && warning_found)
return HighsStatus::kWarning;
return write_status;
Expand All @@ -602,7 +610,7 @@ HighsStatus writeMps(
const vector<HighsInt>& a_start, const vector<HighsInt>& a_index,
const vector<double>& a_value, const vector<HighsInt>& q_start,
const vector<HighsInt>& q_index, const vector<double>& q_value,
const vector<HighsVarType>& integrality,
const vector<HighsVarType>& integrality, const std::string objective_name,
const vector<std::string>& col_names, const vector<std::string>& row_names,
const bool use_free_format) {
const bool write_zero_no_cost_columns = true;
Expand All @@ -629,6 +637,7 @@ HighsStatus writeMps(
max_name_length);
return HighsStatus::kError;
}
assert(objective_name != "");
vector<HighsInt> r_ty;
vector<double> rhs, ranges;
bool have_rhs = false;
Expand Down Expand Up @@ -748,7 +757,7 @@ HighsStatus writeMps(

fprintf(file, "NAME %s\n", model_name.c_str());
fprintf(file, "ROWS\n");
fprintf(file, " N COST\n");
fprintf(file, " N %-8s\n", objective_name.c_str());
for (HighsInt r_n = 0; r_n < num_row; r_n++) {
if (r_ty[r_n] == MPS_ROW_TY_E) {
fprintf(file, " E %-8s\n", row_names[r_n].c_str());
Expand All @@ -770,7 +779,8 @@ HighsStatus writeMps(
if (write_zero_no_cost_columns) {
// Give the column a presence by writing out a zero cost
double v = 0;
fprintf(file, " %-8s COST %.15g\n", col_names[c_n].c_str(), v);
fprintf(file, " %-8s %-8s %.15g\n", col_names[c_n].c_str(),
objective_name.c_str(), v);
}
continue;
}
Expand All @@ -793,7 +803,8 @@ HighsStatus writeMps(
}
if (col_cost[c_n] != 0) {
double v = (HighsInt)sense * col_cost[c_n];
fprintf(file, " %-8s COST %.15g\n", col_names[c_n].c_str(), v);
fprintf(file, " %-8s %-8s %.15g\n", col_names[c_n].c_str(),
objective_name.c_str(), v);
}
for (HighsInt el_n = a_start[c_n]; el_n < a_start[c_n + 1]; el_n++) {
double v = a_value[el_n];
Expand All @@ -814,7 +825,7 @@ HighsStatus writeMps(
if (offset) {
// Handle the objective offset as a RHS entry for the cost row
double v = -(HighsInt)sense * offset;
fprintf(file, " RHS_V COST %.15g\n", v);
fprintf(file, " RHS_V %-8s %.15g\n", objective_name.c_str(), v);
}
for (HighsInt r_n = 0; r_n < num_row; r_n++) {
double v = rhs[r_n];
Expand Down
9 changes: 5 additions & 4 deletions src/io/HMPSIO.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,10 @@ FilereaderRetcode readMps(
vector<HighsInt>& Aindex, vector<double>& Avalue, vector<double>& colCost,
vector<double>& colLower, vector<double>& colUpper,
vector<double>& rowLower, vector<double>& rowUpper,
vector<HighsVarType>& integerColumn, vector<std::string>& col_names,
vector<std::string>& row_names, HighsInt& Qdim, vector<HighsInt>& Qstart,
vector<HighsInt>& Qindex, vector<double>& Qvalue,
vector<HighsVarType>& integerColumn, std::string& objective_name,
vector<std::string>& col_names, vector<std::string>& row_names,
HighsInt& Qdim, vector<HighsInt>& Qstart, vector<HighsInt>& Qindex,
vector<double>& Qvalue, HighsInt& cost_row_location,
const HighsInt keep_n_rows = 0);

HighsStatus writeMps(
Expand All @@ -68,7 +69,7 @@ HighsStatus writeMps(
const vector<HighsInt>& a_start, const vector<HighsInt>& a_index,
const vector<double>& a_value, const vector<HighsInt>& q_start,
const vector<HighsInt>& q_index, const vector<double>& q_value,
const vector<HighsVarType>& integrality,
const vector<HighsVarType>& integrality, std::string objective_name,
const vector<std::string>& col_names, const vector<std::string>& row_names,
const bool use_free_format = true);

Expand Down
14 changes: 12 additions & 2 deletions src/io/HMpsFF.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@

#include "io/HMpsFF.h"

#include "lp_data/HighsModelUtils.h"

#ifdef ZLIB_FOUND
#include "zstr.hpp"
#endif
Expand Down Expand Up @@ -98,6 +100,7 @@ FreeFormatParserReturnCode HMpsFF::loadProblem(
lp.row_lower_ = std::move(row_lower);
lp.row_upper_ = std::move(row_upper);

lp.objective_name_ = objective_name;
lp.row_names_ = std::move(row_names);
lp.col_names_ = std::move(col_names);

Expand All @@ -120,6 +123,10 @@ FreeFormatParserReturnCode HMpsFF::loadProblem(
// 0
if (hessian.start_.size() == 0) hessian.clear();

// Set the objective name, creating one if necessary
lp.objective_name_ = findModelObjectiveName(&lp, &hessian);
lp.cost_row_location_ = cost_row_location;

return FreeFormatParserReturnCode::kSuccess;
}

Expand Down Expand Up @@ -228,6 +235,7 @@ FreeFormatParserReturnCode HMpsFF::parse(const HighsLogOptions& log_options,
num_row = 0;
num_col = 0;
num_nz = 0;
cost_row_location = -1;
// Indicate that no duplicate rows or columns have been found
has_duplicate_row_name_ = false;
has_duplicate_col_name_ = false;
Expand Down Expand Up @@ -521,7 +529,8 @@ HMpsFF::Parsekey HMpsFF::parseRows(const HighsLogOptions& log_options,
std::istream& file) {
std::string strline, word;
bool hasobj = false;
objective_name = "";
// Assign a default objective name
objective_name = "Objective";

assert(num_row == 0);
assert(row_lower.size() == 0);
Expand Down Expand Up @@ -565,9 +574,10 @@ HMpsFF::Parsekey HMpsFF::parseRows(const HighsLogOptions& log_options,
row_upper.push_back(0.0);
row_type.push_back(Boundtype::kLe);
} else if (strline[start] == 'N') {
if (objective_name == "") {
if (!hasobj) {
isobj = true;
hasobj = true;
cost_row_location = num_row;
} else {
isFreeRow = true;
}
Expand Down
3 changes: 3 additions & 0 deletions src/io/HMpsFF.h
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,9 @@ class HMpsFF {
// any LI or UI flags in the BOUNDS section
std::vector<bool> col_binary;

// Record where the cost row is encountered
HighsInt cost_row_location;

// Record whether there are duplicate row or column names, and the
// name and indices of the first duplicates
bool has_duplicate_row_name_;
Expand Down
24 changes: 19 additions & 5 deletions src/lp_data/HConst.h
Original file line number Diff line number Diff line change
Expand Up @@ -122,11 +122,20 @@ enum BasisValidity {
};

enum SolutionStyle {
kSolutionStyleOldRaw = -1,
kSolutionStyleRaw = 0,
kSolutionStylePretty, // 1;
kSolutionStyleOldRaw, // 2;
kSolutionStyleMin = kSolutionStyleRaw,
kSolutionStyleMax = kSolutionStyleOldRaw
kSolutionStylePretty, // 1;
kSolutionStyleGlpsolRaw, // 2;
kSolutionStyleGlpsolPretty, // 3;
kSolutionStyleMin = kSolutionStyleOldRaw,
kSolutionStyleMax = kSolutionStyleGlpsolPretty
};

enum GlpsolCostRowLocation {
kGlpsolCostRowLocationLast = -2,
kGlpsolCostRowLocationNone, // -1
kGlpsolCostRowLocationNoneIfEmpty, // 0
kGlpsolCostRowLocationMin = kGlpsolCostRowLocationLast
};

const std::string kHighsFilenameDefault = "";
Expand Down Expand Up @@ -187,8 +196,13 @@ const HighsInt kMaxAllowedMatrixPow2Scale = 30;

// Illegal values of num/max/sum infeasibility - used to indicate that true
// values aren't known
const HighsInt kHighsIllegalInfeasibilityCount = -1;
const double kHighsIllegalInfeasibilityMeasure = kHighsInf;
const HighsInt kHighsIllegalInfeasibilityCount = -1;

// Illegal values for HighsError - used to indicate that true
// values aren't known
const double kHighsIllegalErrorValue = kHighsInf;
const HighsInt kHighsIllegalErrorIndex = -1;

// Maximum upper bound on semi-variables
const double kMaxSemiVariableUpper = 1e5;
Expand Down
33 changes: 20 additions & 13 deletions src/lp_data/Highs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1201,17 +1201,22 @@ HighsStatus Highs::run() {
basis_.col_status = presolve_.data_.recovered_basis_.col_status;
basis_.row_status = presolve_.data_.recovered_basis_.row_status;
basis_.debug_origin_name += ": after postsolve";
// Possibly force debug to perform KKT check on what's
// returned from postsolve
const bool force_debug = false;
HighsInt save_highs_debug_level = options_.highs_debug_level;
if (force_debug)
options_.highs_debug_level = kHighsDebugLevelCostly;
if (debugHighsSolution("After returning from postsolve", options_,
model_, solution_,
basis_) == HighsDebugStatus::kLogicalError)
return returnFromRun(HighsStatus::kError);
options_.highs_debug_level = save_highs_debug_level;
// Basic primal activities are wrong after postsolve, so
// possibly skip KKT check
const bool perform_kkt_check = true;
if (perform_kkt_check) {
// Possibly force debug to perform KKT check on what's
// returned from postsolve
const bool force_debug = false;
HighsInt save_highs_debug_level = options_.highs_debug_level;
if (force_debug)
options_.highs_debug_level = kHighsDebugLevelCostly;
if (debugHighsSolution("After returning from postsolve", options_,
model_, solution_,
basis_) == HighsDebugStatus::kLogicalError)
return returnFromRun(HighsStatus::kError);
options_.highs_debug_level = save_highs_debug_level;
}
// Save the options to allow the best simplex strategy to
// be used
HighsOptions save_options = options_;
Expand Down Expand Up @@ -2390,8 +2395,8 @@ HighsStatus Highs::writeSolution(const std::string filename,
return_status = interpretCallStatus(options_.log_options, call_status,
return_status, "openWriteFile");
if (return_status == HighsStatus::kError) return return_status;
writeSolutionFile(file, model_.lp_, basis_, solution_, info_, model_status_,
style);
writeSolutionFile(file, options_, model_, basis_, solution_, info_,
model_status_, style);
if (style == kSolutionStyleRaw) {
fprintf(file, "\n# Basis\n");
writeBasisFile(file, basis_);
Expand Down Expand Up @@ -2560,6 +2565,8 @@ HighsPostsolveStatus Highs::runPostsolve() {
presolve_.data_.postSolveStack.undo(options_,
presolve_.data_.recovered_solution_,
presolve_.data_.recovered_basis_);
// Compute the row activities
calculateRowValuesQuad(model_.lp_, presolve_.data_.recovered_solution_);

if (have_dual_solution && model_.lp_.sense_ == ObjSense::kMaximize)
presolve_.negateReducedLpColDuals(true);
Expand Down
3 changes: 2 additions & 1 deletion src/lp_data/HighsDeprecated.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,8 @@ HighsStatus Highs::writeSolution(const std::string filename,
} else {
style = kSolutionStyleRaw;
}
writeSolutionFile(file, model_.lp_, basis_, solution_, info_, model_status_,
writeSolutionFile(file, options_,
model_, basis_, solution_, info_, model_status_,
style);
if (file != stdout) fclose(file);
return HighsStatus::kOk;
Expand Down
Loading

0 comments on commit e1dc76d

Please sign in to comment.