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