@@ -734,3 +734,107 @@ TEST_CASE("presolve-issue-2446", "[highs_test_presolve]") {
734734 REQUIRE (highs.presolve () == HighsStatus::kOk );
735735 REQUIRE (highs.getModelPresolveStatus () == HighsPresolveStatus::kReduced );
736736}
737+
738+ TEST_CASE (" presolve-solve-postsolve-no-col-dual" , " [highs_test_presolve]" ) {
739+ Highs highs;
740+ highs.setOptionValue (" output_flag" , dev_run);
741+ std::string model_file =
742+ std::string (HIGHS_DIR) + " /check/instances/afiro.mps" ;
743+ highs.readModel (model_file);
744+ highs.presolve ();
745+ HighsLp presolved_lp = highs.getPresolvedLp ();
746+ Highs highs1;
747+ highs1.setOptionValue (" output_flag" , dev_run);
748+ highs1.setOptionValue (" presolve" , kHighsOffString );
749+ highs1.passModel (presolved_lp);
750+ highs1.run ();
751+ HighsSolution solution = highs1.getSolution ();
752+
753+ // Perform postsolve using the optimal solution and basis for the
754+ // presolved model
755+ REQUIRE (highs.postsolve (solution) == HighsStatus::kOk );
756+
757+ // If row duals are supplied, then column duals must also be suppplied
758+ solution.col_dual .clear ();
759+ REQUIRE (highs.postsolve (solution) == HighsStatus::kError );
760+
761+ highs.resetGlobalScheduler (true );
762+ }
763+
764+ TEST_CASE (" presolve-egout-ac" , " [highs_test_presolve]" ) {
765+ // Tests the case where, for this model when run_crossover is off,
766+ // sparsify is used to reduce the LP to empty. However, when
767+ // starting from an empty LP, basis postsolve is always used
768+ std::string model_file =
769+ std::string (HIGHS_DIR) + " /check/instances/egout-ac.mps" ;
770+ Highs h;
771+ h.setOptionValue (" output_flag" , dev_run);
772+ REQUIRE (h.setOptionValue (" presolve_rule_logging" , true ) == HighsStatus::kOk );
773+ if (dev_run)
774+ REQUIRE (h.setOptionValue (" log_dev_level" , 1 ) == HighsStatus::kOk );
775+ REQUIRE (h.readModel (model_file) == HighsStatus::kOk );
776+ // Firstly check that pure presolve reduces the LP to empty
777+ REQUIRE (h.presolve () == HighsStatus::kOk );
778+ // Ensure that sparsify isn't called
779+ REQUIRE (h.getPresolveLog ().rule [kPresolveRuleSparsify ].call == 0 );
780+
781+ const HighsLp& presolved_lp = h.getPresolvedLp ();
782+ REQUIRE (presolved_lp.num_col_ == 0 );
783+ REQUIRE (presolved_lp.num_row_ == 0 );
784+
785+ HighsSolution solution;
786+ HighsBasis basis;
787+ basis.useful = true ;
788+ solution.value_valid = true ;
789+ solution.dual_valid = true ;
790+ // Check that postsolve with the empty solution and basis runs OK -
791+ // ie doesn't trigger assert due to sparsify having been used
792+ REQUIRE (h.postsolve (solution, basis) == HighsStatus::kOk );
793+
794+ // Check that using IPM with crossover runs OK without using
795+ // sparsify
796+ REQUIRE (h.setOptionValue (" solver" , kIpmString ) == HighsStatus::kOk );
797+ REQUIRE (h.run () == HighsStatus::kOk );
798+ REQUIRE (h.getPresolveLog ().rule [kPresolveRuleSparsify ].call == 0 );
799+
800+ // Check that pure presolve reduces the LP to empty without using
801+ // sparsify
802+ REQUIRE (h.presolve () == HighsStatus::kOk );
803+ REQUIRE (h.getPresolveLog ().rule [kPresolveRuleSparsify ].call == 0 );
804+ REQUIRE (h.postsolve (solution, basis) == HighsStatus::kOk );
805+
806+ // Check that pure presolve reduces the LP to empty using sparsify
807+ // when lp_presolve_requires_basis_postsolve is false
808+ bool lp_presolve_requires_basis_postsolve = false ;
809+ REQUIRE (h.setOptionValue (" lp_presolve_requires_basis_postsolve" ,
810+ lp_presolve_requires_basis_postsolve) ==
811+ HighsStatus::kOk );
812+ REQUIRE (h.presolve () == HighsStatus::kOk );
813+ REQUIRE (h.getPresolveLog ().rule [kPresolveRuleSparsify ].call > 0 );
814+ REQUIRE (h.postsolve (solution, basis) == HighsStatus::kOk );
815+ REQUIRE (h.getOptions ().lp_presolve_requires_basis_postsolve ==
816+ lp_presolve_requires_basis_postsolve);
817+
818+ // Now, with crossover off
819+ REQUIRE (h.setOptionValue (" run_crossover" , kHighsOffString ) ==
820+ HighsStatus::kOk );
821+
822+ // Now reset lp_presolve_requires_basis_postsolve default to true,
823+ // to test whether it's set false due to running IPM without
824+ // crossover
825+ lp_presolve_requires_basis_postsolve = true ;
826+ REQUIRE (h.setOptionValue (" lp_presolve_requires_basis_postsolve" ,
827+ lp_presolve_requires_basis_postsolve) ==
828+ HighsStatus::kOk );
829+
830+ REQUIRE (h.clearSolver () == HighsStatus::kOk );
831+ REQUIRE (h.run () == HighsStatus::kOk );
832+ REQUIRE (h.getPresolveLog ().rule [kPresolveRuleSparsify ].call > 0 );
833+ // Ensure that lp_presolve_requires_basis_postsolve has been reset
834+ // to true, after being set false before presolve when using IPM
835+ // without crossover
836+ REQUIRE (h.getOptions ().lp_presolve_requires_basis_postsolve ==
837+ lp_presolve_requires_basis_postsolve);
838+
839+ h.resetGlobalScheduler (true );
840+ }
0 commit comments