Skip to content

Commit 556736a

Browse files
authored
Merge branch 'latest' into fix-2460
2 parents a26b889 + b161f68 commit 556736a

18 files changed

+701
-292
lines changed

BUILD.bazel

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1+
load("@bazel_skylib//rules:common_settings.bzl", "bool_flag")
12
load("@bazel_skylib//rules:copy_file.bzl", "copy_file")
23
load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library", "cc_test")
4+
load("@rules_cuda//cuda:defs.bzl", "cuda_library", "requires_cuda")
35

46
copy_file(
57
name = "highs-config",
@@ -8,12 +10,43 @@ copy_file(
810
visibility = ["//visibility:public"],
911
)
1012

13+
bool_flag(
14+
name = "cupdlp_gpu",
15+
build_setting_default = False,
16+
visibility = ["//visibility:public"],
17+
)
18+
19+
config_setting(
20+
name = "cupdlp_gpu_enabled",
21+
flag_values = {
22+
"@rules_cuda//cuda:enable": "True",
23+
"@local_cuda//:valid_toolchain_found": "True",
24+
":cupdlp_gpu": "True",
25+
},
26+
)
27+
1128
cc_library(
1229
name = "config",
1330
srcs = ["HConfig.h"],
1431
visibility = ["//visibility:public"],
1532
)
1633

34+
cuda_library(
35+
name = "cupdlp",
36+
srcs = glob([
37+
"highs/pdlp/cupdlp/cuda/*.cu",
38+
]),
39+
hdrs = glob([
40+
"highs/pdlp/cupdlp/cuda/*.cuh",
41+
]),
42+
defines = ["CUPDLP_GPU"],
43+
target_compatible_with = requires_cuda(),
44+
deps = [
45+
"@local_cuda//:cublas",
46+
"@local_cuda//:cusparse",
47+
],
48+
)
49+
1750
cc_library(
1851
name = "highs",
1952
srcs = ["highs/interfaces/highs_c_api.cpp"] + glob([
@@ -49,6 +82,10 @@ cc_library(
4982
"-Wno-unused-but-set-variable",
5083
],
5184
}),
85+
defines = select({
86+
":cupdlp_gpu_enabled": ["CUPDLP_GPU"],
87+
"//conditions:default": ["CUPDLP_CPU"],
88+
}),
5289
includes = [
5390
"extern",
5491
# "extern/filereaderlp",
@@ -66,7 +103,6 @@ cc_library(
66103
# "highs/simplex",
67104
# "highs/test_kkt",
68105
# "highs/util",
69-
"bazel-bin",
70106
],
71107
linkopts = select({
72108
"@rules_cc//cc/compiler:msvc-cl": ["-DEFAULTLIB:shell32.lib"],
@@ -76,7 +112,10 @@ cc_library(
76112
deps = [
77113
"//:config",
78114
"@zlib",
79-
],
115+
] + select({
116+
":cupdlp_gpu_enabled": [":cupdlp"],
117+
"//conditions:default": [],
118+
}),
80119
)
81120

82121
cc_library(

FEATURES.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,7 @@ 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+
Refactored strong branching to minimize duplicated code
16+
1517
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.
18+

MODULE.bazel

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,14 @@ bazel_dep(
1919
name = "zlib",
2020
version = "1.3.1.bcr.5",
2121
)
22+
23+
bazel_dep(
24+
name = "rules_cuda",
25+
version = "0.2.5",
26+
)
27+
28+
local_cuda = use_extension("@rules_cuda//cuda:extensions.bzl", "toolchain")
29+
local_cuda.local_toolchain(
30+
toolkit_path = "",
31+
)
32+
use_repo(local_cuda, "local_cuda")

check/TestCAPI.c

Lines changed: 107 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1820,7 +1820,6 @@ void testGetModel() {
18201820
assert(ck_sense == sense);
18211821

18221822
double* ck_col_cost = (double*)malloc(sizeof(double) * ck_num_col);
1823-
;
18241823
double* ck_col_lower = (double*)malloc(sizeof(double) * ck_num_col);
18251824
double* ck_col_upper = (double*)malloc(sizeof(double) * ck_num_col);
18261825
double* ck_row_lower = (double*)malloc(sizeof(double) * ck_num_row);
@@ -2301,6 +2300,112 @@ void testUserObjectiveBoundScaling() {
23012300

23022301
double scaled_objective_value = Highs_getObjectiveValue(highs);
23032302
assertDoubleValuesEqual("objective_value", unscaled_objective_value, scaled_objective_value);
2303+
void testFixedLp() {
2304+
// The use of Highs_getFixedLp is illustrated for the MIP
2305+
//
2306+
// Min f = -3x_0 - 2x_1 - x_2
2307+
// s.t. x_0 + x_1 + x_2 <= 7
2308+
// 4x_0 + 2x_1 + x_2 = 12
2309+
// x_0 >=0; x_1 >= 0; x_2 binary
2310+
2311+
const HighsInt num_col = 3;
2312+
const HighsInt num_row = 2;
2313+
const HighsInt num_nz = 6;
2314+
HighsInt a_format = kHighsMatrixFormatColwise;
2315+
HighsInt sense = kHighsObjSenseMinimize;
2316+
double offset = 0;
2317+
2318+
// Define the column costs, lower bounds and upper bounds
2319+
double col_cost[3] = {-3.0, -2.0, -1.0};
2320+
double col_lower[3] = {0.0, 0.0, 0.0};
2321+
double col_upper[3] = {1.0e30, 1.0e30, 1.0};
2322+
// Define the row lower bounds and upper bounds
2323+
double row_lower[2] = {-1.0e30, 12.0};
2324+
double row_upper[2] = {7.0, 12.0};
2325+
// Define the constraint matrix column-wise
2326+
HighsInt a_start[3] = {0, 2, 4};
2327+
HighsInt a_index[6] = {0, 1, 0, 1, 0, 1};
2328+
double a_value[6] = {1.0, 4.0, 1.0, 2.0, 1.0, 1.0};
2329+
HighsInt integrality[3] = {kHighsVarTypeContinuous, kHighsVarTypeContinuous,
2330+
kHighsVarTypeInteger};
2331+
2332+
void* highs = Highs_create();
2333+
Highs_setBoolOptionValue(highs, "output_flag", dev_run);
2334+
Highs_setStringOptionValue(highs, "presolve", "off");
2335+
HighsInt return_status =
2336+
Highs_passMip(highs, num_col, num_row, num_nz, a_format, sense, offset,
2337+
col_cost, col_lower, col_upper, row_lower, row_upper,
2338+
a_start, a_index, a_value, integrality);
2339+
assert(return_status == kHighsStatusOk);
2340+
return_status = Highs_run(highs);
2341+
double mip_objective_function_value;
2342+
return_status = Highs_getDoubleInfoValue(highs, "objective_function_value",
2343+
&mip_objective_function_value);
2344+
assert(return_status == kHighsStatusOk);
2345+
2346+
double* col_value = (double*)malloc(sizeof(double) * num_col);
2347+
return_status = Highs_getSolution(highs, col_value, NULL, NULL, NULL);
2348+
assert(return_status == kHighsStatusOk);
2349+
2350+
HighsInt fixed_lp_num_col;
2351+
HighsInt fixed_lp_num_row;
2352+
HighsInt fixed_lp_num_nz;
2353+
HighsInt fixed_lp_sense;
2354+
double fixed_lp_offset;
2355+
Highs_getFixedLp(highs, kHighsMatrixFormatColwise, &fixed_lp_num_col, &fixed_lp_num_row,
2356+
&fixed_lp_num_nz, &fixed_lp_sense, &fixed_lp_offset, NULL, NULL, NULL, NULL, NULL,
2357+
NULL, NULL, NULL);
2358+
2359+
assert(fixed_lp_num_col == num_col);
2360+
assert(fixed_lp_num_row == num_row);
2361+
assert(fixed_lp_num_nz == num_nz);
2362+
assert(fixed_lp_sense == sense);
2363+
2364+
double* fixed_lp_col_cost = (double*)malloc(sizeof(double) * fixed_lp_num_col);
2365+
double* fixed_lp_col_lower = (double*)malloc(sizeof(double) * fixed_lp_num_col);
2366+
double* fixed_lp_col_upper = (double*)malloc(sizeof(double) * fixed_lp_num_col);
2367+
double* fixed_lp_row_lower = (double*)malloc(sizeof(double) * fixed_lp_num_row);
2368+
double* fixed_lp_row_upper = (double*)malloc(sizeof(double) * fixed_lp_num_row);
2369+
HighsInt* fixed_lp_a_start = (HighsInt*)malloc(sizeof(HighsInt) * fixed_lp_num_col);
2370+
HighsInt* fixed_lp_a_index = (HighsInt*)malloc(sizeof(HighsInt) * fixed_lp_num_nz);
2371+
double* fixed_lp_a_value = (double*)malloc(sizeof(double) * num_nz);
2372+
2373+
// Get the arrays
2374+
Highs_getFixedLp(highs, kHighsMatrixFormatColwise, &fixed_lp_num_col, &fixed_lp_num_row,
2375+
&fixed_lp_num_nz, &fixed_lp_sense, &fixed_lp_offset, fixed_lp_col_cost, fixed_lp_col_lower,
2376+
fixed_lp_col_upper, fixed_lp_row_lower, fixed_lp_row_upper, fixed_lp_a_start, fixed_lp_a_index,
2377+
fixed_lp_a_value);
2378+
2379+
return_status = Highs_passLp(highs,
2380+
fixed_lp_num_col, fixed_lp_num_row, fixed_lp_num_nz,
2381+
kHighsMatrixFormatColwise,
2382+
fixed_lp_sense, fixed_lp_offset,
2383+
fixed_lp_col_cost, fixed_lp_col_lower, fixed_lp_col_upper,
2384+
fixed_lp_row_lower, fixed_lp_row_upper,
2385+
fixed_lp_a_start, fixed_lp_a_index, fixed_lp_a_value);
2386+
assert(return_status == kHighsStatusOk);
2387+
2388+
return_status = Highs_setSolution(highs, col_value, NULL, NULL, NULL);
2389+
assert(return_status == kHighsStatusOk);
2390+
2391+
return_status = Highs_run(highs);
2392+
double objective_function_value;
2393+
return_status = Highs_getDoubleInfoValue(highs, "objective_function_value",
2394+
&objective_function_value);
2395+
assert(return_status == kHighsStatusOk);
2396+
assert(objective_function_value == mip_objective_function_value);
2397+
2398+
2399+
free(col_value);
2400+
free(fixed_lp_col_cost);
2401+
free(fixed_lp_col_lower);
2402+
free(fixed_lp_col_upper);
2403+
free(fixed_lp_row_lower);
2404+
free(fixed_lp_row_upper);
2405+
free(fixed_lp_a_start);
2406+
free(fixed_lp_a_index);
2407+
free(fixed_lp_a_value);
2408+
23042409
Highs_destroy(highs);
23052410
}
23062411

@@ -2329,6 +2434,7 @@ int main() {
23292434
testDeleteRowResolveWithBasis();
23302435
testIis();
23312436
testUserObjectiveBoundScaling();
2437+
testFixedLp();
23322438
return 0;
23332439
}
23342440
// testSetSolution();

check/TestMipSolver.cpp

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1035,3 +1035,157 @@ TEST_CASE("issue-2432", "[highs_test_mip_solver]") {
10351035
"found\n");
10361036
solve(highs, kHighsOffString, require_model_status, optimal_objective);
10371037
}
1038+
1039+
TEST_CASE("get-fixed-lp", "[highs_test_mip_solver]") {
1040+
std::string model = "avgas";
1041+
std::string model_file =
1042+
std::string(HIGHS_DIR) + "/check/instances/" + model + ".mps";
1043+
HighsLp fixed_lp;
1044+
Highs h;
1045+
h.setOptionValue("output_flag", dev_run);
1046+
REQUIRE(h.readModel(model_file) == HighsStatus::kOk);
1047+
REQUIRE(h.getFixedLp(fixed_lp) == HighsStatus::kError);
1048+
1049+
model = "flugpl";
1050+
model_file = std::string(HIGHS_DIR) + "/check/instances/" + model + ".mps";
1051+
REQUIRE(h.readModel(model_file) == HighsStatus::kOk);
1052+
REQUIRE(h.getFixedLp(fixed_lp) == HighsStatus::kError);
1053+
1054+
REQUIRE(h.run() == HighsStatus::kOk);
1055+
double mip_optimal_objective = h.getInfo().objective_function_value;
1056+
HighsSolution solution = h.getSolution();
1057+
1058+
// Transform the incumbent MIP into the fixed LP
1059+
HighsLp mip = h.getLp();
1060+
std::vector<HighsInt> col_set;
1061+
std::vector<double> fixed_value;
1062+
for (HighsInt iCol = 0; iCol < mip.num_col_; iCol++) {
1063+
if (mip.integrality_[iCol] == HighsVarType::kInteger) {
1064+
col_set.push_back(iCol);
1065+
fixed_value.push_back(solution.col_value[iCol]);
1066+
}
1067+
}
1068+
h.clearIntegrality();
1069+
HighsInt num_set_entries = col_set.size();
1070+
h.changeColsBounds(num_set_entries, col_set.data(), fixed_value.data(),
1071+
fixed_value.data());
1072+
h.setOptionValue("presolve", kHighsOffString);
1073+
REQUIRE(h.run() == HighsStatus::kOk);
1074+
1075+
REQUIRE(h.getInfo().objective_function_value == mip_optimal_objective);
1076+
// In calling changeColsBounds, the incumbent solution was always
1077+
// cleared, so there was no information from which to construct an
1078+
// advanced basis. Hence simplex starts from a logical basis and
1079+
// requires a positive number of iterations (#2556)
1080+
//
1081+
// Before code to retain solution if changing the bounds and
1082+
// solution remains feasible
1083+
//
1084+
// REQUIRE(h.getInfo().simplex_iteration_count > 0);
1085+
REQUIRE(h.getInfo().simplex_iteration_count == 0);
1086+
1087+
// Now, passing the MIP solution, there is information from which to
1088+
// construct an advanced basis. In the case of flugpl, this is
1089+
// optimal, so no simplex iterations are required
1090+
h.clearSolver();
1091+
h.setSolution(solution);
1092+
REQUIRE(h.run() == HighsStatus::kOk);
1093+
1094+
REQUIRE(h.getInfo().objective_function_value == mip_optimal_objective);
1095+
REQUIRE(h.getInfo().simplex_iteration_count == 0);
1096+
1097+
// Now re-load the MIP, re-solve, and get the fixed LP
1098+
REQUIRE(h.passModel(mip) == HighsStatus::kOk);
1099+
REQUIRE(h.run() == HighsStatus::kOk);
1100+
REQUIRE(h.getInfo().objective_function_value == mip_optimal_objective);
1101+
1102+
REQUIRE(h.getFixedLp(fixed_lp) == HighsStatus::kOk);
1103+
1104+
REQUIRE(h.passModel(fixed_lp) == HighsStatus::kOk);
1105+
REQUIRE(h.run() == HighsStatus::kOk);
1106+
1107+
REQUIRE(h.getInfo().objective_function_value == mip_optimal_objective);
1108+
1109+
// Now run from saved solution (without presolve)
1110+
h.clearSolver();
1111+
h.setSolution(solution);
1112+
REQUIRE(h.run() == HighsStatus::kOk);
1113+
1114+
REQUIRE(h.getInfo().objective_function_value == mip_optimal_objective);
1115+
REQUIRE(h.getInfo().simplex_iteration_count == 0);
1116+
1117+
REQUIRE(h.readModel(model_file) == HighsStatus::kOk);
1118+
// Perturb one of the integer variables for code coverage of
1119+
// warning: makes fixed LP of flugpl infeasible
1120+
std::vector<HighsVarType> integrality = h.getLp().integrality_;
1121+
for (HighsInt iCol = 0; iCol < fixed_lp.num_col_; iCol++) {
1122+
if (integrality[iCol] != HighsVarType::kContinuous) {
1123+
solution.col_value[iCol] -= 0.01;
1124+
break;
1125+
}
1126+
}
1127+
1128+
REQUIRE(h.run() == HighsStatus::kOk);
1129+
h.setSolution(solution);
1130+
1131+
REQUIRE(h.getFixedLp(fixed_lp) == HighsStatus::kWarning);
1132+
1133+
REQUIRE(h.passModel(fixed_lp) == HighsStatus::kOk);
1134+
REQUIRE(h.run() == HighsStatus::kOk);
1135+
1136+
REQUIRE(h.getModelStatus() == HighsModelStatus::kInfeasible);
1137+
1138+
h.resetGlobalScheduler(true);
1139+
}
1140+
1141+
TEST_CASE("get-fixed-lp-semi", "[highs_test_mip_solver]") {
1142+
HighsLp lp;
1143+
lp.num_col_ = 4;
1144+
lp.num_row_ = 2;
1145+
lp.col_cost_ = {1, 3, 1, 2};
1146+
lp.col_lower_ = {0, 0, 1, 1};
1147+
lp.col_upper_ = {1, 1, 3, 5};
1148+
lp.integrality_ = {HighsVarType::kContinuous, HighsVarType::kInteger,
1149+
HighsVarType::kSemiContinuous, HighsVarType::kSemiInteger};
1150+
lp.row_lower_ = {4, 10};
1151+
lp.row_upper_ = {kHighsInf, kHighsInf};
1152+
lp.a_matrix_.start_ = {0, 2, 4, 6, 8};
1153+
lp.a_matrix_.index_ = {0, 1, 0, 1, 0, 1, 0, 1, 0, 1};
1154+
lp.a_matrix_.value_ = {1, 1, 1, 2, 1, 3, 1, 4, 5, 1};
1155+
Highs h;
1156+
h.setOptionValue("output_flag", dev_run);
1157+
h.setOptionValue("presolve", kHighsOffString);
1158+
h.passModel(lp);
1159+
h.run();
1160+
double mip_optimal_objective = h.getInfo().objective_function_value;
1161+
HighsSolution solution = h.getSolution();
1162+
HighsLp fixed_lp;
1163+
REQUIRE(h.getFixedLp(fixed_lp) == HighsStatus::kOk);
1164+
1165+
REQUIRE(h.passModel(fixed_lp) == HighsStatus::kOk);
1166+
REQUIRE(h.run() == HighsStatus::kOk);
1167+
1168+
REQUIRE(h.getInfo().objective_function_value == mip_optimal_objective);
1169+
}
1170+
1171+
TEST_CASE("row-fixed-lp", "[highs_test_mip_solver]") {
1172+
std::string model = "flugpl";
1173+
std::string model_file =
1174+
std::string(HIGHS_DIR) + "/check/instances/" + model + ".mps";
1175+
Highs h;
1176+
// h.setOptionValue("output_flag", dev_run);
1177+
REQUIRE(h.readModel(model_file) == HighsStatus::kOk);
1178+
REQUIRE(h.run() == HighsStatus::kOk);
1179+
double mip_optimal_objective = h.getInfo().objective_function_value;
1180+
HighsSolution solution = h.getSolution();
1181+
1182+
HighsLp lp = h.getLp();
1183+
h.clearIntegrality();
1184+
h.changeRowsBounds(0, lp.num_row_ - 1, solution.row_value.data(),
1185+
solution.row_value.data());
1186+
h.setOptionValue("presolve", kHighsOffString);
1187+
REQUIRE(h.run() == HighsStatus::kOk);
1188+
REQUIRE(h.getInfo().objective_function_value <= mip_optimal_objective);
1189+
1190+
h.resetGlobalScheduler(true);
1191+
}

0 commit comments

Comments
 (0)