Skip to content

Commit 0c57776

Browse files
committed
Merge branch 'latest' into strongcg
2 parents bf28f6a + 6f57c32 commit 0c57776

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

76 files changed

+1580
-862
lines changed

.github/workflows/build-fast.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ jobs:
1818
- name: Configure CMake
1919
shell: bash
2020
working-directory: ${{runner.workspace}}/build
21-
run: cmake $GITHUB_WORKSPACE -DFAST_BUILD=ON -DCMAKE_BUILD_TYPE=RELEASE
21+
run: cmake $GITHUB_WORKSPACE -DFAST_BUILD=ON -DCMAKE_BUILD_TYPE=RELEASE -DCMAKE_UNITY_BUILD=ON
2222

2323
- name: Build
2424
working-directory: ${{runner.workspace}}/build
@@ -45,7 +45,7 @@ jobs:
4545
- name: Configure CMake
4646
shell: bash
4747
working-directory: ${{runner.workspace}}/build
48-
run: cmake $GITHUB_WORKSPACE -DFAST_BUILD=ON -DCMAKE_BUILD_TYPE=DEBUG
48+
run: cmake $GITHUB_WORKSPACE -DFAST_BUILD=ON -DCMAKE_BUILD_TYPE=DEBUG -DCMAKE_UNITY_BUILD=ON
4949

5050
- name: Build
5151
working-directory: ${{runner.workspace}}/build

.github/workflows/build-wheels-push.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ jobs:
5959
- uses: actions/checkout@v4
6060

6161
- name: Build wheels
62-
uses: pypa/cibuildwheel@v2.21
62+
uses: pypa/cibuildwheel@v3.0
6363
env:
6464
CIBW_BUILD: ${{ matrix.python }}-${{ matrix.buildplat[1] }}
6565

.github/workflows/build-wheels.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name: build-wheels
22

3-
on: [push]
4-
# on: [pull_request]
3+
# on: [push]
4+
on: [pull_request]
55

66
concurrency:
77
group: ${{ github.workflow }}-${{ github.ref }}
@@ -47,6 +47,6 @@ jobs:
4747
steps:
4848
- uses: actions/checkout@v4
4949
- name: Build wheels
50-
uses: pypa/cibuildwheel@v2.21
50+
uses: pypa/cibuildwheel@v3.0
5151
env:
5252
CIBW_BUILD: ${{ matrix.python }}-${{ matrix.buildplat[1] }}

CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ option(BUILD_CXX "Build C++ library" ON)
7373
message(STATUS "Build C++ library: ${BUILD_CXX}")
7474

7575
option(BUILD_CXX_EXE "Build C++ executable" ON)
76-
message(STATUS "Build C++ library: ${BUILD_CXX_EXE}")
76+
message(STATUS "Build C++ executable: ${BUILD_CXX_EXE}")
7777

7878
option(BUILD_TESTING "Build Tests" ON)
7979

FEATURES.md

Lines changed: 4 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,61 +1,12 @@
11
## Build changes
22

