Skip to content

Commit 0e0e3b2

Browse files
committed
Merge branch 'latest' of https://github.com/ERGO-Code/HiGHS into hmw-mt
2 parents 5a3da16 + 12dba7b commit 0e0e3b2

57 files changed

Lines changed: 2059 additions & 1492 deletions

Some content is hidden

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

check/TestFilereader.cpp

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -566,3 +566,48 @@ TEST_CASE("lp-duplicate-variable", "[highs_filereader]") {
566566

567567
std::remove(lp_file.c_str());
568568
}
569+
570+
inline double getWallTime() {
571+
using namespace std::chrono;
572+
return duration_cast<duration<double> >(
573+
std::chrono::high_resolution_clock::now().time_since_epoch())
574+
.count();
575+
}
576+
577+
TEST_CASE("efficient-add-row", "[highs_filereader]") {
578+
std::string filename;
579+
filename = std::string(HIGHS_DIR) + "/check/instances/adlittle.mps";
580+
// "/srv/mps_da/neos.mps.gz";
581+
Highs h;
582+
h.setOptionValue("output_flag", dev_run);
583+
REQUIRE(h.readModel(filename) == HighsStatus::kOk);
584+
HighsLp lp = h.getLp();
585+
h.passModel(lp.num_col_, 0, 0, 0, 0, 0, 0, 0.0, lp.col_cost_.data(),
586+
lp.col_lower_.data(), lp.col_upper_.data(), nullptr, nullptr,
587+
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr);
588+
lp.a_matrix_.ensureRowwise();
589+
double tt = 0;
590+
if (dev_run) tt = -getWallTime();
591+
for (HighsInt iRow = 0; iRow < lp.num_row_; iRow++) {
592+
HighsInt iEl = lp.a_matrix_.start_[iRow];
593+
h.addRow(lp.row_lower_[iRow], lp.row_upper_[iRow],
594+
lp.a_matrix_.start_[iRow + 1] - iEl, &lp.a_matrix_.index_[iEl],
595+
&lp.a_matrix_.value_[iEl]);
596+
}
597+
if (dev_run) {
598+
tt += getWallTime();
599+
printf("Added %d rows individually in %.2gs\n", int(lp.num_row_), tt);
600+
}
601+
602+
REQUIRE(h.deleteRows(0, lp.num_row_ - 1) == HighsStatus::kOk);
603+
REQUIRE(h.getLp().num_row_ == 0);
604+
605+
if (dev_run) tt = -getWallTime();
606+
h.addRows(lp.num_row_, lp.row_lower_.data(), lp.row_upper_.data(),
607+
lp.a_matrix_.start_[lp.num_row_], lp.a_matrix_.start_.data(),
608+
lp.a_matrix_.index_.data(), lp.a_matrix_.value_.data());
609+
if (dev_run) {
610+
tt += getWallTime();
611+
printf("Added %d rows together in %.2gs\n", int(lp.num_row_), tt);
612+
}
613+
}

check/TestHighsHessian.cpp

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ const bool dev_run = false;
99
// No commas in test case name.
1010
TEST_CASE("HighsHessian", "[highs_hessian]") {
1111
HighsOptions options;
12-
if (!dev_run) options.output_flag = false;
12+
options.output_flag = dev_run;
1313

1414
HighsHessian square_hessian;
1515
// . 0 1 2 3 4
@@ -327,3 +327,28 @@ TEST_CASE("HighsHessian", "[highs_hessian]") {
327327
triangular_hessian.print();
328328
}
329329
}
330+
331+
TEST_CASE("square-near-symmetric-hessian", "[highs_hessian]") {
332+
const double epsilon = 1e-15;
333+
HighsOptions options;
334+
options.output_flag = dev_run;
335+
336+
HighsHessian square_hessian;
337+
// . 0 1 2
338+
// 0 5 1 1+eps
339+
// 1 1 4 0
340+
// 2 1 0 3
341+
342+
square_hessian.dim_ = 3;
343+
square_hessian.format_ = HessianFormat::kSquare;
344+
square_hessian.start_ = {0, 4, 6, 8};
345+
square_hessian.index_ = {0, 1, 2, 1, 0, 1, 0, 2};
346+
square_hessian.value_ = {5, 1, 1, 0.1, 1.1, 4, 1 + epsilon, 3};
347+
if (dev_run)
348+
printf(
349+
"square-near-symmetric-hessian: 0-1 and 1-0 values are %.24g and "
350+
"%.24g, with difference %g\n",
351+
square_hessian.value_[2], square_hessian.value_[6],
352+
std::fabs(square_hessian.value_[2] - square_hessian.value_[6]));
353+
REQUIRE(assessHessian(square_hessian, options) == HighsStatus::kOk);
354+
}

