Skip to content

Commit 0cac3fd

Browse files
committed
Merge branch 'latest' of https://github.com/ERGO-Code/HiGHS into coeffStren
2 parents 31650d9 + a179eb6 commit 0cac3fd

File tree

9 files changed

+659
-20
lines changed

9 files changed

+659
-20
lines changed

.github/julia/build_tarballs.jl

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
# !!! note
2+
# This file is a version of the file we use to package HiGHS for the Julia
3+
# ecosystem. If you make changes to this file during the development of
4+
# HiGHS, please tag `@odow` so we can make the correponding changes to:
5+
# https://github.com/JuliaPackaging/Yggdrasil/blob/master/H/HiGHS
6+
7+
using BinaryBuilder, Pkg
8+
9+
name = "HiGHS"
10+
version = VersionNumber(ENV["HIGHS_RELEASE"])
11+
12+
sources = [GitSource(ENV["HIGHS_URL"], ENV["HIGHS_COMMIT"])]
13+
14+
script = raw"""
15+
export BUILD_SHARED="ON"
16+
export BUILD_STATIC="OFF"
17+
18+
cd $WORKSPACE/srcdir/HiGHS
19+
20+
# Remove system CMake to use the jll version
21+
apk del cmake
22+
23+
mkdir -p build
24+
cd build
25+
26+
# Do fully static build only on Windows
27+
if [[ "${BUILD_SHARED}" == "OFF" ]] && [[ "${target}" == *-mingw* ]]; then
28+
export CXXFLAGS="-static"
29+
fi
30+
31+
cmake -DCMAKE_INSTALL_PREFIX=${prefix} \
32+
-DCMAKE_TOOLCHAIN_FILE=${CMAKE_TARGET_TOOLCHAIN} \
33+
-DCMAKE_BUILD_TYPE=Release \
34+
-DBUILD_SHARED_LIBS=${BUILD_SHARED} \
35+
-DZLIB_USE_STATIC_LIBS=${BUILD_STATIC} \
36+
-DFAST_BUILD=ON ..
37+
38+
if [[ "${target}" == *-linux-* ]]; then
39+
make -j ${nproc}
40+
else
41+
if [[ "${target}" == *-mingw* ]]; then
42+
cmake --build . --config Release
43+
else
44+
cmake --build . --config Release --parallel
45+
fi
46+
fi
47+
make install
48+
49+
install_license ../LICENSE.txt
50+
"""
51+
52+
products = [
53+
LibraryProduct("libhighs", :libhighs),
54+
ExecutableProduct("highs", :highs),
55+
]
56+
57+
platforms = supported_platforms()
58+
platforms = expand_cxxstring_abis(platforms)
59+
60+
dependencies = [
61+
Dependency("CompilerSupportLibraries_jll"),
62+
Dependency("Zlib_jll"),
63+
HostBuildDependency(PackageSpec(; name="CMake_jll")),
64+
]
65+
66+
build_tarballs(
67+
ARGS,
68+
name,
69+
version,
70+
sources,
71+
script,
72+
platforms,
73+
products,
74+
dependencies;
75+
preferred_gcc_version = v"6",
76+
julia_compat = "1.6",
77+
)
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
name: JuliaCompileAndTest
2+
on:
3+
push:
4+
branches: [master, latest]
5+
pull_request:
6+
types: [opened, synchronize, ready_for_review, reopened]
7+
# needed to allow julia-actions/cache to delete old caches that it has created
8+
permissions:
9+
actions: write
10+
contents: read
11+
jobs:
12+
test:
13+
name: Julia - ${{ github.event_name }}
14+
runs-on: ubuntu-latest
15+
steps:
16+
- uses: actions/checkout@v4
17+
# Install Julia 1.7 for BinaryBuilder. Note that this is an old version of
18+
# Julia, but it is required for compatibility with BinaryBuilder.
19+
- uses: julia-actions/setup-julia@v2
20+
with:
21+
version: "1.7"
22+
arch: x64
23+
- uses: julia-actions/cache@v2
24+
# Set the environment variables required by BinaryBuilder.
25+
- run: |
26+
git fetch --tags
27+
echo "HIGHS_RELEASE=$(git describe --tags $(git rev-list --tags --max-count=1) | sed 's/^v//')" >> $GITHUB_ENV
28+
if [ "${{ github.event_name }}" = "pull_request" ]; then
29+
echo "HIGHS_COMMIT=${{ github.event.pull_request.head.sha }}" >> $GITHUB_ENV
30+
echo "HIGHS_URL=${{ github.event.pull_request.head.repo.clone_url }}" >> $GITHUB_ENV
31+
else
32+
echo "HIGHS_COMMIT=${{ github.sha }}" >> $GITHUB_ENV
33+
echo "HIGHS_URL=https://github.com/${{ github.repository }}.git" >> $GITHUB_ENV
34+
fi
35+
- run: |
36+
julia --color=yes -e 'using Pkg; Pkg.add("BinaryBuilder")'
37+
julia --color=yes .github/julia/build_tarballs.jl x86_64-linux-gnu-cxx11 --verbose --deploy="local"
38+
# Now install a newer version of Julia to actually test HiGHS_jll. We
39+
# choose v1.10 because it is the current Long-Term Support (LTS).
40+
- uses: julia-actions/setup-julia@v2
41+
with:
42+
version: "1.10"
43+
arch: x64
44+
# We want to install the latest version of HiGHS.jl, but we also want it
45+
# to be compatible with our newly compiled HiGHS_jll. To do so, we
46+
# manually edit HiGHS.jl's Project.toml file to allow any v1.X.Y version
47+
# of HiGHS_jll
48+
- shell: julia --color=yes {0}
49+
run: |
50+
using Pkg
51+
Pkg.develop("HiGHS")
52+
project_filename = "/home/runner/.julia/dev/HiGHS/Project.toml"
53+
project = read(project_filename, String)
54+
write(
55+
project_filename,
56+
replace(project, r"HiGHS_jll = \"=.+?\"" => "HiGHS_jll = \"1\""),
57+
)
58+
# Now we can add HiGHS_jll and run the tests for HiGHS.
59+
- shell: julia --color=yes {0}
60+
run: |
61+
using Pkg
62+
Pkg.develop(; path="/home/runner/.julia/dev/HiGHS_jll")
63+
Pkg.test("HiGHS")

