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