Skip to content

Commit 8db2116

Browse files
committed
Merge branch 'latest' of https://github.com/ERGO-Code/HiGHS into dualBoundTightening
2 parents 6bb8ac0 + 26c42ae commit 8db2116

File tree

8 files changed

+73
-68
lines changed

8 files changed

+73
-68
lines changed

FEATURES.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,4 @@ The irreducible infeasibility system (IIS) facility now detects infeasibility du
1212

1313
Prompted by [#2463](https://github.com/ERGO-Code/HiGHS/issues/2463), the HiGHS solution and basis files now match data to any column and row names in the model, only assuming that the data are aligned with column and row indices if there are no names in the model. This requires a new version (v2) of the HiGHS basis file. Basis files from v1 are still read, but deprecated. Now, when writing out a model, basis or solution, column and row names are added to the model - previously they were created temporarily and inconsistentyly on the fly. If the model has existing names, then distinctive names are created to replace any blank names, but names with spaces or duplicate names yield an error status return.
1414

15+
As per [#2487](https://github.com/ERGO-Code/HiGHS/issues/2487), trivial heuristics now run before feasibility jump (FJ), and FJ will use any existing incumbent. FJ will clip any finite variable values in the incumbent to lower and upper bounds, and falls back to the existing logic (lower bound if finite, else upper bound if finite, else 0) for any infinite values in the incumbent.

highs/ipm/ipx/ipm.cc

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -821,13 +821,15 @@ void IPM::PrintHeader() {
821821
std::stringstream h_logging_stream;
822822
h_logging_stream.str(std::string());
823823
h_logging_stream
824-
<< (kTerminationLogging ? "\n" : "")
825-
<< " " << Format("Iter", 4)
826-
<< " " << Format("P.res", 8) << " " << Format("D.res", 8)
827-
<< " " << Format("P.obj", 15) << " " << Format("D.obj", 15)
828-
<< " " << Format("mu", 8);
824+
<< " " << Format("Iter", 4)
825+
<< " " << Format("primal obj", 15)
826+
<< " " << Format("dual obj", 15)
827+
<< " " << Format("pinf", 9)
828+
<< " " << Format("dinf", 9)
829+
<< " " << Format("gap", 8);
830+
// h_logging_stream << " " << Format("mu", 8);
829831
if (!control_.timelessLog())
830-
h_logging_stream << " " << Format("Time", 7);
832+
h_logging_stream << " " << Format("time", 7);
831833
control_.hLog(h_logging_stream);
832834
control_.Debug()
833835
<< " " << Format("stepsizes", 9)
@@ -841,17 +843,29 @@ void IPM::PrintHeader() {
841843
void IPM::PrintOutput() {
842844
const bool ipm_optimal = iterate_->feasible() && iterate_->optimal();
843845

844-
if (kTerminationLogging) PrintHeader();
846+
double logging_pobj = iterate_->pobjective_after_postproc();
847+
double logging_dobj = iterate_->dobjective_after_postproc();
848+
double logging_presidual = iterate_->presidual();
849+
double logging_dresidual = iterate_->dresidual();
850+
851+
// Now logging relative primal and dual infeasibility, and also
852+
// the relative primal dual objective gap
853+
logging_presidual /= iterate_->bounds_measure_;
854+
logging_dresidual /= iterate_->costs_measure_;
855+
double logging_gap = std::abs(logging_pobj - logging_dobj) /
856+
(1.0+0.5 *std::fabs(logging_pobj + logging_dobj));
857+
845858
std::stringstream h_logging_stream;
846859
h_logging_stream.str(std::string());
847860
h_logging_stream
848861
<< " " << Format(info_->iter, 3)
849862
<< (ipm_optimal ? "*" : " ")
850-
<< " " << Scientific(iterate_->presidual(), 8, 2)
851-
<< " " << Scientific(iterate_->dresidual(), 8, 2)
852-
<< " " << Scientific(iterate_->pobjective_after_postproc(), 15, 8)
853-
<< " " << Scientific(iterate_->dobjective_after_postproc(), 15, 8)
854-
<< " " << Scientific(iterate_->mu(), 8, 2);
863+
<< " " << Scientific(logging_pobj, 15, 8)
864+
<< " " << Scientific(logging_dobj, 15, 8)
865+
<< " " << Scientific(logging_presidual, 9, 2)
866+
<< " " << Scientific(logging_dresidual, 9, 2)
867+
<< " " << Scientific(logging_gap, 8, 2);
868+
// h_logging_stream << " " << Scientific(iterate_->mu(), 8, 2);
855869
if (!control_.timelessLog())
856870
h_logging_stream << " " << Fixed(control_.Elapsed(), 6, 0) << "s";
857871
control_.hLog(h_logging_stream);

highs/ipm/ipx/iterate.cc

Lines changed: 7 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,9 @@ Iterate::Iterate(const Model& model) : model_(model) {
5555
}
5656
}
5757
assert_consistency();
58+
this->bounds_measure_ = 1.0 + model_.norm_bounds();
59+
this->costs_measure_ = 1.0 + model_.norm_c();
60+
5861
}
5962

6063
void Iterate::Initialize(const Vector& x, const Vector& xl, const Vector& xu,
@@ -219,40 +222,21 @@ double Iterate::mu_max() const { Evaluate(); return mu_max_; }
219222

220223
bool Iterate::feasible() const {
221224
Evaluate();
222-
const double bounds_measure = 1.0 + model_.norm_bounds();
223-
const double costs_measure = 1.0 + model_.norm_c();
224-
const double rel_presidual = presidual_ / bounds_measure;
225-
const double rel_dresidual = dresidual_ / costs_measure;
226-
const bool primal_feasible = presidual_ <= feasibility_tol_ * (bounds_measure);
227-
const bool dual_feasible = dresidual_ <= feasibility_tol_ * (costs_measure);
225+
const bool primal_feasible = presidual_ <= feasibility_tol_ * bounds_measure_;
226+
const bool dual_feasible = dresidual_ <= feasibility_tol_ * costs_measure_;
228227
const bool is_feasible = primal_feasible && dual_feasible;
229-
if (kTerminationLogging) {
230-
printf("\nIterate::feasible presidual_ = %11.4g; bounds_measure = %11.4g; "
231-
"rel_presidual = %11.4g; feasibility_tol = %11.4g: primal_feasible = %d\n",
232-
presidual_, bounds_measure, rel_presidual, feasibility_tol_, primal_feasible);
233-
printf("Iterate::feasible dresidual_ = %11.4g; costs_measure = %11.4g; "
234-
"rel_dresidual = %11.4g; feasibility_tol = %11.4g: dual_feasible = %d\n",
235-
dresidual_, costs_measure, rel_dresidual, feasibility_tol_, dual_feasible);
236-
}
237228
return is_feasible;
238229
}
239230

240231
bool Iterate::optimal() const {
241232
Evaluate();
242233
double pobj = pobjective_after_postproc();
243234
double dobj = dobjective_after_postproc();
244-
double obj = 0.5 * (pobj + dobj);
235+
double ave_obj = 0.5 * (pobj + dobj);
245236
double gap = pobj - dobj;
246237
const double abs_gap = std::abs(gap);
247-
const double obj_measure = 1.0+std::abs(obj);
238+
const double obj_measure = 1.0+std::abs(ave_obj);
248239
const bool is_optimal = abs_gap <= optimality_tol_ * obj_measure;
249-
if (kTerminationLogging) {
250-
const double rel_gap = abs_gap / obj_measure;
251-
printf("Iterate::optimal abs_gap = %11.4g;"
252-
" obj_measure = %11.4g; rel_gap = %11.4g;"
253-
" optimality_tol = %11.4g: optimal = %d\n",
254-
abs_gap, obj_measure, rel_gap, optimality_tol_, is_optimal);
255-
}
256240
return is_optimal;
257241
}
258242

highs/ipm/ipx/iterate.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,9 @@ class Iterate {
199199
// The method can only be called after Postprocess().
200200
void DropToComplementarity(Vector& x, Vector& y, Vector& z) const;
201201

202+
double bounds_measure_;
203+
double costs_measure_;
204+
202205
private:
203206
// A (primal or dual) variable that is required to be positive in the IPM is
204207
// not moved closer to zero than kBarrierMin.

highs/ipm/ipx/utils.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@
44
#include <vector>
55
#include "ipm/ipx/ipx_internal.h"
66

7-
const bool kTerminationLogging = false;
8-
97
namespace ipx {
108

119
bool AllFinite(const Vector& x);

highs/mip/HighsFeasibilityJump.cpp

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ HighsModelStatus HighsMipSolverData::feasibilityJump() {
2727
std::vector<double> col_value(model->num_col_, 0.0);
2828
double objective_function_value;
2929

30+
const bool use_incumbent = !incumbent.empty();
31+
3032
// Configure Feasibility Jump and pass it the problem
3133
auto solver = external_feasibilityjump::FeasibilityJumpSolver(
3234
log_options,
@@ -66,10 +68,14 @@ HighsModelStatus HighsMipSolverData::feasibilityJump() {
6668
sense_multiplier * model->col_cost_[col]);
6769

6870
double initial_assignment = 0.0;
69-
if (std::isfinite(lower)) {
70-
initial_assignment = lower;
71-
} else if (std::isfinite(upper)) {
72-
initial_assignment = upper;
71+
if (use_incumbent && std::isfinite(incumbent[col])) {
72+
initial_assignment = std::max(lower, std::min(upper, incumbent[col]));
73+
} else {
74+
if (std::isfinite(lower)) {
75+
initial_assignment = lower;
76+
} else if (std::isfinite(upper)) {
77+
initial_assignment = upper;
78+
}
7379
}
7480
col_value[col] = initial_assignment;
7581
}

highs/mip/HighsMipSolver.cpp

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -148,8 +148,19 @@ void HighsMipSolver::run() {
148148
mipdata_->queryExternalSolution(
149149
solution_objective_, kExternalMipSolutionQueryOriginAfterSetup);
150150

151+
// Apply the trivial heuristics
152+
analysis_.mipTimerStart(kMipClockTrivialHeuristics);
153+
HighsModelStatus returned_model_status = mipdata_->trivialHeuristics();
154+
analysis_.mipTimerStop(kMipClockTrivialHeuristics);
155+
if (modelstatus_ == HighsModelStatus::kNotset &&
156+
returned_model_status == HighsModelStatus::kInfeasible) {
157+
// trivialHeuristics can spot trivial infeasibility, so act on it
158+
modelstatus_ = returned_model_status;
159+
cleanupSolve();
160+
return;
161+
}
162+
// Apply the feasibility jump heuristic (if enabled)
151163
if (options_mip_->mip_heuristic_run_feasibility_jump) {
152-
// Apply the feasibility jump before evaluating the root node
153164
analysis_.mipTimerStart(kMipClockFeasibilityJump);
154165
HighsModelStatus returned_model_status = mipdata_->feasibilityJump();
155166
analysis_.mipTimerStop(kMipClockFeasibilityJump);
@@ -161,17 +172,7 @@ void HighsMipSolver::run() {
161172
return;
162173
}
163174
}
164-
// Apply the trivial heuristics
165-
analysis_.mipTimerStart(kMipClockTrivialHeuristics);
166-
HighsModelStatus returned_model_status = mipdata_->trivialHeuristics();
167-
analysis_.mipTimerStop(kMipClockTrivialHeuristics);
168-
if (modelstatus_ == HighsModelStatus::kNotset &&
169-
returned_model_status == HighsModelStatus::kInfeasible) {
170-
// trivialHeuristics can spot trivial infeasibility, so act on it
171-
modelstatus_ = returned_model_status;
172-
cleanupSolve();
173-
return;
174-
}
175+
// End of pre-root-node heuristics
175176
if (analysis_.analyse_mip_time && !submip)
176177
if (analysis_.analyse_mip_time & !submip)
177178
highsLogUser(options_mip_->log_options, HighsLogType::kInfo,

highs/pdlp/cupdlp/cupdlp_solver.c

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -843,15 +843,14 @@ void PDHG_Print_Iter(CUPDLPwork *pdhg) {
843843
else
844844
cupdlp_snprintf(timeString, 8, "%6ds", (cupdlp_int)timers->dSolvingTime);
845845

846-
// cupdlp_printf("%9d %+15.8e %+15.8e %+8.2e %8.2e %10.2e %8.2e %7s
847-
// [L]\n",
848-
// timers->nIter, resobj->dPrimalObj, resobj->dDualObj,
849-
// resobj->dDualityGap, resobj->dComplementarity,
850-
// resobj->dPrimalFeas, resobj->dDualFeas, timeString);
851-
846+
CUPDLPscaling *scaling = pdhg->scaling;
847+
const double gap_log_value = resobj->dRelObjGap;
848+
const double primal_feas_log_value = resobj->dPrimalFeas / (1.0 + scaling->dNormRhs);
849+
const double dual_feas_log_value = resobj->dDualFeas / (1.0 + scaling->dNormCost);
852850
cupdlp_printf("%9d %+15.8e %+15.8e %+8.2e %10.2e %8.2e %7s [L]\n",
853851
timers->nIter, resobj->dPrimalObj, resobj->dDualObj,
854-
resobj->dDualityGap, resobj->dPrimalFeas, resobj->dDualFeas,
852+
// resobj->dDualityGap, resobj->dPrimalFeas, resobj->dDualFeas,
853+
gap_log_value, primal_feas_log_value, dual_feas_log_value,
855854
timeString);
856855
}
857856

@@ -865,16 +864,15 @@ void PDHG_Print_Iter_Average(CUPDLPwork *pdhg) {
865864
else
866865
cupdlp_snprintf(timeString, 8, "%6ds", (cupdlp_int)timers->dSolvingTime);
867866

868-
// cupdlp_printf("%9d %+15.8e %+15.8e %+8.2e %8.2e %10.2e %8.2e %7s
869-
// [A]\n",
870-
// timers->nIter, resobj->dPrimalObjAverage,
871-
// resobj->dDualObjAverage, resobj->dDualityGapAverage,
872-
// resobj->dComplementarityAverage, resobj->dPrimalFeasAverage,
873-
// resobj->dDualFeasAverage, timeString);
867+
CUPDLPscaling *scaling = pdhg->scaling;
868+
const double gap_log_value = resobj->dRelObjGapAverage;
869+
const double primal_feas_log_value = resobj->dPrimalFeasAverage / (1.0 + scaling->dNormRhs);
870+
const double dual_feas_log_value = resobj->dDualFeasAverage / (1.0 + scaling->dNormCost);
874871
cupdlp_printf("%9d %+15.8e %+15.8e %+8.2e %10.2e %8.2e %7s [A]\n",
875872
timers->nIter, resobj->dPrimalObjAverage,
876-
resobj->dDualObjAverage, resobj->dDualityGapAverage,
877-
resobj->dPrimalFeasAverage, resobj->dDualFeasAverage,
873+
resobj->dDualObjAverage,
874+
// resobj->dDualityGapAverage, resobj->dPrimalFeasAverage, resobj->dDualFeasAverage,
875+
gap_log_value, primal_feas_log_value, dual_feas_log_value,
878876
timeString);
879877
}
880878

0 commit comments

Comments
 (0)