@@ -89,10 +89,19 @@ TEST_CASE("test-analytic-centre-box", "[highs_ipm]") {
8989}
9090
9191TEST_CASE (" test-1966" , " [highs_ipm]" ) {
92+ // This is the primal-dual infeasible instance
93+ // ("PrimalAndDualInfeasible") from OR-Tools that exposed the need
94+ // for primal_solution_status and dual_solution_status not to be set
95+ // to kSolutionStatusFeasible when there are no primal/dual
96+ // infeasiblities, but meaningful primal/dual residual errors
97+ //
98+ // Takehome: make sure HiGHS unit tests include what failed in unit
99+ // tests elsewhere!
92100 Highs highs;
93101 highs.setOptionValue (" output_flag" , dev_run);
94102 const HighsInfo& info = highs.getInfo ();
95103 HighsLp lp;
104+ lp.sense_ = ObjSense::kMaximize ;
96105 lp.num_col_ = 2 ;
97106 lp.num_row_ = 2 ;
98107 lp.col_cost_ = {2 , -1 };
@@ -103,43 +112,70 @@ TEST_CASE("test-1966", "[highs_ipm]") {
103112 lp.a_matrix_ .start_ = {0 , 2 , 4 };
104113 lp.a_matrix_ .index_ = {0 , 1 , 0 , 1 };
105114 lp.a_matrix_ .value_ = {1 , -1 , 1 , -1 };
115+ lp.a_matrix_ .format_ = MatrixFormat::kRowwise ;
106116 highs.passModel (lp);
107- highs.setOptionValue (" solver" , kIpmString );
108117 highs.setOptionValue (" presolve" , kHighsOffString );
109-
110- if (dev_run) printf (" \n With default residual tolerances\n " );
111- highs.run ();
112- if (dev_run) {
113- highs.writeSolution (" " , kSolutionStylePretty );
114- printf (" Num primal infeasibilities = %d\n " ,
115- int (info.num_primal_infeasibilities ));
116- printf (" Max primal infeasibility = %g\n " , info.max_primal_infeasibility );
117- printf (" Sum primal infeasibilities = %g\n " ,
118- info.sum_primal_infeasibilities );
119- printf (" Num dual infeasibilities = %d\n " ,
120- int (info.num_dual_infeasibilities ));
121- printf (" Max dual infeasibility = %g\n " , info.max_dual_infeasibility );
122- printf (" Sum dual infeasibilities = %g\n " , info.sum_dual_infeasibilities );
123- }
124- highs.clearSolver ();
125-
126- if (dev_run) printf (" \n With infinite residual tolerances\n " );
127- highs.setOptionValue (" primal_residual_tolerance" , 1e30 );
128- highs.setOptionValue (" dual_residual_tolerance" , 1e30 );
129- highs.run ();
130- if (dev_run) {
131- highs.writeSolution (" " , kSolutionStylePretty );
132- printf (" Num primal infeasibilities = %d\n " ,
133- int (info.num_primal_infeasibilities ));
134- printf (" Max primal infeasibility = %g\n " , info.max_primal_infeasibility );
135- printf (" Sum primal infeasibilities = %g\n " ,
136- info.sum_primal_infeasibilities );
137- printf (" Num dual infeasibilities = %d\n " ,
138- int (info.num_dual_infeasibilities ));
139- printf (" Max dual infeasibility = %g\n " , info.max_dual_infeasibility );
140- printf (" Sum dual infeasibilities = %g\n " , info.sum_dual_infeasibilities );
118+ // if (dev_run) highs.writeModel("");
119+ HighsModelStatus require_model_status = HighsModelStatus::kNotset ;
120+ for (int k = 0 ; k < 2 ; k++) {
121+ if (k == 0 ) {
122+ highs.setOptionValue (" solver" , kIpmString );
123+ if (dev_run) printf (" Solving with IPX\n " );
124+ require_model_status = HighsModelStatus::kInfeasible ;
125+ } else {
126+ highs.setOptionValue (" solver" , kPdlpString );
127+ if (dev_run) printf (" Solving with PDLP\n " );
128+ require_model_status = HighsModelStatus::kUnboundedOrInfeasible ;
129+ }
130+ highs.run ();
131+ REQUIRE (info.primal_solution_status != kSolutionStatusFeasible );
132+ REQUIRE (info.dual_solution_status != kSolutionStatusFeasible );
133+ REQUIRE (highs.getModelStatus () == require_model_status);
134+ if (dev_run) {
135+ // Nice illustration that IPX
136+ //
137+ // * identifies "infeasible"
138+ //
139+ // * gets no primal or dual infeasibilies
140+ //
141+ // * gets primal and dual residual errors
142+ //
143+ // whereas PDLP
144+ //
145+ // * identifies only "infeasible or unbounded"
146+ //
147+ // * gets primal infeasibilies and no primal residual errors
148+ //
149+ // * gets no primal infeasibilies but primal residual errors
150+ //
151+ // highs.writeSolution("", kSolutionStylePretty);
152+ printf (" Primal solution status = %d\n " , int (info.primal_solution_status ));
153+ printf (" Dual solution status = %d\n " , int (info.dual_solution_status ));
154+ printf (" Num primal infeasibilities = %d\n " ,
155+ int (info.num_primal_infeasibilities ));
156+ printf (" Max primal infeasibility = %g\n " ,
157+ info.max_primal_infeasibility );
158+ printf (" Sum primal infeasibilities = %g\n " ,
159+ info.sum_primal_infeasibilities );
160+ printf (" Num dual infeasibilities = %d\n " ,
161+ int (info.num_dual_infeasibilities ));
162+ printf (" Max dual infeasibility = %g\n " ,
163+ info.max_dual_infeasibility );
164+ printf (" Sum dual infeasibilities = %g\n " ,
165+ info.sum_dual_infeasibilities );
166+ printf (" Num primal residual errors = %d\n " ,
167+ int (info.num_primal_residual_errors ));
168+ printf (" Max primal residual error = %g\n " ,
169+ info.max_primal_residual_error );
170+ printf (" Num dual residual errors = %d\n " ,
171+ int (info.num_dual_residual_errors ));
172+ printf (" Max dual residual error = %g\n " ,
173+ info.max_dual_residual_error );
174+ printf (" Primal-dual objective error = %g\n " ,
175+ info.primal_dual_objective_error );
176+ }
177+ highs.clearSolver ();
141178 }
142-
143179 highs.resetGlobalScheduler (true );
144180}
145181
0 commit comments