Skip to content

Commit b5cbeab

Browse files
authored
Merge pull request ERGO-Code#2268 from ERGO-Code/fix-2267
Fix 2267
2 parents 449f695 + 601c190 commit b5cbeab

File tree

7 files changed

+76
-28
lines changed

7 files changed

+76
-28
lines changed

FEATURES.md

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

33
## Code changes
44

5-
Fixed incorrect assertion in `HighsMipSolver::solutionFeasible()` (fixing #2204)
5+
Fixed incorrect assertion in `HighsMipSolver::solutionFeasible()` (fixing [#2204](https://github.com/ERGO-Code/HiGHS/issues/2204)))
66

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

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: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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();

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/HighsLpUtils.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1439,6 +1439,7 @@ HighsStatus applyScalingToLpRow(HighsLp& lp, const HighsInt row,
14391439
}
14401440

14411441
void unscaleSolution(HighsSolution& solution, const HighsScale& scale) {
1442+
assert(scale.has_scaling);
14421443
assert(solution.col_value.size() == static_cast<size_t>(scale.num_col));
14431444
assert(solution.col_dual.size() == static_cast<size_t>(scale.num_col));
14441445
assert(solution.row_value.size() == static_cast<size_t>(scale.num_row));

src/lp_data/HighsSolution.cpp

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1357,8 +1357,9 @@ HighsStatus formSimplexLpBasisAndFactor(HighsLpSolverObject& solver_object,
13571357
HEkk& ekk_instance = solver_object.ekk_instance_;
13581358
HighsSimplexStatus& ekk_status = ekk_instance.status_;
13591359
lp.ensureColwise();
1360+
const bool passed_scaled = lp.is_scaled_;
13601361
// Consider scaling the LP
1361-
const bool new_scaling = considerScaling(options, lp);
1362+
if (!passed_scaled) considerScaling(options, lp);
13621363
const bool check_basis = basis.alien || (!basis.valid && basis.useful);
13631364
if (check_basis) {
13641365
// The basis needs to be checked for rank deficiency, and possibly
@@ -1370,7 +1371,11 @@ HighsStatus formSimplexLpBasisAndFactor(HighsLpSolverObject& solver_object,
13701371
assert(!only_from_known_basis);
13711372
accommodateAlienBasis(solver_object);
13721373
basis.alien = false;
1373-
lp.unapplyScale();
1374+
// Unapply any scaling used only for factorization to check and
1375+
// complete the basis
1376+
if (!passed_scaled) lp.unapplyScale();
1377+
// Check that any scaling the LP arrived with has not been removed
1378+
assert(lp.is_scaled_ == passed_scaled);
13741379
return HighsStatus::kOk;
13751380
}
13761381
// Move the HighsLpSolverObject's LP to EKK

src/simplex/HApp.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ inline HighsStatus solveLpSimplex(HighsLpSolverObject& solver_object) {
134134
// then move to EKK
135135
considerScaling(options, incumbent_lp);
136136
//
137+
const bool was_scaled = incumbent_lp.is_scaled_;
137138
if (!status.has_basis && !basis.valid && basis.useful) {
138139
// There is no simplex basis, but there is a useful HiGHS basis
139140
// that is not validated
@@ -149,6 +150,8 @@ inline HighsStatus solveLpSimplex(HighsLpSolverObject& solver_object) {
149150
refineBasis(incumbent_lp, solution, basis);
150151
basis.valid = true;
151152
}
153+
// Check that scaling has not been removed
154+
assert(incumbent_lp.is_scaled_ == was_scaled);
152155
// Move the LP to EKK, updating other EKK pointers and any simplex
153156
// NLA pointers, since they may have moved if the LP has been
154157
// modified
@@ -221,6 +224,9 @@ inline HighsStatus solveLpSimplex(HighsLpSolverObject& solver_object) {
221224
kSimplexUnscaledSolutionStrategyNone ||
222225
options.simplex_unscaled_solution_strategy ==
223226
kSimplexUnscaledSolutionStrategyRefine) {
227+
// Check that the incumbent LP has scaling and is scaled
228+
assert(incumbent_lp.scale_.has_scaling);
229+
assert(incumbent_lp.is_scaled_);
224230
//
225231
// Solve the scaled LP!
226232
//

0 commit comments

Comments
 (0)