@@ -1363,6 +1363,95 @@ const std::vector<double>& HighsMipSolverData::getSolution() const {
13631363 return incumbent;
13641364}
13651365
1366+ bool HighsMipSolverData::oneOptImprovement (std::vector<double >& sol,
1367+ double & solobj) {
1368+ bool success = false ;
1369+ for (const HighsInt col : integer_cols) {
1370+ double max_movement = kHighsInf ;
1371+ if (mipsolver.model_ ->col_cost_ [col] > 0 &&
1372+ sol[col] > domain.col_lower_ [col] + feastol) {
1373+ max_movement = 0 ;
1374+ } else if (mipsolver.model_ ->col_cost_ [col] < 0 &&
1375+ sol[col] < domain.col_upper_ [col] - feastol) {
1376+ max_movement = 0 ;
1377+ } else if (mipsolver.model_ ->col_cost_ [col] == 0 ) {
1378+ max_movement = 0 ;
1379+ }
1380+ if (max_movement == 0 ) continue ;
1381+ HighsInt dir = mipsolver.model_ ->col_cost_ [col] > 0 ? 1 : -1 ;
1382+ max_movement = dir == 1 ? sol[col] - domain.col_lower_ [col]
1383+ : domain.col_upper_ [col] - sol[col];
1384+ if (max_movement < 1 - feastol) continue ;
1385+ const HighsInt start = mipsolver.model_ ->a_matrix_ .start_ [col];
1386+ const HighsInt end = mipsolver.model_ ->a_matrix_ .start_ [col + 1 ];
1387+ for (HighsInt i = start; i != end; ++i) {
1388+ HighsInt row = mipsolver.model_ ->a_matrix_ .index_ [i];
1389+ double val = mipsolver.model_ ->a_matrix_ .value_ [i];
1390+ if (mipsolver.model_ ->row_lower_ [row] ==
1391+ mipsolver.model_ ->row_upper_ [row]) {
1392+ max_movement = 0 ;
1393+ break ;
1394+ }
1395+ // calc activity of row
1396+ double activity = 0 ;
1397+ for (HighsInt j = ARstart_[row]; j < ARstart_[row + 1 ]; j++) {
1398+ activity += ARvalue_[j] * sol[ARindex_[j]];
1399+ }
1400+ // if dir = 1 && val > 0 && row_lower_ is not tight -> can move
1401+ // if dir = 1 && val < 0 && row_upper_ is not tight -> can move
1402+ // if dir = -1 && val > 0 && row_upper_ is not tight -> can move
1403+ // if dir = -1 && val < 0 && row_lower_ is not tight -> can move
1404+ if (dir == 1 && val > 0 ) {
1405+ if (mipsolver.model_ ->row_lower_ [row] + feastol < activity) {
1406+ max_movement =
1407+ std::min (max_movement,
1408+ (activity - mipsolver.model_ ->row_lower_ [row]) / val);
1409+ } else {
1410+ max_movement = 0 ;
1411+ }
1412+ } else if (dir == 1 && val < 0 ) {
1413+ if (mipsolver.model_ ->row_upper_ [row] - feastol > activity) {
1414+ max_movement =
1415+ std::min (max_movement,
1416+ (activity - mipsolver.model_ ->row_upper_ [row]) / val);
1417+ } else {
1418+ max_movement = 0 ;
1419+ }
1420+ } else if (dir == -1 && val > 0 ) {
1421+ if (mipsolver.model_ ->row_upper_ [row] - feastol > activity) {
1422+ max_movement =
1423+ std::min (max_movement,
1424+ (mipsolver.model_ ->row_upper_ [row] - activity) / val);
1425+ } else {
1426+ max_movement = 0 ;
1427+ }
1428+ } else {
1429+ assert (dir == -1 && val < 0 );
1430+ if (mipsolver.model_ ->row_lower_ [row] + feastol < activity) {
1431+ max_movement =
1432+ std::min (max_movement,
1433+ (mipsolver.model_ ->row_lower_ [row] - activity) / val);
1434+ } else {
1435+ max_movement = 0 ;
1436+ }
1437+ }
1438+ // Only move by integer amounts
1439+ max_movement = floor (max_movement + feastol);
1440+ if (max_movement <= 0 ) {
1441+ break ;
1442+ }
1443+ }
1444+ if (max_movement >= 1 ) {
1445+ sol[col] -= dir * max_movement;
1446+ success = true ;
1447+ }
1448+ }
1449+ if (success) {
1450+ solobj = transformNewIntegerFeasibleSolution (sol, true );
1451+ }
1452+ return success;
1453+ }
1454+
13661455bool HighsMipSolverData::addIncumbent (const std::vector<double >& sol,
13671456 double solobj, const int solution_source,
13681457 const bool print_display_line,
@@ -1386,8 +1475,26 @@ bool HighsMipSolverData::addIncumbent(const std::vector<double>& sol,
13861475 sol, possibly_store_as_new_incumbent)
13871476 : 0 ;
13881477
1478+ // Apply one-opt (check if any integral variable can be moved s.t
1479+ // objective improves and feasibility is maintained.
1480+ bool one_opt_success = false ;
1481+ double one_opt_transformed_solobj = transformed_solobj;
1482+ std::vector<double > one_opt_sol;
1483+ if (possibly_store_as_new_incumbent && transformed_solobj != kHighsInf ) {
1484+ one_opt_sol = sol;
1485+ one_opt_success =
1486+ oneOptImprovement (one_opt_sol, one_opt_transformed_solobj);
1487+ if (one_opt_success && one_opt_transformed_solobj >= transformed_solobj) {
1488+ one_opt_success = false ;
1489+ }
1490+ }
1491+
13891492 if (possibly_store_as_new_incumbent) {
1390- solobj = transformed_solobj;
1493+ if (!one_opt_success) {
1494+ solobj = transformed_solobj;
1495+ } else {
1496+ solobj = one_opt_transformed_solobj;
1497+ }
13911498 if (solobj >= upper_bound) return false ;
13921499
13931500 double prev_upper_bound = upper_bound;
@@ -1399,7 +1506,11 @@ bool HighsMipSolverData::addIncumbent(const std::vector<double>& sol,
13991506 updatePrimalDualIntegral (lower_bound, lower_bound, prev_upper_bound,
14001507 upper_bound);
14011508
1402- incumbent = sol;
1509+ if (!one_opt_success) {
1510+ incumbent = sol;
1511+ } else {
1512+ incumbent = one_opt_sol;
1513+ }
14031514 double new_upper_limit = computeNewUpperLimit (solobj, 0.0 , 0.0 );
14041515
14051516 if (!is_user_solution && !mipsolver.submip )
@@ -1437,8 +1548,13 @@ bool HighsMipSolverData::addIncumbent(const std::vector<double>& sol,
14371548 pruned_treeweight += nodequeue.performBounding (upper_limit);
14381549 printDisplayLine (solution_source);
14391550 }
1440- } else if (incumbent.empty ())
1441- incumbent = sol;
1551+ } else if (incumbent.empty ()) {
1552+ if (!one_opt_success) {
1553+ incumbent = sol;
1554+ } else {
1555+ incumbent = one_opt_sol;
1556+ }
1557+ }
14421558
14431559 return true ;
14441560}
0 commit comments