@@ -909,6 +909,158 @@ TEST_CASE("presolve-issue-2874", "[highs_test_presolve]") {
909909 highs.readModel (model_file);
910910 REQUIRE (highs.presolve () == HighsStatus::kOk );
911911 REQUIRE (highs.getModelPresolveStatus () == HighsPresolveStatus::kInfeasible );
912+
913+ highs.resetGlobalScheduler (true );
914+ }
915+
916+ TEST_CASE (" presolve-light" , " [highs_test_presolve]" ) {
917+ std::string model_file =
918+ std::string (HIGHS_DIR ) + " /check/instances/afiro.mps" ;
919+ Highs highs;
920+ highs.setOptionValue (" output_flag" , dev_run);
921+ highs.readModel (model_file);
922+ HighsInt presolved_num_col;
923+ HighsInt presolved_num_row;
924+ HighsInt presolved_num_nz;
925+ for (HighsInt k = 0 ; k < 2 ; k++) {
926+ REQUIRE (highs.presolve () == HighsStatus::kOk );
927+ REQUIRE (highs.getModelPresolveStatus () == HighsPresolveStatus::kReduced );
928+ const HighsLp& presolved_lp = highs.getPresolvedLp ();
929+ if (k == 1 ) {
930+ REQUIRE (presolved_lp.num_col_ > presolved_num_col);
931+ REQUIRE (presolved_lp.num_row_ > presolved_num_row);
932+ REQUIRE (presolved_lp.numNz () > presolved_num_nz);
933+ }
934+ presolved_num_col = presolved_lp.num_col_ ;
935+ presolved_num_row = presolved_lp.num_row_ ;
936+ presolved_num_nz = presolved_lp.numNz ();
937+
938+ if (dev_run)
939+ printf (" %s presolved LP has %d columns; %d rows and %d nonzeros\n " ,
940+ k == 0 ? " Fully" : " Lightly" , int (presolved_num_col),
941+ int (presolved_num_row), int (presolved_num_nz));
942+ highs.setOptionValue (" presolve_light" , kHighsOnString );
943+ }
944+
945+ highs.resetGlobalScheduler (true );
946+ }
947+
948+ TEST_CASE (" presolve-initial-sweep-postsolve" , " [highs_test_presolve]" ) {
949+ Highs highs;
950+ highs.setOptionValue (" output_flag" , dev_run);
951+ if (dev_run) {
952+ highs.setOptionValue (" log_dev_level" , 1 );
953+ highs.setOptionValue (" presolve_rule_logging" , true );
954+ }
955+ HighsLp lp;
956+ lp.num_col_ = 3 ;
957+ lp.num_row_ = 1 ;
958+ lp.col_cost_ = {0 , 1 , 2 };
959+ lp.col_lower_ = {1 , 2 , 0 };
960+ lp.col_upper_ = {1 , kHighsInf , kHighsInf };
961+ lp.row_lower_ = {-kHighsInf };
962+ lp.row_upper_ = {5 };
963+ lp.a_matrix_ .start_ = {0 , 1 , 2 , 3 };
964+ lp.a_matrix_ .index_ = {0 , 0 , 0 };
965+ lp.a_matrix_ .value_ = {3 , 2 , -1 };
966+ highs.passModel (lp);
967+ // Presolved to empty, so no simplex iterations if postsolve is correct
968+ highs.run ();
969+ REQUIRE (highs.getInfo ().simplex_iteration_count == 0 );
970+ if (dev_run) highs.writeSolution (" " , 1 );
971+
972+ highs.resetGlobalScheduler (true );
973+ }
974+
975+ TEST_CASE (" presolve-initial-sweep" , " [highs_test_presolve]" ) {
976+ Highs highs;
977+ highs.setOptionValue (" output_flag" , dev_run);
978+ if (dev_run) {
979+ highs.setOptionValue (" log_dev_level" , 1 );
980+ highs.setOptionValue (" presolve_rule_logging" , true );
981+ }
982+ HighsLp lp;
983+ lp.num_col_ = 2 ;
984+ lp.num_row_ = 1 ;
985+ lp.col_cost_ = {-1 , 1 };
986+ lp.col_lower_ = {1 , 0 };
987+ lp.col_upper_ = {1 , kHighsInf };
988+ lp.row_lower_ = {-kHighsInf };
989+ lp.row_upper_ = {5 };
990+ lp.a_matrix_ .start_ = {0 , 1 , 1 };
991+ lp.a_matrix_ .index_ = {0 };
992+ lp.a_matrix_ .value_ = {3 };
993+ HighsStatus pass_model_status = HighsStatus::kOk ;
994+ for (HighsInt k = 0 ; k < 4 ; k++) {
995+ if (dev_run) printf (" \n Pass k = %d\n ==========\n " , int (k));
996+ REQUIRE (highs.passModel (lp) == pass_model_status);
997+ highs.run ();
998+ if (k == 0 ) {
999+ // Remove the empty column
1000+ lp.num_col_ = 1 ;
1001+ lp.col_cost_ = {-1 };
1002+ lp.col_lower_ = {1 };
1003+ lp.col_upper_ = {1 };
1004+ lp.a_matrix_ .start_ = {0 , 1 };
1005+ } else if (k == 1 ) {
1006+ REQUIRE (highs.getModelStatus () == HighsModelStatus::kOptimal );
1007+ lp.col_upper_ = {0 };
1008+ pass_model_status = HighsStatus::kWarning ;
1009+ // Can infeasible column bounds even reach presolve?
1010+ } else if (k == 2 ) {
1011+ REQUIRE (highs.getModelStatus () == HighsModelStatus::kInfeasible );
1012+ lp.col_lower_ = {1e+20 };
1013+ lp.col_upper_ = {kHighsInf };
1014+ lp.row_upper_ = {kHighsInf };
1015+ pass_model_status = HighsStatus::kError ;
1016+ // Why is this model accepted when error returns?
1017+ } else {
1018+ REQUIRE (highs.getModelStatus () == HighsModelStatus::kUnbounded );
1019+ }
1020+ if (dev_run) highs.writeSolution (" " , 1 );
1021+ }
1022+ highs.resetGlobalScheduler (true );
1023+ }
1024+
1025+ TEST_CASE (" presolve-initial-sweep-all" , " [highs_test_presolve]" ) {
1026+ Highs highs;
1027+ highs.setOptionValue (" output_flag" , dev_run);
1028+ highs.setOptionValue (" presolve_light" , kHighsOnString );
1029+ if (dev_run) {
1030+ highs.setOptionValue (" log_dev_level" , 1 );
1031+ highs.setOptionValue (" presolve_rule_logging" , true );
1032+ }
1033+ HighsLp lp;
1034+ lp.num_col_ = 7 ;
1035+ lp.num_row_ = 4 ;
1036+ lp.col_cost_ = {1 , 1 , 1 , 1 , 1 , 1 , 1 };
1037+ lp.col_lower_ = {0 , 1 , 0 , 1 , 1 , -kHighsInf , 0 };
1038+ lp.col_upper_ = {1 , 1 , kHighsInf , 3 , 1 , 1 , 1 };
1039+ lp.row_lower_ = {2 , 8 , 10 , 13 };
1040+ lp.row_upper_ = {4 , 9 , 16 , 26 };
1041+ lp.a_matrix_ .start_ = {0 , 1 , 5 , 6 , 6 , 10 , 12 , 13 };
1042+ lp.a_matrix_ .index_ = {2 , 0 , 1 , 2 , 3 , 0 , 0 , 1 , 2 , 3 , 2 , 3 , 2 };
1043+ lp.a_matrix_ .value_ = {6 , 1 , 4 , 7 , 11 , 2 , 3 , 5 , 8 , 12 , 9 , 13 , 10 };
1044+ // Cols 1 and 4 fixed at 1; col 3 empty (fixed at LB = 1) then
1045+ //
1046+ // Rows 0 and 3 singletons; row 1 empty
1047+ REQUIRE (highs.passModel (lp) == HighsStatus::kOk );
1048+
1049+ highs.run ();
1050+ REQUIRE (highs.getModelStatus () == HighsModelStatus::kOptimal );
1051+ if (dev_run) highs.writeSolution (" " , 1 );
1052+
1053+ // Add a redundant row
1054+ std::vector<HighsInt> index = {0 , 5 , 6 };
1055+ std::vector<double > value = {1 , 1 , 1 };
1056+ highs.addRow (-kHighsInf , 4 , 3 , index.data (), value.data ());
1057+
1058+ highs.setOptionValue (" use_warm_start" , false );
1059+ highs.run ();
1060+ REQUIRE (highs.getModelStatus () == HighsModelStatus::kOptimal );
1061+ if (dev_run) highs.writeSolution (" " , 1 );
1062+
1063+ highs.resetGlobalScheduler (true );
9121064}
9131065
9141066TEST_CASE (" bound_implied" , " [highs_test_presolve]" ) {
0 commit comments