Skip to content

Commit 95f7260

Browse files
author
Julian Hall
committed
Reintroduced spurious unboundedness test in feasibility_bounded.cpp
1 parent c879be4 commit 95f7260

6 files changed

Lines changed: 71 additions & 33 deletions

File tree

check/TestQpSolver.cpp

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -57,24 +57,41 @@ void testPrimalDualObjective(Highs& h,
5757
REQUIRE(fabs(info.primal_dual_objective_error) < optimality_tolerance);
5858
}
5959

60-
/*
6160
TEST_CASE("qp-unbounded", "[qpsolver]") {
6261
std::string filename;
6362
filename = std::string(HIGHS_DIR) + "/check/instances/qpunbounded.lp";
64-
63+
// Maximize
64+
// obj: x2 + [ - x1^2 ]/2
65+
// Bounds
66+
// 0 <= x1 <= 40
67+
// End
6568
Highs highs;
6669
highs.setOptionValue("output_flag", dev_run);
6770
REQUIRE(highs.readModel(filename) == HighsStatus::kOk);
6871

69-
for (auto& solver : solvers) {
70-
highs.setOptionValue("solver", solver);
71-
REQUIRE(highs.run() == HighsStatus::kOk);
72-
REQUIRE(highs.getModelStatus() == unboundedStatus(solver));
72+
for (HighsInt k = 0; k < 2; k++) {
73+
for (auto& solver : solvers) {
74+
highs.setOptionValue("solver", solver);
75+
REQUIRE(highs.run() == HighsStatus::kOk);
76+
REQUIRE(highs.getModelStatus() == unboundedStatus(solver));
77+
// Test the oracle Hessian
78+
highs.setOptionValue("test_qp_oracle", true);
79+
REQUIRE(highs.run() == HighsStatus::kOk);
80+
REQUIRE(highs.getModelStatus() == unboundedStatus(solver));
81+
highs.setOptionValue("test_qp_oracle", false);
82+
}
83+
// Now flip the cost and bounds of x2 (which has index 0 since it
84+
// appears first in LP file objective) for code coverage
85+
REQUIRE(highs.changeColCost(0, -highs.getLp().col_cost_[0]) ==
86+
HighsStatus::kOk);
87+
REQUIRE(highs.changeColBounds(0, -highs.getLp().col_upper_[0],
88+
-highs.getLp().col_lower_[0]) ==
89+
HighsStatus::kOk);
90+
highs.clearSolver();
7391
}
7492

7593
highs.resetGlobalScheduler(true);
7694
}
77-
*/
7895

