Skip to content

Commit dd69d7a

Browse files
committed
Merge branch 'latest' of https://github.com/ERGO-Code/HiGHS into refactorPreDomChecks
2 parents aa79316 + f8b218d commit dd69d7a

20 files changed

Lines changed: 557 additions & 100 deletions

‎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.

‎README.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ linear optimization problems of the form
4646

4747
$$ \min \quad \dfrac{1}{2}x^TQx + c^Tx \qquad \textrm{s.t.}~ \quad L \leq Ax \leq U; \quad l \leq x \leq u $$
4848

49-
where Q must be positive semi-definite and, if Q is zero, there may be a requirement that some of the variables take integer values. Thus HiGHS can solve linear programming (LP) problems, convex quadratic programming (QP) problems, and mixed integer programming (MIP) problems. It is mainly written in C++, but also has some C. It has been developed and tested on various Linux, MacOS and Windows installations. No third-party dependencies are required.
49+
where $Q$ must be positive semi-definite and, if $Q$ is zero, there may be a requirement that some of the variables take integer values. Thus HiGHS can solve linear programming (LP) problems, convex quadratic programming (QP) problems, and mixed integer programming (MIP) problems. It is mainly written in C++, but also has some C. It has been developed and tested on various Linux, MacOS and Windows installations. No third-party dependencies are required.
5050

5151
HiGHS has primal and dual revised simplex solvers, originally written by Qi Huangfu and further developed by Julian Hall. It also has an interior point solver for LP written by Lukas Schork, an active set solver for QP written by Michael Feldmeier, and a MIP solver written by Leona Gottwald. Other features have been added by Julian Hall and Ivet Galabova, who manages the software engineering of HiGHS and interfaces to C, C#, FORTRAN, Julia and Python.
5252

‎check/TestBasis.cpp‎

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,3 +315,43 @@ void testBasisRestart(Highs& highs, const std::string& basis_file,
315315

316316
REQUIRE(info.simplex_iteration_count == 0);
317317
}
318+
319+
TEST_CASE("Basis-read", "[highs_basis_data]") {
320+
// Duplicates test_read_basis in test_highspy.py
321+
const std::string test_name = Catch::getResultCapture().getCurrentTestName();
322+
323+
HighsLp lp;
324+
lp.num_col_ = 2;
325+
lp.num_row_ = 2;
326+
lp.col_cost_ = {0, 1};
327+
lp.col_lower_.assign(lp.num_col_, -kHighsInf);
328+
lp.col_upper_.assign(lp.num_col_, kHighsInf);
329+
lp.row_lower_ = {2, 0};
330+
lp.row_upper_.assign(lp.num_row_, kHighsInf);
331+
lp.a_matrix_.start_ = {0, 2, 4};
332+
lp.a_matrix_.index_ = {0, 1, 0, 1};
333+
lp.a_matrix_.value_ = {-1, 1, 1, 1};
334+
335+
HighsBasisStatus status_before = HighsBasisStatus::kNonbasic;
336+
HighsBasisStatus status_after = HighsBasisStatus::kBasic;
337+
Highs h1;
338+
const HighsBasis& basis1 = h1.getBasis();
339+
h1.passModel(lp);
340+
REQUIRE(basis1.col_status[0] == status_before);
341+
h1.run();
342+
REQUIRE(basis1.col_status[0] == status_after);
343+
344+
Highs h2;
345+
const HighsBasis& basis2 = h2.getBasis();
346+
h2.passModel(lp);
347+
REQUIRE(basis2.col_status[0] == status_before);
348+
349+
const std::string basis_file = test_name + ".bas";
350+
h1.writeBasis(basis_file);
351+
h2.readBasis(basis_file);
352+
REQUIRE(basis2.col_status[0] == status_after);
353+
354+
std::remove(basis_file.c_str());
355+
h1.resetGlobalScheduler(true);
356+
h2.resetGlobalScheduler(true);
357+
}

