Skip to content

Commit 4f460a5

Browse files
committed
Merged latest into this branch and resolved two merge conflicts
2 parents a9988cc + 9bf49e1 commit 4f460a5

35 files changed

+983
-388
lines changed

FEATURES.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,9 @@ 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+
<<<<<<< HEAD
1516
Only for LPs is there a choice of solver. Previously, when setting the `solver` option to anything other than "choose", any incumbent model was solved as an LP, using that LP solver. This has caused confusiuon for users, and is unnecessary now that there is the `solve_relaxation` option. Now, if the incumbent model is a QP or MIP, it is solved as such (unless `solve_relaxation` is true for a MIP), and the value of the `solver` option only determines what solver is used to solve an LP. If the value of `solver` is "choose", then HiGHS will use what it expects to be the best solver for the problem; if value of `solver` is "ipm", then HiGHS will use what it expects to be the better IPM solver (of HiPO and IPX) for the problem; if value of `solver` is "hipo", then HiGHS will use the HiPO IPM solver (if available in the build); if value of `solver` is "ipx", then HiGHS will use the IPX IPM solver; if value of `solver` is "pdlp", then HiGHS will use the PDLP first-order solver. The option `mip_lp_solver` has been introduced to define which LP solver is used when solving LPs in the MIP solver for which an advanced basis is not known - typically the "root node" LP. Note that The PDLP solver cannot be used to solve such LPs, since it does not yield a basic solution. If an interior point solver fails to obtain a basic solution, the simplex solver will then be used. The option `mip_ipm_solver` has been introduced to define which IPM solver is used when solving LPs in the MIP solver for which IPM is mandatory - typically the analytic centre calculation. When LPs are to be solved by an IPM solver, the HiPO solver is used (if available in the build) unless IPX has been specified explicitly.
1617

