@@ -1483,3 +1483,125 @@ TEST_CASE("issue-2975", "[highs_test_mip_solver]") {
14831483
14841484 highs.resetGlobalScheduler (true );
14851485}
1486+
1487+ TEST_CASE (" issue-3118" , " [highs_test_mip_solver]" ) {
1488+ const double M = 1e10 ;
1489+ // min x + y
1490+ // s.t. x + M*y = 1
1491+ // M*x + y = 1
1492+ // x, y binary
1493+ // Initial "solution" x = y = 1/M
1494+ //
1495+ // This problem is found infeasible in presolve, but the point x = y
1496+ // = 1/M is integer feasible to within the tolerances, so is
1497+ // acccepted as an optimal solution to the problem
1498+ Highs highs;
1499+ highs.setOptionValue (" output_flag" , dev_run);
1500+
1501+ HighsInt x = 0 ;
1502+ HighsInt y = 1 ;
1503+ HighsLp lp;
1504+ lp.num_col_ = 2 ;
1505+ lp.num_row_ = 2 ;
1506+ lp.col_lower_ = {0 ., 0 .};
1507+ lp.col_upper_ = {1 ., 1 .};
1508+ lp.col_cost_ = {1 ., 1 .};
1509+ lp.integrality_ = {HighsVarType::kInteger , HighsVarType::kInteger };
1510+ lp.row_lower_ = {1 ., 1 .};
1511+ lp.row_upper_ = {1 ., 1 .};
1512+ lp.a_matrix_ .format_ = MatrixFormat::kRowwise ;
1513+ lp.a_matrix_ .start_ = {0 , 2 , 4 };
1514+ lp.a_matrix_ .index_ = {x, y, x, y};
1515+ lp.a_matrix_ .value_ = {1 ., M, M, 1 };
1516+ highs.passModel (lp);
1517+
1518+ std::vector<double > solution_values (lp.num_col_ , 1 / M);
1519+ highs.setSolution (2 , nullptr , solution_values.data ());
1520+
1521+ highs.run ();
1522+ REQUIRE (highs.getModelStatus () == HighsModelStatus::kOptimal );
1523+ REQUIRE (std::abs (2 / M - highs.getInfo ().objective_function_value ) < 1e-9 );
1524+ if (dev_run) highs.writeSolution (" " , 1 );
1525+
1526+ highs.resetGlobalScheduler (true );
1527+ }
1528+
1529+ TEST_CASE (" issue-3118a" , " [highs_test_mip_solver]" ) {
1530+ const double M = 1e10 ;
1531+ // max f = x
1532+ // s.t. x - M*y = -1
1533+ // x <= b
1534+ // x, y integer in [0, 2]x[0, 1]
1535+ //
1536+ // The MIP is not feasible over the integers for any b
1537+ //
1538+ // With initial "solution" x = 0; y = 1/M
1539+ //
1540+ // For b = 0: (0, 1/M) with f = 0 is the only feasible solution of
1541+ // the relaxation, and it's also feasible for the MIP, so claiming
1542+ // it is optimal when presolve identifies infeasibility is clearly
1543+ // justified
1544+ //
1545+ // For b = 1: (1, 2/M) with f = 1 is the optimal solution of the
1546+ // relaxation, and it's also feasible for the MIP. However, claiming
1547+ // that the initial "solution" (0, 1/M) with f = 0 is optimal when
1548+ // presolve identifies infeasibility is still justified, because
1549+ // it's a point that is feasible for a MIP that is deemed infeasible
1550+ // by presolve
1551+ Highs highs;
1552+ highs.setOptionValue (" output_flag" , dev_run);
1553+
1554+ HighsInt x = 0 ;
1555+ HighsInt y = 1 ;
1556+ HighsLp lp;
1557+ lp.sense_ = ObjSense::kMaximize ;
1558+ lp.num_col_ = 2 ;
1559+ lp.num_row_ = 2 ;
1560+ lp.col_lower_ = {0 , 0 };
1561+ lp.col_upper_ = {2 , 1 };
1562+ lp.col_cost_ = {1 , 0 };
1563+ lp.integrality_ = {HighsVarType::kInteger , HighsVarType::kInteger };
1564+ lp.row_lower_ = {-1 , -kHighsInf };
1565+ lp.row_upper_ = {-1 , 0 };
1566+ lp.a_matrix_ .format_ = MatrixFormat::kRowwise ;
1567+ lp.a_matrix_ .start_ = {0 , 2 , 3 };
1568+ lp.a_matrix_ .index_ = {x, y, x};
1569+ lp.a_matrix_ .value_ = {1 ., -M, 1 };
1570+
1571+ for (HighsInt k = 0 ; k < 2 ; k++) {
1572+ HighsInt b = k == 0 ? 0 : 1 ;
1573+ lp.row_upper_ [1 ] = b;
1574+
1575+ highs.passModel (lp);
1576+
1577+ // Solve as MIP
1578+ if (dev_run)
1579+ printf (" ================\n Case b = %d (MIP)\n ================\n " , int (b));
1580+ highs.setOptionValue (" solve_relaxation" , false );
1581+ std::vector<double > solution_values = {0 , 1 / M};
1582+ highs.setSolution (2 , nullptr , solution_values.data ());
1583+
1584+ highs.run ();
1585+ if (dev_run) highs.writeSolution (" " , 1 );
1586+ REQUIRE (highs.getModelStatus () == HighsModelStatus::kOptimal );
1587+
1588+ // Solve as LP
1589+ if (dev_run)
1590+ printf (" ===============\n Case b = %d (LP)\n ===============\n " , int (b));
1591+ highs.setOptionValue (" solve_relaxation" , true );
1592+ highs.clearSolver ();
1593+ highs.run ();
1594+ highs.writeSolution (" " , 1 );
1595+ REQUIRE (highs.getModelStatus () == HighsModelStatus::kOptimal );
1596+ double lp_objective_value = highs.getInfo ().objective_function_value ;
1597+
1598+ solution_values = highs.getSolution ().col_value ;
1599+ highs.setSolution (2 , nullptr , solution_values.data ());
1600+
1601+ bool valid, integral, feasible;
1602+ REQUIRE (highs.assessPrimalSolution (valid, integral, feasible) ==
1603+ HighsStatus::kOk );
1604+ }
1605+
1606+ highs.resetGlobalScheduler (true );
1607+ }
0 commit comments