1 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2 /* */
3 /* This file is part of the HiGHS linear optimization suite */
4 /* */
5 /* Written and engineered 2008-2020 at the University of Edinburgh */
6 /* */
7 /* Available as open-source under the MIT License */
8 /* */
9 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
10 /**@file simplex/HQPrimal.cpp
11 * @brief
12 * @author Julian Hall, Ivet Galabova, Qi Huangfu and Michael Feldmeier
13 */
14 #include "simplex/HQPrimal.h"
15
16 #include <cassert>
17 #include <cstdio>
18 #include <iostream>
19
20 #include "io/HighsIO.h"
21 #include "lp_data/HConst.h"
22 #include "simplex/HSimplex.h"
23 #include "simplex/HSimplexDebug.h"
24 #include "simplex/SimplexTimer.h"
25 #include "util/HighsRandom.h"
26 #include "util/HighsUtils.h"
27
28 using std::runtime_error;
29
solve()30 HighsStatus HQPrimal::solve() {
31 HighsOptions& options = workHMO.options_;
32 HighsSimplexInfo& simplex_info = workHMO.simplex_info_;
33 HighsSimplexLpStatus& simplex_lp_status = workHMO.simplex_lp_status_;
34 workHMO.scaled_model_status_ = HighsModelStatus::NOTSET;
35 // Assumes that the LP has a positive number of rows, since
36 // unconstrained LPs should be solved in solveLpSimplex
37 bool positive_num_row = workHMO.simplex_lp_.numRow_ > 0;
38 assert(positive_num_row);
39 if (!positive_num_row) {
40 HighsLogMessage(options.logfile, HighsMessageType::ERROR,
41 "HPrimal::solve called for LP with non-positive (%d) "
42 "number of constraints",
43 workHMO.simplex_lp_.numRow_);
44 return HighsStatus::Error;
45 }
46 invertHint = INVERT_HINT_NO;
47
48 // Setup aspects of the model data which are needed for solve() but better
49 // left until now for efficiency reasons.
50 // ToDo primal simplex version
51 // setup_for_solve(workHMO);
52
53 // Set SolveBailout to be true if control is to be returned immediately to
54 // calling function
55 // ToDo Move to Simplex
56 // SolveBailout = false;
57
58 // Initialise working environment
59 // Does LOTS, including initialisation of edge weights. Should only
60 // be called if model dimension changes
61 // ToDo primal simplex version
62 // init();
63 // initParallel();
64
65 // ToDo primal simplex version
66 // initialiseCost(workHMO, 1); // model->initCost(1);
67 assert(simplex_lp_status.has_fresh_invert);
68 if (!simplex_lp_status.has_fresh_invert) {
69 printf(
70 "ERROR: Should enter with fresh INVERT - unless no_invert_on_optimal "
71 "is set\n");
72 }
73 // Consider initialising edge weights - create Primal variants
74 //
75 #ifdef HiGHSDEV
76 // printf("simplex_lp_status.has_dual_steepest_edge_weights 2 = %d;
77 // dual_edge_weight_mode = %d; DualEdgeWeightMode::STEEPEST_EDGE = %d\n",
78 // simplex_lp_status.has_dual_steepest_edge_weights,
79 // dual_edge_weight_mode, DualEdgeWeightMode::STEEPEST_EDGE);cout<<flush;
80 // printf("Edge weights known? %d\n",
81 // !simplex_lp_status.has_dual_steepest_edge_weights);cout<<flush;
82 #endif
83 /*
84 if (!simplex_lp_status.has_dual_steepest_edge_weights) {
85 // Edge weights are not known
86 // Set up edge weights according to dual_edge_weight_mode and
87 initialise_dual_steepest_edge_weights
88 // Using dual Devex edge weights
89 // Zero the number of Devex frameworks used and set up the first one
90 devex_index.assign(solver_num_tot, 0);
91 initialiseDevexFramework();
92 // Indicate that edge weights are known
93 simplex_lp_status.has_dual_steepest_edge_weights = true;
94 }
95 */
96
97 // ToDo Determine primal simplex phase from initial primal values
98 //
99 /*
100 computePrimal(workHMO);
101 compute_primal_infeasible_in_??(workHMO, &dualInfeasCount);
102 solvePhase = ??InfeasCount > 0 ? 1 : 2;
103 */
104 solvePhase = 0; // Frig to skip while (solvePhase) {*}
105 assert(simplex_lp_status.has_primal_objective_value);
106 simplex_info.updated_primal_objective_value =
107 simplex_info.primal_objective_value;
108 solve_bailout = false;
109 // Possibly bail out immediately if iteration limit is current value
110 if (bailout()) return HighsStatus::Warning;
111 // Check that the model is OK to solve:
112 //
113 // Level 0 just checks the flags
114 //
115 // Level 1 also checks that the basis is OK and that the necessary
116 // data in work* is populated.
117 //
118 // Level 2 (will) checks things like the nonbasic duals and basic
119 // primal values
120 //
121 // Level 3 (will) checks expensive things like the INVERT and
122 // steepeest edge weights
123 //
124 // ToDo Write primal simplex equivalent
125 /*
126 // ToDo Adapt debugOkForSolve to be used by primal
127 if (debugOkForSolve(workHMO, solvePhase) == HighsDebugStatus::LOGICAL_ERROR)
128 return HighsStatus::Error;
129 */
130 #ifdef HiGHSDEV
131 // reportSimplexLpStatus(simplex_lp_status, "Before HQPrimal major solving
132 // loop");
133 #endif
134 // The major solving loop
135
136 // Initialise the iteration analysis. Necessary for strategy, but
137 // much is for development and only switched on with HiGHSDEV
138 // ToDo Move to simplex and adapt so it's OK for primal and dual
139 // iterationAnalysisInitialise();
140
141 while (solvePhase) {
142 /*
143 int it0 = workHMO.iteration_counts_.simplex;
144 switch (solvePhase) {
145 case 1:
146 analysis->simplexTimerStart(SimplexPrimalPhase1Clock);
147 solvePhase1();
148 analysis->simplexTimerStop(SimplexPrimalPhase1Clock);
149 simplex_info.primal_phase1_iteration_count +=
150 (workHMO.iteration_counts_.simplex - it0); break; case 2:
151 analysis->simplexTimerStart(SimplexPrimalPhase2Clock);
152 solvePhase2();
153 analysis->simplexTimerStop(SimplexPrimalPhase2Clock);
154 simplex_info.primal_phase2_iteration_count +=
155 (workHMO.iteration_counts_.simplex - it0); break; case 4:
156 break; default: solvePhase = 0; break;
157 }
158 // Jump for primal
159 if (solvePhase == 4) break;
160 // Possibly bail out
161 if (SolveBailout) break;
162 */
163 }
164 solvePhase = 2;
165 assert(solve_bailout == false);
166 analysis = &workHMO.simplex_analysis_;
167 if (solvePhase == 2) {
168 int it0 = workHMO.iteration_counts_.simplex;
169
170 analysis->simplexTimerStart(SimplexPrimalPhase2Clock);
171 solvePhase2();
172 analysis->simplexTimerStop(SimplexPrimalPhase2Clock);
173
174 simplex_info.primal_phase2_iteration_count +=
175 (workHMO.iteration_counts_.simplex - it0);
176 if (bailout()) return HighsStatus::Warning;
177 }
178 /*
179 // ToDo Adapt debugOkForSolve to be used by primal
180 if (debugOkForSolve(workHMO, solvePhase) == HighsDebugStatus::LOGICAL_ERROR)
181 return HighsStatus::Error;
182 */
183 return HighsStatus::OK;
184 }
185
solvePhase2()186 void HQPrimal::solvePhase2() {
187 HighsSimplexInfo& simplex_info = workHMO.simplex_info_;
188 HighsSimplexLpStatus& simplex_lp_status = workHMO.simplex_lp_status_;
189 // When starting a new phase the (updated) primal objective function
190 // value isn't known. Indicate this so that when the value
191 // computed from scratch in build() isn't checked against the the
192 // updated value
193 simplex_lp_status.has_primal_objective_value = false;
194 simplex_lp_status.has_dual_objective_value = false;
195 // Set invertHint so that it's assigned when first tested
196 invertHint = INVERT_HINT_NO;
197 // Set solvePhase=2 so it's set if solvePhase2() is called directly
198 solvePhase = 2;
199 solve_bailout = false;
200 // Possibly bail out immediately if iteration limit is current value
201 if (bailout()) return;
202 // Set up local copies of model dimensions
203 solver_num_col = workHMO.simplex_lp_.numCol_;
204 solver_num_row = workHMO.simplex_lp_.numRow_;
205 solver_num_tot = solver_num_col + solver_num_row;
206
207 analysis = &workHMO.simplex_analysis_;
208
209 // Setup update limits
210 simplex_info.update_limit =
211 min(100 + solver_num_row / 100,
212 1000); // TODO: Consider allowing the dual limit to be used
213 simplex_info.update_count = 0;
214
215 // Setup local vectors
216 col_aq.setup(solver_num_row);
217 row_ep.setup(solver_num_row);
218 row_ap.setup(solver_num_col);
219
220 ph1SorterR.reserve(solver_num_row);
221 ph1SorterT.reserve(solver_num_row);
222
223 #ifdef HiGHSDEV
224 printf(
225 "HQPrimal::solvePhase2 - WARNING: Not setting analysis->col_aq_density = "
226 "0\n");
227 printf(
228 "HQPrimal::solvePhase2 - WARNING: Not setting analysis->row_ep_density = "
229 "0\n");
230 #endif
231 // analysis->col_aq_density = 0;
232 // analysis->row_ep_density = 0;
233
234 devexReset();
235
236 no_free_columns = true;
237 for (int iCol = 0; iCol < solver_num_tot; iCol++) {
238 if (highs_isInfinity(-workHMO.simplex_info_.workLower_[iCol])) {
239 if (highs_isInfinity(workHMO.simplex_info_.workUpper_[iCol])) {
240 // Free column
241 no_free_columns = false;
242 break;
243 }
244 }
245 }
246 #ifdef HiGHSDEV
247 if (no_free_columns) {
248 printf("Model has no free columns\n");
249 } else {
250 printf("Model has free columns\n");
251 }
252 #endif
253
254 // Setup other buffers
255
256 HighsPrintMessage(workHMO.options_.output, workHMO.options_.message_level,
257 ML_DETAILED, "primal-phase2-start\n");
258 // Main solving structure
259 for (;;) {
260 analysis->simplexTimerStart(IteratePrimalRebuildClock);
261 primalRebuild();
262 analysis->simplexTimerStop(IteratePrimalRebuildClock);
263
264 if (isPrimalPhase1) {
265 for (;;) {
266 /* Primal phase 1 choose column */
267 phase1ChooseColumn();
268 if (columnIn == -1) {
269 invertHint = INVERT_HINT_CHOOSE_COLUMN_FAIL;
270 break;
271 }
272
273 /* Primal phase 1 choose row */
274 phase1ChooseRow();
275 if (rowOut == -1) {
276 HighsLogMessage(workHMO.options_.logfile, HighsMessageType::ERROR,
277 "Primal phase 1 choose row failed");
278 exit(0);
279 }
280
281 /* Primal phase 1 update */
282 phase1Update();
283 if (invertHint) {
284 break;
285 }
286 if (bailout()) return;
287 }
288 /* Go to the next rebuild */
289 if (invertHint) {
290 /* Stop when the invert is new */
291 if (simplex_lp_status.has_fresh_rebuild) {
292 break;
293 }
294 continue;
295 }
296 }
297 for (;;) {
298 primalChooseColumn();
299 if (columnIn == -1) {
300 invertHint = INVERT_HINT_POSSIBLY_OPTIMAL;
301 break;
302 }
303 primalChooseRow();
304 if (rowOut == -1) {
305 invertHint = INVERT_HINT_POSSIBLY_PRIMAL_UNBOUNDED;
306 break;
307 }
308 primalUpdate();
309 if (bailout()) return;
310 if (invertHint) {
311 break;
312 }
313 }
314 // If the data are fresh from rebuild() and no flips have occurred, break
315 // out of the outer loop to see what's ocurred
316 if (simplex_lp_status.has_fresh_rebuild && num_flip_since_rebuild == 0)
317 break;
318 }
319 // If bailing out, should have returned already
320 assert(!solve_bailout);
321
322 if (columnIn == -1) {
323 HighsPrintMessage(workHMO.options_.output, workHMO.options_.message_level,
324 ML_DETAILED, "primal-optimal\n");
325 HighsPrintMessage(workHMO.options_.output, workHMO.options_.message_level,
326 ML_DETAILED, "problem-optimal\n");
327 workHMO.scaled_model_status_ = HighsModelStatus::OPTIMAL;
328 } else {
329 HighsPrintMessage(workHMO.options_.output, workHMO.options_.message_level,
330 ML_MINIMAL, "primal-unbounded\n");
331 workHMO.scaled_model_status_ = HighsModelStatus::PRIMAL_UNBOUNDED;
332 }
333 computeDualObjectiveValue(workHMO);
334 }
335
primalRebuild()336 void HQPrimal::primalRebuild() {
337 HighsSimplexInfo& simplex_info = workHMO.simplex_info_;
338 HighsSimplexLpStatus& simplex_lp_status = workHMO.simplex_lp_status_;
339
340 // Record whether the update objective value should be tested. If
341 // the objective value is known, then the updated objective value
342 // should be correct - once the correction due to recomputing the
343 // dual values has been applied.
344 //
345 // Note that computePrimalObjectiveValue sets
346 // has_primal_objective_value
347 //
348 // Have to do this before INVERT, as this permutes the indices of
349 // basic variables, and baseValue only corresponds to the new
350 // ordering once computePrimal has been called
351 const bool check_updated_objective_value =
352 simplex_lp_status.has_primal_objective_value;
353 double previous_primal_objective_value;
354 if (check_updated_objective_value) {
355 debugUpdatedObjectiveValue(workHMO, algorithm, solvePhase, "Before INVERT");
356 previous_primal_objective_value =
357 simplex_info.updated_primal_objective_value;
358 } else {
359 // Reset the knowledge of previous objective values
360 debugUpdatedObjectiveValue(workHMO, algorithm, -1, "");
361 }
362
363 // Rebuild workHMO.factor_ - only if we got updates
364 int sv_invertHint = invertHint;
365 invertHint = INVERT_HINT_NO;
366 // Possibly Rebuild workHMO.factor_
367 bool reInvert = simplex_info.update_count > 0;
368 if (!invert_if_row_out_negative) {
369 // Don't reinvert if columnIn is negative [equivalently, if sv_invertHint ==
370 // INVERT_HINT_POSSIBLY_OPTIMAL]
371 if (sv_invertHint == INVERT_HINT_POSSIBLY_OPTIMAL) {
372 assert(columnIn == -1);
373 reInvert = false;
374 }
375 }
376 if (reInvert) {
377 analysis->simplexTimerStart(InvertClock);
378 int rank_deficiency = computeFactor(workHMO);
379 analysis->simplexTimerStop(InvertClock);
380 if (rank_deficiency) {
381 throw runtime_error("Primal reInvert: singular-basis-matrix");
382 }
383 simplex_info.update_count = 0;
384 }
385 analysis->simplexTimerStart(ComputeDualClock);
386 computeDual(workHMO);
387 analysis->simplexTimerStop(ComputeDualClock);
388
389 analysis->simplexTimerStart(ComputePrimalClock);
390 computePrimal(workHMO);
391 analysis->simplexTimerStop(ComputePrimalClock);
392
393 // Primal objective section
394 analysis->simplexTimerStart(ComputePrObjClock);
395 computePrimalObjectiveValue(workHMO);
396 analysis->simplexTimerStop(ComputePrObjClock);
397
398 if (check_updated_objective_value) {
399 // Apply the objective value correction due to computing primal
400 // values from scratch.
401 const double primal_objective_value_correction =
402 simplex_info.primal_objective_value - previous_primal_objective_value;
403 simplex_info.updated_primal_objective_value +=
404 primal_objective_value_correction;
405 debugUpdatedObjectiveValue(workHMO, algorithm);
406 }
407 // Now that there's a new dual_objective_value, reset the updated
408 // value
409 simplex_info.updated_primal_objective_value =
410 simplex_info.primal_objective_value;
411
412 computeSimplexInfeasible(workHMO);
413 // Determine whether simplex_info.num_primal_infeasibilities and
414 // simplex_info.num_dual_infeasibilities can be used
415 copySimplexInfeasible(workHMO);
416
417 /* Whether to switch to primal phase 1 */
418 isPrimalPhase1 = 0;
419 HighsSolutionParams& scaled_solution_params = workHMO.scaled_solution_params_;
420 if (scaled_solution_params.num_primal_infeasibilities > 0) {
421 isPrimalPhase1 = 1;
422 phase1ComputeDual();
423 }
424
425 reportRebuild(sv_invertHint);
426 #ifdef HiGHSDEV
427 if (simplex_info.analyse_rebuild_time) {
428 int total_rebuilds =
429 analysis->simplexTimerNumCall(IteratePrimalRebuildClock);
430 double total_rebuild_time =
431 analysis->simplexTimerRead(IteratePrimalRebuildClock);
432 printf(
433 "Primal rebuild %d (%1d) on iteration %9d: Total rebuild time %g\n",
434 total_rebuilds, sv_invertHint, workHMO.iteration_counts_.simplex,
435 total_rebuild_time);
436 }
437 #endif
438 num_flip_since_rebuild = 0;
439 // Data are fresh from rebuild
440 simplex_lp_status.has_fresh_rebuild = true;
441 }
442
primalChooseColumn()443 void HQPrimal::primalChooseColumn() {
444 HighsRandom& random = workHMO.random_;
445 const int* jFlag = &workHMO.simplex_basis_.nonbasicFlag_[0];
446 const int* jMove = &workHMO.simplex_basis_.nonbasicMove_[0];
447 double* workDual = &workHMO.simplex_info_.workDual_[0];
448 const double* workLower = &workHMO.simplex_info_.workLower_[0];
449 const double* workUpper = &workHMO.simplex_info_.workUpper_[0];
450 const double dualTolerance =
451 workHMO.scaled_solution_params_.dual_feasibility_tolerance;
452
453 analysis->simplexTimerStart(ChuzcPrimalClock);
454 columnIn = -1;
455 double bestInfeas = 0;
456 if (no_free_columns) {
457 const int numSection = 1;
458 int startSection = random.integer() % numSection;
459 int deltaCol = (solver_num_tot + numSection - 1) / numSection;
460 int fromCol = startSection * deltaCol;
461 int toCol = min(fromCol + deltaCol, solver_num_tot);
462 int numPass = 1;
463 // printf("\nstartSection = %1d; deltaCol = %d\n", startSection,
464 // deltaCol);
465 for (;;) {
466 // printf("CHUZC: %1d [%6d, %6d] %6d\n", numPass, fromCol, toCol,
467 // solver_num_tot);
468 for (int iCol = fromCol; iCol < toCol; iCol++) {
469 // Then look at dual infeasible
470 if (jMove[iCol] * workDual[iCol] < -dualTolerance) {
471 if (bestInfeas * devex_weight[iCol] < fabs(workDual[iCol])) {
472 bestInfeas = fabs(workDual[iCol]) / devex_weight[iCol];
473 columnIn = iCol;
474 }
475 }
476 }
477 if (columnIn >= 0 || numPass == numSection) {
478 // printf("Break from CHUZC after %d passes\n", numPass);
479 break;
480 }
481 if (toCol == solver_num_tot) {
482 fromCol = 0;
483 toCol = deltaCol;
484 } else {
485 fromCol = toCol;
486 toCol = min(fromCol + deltaCol, solver_num_tot);
487 }
488 numPass++;
489 }
490 } else {
491 for (int iCol = 0; iCol < solver_num_tot; iCol++) {
492 if (jFlag[iCol] && fabs(workDual[iCol]) > dualTolerance) {
493 // Always take free
494 // TODO: if we found free,
495 // Then deal with it in dual phase 1
496 if (workLower[iCol] <= -HIGHS_CONST_INF &&
497 workUpper[iCol] >= HIGHS_CONST_INF) {
498 columnIn = iCol;
499 break;
500 }
501 // Then look at dual infeasible
502 if (jMove[iCol] * workDual[iCol] < -dualTolerance) {
503 if (bestInfeas * devex_weight[iCol] < fabs(workDual[iCol])) {
504 bestInfeas = fabs(workDual[iCol]) / devex_weight[iCol];
505 columnIn = iCol;
506 }
507 }
508 }
509 }
510 }
511 analysis->simplexTimerStop(ChuzcPrimalClock);
512 }
513
primalChooseRow()514 void HQPrimal::primalChooseRow() {
515 const double* baseLower = &workHMO.simplex_info_.baseLower_[0];
516 const double* baseUpper = &workHMO.simplex_info_.baseUpper_[0];
517 double* baseValue = &workHMO.simplex_info_.baseValue_[0];
518 const double primalTolerance =
519 workHMO.scaled_solution_params_.primal_feasibility_tolerance;
520
521 // Compute pivot column
522 analysis->simplexTimerStart(FtranClock);
523 col_aq.clear();
524 col_aq.packFlag = true;
525 workHMO.matrix_.collect_aj(col_aq, columnIn, 1);
526 #ifdef HiGHSDEV
527 HighsSimplexInfo& simplex_info = workHMO.simplex_info_;
528 if (simplex_info.analyse_iterations)
529 analysis->operationRecordBefore(ANALYSIS_OPERATION_TYPE_FTRAN, col_aq,
530 analysis->col_aq_density);
531 #endif
532 workHMO.factor_.ftran(col_aq, analysis->col_aq_density,
533 analysis->pointer_serial_factor_clocks);
534 analysis->simplexTimerStop(FtranClock);
535 #ifdef HiGHSDEV
536 if (simplex_info.analyse_iterations)
537 analysis->operationRecordAfter(ANALYSIS_OPERATION_TYPE_FTRAN, col_aq);
538 #endif
539
540 const double local_col_aq_density = (double)col_aq.count / solver_num_row;
541 analysis->updateOperationResultDensity(local_col_aq_density,
542 analysis->col_aq_density);
543
544 const bool check_dual = false;
545 if (check_dual) {
546 const double* workCost = &workHMO.simplex_info_.workCost_[0];
547 const double* workDual = &workHMO.simplex_info_.workDual_[0];
548 const int* basicIndex = &workHMO.simplex_basis_.basicIndex_[0];
549 double check_dual_value = workCost[columnIn];
550 for (int i = 0; i < col_aq.count; i++) {
551 int row = col_aq.index[i];
552 int col = basicIndex[row];
553 double value = col_aq.array[row];
554 double cost = workCost[col];
555 check_dual_value -= value * cost;
556 // printf("Entry %2d: [%2d, %12g] Cost = %12g; check_dual_value =
557 // %12g\n", i, row, value, cost, check_dual_value);
558 }
559 thetaDual = workDual[columnIn];
560 double dual_error =
561 fabs(check_dual_value - thetaDual) / max(1.0, fabs(thetaDual));
562 if (dual_error > 1e-8)
563 printf("Checking dual: updated = %12g; direct = %12g; error = %12g\n",
564 thetaDual, check_dual_value, dual_error);
565 }
566
567 analysis->simplexTimerStart(Chuzr1Clock);
568 // Initialize
569 rowOut = -1;
570
571 // Choose row pass 1
572 double alphaTol = workHMO.simplex_info_.update_count < 10
573 ? 1e-9
574 : workHMO.simplex_info_.update_count < 20 ? 1e-8 : 1e-7;
575 const int* jMove = &workHMO.simplex_basis_.nonbasicMove_[0];
576 int moveIn = jMove[columnIn];
577 if (moveIn == 0) {
578 // If there's still free in the N
579 // We would report not-solved
580 // Need to handle free
581 }
582 double relaxTheta = 1e100;
583 double relaxSpace;
584 for (int i = 0; i < col_aq.count; i++) {
585 int index = col_aq.index[i];
586 alpha = col_aq.array[index] * moveIn;
587 if (alpha > alphaTol) {
588 relaxSpace = baseValue[index] - baseLower[index] + primalTolerance;
589 if (relaxSpace < relaxTheta * alpha) relaxTheta = relaxSpace / alpha;
590 } else if (alpha < -alphaTol) {
591 relaxSpace = baseValue[index] - baseUpper[index] - primalTolerance;
592 if (relaxSpace > relaxTheta * alpha) relaxTheta = relaxSpace / alpha;
593 }
594 }
595 analysis->simplexTimerStop(Chuzr1Clock);
596
597 analysis->simplexTimerStart(Chuzr2Clock);
598 double bestAlpha = 0;
599 for (int i = 0; i < col_aq.count; i++) {
600 int index = col_aq.index[i];
601 alpha = col_aq.array[index] * moveIn;
602 if (alpha > alphaTol) {
603 // Positive pivotal column entry
604 double tightSpace = baseValue[index] - baseLower[index];
605 if (tightSpace < relaxTheta * alpha) {
606 if (bestAlpha < alpha) {
607 bestAlpha = alpha;
608 rowOut = index;
609 }
610 }
611 } else if (alpha < -alphaTol) {
612 // Negative pivotal column entry
613 double tightSpace = baseValue[index] - baseUpper[index];
614 if (tightSpace > relaxTheta * alpha) {
615 if (bestAlpha < -alpha) {
616 bestAlpha = -alpha;
617 rowOut = index;
618 }
619 }
620 }
621 }
622 analysis->simplexTimerStop(Chuzr2Clock);
623 }
624
primalUpdate()625 void HQPrimal::primalUpdate() {
626 int* jMove = &workHMO.simplex_basis_.nonbasicMove_[0];
627 double* workDual = &workHMO.simplex_info_.workDual_[0];
628 const double* workLower = &workHMO.simplex_info_.workLower_[0];
629 const double* workUpper = &workHMO.simplex_info_.workUpper_[0];
630 const double* baseLower = &workHMO.simplex_info_.baseLower_[0];
631 const double* baseUpper = &workHMO.simplex_info_.baseUpper_[0];
632 double* workValue = &workHMO.simplex_info_.workValue_[0];
633 double* baseValue = &workHMO.simplex_info_.baseValue_[0];
634 const double primalTolerance =
635 workHMO.scaled_solution_params_.primal_feasibility_tolerance;
636 HighsSimplexInfo& simplex_info = workHMO.simplex_info_;
637
638 // Compute thetaPrimal
639 int moveIn = jMove[columnIn];
640 // int
641 columnOut = workHMO.simplex_basis_.basicIndex_[rowOut];
642 // double
643 alpha = col_aq.array[rowOut];
644 // double
645 thetaPrimal = 0;
646 if (alpha * moveIn > 0) {
647 // Lower bound
648 thetaPrimal = (baseValue[rowOut] - baseLower[rowOut]) / alpha;
649 } else {
650 // Upper bound
651 thetaPrimal = (baseValue[rowOut] - baseUpper[rowOut]) / alpha;
652 }
653
654 // 1. Make sure it is inside bounds or just flip bound
655 double lowerIn = workLower[columnIn];
656 double upperIn = workUpper[columnIn];
657 double valueIn = workValue[columnIn] + thetaPrimal;
658 bool flipped = false;
659 if (jMove[columnIn] == 1) {
660 if (valueIn > upperIn + primalTolerance) {
661 // Flip to upper
662 workValue[columnIn] = upperIn;
663 thetaPrimal = upperIn - lowerIn;
664 flipped = true;
665 jMove[columnIn] = -1;
666 }
667 } else if (jMove[columnIn] == -1) {
668 if (valueIn < lowerIn - primalTolerance) {
669 // Flip to lower
670 workValue[columnIn] = lowerIn;
671 thetaPrimal = lowerIn - upperIn;
672 flipped = true;
673 jMove[columnIn] = 1;
674 }
675 }
676
677 analysis->simplexTimerStart(UpdatePrimalClock);
678 for (int i = 0; i < col_aq.count; i++) {
679 int index = col_aq.index[i];
680 baseValue[index] -= thetaPrimal * col_aq.array[index];
681 }
682 analysis->simplexTimerStop(UpdatePrimalClock);
683
684 simplex_info.updated_primal_objective_value +=
685 workDual[columnIn] * thetaPrimal;
686
687 // Why is the detailed primal infeasibility information needed?
688 computeSimplexPrimalInfeasible(workHMO);
689 copySimplexPrimalInfeasible(workHMO);
690
691 // If flipped, then no need touch the pivots
692 if (flipped) {
693 rowOut = -1;
694 numericalTrouble = 0;
695 thetaDual = workDual[columnIn];
696 iterationAnalysis();
697 num_flip_since_rebuild++;
698 return;
699 }
700
701 // Pivot in
702 int sourceOut = alpha * moveIn > 0 ? -1 : 1;
703 update_pivots(workHMO, columnIn, rowOut, sourceOut);
704
705 baseValue[rowOut] = valueIn;
706
707 analysis->simplexTimerStart(CollectPrIfsClock);
708 // Check for any possible infeasible
709 for (int iRow = 0; iRow < solver_num_row; iRow++) {
710 if (baseValue[iRow] < baseLower[iRow] - primalTolerance) {
711 invertHint = INVERT_HINT_PRIMAL_INFEASIBLE_IN_PRIMAL_SIMPLEX;
712 } else if (baseValue[iRow] > baseUpper[iRow] + primalTolerance) {
713 invertHint = INVERT_HINT_PRIMAL_INFEASIBLE_IN_PRIMAL_SIMPLEX;
714 }
715 }
716 analysis->simplexTimerStop(CollectPrIfsClock);
717
718 // 2. Now we can update the dual
719
720 analysis->simplexTimerStart(BtranClock);
721 row_ep.clear();
722 row_ap.clear();
723 row_ep.count = 1;
724 row_ep.index[0] = rowOut;
725 row_ep.array[rowOut] = 1;
726 row_ep.packFlag = true;
727 #ifdef HiGHSDEV
728 if (simplex_info.analyse_iterations)
729 analysis->operationRecordBefore(ANALYSIS_OPERATION_TYPE_BTRAN_EP, row_ep,
730 analysis->row_ep_density);
731 #endif
732 workHMO.factor_.btran(row_ep, analysis->row_ep_density,
733 analysis->pointer_serial_factor_clocks);
734 #ifdef HiGHSDEV
735 if (simplex_info.analyse_iterations)
736 analysis->operationRecordAfter(ANALYSIS_OPERATION_TYPE_BTRAN_EP, row_ep);
737 #endif
738 analysis->simplexTimerStop(BtranClock);
739 //
740 // PRICE
741 //
742 computeTableauRowFromPiP(workHMO, row_ep, row_ap);
743 /*
744 #ifdef HiGHSDEV
745 if (simplex_info.analyse_iterations) {
746 analysis->operationRecordBefore(ANALYSIS_OPERATION_TYPE_PRICE_AP, row_ep,
747 analysis->row_ap_density); analysis->num_row_price++;
748 }
749 #endif
750 analysis->simplexTimerStart(PriceClock);
751 workHMO.matrix_.priceByRowSparseResult(row_ap, row_ep);
752 analysis->simplexTimerStop(PriceClock);
753 #ifdef HiGHSDEV
754 if (simplex_info.analyse_iterations)
755 analysis->operationRecordAfter(ANALYSIS_OPERATION_TYPE_PRICE_AP, row_ep);
756 #endif
757
758 const double local_row_ep_density = (double)row_ep.count / solver_num_row;
759 analysis->updateOperationResultDensity(local_row_ep_density,
760 analysis->row_ep_density);
761 */
762 analysis->simplexTimerStart(UpdateDualClock);
763 // double
764 thetaDual = workDual[columnIn] / alpha;
765 for (int i = 0; i < row_ap.count; i++) {
766 int iCol = row_ap.index[i];
767 workDual[iCol] -= thetaDual * row_ap.array[iCol];
768 }
769 for (int i = 0; i < row_ep.count; i++) {
770 int iGet = row_ep.index[i];
771 int iCol = iGet + solver_num_col;
772 workDual[iCol] -= thetaDual * row_ep.array[iGet];
773 }
774 analysis->simplexTimerStop(UpdateDualClock);
775
776 /* Update the devex weight */
777 devexUpdate();
778
779 // After dual update in primal simplex the dual objective value is not known
780 workHMO.simplex_lp_status_.has_dual_objective_value = false;
781
782 // updateVerify for primal
783 numericalTrouble = 0;
784 /*
785 double aCol = fabs(alpha);
786 double alphaRow;
787 if (columnIn < workHMO.simplex_lp_.numCol_) {
788 alphaRow = row_ap.array[columnIn];
789 } else {
790 alphaRow = row_ep.array[rowOut];
791 }
792 double aRow = fabs(alphaRow);
793 double aDiff = fabs(aCol - aRow);
794 numericalTrouble = aDiff / min(aCol, aRow);
795 if (numericalTrouble > 1e-7)
796 printf("Numerical check: alphaCol = %12g, alphaRow = a%12g, aDiff = a%12g:
797 measure = %12g\n", alpha, alphaRow, aDiff, numericalTrouble);
798 // Reinvert if the relative difference is large enough, and updates have been
799 performed
800 // if (numericalTrouble > 1e-7 && workHMO.simplex_info_.update_count > 0)
801 invertHint = INVERT_HINT_POSSIBLY_SINGULAR_BASIS;
802 */
803 // Dual for the pivot
804 workDual[columnIn] = 0;
805 workDual[columnOut] = -thetaDual;
806
807 // Update workHMO.factor_ basis
808 update_factor(workHMO, &col_aq, &row_ep, &rowOut, &invertHint);
809 update_matrix(workHMO, columnIn, columnOut);
810 if (simplex_info.update_count >= simplex_info.update_limit) {
811 invertHint = INVERT_HINT_UPDATE_LIMIT_REACHED;
812 }
813 // Move this to Simplex class once it's created
814 // simplex_method.record_pivots(columnIn, columnOut, alpha);
815 workHMO.iteration_counts_.simplex++;
816
817 /* Reset the devex when there are too many errors */
818 if (num_bad_devex_weight > 3) {
819 devexReset();
820 }
821
822 // Report on the iteration
823 iterationAnalysis();
824 }
825
826 /* Compute the reduced cost for primal phase 1 with artificial cost. */
phase1ComputeDual()827 void HQPrimal::phase1ComputeDual() {
828 /* Alias to problem size, tolerance and work arrays */
829 const int nRow = workHMO.lp_.numRow_;
830 const int nCol = workHMO.lp_.numCol_;
831 const double dFeasTol =
832 workHMO.scaled_solution_params_.primal_feasibility_tolerance;
833 const double* baseLower = &workHMO.simplex_info_.baseLower_[0];
834 const double* baseUpper = &workHMO.simplex_info_.baseUpper_[0];
835 const double* baseValue = &workHMO.simplex_info_.baseValue_[0];
836
837 analysis->simplexTimerStart(BtranClock);
838 /* Setup artificial cost and compute pi with BTran */
839 HVector buffer;
840 buffer.setup(nRow);
841 buffer.clear();
842 for (int iRow = 0; iRow < nRow; iRow++) {
843 buffer.index[iRow] = iRow;
844 if (baseValue[iRow] < baseLower[iRow] - dFeasTol) {
845 buffer.array[iRow] = -1.0;
846 } else if (baseValue[iRow] > baseUpper[iRow] + dFeasTol) {
847 buffer.array[iRow] = 1.0;
848 } else {
849 buffer.array[iRow] = 0.0;
850 }
851 }
852 #ifdef HiGHSDEV
853 HighsSimplexInfo& simplex_info = workHMO.simplex_info_;
854 if (simplex_info.analyse_iterations)
855 analysis->operationRecordBefore(ANALYSIS_OPERATION_TYPE_BTRAN_EP, buffer,
856 analysis->row_ep_density);
857 #endif
858 workHMO.factor_.btran(buffer, 1, analysis->pointer_serial_factor_clocks);
859 #ifdef HiGHSDEV
860 if (simplex_info.analyse_iterations)
861 analysis->operationRecordAfter(ANALYSIS_OPERATION_TYPE_BTRAN_EP, buffer);
862 #endif
863 analysis->simplexTimerStop(BtranClock);
864
865 analysis->simplexTimerStart(PriceClock);
866 /* The compute the phase 1 reduced cost for all variables by SpMV */
867 HVector bufferLong;
868 bufferLong.setup(nCol);
869 bufferLong.clear();
870 #ifdef HiGHSDEV
871 if (simplex_info.analyse_iterations) {
872 analysis->operationRecordBefore(ANALYSIS_OPERATION_TYPE_PRICE_AP, buffer,
873 0.0);
874 analysis->num_col_price++;
875 }
876 #endif
877 workHMO.matrix_.priceByColumn(bufferLong, buffer);
878 #ifdef HiGHSDEV
879 if (simplex_info.analyse_iterations)
880 analysis->operationRecordAfter(ANALYSIS_OPERATION_TYPE_PRICE_AP, row_ap);
881 #endif
882 analysis->simplexTimerStop(PriceClock);
883
884 const int* nbFlag = &workHMO.simplex_basis_.nonbasicFlag_[0];
885 double* workDual = &workHMO.simplex_info_.workDual_[0];
886 for (int iSeq = 0; iSeq < nCol + nRow; iSeq++) {
887 workDual[iSeq] = 0.0;
888 }
889 for (int iSeq = 0; iSeq < nCol; iSeq++) {
890 if (nbFlag[iSeq]) workDual[iSeq] = -bufferLong.array[iSeq];
891 }
892 for (int iRow = 0, iSeq = nCol; iRow < nRow; iRow++, iSeq++) {
893 if (nbFlag[iSeq]) workDual[iSeq] = -buffer.array[iRow];
894 }
895
896 /* Recompute number of dual infeasible variables with the phase 1 cost */
897 computeSimplexDualInfeasible(workHMO);
898 // Determine whether simplex_info.num_dual_infeasibilities can be used
899 copySimplexDualInfeasible(workHMO);
900 }
901
902 /* Choose a pivot column for the phase 1 primal simplex method */
phase1ChooseColumn()903 void HQPrimal::phase1ChooseColumn() {
904 const int nSeq = workHMO.lp_.numCol_ + workHMO.lp_.numRow_;
905 const int* nbMove = &workHMO.simplex_basis_.nonbasicMove_[0];
906 const double* workDual = &workHMO.simplex_info_.workDual_[0];
907 const double dDualTol =
908 workHMO.scaled_solution_params_.dual_feasibility_tolerance;
909 analysis->simplexTimerStart(ChuzcPrimalClock);
910 double dBestScore = 0;
911 columnIn = -1;
912 for (int iSeq = 0; iSeq < nSeq; iSeq++) {
913 double dMyDual = nbMove[iSeq] * workDual[iSeq];
914 double dMyScore = dMyDual / devex_weight[iSeq];
915 if (dMyDual < -dDualTol && dMyScore < dBestScore) {
916 dBestScore = dMyScore;
917 columnIn = iSeq;
918 }
919 }
920 analysis->simplexTimerStop(ChuzcPrimalClock);
921 }
922
923 /* Choose a pivot row for the phase 1 primal simplex method */
phase1ChooseRow()924 void HQPrimal::phase1ChooseRow() {
925 /* Alias to work arrays */
926 const double dFeasTol =
927 workHMO.scaled_solution_params_.primal_feasibility_tolerance;
928 const double* baseLower = &workHMO.simplex_info_.baseLower_[0];
929 const double* baseUpper = &workHMO.simplex_info_.baseUpper_[0];
930 const double* baseValue = &workHMO.simplex_info_.baseValue_[0];
931
932 /* Compute the transformed pivot column and update its density */
933 analysis->simplexTimerStart(FtranClock);
934 col_aq.clear();
935 col_aq.packFlag = true;
936 workHMO.matrix_.collect_aj(col_aq, columnIn, 1);
937 #ifdef HiGHSDEV
938 HighsSimplexInfo& simplex_info = workHMO.simplex_info_;
939 if (simplex_info.analyse_iterations)
940 analysis->operationRecordBefore(ANALYSIS_OPERATION_TYPE_FTRAN, col_aq,
941 analysis->col_aq_density);
942 #endif
943 workHMO.factor_.ftran(col_aq, analysis->col_aq_density,
944 analysis->pointer_serial_factor_clocks);
945 analysis->simplexTimerStop(FtranClock);
946 #ifdef HiGHSDEV
947 if (simplex_info.analyse_iterations)
948 analysis->operationRecordAfter(ANALYSIS_OPERATION_TYPE_FTRAN, col_aq);
949 #endif
950
951 const double local_col_aq_density = (double)col_aq.count / solver_num_row;
952 analysis->updateOperationResultDensity(local_col_aq_density,
953 analysis->col_aq_density);
954
955 /* Compute the reduced cost for the pivot column and compare it with the kept
956 * value */
957 double dCompDual = 0.0;
958 for (int i = 0; i < col_aq.count; i++) {
959 int iRow = col_aq.index[i];
960 if (baseValue[iRow] < baseLower[iRow] - dFeasTol) {
961 dCompDual -= col_aq.array[iRow] * -1.0;
962 } else if (baseValue[iRow] > baseUpper[iRow] + dFeasTol) {
963 dCompDual -= col_aq.array[iRow] * +1.0;
964 }
965 }
966 if (fabs(workHMO.simplex_info_.workDual_[columnIn] - dCompDual) >
967 (fabs(dCompDual) + 1.0) * 1e-9) {
968 printf("==> Phase 1 reduced cost. Updated %g, Computed %g\n",
969 workHMO.simplex_info_.workDual_[columnIn], dCompDual);
970 }
971
972 analysis->simplexTimerStart(Chuzr1Clock);
973 /* Collect phase 1 theta lists */
974 int nRow = workHMO.lp_.numRow_;
975 const int iMoveIn = workHMO.simplex_basis_.nonbasicMove_[columnIn];
976 const double dPivotTol =
977 workHMO.simplex_info_.update_count < 10
978 ? 1e-9
979 : workHMO.simplex_info_.update_count < 20 ? 1e-8 : 1e-7;
980 ph1SorterR.clear();
981 ph1SorterT.clear();
982 for (int i = 0; i < col_aq.count; i++) {
983 int iRow = col_aq.index[i];
984 double dAlpha = col_aq.array[iRow] * iMoveIn;
985
986 /* When the basic variable x[i] decrease */
987 if (dAlpha > +dPivotTol) {
988 /* Whether it can become feasible by going below its upper bound */
989 if (baseValue[iRow] > baseUpper[iRow] + dFeasTol) {
990 double dFeasTheta =
991 (baseValue[iRow] - baseUpper[iRow] - dFeasTol) / dAlpha;
992 ph1SorterR.push_back(std::make_pair(dFeasTheta, iRow));
993 ph1SorterT.push_back(std::make_pair(dFeasTheta, iRow));
994 }
995 /* Whether it can become infeasible (again) by going below its lower bound
996 */
997 if (baseValue[iRow] > baseLower[iRow] - dFeasTol &&
998 baseLower[iRow] > -HIGHS_CONST_INF) {
999 double dRelaxTheta =
1000 (baseValue[iRow] - baseLower[iRow] + dFeasTol) / dAlpha;
1001 double dTightTheta = (baseValue[iRow] - baseLower[iRow]) / dAlpha;
1002 ph1SorterR.push_back(std::make_pair(dRelaxTheta, iRow - nRow));
1003 ph1SorterT.push_back(std::make_pair(dTightTheta, iRow - nRow));
1004 }
1005 }
1006
1007 /* When the basic variable x[i] increase */
1008 if (dAlpha < -dPivotTol) {
1009 /* Whether it can become feasible by going above its lower bound */
1010 if (baseValue[iRow] < baseLower[iRow] - dFeasTol) {
1011 double dFeasTheta =
1012 (baseValue[iRow] - baseLower[iRow] + dFeasTol) / dAlpha;
1013 ph1SorterR.push_back(std::make_pair(dFeasTheta, iRow - nRow));
1014 ph1SorterT.push_back(std::make_pair(dFeasTheta, iRow - nRow));
1015 }
1016
1017 /* Whether it can become infeasible (again) by going above its upper bound
1018 */
1019 if (baseValue[iRow] < baseUpper[iRow] + dFeasTol &&
1020 baseUpper[iRow] < +HIGHS_CONST_INF) {
1021 double dRelaxTheta =
1022 (baseValue[iRow] - baseUpper[iRow] - dFeasTol) / dAlpha;
1023 double dTightTheta = (baseValue[iRow] - baseUpper[iRow]) / dAlpha;
1024 ph1SorterR.push_back(std::make_pair(dRelaxTheta, iRow));
1025 ph1SorterT.push_back(std::make_pair(dTightTheta, iRow));
1026 }
1027 }
1028 }
1029
1030 analysis->simplexTimerStop(Chuzr1Clock);
1031 /* When there is no candidates at all, we can leave it here */
1032 if (ph1SorterR.empty()) {
1033 rowOut = -1;
1034 columnOut = -1;
1035 return;
1036 }
1037
1038 /*
1039 * Now sort the relaxed theta to find the final break point.
1040 * TODO: Consider partial sort.
1041 * Or heapify [O(n)] and then pop k points [kO(log(n))].
1042 */
1043 analysis->simplexTimerStart(Chuzr2Clock);
1044 std::sort(ph1SorterR.begin(), ph1SorterR.end());
1045 double dMaxTheta = ph1SorterR.at(0).first;
1046 double dGradient = fabs(workHMO.simplex_info_.workDual_[columnIn]);
1047 for (unsigned int i = 0; i < ph1SorterR.size(); i++) {
1048 double dMyTheta = ph1SorterR.at(i).first;
1049 int index = ph1SorterR.at(i).second;
1050 int iRow = index >= 0 ? index : index + nRow;
1051 dGradient -= fabs(col_aq.array[iRow]);
1052 /* Stop when the gradient start to decrease */
1053 if (dGradient <= 0) {
1054 break;
1055 }
1056 dMaxTheta = dMyTheta;
1057 }
1058
1059 /* Find out the biggest possible alpha for pivot */
1060 std::sort(ph1SorterT.begin(), ph1SorterT.end());
1061 double dMaxAlpha = 0.0;
1062 unsigned int iLast = ph1SorterT.size();
1063 for (unsigned int i = 0; i < ph1SorterT.size(); i++) {
1064 double dMyTheta = ph1SorterT.at(i).first;
1065 int index = ph1SorterT.at(i).second;
1066 int iRow = index >= 0 ? index : index + nRow;
1067 double dAbsAlpha = fabs(col_aq.array[iRow]);
1068 /* Stop when the theta is too large */
1069 if (dMyTheta > dMaxTheta) {
1070 iLast = i;
1071 break;
1072 }
1073 /* Update the maximal possible alpha */
1074 if (dMaxAlpha < dAbsAlpha) {
1075 dMaxAlpha = dAbsAlpha;
1076 }
1077 }
1078
1079 /* Finally choose a pivot with good enough alpha, backwardly */
1080 rowOut = -1;
1081 columnOut = -1;
1082 phase1OutBnd = 0;
1083 for (int i = iLast - 1; i >= 0; i--) {
1084 int index = ph1SorterT.at(i).second;
1085 int iRow = index >= 0 ? index : index + nRow;
1086 double dAbsAlpha = fabs(col_aq.array[iRow]);
1087 if (dAbsAlpha > dMaxAlpha * 0.1) {
1088 rowOut = iRow;
1089 phase1OutBnd = index > 0 ? 1 : -1;
1090 break;
1091 }
1092 }
1093 if (rowOut != -1) {
1094 columnOut = workHMO.simplex_basis_.basicIndex_[rowOut];
1095 }
1096 analysis->simplexTimerStop(Chuzr2Clock);
1097 }
1098
1099 /* Update the primal and dual solutions for the primal phase 1 */
phase1Update()1100 void HQPrimal::phase1Update() {
1101 /* Alias to bounds arrays */
1102 const double* workLower = &workHMO.simplex_info_.workLower_[0];
1103 const double* workUpper = &workHMO.simplex_info_.workUpper_[0];
1104 const double* baseLower = &workHMO.simplex_info_.baseLower_[0];
1105 const double* baseUpper = &workHMO.simplex_info_.baseUpper_[0];
1106 double* workValue = &workHMO.simplex_info_.workValue_[0];
1107 double* baseValue = &workHMO.simplex_info_.baseValue_[0];
1108 const int iMoveIn = workHMO.simplex_basis_.nonbasicMove_[columnIn];
1109 const double dFeasTol =
1110 workHMO.scaled_solution_params_.primal_feasibility_tolerance;
1111
1112 /* Compute the primal theta and see if we should have do bound flip instead */
1113 alpha = col_aq.array[rowOut];
1114 thetaPrimal = 0.0;
1115 if (phase1OutBnd == 1) {
1116 thetaPrimal = (baseValue[rowOut] - baseUpper[rowOut]) / alpha;
1117 } else {
1118 thetaPrimal = (baseValue[rowOut] - baseLower[rowOut]) / alpha;
1119 }
1120
1121 double lowerIn = workLower[columnIn];
1122 double upperIn = workUpper[columnIn];
1123 double valueIn = workValue[columnIn] + thetaPrimal;
1124 int ifFlip = 0;
1125 if (iMoveIn == +1 && valueIn > upperIn + dFeasTol) {
1126 workValue[columnIn] = upperIn;
1127 thetaPrimal = upperIn - lowerIn;
1128 ifFlip = 1;
1129 workHMO.simplex_basis_.nonbasicMove_[columnIn] = -1;
1130 }
1131 if (iMoveIn == -1 && valueIn < lowerIn - dFeasTol) {
1132 workValue[columnIn] = lowerIn;
1133 thetaPrimal = lowerIn - upperIn;
1134 ifFlip = 1;
1135 workHMO.simplex_basis_.nonbasicMove_[columnIn] = +1;
1136 }
1137
1138 /* Update for the flip case */
1139 if (ifFlip) {
1140 /* Recompute things on flip */
1141 if (invertHint == 0) {
1142 analysis->simplexTimerStart(ComputePrimalClock);
1143 computePrimal(workHMO);
1144 analysis->simplexTimerStop(ComputePrimalClock);
1145 computeSimplexPrimalInfeasible(workHMO);
1146 // Assumes that only simplex_info_.*_primal_infeasibilities is
1147 // needed - probably only num_primal_infeasibilities
1148 //
1149 // copySimplexPrimalInfeasible(workHMO);
1150 if (workHMO.simplex_info_.num_primal_infeasibilities > 0) {
1151 isPrimalPhase1 = 1;
1152 analysis->simplexTimerStart(ComputeDualClock);
1153 phase1ComputeDual();
1154 analysis->simplexTimerStop(ComputeDualClock);
1155 } else {
1156 invertHint = INVERT_HINT_UPDATE_LIMIT_REACHED;
1157 }
1158 }
1159 return;
1160 }
1161
1162 /* Compute BTran for update LU */
1163 analysis->simplexTimerStart(BtranClock);
1164 row_ep.clear();
1165 row_ep.count = 1;
1166 row_ep.index[0] = rowOut;
1167 row_ep.array[rowOut] = 1;
1168 row_ep.packFlag = true;
1169 #ifdef HiGHSDEV
1170 HighsSimplexInfo& simplex_info = workHMO.simplex_info_;
1171 if (simplex_info.analyse_iterations)
1172 analysis->operationRecordBefore(ANALYSIS_OPERATION_TYPE_BTRAN_EP, row_ep,
1173 analysis->row_ep_density);
1174 #endif
1175 workHMO.factor_.btran(row_ep, analysis->row_ep_density,
1176 analysis->pointer_serial_factor_clocks);
1177 #ifdef HiGHSDEV
1178 if (simplex_info.analyse_iterations)
1179 analysis->operationRecordAfter(ANALYSIS_OPERATION_TYPE_BTRAN_EP, row_ep);
1180 #endif
1181 analysis->simplexTimerStop(BtranClock);
1182
1183 const double local_row_ep_density = (double)row_ep.count / solver_num_row;
1184 analysis->updateOperationResultDensity(local_row_ep_density,
1185 analysis->row_ep_density);
1186
1187 /* Compute the whole pivot row for updating the devex weight */
1188 #ifdef HiGHSDEV
1189 if (simplex_info.analyse_iterations) {
1190 analysis->operationRecordBefore(ANALYSIS_OPERATION_TYPE_PRICE_AP, row_ep,
1191 analysis->row_ap_density);
1192 analysis->num_row_price++;
1193 }
1194 #endif
1195 analysis->simplexTimerStart(PriceClock);
1196 row_ap.clear();
1197 workHMO.matrix_.priceByRowSparseResult(row_ap, row_ep);
1198 analysis->simplexTimerStop(PriceClock);
1199 #ifdef HiGHSDEV
1200 if (simplex_info.analyse_iterations)
1201 analysis->operationRecordAfter(ANALYSIS_OPERATION_TYPE_PRICE_AP, row_ep);
1202 #endif
1203
1204 /* Update the devex weight */
1205 devexUpdate();
1206
1207 /* Update other things */
1208 update_pivots(workHMO, columnIn, rowOut, phase1OutBnd);
1209 update_factor(workHMO, &col_aq, &row_ep, &rowOut, &invertHint);
1210 update_matrix(workHMO, columnIn, columnOut);
1211 if (workHMO.simplex_info_.update_count >=
1212 workHMO.simplex_info_.update_limit) {
1213 invertHint = INVERT_HINT_UPDATE_LIMIT_REACHED;
1214 }
1215
1216 /* Recompute dual and primal */
1217 if (invertHint == 0) {
1218 analysis->simplexTimerStart(ComputePrimalClock);
1219 computePrimal(workHMO);
1220 analysis->simplexTimerStop(ComputePrimalClock);
1221 computeSimplexPrimalInfeasible(workHMO);
1222 // Assumes that only simplex_info_.*_primal_infeasibilities is
1223 // needed - probably only num_primal_infeasibilities
1224 //
1225 // copySimplexPrimalInfeasible(workHMO);
1226 if (workHMO.simplex_info_.num_primal_infeasibilities > 0) {
1227 isPrimalPhase1 = 1;
1228 analysis->simplexTimerStart(ComputeDualClock);
1229 phase1ComputeDual();
1230 analysis->simplexTimerStop(ComputeDualClock);
1231 } else {
1232 invertHint = INVERT_HINT_UPDATE_LIMIT_REACHED;
1233 }
1234 }
1235
1236 /* Reset the devex framework when necessary */
1237 if (num_bad_devex_weight > 3) {
1238 devexReset();
1239 }
1240
1241 // Move this to Simplex class once it's created
1242 // simplex_method.record_pivots(columnIn, columnOut, alpha);
1243 workHMO.iteration_counts_.simplex++;
1244 }
1245
1246 /* Reset the devex weight */
devexReset()1247 void HQPrimal::devexReset() {
1248 const int nSeq = workHMO.lp_.numCol_ + workHMO.lp_.numRow_;
1249 devex_weight.assign(nSeq, 1.0);
1250 devex_index.assign(nSeq, 0);
1251 for (int iSeq = 0; iSeq < nSeq; iSeq++) {
1252 const int nonbasicFlag = workHMO.simplex_basis_.nonbasicFlag_[iSeq];
1253 devex_index[iSeq] = nonbasicFlag * nonbasicFlag;
1254 }
1255 num_devex_iterations = 0;
1256 num_bad_devex_weight = 0;
1257 }
1258
devexUpdate()1259 void HQPrimal::devexUpdate() {
1260 /* Compute the pivot weight from the reference set */
1261 analysis->simplexTimerStart(DevexUpdateWeightClock);
1262 double dPivotWeight = 0.0;
1263 for (int i = 0; i < col_aq.count; i++) {
1264 int iRow = col_aq.index[i];
1265 int iSeq = workHMO.simplex_basis_.basicIndex_[iRow];
1266 double dAlpha = devex_index[iSeq] * col_aq.array[iRow];
1267 dPivotWeight += dAlpha * dAlpha;
1268 }
1269 dPivotWeight += devex_index[columnIn] * 1.0;
1270 dPivotWeight = sqrt(dPivotWeight);
1271
1272 /* Check if the saved weight is too large */
1273 if (devex_weight[columnIn] > 3.0 * dPivotWeight) {
1274 num_bad_devex_weight++;
1275 }
1276
1277 /* Update the devex weight for all */
1278 double dPivot = col_aq.array[rowOut];
1279 dPivotWeight /= fabs(dPivot);
1280
1281 for (int i = 0; i < row_ap.count; i++) {
1282 int iSeq = row_ap.index[i];
1283 double alpha = row_ap.array[iSeq];
1284 double devex = dPivotWeight * fabs(alpha);
1285 devex += devex_index[iSeq] * 1.0;
1286 if (devex_weight[iSeq] < devex) {
1287 devex_weight[iSeq] = devex;
1288 }
1289 }
1290 for (int i = 0; i < row_ep.count; i++) {
1291 int iPtr = row_ep.index[i];
1292 int iSeq = row_ep.index[i] + solver_num_col;
1293 double alpha = row_ep.array[iPtr];
1294 double devex = dPivotWeight * fabs(alpha);
1295 devex += devex_index[iSeq] * 1.0;
1296 if (devex_weight[iSeq] < devex) {
1297 devex_weight[iSeq] = devex;
1298 }
1299 }
1300
1301 /* Update devex weight for the pivots */
1302 devex_weight[columnOut] = max(1.0, dPivotWeight);
1303 devex_weight[columnIn] = 1.0;
1304 num_devex_iterations++;
1305 analysis->simplexTimerStop(DevexUpdateWeightClock);
1306 }
1307
iterationAnalysisData()1308 void HQPrimal::iterationAnalysisData() {
1309 // HighsSolutionParams& scaled_solution_params =
1310 // workHMO.scaled_solution_params_;
1311 HighsSimplexInfo& simplex_info = workHMO.simplex_info_;
1312 analysis->simplex_strategy = SIMPLEX_STRATEGY_PRIMAL;
1313 analysis->edge_weight_mode = DualEdgeWeightMode::DEVEX;
1314 analysis->solve_phase = solvePhase;
1315 analysis->simplex_iteration_count = workHMO.iteration_counts_.simplex;
1316 analysis->devex_iteration_count = num_devex_iterations;
1317 analysis->pivotal_row_index = rowOut;
1318 analysis->leaving_variable = columnOut;
1319 analysis->entering_variable = columnIn;
1320 analysis->invert_hint = invertHint;
1321 analysis->reduced_rhs_value = 0;
1322 analysis->reduced_cost_value = 0;
1323 analysis->edge_weight = 0;
1324 analysis->primal_delta = 0;
1325 analysis->primal_step = thetaPrimal;
1326 analysis->dual_step = thetaDual;
1327 analysis->pivot_value_from_column = alpha;
1328 analysis->pivot_value_from_row = alpha; // Row;
1329 analysis->numerical_trouble = numericalTrouble;
1330 analysis->objective_value = simplex_info.updated_primal_objective_value;
1331 analysis->num_primal_infeasibilities =
1332 simplex_info.num_primal_infeasibilities;
1333 analysis->num_dual_infeasibilities = simplex_info.num_dual_infeasibilities;
1334 analysis->sum_primal_infeasibilities =
1335 simplex_info.sum_primal_infeasibilities;
1336 analysis->sum_dual_infeasibilities = simplex_info.sum_dual_infeasibilities;
1337 #ifdef HiGHSDEV
1338 analysis->basis_condition = simplex_info.invert_condition;
1339 #endif
1340 if ((analysis->edge_weight_mode == DualEdgeWeightMode::DEVEX) &&
1341 (num_devex_iterations == 0))
1342 analysis->num_devex_framework++;
1343 }
1344
iterationAnalysis()1345 void HQPrimal::iterationAnalysis() {
1346 // Possibly report on the iteration
1347 iterationAnalysisData();
1348 analysis->iterationReport();
1349
1350 #ifdef HiGHSDEV
1351 analysis->iterationRecord();
1352 #endif
1353 }
1354
reportRebuild(const int rebuild_invert_hint)1355 void HQPrimal::reportRebuild(const int rebuild_invert_hint) {
1356 analysis->simplexTimerStart(ReportRebuildClock);
1357 iterationAnalysisData();
1358 analysis->invert_hint = rebuild_invert_hint;
1359 analysis->invertReport();
1360 analysis->simplexTimerStop(ReportRebuildClock);
1361 }
1362
bailout()1363 bool HQPrimal::bailout() {
1364 if (solve_bailout) {
1365 // Bailout has already been decided: check that it's for one of these
1366 // reasons
1367 assert(workHMO.scaled_model_status_ ==
1368 HighsModelStatus::REACHED_TIME_LIMIT ||
1369 workHMO.scaled_model_status_ ==
1370 HighsModelStatus::REACHED_ITERATION_LIMIT);
1371 } else if (workHMO.timer_.readRunHighsClock() > workHMO.options_.time_limit) {
1372 solve_bailout = true;
1373 workHMO.scaled_model_status_ = HighsModelStatus::REACHED_TIME_LIMIT;
1374 } else if (workHMO.iteration_counts_.simplex >=
1375 workHMO.options_.simplex_iteration_limit) {
1376 solve_bailout = true;
1377 workHMO.scaled_model_status_ = HighsModelStatus::REACHED_ITERATION_LIMIT;
1378 }
1379 return solve_bailout;
1380 }
1381