1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
|
#include "HCheckConfig.h"
#include "catch.hpp"
#include "lp_data/HighsOptions.h"
#include "model/HighsHessian.h"
#include "model/HighsHessianUtils.h"
// #include "<cstdio>"
const bool dev_run = false;
// No commas in test case name.
TEST_CASE("HighsHessian", "[highs_hessian]") {
HighsOptions options;
if (!dev_run) options.output_flag = false;
HighsHessian square_hessian;
square_hessian.dim_ = 5;
square_hessian.format_ = HessianFormat::kSquare;
square_hessian.start_ = {0, 4, 7, 9, 12, 15};
square_hessian.index_ = {0, 1, 3, 4, 0, 1, 4, 2, 3, 0, 2, 3, 0, 1, 4};
square_hessian.value_ = {5, 1, -1, 2, 1, 4, 1, 3, -1, -1, -1, 4, 2, 1, 5};
HighsHessian triangular_hessian;
triangular_hessian.dim_ = 5;
triangular_hessian.format_ = HessianFormat::kTriangular;
triangular_hessian.start_ = {0, 4, 6, 8, 9, 10};
triangular_hessian.index_ = {0, 1, 3, 4, 1, 4, 2, 3, 3, 4};
triangular_hessian.value_ = {5, 1, -1, 2, 4, 1, 3, -1, 4, 5};
HighsHessian triangular_hessian0 = triangular_hessian;
// Check that the positive diagonal entries are recognised as being
// OK for default assessHessian (minimization) but not for
// maximization.
REQUIRE(assessHessian(square_hessian, options) == HighsStatus::kOk);
REQUIRE(okHessianDiagonal(options, square_hessian, ObjSense::kMinimize));
REQUIRE(!okHessianDiagonal(options, square_hessian, ObjSense::kMaximize));
if (dev_run) {
printf("\nReturned square Hessian\n");
square_hessian.print();
}
// Check that the positive diagonal entries are recognised as being
// OK for default assessHessian (minimization) but not for
// maximization.
REQUIRE(assessHessian(triangular_hessian, options) == HighsStatus::kOk);
REQUIRE(okHessianDiagonal(options, square_hessian, ObjSense::kMinimize));
REQUIRE(!okHessianDiagonal(options, square_hessian, ObjSense::kMaximize));
if (dev_run) {
printf("\nReturned triangular Hessian\n");
triangular_hessian.print();
}
REQUIRE((triangular_hessian == triangular_hessian0));
// Extract the triangluar Hessian from the square Hessian
REQUIRE(extractTriangularHessian(options, square_hessian) ==
HighsStatus::kOk);
if (dev_run) {
printf("\nReturned triangularised square Hessian\n");
square_hessian.print();
}
// Extract the triangluar Hessian from the triangular Hessian
REQUIRE(extractTriangularHessian(options, triangular_hessian) ==
HighsStatus::kOk);
if (dev_run) {
printf("\nReturned triangularised triangular Hessian\n");
triangular_hessian.print();
}
HighsHessian negative_diagonal_hessian = triangular_hessian;
negative_diagonal_hessian.value_[0] = -negative_diagonal_hessian.value_[0];
negative_diagonal_hessian.value_[4] = -negative_diagonal_hessian.value_[4];
negative_diagonal_hessian.value_[6] = -negative_diagonal_hessian.value_[6];
negative_diagonal_hessian.value_[8] = -negative_diagonal_hessian.value_[8];
negative_diagonal_hessian.value_[9] = -negative_diagonal_hessian.value_[9];
REQUIRE(assessHessian(negative_diagonal_hessian, options) ==
HighsStatus::kOk);
REQUIRE(!okHessianDiagonal(options, negative_diagonal_hessian,
ObjSense::kMinimize));
REQUIRE(okHessianDiagonal(options, negative_diagonal_hessian,
ObjSense::kMaximize));
// Square Hessian with only triangular entries - doubled strictly triangular
// entries.
HighsHessian hessian0;
hessian0.dim_ = 5;
hessian0.format_ = HessianFormat::kSquare;
hessian0.start_ = {0, 1, 3, 4, 7, 10};
hessian0.index_ = {0, 0, 1, 2, 0, 2, 3, 0, 1, 4};
hessian0.value_ = {5, 2, 4, 3, -2, -2, 4, 4, 2, 5};
REQUIRE(assessHessian(hessian0, options) == HighsStatus::kOk);
if (dev_run) {
printf("\nReturned\n");
hessian0.print();
}
REQUIRE((hessian0 == triangular_hessian0));
// Nonsymmetric Hessian - with entries resulting in cancellation
HighsHessian hessian1;
hessian1.format_ = HessianFormat::kSquare;
hessian1.dim_ = 5;
hessian1.start_ = {0, 3, 5, 7, 10, 14};
hessian1.index_ = {0, 3, 4, 0, 1, 2, 4, 0, 2, 3, 0, 1, 2, 4};
hessian1.value_ = {5, -5, 1, 2, 4, 3, 1, 3, -2, 4, 3, 2, -1, 5};
REQUIRE(assessHessian(hessian1, options) == HighsStatus::kOk);
if (dev_run) {
printf("\nReturned\n");
hessian1.print();
}
REQUIRE((hessian1 == triangular_hessian0));
HighsHessian indefinite;
indefinite.dim_ = 3;
indefinite.format_ = HessianFormat::kTriangular;
indefinite.start_ = {0, 2, 2, 3};
indefinite.index_ = {0, 2, 2};
indefinite.value_ = {2, 1, 4};
HighsInt indefinite_num_nz0 = indefinite.numNz();
REQUIRE(assessHessian(indefinite, options) == HighsStatus::kOk);
// Check that there is one more entry due to the explicit zero
REQUIRE(indefinite.numNz() == indefinite_num_nz0 + 1);
// Check that all first indices are for the diagonal
for (HighsInt iCol = 0; iCol < indefinite.dim_; iCol++) {
const HighsInt iEl = indefinite.start_[iCol];
REQUIRE(indefinite.index_[iEl] == iCol);
}
// Negate the diagonal entries
for (HighsInt iCol = 0; iCol < indefinite.dim_; iCol++) {
const HighsInt iEl = indefinite.start_[iCol];
indefinite.value_[iEl] = -indefinite.value_[iEl];
}
if (dev_run) {
printf("\nIndefinite\n");
indefinite.print();
}
REQUIRE(assessHessian(indefinite, options) == HighsStatus::kOk);
// Can tell that the indefinite Hessian is not OK for minimization
REQUIRE(!okHessianDiagonal(options, indefinite, ObjSense::kMinimize));
// Cannot tell that the indefinite Hessian is not OK for
// maximization since its diagonal entries are in [-4 0)
REQUIRE(okHessianDiagonal(options, indefinite, ObjSense::kMaximize));
}
|