check/TestMipSolver.cpp

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1451,3 +1451,35 @@ TEST_CASE("issue-2957", "[highs_test_mip_solver]") {
14511451
const double optimal_objective = 28.2;
14521452
solve(highs, kHighsOnString, require_model_status, optimal_objective);
14531453
}
1454+
1455+
TEST_CASE("issue-2975", "[highs_test_mip_solver]") {
1456+
// min 2*b + 99999*y
1457+
// s.t. a + b = 10
1458+
// a - 100*y <= 0
1459+
// a, b >= 0; y binary
1460+
Highs highs;
1461+
highs.setOptionValue("output_flag", dev_run);
1462+
HighsInt a = 0;
1463+
HighsInt b = 1;
1464+
HighsInt y = 2;
1465+
HighsLp lp;
1466+
lp.num_col_ = 3;
1467+
lp.num_row_ = 2;
1468+
lp.col_lower_ = {0, 0, 0};
1469+
lp.col_upper_ = {kHighsInf, kHighsInf, 1};
1470+
lp.col_cost_ = {0, 2, 99999};
1471+
lp.integrality_ = {HighsVarType::kContinuous, HighsVarType::kContinuous,
1472+
HighsVarType::kInteger};
1473+
lp.row_lower_ = {10, -kHighsInf};
1474+
lp.row_upper_ = {10, 0};
1475+
lp.a_matrix_.format_ = MatrixFormat::kRowwise;
1476+
lp.a_matrix_.start_ = {0, 2, 4};
1477+
lp.a_matrix_.index_ = {a, b, a, y};
1478+
lp.a_matrix_.value_ = {1, 1, 1, -100};
1479+
highs.passModel(lp);
1480+
highs.run();
1481+
REQUIRE(highs.getInfo().objective_function_value == 20);
1482+
REQUIRE(highs.getSolution().col_value[y] == 0.0);
1483+
1484+
highs.resetGlobalScheduler(true);
1485+
}

