#include <sstream>
#include "HCheckConfig.h"
#include "Highs.h"
#include "catch.hpp"
#include "util/HFactor.h"
#include "util/HighsRandom.h"
const double inf = kHighsInf;
const bool dev_run = false;
const double double_equal_tolerance = 1e-5;
void testAlienBasis(const bool avgas, const HighsInt seed);
void getDependentCols(const HighsSparseMatrix& matrix,
std::vector<HighsInt>& col_set,
std::vector<HighsInt>& dependent_col_set,
const HighsInt required_rank_deficiency);
void reportColSet(const std::string message,
const std::vector<HighsInt>& col_set);
void reportDependentCols(const std::vector<HighsInt>& dependent_col_set);
TEST_CASE("Hessian-rank-detection", "[highs_test_alien_basis]") {
HighsSparseMatrix matrix;
matrix.num_col_ = 7;
matrix.num_row_ = 7;
matrix.start_ = {0, 2, 2, 4, 6, 8, 8, 11};
matrix.index_ = {0, 3, 2, 6, 0, 3, 4, 6, 2, 4, 6};
matrix.value_ = {1, 2, 1, 1, 2, 4, 1, 2, 1, 2, 8};
std::vector<HighsInt> col_set = {0, 1, 2, 3, 4, 5, 6};
std::vector<HighsInt> dependent_col_set;
const HighsInt required_rank_deficiency = 3;
if (dev_run) reportColSet("\nOriginal", col_set);
getDependentCols(matrix, col_set, dependent_col_set,
required_rank_deficiency);
if (dev_run) reportColSet("Returned", col_set);
if (dev_run) reportDependentCols(dependent_col_set);
}
TEST_CASE("AlienBasis-rank-detection", "[highs_test_alien_basis]") {
HighsSparseMatrix matrix;
matrix.num_col_ = 5;
matrix.num_row_ = 3;
matrix.start_ = {0, 2, 4, 6, 8, 11};
matrix.index_ = {0, 1, 0, 1, 0, 2, 0, 2, 0, 1, 2};
matrix.value_ = {1, 1, 2, 2, 1, 1, 2, 2, 6, 3, 3};
std::vector<HighsInt> col_set;
std::vector<HighsInt> dependent_col_set;
HighsInt required_rank_deficiency;
col_set = {0, 1, 2, 3, 4};
required_rank_deficiency = 3;
if (dev_run) reportColSet("\nOriginal", col_set);
getDependentCols(matrix, col_set, dependent_col_set,
required_rank_deficiency);
if (dev_run) reportColSet("Returned", col_set);
if (dev_run) reportDependentCols(dependent_col_set);
col_set = {2, 0, 1, 3};
required_rank_deficiency = 2;
if (dev_run) reportColSet("\nOriginal", col_set);
getDependentCols(matrix, col_set, dependent_col_set,
required_rank_deficiency);
if (dev_run) reportColSet("Returned", col_set);
if (dev_run) reportDependentCols(dependent_col_set);
}
TEST_CASE("AlienBasis-rectangular-completion", "[highs_test_alien_basis]") {
HighsSparseMatrix matrix;
matrix.num_col_ = 5;
matrix.num_row_ = 6;
matrix.start_ = {0, 4, 6, 12, 17, 20};
matrix.index_ = {0, 2, 4, 5, 0, 3, 0, 1, 2, 3, 4, 5, 0, 1, 2, 4, 5, 1, 4, 5};
matrix.value_ = {1, 1, 2, 1, 2, 1, 1, 1, 1, 1,
1, 1, -1, 2, -1, 2, 1, 1, 2, 1};
const HighsInt num_row = matrix.num_row_;
const HighsInt num_col = matrix.num_col_;
const HighsInt num_basic_col = num_col - 1;
HighsInt rank_deficiency;
HighsInt required_rank_deficiency;
required_rank_deficiency = 1;
std::vector<HighsInt> col_set = {0, 1, 3, 4};
HFactor factor;
factor.setup(matrix, col_set);
rank_deficiency = factor.build();
REQUIRE(rank_deficiency == required_rank_deficiency);
if (dev_run) reportColSet("Returned", col_set);
if (dev_run)
printf("Returned rank_deficiency = %d:\n No pivot in\nk Row Col Var\n",
(int)rank_deficiency);
if (dev_run) {
for (HighsInt k = 0; k < rank_deficiency; k++)
printf("%1d %3d %3d %3d\n", (int)k, (int)factor.row_with_no_pivot[k],
(int)factor.col_with_no_pivot[k],
(int)factor.var_with_no_pivot[k]);
}
for (HighsInt k = rank_deficiency; k < num_row - num_basic_col + 1; k++) {
if (dev_run) printf("%1d %3d\n", (int)k, (int)factor.row_with_no_pivot[k]);
const HighsInt introduce_logical = factor.row_with_no_pivot[k];
const HighsInt introduce_column = num_col + introduce_logical;
col_set.push_back(introduce_column);
}
factor.setup(matrix, col_set);
required_rank_deficiency = 0;
rank_deficiency = factor.build();
REQUIRE(rank_deficiency == required_rank_deficiency);
HighsRandom random;
vector<double> solution;
HVector rhs;
rhs.setup(num_row);
rhs.clear();
for (HighsInt iRow = 0; iRow < num_row; iRow++) {
HighsInt iVar = col_set[iRow];
double solution_value = random.fraction();
solution.push_back(solution_value);
if (iVar < num_col) {
for (HighsInt iEl = matrix.start_[iVar]; iEl < matrix.start_[iVar + 1];
iEl++)
rhs.array[matrix.index_[iEl]] += solution_value * matrix.value_[iEl];
} else {
rhs.array[iVar - num_col] += solution_value;
}
}
std::iota(rhs.index.begin(), rhs.index.end(), 0);
rhs.count++;
factor.ftranCall(rhs, 1);
double solution_error = 0;
for (HighsInt iRow = 0; iRow < num_row; iRow++)
solution_error += std::abs(rhs.array[iRow] - solution[iRow]);
if (dev_run)
printf("AlienBasis-rectangular-completion: solution_error = %g\n",
solution_error);
fflush(stdout);
REQUIRE(solution_error < 1e-8);
}
TEST_CASE("AlienBasis-delay-singularity0", "[highs_test_alien_basis]") {
HighsSparseMatrix matrix;
matrix.num_col_ = 6;
matrix.num_row_ = 5;
matrix.start_ = {0, 2, 4, 9, 12, 15, 18};
matrix.index_ = {0, 1, 0, 1, 0, 1, 2, 3, 4, 2, 3, 4, 2, 3, 4, 2, 3, 4};
matrix.value_ = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 4, 1, 2, 3, 1, -1, -1};
const HighsInt perturbed_entry = 3;
const double perturbation = -1e-12;
matrix.value_[perturbed_entry] += perturbation;
HighsInt rank_deficiency;
HighsInt required_rank_deficiency;
required_rank_deficiency = 2;
std::vector<HighsInt> col_set = {0, 1, 2, 3, 4, 5};
if (dev_run) reportColSet("\nOriginal", col_set);
HFactor factor;
factor.setup(matrix, col_set);
rank_deficiency = factor.build();
REQUIRE(rank_deficiency == required_rank_deficiency);
}
TEST_CASE("AlienBasis-delay-singularity1", "[highs_test_alien_basis]") {
HighsSparseMatrix matrix;
matrix.num_col_ = 5;
matrix.num_row_ = 5;
matrix.start_ = {0, 5, 10, 14, 17, 20};
matrix.index_ = {0, 1, 2, 3, 4, 0, 1, 2, 3, 4, 1, 2, 3, 4, 2, 3, 4, 2, 3, 4};
matrix.value_ = {1, 1, 2, -1, 3, 1, 1, 2, -1, 3,
1, 1, 2, 4, 1, 1, 1, 1, 2, 3};
const HighsInt perturbed_from_entry = 6;
const HighsInt perturbed_to_entry = 10;
const double perturbation_multiplier = 1 + 1e-12;
for (HighsInt iEl = perturbed_from_entry; iEl < perturbed_to_entry; iEl++)
matrix.value_[iEl] *= perturbation_multiplier;
HighsInt rank_deficiency;
HighsInt required_rank_deficiency;
required_rank_deficiency = 1;
std::vector<HighsInt> col_set = {0, 1, 2, 3, 4};
if (dev_run) reportColSet("\nOriginal", col_set);
HFactor factor;
factor.setup(matrix, col_set);
rank_deficiency = factor.build();
REQUIRE(rank_deficiency == required_rank_deficiency);
}
TEST_CASE("AlienBasis-qap10", "[highs_test_alien_basis]") {
HighsSparseMatrix matrix;
matrix.num_col_ = 15;
matrix.num_row_ = 3;
matrix.start_ = {0, 3, 5, 6, 8, 9, 10, 12, 14, 17, 20, 23, 26, 29, 31, 32};
matrix.index_ = {2, 0, 1, 2, 0, 0, 2, 0, 0, 0, 0, 2, 0, 2, 2, 0,
1, 1, 0, 2, 0, 1, 2, 2, 1, 0, 2, 1, 0, 2, 0, 1};
matrix.value_ = {1, 0.5, 0.5, 0.75, 1, 1, 0.5, 1, 1, 1, 2,
0.5, 1, -1, 1, 1, 1, 1, 1, -1, 0.5, 0.5,
1.125, 1.5, 1, 1, -0.5, 2, 2, -0.5, 1, 1};
std::vector<HighsInt> col_set = {0, 1, 2, 3, 6, 7, 8, 9, 10, 11, 12, 13, 15};
const HighsInt required_rank_deficiency = 10;
if (dev_run) reportColSet("\nOriginal", col_set);
HFactor factor;
factor.setup(matrix, col_set);
HighsInt rank_deficiency = factor.build();
REQUIRE(rank_deficiency == required_rank_deficiency);
}
TEST_CASE("AlienBasis-LP", "[highs_test_alien_basis]") {
const HighsInt num_seed = 10;
bool avgas = true;
for (HighsInt seed = 0; seed < num_seed; seed++) testAlienBasis(avgas, seed);
avgas = false;
for (HighsInt seed = 0; seed < num_seed; seed++) testAlienBasis(avgas, seed);
}
void getDependentCols(const HighsSparseMatrix& matrix,
std::vector<HighsInt>& col_set,
std::vector<HighsInt>& dependent_col_set,
const HighsInt required_rank_deficiency) {
HFactor factor;
factor.setup(matrix, col_set);
HighsInt rank_deficiency = factor.build();
REQUIRE(rank_deficiency == required_rank_deficiency);
if (dev_run)
printf("Returned rank_deficiency = %d:\n No pivot in\nk Row Col Var\n",
(int)rank_deficiency);
dependent_col_set.clear();
for (HighsInt k = 0; k < rank_deficiency; k++) {
if (dev_run)
printf("%1d %3d %3d %3d\n", (int)k, (int)factor.row_with_no_pivot[k],
(int)factor.col_with_no_pivot[k],
(int)factor.var_with_no_pivot[k]);
dependent_col_set.push_back(factor.var_with_no_pivot[k]);
}
}
void reportDependentCols(const std::vector<HighsInt>& dependent_col_set) {
printf("Dependent column(s) in col_set:");
for (HighsInt k = 0; k < (HighsInt)dependent_col_set.size(); k++)
printf(" %1d", (int)dependent_col_set[k]);
printf("\n");
}
void reportColSet(const std::string message,
const std::vector<HighsInt>& col_set) {
printf("%s col_set:\n", message.c_str());
for (HighsInt k = 0; k < (HighsInt)col_set.size(); k++)
printf(" %1d", (int)col_set[k]);
printf("\n");
}
void testAlienBasis(const bool avgas, const HighsInt seed) {
std::string filename;
std::string model;
if (avgas) {
model = "avgas";
} else {
model = "israel";
}
filename = std::string(HIGHS_DIR) + "/check/instances/" + model + ".mps";
std::stringstream ss;
Highs highs;
if (!dev_run) highs.setOptionValue("output_flag", false);
HighsStatus read_status = highs.readModel(filename);
assert(read_status == HighsStatus::kOk);
HighsLp lp = highs.getLp();
HighsInt num_col = lp.num_col_;
HighsInt num_row = lp.num_row_;
assert(num_col < num_row);
const HighsInt num_var = num_col + num_row;
HighsBasis basis;
basis.col_status.resize(num_col);
basis.row_status.resize(num_row);
const bool run_square_test = true;
if (run_square_test && !seed) {
ss.str(std::string());
ss << "AlienBasis: " << model << " square";
basis.debug_origin_name = ss.str();
HighsBasisStatus status = HighsBasisStatus::kBasic;
for (HighsInt iCol = 0; iCol < num_col; iCol++) {
if (iCol >= num_row) status = HighsBasisStatus::kNonbasic;
basis.col_status[iCol] = status;
}
for (HighsInt iRow = 0; iRow < num_row; iRow++) {
if (num_col + iRow >= num_row) status = HighsBasisStatus::kNonbasic;
basis.row_status[iRow] = status;
}
REQUIRE(highs.setBasis(basis) == HighsStatus::kOk);
highs.run();
}
const bool run_square_random_test = true;
if (run_square_random_test) {
ss.str(std::string());
ss << "AlienBasis: " << model << " random-" << seed << " square";
basis.debug_origin_name = ss.str();
basis.col_status.assign(num_col, HighsBasisStatus::kNonbasic);
basis.row_status.assign(num_row, HighsBasisStatus::kNonbasic);
HighsRandom random(seed);
HighsInt num_basic = 0;
for (;;) {
HighsInt iVar = random.integer(num_var);
if (iVar < num_col) {
if (basis.col_status[iVar] == HighsBasisStatus::kNonbasic) {
basis.col_status[iVar] = HighsBasisStatus::kBasic;
num_basic++;
}
} else {
if (basis.row_status[iVar - num_col] == HighsBasisStatus::kNonbasic) {
basis.row_status[iVar - num_col] = HighsBasisStatus::kBasic;
num_basic++;
}
}
if (num_basic == num_row) break;
}
REQUIRE(highs.setBasis(basis) == HighsStatus::kOk);
highs.run();
}
std::string profile = num_col < num_row ? "portrait" : "landscape";
const bool run_primal_test = true;
if (run_primal_test && !seed) {
ss.str(std::string());
ss << "AlienBasis: " << model << " primal " << profile;
basis.debug_origin_name = ss.str();
for (HighsInt iCol = 0; iCol < num_col; iCol++)
basis.col_status[iCol] = HighsBasisStatus::kBasic;
for (HighsInt iRow = 0; iRow < num_row; iRow++)
basis.row_status[iRow] = HighsBasisStatus::kNonbasic;
REQUIRE(highs.setBasis(basis) == HighsStatus::kOk);
highs.run();
}
const bool run_primal_random_test = true;
if (run_primal_random_test) {
basis.col_status.assign(num_col, HighsBasisStatus::kNonbasic);
basis.row_status.assign(num_row, HighsBasisStatus::kNonbasic);
ss.str(std::string());
ss << "AlienBasis: " << model << " primal random-" << seed << " "
<< profile;
basis.debug_origin_name = ss.str();
HighsRandom random(seed);
for (HighsInt iCol = 0; iCol < num_col; iCol++) {
HighsInt iVar = random.integer(num_var);
if (iVar < num_col) {
basis.col_status[iVar] = HighsBasisStatus::kBasic;
} else {
basis.row_status[iVar - num_col] = HighsBasisStatus::kBasic;
}
}
REQUIRE(highs.setBasis(basis) == HighsStatus::kOk);
highs.run();
}
HighsLp dual_lp;
dual_lp.num_col_ = lp.num_row_;
dual_lp.num_row_ = lp.num_col_;
dual_lp.sense_ = ObjSense::kMinimize;
dual_lp.col_lower_.assign(dual_lp.num_col_, 0);
dual_lp.col_upper_.assign(dual_lp.num_col_, inf);
if (lp.row_lower_[0] > -inf) {
for (HighsInt iCol = 0; iCol < dual_lp.num_col_; iCol++)
dual_lp.col_cost_.push_back(-lp.row_lower_[iCol]);
dual_lp.row_lower_.assign(dual_lp.num_row_, -inf);
dual_lp.row_upper_ = lp.col_cost_;
} else {
dual_lp.col_cost_ = lp.row_upper_;
for (HighsInt iRow = 0; iRow < dual_lp.num_row_; iRow++)
dual_lp.row_lower_.push_back(-lp.col_cost_[iRow]);
dual_lp.row_upper_.assign(dual_lp.num_row_, inf);
}
dual_lp.a_matrix_ = lp.a_matrix_;
dual_lp.a_matrix_.num_col_ = dual_lp.num_col_;
dual_lp.a_matrix_.num_row_ = dual_lp.num_row_;
dual_lp.a_matrix_.format_ = MatrixFormat::kRowwise;
dual_lp.a_matrix_.ensureColwise();
highs.passModel(dual_lp);
num_col = dual_lp.num_col_;
num_row = dual_lp.num_row_;
basis.col_status.resize(num_col);
basis.row_status.resize(num_row);
profile = num_col < num_row ? "portrait" : "landscape";
const bool run_dual_test = true;
if (run_dual_test && !seed) {
ss.str(std::string());
ss << "AlienBasis: " << model << " dual " << profile;
basis.debug_origin_name = ss.str();
for (HighsInt iCol = 0; iCol < num_col; iCol++)
basis.col_status[iCol] = HighsBasisStatus::kBasic;
for (HighsInt iRow = 0; iRow < num_row; iRow++)
basis.row_status[iRow] = HighsBasisStatus::kNonbasic;
REQUIRE(highs.setBasis(basis) == HighsStatus::kOk);
highs.run();
}
const bool run_dual_random_test = true;
if (run_dual_random_test) {
basis.col_status.assign(num_col, HighsBasisStatus::kNonbasic);
basis.row_status.assign(num_row, HighsBasisStatus::kNonbasic);
basis.debug_origin_name =
"AlienBasis: " + model + " dual random " + profile;
ss.str(std::string());
ss << "AlienBasis: " << model << " dual random-" << seed << " " << profile;
basis.debug_origin_name = ss.str();
HighsRandom random(seed);
for (HighsInt iCol = 0; iCol < num_col; iCol++) {
HighsInt iVar = random.integer(num_var);
if (iVar < num_col) {
basis.col_status[iVar] = HighsBasisStatus::kBasic;
} else {
basis.row_status[iVar - num_col] = HighsBasisStatus::kBasic;
}
}
REQUIRE(highs.setBasis(basis) == HighsStatus::kOk);
highs.run();
}
highs.resetGlobalScheduler(true);
}
TEST_CASE("AlienBasis-reuse-basis", "[highs_test_alien_basis]") {
HighsLp lp;
lp.num_col_ = 2;
lp.num_row_ = 3;
lp.col_cost_ = {400, 650};
lp.col_lower_ = {0, 0};
lp.col_upper_ = {inf, inf};
lp.row_lower_ = {-inf, -inf, -inf};
lp.row_upper_ = {140, 14, 85};
lp.a_matrix_.start_ = {0, 3, 6};
lp.a_matrix_.index_ = {0, 1, 2, 0, 1, 2};
lp.a_matrix_.value_ = {15, 2, 10, 25, 3, 20};
lp.sense_ = ObjSense::kMaximize;
Highs highs;
if (!dev_run) highs.setOptionValue("output_flag", false);
highs.passModel(lp);
highs.run();
if (dev_run) highs.writeSolution("", 1);
HighsBasis basis = highs.getBasis();
vector<HighsInt> new_index = {0, 1, 2};
vector<double> new_value = {50, 4, 30};
highs.addCol(850, 0, inf, 3, new_index.data(), new_value.data());
new_value[0] = 15;
new_value[1] = 24;
new_value[2] = 30;
highs.addRow(-inf, 108, 3, new_index.data(), new_value.data());
const bool singlar_also = true;
if (singlar_also) {
const HighsInt from_col = 0;
const HighsInt to_col = 0;
HighsInt get_num_col;
double get_cost;
double get_lower;
double get_upper;
HighsInt get_num_nz;
highs.getCols(from_col, to_col, get_num_col, &get_cost, &get_lower,
&get_upper, get_num_nz, NULL, NULL, NULL);
vector<HighsInt> get_start(get_num_col + 1);
vector<HighsInt> get_index(get_num_nz);
vector<double> get_value(get_num_nz);
highs.getCols(from_col, to_col, get_num_col, &get_cost, &get_lower,
&get_upper, get_num_nz, get_start.data(), get_index.data(),
get_value.data());
REQUIRE(highs.changeCoeff(0, 1, 30) == HighsStatus::kOk);
REQUIRE(highs.changeCoeff(1, 1, 4) == HighsStatus::kOk);
REQUIRE(highs.changeCoeff(2, 1, 20) == HighsStatus::kOk);
REQUIRE(highs.changeCoeff(3, 1, 30) == HighsStatus::kOk);
}
if (dev_run) highs.setOptionValue("log_dev_level", 3);
highs.setOptionValue("simplex_scale_strategy", 0);
basis.col_status.push_back(HighsBasisStatus::kNonbasic);
basis.row_status.push_back(HighsBasisStatus::kNonbasic);
basis.alien = true;
REQUIRE(highs.setBasis(basis) == HighsStatus::kOk);
highs.run();
if (dev_run) highs.writeSolution("", 1);
highs.resetGlobalScheduler(true);
}
TEST_CASE("AlienBasis-singular-basis", "[highs_test_alien_basis]") {
HighsStatus return_status;
HighsLp lp;
lp.num_col_ = 2;
lp.num_row_ = 2;
lp.col_cost_ = {-1, -1};
lp.col_lower_ = {0, 0};
lp.col_upper_ = {inf, inf};
lp.row_lower_ = {-inf, -inf};
lp.row_upper_ = {3, 2};
lp.a_matrix_.start_ = {0, 2, 4};
lp.a_matrix_.index_ = {0, 1, 0, 1};
lp.a_matrix_.value_ = {1, 2, 3, 1};
lp.sense_ = ObjSense::kMinimize;
Highs highs;
highs.setOptionValue("output_flag", dev_run);
if (dev_run) highs.setOptionValue("log_dev_level", 3);
highs.passModel(lp);
highs.run();
if (dev_run) highs.writeSolution("", 1);
HighsBasis basis = highs.getBasis();
highs.changeCoeff(1, 0, 1);
highs.changeCoeff(1, 1, 3);
highs.changeRowBounds(1, -inf, 3);
highs.setBasis(basis);
std::vector<HighsInt> basic_variables;
basic_variables.resize(lp.num_row_);
return_status = highs.getBasicVariables(basic_variables.data());
REQUIRE(return_status == HighsStatus::kError);
highs.resetGlobalScheduler(true);
}