@@ -734,3 +734,81 @@ 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-egout-ac" , " [highs_test_presolve]" ) {
739+ // Tests the case where, for this model when run_crossover is off,
740+ // sparsify is used to reduce the LP to empty. However, when
741+ // starting from an empty LP, basis postsolve is always used
742+ std::string model_file =
743+ std::string (HIGHS_DIR) + " /check/instances/egout-ac.mps" ;
744+ Highs h;
745+ h.setOptionValue (" output_flag" , dev_run);
746+ REQUIRE (h.setOptionValue (" presolve_rule_logging" , true ) == HighsStatus::kOk );
747+ if (dev_run)
748+ REQUIRE (h.setOptionValue (" log_dev_level" , 1 ) == HighsStatus::kOk );
749+ REQUIRE (h.readModel (model_file) == HighsStatus::kOk );
750+ // Firstly check that pure presolve reduces the LP to empty
751+ REQUIRE (h.presolve () == HighsStatus::kOk );
752+ // Ensure that sparsify isn't called
753+ REQUIRE (h.getPresolveLog ().rule [kPresolveRuleSparsify ].call == 0 );
754+
755+ const HighsLp& presolved_lp = h.getPresolvedLp ();
756+ REQUIRE (presolved_lp.num_col_ == 0 );
757+ REQUIRE (presolved_lp.num_row_ == 0 );
758+
759+ HighsSolution solution;
760+ HighsBasis basis;
761+ basis.useful = true ;
762+ solution.value_valid = true ;
763+ solution.dual_valid = true ;
764+ // Check that postsolve with the empty solution and basis runs OK -
765+ // ie doesn't trigger assert due to sparsify having been used
766+ REQUIRE (h.postsolve (solution, basis) == HighsStatus::kOk );
767+
768+ // Check that using IPM with crossover runs OK without using
769+ // sparsify
770+ REQUIRE (h.setOptionValue (" solver" , kIpmString ) == HighsStatus::kOk );
771+ REQUIRE (h.run () == HighsStatus::kOk );
772+ REQUIRE (h.getPresolveLog ().rule [kPresolveRuleSparsify ].call == 0 );
773+
774+ // Check that pure presolve reduces the LP to empty without using
775+ // sparsify
776+ REQUIRE (h.presolve () == HighsStatus::kOk );
777+ REQUIRE (h.getPresolveLog ().rule [kPresolveRuleSparsify ].call == 0 );
778+ REQUIRE (h.postsolve (solution, basis) == HighsStatus::kOk );
779+
780+ // Check that pure presolve reduces the LP to empty using sparsify
781+ // when lp_presolve_requires_basis_postsolve is false
782+ bool lp_presolve_requires_basis_postsolve = false ;
783+ REQUIRE (h.setOptionValue (" lp_presolve_requires_basis_postsolve" ,
784+ lp_presolve_requires_basis_postsolve) ==
785+ HighsStatus::kOk );
786+ REQUIRE (h.presolve () == HighsStatus::kOk );
787+ REQUIRE (h.getPresolveLog ().rule [kPresolveRuleSparsify ].call > 0 );
788+ REQUIRE (h.postsolve (solution, basis) == HighsStatus::kOk );
789+ REQUIRE (h.getOptions ().lp_presolve_requires_basis_postsolve ==
790+ lp_presolve_requires_basis_postsolve);
791+
792+ // Now, with crossover off
793+ REQUIRE (h.setOptionValue (" run_crossover" , kHighsOffString ) ==
794+ HighsStatus::kOk );
795+
796+ // Now reset lp_presolve_requires_basis_postsolve default to true,
797+ // to test whether it's set false due to running IPM without
798+ // crossover
799+ lp_presolve_requires_basis_postsolve = true ;
800+ REQUIRE (h.setOptionValue (" lp_presolve_requires_basis_postsolve" ,
801+ lp_presolve_requires_basis_postsolve) ==
802+ HighsStatus::kOk );
803+
804+ REQUIRE (h.clearSolver () == HighsStatus::kOk );
805+ REQUIRE (h.run () == HighsStatus::kOk );
806+ REQUIRE (h.getPresolveLog ().rule [kPresolveRuleSparsify ].call > 0 );
807+ // Ensure that lp_presolve_requires_basis_postsolve has been reset
808+ // to true, after being set false before presolve when using IPM
809+ // without crossover
810+ REQUIRE (h.getOptions ().lp_presolve_requires_basis_postsolve ==
811+ lp_presolve_requires_basis_postsolve);
812+
813+ h.resetGlobalScheduler (true );
814+ }
0 commit comments