Skip to content

Commit ae4c0c3

Browse files
authored
Merge pull request #2965 from ERGO-Code/fix-2947a
`Highs::qFormatOk` now allows square Hessian
2 parents 8a9ea48 + c937b85 commit ae4c0c3

5 files changed

Lines changed: 88 additions & 46 deletions

File tree

check/TestCAPI.c

Lines changed: 63 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -241,16 +241,16 @@ void assertLogical(const char* name, const HighsInt is) {
241241
void createBlendingLp(void* highs) {
242242
// Special variant of the blending LP, with redundant constraint so
243243
// that LP is reduced by presolve - but not to empty!
244-
const double inf = Highs_getInfinity(highs);
244+
const double kHighsInf = Highs_getInfinity(highs);
245245

246246
HighsInt num_col = 2;
247247
HighsInt num_row = 3;
248248
HighsInt num_nz = 6;
249249
HighsInt sense = -1;
250250
double col_cost[2] = {8, 10};
251251
double col_lower[2] = {0, 0};
252-
double col_upper[2] = {inf, inf};
253-
double row_lower[3] = {-inf, -inf, -inf};
252+
double col_upper[2] = {kHighsInf, kHighsInf};
253+
double row_lower[3] = {-kHighsInf, -kHighsInf, -kHighsInf};
254254
double row_upper[3] = {500, 120, 210};
255255
HighsInt a_index[6] = {0, 1, 0, 1, 0, 1};
256256
double a_value[6] = {0.5, 0.5, 0.3, 0.5, 0.7, 0.5};
@@ -488,41 +488,64 @@ void minimalApiQp() {
488488
HighsInt num_col = 3;
489489
HighsInt num_row = 1;
490490
HighsInt num_nz = 2;
491-
HighsInt q_num_nz = 4;
492491
HighsInt a_format = kHighsMatrixFormatColwise;
493-
HighsInt q_format = kHighsHessianFormatTriangular;
494492
HighsInt sense = kHighsObjSenseMinimize;
495493
double offset = 0;
496494
double col_cost[3] = {0.0, -1.0, -3.0};
497495
double col_lower[3] = {-inf, -inf, -inf};
498496
double col_upper[3] = {inf, inf, inf};
499497
double row_lower[1] = {-inf};
500498
double row_upper[1] = {2};
499+
501500
HighsInt a_start[3] = {0, 1, 1};
502501
HighsInt a_index[2] = {0, 0};
503502
double a_value[2] = {1.0, 1.0};
503+
504+
// Start with triangular Hessian, then
505+
HighsInt q_format = kHighsHessianFormatTriangular;
506+
HighsInt q_num_nz = 4;
504507
HighsInt q_start[3] = {0, 2, 3};
505508
HighsInt q_index[4] = {0, 2, 1, 2};
506509
double q_value[4] = {2.0, -1.0, 0.2, 2.0};
507510

511+
double required_x[3] = {0.5, 5.0, 1.5};
512+
508513
double* col_value = (double*)malloc(sizeof(double) * num_col);
514+
509515
HighsInt model_status;
510-
HighsInt return_status = Highs_qpCall(
511-
num_col, num_row, num_nz, q_num_nz, a_format, q_format, sense, offset,
512-
col_cost, col_lower, col_upper, row_lower, row_upper, a_start, a_index,
513-
a_value, q_start, q_index, q_value, col_value, NULL, NULL, NULL, NULL,
514-
NULL, &model_status);
516+
HighsInt return_status =
517+
Highs_qpCall(num_col, num_row, num_nz, q_num_nz, a_format, q_format, sense, offset,
518+
col_cost, col_lower, col_upper, row_lower, row_upper, a_start, a_index,
519+
a_value, q_start, q_index, q_value, col_value, NULL, NULL, NULL, NULL,
520+
NULL, &model_status);
515521
assert(return_status == kHighsStatusOk);
516522
assertIntValuesEqual("Model status for QP qph", model_status,
517-
kHighsModelStatusOptimal);
518-
double required_x[3] = {0.5, 5.0, 1.5};
519-
if (dev_run) {
520-
for (HighsInt iCol = 0; iCol < num_col; iCol++) {
521-
printf("x%d1 = %g\n", (int)iCol, col_value[iCol]);
522-
assertDoubleValuesEqual("Solution value for QP qph", col_value[iCol],
523-
required_x[iCol]);
524-
}
523+
kHighsModelStatusOptimal);
524+
for (HighsInt iCol = 0; iCol < num_col; iCol++) {
525+
if (dev_run) printf("x%d1 = %g\n", (int)iCol, col_value[iCol]);
526+
assertDoubleValuesEqual("Solution value for QP qph", col_value[iCol],
527+
required_x[iCol]);
525528
}
529+
530+
HighsInt square_q_format = kHighsHessianFormatSquare;
531+
HighsInt square_q_num_nz = 5;
532+
HighsInt square_q_start[3] = {0, 2, 3};
533+
HighsInt square_q_index[5] = {0, 2, 1, 0, 2};
534+
double square_q_value[5] = {2.0, -1.0, 0.2, -1.0, 2.0};
535+
536+
Highs_qpCall(num_col, num_row, num_nz, square_q_num_nz, a_format, square_q_format, sense, offset,
537+
col_cost, col_lower, col_upper, row_lower, row_upper, a_start, a_index,
538+
a_value, square_q_start, square_q_index, square_q_value, col_value, NULL, NULL, NULL, NULL,
539+
NULL, &model_status);
540+
assert(return_status == kHighsStatusOk);
541+
assertIntValuesEqual("Model status for QP qph", model_status,
542+
kHighsModelStatusOptimal);
543+
for (HighsInt iCol = 0; iCol < num_col; iCol++) {
544+
if (dev_run) printf("x%d1 = %g\n", (int)iCol, col_value[iCol]);
545+
assertDoubleValuesEqual("Solution value for QP qph", col_value[iCol],
546+
required_x[iCol]);
547+
}
548+
526549
free(col_value);
527550
}
528551

@@ -1513,23 +1536,33 @@ void testPassHessian() {
15131536
HighsInt index[1] = {0};
15141537
double value[1] = {-2.0};
15151538
HighsInt return_status;
1516-
return_status = Highs_passHessian(highs, 1, 1, 1, start, index, value);
1517-
assertIntValuesEqual("Return of passHessian", return_status, kHighsStatusOk);
1518-
Highs_run(highs);
1519-
// Solving max -x^2 + 2x
1539+
15201540
const double optimal_objective_value = 1;
15211541
const double primal = 1;
15221542
const double dual = 0;
1523-
assertIntValuesEqual("Status", Highs_getModelStatus(highs),
1524-
kHighsModelStatusOptimal); // kOptimal
15251543
double col_value[1] = {-123.0};
15261544
double col_dual[1] = {0.0};
1527-
Highs_getSolution(highs, col_value, col_dual, NULL, NULL);
1528-
double objective_value = Highs_getObjectiveValue(highs);
1529-
assertDoubleValuesEqual("Objective", objective_value,
1530-
optimal_objective_value);
1531-
assertDoubleValuesEqual("Primal", col_value[0], primal);
1532-
assertDoubleValuesEqual("Dual", col_dual[0], dual);
1545+
1546+
for (HighsInt k = 0; k < 2; k++) {
1547+
HighsInt q_format = -1;
1548+
if (k == 0) {
1549+
q_format = kHighsHessianFormatTriangular;
1550+
} else {
1551+
q_format = kHighsHessianFormatSquare;
1552+
}
1553+
return_status = Highs_passHessian(highs, 1, 1, q_format, start, index, value);
1554+
assertIntValuesEqual("Return of passHessian", return_status, kHighsStatusOk);
1555+
Highs_run(highs);
1556+
// Solving max -x^2 + 2x
1557+
assertIntValuesEqual("Status", Highs_getModelStatus(highs),
1558+
kHighsModelStatusOptimal); // kOptimal
1559+
Highs_getSolution(highs, col_value, col_dual, NULL, NULL);
1560+
double objective_value = Highs_getObjectiveValue(highs);
1561+
assertDoubleValuesEqual("Objective", objective_value,
1562+
optimal_objective_value);
1563+
assertDoubleValuesEqual("Primal", col_value[0], primal);
1564+
assertDoubleValuesEqual("Dual", col_dual[0], dual);
1565+
}
15331566

15341567
Highs_destroy(highs);
15351568
}

check/TestQpSolver.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1358,7 +1358,7 @@ TEST_CASE("pass-square-hessian", "[qpsolver]") {
13581358
REQUIRE(h.run() == HighsStatus::kOk);
13591359
REQUIRE(okValueDifference(info.objective_function_value,
13601360
optimal_objective_value));
1361-
1361+
13621362
h.resetGlobalScheduler(true);
13631363
}
13641364

highs/lp_data/Highs.cpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -491,7 +491,7 @@ HighsStatus Highs::passModel(
491491
assert(q_value != NULL);
492492
HighsHessian& hessian = model.hessian_;
493493
hessian.dim_ = num_col;
494-
hessian.format_ = HessianFormat::kTriangular;
494+
hessian.format_ = static_cast<HessianFormat>(q_format);
495495
hessian.start_.assign(q_start, q_start + num_col);
496496
hessian.start_.resize(num_col + 1);
497497
hessian.start_[num_col] = q_num_nz;
@@ -556,7 +556,7 @@ HighsStatus Highs::passHessian(const HighsInt dim, const HighsInt num_nz,
556556
HighsInt num_col = model_.lp_.num_col_;
557557
if (dim != num_col) return HighsStatus::kError;
558558
hessian.dim_ = num_col;
559-
hessian.format_ = HessianFormat::kTriangular;
559+
hessian.format_ = static_cast<HessianFormat>(format);
560560
if (dim > 0) {
561561
assert(start != NULL);
562562
hessian.start_.assign(start, start + num_col);
@@ -3960,8 +3960,9 @@ HighsStatus Highs::callSolveLp(HighsLp& lp, const string message) {
39603960
HighsStatus Highs::callSolveQp() {
39613961
// Check that the model is column-wise
39623962
HighsLp& lp = model_.lp_;
3963-
HighsHessian& hessian = model_.hessian_;
39643963
assert(model_.lp_.a_matrix_.isColwise());
3964+
HighsHessian& hessian = model_.hessian_;
3965+
assert(hessian.format_ == HessianFormat::kTriangular);
39653966
if (hessian.dim_ > lp.num_col_) {
39663967
highsLogDev(
39673968
options_.log_options, HighsLogType::kError,

highs/lp_data/HighsInterface.cpp

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2865,25 +2865,24 @@ bool Highs::aFormatOk(const HighsInt num_nz, const HighsInt format) {
28652865
if (!num_nz) return true;
28662866
const bool ok_format = format == (HighsInt)MatrixFormat::kColwise ||
28672867
format == (HighsInt)MatrixFormat::kRowwise;
2868-
assert(ok_format);
28692868
if (!ok_format)
2870-
highsLogUser(
2871-
options_.log_options, HighsLogType::kError,
2872-
"Non-empty Constraint matrix has illegal format = %" HIGHSINT_FORMAT
2873-
"\n",
2874-
format);
2869+
highsLogUser(options_.log_options, HighsLogType::kError,
2870+
"Non-empty Constraint matrix has illegal format = %d\n",
2871+
int(format));
2872+
assert(ok_format);
28752873
return ok_format;
28762874
}
28772875

28782876
bool Highs::qFormatOk(const HighsInt num_nz, const HighsInt format) {
28792877
if (!num_nz) return true;
2880-
const bool ok_format = format == (HighsInt)HessianFormat::kTriangular;
2881-
assert(ok_format);
2878+
const bool ok_format =
2879+
format == static_cast<HighsInt>(HessianFormat::kTriangular) ||
2880+
static_cast<HighsInt>(HessianFormat::kSquare);
28822881
if (!ok_format)
2883-
highsLogUser(
2884-
options_.log_options, HighsLogType::kError,
2885-
"Non-empty Hessian matrix has illegal format = %" HIGHSINT_FORMAT "\n",
2886-
format);
2882+
highsLogUser(options_.log_options, HighsLogType::kError,
2883+
"Non-empty Hessian matrix has illegal format = %d\n",
2884+
int(format));
2885+
assert(ok_format);
28872886
return ok_format;
28882887
}
28892888

highs/model/HighsHessianUtils.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,15 @@ void triangularToSquareHessian(const HighsHessian& hessian,
260260
return;
261261
}
262262
assert(hessian.format_ == HessianFormat::kTriangular);
263+
// Cannot use the following, since it puts the diagonal entry first
264+
// in each column and, strangely, the active set QP solver seems to
265+
// need the entries column-by-column in row order
266+
/*
267+
HighsHessian square_hessian = hessian.toSquare();
268+
std::vector<HighsInt> nw_start = square_hessian.start_;
269+
std::vector<HighsInt> nw_index = square_hessian.index_;
270+
std::vector<double> nw_value = square_hessian.value_;
271+
*/
263272
const HighsInt nnz = hessian.start_[dim];
264273
const HighsInt square_nnz = nnz + (nnz - dim);
265274
start.resize(dim + 1);

0 commit comments

Comments
 (0)