3-
Replace command line parsing library with Cpp11 [#2211](https://github.com/ERGO-Code/HiGHS/pull/2211)
4-
5-
CMake updates [#2286](https://github.com/ERGO-Code/HiGHS/pull/2286), the root of the HiGHS source files is now `highs/`, rather than `src/`
6-
7-
Add missing include to zstr needed for gcc v15 and clang v19 [#2313](https://github.com/ERGO-Code/HiGHS/pull/2313)
8-
9-
Updates for intel llvm compiler on linux [#2257](https://github.com/ERGO-Code/HiGHS/pull/2257)
10-
11-
Build directory for Windows MSVC now `Release/bin` replacing `bin/Release` [#2376](https://github.com/ERGO-Code/HiGHS/pull/2376)
12-
133
## Code changes
144

15-
Fixed incorrect assertion in `HighsMipSolver::solutionFeasible()` (fixing [#2204](https://github.com/ERGO-Code/HiGHS/issues/2204))
16-
17-
As part of [#2251](https://github.com/ERGO-Code/HiGHS/issues/2251) cuPDLP-C will start from the incumbent solution in HiGHS. For a model that has been changed, the user must supply a starting solution via a call to `Highs::setSolution`
18-
19-
getColIntegrality now returns `HighsVarType::kContinuous` when `model_.lp_.integrality_` is empty (fixing [#2261](https://github.com/ERGO-Code/HiGHS/issues/2261))
20-
21-
Now ensuring that when solving a scaled LP with useful but unvalidated basis, it does not lose its scaling after validation, since the scaling factors will be applied to the solution (fixing [#2267](https://github.com/ERGO-Code/HiGHS/issues/2267))
22-
23-
By setting non-empty values of options `read_solution_file`, `read_basis_file`, `write_model_file` (with extension `.lp` or `.mps`), `write_solution_file`, `solution_file`, `write_basis_file`, these files will be read or written when calling `Highs::run()`. Hence options previously only available via the command line interface can be use (for example) by modelling languages that only call `Highs::run()` (fixing [#2269](https://github.com/ERGO-Code/HiGHS/issues/2269)).
24-
25-
Bug [#2273](https://github.com/ERGO-Code/HiGHS/issues/2273) fixed
26-
27-
Added the feasibility jump heuristic as per [#1423](https://github.com/ERGO-Code/HiGHS/issues/1423). This is on by default, but using the option `mip_heuristic_run_feasibility_jump=false` switches it off.
28-
29-
ZI rounding and shifting MIP primal heuristics have been added (see [#2287](https://github.com/ERGO-Code/HiGHS/pull/2287)). They are off by default, but can be activated by setting the options `mip_heuristic_run_zi_round` and `mip_heuristic_run_shifting` to be true. Options `mip_heuristic_run_rins`, `mip_heuristic_run_rens` and `mip_heuristic_run_root_reduced_cost` to run the RINS, RENS and rootReducedCost heuristics have been added. These are true by default, but setting them to be false can accelerate the MIP solver on easy problems.
30-
31-
Added `Highs_changeRowsBoundsByRange` to C API, fixing [#2296](https://github.com/ERGO-Code/HiGHS/issues/2296)
32-
33-
Corrected docstrings for `Highs_getReducedRow`, motivated by [#2312](https://github.com/ERGO-Code/HiGHS/issues/2312)
34-
35-
LP file reader no longer fails when there is no objective section. Fix is [#2316](https://github.com/ERGO-Code/HiGHS/pull/2316), but this exposes code quality issue [#2318](https://github.com/ERGO-Code/HiGHS/issues/2318)
36-
37-
Added a max scale factor (+1024) when scaling up coefficients in `preprocessBaseInequality` and `postprocessCut`. Fix is [#2337](https://github.com/ERGO-Code/HiGHS/pull/2337)
38-
39-
Corrected the bounds used in when strengthening coefficients in `HPresolve::rowPresolve`, fixing [#1517](https://github.com/ERGO-Code/HiGHS/issues/1517)
40-
41-
Fixed numerical error in `highs/mip/HighsCliqueTable.cpp`, closing [#2320](https://github.com/ERGO-Code/HiGHS/issues/2320)
42-
43-
Fixed bug in `highs/mip/HighsFeasibilityJump.cpp`, closing [#2331](https://github.com/ERGO-Code/HiGHS/issues/2331)
44-
45-
Tightened CMIR cuts, leading to small performance gain, closing [#2333](https://github.com/ERGO-Code/HiGHS/issues/2333)
46-
47-
Scaling the tolerance in forcing row reduction to avoid use of rows with small coefficients and bounds, closing [#2290](https://github.com/ERGO-Code/HiGHS/issues/2290)
48-
49-
Fixed bug when calculating a coefficient in one of the cuts in `separateImpliedBounds` in `highs/mip/HighsImplications.cpp`
50-
51-
Added `CSECTION` to the exceptions for keywords that are followed by text, and thus cannot be used as names of columns, RHS, ranges, bounds etc.
52-
53-
Introduced the following KKT error measures to `HighsInfo`: `num_relative_primal_infeasibilities`; `max_relative_primal_infeasibility`; `num_relative_dual_infeasibilities`; `max_relative_dual_infeasibility`; `num_primal_residual_errors`; `max_primal_residual_error`; `num_dual_residual_errors`; `max_dual_residual_error`; `num_relative_primal_residual_errors`; `max_relative_primal_residual_error`; `num_relative_dual_residual_errors`; `max_relative_dual_residual_error`; `num_complementarity_violations`; `max_complementarity_violation`; `primal_dual_objective_error.` The relative values are used to assess whether a solution deemed to be optimal by the first order LP solver `cuPDLP-C` or interior point solver `IPX` (without crossover) is truly acceptable. They also enable users to determine whether a solution corresponding to `HighsModelStatus::kUnknown` is acceptable to them as optimal. Also introduced options `complementarity_tolerance` used to assess whether the (relative) primal-dual objective error is acceptable, and `kkt_tolerance` which, if set to a value other than `kDefaultKktTolerance = 1e-7` is used as the tolerance for all the KKT error measures. The HiGHS documentation has been updated to reflect the new options and `HighsInfo` data, and logging messages indicate when KKT error measures are not satisfied, despite the solver considering the LP solution to be optimal.
54-
55-
Added a max scale factor (+1024) when scaling up coefficients in `preprocessBaseInequality` and `postprocessCut`. Fix is [#2337](https://github.com/ERGO-Code/HiGHS/pull/2337).
5+
Forcing column reduction now checks the bound on the column dual rather than whether the dual row activity is zero fixing [#2409](https://github.com/ERGO-Code/HiGHS/issues/2409)
566

57-
Renamed `HighsOptions::pdlp_d_gap_tol` to `HighsOptions::pdlp_optimality_tolerance` for consistency with IPM
7+
Now handling correctly the case where an infeasible MIP has a feasible relaxation, so no ray is computed fixing [#2415](https://github.com/ERGO-Code/HiGHS/issues/2415)
588

59-
Renamed `HighsOptions::complementarity_tolerance` to `HighsOptions::optimality_tolerance` for consistency with IPM and PDLP
9+
Fixed minor bug exposed by [#2441](https://github.com/ERGO-Code/HiGHS/issues/2441) in Highs::setSolution() for a sparse user solution when the moidel is empty, and only clearing the dual data before solving with modified objective in Highs::multiobjectiveSolve() so that user-supplied solution is not cleared.
6010

11+
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.
6112

app/CMakeLists.txt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ if(FAST_BUILD)
2525
endif()
2626

2727
set_target_properties(highs-bin PROPERTIES OUTPUT_NAME highs)
28+
set_target_properties(highs-bin PROPERTIES UNITY_BUILD OFF)
2829

2930
target_compile_features(highs-bin PRIVATE cxx_std_11)
3031
target_link_libraries(highs-bin PRIVATE ${PROJECT_NAMESPACE}::highs)
@@ -78,6 +79,8 @@ else()
7879
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/app>
7980
)
8081

82+
set_target_properties(highs PROPERTIES UNITY_BUILD OFF)
83+
8184
# install the binary
8285
install(TARGETS highs EXPORT highs-targets
8386
RUNTIME)
@@ -87,4 +90,4 @@ endif()
8790
if(EMSCRIPTEN AND EMSCRIPTEN_HTML)
8891
set(CMAKE_EXECUTABLE_SUFFIX ".html")
8992
set_target_properties(highs PROPERTIES LINK_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/highs_webdemo_shell.html)
90-
endif()
93+
endif()

check/CMakeLists.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,8 @@ if ((NOT FAST_BUILD OR ALL_TESTS) AND NOT (BUILD_EXTRA_UNIT_ONLY))
106106

107107
add_executable(unit_tests ${TEST_SOURCES})
108108

109+
set_target_properties(unit_tests PROPERTIES UNITY_BUILD OFF)
110+
109111
if (UNIX)
110112
target_compile_options(unit_tests PRIVATE "-Wno-unused-variable")
111113
target_compile_options(unit_tests PRIVATE "-Wno-unused-const-variable")
@@ -258,7 +260,7 @@ if ((NOT FAST_BUILD OR ALL_TESTS) AND NOT (BUILD_EXTRA_UNIT_ONLY))
258260
"lseu\;1120|1119.9999999\;"
259261
"egout\;(568.1007|568.1006999)\;"
260262
"gt2\;21166\;"
261-
"rgn\;82.1999992\;"
263+
"rgn\;82.19999\;"
262264
"bell5\;(8966406.49152|8966406.491519|8966406.49151)\;"
263265
"sp150x300d\;(69|68.9999999)\;"
264266
"p0548\;(8691|8690.9999999)\;"

check/TestBasis.cpp

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,16 @@ TEST_CASE("Basis-file", "[highs_basis_file]") {
5656
f << "HiGHS v1" << std::endl;
5757
f << "None" << std::endl;
5858
f.close();
59-
return_status = highs.readBasis(invalid_basis_file);
60-
REQUIRE(return_status == HighsStatus::kOk);
59+
// HiGHS v1 basis file is deprecated, but read, so warning is
60+
// returned
61+
REQUIRE(highs.readBasis(invalid_basis_file) == HighsStatus::kWarning);
62+
63+
// Write and read a file for an invalid basis
64+
f.open(invalid_basis_file, std::ios::out);
65+
f << "HiGHS_basis_file v2" << std::endl;
66+
f << "None" << std::endl;
67+
f.close();
68+
REQUIRE(highs.readBasis(invalid_basis_file) == HighsStatus::kOk);
6169

6270
// Write and read a file for incompatible number of columns
6371
f.open(invalid_basis_file, std::ios::out);

check/TestCheckSolution.cpp

Lines changed: 163 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -90,9 +90,9 @@ TEST_CASE("check-set-mip-solution", "[highs_check_solution]") {
9090
if (dev_run) printf("Num nodes = %d\n", int(scratch_num_nodes));
9191

9292
std::string solution_file = test_name + model + ".sol";
93-
if (dev_run) return_status = highs.writeSolution("");
94-
return_status = highs.writeSolution(solution_file);
95-
REQUIRE(return_status == HighsStatus::kOk);
93+
if (dev_run) REQUIRE(highs.writeSolution("") == HighsStatus::kOk);
94+
;
95+
REQUIRE(highs.writeSolution(solution_file) == HighsStatus::kOk);
9696

9797
highs.clear();
9898

@@ -459,6 +459,118 @@ TEST_CASE("read-miplib-solution", "[highs_check_solution]") {
459459
h.resetGlobalScheduler(true);
460460
}
461461

462+
TEST_CASE("read-lp-file-solution", "[highs_check_solution]") {
463+
const std::string test_name = Catch::getResultCapture().getCurrentTestName();
464+
const std::string model_file_name = test_name + ".lp";
465+
const std::string solution_file_name = test_name + ".sol";
466+
const bool with_names = false;
467+
HighsLp lp;
468+
lp.num_col_ = 3;
469+
lp.num_row_ = 2;
470+
lp.col_cost_ = {0, 1, 1};
471+
lp.col_lower_ = {0, 10, 0};
472+
lp.col_upper_ = {kHighsInf, kHighsInf, kHighsInf};
473+
if (with_names) lp.col_names_ = {"x", "y", "z"};
474+
lp.row_lower_ = {1, -kHighsInf};
475+
lp.row_upper_ = {kHighsInf, 2};
476+
if (with_names) lp.row_names_ = {"r-lo", "r-up"};
477+
lp.a_matrix_.start_ = {0, 2, 2, 4};
478+
lp.a_matrix_.index_ = {0, 1, 0, 1};
479+
lp.a_matrix_.value_ = {1, 1, 1, 1};
480+
lp.integrality_ = {HighsVarType::kContinuous, HighsVarType::kContinuous,
481+
HighsVarType::kInteger};
482+
Highs h;
483+
h.setOptionValue("output_flag", dev_run);
484+
REQUIRE(h.passModel(lp) == HighsStatus::kOk);
485+
h.run();
486+
h.writeModel(model_file_name);
487+
h.writeSolution(solution_file_name);
488+
489+
h.readModel(model_file_name);
490+
h.writeModel("");
491+
h.readSolution(solution_file_name);
492+
h.run();
493+
494+
std::remove(model_file_name.c_str());
495+
std::remove(solution_file_name.c_str());
496+
497+
h.resetGlobalScheduler(true);
498+
}
499+
500+
TEST_CASE("read-lp-file-basis", "[highs_check_solution]") {
501+
const std::string test_name = Catch::getResultCapture().getCurrentTestName();
502+
const std::string model_file_name = test_name + ".lp";
503+
const std::string basis_file_name = test_name + ".bas";
504+
const bool with_names = false;
505+
HighsLp lp;
506+
lp.num_col_ = 3;
507+
lp.num_row_ = 2;
508+
lp.col_cost_ = {0, 1, 1};
509+
lp.col_lower_ = {0, 10, 0};
510+
lp.col_upper_ = {kHighsInf, kHighsInf, kHighsInf};
511+
if (with_names) lp.col_names_ = {"x", "y", "z"};
512+
lp.row_lower_ = {1, -kHighsInf};
513+
lp.row_upper_ = {kHighsInf, 2};
514+
if (with_names) lp.row_names_ = {"r-lo", "r-up"};
515+
lp.a_matrix_.start_ = {0, 2, 2, 4};
516+
lp.a_matrix_.index_ = {0, 1, 0, 1};
517+
lp.a_matrix_.value_ = {1, 1, 1, 1};
518+
Highs h;
519+
h.setOptionValue("output_flag", dev_run);
520+
REQUIRE(h.passModel(lp) == HighsStatus::kOk);
521+
h.run();
522+
// Optimally x - basic; y - lower; z - lower
523+
h.writeModel(model_file_name);
524+
h.writeSolution("", 1);
525+
h.writeBasis("");
526+
h.writeBasis(basis_file_name);
527+
528+
h.readModel(model_file_name);
529+
// Variables now ordered y; z; x
530+
h.writeModel("");
531+
h.readBasis(basis_file_name);
532+
// Old read basis yields initial basis: y - basic; z - lower; x -
533+
// lower, using basis for original ordering with new ordering. Not
534+
// optimal - in fact basis matrix B = [0] is singular!
535+
h.run();
536+
REQUIRE(h.getInfo().simplex_iteration_count == 0);
537+
538+
std::remove(model_file_name.c_str());
539+
std::remove(basis_file_name.c_str());
540+
541+
h.resetGlobalScheduler(true);
542+
}
543+
544+
TEST_CASE("read-lp-file-rgn", "[highs_check_solution]") {
545+
const std::string test_name = Catch::getResultCapture().getCurrentTestName();
546+
const std::string filename =
547+
std::string(HIGHS_DIR) + "/check/instances/rgn.mps";
548+
const std::string model_file_name = test_name + ".lp";
549+
const std::string solution_file_name = test_name + ".sol";
550+
Highs h;
551+
h.setOptionValue("output_flag", dev_run);
552+
REQUIRE(h.readModel(filename) == HighsStatus::kOk);
553+
REQUIRE(h.run() == HighsStatus::kOk);
554+
REQUIRE(h.writeSolution(solution_file_name) == HighsStatus::kOk);
555+
REQUIRE(h.writeModel(model_file_name) == HighsStatus::kOk);
556+
557+
REQUIRE(h.readModel(model_file_name) == HighsStatus::kOk);
558+
REQUIRE(h.readSolution(solution_file_name) == HighsStatus::kOk);
559+
bool valid;
560+
bool integral;
561+
bool feasible;
562+
REQUIRE(h.assessPrimalSolution(valid, integral, feasible) ==
563+
HighsStatus::kOk);
564+
REQUIRE(valid);
565+
REQUIRE(integral);
566+
REQUIRE(feasible);
567+
568+
std::remove(model_file_name.c_str());
569+
std::remove(solution_file_name.c_str());
570+
571+
h.resetGlobalScheduler(true);
572+
}
573+
462574
void runWriteReadCheckSolution(Highs& highs, const std::string& test_name,
463575
const std::string& model,
464576
const HighsModelStatus require_model_status,
@@ -478,9 +590,15 @@ void runWriteReadCheckSolution(Highs& highs, const std::string& test_name,
478590
if (dev_run)
479591
printf("Writing solution in style %d to %s\n", int(write_solution_style),
480592
solution_file.c_str());
481-
if (dev_run) return_status = highs.writeSolution("", write_solution_style);
482-
return_status = highs.writeSolution(solution_file, write_solution_style);
483-
REQUIRE(return_status == HighsStatus::kOk);
593+
// For models without names, Highs::writeSolution will return
594+
// HighsStatus::kWarning
595+
HighsStatus require_status = highs.getLp().col_names_.size()
596+
? HighsStatus::kOk
597+
: HighsStatus::kWarning;
598+
REQUIRE(highs.writeSolution(solution_file, write_solution_style) ==
599+
require_status);
600+
if (dev_run)
601+
REQUIRE(highs.writeSolution("", write_solution_style) == HighsStatus::kOk);
484602

485603
const bool& value_valid = highs.getSolution().value_valid;
486604
bool valid, integral, feasible;
@@ -581,3 +699,42 @@ void runSetLpSolution(const std::string model) {
581699

582700
highs.resetGlobalScheduler(true);
583701
}
702+
703+
TEST_CASE("miplib-sol-file", "[highs_filereader]") {
704+
const std::string test_name = Catch::getResultCapture().getCurrentTestName();
705+
std::string sol_file = test_name + ".sol";
706+
std::string lp_file = test_name + ".lp";
707+
FILE* file = fopen(lp_file.c_str(), "w");
708+
std::string file_content =
709+
"Minimize\n obj: 2 sel_2 + sel_3\nSubject To\nr0: sel_0 - sel_1 + sel_4 "
710+
">= "
711+
"2\nEnd\n";
712+
if (dev_run) printf("Using .lp file\n%s", file_content.c_str());
713+
fprintf(file, "%s", file_content.c_str());
714+
fclose(file);
715+
Highs h;
716+
h.setOptionValue("output_flag", dev_run);
717+
REQUIRE(h.readModel(lp_file) == HighsStatus::kOk);
718+
719+
file = fopen(sol_file.c_str(), "w");
720+
file_content =
721+
"=obj= 203672547.1\nsel_0 1\nsel_1 0\nsel_2 0\nsel_3 0\nsel_4 1\n";
722+
if (dev_run) printf("Using .sol file\n%s", file_content.c_str());
723+
fprintf(file, "%s", file_content.c_str());
724+
fclose(file);
725+
REQUIRE(h.readSolution(sol_file) == HighsStatus::kOk);
726+
727+
std::vector<double> solution = h.getSolution().col_value;
728+
REQUIRE(solution[0] == 0);
729+
REQUIRE(solution[1] == 0);
730+
REQUIRE(solution[2] == 1);
731+
REQUIRE(solution[3] == 0);
732+
REQUIRE(solution[4] == 1);
733+
734+
REQUIRE(h.run() == HighsStatus::kOk);
735+
736+
std::remove(lp_file.c_str());
737+
std::remove(sol_file.c_str());
738+
739+
h.resetGlobalScheduler(true);
740+
}

0 commit comments

Comments
 (0)