Skip to content

Commit 41d783e

Browse files
committed
Now investigate and document positive initial null space with primal1.mps
1 parent ba46f84 commit 41d783e

File tree

5 files changed

+128
-133
lines changed

5 files changed

+128
-133
lines changed

check/TestQpSolver.cpp

Lines changed: 35 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
#include "catch.hpp"
77
#include "io/FilereaderLp.h"
88

9-
const bool dev_run = true;//false;
9+
const bool dev_run = false;
1010
const double inf = kHighsInf;
1111
const double double_equal_tolerance = 1e-5;
1212

@@ -954,7 +954,7 @@ TEST_CASE("test-qp-hot-start", "[qpsolver]") {
954954
const std::string filename =
955955
std::string(HIGHS_DIR) + "/check/instances/primal1.mps";
956956
REQUIRE(highs.readModel(filename) == HighsStatus::kOk);
957-
highs.getBasis().print("test-qp-hot-start");
957+
// highs.getBasis().print("test-qp-hot-start");
958958
required_objective_function_value = -0.035012965733477348;
959959
} else if (k == 2) {
960960
// Not currently tested
@@ -1150,12 +1150,13 @@ TEST_CASE("rowless-qp", "[qpsolver]") {
11501150

11511151
TEST_CASE("test-qp-atwood", "[qpsolver]") {
11521152
Highs h;
1153-
// highs.setOptionValue("output_flag", dev_run);
1153+
h.setOptionValue("output_flag", dev_run);
11541154
std::string filename =
11551155
std::string(HIGHS_DIR) + "/check/instances/atwood0.mps";
11561156
REQUIRE(h.readModel(filename) == HighsStatus::kOk);
11571157

1158-
const double primal_feasibility_tolerance = h.getOptions().primal_feasibility_tolerance;
1158+
const double primal_feasibility_tolerance =
1159+
h.getOptions().primal_feasibility_tolerance;
11591160

11601161
const double required_objective0 = 4.16347077e-02;
11611162
const double required_objective1 = 2.91530651e-02;
@@ -1165,15 +1166,28 @@ TEST_CASE("test-qp-atwood", "[qpsolver]") {
11651166
const HighsBasis& basis_ = h.getBasis();
11661167
REQUIRE(model.lp_.row_lower_[1] == 0.26);
11671168

1169+
// After solving QP0, the lower bound on the second constraint is
1170+
// reduced to give QP1, so the optimal solution of QP0 is feasible
1171+
// for QP1. It should be possible to hot start QP1 using the optimal
1172+
// basis of QP0, but that basis has a null space dimension of 1, and
1173+
// the QP solver currently only hot starts from a vertex
1174+
// solution. This has a null space dimension of 0, although any free
1175+
// variables are considered as a special case (see primal1.mps),
1176+
// leading to a positive initial null space.
1177+
//
1178+
// Previously, this test case exposed a bug in hot start of the ASM
1179+
// solver, so it's a useful test case as/when hot start is improved.
1180+
11681181
const bool hot_start = true;
1169-
for (HighsInt k= 0; k < 2; k++) {
1182+
for (HighsInt k = 0; k < 2; k++) {
11701183
REQUIRE(h.run() == HighsStatus::kOk);
11711184
HighsSolution solution = h.getSolution();
11721185
HighsBasis basis = basis_;
11731186

11741187
HighsInt num_basic = 0;
11751188
HighsInt num_non_basic = 0;
11761189
const HighsLp& lp = model.lp_;
1190+
/*
11771191
printf("\nColumns\n");
11781192
for (HighsInt iCol = 0; iCol < lp.num_col_; iCol++) {
11791193
double lower = lp.col_lower_[iCol];
@@ -1182,16 +1196,13 @@ TEST_CASE("test-qp-atwood", "[qpsolver]") {
11821196
double rsdu = std::min(value-lower, upper-value);
11831197
HighsBasisStatus status = basis_.col_status[iCol];
11841198
printf("%2d [%11.4g, %11.4g, %11.4g] %11.4g %s\n",
1185-
int(iCol), lower, value, upper, rsdu, h.basisStatusToString(status).c_str());
1186-
if (status == HighsBasisStatus::kBasic) {
1187-
num_basic++;
1188-
} else if (status == HighsBasisStatus::kNonbasic) {
1189-
num_non_basic++;
1190-
} else {
1191-
REQUIRE(rsdu <= primal_feasibility_tolerance);
1199+
int(iCol), lower, value, upper, rsdu,
1200+
h.basisStatusToString(status).c_str()); if (status ==
1201+
HighsBasisStatus::kBasic) { num_basic++; } else if (status ==
1202+
HighsBasisStatus::kNonbasic) { num_non_basic++; } else { REQUIRE(rsdu <=
1203+
primal_feasibility_tolerance);
11921204
}
11931205
}
1194-
11951206
printf("Rows\n");
11961207
for (HighsInt iRow = 0; iRow < lp.num_row_; iRow++) {
11971208
double lower = lp.row_lower_[iRow];
@@ -1200,25 +1211,24 @@ TEST_CASE("test-qp-atwood", "[qpsolver]") {
12001211
double rsdu = std::min(value-lower, upper-value);
12011212
HighsBasisStatus status = basis_.row_status[iRow];
12021213
printf("%2d [%11.4g, %11.4g, %11.4g] %11.4g %s\n",
1203-
int(iRow), lower, value, upper, rsdu, h.basisStatusToString(status).c_str());
1204-
if (status == HighsBasisStatus::kBasic) {
1205-
num_basic++;
1206-
} else if (status == HighsBasisStatus::kNonbasic) {
1207-
num_non_basic++;
1208-
} else {
1209-
REQUIRE(rsdu <= primal_feasibility_tolerance);
1214+
int(iRow), lower, value, upper, rsdu,
1215+
h.basisStatusToString(status).c_str()); if (status ==
1216+
HighsBasisStatus::kBasic) { num_basic++; } else if (status ==
1217+
HighsBasisStatus::kNonbasic) { num_non_basic++; } else { REQUIRE(rsdu <=
1218+
primal_feasibility_tolerance);
12101219
}
12111220
}
1212-
printf("QP has %d basic and %d nonbasic variables\n", int(num_basic), int(num_non_basic));
1213-
1221+
printf("QP has %d basic and %d nonbasic variables\n", int(num_basic),
1222+
int(num_non_basic));
1223+
*/
12141224
if (k == 0) {
12151225
const double objective0 = h.getInfo().objective_function_value;
12161226
REQUIRE(std::fabs(objective0 - required_objective0) < 1e-4);
12171227
} else {
12181228
const double objective1 = h.getInfo().objective_function_value;
12191229
REQUIRE(std::fabs(objective1 - required_objective1) < 1e-4);
1220-
}
1221-
1230+
}
1231+
12221232
double lower = 0.25;
12231233
double upper = kHighsInf;
12241234
h.changeRowBounds(1, lower, upper);
@@ -1231,6 +1241,6 @@ TEST_CASE("test-qp-atwood", "[qpsolver]") {
12311241
assert(basis_.valid);
12321242
}
12331243
}
1234-
1244+
12351245
h.resetGlobalScheduler(true);
12361246
}

highs/lp_data/Highs.cpp

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -314,7 +314,6 @@ HighsStatus Highs::passModel(HighsModel model) {
314314
HighsStatus return_status = HighsStatus::kOk;
315315
// Clear the incumbent model and any associated data
316316
clearModel();
317-
basis_.print("Highs::passModel(HighsModel model) after clearModel()");
318317
HighsLp& lp = model_.lp_;
319318
HighsHessian& hessian = model_.hessian_;
320319
// Move the model's LP and Hessian to the internal LP and Hessian
@@ -378,15 +377,12 @@ HighsStatus Highs::passModel(HighsModel model) {
378377
// Clear solver status, solution, basis and info associated with any
379378
// previous model; clear any HiGHS model object; create a HiGHS
380379
// model object for this LP
381-
basis_.print("Highs::passModel(HighsModel model) before clearSolver()");
382380
return_status = interpretCallStatus(options_.log_options, clearSolver(),
383381
return_status, "clearSolver");
384-
basis_.print("Highs::passModel(HighsModel model) after clearSolver()");
385382
// Apply any user scaling in call to optionChangeAction
386383
return_status =
387384
interpretCallStatus(options_.log_options, optionChangeAction(),
388385
return_status, "optionChangeAction");
389-
basis_.print("Highs::passModel(HighsModel model) exit");
390386
return returnFromHighs(return_status);
391387
}
392388

highs/lp_data/HighsSolution.cpp

Lines changed: 1 addition & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1815,25 +1815,7 @@ void HighsObjectiveSolution::clear() { this->col_value.clear(); }
18151815

18161816
void HighsBasis::print(std::string message) const {
18171817
this->printScalars(message);
1818-
if (!this->useful) {
1819-
if (this->col_status.size() > 0) {
1820-
HighsInt iCol = 0;
1821-
printf("Basis: col_status[%2d] = %d\n", int(iCol),
1822-
int(this->col_status[iCol]));
1823-
iCol = this->col_status.size();
1824-
printf("Basis: col_status[%2d] = %d\n", int(iCol),
1825-
int(this->col_status[iCol]));
1826-
}
1827-
if (this->row_status.size() > 0) {
1828-
HighsInt iRow = 0;
1829-
printf(" Basis: row_status[%2d] = %d\n", int(iRow),
1830-
int(this->row_status[iRow]));
1831-
iRow = this->row_status.size();
1832-
printf(" Basis: row_status[%2d] = %d\n", int(iRow),
1833-
int(this->row_status[iRow]));
1834-
}
1835-
return;
1836-
}
1818+
if (!this->useful) return;
18371819
for (HighsInt iCol = 0; iCol < HighsInt(this->col_status.size()); iCol++)
18381820
printf(" Basis: col_status[%2d] = %d\n", int(iCol),
18391821
int(this->col_status[iCol]));

highs/qpsolver/a_quass.cpp

Lines changed: 31 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -128,13 +128,15 @@ QpAsmStatus solveqp(Instance& instance, Settings& settings, Statistics& stats,
128128

129129
QpSolution qp_solution(instance);
130130

131-
// presolve
132-
133-
// scale instance, store scaling factors
134-
135-
// perturb instance, store perturbance information
136-
137-
// regularize
131+
// To be done!
132+
//
133+
// * Presolve
134+
//
135+
// * Scaling
136+
//
137+
// * Perturbation
138+
139+
// Regularize
138140
for (HighsInt i = 0; i < instance.num_var; i++) {
139141
for (HighsInt index = instance.Q.mat.start[i];
140142
index < instance.Q.mat.start[i + 1]; index++) {
@@ -144,7 +146,18 @@ QpAsmStatus solveqp(Instance& instance, Settings& settings, Statistics& stats,
144146
}
145147
}
146148

147-
// compute initial feasible point
149+
// Determine an initial feasible point
150+
//
151+
// Cold start uses simplex to solve the LP feasibility problem to
152+
// obtain a feasible vertex
153+
//
154+
// Hot start is currently limited to using the basis (and solution,
155+
// if there is no basis) to hot start simplex when solving the LP
156+
// feasibility problem
157+
//
158+
// It should be possible to hot start by using the QP basis to
159+
// construct x = Yb, where Y and b correspond to the active
160+
// constraints, and form the null space data structures
148161
QpHotstartInformation startinfo(instance.num_var, instance.num_con);
149162
if (instance.num_con == 0 && instance.num_var <= 15000) {
150163
computeStartingPointBounded(instance, settings, stats, qp_model_status,
@@ -162,10 +175,9 @@ QpAsmStatus solveqp(Instance& instance, Settings& settings, Statistics& stats,
162175
}
163176
} else {
164177
computeStartingPointByLp(instance, settings, stats, qp_model_status,
165-
startinfo,
166-
// highs_model_status,
167-
highs_basis,
168-
highs_solution, qp_timer);
178+
startinfo,
179+
// highs_model_status,
180+
highs_basis, highs_solution, qp_timer);
169181
if (qp_model_status != QpModelStatus::kNotset) {
170182
return quass2highs(instance, settings, stats, qp_model_status,
171183
qp_solution, highs_model_status, highs_basis,
@@ -177,11 +189,13 @@ QpAsmStatus solveqp(Instance& instance, Settings& settings, Statistics& stats,
177189
solveqp_actual(instance, settings, startinfo, stats, qp_model_status,
178190
qp_solution, qp_timer);
179191

180-
// undo perturbation and resolve
181-
182-
// undo scaling and resolve
183-
184-
// postsolve
192+
// To be done!
193+
//
194+
// * Undo perturbation and resolve
195+
//
196+
// * Undo scaling and resolve
197+
//
198+
// * Postsolve
185199

186200
// Transform QP status and qp_solution to HiGHS highs_basis and highs_solution
187201
return quass2highs(instance, settings, stats, qp_model_status, qp_solution,

0 commit comments

Comments
 (0)