7996
TEST_CASE("qp-infeasible", "[qpsolver]") {
8097
std::string filename;
@@ -216,7 +233,6 @@ TEST_CASE("qpsolver", "[qpsolver]") {
216233
highs.resetGlobalScheduler(true);
217234
}
218235

219-
/*
220236
TEST_CASE("test-qod", "[qpsolver]") {
221237
HighsStatus return_status;
222238
HighsModelStatus model_status;
@@ -225,6 +241,7 @@ TEST_CASE("test-qod", "[qpsolver]") {
225241
double required_x1;
226242

227243
Highs highs;
244+
highs.setOptionValue("output_flag", dev_run);
228245

229246
for (auto& solver : solvers) {
230247
HighsModel local_model;
@@ -250,7 +267,6 @@ TEST_CASE("test-qod", "[qpsolver]") {
250267
hessian.index_ = {0};
251268
hessian.value_ = {2.0};
252269

253-
highs.setOptionValue("output_flag", dev_run);
254270
const HighsInfo& info = highs.getInfo();
255271
const HighsSolution& solution = highs.getSolution();
256272
const double& objective_function_value = info.objective_function_value;
@@ -369,7 +385,6 @@ TEST_CASE("test-qod", "[qpsolver]") {
369385

370386
highs.resetGlobalScheduler(true);
371387
}
372-
*/
373388

374389
TEST_CASE("test-qjh", "[qpsolver]") {
375390
// Test passing/reading and solving the problem qjh
@@ -382,6 +397,7 @@ TEST_CASE("test-qjh", "[qpsolver]") {
382397
double required_objective_function_value;
383398

384399
Highs highs;
400+
highs.setOptionValue("output_flag", dev_run);
385401

386402
for (auto& solver : solvers) {
387403
HighsModel local_model;
@@ -408,7 +424,6 @@ TEST_CASE("test-qjh", "[qpsolver]") {
408424
hessian.index_ = {0, 2, 1, 2};
409425
hessian.value_ = {2.0, -1.0, 0.2, 2.0};
410426

411-
highs.setOptionValue("output_flag", dev_run);
412427
highs.setOptionValue("solver", solver);
413428
const HighsInfo& info = highs.getInfo();
414429
const double& objective_function_value = info.objective_function_value;

highs/lp_data/HStruct.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -257,8 +257,8 @@ struct HessianOracle {
257257
// form products with Q and then apply any non-trivial multiplier_
258258
// or shift_
259259
HighsInt dim_ = 0;
260-
HighsInt multiplier_ = 1.0; // Minimize
261-
HighsInt shift_ = 0.0; // No regularization
260+
double multiplier_ = 1.0; // Minimize
261+
double shift_ = 0.0; // No regularization
262262
HighsHessianFunctionType call_ = nullptr;
263263
void* data_ = nullptr;
264264
void clear();

highs/lp_data/Highs.cpp

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4323,6 +4323,8 @@ HighsStatus Highs::callSolveQp() {
43234323
instance.con_up = lp.row_upper_;
43244324
instance.var_lo = lp.col_lower_;
43254325
instance.var_up = lp.col_upper_;
4326+
// Clear the instance Hessian data
4327+
instance.Q.mat.clear();
43264328
HighsHessian oracle_hessian;
43274329
if (hessian.dim_ > 0) {
43284330
if (options_.test_qp_oracle) {
@@ -4331,9 +4333,6 @@ HighsStatus Highs::callSolveQp() {
43314333
instance.Q.mat.oracle_.dim_ = hessian.dim_;
43324334
instance.Q.mat.oracle_.call_ = oracleCallSquareHessian;
43334335
instance.Q.mat.oracle_.data_ = &oracle_hessian;
4334-
instance.Q.mat.start.clear();
4335-
instance.Q.mat.index.clear();
4336-
instance.Q.mat.value.clear();
43374336
} else {
43384337
assert(instance.Q.mat.oracle_.call_ == nullptr);
43394338
instance.Q.mat.num_col = lp.num_col_;
@@ -4344,8 +4343,6 @@ HighsStatus Highs::callSolveQp() {
43444343
} else {
43454344
assert(hessian.isOracle());
43464345
instance.Q.mat.oracle_ = hessian.oracle_;
4347-
instance.Q.mat.num_col = 0;
4348-
instance.Q.mat.num_row = 0;
43494346
}
43504347

43514348
for (HighsInt i = 0; i < (HighsInt)instance.c.value.size(); i++) {

highs/qpsolver/a_quass.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,9 @@ static QpAsmStatus quass2highs(Instance& instance, Settings& settings,
5252
case QpModelStatus::kLargeNullspace:
5353
highs_model_status = HighsModelStatus::kSolveError;
5454
return QpAsmStatus::kError;
55+
case QpModelStatus::kNonConvex:
56+
highs_model_status = HighsModelStatus::kSolveError;
57+
return QpAsmStatus::kError;
5558
case QpModelStatus::kError:
5659
highs_model_status = HighsModelStatus::kSolveError;
5760
return QpAsmStatus::kError;
@@ -60,6 +63,7 @@ static QpAsmStatus quass2highs(Instance& instance, Settings& settings,
6063
return QpAsmStatus::kError;
6164
default:
6265
highs_model_status = HighsModelStatus::kNotset;
66+
assert(highs_model_status != HighsModelStatus::kNotset);
6367
return QpAsmStatus::kError;
6468
}
6569

highs/qpsolver/feasibility_bounded.hpp

Lines changed: 28 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ static void computeStartingPointBounded(Instance& instance, Settings& settings,
1818
HighsTimer& timer) {
1919
// compute initial feasible point for problems with bounds only (no general
2020
// linear constraints)
21-
21+
const bool debug_printing = false;
2222
// Solve Qx + c = 0 --> x = -Q^-1c
2323
HighsInt dim = instance.num_var;
2424
QpVector res = -instance.c;
@@ -40,6 +40,7 @@ static void computeStartingPointBounded(Instance& instance, Settings& settings,
4040
L.resize(dim * dim);
4141

4242
auto printL = [&]() {
43+
if (!debug_printing) return;
4344
for (HighsInt iRow = 0; iRow < dim; iRow++) {
4445
for (HighsInt iCol = 0; iCol <= iRow; iCol++)
4546
printf(" %11.4g", L[iRow * dim + iCol]);
@@ -71,17 +72,20 @@ static void computeStartingPointBounded(Instance& instance, Settings& settings,
7172
sum += L[iRow * dim + k] * L[iCol * dim + k];
7273
if (iCol < iRow) {
7374
double value = (L[iRow * dim + iCol] - sum) / L[iCol * dim + iCol];
74-
printf("Computed L[%1d, %1d] = (%11.4g - %11.4g) / %11.4g = %11.4g\n",
75-
int(iRow), int(iCol), L[iRow * dim + iCol], sum,
76-
L[iCol * dim + iCol], value);
75+
if (debug_printing)
76+
printf(
77+
"Computed L[%1d, %1d] = (%11.4g - %11.4g) / %11.4g = %11.4g\n",
78+
int(iRow), int(iCol), L[iRow * dim + iCol], sum,
79+
L[iCol * dim + iCol], value);
7780
L[iRow * dim + iCol] = value;
7881
} else {
7982
double value = L[iCol * dim + iCol] - sum;
80-
printf(
81-
"Computed L[%1d, %1d] = sqrt(%11.4g - %11.4g) = sqrt(%11.4g) = "
82-
"%11.4g\n",
83-
int(iCol), int(iCol), L[iCol * dim + iCol], sum, value,
84-
std::sqrt(value));
83+
if (debug_printing)
84+
printf(
85+
"Computed L[%1d, %1d] = sqrt(%11.4g - %11.4g) = sqrt(%11.4g) = "
86+
"%11.4g\n",
87+
int(iCol), int(iCol), L[iCol * dim + iCol], sum, value,
88+
std::sqrt(value));
8589
if (value <= 0) {
8690
modelstatus = QpModelStatus::kNonConvex;
8791
return;
@@ -115,12 +119,21 @@ static void computeStartingPointBounded(Instance& instance, Settings& settings,
115119
std::vector<BasisStatus> atlower;
116120

117121
for (int i = 0; i < instance.num_var; i++) {
118-
// Deleted spurious identification of unboundedness based on
119-
// res.value[i] exceeding
120-
// 0.5/settings.hessian_regularization_value, where the
121-
// denominator can be zero
122-
//
123-
// Clearly need something for qp-unbounded and test-qod
122+
// Check for unboundedness so that qp-unbounded and test-qod pass:
123+
// assumes diagonal Hessian and
124+
// settings.hessian_regularization_value > 0
125+
if (settings.hessian_regularization_value > 0) {
126+
if (res.value[i] > 0.5 / settings.hessian_regularization_value &&
127+
instance.var_up[i] == kHighsInf && instance.c.value[i] < 0.0) {
128+
modelstatus = QpModelStatus::kUnbounded;
129+
return;
130+
} else if (res.value[i] < -0.5 / settings.hessian_regularization_value &&
131+
instance.var_lo[i] == -kHighsInf &&
132+
instance.c.value[i] > 0.0) {
133+
modelstatus = QpModelStatus::kUnbounded;
134+
return;
135+
}
136+
}
124137
if (res.value[i] <
125138
instance.var_lo[i] - settings.primal_feasibility_tolerance) {
126139
res.value[i] = instance.var_lo[i];

highs/qpsolver/matrix.hpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,15 @@ struct MatrixBase {
2222
std::vector<double> value;
2323
HessianOracle oracle_;
2424

25+
void clear() {
26+
num_row = 0;
27+
num_col = 0;
28+
start.clear();
29+
index.clear();
30+
value.clear();
31+
oracle_.clear();
32+
}
33+
2534
bool isOracle() const { return oracle_.call_ != nullptr; }
2635

2736
double diagonal(const HighsInt iCol) const {

0 commit comments

Comments
 (0)