18+
=======
19+
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.
20+
>>>>>>> latest

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/TestCallbacks.cpp

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ struct MipData {
3636
struct UserMipSolution {
3737
double optimal_objective_value;
3838
std::vector<double> optimal_solution;
39-
HighsInt require_user_solution_callback_origin;
39+
HighsInt require_external_solution_query_origin;
4040
};
4141

4242
// Callback that saves message for comparison
@@ -193,8 +193,8 @@ HighsCallbackFunctionType userkMipUserSolution =
193193
void* user_callback_data) {
194194
UserMipSolution callback_data =
195195
*(static_cast<UserMipSolution*>(user_callback_data));
196-
if (data_out->user_solution_callback_origin ==
197-
callback_data.require_user_solution_callback_origin) {
196+
if (data_out->external_solution_query_origin ==
197+
callback_data.require_external_solution_query_origin) {
198198
if (data_out->mip_primal_bound >
199199
callback_data.optimal_objective_value) {
200200
// If current objective value is not optimal, pass the
@@ -203,7 +203,7 @@ HighsCallbackFunctionType userkMipUserSolution =
203203
printf(
204204
"userkMipUserSolution: origin = %d; %g = mip_primal_bound > "
205205
"optimal_objective_value = %g\n",
206-
int(data_out->user_solution_callback_origin),
206+
int(data_out->external_solution_query_origin),
207207
data_out->mip_primal_bound,
208208
callback_data.optimal_objective_value);
209209
data_in->user_has_solution = true;
@@ -218,13 +218,13 @@ HighsCallbackFunctionType userkMipUserSetSolution =
218218
void* user_callback_data) {
219219
const auto& callback_data =
220220
*(static_cast<UserMipSolution*>(user_callback_data));
221-
if (data_out->user_solution_callback_origin ==
222-
callback_data.require_user_solution_callback_origin) {
221+
if (data_out->external_solution_query_origin ==
222+
callback_data.require_external_solution_query_origin) {
223223
if (dev_run)
224224
printf(
225225
"userkMipUserSetSolution: origin = %d; %g = mip_primal_bound > "
226226
"optimal_objective_value = %g\n",
227-
int(data_out->user_solution_callback_origin),
227+
int(data_out->external_solution_query_origin),
228228
data_out->mip_primal_bound,
229229
callback_data.optimal_objective_value);
230230

@@ -239,14 +239,14 @@ HighsCallbackFunctionType userkMipUserSetPartialSolution =
239239
void* user_callback_data) {
240240
const auto& callback_data =
241241
*(static_cast<UserMipSolution*>(user_callback_data));
242-
if (data_out->user_solution_callback_origin ==
243-
callback_data.require_user_solution_callback_origin) {
242+
if (data_out->external_solution_query_origin ==
243+
callback_data.require_external_solution_query_origin) {
244244
if (dev_run)
245245
printf(
246246
"userkMipUserSetPartialSolution: origin = %d; %g = "
247247
"mip_primal_bound > "
248248
"optimal_objective_value = %g\n",
249-
int(data_out->user_solution_callback_origin),
249+
int(data_out->external_solution_query_origin),
250250
data_out->mip_primal_bound,
251251
callback_data.optimal_objective_value);
252252

@@ -520,7 +520,7 @@ static void runMipUserSolutionTest(
520520
UserMipSolution user_callback_data;
521521
user_callback_data.optimal_objective_value = objective_function_value0;
522522
user_callback_data.optimal_solution = optimal_solution;
523-
user_callback_data.require_user_solution_callback_origin =
523+
user_callback_data.require_external_solution_query_origin =
524524
require_origin[iModel];
525525
void* p_user_callback_data = (void*)(&user_callback_data);
526526

check/TestIpm.cpp

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

209209
h.resetGlobalScheduler(true);
210210
}
211+
212+
TEST_CASE("test-2527", "[highs_ipm]") {
213+
std::string filename =
214+
std::string(HIGHS_DIR) + "/check/instances/primal1.mps";
215+
Highs h;
216+
// h.setOptionValue("output_flag", dev_run);
217+
REQUIRE(h.readModel(filename) == HighsStatus::kOk);
218+
HighsLp lp = h.getLp();
219+
lp.col_cost_.assign(lp.num_col_, 0);
220+
REQUIRE(h.passModel(lp) == HighsStatus::kOk);
221+
h.setOptionValue("solver", kIpmString);
222+
h.setOptionValue("presolve", kHighsOffString);
223+
REQUIRE(h.run() == HighsStatus::kOk);
224+
225+
h.resetGlobalScheduler(true);
226+
}

check/TestMipSolver.cpp

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,12 +40,16 @@ TEST_CASE("MIP-rowless-1", "[highs_test_mip_solver]") {
4040
Highs highs;
4141
if (!dev_run) highs.setOptionValue("output_flag", false);
4242
rowlessMIP1(highs);
43+
44+
highs.resetGlobalScheduler(true);
4345
}
4446

4547
TEST_CASE("MIP-rowless-2", "[highs_test_mip_solver]") {
4648
Highs highs;
4749
if (!dev_run) highs.setOptionValue("output_flag", false);
4850
rowlessMIP2(highs);
51+
52+
highs.resetGlobalScheduler(true);
4953
}
5054

5155
TEST_CASE("MIP-solution-limit", "[highs_test_mip_solver]") {
@@ -79,6 +83,8 @@ TEST_CASE("MIP-solution-limit", "[highs_test_mip_solver]") {
7983
REQUIRE(highs.getModelStatus() == HighsModelStatus::kSolutionLimit);
8084
highs.setOptionValue("mip_max_improving_sols", kHighsIInf);
8185
highs.clearSolver();
86+
87+
highs.resetGlobalScheduler(true);
8288
}
8389

8490
TEST_CASE("MIP-integrality", "[highs_test_mip_solver]") {
@@ -173,6 +179,8 @@ TEST_CASE("MIP-integrality", "[highs_test_mip_solver]") {
173179
REQUIRE(info.mip_node_count == 1);
174180
REQUIRE(fabs(info.mip_dual_bound + 6) < double_equal_tolerance);
175181
REQUIRE(std::fabs(info.mip_gap) < 1e-12);
182+
183+
highs.resetGlobalScheduler(true);
176184
}
177185

178186
TEST_CASE("MIP-clear-integrality", "[highs_test_mip_solver]") {
@@ -215,6 +223,8 @@ TEST_CASE("MIP-nmck", "[highs_test_mip_solver]") {
215223
REQUIRE(info.num_primal_infeasibilities == 0);
216224
REQUIRE(info.max_primal_infeasibility == 0);
217225
REQUIRE(info.sum_primal_infeasibilities == 0);
226+
227+
highs.resetGlobalScheduler(true);
218228
}
219229

220230
TEST_CASE("MIP-maximize", "[highs_test_mip_solver]") {
@@ -295,6 +305,8 @@ TEST_CASE("MIP-maximize", "[highs_test_mip_solver]") {
295305
REQUIRE(std::abs(info.objective_function_value - info.mip_dual_bound) <=
296306
options.mip_abs_gap);
297307
REQUIRE(std::abs(info.mip_gap) <= options.mip_rel_gap);
308+
309+
highs.resetGlobalScheduler(true);
298310
}
299311

300312
TEST_CASE("MIP-unbounded", "[highs_test_mip_solver]") {
@@ -403,6 +415,8 @@ TEST_CASE("MIP-unbounded", "[highs_test_mip_solver]") {
403415

404416
model_status = highs.getModelStatus();
405417
REQUIRE(model_status == HighsModelStatus::kInfeasible);
418+
419+
highs.resetGlobalScheduler(true);
406420
}
407421

408422
TEST_CASE("MIP-od", "[highs_test_mip_solver]") {
@@ -470,6 +484,8 @@ TEST_CASE("MIP-od", "[highs_test_mip_solver]") {
470484
double_equal_tolerance);
471485
REQUIRE(fabs(solution.col_value[0] - required_x0_value) <
472486
double_equal_tolerance);
487+
488+
highs.resetGlobalScheduler(true);
473489
}
474490

475491
TEST_CASE("MIP-infeasible-start", "[highs_test_mip_solver]") {
@@ -513,6 +529,8 @@ TEST_CASE("MIP-infeasible-start", "[highs_test_mip_solver]") {
513529
HighsStatus::kOk);
514530
highs.run();
515531
REQUIRE(model_status == HighsModelStatus::kInfeasible);
532+
533+
highs.resetGlobalScheduler(true);
516534
}
517535

518536
TEST_CASE("get-integrality", "[highs_test_mip_solver]") {}
@@ -556,6 +574,8 @@ TEST_CASE("MIP-bounds", "[highs_test_mip_solver]") {
556574
obj1);
557575
REQUIRE(obj0 == obj1);
558576
std::remove(test_mps.c_str());
577+
578+
highs.resetGlobalScheduler(true);
559579
}
560580

561581
TEST_CASE("MIP-get-saved-solutions", "[highs_test_mip_solver]") {
@@ -583,6 +603,8 @@ TEST_CASE("MIP-get-saved-solutions", "[highs_test_mip_solver]") {
583603
REQUIRE(saved_objective_and_solution[last_saved_solution].col_value[iCol] ==
584604
highs.getSolution().col_value[iCol]);
585605
std::remove(solution_file.c_str());
606+
607+
highs.resetGlobalScheduler(true);
586608
}
587609

588610
TEST_CASE("MIP-objective-target", "[highs_test_mip_solver]") {
@@ -597,6 +619,8 @@ TEST_CASE("MIP-objective-target", "[highs_test_mip_solver]") {
597619
highs.run();
598620
REQUIRE(highs.getModelStatus() == HighsModelStatus::kObjectiveTarget);
599621
REQUIRE(highs.getInfo().objective_function_value > egout_optimal_objective);
622+
623+
highs.resetGlobalScheduler(true);
600624
}
601625

602626
TEST_CASE("MIP-max-offset-test", "[highs_test_mip_solver]") {
@@ -625,6 +649,8 @@ TEST_CASE("MIP-max-offset-test", "[highs_test_mip_solver]") {
625649
highs.getInfo().objective_function_value;
626650
REQUIRE(objectiveOk(max_offset_optimal_objective, -offset_optimal_objective,
627651
dev_run));
652+
653+
highs.resetGlobalScheduler(true);
628654
}
629655

630656
TEST_CASE("MIP-get-saved-solutions-presolve", "[highs_test_mip_solver]") {
@@ -663,6 +689,8 @@ TEST_CASE("MIP-get-saved-solutions-presolve", "[highs_test_mip_solver]") {
663689
REQUIRE(saved_objective_and_solution[last_saved_solution].col_value[iCol] ==
664690
highs.getSolution().col_value[iCol]);
665691
std::remove(solution_file.c_str());
692+
693+
highs.resetGlobalScheduler(true);
666694
}
667695

668696
TEST_CASE("IP-infeasible-unbounded", "[highs_test_mip_solver]") {
@@ -720,6 +748,8 @@ TEST_CASE("IP-infeasible-unbounded", "[highs_test_mip_solver]") {
720748
}
721749
highs.setOptionValue("presolve", kHighsOnString);
722750
}
751+
752+
highs.resetGlobalScheduler(true);
723753
}
724754

725755
TEST_CASE("IP-with-fract-bounds-no-presolve", "[highs_test_mip_solver]") {
@@ -755,6 +785,8 @@ TEST_CASE("IP-with-fract-bounds-no-presolve", "[highs_test_mip_solver]") {
755785

756786
// Infeasible
757787
REQUIRE(highs.getModelStatus() == HighsModelStatus::kInfeasible);
788+
789+
highs.resetGlobalScheduler(true);
758790
}
759791

760792
bool objectiveOk(const double optimal_objective,
@@ -787,6 +819,8 @@ void solve(Highs& highs, std::string presolve,
787819
require_optimal_objective, dev_run));
788820
}
789821
REQUIRE(highs.resetOptions() == HighsStatus::kOk);
822+
823+
highs.resetGlobalScheduler(true);
790824
}
791825

792826
void distillationMIP(Highs& highs) {

check/TestPresolve.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -838,4 +838,4 @@ TEST_CASE("presolve-egout-ac", "[highs_test_presolve]") {
838838
lp_presolve_requires_basis_postsolve);
839839

840840
h.resetGlobalScheduler(true);
841-
}
841+
}

check/TestSemiVariables.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ TEST_CASE("semi-variable-model", "[highs_test_semi_variables]") {
1717
const HighsInfo& info = highs.getInfo();
1818
HighsStatus return_status;
1919
double optimal_objective_function_value;
20-
if (!dev_run) highs.setOptionValue("output_flag", false);
20+
highs.setOptionValue("output_flag", dev_run);
2121
HighsModel model;
2222
HighsLp& lp = model.lp_;
2323
semiModel0(lp);

highs/Highs.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1722,7 +1722,7 @@ class Highs {
17221722
bool qFormatOk(const HighsInt num_nz, const HighsInt format);
17231723
void clearZeroHessian();
17241724
HighsStatus checkOptimality(const std::string& solver_type);
1725-
HighsStatus lpKktCheck(const std::string& message);
1725+
HighsStatus lpKktCheck(const HighsLp& lp, const std::string& message = "");
17261726
HighsStatus invertRequirementError(std::string method_name) const;
17271727

17281728
HighsStatus handleInfCost();

highs/highs_bindings.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -975,7 +975,8 @@ PYBIND11_MODULE(_core, m, py::mod_gil_not_used()) {
975975
.value("kUnknown", HighsModelStatus::kUnknown)
976976
.value("kSolutionLimit", HighsModelStatus::kSolutionLimit)
977977
.value("kInterrupt", HighsModelStatus::kInterrupt)
978-
.value("kMemoryLimit", HighsModelStatus::kMemoryLimit);
978+
.value("kMemoryLimit", HighsModelStatus::kMemoryLimit)
979+
.value("kHighsInterrupt", HighsModelStatus::kHighsInterrupt);
979980
py::enum_<HighsPresolveStatus>(m, "HighsPresolveStatus", py::module_local())
980981
.value("kNotPresolved", HighsPresolveStatus::kNotPresolved)
981982
.value("kNotReduced", HighsPresolveStatus::kNotReduced)

0 commit comments

Comments
 (0)