1 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2 /*                                                                       */
3 /*    This file is part of the HiGHS linear optimization suite           */
4 /*                                                                       */
5 /*    Written and engineered 2008-2021 at the University of Edinburgh    */
6 /*                                                                       */
7 /*    Available as open-source under the MIT License                     */
8 /*                                                                       */
9 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
10 /**@file simplex/HDual.h
11  * @brief Dual simplex solver for HiGHS
12  * @author Julian Hall, Ivet Galabova, Qi Huangfu and Michael Feldmeier
13  */
14 #ifndef SIMPLEX_HDUAL_H_
15 #define SIMPLEX_HDUAL_H_
16 
17 #include <set>
18 #include <string>
19 #include <vector>
20 
21 #include "HConfig.h"
22 #include "lp_data/HighsModelObject.h"
23 #include "simplex/HCrash.h"
24 #include "simplex/HDualRHS.h"
25 #include "simplex/HDualRow.h"
26 #include "simplex/HSimplex.h"
27 #include "simplex/HVector.h"
28 
29 class HFactor;
30 
31 /**
32  * Limit on the number of column slices for parallel calculations. SIP uses
33  * num_threads-2 slices; PAMI uses num_threads-1 slices
34  */
35 const int HIGHS_SLICED_LIMIT =
36     HIGHS_THREAD_LIMIT;  // Was 100, but can't see why this should be higher
37                          // than HIGHS_THREAD_LIMIT;
38 
39 /**
40  * Parameters controlling number of Devex iterations.
41  *
42  * There is a new Devex framework if either
43  *
44  * 1) The weight inaccuracy ratio exceeds maxAllowedDevexWeightRatio
45  *
46  * 2) There have been max(minAbsNumberDevexIterations,
47  * numRow/minRlvNumberDevexIterations) Devex iterations
48  */
49 const int minAbsNumberDevexIterations = 25;
50 const double minRlvNumberDevexIterations = 1e-2;
51 const double maxAllowedDevexWeightRatio = 3.0;
52 
53 /**
54  * Multiplier used in running average calculations
55  */
56 const double runningAverageMu = 0.05;
57 
58 /**
59  * Candidate persistence cut-off in PAMI
60  */
61 const double pami_cutoff = 0.95;
62 
63 /**
64  * @brief Dual simplex solver for HiGHS
65  */
66 class HDual {
67  public:
HDual(HighsModelObject & model_object)68   HDual(HighsModelObject& model_object)
69       : workHMO(model_object), dualRow(model_object), dualRHS(model_object) {
70     dualRow.setup();
71     for (int i = 0; i < HIGHS_SLICED_LIMIT; i++)
72       slice_dualRow.push_back(HDualRow(model_object));
73     dualRHS.setup();
74   }
75 
76   /**
77    * @brief Solve a model instance
78    */
79   HighsStatus solve();
80 
81   const SimplexAlgorithm algorithm = SimplexAlgorithm::DUAL;
82 
83  public:
84   /**
85    * @brief Set solver options from simplex options
86    */
87   void options();
88   /**
89    * @brief Initialise a dual simplex instance
90    *
91    * Copy dimensions and pointers to matrix, factor and solver-related
92    * model data, plus tolerances. Sets up local std::vectors (columnDSE,
93    * columnBFRT, column, row_ep and row_ap), scalars for their average
94    * density and buffers for dualRow and dualRHS.
95    */
96   void init();
97 
98   /**
99    * @brief Initialise parallel aspects of a dual simplex instance
100    *
101    * Sets up data structures for SIP or PAMI
102    */
103   void initParallel();
104 
105   /**
106    * @brief Initialise matrix slices and slices of row_ap or dualRow for SIP or
107    * PAMI
108    *
109    * TODO generalise call slice_matrix[i].setup_lgBs so slice can be
110    * used with non-logical initial basis
111    */
112   void initSlice(
113       const int init_sliced_num  //!< Ideal number of slices - true number
114                                  //!< is modified in light of limits
115   );
116 
117   /**
118    * @brief Perform Phase 1 dual simplex iterations
119    */
120   void solvePhase1();
121 
122   /**
123    * @brief Perform Phase 2 dual simplex iterations
124    */
125   void solvePhase2();
126 
127   /**
128    * @brief Reinvert if INVERT not fresh, then recompute dual and primal values
129    *
130    * Also collects primal infeasibilities and computes the dual objective value
131    */
132 
133   void rebuild();
134 
135   /**
136    * @brief Remove perturbation and recompute the dual solution
137    *
138    * Also collects primal infeasibilities and computes the dual objective value
139    */
140   void cleanup();
141 
142   /**
143    * @brief Perform a single serial dual simplex iteration
144    *
145    * All the methods it calls have as their first line "if (invertHint)
146    * return;", where invertHint is, for example, set to 1 when CHUZR
147    * finds no candidate. This causes a break from the inner loop of
148    * solve_phase% and, hence, a call to rebuild().
149    */
150   void iterate();
151 
152   /**
153    * @brief Perform a single SIP dual simplex iteration
154    */
155   void iterateTasks();
156 
157   /**
158    * @brief Perform a single PAMI dual simplex iteration - source code in
159    * HDualMulti.cpp
160    */
161   void iterateMulti();  // in HDualMulti.cpp
162 
163   /**
164    * @brief Pass the data for the serial iteration analysis, report and rebuild
165    * report
166    */
167   void iterationAnalysisData();
168 
169   /**
170    * @brief Perform the serial iteration analysis
171    */
172   void iterationAnalysis();
173 
174   /**
175    * @brief Pass the data for the PAMI iteration analysis for a minor iteration,
176    * report and rebuild report
177    */
178   void iterationAnalysisMinorData();
179 
180   /**
181    * @brief Perform the PAMI iteration analysis for a minor iteration
182    */
183   void iterationAnalysisMinor();
184 
185   /**
186    * @brief Pass the data for the PAMI iteration analysis for a major iteration
187    */
188   void iterationAnalysisMajorData();
189 
190   /**
191    * @brief Perform the PAMI iteration analysis for a major iteration
192    */
193   void iterationAnalysisMajor();
194 
195   /**
196    * @brief Single line report after rebuild
197    */
198   void reportRebuild(const int rebuild_invert_hint = -1);
199 
200   /**
201    * @brief Choose the index of a good row to leave the basis (CHUZR)
202    */
203   void chooseRow();
204 
205   /**
206    * @brief Determine whether the updated_edge_weight is accurate enough to
207    * be accepted, and update the analysis of weight errors
208    */
209   bool acceptDualSteepestEdgeWeight(const double updated_edge_weight);
210 
211   /**
212    * @brief Determine whether the updated_edge_weight error should trigger a new
213    * Devex framework
214    */
215   bool newDevexFramework(const double updated_edge_weight);
216 
217   /**
218    * @brief Compute pivot row (PRICE) and choose the index of a good column to
219    * enter the basis (CHUZC)
220    */
221   void chooseColumn(HVector* row_ep);
222 
223   /**
224    * @brief Choose the index of a good column to enter the basis (CHUZC) by
225    * exploiting slices of the pivotal row - for SIP and PAMI
226    */
227   void chooseColumnSlice(HVector* row_ep);
228 
229   /**
230    * @brief Compute the pivotal column (FTRAN)
231    */
232   void updateFtran();
233 
234   /**
235    * @brief Compute the RHS changes corresponding to the BFRT
236    * (FTRAN-BFRT)
237    */
238   void updateFtranBFRT();
239 
240   /**
241    * @brief Compute the std::vector required to update DSE weights - being
242    * FTRAN applied to the pivotal column (FTRAN-DSE)
243    */
244   void updateFtranDSE(HVector* DSE_Vector  //!< Pivotal column as RHS for FTRAN
245   );
246   /**
247    * @brief Compare the pivot value computed row-wise and column-wise
248    * and determine whether reinversion is advisable
249    */
250   void updateVerify();
251 
252   /**
253    * @brief Update the dual values
254    */
255   void updateDual();
256 
257   /**
258    * @brief Update the primal values and any edge weights
259    */
260   void updatePrimal(HVector* DSE_Vector  //!< FTRANned pivotal column
261   );
262 
263   /**
264    * @brief Update the basic and nonbasic variables, iteration count,
265    * invertible representation of the basis matrix and row-wise
266    * representation of the nonbasic columns, delete the Freelist entry
267    * for the entering column, update the primal value for the row
268    * where the basis change has occurred, and set the corresponding
269    * primal infeasibility value in dualRHS.work_infeasibility, and
270    * then determine whether to reinvert according to the synthetic
271    * clock
272    */
273   void updatePivots();
274 
275   /**
276    * @brief Initialise a Devex framework: reference set is all basic
277    * variables
278    */
279   void initialiseDevexFramework(const bool parallel = false);
280 
281   /**
282    * @brief Interpret the dual edge weight strategy as setting of a mode and
283    * other actions
284    */
285   void interpretDualEdgeWeightStrategy(
286       const int simplex_dual_edge_weight_strategy);
287 
288   bool reachedExactDualObjectiveValueUpperBound();
289   double computeExactDualObjectiveValue();
290 
291   /**
292    * @brief PAMI: Choose the indices of a good set of rows to leave the
293    * basis (CHUZR)
294    */
295   void majorChooseRow();
296 
297   /**
298    * @brief PAMI: Perform multiple BTRAN
299    */
300   void majorChooseRowBtran();
301 
302   /**
303    * @brief PAMI: Choose the index (from the set of indices) of a good
304    * row to leave the basis (CHUZR-MI)
305    */
306   void minorChooseRow();
307 
308   /**
309    * @brief PAMI: Update the data during minor iterations
310    */
311   void minorUpdate();
312 
313   /**
314    * @brief PAMI: Update the dual values during minor iterations
315    */
316   void minorUpdateDual();
317 
318   /**
319    * @brief PAMI: Update the primal values during minor iterations
320    */
321   void minorUpdatePrimal();
322 
323   /**
324    * @brief PAMI: Perform a basis change during minor iterations
325    */
326   void minorUpdatePivots();
327 
328   /**
329    * @brief PAMI: Update the tableau rows during minor iterations
330    */
331   void minorUpdateRows();
332 
333   /**
334    * @brief PAMI: Initialise a new Devex framework during minor iterations
335    */
336   void minorInitialiseDevexFramework();
337 
338   /**
339    * @brief PAMI: Perform updates after a set of minor iterations
340    */
341   void majorUpdate();
342 
343   /**
344    * @brief PAMI: Prepare for the FTRANs after a set of minor iterations
345    */
346   void majorUpdateFtranPrepare();
347 
348   /**
349    * @brief PAMI: Perform the parallel part of multiple FTRANs after a
350    * set of minor iterations
351    */
352   void majorUpdateFtranParallel();
353 
354   /**
355    * @brief PAMI: Perform the final part of multiple FTRANs after a set
356    * of minor iterations
357    */
358   void majorUpdateFtranFinal();
359 
360   /**
361    * @brief PAMI: Update the primal values after a set of minor
362    * iterations
363    */
364   void majorUpdatePrimal();
365 
366   /**
367    * @brief PAMI: Update the invertible representation of the basis
368    * matrix after a set of minor iterations
369    */
370   void majorUpdateFactor();
371 
372   /**
373    * @brief PAMI: Roll back some iterations if numerical trouble
374    * detected when updating the invertible representation of the basis
375    * matrix after a set of minor iterations
376    */
377   void majorRollback();
378 
379   // private:
380   HighsStatus returnFromSolve(const HighsStatus return_status);
381   void saveDualRay();
382   bool getNonsingularInverse();
383   bool getBacktrackingBasis(vector<double>& scattered_edge_weights);
384   void putBacktrackingBasis();
385   void putBacktrackingBasis(const vector<int>& basicIndex_before_compute_factor,
386                             const vector<double>& scattered_edge_weights);
387 
388   void assessPhase1Optimality();
389   void exitPhase1ResetDuals();
390   void reportOnPossibleLpDualInfeasibility();
391 
392   bool checkNonUnitWeightError(std::string message);
393   bool dualInfoOk(const HighsLp& lp);
394   bool bailoutReturn();
395   bool bailoutOnTimeIterations();
396   bool bailoutOnDualObjective();
397 
398   bool solve_bailout;  //!< Set true if control is to be returned immediately to
399                        //!< calling function
400 
401   // Devex scalars
402   int num_devex_iterations =
403       0;  //!< Number of Devex iterations with the current framework
404   bool new_devex_framework = false;  //!< Set a new Devex framework
405   bool minor_new_devex_framework =
406       false;  //!< Set a new Devex framework in PAMI minor iterations
407 
408   // Model
409   HighsModelObject& workHMO;
410   int solver_num_row;
411   int solver_num_col;
412   int solver_num_tot;
413 
414   const HMatrix* matrix;
415   const HFactor* factor;
416   HighsSimplexAnalysis* analysis;
417 
418   const int* jMove;
419   const double* workRange;
420   const double* baseLower;
421   const double* baseUpper;
422   double* baseValue;
423   double* workDual;
424   double* workValue;
425   double* colLower;
426   double* colUpper;
427   double* rowLower;
428   double* rowUpper;
429   int* nonbasicFlag;
430 
431   // Options
432   DualEdgeWeightMode dual_edge_weight_mode;
433   bool initialise_dual_steepest_edge_weights;
434   bool allow_dual_steepest_edge_to_devex_switch;
435 
436   const double min_dual_steepest_edge_weight = 1e-4;
437 
438   double Tp;  // Tolerance for primal
439   double primal_feasibility_tolerance;
440 
441   double Td;  // Tolerance for dual
442   double dual_feasibility_tolerance;
443   double dual_objective_value_upper_bound;
444 
445   int solvePhase;
446   int invertHint;
447 
448   HVector row_ep;
449   HVector row_ap;
450   HVector col_aq;
451   HVector col_BFRT;
452   HVector col_DSE;
453 
454   HDualRow dualRow;
455 
456   // Solving related buffers
457   int dualInfeasCount;
458 
459   HDualRHS dualRHS;
460 
461   // Simplex pivotal information
462   int rowOut;
463   int columnOut;
464   int sourceOut;  // -1 from small to lower, +1 to upper
465   int columnIn;
466   double deltaPrimal;
467   double thetaDual;
468   double thetaPrimal;
469   double alpha;
470   double alphaRow;
471   double numericalTrouble;
472   // (Local) value of computed weight
473   double computed_edge_weight;
474 
475   bool check_invert_condition = false;
476 
477   // Partitioned coefficient matrix
478   int slice_num;
479   int slice_PRICE;
480   int slice_start[HIGHS_SLICED_LIMIT + 1];
481   HMatrix slice_matrix[HIGHS_SLICED_LIMIT];
482   HVector slice_row_ap[HIGHS_SLICED_LIMIT];
483   std::vector<HDualRow> slice_dualRow;
484 
485   /**
486    * @brief Multiple CHUZR data
487    */
488   struct MChoice {
489     int rowOut;
490     double baseValue;
491     double baseLower;
492     double baseUpper;
493     double infeasValue;
494     double infeasEdWt;
495     double infeasLimit;
496     HVector row_ep;
497     HVector col_aq;
498     HVector col_BFRT;
499   };
500 
501   /**
502    * @brief Multiple minor iteration data
503    */
504   struct MFinish {
505     int moveIn;
506     double shiftOut;
507     std::vector<int> flipList;
508 
509     int rowOut;
510     int columnOut;
511     int columnIn;
512     double alphaRow;
513     double thetaPrimal;
514     double basicBound;
515     double basicValue;
516     double EdWt;
517     HVector_ptr row_ep;
518     HVector_ptr col_aq;
519     HVector_ptr col_BFRT;
520   };
521 
522   int multi_num;
523   int multi_chosen;
524   int multi_iChoice;
525   int multi_nFinish;
526   int multi_iteration;
527   int multi_chooseAgain;
528   MChoice multi_choice[HIGHS_THREAD_LIMIT];
529   MFinish multi_finish[HIGHS_THREAD_LIMIT];
530 
531 #ifdef HiGHSDEV
532   const bool rp_reinvert_syntheticClock = false;  // true;//
533   const bool rp_numericalTrouble = false;         // true;//
534 #endif
535   const double original_multi_build_syntheticTick_mu = 1.5;
536   const double multi_build_syntheticTick_mu = 1.0;
537   // original_multi_build_syntheticTick_mu;//
538   const double numerical_trouble_tolerance = 1e-7;
539   const double original_multi_numerical_trouble_tolerance = 1e-8;
540   const double multi_numerical_trouble_tolerance = 1e-7;
541   // original_multi_numerical_trouble_tolerance;
542 
543   const int synthetic_tick_reinversion_min_update_count = 50;
544   const int original_multi_synthetic_tick_reinversion_min_update_count = 201;
545   const int multi_synthetic_tick_reinversion_min_update_count =
546       synthetic_tick_reinversion_min_update_count;
547   // original_multi_synthetic_tick_reinversion_min_update_count;
548 
549   double build_syntheticTick;
550   double total_syntheticTick;
551 };
552 
553 #endif /* SIMPLEX_HDUAL_H_ */
554