check/TestMipSolver.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -833,3 +833,18 @@ TEST_CASE("issue-2204", "[highs_test_mip_solver]") {
833833
const double optimal_objective = 6.0;
834834
solve(highs, kHighsOnString, require_model_status, optimal_objective);
835835
}
836+
837+
TEST_CASE("ZI Round and Shifting Heuristics", "[highs_test_mip_solver]") {
838+
std::string model_file =
839+
std::string(HIGHS_DIR) + "/check/instances/rgn.mps";
840+
841+
Highs highs;
842+
highs.setOptionValue("output_flag", dev_run);
843+
// Enable both heuristics
844+
highs.setOptionValue("mip_heuristic_run_zi_round", true);
845+
highs.setOptionValue("mip_heuristic_run_shifting", true);
846+
highs.readModel(model_file);
847+
const HighsModelStatus require_model_status = HighsModelStatus::kOptimal;
848+
const double optimal_objective = 82.19999924;
849+
solve(highs, kHighsOnString, require_model_status, optimal_objective);
850+
}

highs/lp_data/HighsOptions.h

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -437,6 +437,12 @@ struct HighsOptionsStruct {
437437
double mip_abs_gap;
438438
double mip_heuristic_effort;
439439
double mip_min_logging_interval;
440+
bool mip_heuristic_run_rins;
441+
bool mip_heuristic_run_rens;
442+
bool mip_heuristic_run_root_reduced_cost;
443+
bool mip_heuristic_run_zi_round;
444+
bool mip_heuristic_run_shifting;
445+
440446
#ifdef HIGHS_DEBUGSOL
441447
std::string mip_debug_solution_file;
442448
#endif
@@ -1109,6 +1115,32 @@ class HighsOptions : public HighsOptionsStruct {
11091115
&mip_heuristic_effort, 0.0, 0.05, 1.0);
11101116
records.push_back(record_double);
11111117

1118+
record_bool = new OptionRecordBool("mip_heuristic_run_rins",
1119+
"Run RINS heuristic: Default = true",
1120+
advanced, &mip_heuristic_run_rins, true);
1121+
records.push_back(record_bool);
1122+
1123+
record_bool = new OptionRecordBool("mip_heuristic_run_rens",
1124+
"Run RENS heuristic: Default = true",
1125+
advanced, &mip_heuristic_run_rens, true);
1126+
records.push_back(record_bool);
1127+
1128+
record_bool = new OptionRecordBool(
1129+
"mip_heuristic_run_root_reduced_cost",
1130+
"Run rootReducedCost heuristic: Default = true", advanced,
1131+
&mip_heuristic_run_root_reduced_cost, true);
1132+
records.push_back(record_bool);
1133+
1134+
record_bool = new OptionRecordBool(
1135+
"mip_heuristic_run_zi_round", "Run ZI Round heuristic: Default = false",
1136+
advanced, &mip_heuristic_run_zi_round, false);
1137+
records.push_back(record_bool);
1138+
1139+
record_bool = new OptionRecordBool(
1140+
"mip_heuristic_run_shifting", "Run Shifting heuristic: Default = false",
1141+
advanced, &mip_heuristic_run_shifting, false);
1142+
records.push_back(record_bool);
1143+
11121144
record_double = new OptionRecordDouble(
11131145
"mip_rel_gap",
11141146
"Tolerance on relative gap, |ub-lb|/|ub|, to determine whether "

highs/mip/HighsMipSolver.cpp

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -257,15 +257,19 @@ void HighsMipSolver::run() {
257257
}
258258

259259
if (mipdata_->incumbent.empty()) {
260-
analysis_.mipTimerStart(kMipClockDiveRens);
261-
mipdata_->heuristics.RENS(
262-
mipdata_->lp.getLpSolver().getSolution().col_value);
263-
analysis_.mipTimerStop(kMipClockDiveRens);
260+
if (options_mip_->mip_heuristic_run_rens) {
261+
analysis_.mipTimerStart(kMipClockDiveRens);
262+
mipdata_->heuristics.RENS(
263+
mipdata_->lp.getLpSolver().getSolution().col_value);
264+
analysis_.mipTimerStop(kMipClockDiveRens);
265+
}
264266
} else {
265-
analysis_.mipTimerStart(kMipClockDiveRins);
266-
mipdata_->heuristics.RINS(
267-
mipdata_->lp.getLpSolver().getSolution().col_value);
268-
analysis_.mipTimerStop(kMipClockDiveRins);
267+
if (options_mip_->mip_heuristic_run_rins) {
268+
analysis_.mipTimerStart(kMipClockDiveRins);
269+
mipdata_->heuristics.RINS(
270+
mipdata_->lp.getLpSolver().getSolution().col_value);
271+
analysis_.mipTimerStop(kMipClockDiveRins);
272+
}
269273
}
270274

271275
mipdata_->heuristics.flushStatistics();

highs/mip/HighsMipSolverData.cpp

Lines changed: 65 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,12 @@ std::string HighsMipSolverData::solutionSourceToString(
4848
} else if (solution_source == kSolutionSourceRandomizedRounding) {
4949
if (code) return "R";
5050
return "Randomized rounding";
51+
} else if (solution_source == kSolutionSourceZiRound) {
52+
if (code) return "Z";
53+
return "ZI Round";
54+
} else if (solution_source == kSolutionSourceShifting) {
55+
if (code) return "I";
56+
return "Shifting";
5157
} else if (solution_source == kSolutionSourceSolveLp) {
5258
if (code) return "S";
5359
return "Solve LP";
@@ -110,6 +116,32 @@ bool HighsMipSolverData::checkSolution(
110116
return true;
111117
}
112118

119+
std::vector<std::tuple<HighsInt, HighsInt, double>>
120+
HighsMipSolverData::getInfeasibleRows(
121+
const std::vector<double>& solution) const {
122+
std::vector<std::tuple<HighsInt, HighsInt, double>> infeasibleRows;
123+
for (HighsInt i = 0; i != mipsolver.model_->num_row_; ++i) {
124+
HighsInt start = ARstart_[i];
125+
HighsInt end = ARstart_[i + 1];
126+
127+
HighsCDouble row_activity_quad = 0.0;
128+
for (HighsInt j = start; j != end; ++j)
129+
row_activity_quad +=
130+
static_cast<HighsCDouble>(solution[ARindex_[j]]) * ARvalue_[j];
131+
132+
double row_activity = static_cast<double>(row_activity_quad);
133+
if (row_activity > mipsolver.rowUpper(i) + feastol) {
134+
double difference = std::abs(row_activity - mipsolver.rowUpper(i));
135+
infeasibleRows.push_back({i, +1, difference});
136+
}
137+
if (row_activity < mipsolver.rowLower(i) - feastol) {
138+
double difference = std::abs(mipsolver.rowLower(i) - row_activity);
139+
infeasibleRows.push_back({i, -1, difference});
140+
}
141+
}
142+
return infeasibleRows;
143+
}
144+
113145
bool HighsMipSolverData::trySolution(const std::vector<double>& solution,
114146
const int solution_source) {
115147
if (int(solution.size()) != mipsolver.model_->num_col_) return false;
@@ -1601,6 +1633,8 @@ bool HighsMipSolverData::rootSeparationRound(
16011633

16021634
if (mipsolver.submip || incumbent.empty()) {
16031635
heuristics.randomizedRounding(solvals);
1636+
if (mipsolver.options_mip_->mip_heuristic_run_shifting)
1637+
heuristics.shifting(solvals);
16041638
heuristics.flushStatistics();
16051639
status = evaluateRootLp();
16061640
if (status == HighsLpRelaxation::Status::kInfeasible) return true;
@@ -1680,6 +1714,11 @@ HighsLpRelaxation::Status HighsMipSolverData::evaluateRootLp() {
16801714
num_leaves += 1;
16811715
return HighsLpRelaxation::Status::kInfeasible;
16821716
}
1717+
1718+
if (status == HighsLpRelaxation::Status::kOptimal &&
1719+
mipsolver.options_mip_->mip_heuristic_run_zi_round)
1720+
heuristics.ziRound(lp.getLpSolver().getSolution().col_value);
1721+
16831722
} else
16841723
status = lp.getStatus();
16851724

@@ -1876,9 +1915,14 @@ void HighsMipSolverData::evaluateRootNode() {
18761915
last_disptime = -kHighsInf;
18771916
disptime = 0;
18781917

1918+
if (mipsolver.options_mip_->mip_heuristic_run_zi_round)
1919+
heuristics.ziRound(firstlpsol);
18791920
analysis.mipTimerStart(kMipClockRandomizedRounding);
18801921
heuristics.randomizedRounding(firstlpsol);
18811922
analysis.mipTimerStop(kMipClockRandomizedRounding);
1923+
if (mipsolver.options_mip_->mip_heuristic_run_shifting)
1924+
heuristics.shifting(firstlpsol);
1925+
18821926
heuristics.flushStatistics();
18831927

18841928
analysis.mipTimerStart(kMipClockEvaluateRootLp);
@@ -2068,6 +2112,15 @@ void HighsMipSolverData::evaluateRootNode() {
20682112
rootlpsolobj = lp.getObjective();
20692113
lp.setIterationLimit(std::max(10000, int(10 * avgrootlpiters)));
20702114

2115+
if (mipsolver.options_mip_->mip_heuristic_run_zi_round) {
2116+
heuristics.ziRound(firstlpsol);
2117+
heuristics.flushStatistics();
2118+
}
2119+
if (mipsolver.options_mip_->mip_heuristic_run_shifting) {
2120+
heuristics.shifting(rootlpsol);
2121+
heuristics.flushStatistics();
2122+
}
2123+
20712124
if (!analyticCenterComputed && compute_analytic_centre) {
20722125
if (checkLimits()) return clockOff(analysis);
20732126

@@ -2122,10 +2175,12 @@ void HighsMipSolverData::evaluateRootNode() {
21222175
if (rootlpsol.empty()) break;
21232176
if (upper_limit != kHighsInf && !moreHeuristicsAllowed()) break;
21242177

2125-
analysis.mipTimerStart(kMipClockRootHeuristicsReducedCost);
2126-
heuristics.rootReducedCost();
2127-
analysis.mipTimerStop(kMipClockRootHeuristicsReducedCost);
2128-
heuristics.flushStatistics();
2178+
if (mipsolver.options_mip_->mip_heuristic_run_root_reduced_cost) {
2179+
analysis.mipTimerStart(kMipClockRootHeuristicsReducedCost);
2180+
heuristics.rootReducedCost();
2181+
analysis.mipTimerStop(kMipClockRootHeuristicsReducedCost);
2182+
heuristics.flushStatistics();
2183+
}
21292184

21302185
if (checkLimits()) return clockOff(analysis);
21312186

@@ -2151,10 +2206,12 @@ void HighsMipSolverData::evaluateRootNode() {
21512206
if (upper_limit != kHighsInf && !moreHeuristicsAllowed()) break;
21522207

21532208
if (checkLimits()) return clockOff(analysis);
2154-
analysis.mipTimerStart(kMipClockRootHeuristicsRens);
2155-
heuristics.RENS(rootlpsol);
2156-
analysis.mipTimerStop(kMipClockRootHeuristicsRens);
2157-
heuristics.flushStatistics();
2209+
if (mipsolver.options_mip_->mip_heuristic_run_rens) {
2210+
analysis.mipTimerStart(kMipClockRootHeuristicsRens);
2211+
heuristics.RENS(rootlpsol);
2212+
analysis.mipTimerStop(kMipClockRootHeuristicsRens);
2213+
heuristics.flushStatistics();
2214+
}
21582215

21592216
if (checkLimits()) return clockOff(analysis);
21602217
// if there are new global bound changes we re-evaluate the LP and do one

highs/mip/HighsMipSolverData.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ enum MipSolutionSource : int {
5050
kSolutionSourceSubMip,
5151
kSolutionSourceEmptyMip,
5252
kSolutionSourceRandomizedRounding,
53+
kSolutionSourceZiRound,
54+
kSolutionSourceShifting,
5355
kSolutionSourceSolveLp,
5456
kSolutionSourceEvaluateNode,
5557
kSolutionSourceUnbounded,
@@ -257,6 +259,8 @@ struct HighsMipSolverData {
257259
double percentageInactiveIntegers() const;
258260
void performRestart();
259261
bool checkSolution(const std::vector<double>& solution) const;
262+
std::vector<std::tuple<HighsInt, HighsInt, double>> getInfeasibleRows(
263+
const std::vector<double>& solution) const;
260264
bool trySolution(const std::vector<double>& solution,
261265
const int solution_source = kSolutionSourceNone);
262266
bool rootSeparationRound(HighsSeparation& sepa, HighsInt& ncuts,

0 commit comments

Comments
 (0)