‎check/TestIpm.cpp‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,3 +200,19 @@ TEST_CASE("test-2087", "[highs_ipm]") {
200200

201201
h.resetGlobalScheduler(true);
202202
}
203+
204+
TEST_CASE("test-2527", "[highs_ipm]") {
205+
std::string filename =
206+
std::string(HIGHS_DIR) + "/check/instances/primal1.mps";
207+
Highs h;
208+
// h.setOptionValue("output_flag", dev_run);
209+
REQUIRE(h.readModel(filename) == HighsStatus::kOk);
210+
HighsLp lp = h.getLp();
211+
lp.col_cost_.assign(lp.num_col_, 0);
212+
REQUIRE(h.passModel(lp) == HighsStatus::kOk);
213+
h.setOptionValue("solver", kIpmString);
214+
h.setOptionValue("presolve", kHighsOffString);
215+
REQUIRE(h.run() == HighsStatus::kOk);
216+
217+
h.resetGlobalScheduler(true);
218+
}

‎check/TestPresolve.cpp‎

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -838,3 +838,27 @@ TEST_CASE("presolve-egout-ac", "[highs_test_presolve]") {
838838

839839
h.resetGlobalScheduler(true);
840840
}
841+
842+
TEST_CASE("dual-bound-tightening", "[highs_test_presolve]") {
843+
std::string model_file =
844+
std::string(HIGHS_DIR) + "/check/instances/gesa2.mps";
845+
846+
Highs highs;
847+
highs.setOptionValue("output_flag", dev_run);
848+
highs.readModel(model_file);
849+
850+
// complement variables to get code coverage
851+
HighsLp lp = highs.getLp();
852+
std::transform(lp.a_matrix_.value_.begin(), lp.a_matrix_.value_.end(),
853+
lp.a_matrix_.value_.begin(), [](double v) { return -v; });
854+
std::transform(lp.col_cost_.begin(), lp.col_cost_.end(), lp.col_cost_.begin(),
855+
[](double v) { return -v; });
856+
std::transform(lp.col_upper_.begin(), lp.col_upper_.end(),
857+
lp.col_upper_.begin(), [](double v) { return -v; });
858+
std::transform(lp.col_lower_.begin(), lp.col_lower_.end(),
859+
lp.col_lower_.begin(), [](double v) { return -v; });
860+
std::swap(lp.col_lower_, lp.col_upper_);
861+
862+
highs.passModel(lp);
863+
REQUIRE(highs.presolve() == HighsStatus::kOk);
864+
}

‎highs/interfaces/highs_c_api.h‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -400,7 +400,9 @@ HighsInt Highs_presolve(void* highs);
400400
HighsInt Highs_run(void* highs);
401401

402402
/**
403-
* Postsolve a model using a primal (and possibly dual) solution.
403+
* Postsolve a model using a primal (and possibly dual) solution. The
404+
* postsolved solution can be retrieved later by calling
405+
* `Highs_getSolution`.
404406
*
405407
* @param highs A pointer to the Highs instance.
406408
* @param col_value An array of length [num_col] with the column solution

‎highs/ipm/ipx/basis.cc‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,12 +119,21 @@ Int Basis::Factorize() {
119119

120120
// Build column pointers for passing to LU factorization.
121121
std::vector<Int> begin(m), end(m);
122+
Int basis_num_nz = 0;
122123
for (Int i = 0; i < m; i++) {
123124
assert(basis_[i] >= 0);
124125
begin[i] = AI.begin(basis_[i]);
125126
end[i] = AI.end(basis_[i]);
127+
basis_num_nz += (end[i]-begin[i]);
126128
}
127129

130+
std::stringstream h_logging_stream;
131+
h_logging_stream.str(std::string());
132+
h_logging_stream <<
133+
" Start factorization " << num_factorizations_+1 <<
134+
": nonzeros in basis = " << basis_num_nz << "\n";
135+
control_.hIntervalLog(h_logging_stream);
136+
128137
Int err = 0; // return code
129138
while (true) {
130139
Int flag = lu_->Factorize(begin.data(), end.data(), AI.rowidx(),
@@ -151,6 +160,11 @@ Int Basis::Factorize() {
151160
}
152161
time_factorize_ += timer.Elapsed();
153162
factorization_is_fresh_ = true;
163+
h_logging_stream.str(std::string());
164+
h_logging_stream <<
165+
" Finish factorization " << num_factorizations_ <<
166+
": fill factor = " << lu_->fill_factor() << "\n";
167+
control_.hIntervalLog(h_logging_stream);
154168
return err;
155169
}
156170

‎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.

0 commit comments

Comments
 (0)