cmake/sources.cmake

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ set(hipo_sources
182182
ipm/hipo/ipm/FactorHiGHSSolver.cpp
183183
ipm/hipo/ipm/Control.cpp
184184
ipm/hipo/ipm/Iterate.cpp
185-
ipm/hipo/ipm/LogHighs.cpp
185+
ipm/hipo/ipm/KktMatrix.cpp
186186
ipm/hipo/ipm/Model.cpp
187187
ipm/hipo/ipm/PreProcess.cpp
188188
ipm/hipo/ipm/Refine.cpp
@@ -195,8 +195,8 @@ set(hipo_headers
195195
ipm/hipo/ipm/Control.h
196196
ipm/hipo/ipm/Info.h
197197
ipm/hipo/ipm/Iterate.h
198+
ipm/hipo/ipm/KktMatrix.h
198199
ipm/hipo/ipm/LinearSolver.h
199-
ipm/hipo/ipm/LogHighs.h
200200
ipm/hipo/ipm/Model.h
201201
ipm/hipo/ipm/Options.h
202202
ipm/hipo/ipm/PreProcess.h
@@ -246,14 +246,14 @@ set(factor_highs_headers
246246
set(hipo_util_sources
247247
ipm/hipo/auxiliary/Auxiliary.cpp
248248
ipm/hipo/auxiliary/KrylovMethods.cpp
249-
ipm/hipo/auxiliary/Log.cpp
249+
ipm/hipo/auxiliary/Logger.cpp
250250
ipm/hipo/auxiliary/VectorOperations.cpp)
251251

252252
set(hipo_util_headers
253253
ipm/hipo/auxiliary/Auxiliary.h
254254
ipm/hipo/auxiliary/IntConfig.h
255255
ipm/hipo/auxiliary/KrylovMethods.h
256-
ipm/hipo/auxiliary/Log.h
256+
ipm/hipo/auxiliary/Logger.h
257257
ipm/hipo/auxiliary/mycblas.h
258258
ipm/hipo/auxiliary/OrderingPrint.h
259259
ipm/hipo/auxiliary/VectorOperations.h)

examples/Docs.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939
num_nz = 1
4040
index = 1
4141
value = 1
42-
h.addRow(lower, upper, num_nz, index, value)
42+
h.addRow(lower, upper, num_nz, [index], [value])
4343
# The second constraint (5 <= x_0 + 2x_1 <= 15) has two nonzero
4444
# coefficients, so arrays of indices and values are required
4545
num_nz = 2
@@ -70,7 +70,7 @@
7070
start = 0
7171
index = 0
7272
value = 0
73-
h.addCols(2, cost, lower, upper, num_nz, start, index, value)
73+
h.addCols(2, cost, lower, upper, num_nz, [start], [index], [value])
7474
# Add the rows, with the constraint matrix row-wise
7575
lower = np.array([-inf, 5, 6], dtype=np.double)
7676
upper = np.array([7, 15, inf], dtype=np.double)
@@ -87,8 +87,8 @@
8787
h.clear()
8888
print("Passing the model via HighsLp")
8989
lp = highspy.HighsLp()
90-
lp.num_col_ = 2;
91-
lp.num_row_ = 3;
90+
lp.num_col_ = 2
91+
lp.num_row_ = 3
9292
lp.col_cost_ = np.array([1, 1], dtype=np.double)
9393
lp.col_lower_ = np.array([0, 1], dtype=np.double)
9494
lp.col_upper_ = np.array([4, inf], dtype=np.double)

examples/branch-and-price.py

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,11 @@
22
#
33
from collections import defaultdict
44
from operator import itemgetter
5-
import highspy, random, time, math
5+
import highspy
6+
import random
7+
import time
8+
import math
9+
import numpy as np
610

711
# Relative gap for column generation
812
CG_GAP_REL = 1e-4
@@ -22,7 +26,7 @@
2226
#
2327
def solveGreedyModel():
2428
bins = defaultdict(float)
25-
solution = defaultdict(list)
29+
solution = defaultdict(list[int])
2630

2731
for item, w in sorted(enumerate(ItemWeights), reverse=True, key=itemgetter(1)):
2832
index = next((i for i, W in bins.items() if W + w <= BinCapacity), len(bins))
@@ -56,17 +60,17 @@ def solveCompactModel():
5660
x = {(i,j): i*B + j for i in range(NumberItems) for j in range(B) }
5761
y = [len(x) + j for j in range(B)]
5862

59-
m.addVars(len(x), [0]*len(x), [1]*len(x)); # x_{ij} \in {0,1}, \forall i,j
60-
m.addVars(len(y), [0]*len(y), [1]*len(y)); # y_{j} \in {0,1}, \forall j
63+
m.addVars(len(x), [0.0]*len(x), [1.0]*len(x)) # x_{ij} \in {0,1}, \forall i,j
64+
m.addVars(len(y), [0.0]*len(y), [1.0]*len(y)) # y_{j} \in {0,1}, \forall j
6165
m.changeColsIntegrality(
6266
len(x)+len(y),
6367
list(range(len(x)+len(y))),
64-
[highspy.HighsVarType.kInteger]*(len(x)+len(y))
68+
np.array([highspy.HighsVarType.kInteger]*(len(x)+len(y)))
6569
)
6670

6771
# min sum_{j} y_{j}
6872
m.changeObjectiveSense(highspy.ObjSense.kMinimize)
69-
m.changeColsCost(len(y), y, [1]*len(y))
73+
m.changeColsCost(len(y), y, [1.0]*len(y))
7074

7175
# \sum_{j} x_{ij} == 1, \foreach i
7276
# 1 <= \sum_{j} x_{ij} <= 1
@@ -76,7 +80,7 @@ def solveCompactModel():
7680
1, # rhs
7781
B, # Number of non-zero variables
7882
[x[i,j] for j in range(B)], # Indexes of variable
79-
[1] * B # Coefficients of variables
83+
[1.0] * B # Coefficients of variables
8084
)
8185

8286
# sum_{i} w_{i} * x_{ij} <= c * y_{j}, \foreach j
@@ -116,17 +120,17 @@ def createMasterProblem(columns: list):
116120
m.setOptionValue('output_flag', False)
117121
m.setOptionValue('random_seed', SEED)
118122

119-
m.addVars(len(columns), [0]*len(columns), [1]*len(columns))
123+
m.addVars(len(columns), [0.0]*len(columns), [1.0]*len(columns))
120124

121125
# min \sum_{k} \lambda_{k}
122126
m.changeObjectiveSense(highspy.ObjSense.kMinimize)
123-
m.changeColsCost(len(columns), list(range(len(columns))), [1]*len(columns))
127+
m.changeColsCost(len(columns), list(range(len(columns))), [1.0]*len(columns))
124128

125129
# \sum_{k \in K_i} \lambda_{k} == 1, \foreach i
126130
# 1 <= \sum_{k \in K_i} \lambda_{k} <= 1
127131
for i in range(NumberItems):
128132
K_i = [k for k, column in enumerate(columns) if i in column]
129-
m.addRow(1, 1, len(K_i), K_i, [1]*len(K_i))
133+
m.addRow(1, 1, len(K_i), K_i, [1.0]*len(K_i))
130134

131135
m.run()
132136

@@ -150,8 +154,8 @@ def solveSubproblemExact(duals):
150154
m.setOptionValue('output_flag', False)
151155
m.setOptionValue('random_seed', SEED)
152156

153-
m.addVars(NumberItems, [0]*NumberItems, [1]*NumberItems)
154-
m.changeColsIntegrality(NumberItems, list(range(NumberItems)), [highspy.HighsVarType.kInteger]*NumberItems)
157+
m.addVars(NumberItems, [0.0]*NumberItems, [1.0]*NumberItems)
158+
m.changeColsIntegrality(NumberItems, list(range(NumberItems)), np.array([highspy.HighsVarType.kInteger]*NumberItems))
155159

156160
# max \sum_{i} \mu_{i} * z_{i}
157161
# where \mu_{i} is the dual variable for the i-th row of the master problem
@@ -179,7 +183,7 @@ def solveSubproblemExact(duals):
179183
# Solve the knapsack subproblem with greedy heuristic
180184
def solveSubproblemNotExact(duals):
181185
total_weight = 0
182-
new_column = []
186+
new_column : list[int] = []
183187

184188
for i in sorted(range(NumberItems), key=lambda i: -duals[i]/ItemWeights[i]):
185189
if duals[i] >= 0 and ItemWeights[i] + total_weight <= BinCapacity:
@@ -192,8 +196,9 @@ def solveSubproblemNotExact(duals):
192196
#
193197
# Generate columns for the master problem
194198
#
195-
def generateColumns(columns: list, m, msg=True, solve_exact = False, start_time = 0):
199+
def generateColumns(columns: list[list[int]], m, msg=True, solve_exact = False, start_time = 0.0):
196200
best_gap = math.inf
201+
row_format = ""
197202

198203
iter = 0
199204
while True:
@@ -261,7 +266,7 @@ class Node:
261266
assigned_columns: list[int]
262267

263268
value: float
264-
final_columns: list[int]
269+
final_columns: list[list[int]]
265270
final_columns_vals: list[float]
266271
final_fractional_columns: list[int]
267272

@@ -320,7 +325,7 @@ def solveNode(node: Node, columns: list[list[int]], m):
320325
# Specifically, the code "up-branches" columns with fractional value in the RMP solution, i.e., forces specific columns
321326
# to be selected in RMP. The subproblems don't need to explicitly enforce this constraint (unlike other branching strategies).
322327
#
323-
def branchAndPrice(m, vals, columns, start_time=0):
328+
def branchAndPrice(m, vals, columns: list[list[int]], start_time=0.0):
324329
header = ["NodesExpl", "TreeSize", "CurrCols", "FracCols", "UB", "Time"]
325330
row_format ="{:>12}" * (len(header))
326331
print(row_format.format(*header))

examples/call_highs_from_python.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -360,19 +360,19 @@ def user_callback(
360360
# Test MIP callbacks
361361
print("\negout as HighsModel")
362362

363-
h.setOptionValue("output_flag", False);
364-
h.setOptionValue("presolve", "off");
363+
h.setOptionValue("output_flag", False)
364+
h.setOptionValue("presolve", "off")
365365

366366
h.readModel("check/instances/egout.mps")
367367

368368
for iCase in range(0, 2):
369369
if iCase == 0:
370-
user_callback_data = EGOUT_OBJECTIVE_TARGET;
370+
user_callback_data = EGOUT_OBJECTIVE_TARGET
371371
h.setCallback(user_callback, user_callback_data)
372372
h.startCallback(hscb.HighsCallbackType.kCallbackMipInterrupt)
373373
required_model_status = highspy.HighsModelStatus.kInterrupt
374374
else:
375-
user_callback_data = 1e30;
375+
user_callback_data = 1e30
376376
h.setCallback(user_callback, user_callback_data)
377377
h.startCallback(hscb.HighsCallbackType.kCallbackMipImprovingSolution)
378378
required_model_status = highspy.HighsModelStatus.kOptimal

examples/call_highs_from_python_highspy.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
# Set up problem
99
inf = highspy.kHighsInf
1010
h.addVars(2, np.array([-inf, -inf]), np.array([inf, inf]))
11-
h.changeColsCost(2, np.array([0, 1]), np.array([0, 1], dtype=np.double));
11+
h.changeColsCost(2, np.array([0, 1]), np.array([0, 1], dtype=np.double))
1212
num_cons = 2
1313
lower = np.array([2, 0], dtype=np.double)
1414
upper = np.array([inf, inf], dtype=np.double)

examples/call_highs_from_python_mps.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
# from the root directory.
44

55
import highspy
6-
import numpy as np
76

87
# Highs h
98
h = highspy.Highs()

examples/chip0.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@
88

99
h = highspy.Highs()
1010

11-
x1 = h.addVar(obj = 10)
12-
x2 = h.addVar(obj = 25)
11+
x1 = h.addVariable(obj = 10)
12+
x2 = h.addVariable(obj = 25)
1313

1414
h.addConstr(x1 + 2*x2 <= 80)
1515
h.addConstr(x1 + 4*x2 <= 120)

0 commit comments

Comments
 (0)