#include "Avgas.h"
#include "HCheckConfig.h"
#include "Highs.h"
#include "catch.hpp"
#include "lp_data/HighsLpUtils.h"
const bool dev_run = false;
const double inf = kHighsInf;
TEST_CASE("LP-dimension-validation", "[highs_data]") {
const HighsInt true_num_col = 2;
const HighsInt true_num_row = 3;
HighsLp lp;
lp.num_col_ = -1;
lp.num_row_ = -1;
lp.col_cost_.resize(1);
lp.col_lower_.resize(1);
lp.col_upper_.resize(1);
lp.a_matrix_.format_ = MatrixFormat::kRowwisePartitioned;
lp.a_matrix_.num_col_ = 1;
lp.a_matrix_.num_row_ = 1;
lp.a_matrix_.start_.resize(2);
lp.a_matrix_.start_[0] = -1;
lp.a_matrix_.start_[1] = 2;
lp.a_matrix_.index_.resize(2);
lp.a_matrix_.index_[0] = -1;
lp.a_matrix_.index_[1] = 0;
lp.a_matrix_.value_.resize(2);
lp.row_lower_.resize(1);
lp.row_upper_.resize(1);
Highs highs;
if (!dev_run) highs.setOptionValue("output_flag", false);
REQUIRE(highs.passModel(lp) == HighsStatus::kError);
if (dev_run) printf("Give valid number of columns\n");
lp.num_col_ = true_num_col;
REQUIRE(highs.passModel(lp) == HighsStatus::kError);
if (dev_run) printf("Give valid number of rows\n");
lp.num_row_ = true_num_row;
REQUIRE(highs.passModel(lp) == HighsStatus::kError);
if (dev_run) printf("Give valid col_cost.size()\n");
lp.col_cost_.resize(true_num_col);
REQUIRE(highs.passModel(lp) == HighsStatus::kError);
if (dev_run) printf("Give valid col_lower.size()\n");
lp.col_lower_.resize(true_num_col);
REQUIRE(highs.passModel(lp) == HighsStatus::kError);
if (dev_run) printf("Give valid col_upper.size()\n");
lp.col_upper_.resize(true_num_col);
REQUIRE(highs.passModel(lp) == HighsStatus::kError);
if (dev_run) printf("Give valid a_matrix_.format_\n");
lp.a_matrix_.format_ = MatrixFormat::kRowwise;
REQUIRE(highs.passModel(lp) == HighsStatus::kError);
if (dev_run) printf("Give valid a_matrix_.start.size()\n");
lp.a_matrix_.start_.resize(true_num_row + 1);
REQUIRE(highs.passModel(lp) == HighsStatus::kError);
if (dev_run) printf("Give valid row_lower.size()\n");
lp.row_lower_.resize(true_num_row);
REQUIRE(highs.passModel(lp) == HighsStatus::kError);
if (dev_run) printf("Give valid row_upper.size()\n");
lp.row_upper_.resize(true_num_row);
REQUIRE(highs.passModel(lp) == HighsStatus::kError);
if (dev_run) printf("Give valid a_matrix_.start_[0]\n");
lp.a_matrix_.start_[0] = 0;
REQUIRE(highs.passModel(lp) == HighsStatus::kError);
if (dev_run)
printf("Give valid a_matrix_.start_[2] and a_matrix_.start_[3]\n");
lp.a_matrix_.start_[2] = 2;
lp.a_matrix_.start_[3] = 2;
REQUIRE(highs.passModel(lp) == HighsStatus::kError);
if (dev_run) printf("Give valid a_matrix_.index_[0]\n");
lp.a_matrix_.index_[0] = 0;
REQUIRE(highs.passModel(lp) == HighsStatus::kOk);
if (dev_run)
printf("Give nonzero a_matrix_.value_[0] and a_matrix_.value_[1]\n");
lp.a_matrix_.value_[0] = 1;
lp.a_matrix_.value_[1] = 1;
REQUIRE(highs.passModel(lp) == HighsStatus::kError);
if (dev_run) printf("Give valid a_matrix_.index_[1]\n");
lp.a_matrix_.index_[1] = 1;
REQUIRE(highs.passModel(lp) == HighsStatus::kOk);
}
TEST_CASE("LP-validation", "[highs_data]") {
HighsLp lp;
HighsOptions options;
HighsStatus return_status;
options.log_dev_level = kHighsLogDevLevelVerbose;
if (!dev_run) options.output_flag = false;
Avgas avgas;
const HighsInt avgas_num_col = 8;
const HighsInt avgas_num_row = 10;
HighsInt num_row = 0;
HighsInt num_row_nz = 0;
vector<double> rowLower;
vector<double> rowUpper;
vector<HighsInt> ARstart;
vector<HighsInt> ARindex;
vector<double> ARvalue;
for (HighsInt row = 0; row < avgas_num_row; row++) {
avgas.addRow(row, num_row, num_row_nz, rowLower, rowUpper, ARstart, ARindex,
ARvalue);
}
HighsInt num_col = 0;
HighsInt num_col_nz = 0;
vector<double> colCost;
vector<double> colLower;
vector<double> colUpper;
vector<HighsInt> Astart;
vector<HighsInt> Aindex;
vector<double> Avalue;
for (HighsInt col = 0; col < avgas_num_col; col++) {
avgas.addCol(col, num_col, num_col_nz, colCost, colLower, colUpper, Astart,
Aindex, Avalue);
}
return_status = assessLp(lp, options);
REQUIRE(return_status == HighsStatus::kOk);
const double my_infinity = 1e30;
Highs highs;
highs.passOptions(options);
REQUIRE(highs.passModel(lp) == HighsStatus::kOk);
return_status = highs.addRows(num_row, rowLower.data(), rowUpper.data(), 0,
NULL, NULL, NULL);
REQUIRE(return_status == HighsStatus::kOk);
return_status =
highs.addCols(num_col, colCost.data(), colLower.data(), colUpper.data(),
num_col_nz, Astart.data(), Aindex.data(), Avalue.data());
REQUIRE(return_status == HighsStatus::kOk);
REQUIRE(!highs.getLp().has_infinite_cost_);
HighsInt XnumNewCol = 1;
HighsInt XnumNewNZ = 0;
vector<double> XcolCost;
XcolCost.resize(XnumNewCol);
XcolCost[0] = 1;
vector<double> XcolLower;
XcolLower.resize(XnumNewCol);
XcolLower[0] = 0;
vector<double> XcolUpper;
XcolUpper.resize(XnumNewCol);
XcolUpper[0] = 1e25;
vector<HighsInt> XAstart;
XAstart.resize(XnumNewCol);
vector<HighsInt> XAindex;
vector<double> XAvalue;
return_status =
highs.addCols(XnumNewCol, XcolCost.data(), XcolLower.data(),
XcolUpper.data(), XnumNewNZ, XAstart.data(), NULL, NULL);
REQUIRE(return_status == HighsStatus::kOk);
REQUIRE(!highs.getLp().has_infinite_cost_);
XcolUpper[0] = my_infinity;
XcolCost[0] = my_infinity;
return_status =
highs.addCols(XnumNewCol, XcolCost.data(), XcolLower.data(),
XcolUpper.data(), XnumNewNZ, XAstart.data(), NULL, NULL);
REQUIRE(return_status == HighsStatus::kOk);
REQUIRE(highs.getLp().has_infinite_cost_);
XcolCost[0] = -my_infinity;
return_status =
highs.addCols(XnumNewCol, XcolCost.data(), XcolLower.data(),
XcolUpper.data(), XnumNewNZ, XAstart.data(), NULL, NULL);
REQUIRE(return_status == HighsStatus::kOk);
REQUIRE(highs.getLp().has_infinite_cost_);
XcolCost[0] = 1;
XcolLower[0] = 0;
XcolUpper[0] = -1;
return_status =
highs.addCols(XnumNewCol, XcolCost.data(), XcolLower.data(),
XcolUpper.data(), XnumNewNZ, XAstart.data(), NULL, NULL);
REQUIRE(return_status == HighsStatus::kWarning);
XcolLower[0] = 1;
XcolUpper[0] = 0;
return_status =
highs.addCols(XnumNewCol, XcolCost.data(), XcolLower.data(),
XcolUpper.data(), XnumNewNZ, XAstart.data(), NULL, NULL);
REQUIRE(return_status == HighsStatus::kWarning);
XcolLower[0] = my_infinity;
XcolUpper[0] = 0;
return_status =
highs.addCols(XnumNewCol, XcolCost.data(), XcolLower.data(),
XcolUpper.data(), XnumNewNZ, XAstart.data(), NULL, NULL);
REQUIRE(return_status == HighsStatus::kError);
XcolLower[0] = 0;
XcolUpper[0] = -my_infinity;
return_status =
highs.addCols(XnumNewCol, XcolCost.data(), XcolLower.data(),
XcolUpper.data(), XnumNewNZ, XAstart.data(), NULL, NULL);
REQUIRE(return_status == HighsStatus::kError);
XcolLower[0] = 0;
XcolUpper[0] = 0;
return_status =
highs.addCols(XnumNewCol, XcolCost.data(), XcolLower.data(),
XcolUpper.data(), XnumNewNZ, XAstart.data(), NULL, NULL);
REQUIRE(return_status == HighsStatus::kOk);
XnumNewCol = 2;
XnumNewNZ = 7;
XcolCost.resize(XnumNewCol);
XcolCost[0] = 1;
XcolCost[1] = 2;
XcolLower.resize(XnumNewCol);
XcolLower[0] = 0;
XcolLower[1] = 0;
XcolUpper.resize(XnumNewCol);
XcolUpper[0] = 1;
XcolUpper[1] = 1;
XAstart.resize(XnumNewCol + 1);
XAindex.resize(XnumNewNZ);
XAstart[1] = 4;
XAstart[2] = XnumNewNZ;
XAindex[0] = 0;
XAindex[1] = 2;
XAindex[2] = 3;
XAindex[3] = 9;
XAindex[4] = 1;
XAindex[5] = 3;
XAindex[6] = 8;
XAvalue.resize(XnumNewNZ);
XAvalue[0] = 1;
XAvalue[1] = 1e-12;
XAvalue[2] = -1e-20;
XAvalue[3] = -1;
XAvalue[4] = -1e60;
XAvalue[5] = 1e100;
XAvalue[6] = -1;
return_status = highs.addCols(XnumNewCol, XcolCost.data(), XcolLower.data(),
XcolUpper.data(), XnumNewNZ, XAstart.data(),
XAindex.data(), XAvalue.data());
REQUIRE(return_status == HighsStatus::kError);
XAvalue[4] = -1;
XAvalue[5] = 1;
return_status = highs.addCols(XnumNewCol, XcolCost.data(), XcolLower.data(),
XcolUpper.data(), XnumNewNZ, XAstart.data(),
XAindex.data(), XAvalue.data());
REQUIRE(return_status == HighsStatus::kWarning);
if (!dev_run) highs.setOptionValue("output_flag", false);
const HighsLp& internal_lp = highs.getLp();
double check_value;
REQUIRE(highs.getCoeff(-1, 0, check_value) == HighsStatus::kError);
REQUIRE(highs.getCoeff(0, -1, check_value) == HighsStatus::kError);
REQUIRE(highs.getCoeff(internal_lp.num_row_, 0, check_value) ==
HighsStatus::kError);
REQUIRE(highs.getCoeff(0, internal_lp.num_col_, check_value) ==
HighsStatus::kError);
const HighsInt check_col = 4;
const HighsInt check_row = 7;
REQUIRE(highs.getCoeff(check_col, check_row, check_value) ==
HighsStatus::kOk);
REQUIRE(check_value == 0);
const double value = -3;
REQUIRE(highs.getCoeff(check_row, check_col, check_value) ==
HighsStatus::kOk);
REQUIRE(check_value == value);
HighsStatus run_status;
HighsModelStatus model_status;
run_status = highs.run();
REQUIRE(run_status == HighsStatus::kError);
model_status = highs.getModelStatus();
REQUIRE(model_status == HighsModelStatus::kUnknown);
REQUIRE(highs.changeCoeff(-1, 0, check_value) == HighsStatus::kError);
REQUIRE(highs.changeCoeff(0, -1, check_value) == HighsStatus::kError);
REQUIRE(highs.changeCoeff(internal_lp.num_row_, 0, check_value) ==
HighsStatus::kError);
REQUIRE(highs.changeCoeff(0, internal_lp.num_col_, check_value) ==
HighsStatus::kError);
const double to_value = 99;
REQUIRE(highs.changeCoeff(check_row, check_col, to_value) ==
HighsStatus::kOk);
REQUIRE(highs.getCoeff(check_row, check_col, check_value) ==
HighsStatus::kOk);
REQUIRE(check_value == to_value);
REQUIRE(highs.ensureRowwise() == HighsStatus::kOk);
REQUIRE(highs.getCoeff(check_row, check_col, check_value) ==
HighsStatus::kOk);
REQUIRE(check_value == to_value);
highs.resetGlobalScheduler(true);
}
TEST_CASE("LP-row-index-duplication", "[highs_data]") {
Highs highs;
highs.setOptionValue("output_flag", dev_run);
HighsInt num_col = 10;
for (HighsInt iCol = 0; iCol < num_col; iCol++) highs.addVar(0, 1);
std::vector<HighsInt> start = {0, 6, 8};
std::vector<HighsInt> index = {0, 1, 4, 5, 6, 7, 0, 1, 0, 1, 4, 5, 6, 7, 4};
std::vector<double> value = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1};
std::vector<double> lower = {0, 0, 0};
std::vector<double> upper = {inf, inf, inf};
HighsInt num_nz = index.size();
REQUIRE(highs.addRows(3, lower.data(), upper.data(), num_nz, start.data(),
index.data(), value.data()) == HighsStatus::kError);
}
TEST_CASE("LP-extreme-coefficient", "[highs_data]") {
HighsStatus return_status;
std::string filename;
Highs highs;
HighsLp lp;
lp.num_col_ = 1;
lp.num_row_ = 1;
lp.col_cost_ = {1};
lp.col_lower_ = {0};
lp.col_upper_ = {1};
lp.a_matrix_.format_ = MatrixFormat::kColwise;
lp.a_matrix_.num_col_ = 1;
lp.a_matrix_.num_row_ = 1;
lp.a_matrix_.start_ = {0, 1};
lp.a_matrix_.index_ = {0};
lp.a_matrix_.value_ = {1e300};
lp.row_lower_ = {1};
lp.row_upper_ = {inf};
if (!dev_run) highs.setOptionValue("output_flag", false);
return_status = highs.passModel(lp);
if (dev_run)
printf("highs.passModel(filename); returns %d\n", (int)return_status);
REQUIRE(return_status == HighsStatus::kError);
return_status = highs.run();
if (dev_run) printf("highs.run(); returns %d\n", (int)return_status);
REQUIRE(return_status == HighsStatus::kError);
lp.a_matrix_.value_[0] = 1e-300;
return_status = highs.passModel(lp);
if (dev_run)
printf("highs.passModel(filename); returns %d\n", (int)return_status);
REQUIRE(return_status == HighsStatus::kWarning);
return_status = highs.run();
if (dev_run) printf("highs.run(); returns %d\n", (int)return_status);
REQUIRE(return_status == HighsStatus::kOk);
REQUIRE(highs.getModelStatus() == HighsModelStatus::kInfeasible);
highs.resetGlobalScheduler(true);
}
TEST_CASE("LP-inf-cost", "[highs_data]") {
Highs highs;
highs.setOptionValue("output_flag", dev_run);
HighsLp lp;
const double my_infinite_bound = 1e30;
const double my_infinite_cost = kHighsInf;
const double my_large_cost = 1e20;
highs.setOptionValue("infinite_cost", my_infinite_cost);
lp.num_col_ = 3;
lp.num_row_ = 2;
lp.col_cost_ = {-3, -2, -my_infinite_cost};
lp.col_lower_ = {0.0, 0.0, 0.0};
lp.col_upper_ = {my_infinite_bound, my_infinite_bound, 1};
lp.row_lower_ = {-my_infinite_bound, 12};
lp.row_upper_ = {7.0, 12.0};
lp.a_matrix_.start_ = {0, 2, 4, 6};
lp.a_matrix_.index_ = {0, 1, 0, 1, 0, 1};
lp.a_matrix_.value_ = {1.0, 4.0, 1.0, 2.0, 1, 1.0};
lp.integrality_.resize(lp.num_col_);
lp.integrality_[0] = HighsVarType::kContinuous;
lp.integrality_[0] = HighsVarType::kContinuous;
lp.integrality_[0] = HighsVarType::kInteger;
HighsStatus status = highs.passModel(lp);
REQUIRE(status == HighsStatus::kOk);
REQUIRE(highs.getLp().has_infinite_cost_);
status = highs.run();
REQUIRE(status == HighsStatus::kOk);
REQUIRE(highs.getModelStatus() == HighsModelStatus::kOptimal);
status = highs.changeColCost(2, -my_large_cost);
REQUIRE(status == HighsStatus::kOk);
REQUIRE(!highs.getLp().has_infinite_cost_);
status = highs.run();
REQUIRE(status == HighsStatus::kOk);
REQUIRE(highs.getModelStatus() == HighsModelStatus::kOptimal);
status = highs.changeColCost(2, -my_infinite_cost);
status = highs.changeColIntegrality(2, HighsVarType::kContinuous);
REQUIRE(status == HighsStatus::kOk);
REQUIRE(highs.getLp().has_infinite_cost_);
status = highs.run();
REQUIRE(status == HighsStatus::kOk);
REQUIRE(highs.getModelStatus() == HighsModelStatus::kOptimal);
highs.clearModel();
lp.num_col_ = 1;
lp.num_row_ = 1;
lp.col_cost_ = {-my_infinite_cost};
lp.col_lower_ = {1};
lp.col_upper_ = {3};
lp.row_lower_ = {-my_infinite_bound};
lp.row_upper_ = {2};
lp.a_matrix_.start_ = {0, 1};
lp.a_matrix_.index_ = {0};
lp.a_matrix_.value_ = {1};
lp.integrality_.clear();
status = highs.passModel(lp);
REQUIRE(status == HighsStatus::kOk);
REQUIRE(highs.getLp().has_infinite_cost_);
status = highs.run();
REQUIRE(status == HighsStatus::kWarning);
REQUIRE(highs.getModelStatus() == HighsModelStatus::kUnknown);
highs.changeObjectiveSense(ObjSense::kMaximize);
status = highs.run();
REQUIRE(status == HighsStatus::kOk);
REQUIRE(highs.getModelStatus() == HighsModelStatus::kOptimal);
REQUIRE(highs.getInfo().objective_function_value == -my_infinite_cost);
highs.clearModel();
lp.num_col_ = 1;
lp.num_row_ = 1;
lp.col_cost_ = {-my_infinite_cost};
lp.col_lower_ = {0.5};
lp.col_upper_ = {3.5};
lp.row_lower_ = {-my_infinite_bound};
lp.row_upper_ = {2};
lp.a_matrix_.start_ = {0, 1};
lp.a_matrix_.index_ = {0};
lp.a_matrix_.value_ = {1};
lp.integrality_ = {HighsVarType::kInteger};
status = highs.passModel(lp);
REQUIRE(status == HighsStatus::kOk);
REQUIRE(highs.getLp().has_infinite_cost_);
status = highs.run();
REQUIRE(status == HighsStatus::kWarning);
REQUIRE(highs.getModelStatus() == HighsModelStatus::kUnknown);
highs.changeObjectiveSense(ObjSense::kMaximize);
status = highs.run();
REQUIRE(status == HighsStatus::kOk);
REQUIRE(highs.getModelStatus() == HighsModelStatus::kOptimal);
REQUIRE(highs.getInfo().objective_function_value == -my_infinite_cost);
REQUIRE(highs.getSolution().col_value[0] == 1);
highs.resetGlobalScheduler(true);
}
TEST_CASE("LP-change-coefficient", "[highs_data]") {
std::string filename;
filename = std::string(HIGHS_DIR) + "/check/instances/avgas.mps";
Highs highs;
if (!dev_run) highs.setOptionValue("output_flag", false);
highs.readModel(filename);
const HighsInt change_coefficient_col = 4;
const HighsInt zero_coefficient_row = 4;
const HighsInt add_coefficient_row = 5;
const double zero_coefficient_value = 1e-10;
const double add_coefficient_value = -3;
double required_objective_value;
double delta_objective_value;
double original_value;
REQUIRE(highs.getCoeff(zero_coefficient_row, change_coefficient_col,
original_value) == HighsStatus::kOk);
REQUIRE(original_value == -1);
REQUIRE(highs.changeCoeff(zero_coefficient_row, change_coefficient_col,
zero_coefficient_value) == HighsStatus::kOk);
required_objective_value = -8.6666666667;
highs.run();
delta_objective_value = std::fabs(required_objective_value -
highs.getInfo().objective_function_value);
REQUIRE(delta_objective_value < 1e-8);
REQUIRE(highs.changeCoeff(zero_coefficient_row, change_coefficient_col,
original_value) == HighsStatus::kOk);
required_objective_value = -7.75;
highs.run();
delta_objective_value = std::fabs(required_objective_value -
highs.getInfo().objective_function_value);
REQUIRE(delta_objective_value < 1e-8);
REQUIRE(highs.changeCoeff(add_coefficient_row, change_coefficient_col,
add_coefficient_value) == HighsStatus::kOk);
required_objective_value = -7.5;
highs.run();
delta_objective_value = std::fabs(required_objective_value -
highs.getInfo().objective_function_value);
REQUIRE(delta_objective_value < 1e-8);
highs.resetGlobalScheduler(true);
}
TEST_CASE("LP-illegal-empty-start-ok", "[highs_data]") {
Highs highs;
highs.setOptionValue("output_flag", dev_run);
HighsLp lp;
lp.num_col_ = 0;
lp.num_row_ = 1;
lp.row_lower_ = {-inf};
lp.row_upper_ = {1};
lp.a_matrix_.start_ = {1};
REQUIRE(highs.passModel(lp) == HighsStatus::kOk);
REQUIRE(highs.getLp().a_matrix_.start_[0] == 0);
}
TEST_CASE("LP-row-wise", "[highs_data]") {
Highs highs;
highs.setOptionValue("output_flag", dev_run);
HighsLp lp;
lp.sense_ = ObjSense::kMaximize;
lp.num_col_ = 2;
lp.num_row_ = 2;
lp.col_cost_ = {10, 25};
lp.col_lower_ = {0, 0};
lp.col_upper_ = {inf, inf};
lp.a_matrix_.format_ = MatrixFormat::kRowwise;
lp.a_matrix_.start_ = {0, 2, 4};
lp.a_matrix_.index_ = {0, 1, 0, 1};
lp.a_matrix_.value_ = {1, 2, 1, 4};
lp.row_lower_ = {-inf, -inf};
lp.row_upper_ = {80, 120};
highs.passModel(lp);
highs.run();
highs.resetGlobalScheduler(true);
}
TEST_CASE("LP-infeasible-bounds", "[highs_data]") {
Highs highs;
double epsilon = 1e-10;
highs.setOptionValue("output_flag", dev_run);
HighsLp lp;
lp.sense_ = ObjSense::kMaximize;
lp.num_col_ = 2;
lp.num_row_ = 2;
lp.col_cost_ = {10, 25};
lp.col_lower_ = {1, 2.5 + epsilon};
lp.col_upper_ = {1 - epsilon, 2.5 - 2 * epsilon};
lp.a_matrix_.format_ = MatrixFormat::kRowwise;
lp.a_matrix_.start_ = {0, 2, 4};
lp.a_matrix_.index_ = {0, 1, 0, 1};
lp.a_matrix_.value_ = {1, 2, 1, 4};
lp.row_lower_ = {6, -inf};
lp.row_upper_ = {6 - epsilon, 11 - epsilon};
highs.passModel(lp);
highs.run();
REQUIRE(highs.getModelStatus() == HighsModelStatus::kOptimal);
if (dev_run) highs.writeSolution("", 1);
highs.changeColBounds(0, 0, -1);
highs.run();
REQUIRE(highs.getModelStatus() == HighsModelStatus::kInfeasible);
highs.resetGlobalScheduler(true);
}