Skip to content

Commit 4751e93

Browse files
committed
Merge branch 'latest' into workflows
2 parents e682e9d + 999ea1a commit 4751e93

File tree

14 files changed

+139
-84
lines changed

14 files changed

+139
-84
lines changed

FEATURES.md

Lines changed: 3 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,9 @@
11
## Build changes
22

3-
Added code coverage report
4-
5-
Replaced command line parsing library with CLI11. Removed C++17 reference with cxxopts, which is no longer in HiGHS
6-
73
## Code changes
84

9-
Any LP offset is communicated to the IPM solver, and used in logging and primal/dual objective calculations.
10-
11-
If there is a valid basis when Highs::run() is called, presolve isn't skipped unless the solver option is "simplex" or "choose" (when simplex will always be chosen if there is an advanced basis).
12-
13-
Added basis solve methods to highspy
14-
15-
Added methods to get primal/dual ray and dual unboundedness direction to highspy
16-
17-
When a presolved LP has model status kUnknown, rather than returning this to the user, it performs postsolve and then uses the basis to solve the original LP
18-
19-
Fixed bug in presolve when pointers stored in HighsMatrixSlice get invalidated when the coefficient matrix is reallocated (e.g. when non-zeros are added in HPresolve::addToMatrix)
20-
21-
Primal and dual residual tolerances - applied following IPM or PDLP solution - now documented as options
22-
23-
Highs::getCols (Highs::getRows) now runs in linear time if the internal constraint matrix is stored column-wise (row-wise). Added ensureColwise/Rowwise to the Highs class, the C API and highspy so that users can set the internal constraint matrix storage orientation
24-
25-
When columns and rows are deleted from the incumbent LP after a basic solution has been found, HiGHS no longer invalidates the basis. Now it maintains the basic and nonbasic status of the remaining variables and constraints. When the model is re-solved, this information is used to construct a starting basis.
26-
27-
Fixed bugs in presolve
28-
29-
When running from the command line, changes to default option values are reported
30-
31-
Added callback to allow users to supply integer feasible solutions to the MIP solver during execution
32-
33-
Bug fix for primal heuristics in the MIP solver
34-
35-
Model status is set appropriately when a solver's claimed optimality doesn't satify the general HiGHS primal/dual feasibilily tolerances. Affects IPM without crossover and PDLP
36-
37-
Command line parsing now done with pure C++11 code
38-
39-
Added command line flags to read basis from and write basis to a file
40-
41-
Bug fixes in records of primal/dual rays
42-
43-
MPS read utility improved. Error logging is now less verbose; inability to handle USERCUTS section is properly logged
44-
45-
Implemented lifting for probing as described by Achterberg et al in _Presolve Reductions in Mixed Integer Programming._ INFORMS Journal on Computing 32(2):473-506 (2019). Not used by default, but option mip_lifting_for_probing allows it to be used with two levels of modification
5+
Fixed incorrect assertion in `HighsMipSolver::solutionFeasible()` (fixing [#2204](https://github.com/ERGO-Code/HiGHS/issues/2204)))
466

47-
Propagated updates from cuPDLP-C
7+
getColIntegrality now returns `HighsVarType::kContinuous` when `model_.lp_.integrality_` is empty (fixing [#2261](https://github.com/ERGO-Code/HiGHS/issues/2261))
488

49-
Added GPU support for cuPDLP-C
9+
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))

check/TestCAPI.c

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -743,8 +743,8 @@ void fullApiOptions() {
743743
assert(return_status == kHighsStatusOk);
744744
assert(check_simplex_scale_strategy == simplex_scale_strategy);
745745
assert(min_simplex_scale_strategy == 0);
746-
assert(max_simplex_scale_strategy == 5);
747-
assert(default_simplex_scale_strategy == 1);
746+
assert(max_simplex_scale_strategy == 4);
747+
assert(default_simplex_scale_strategy == 2);
748748

749749
// There are some functions to check what type of option value you should
750750
// provide.
@@ -2091,6 +2091,38 @@ iteration_count1); assertLogical("Dual", logic);
20912091
Highs_destroy(highs);
20922092
}
20932093
*/
2094+
void testDeleteRowResolveWithBasis() {
2095+
// Created to expose bug in #2267, but also illustrates case where
2096+
// scaling was performed on the original problem - due to the 6 in
2097+
// row 1, but would not be performed on the problem after row 1 has
2098+
// been removed
2099+
void* highs = Highs_create();
2100+
Highs_setBoolOptionValue(highs, "output_flag", dev_run);
2101+
HighsInt ret;
2102+
double INF = Highs_getInfinity(highs);
2103+
ret = Highs_addCol(highs, 0.0, 2.0, 2.0, 0, NULL, NULL);
2104+
ret = Highs_addCol(highs, 0.0, -INF, INF, 0, NULL, NULL);
2105+
ret = Highs_addCol(highs, 0.0, -INF, INF, 0, NULL, NULL);
2106+
HighsInt index_1[2] = {0, 2};
2107+
double value_1[2] = {2.0, -1.0};
2108+
ret = Highs_addRow(highs, 0.0, 0.0, 2, index_1, value_1);
2109+
HighsInt index_2[1] = {1};
2110+
double value_2[1] = {6.0};
2111+
ret = Highs_addRow(highs, 10.0, INF, 1, index_2, value_2);
2112+
Highs_run(highs);
2113+
double col_value[3] = {0.0, 0.0, 0.0};
2114+
Highs_getSolution(highs, col_value, NULL, NULL, NULL);
2115+
assertDoubleValuesEqual("col_value[0]", col_value[0], 2.0);
2116+
ret = Highs_deleteRowsByRange(highs, 1, 1);
2117+
assert(ret == 0);
2118+
ret = Highs_run(highs);
2119+
assert(ret == 0);
2120+
ret = Highs_getSolution(highs, col_value, NULL, NULL, NULL);
2121+
assert(ret == 0);
2122+
assertDoubleValuesEqual("col_value[0]", col_value[0], 2.0);
2123+
Highs_destroy(highs);
2124+
}
2125+
20942126
int main() {
20952127
minimalApiIllegalLp();
20962128
testCallback();
@@ -2113,6 +2145,8 @@ int main() {
21132145
testMultiObjective();
21142146
testQpIndefiniteFailure();
21152147
testDualRayTwice();
2148+
2149+
testDeleteRowResolveWithBasis();
21162150
return 0;
21172151
}
21182152
// testSetSolution();

docs/src/options/definitions.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@
128128
- Default: 1
129129

130130
## simplex\_scale\_strategy
131-
- Simplex scaling strategy: off / choose / equilibration / forced equilibration / max value 0 / max value 1 (0/1/2/3/4/5)
131+
- Simplex scaling strategy: off / choose / equilibration (default) / forced equilibration / max value (0/1/2/3/4)
132132
- Type: integer
133133
- Range: {0, 5}
134134
- Default: 1

src/highspy/__init__.pyi

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ from highspy._core import (
77
HighsHessian,
88
HighsInfo,
99
HighsInfoType,
10+
HighsLinearObjective,
1011
HighsLogType,
1112
HighsLp,
1213
HighsModel,
@@ -59,6 +60,7 @@ __all__: list[str] = [
5960
"HighsRanging",
6061
"kHighsInf",
6162
"kHighsIInf",
63+
"HighsLinearObjective",
6264
"HIGHS_VERSION_MAJOR",
6365
"HIGHS_VERSION_MINOR",
6466
"HIGHS_VERSION_PATCH",

src/highspy/_core/__init__.pyi

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,17 @@ class HighsModel:
290290

291291
def __init__(self) -> None: ...
292292

293+
class HighsLinearObjective:
294+
weight: float
295+
offset: float
296+
coefficients: list[float]
297+
abs_tolerance: float
298+
rel_tolerance: float
299+
priority: int
300+
301+
def __init__(self) -> None: ...
302+
303+
293304
class HighsModelStatus:
294305
"""
295306
Members:
@@ -777,6 +788,7 @@ class _Highs:
777788
indices: numpy.ndarray[typing.Any, numpy.dtype[numpy.int32]],
778789
values: numpy.ndarray[typing.Any, numpy.dtype[numpy.float64]],
779790
) -> HighsStatus: ...
791+
def addLinearObjective(self, linear_objective: HighsLinearObjective) -> HighsStatus: ...
780792
def addVar(self, lower_bound: float, upper_bound: float) -> HighsStatus: ...
781793
def addVars(
782794
self,
@@ -814,6 +826,7 @@ class _Highs:
814826
def changeRowBounds(self, row: int, lower_bound: float, upper_bound: float) -> HighsStatus: ...
815827
def clear(self) -> HighsStatus: ...
816828
def clearModel(self) -> HighsStatus: ...
829+
def clearLinearObjectives(self) -> HighsStatus: ...
817830
def clearSolver(self) -> HighsStatus: ...
818831
def crossover(self, solution: HighsSolution) -> HighsStatus: ...
819832
def deleteCols(self, num_cols: int, cols: numpy.ndarray[typing.Any, numpy.dtype[numpy.int32]]) -> HighsStatus: ...

src/highspy/highs.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
ObjSense,
1111
HighsVarType,
1212
HighsStatus,
13+
HighsLinearObjective,
1314
cb, # type: ignore
1415
_Highs, # type: ignore
1516
readonly_ptr_wrapper_double,
@@ -249,7 +250,6 @@ def maximize(self, obj: Optional[Union[highs_var, highs_linear_expression]] = No
249250

250251
super().changeObjectiveSense(ObjSense.kMaximize)
251252
return self.solve()
252-
253253
@staticmethod
254254
def internal_get_value(
255255
array_values: Union[Sequence[float], np.ndarray[Any, np.dtype[np.float64]], readonly_ptr_wrapper_double],

src/interfaces/highs_c_api.cpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -744,11 +744,11 @@ HighsInt Highs_setCallback(void* highs, HighsCCallbackType user_callback,
744744
return static_cast<int>(status);
745745
}
746746

747-
HighsInt Highs_startCallback(void* highs, const int callback_type) {
747+
HighsInt Highs_startCallback(void* highs, const HighsInt callback_type) {
748748
return (HighsInt)((Highs*)highs)->startCallback(callback_type);
749749
}
750750

751-
HighsInt Highs_stopCallback(void* highs, const int callback_type) {
751+
HighsInt Highs_stopCallback(void* highs, const HighsInt callback_type) {
752752
return (HighsInt)((Highs*)highs)->stopCallback(callback_type);
753753
}
754754

@@ -1291,9 +1291,9 @@ HighsInt Highs_getPresolvedLp(const void* highs, const HighsInt a_format,
12911291
a_start, a_index, a_value, integrality);
12921292
}
12931293

1294-
HighsInt Highs_crossover(void* highs, const int num_col, const int num_row,
1295-
const double* col_value, const double* col_dual,
1296-
const double* row_dual) {
1294+
HighsInt Highs_crossover(void* highs, const HighsInt num_col,
1295+
const HighsInt num_row, const double* col_value,
1296+
const double* col_dual, const double* row_dual) {
12971297
HighsSolution solution;
12981298
if (col_value) {
12991299
solution.value_valid = true;

src/interfaces/highs_c_api.h

Lines changed: 20 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -770,7 +770,7 @@ HighsInt Highs_getStringOptionValue(const void* highs, const char* option,
770770
*
771771
* @param highs A pointer to the Highs instance.
772772
* @param option The name of the option.
773-
* @param type An int in which the corresponding `kHighsOptionType`
773+
* @param type A HighsInt in which the corresponding `kHighsOptionType`
774774
* constant should be placed.
775775
*
776776
* @returns A `kHighsStatus` constant indicating whether the call succeeded.
@@ -842,7 +842,7 @@ HighsInt Highs_getBoolOptionValues(const void* highs, const char* option,
842842
HighsInt* current_value,
843843
HighsInt* default_value);
844844
/**
845-
* Get the current and default values of an int option
845+
* Get the current and default values of a HighsInt option
846846
*
847847
* @param highs A pointer to the Highs instance.
848848
* @param current_value A pointer to the current value of the option.
@@ -924,7 +924,7 @@ HighsInt Highs_getInt64InfoValue(const void* highs, const char* info,
924924
*
925925
* @param highs A pointer to the Highs instance.
926926
* @param info The name of the info item.
927-
* @param type An int in which the corresponding `kHighsOptionType`
927+
* @param type A HighsInt in which the corresponding `kHighsOptionType`
928928
* constant is stored.
929929
*
930930
* @returns A `kHighsStatus` constant indicating whether the call succeeded.
@@ -983,7 +983,7 @@ HighsInt Highs_getModelStatus(const void* highs);
983983
* LP) gets it if it does not and dual_ray_value is not nullptr.
984984
*
985985
* @param highs A pointer to the Highs instance.
986-
* @param has_dual_ray A pointer to an int to store 1 if a dual ray
986+
* @param has_dual_ray A pointer to a HighsInt to store 1 if a dual ray
987987
* currently exists.
988988
* @param dual_ray_value An array of length [num_row] filled with the
989989
* unbounded ray.
@@ -1001,9 +1001,10 @@ HighsInt Highs_getDualRay(const void* highs, HighsInt* has_dual_ray,
10011001
*
10021002
* @param highs A pointer to the Highs
10031003
* instance.
1004-
* @param has_dual_unboundedness_direction A pointer to an int to store 1
1005-
* if the dual unboundedness
1006-
* direction exists.
1004+
* @param has_dual_unboundedness_direction A pointer to a HighsInt to
1005+
* store 1 if the dual
1006+
* unboundedness direction
1007+
* exists.
10071008
* @param dual_unboundedness_direction_value An array of length [num_col]
10081009
* filled with the unboundedness
10091010
* direction.
@@ -1018,7 +1019,7 @@ HighsInt Highs_getDualUnboundednessDirection(
10181019
* LP) gets it if it does not and primal_ray_value is not nullptr.
10191020
*
10201021
* @param highs A pointer to the Highs instance.
1021-
* @param has_primal_ray A pointer to an int to store 1 if the primal ray
1022+
* @param has_primal_ray A pointer to a HighsInt to store 1 if the primal ray
10221023
* exists.
10231024
* @param primal_ray_value An array of length [num_col] filled with the
10241025
* unbounded ray.
@@ -1279,7 +1280,7 @@ HighsInt Highs_setCallback(void* highs, HighsCCallbackType user_callback,
12791280
*
12801281
* @returns A `kHighsStatus` constant indicating whether the call succeeded.
12811282
*/
1282-
HighsInt Highs_startCallback(void* highs, const int callback_type);
1283+
HighsInt Highs_startCallback(void* highs, const HighsInt callback_type);
12831284

12841285
/**
12851286
* Stop callback of given type
@@ -1289,7 +1290,7 @@ HighsInt Highs_startCallback(void* highs, const int callback_type);
12891290
*
12901291
* @returns A `kHighsStatus` constant indicating whether the call succeeded.
12911292
*/
1292-
HighsInt Highs_stopCallback(void* highs, const int callback_type);
1293+
HighsInt Highs_stopCallback(void* highs, const HighsInt callback_type);
12931294

12941295
/**
12951296
* Return the cumulative wall-clock time spent in `Highs_run`.
@@ -1754,15 +1755,15 @@ HighsInt Highs_getObjectiveOffset(const void* highs, double* offset);
17541755
* @param highs A pointer to the Highs instance.
17551756
* @param from_col The first column for which to query data for.
17561757
* @param to_col The last column (inclusive) for which to query data for.
1757-
* @param num_col An integer populated with the number of columns got from
1758+
* @param num_col A HighsInt populated with the number of columns got from
17581759
* the model (this should equal `to_col - from_col + 1`).
17591760
* @param costs An array of size [to_col - from_col + 1] for the column
17601761
* cost coefficients.
17611762
* @param lower An array of size [to_col - from_col + 1] for the column
17621763
* lower bounds.
17631764
* @param upper An array of size [to_col - from_col + 1] for the column
17641765
* upper bounds.
1765-
* @param num_nz An integer to be populated with the number of non-zero
1766+
* @param num_nz A HighsInt to be populated with the number of non-zero
17661767
* elements in the constraint matrix.
17671768
* @param matrix_start An array of size [to_col - from_col + 1] with the start
17681769
* indices of each column in `matrix_index` and
@@ -1831,13 +1832,13 @@ HighsInt Highs_getColsByMask(const void* highs, const HighsInt* mask,
18311832
* @param highs A pointer to the Highs instance.
18321833
* @param from_row The first row for which to query data for.
18331834
* @param to_row The last row (inclusive) for which to query data for.
1834-
* @param num_row An integer to be populated with the number of rows got
1835+
* @param num_row A HighsInt to be populated with the number of rows got
18351836
* from the model.
18361837
* @param lower An array of size [to_row - from_row + 1] for the row
18371838
* lower bounds.
18381839
* @param upper An array of size [to_row - from_row + 1] for the row
18391840
* upper bounds.
1840-
* @param num_nz An integer to be populated with the number of non-zero
1841+
* @param num_nz A HighsInt to be populated with the number of non-zero
18411842
* elements in the constraint matrix.
18421843
* @param matrix_start An array of size [to_row - from_row + 1] with the start
18431844
* indices of each row in `matrix_index` and
@@ -1940,7 +1941,7 @@ HighsInt Highs_getColByName(const void* highs, const char* name, HighsInt* col);
19401941
* Get the integrality of a column.
19411942
*
19421943
* @param col The index of the column to query.
1943-
* @param integrality An integer in which the integrality of the column should
1944+
* @param integrality A HighsInt in which the integrality of the column should
19441945
* be placed. The integer is one of the `kHighsVarTypeXXX`
19451946
* constants.
19461947
*
@@ -1994,7 +1995,7 @@ HighsInt Highs_deleteColsByMask(void* highs, HighsInt* mask);
19941995
*
19951996
* @returns A `kHighsStatus` constant indicating whether the call succeeded.
19961997
*/
1997-
HighsInt Highs_deleteRowsByRange(void* highs, const int from_row,
1998+
HighsInt Highs_deleteRowsByRange(void* highs, const HighsInt from_row,
19981999
const HighsInt to_row);
19992000

20002001
/**
@@ -2222,9 +2223,9 @@ HighsInt Highs_getPresolvedLp(const void* highs, const HighsInt a_format,
22222223
*
22232224
* @returns A `kHighsStatus` constant indicating whether the call succeeded.
22242225
*/
2225-
HighsInt Highs_crossover(void* highs, const int num_col, const int num_row,
2226-
const double* col_value, const double* col_dual,
2227-
const double* row_dual);
2226+
HighsInt Highs_crossover(void* highs, const HighsInt num_col,
2227+
const HighsInt num_row, const double* col_value,
2228+
const double* col_dual, const double* row_dual);
22282229

22292230
/**
22302231
* Compute the ranging information for all costs and bounds. For

src/lp_data/HConst.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,10 @@ enum SimplexScaleStrategy {
5050
kSimplexScaleStrategyChoose, // 1
5151
kSimplexScaleStrategyEquilibration, // 2
5252
kSimplexScaleStrategyForcedEquilibration, // 3
53-
kSimplexScaleStrategyMaxValue015, // 4
54-
kSimplexScaleStrategyMaxValue0157, // 5
55-
kSimplexScaleStrategyMax = kSimplexScaleStrategyMaxValue0157
53+
kSimplexScaleStrategyMaxValue, // 4
54+
kSimplexScaleStrategyMaxValue015 = kSimplexScaleStrategyMaxValue,
55+
kSimplexScaleStrategyMaxValue0157 = kSimplexScaleStrategyMaxValue,
56+
kSimplexScaleStrategyMax = kSimplexScaleStrategyMaxValue
5657
};
5758

5859
enum HighsDebugLevel {

src/lp_data/HighsLpUtils.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1265,8 +1265,9 @@ bool maxValueScaleMatrix(const HighsOptions& options, HighsLp& lp,
12651265
vector<HighsInt>& Aindex = lp.a_matrix_.index_;
12661266
vector<double>& Avalue = lp.a_matrix_.value_;
12671267

1268-
assert(options.simplex_scale_strategy == kSimplexScaleStrategyMaxValue015 ||
1269-
options.simplex_scale_strategy == kSimplexScaleStrategyMaxValue0157);
1268+
assert(options.simplex_scale_strategy == kSimplexScaleStrategyMaxValue);
1269+
assert(kSimplexScaleStrategyMaxValue015 == kSimplexScaleStrategyMaxValue);
1270+
assert(kSimplexScaleStrategyMaxValue0157 == kSimplexScaleStrategyMaxValue);
12701271

12711272
// The 015(7) values refer to bit settings in FICO's scaling options.
12721273
// Specifically
@@ -1438,6 +1439,7 @@ HighsStatus applyScalingToLpRow(HighsLp& lp, const HighsInt row,
14381439
}
14391440

14401441
void unscaleSolution(HighsSolution& solution, const HighsScale& scale) {
1442+
assert(scale.has_scaling);
14411443
assert(solution.col_value.size() == static_cast<size_t>(scale.num_col));
14421444
assert(solution.col_dual.size() == static_cast<size_t>(scale.num_col));
14431445
assert(solution.row_value.size() == static_cast<size_t>(scale.num_row));

0 commit comments

Comments
 (0)