Skip to content

Commit ec06da6

Browse files
committed
Add first draft of one-opt
1 parent 340f098 commit ec06da6

File tree

2 files changed

+122
-4
lines changed

2 files changed

+122
-4
lines changed

highs/mip/HighsMipSolverData.cpp

Lines changed: 120 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
13661455
bool 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
}

highs/mip/HighsMipSolverData.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,8 @@ struct HighsMipSolverData {
276276

277277
const std::vector<double>& getSolution() const;
278278

279+
bool oneOptImprovement(std::vector<double>& sol, double& solobj);
280+
279281
std::string solutionSourceToString(const int solution_source,
280282
const bool code = true) const;
281283
void printSolutionSourceKey() const;

0 commit comments

Comments
 (0)