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