Skip to content

Commit 2f75610

Browse files
authored
Merge pull request #2740 from ERGO-Code/fix-2738
Refactoring `Highs::run()`
2 parents 889c0a2 + 46947d0 commit 2f75610

File tree

7 files changed

+291
-191
lines changed

7 files changed

+291
-191
lines changed

highs/Highs.h

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,8 @@ class Highs {
212212
HighsStatus presolve();
213213

214214
/**
215-
* @brief Run the solver, accounting for any multiple objective
215+
* @brief Run the solver, applying file-based options and user
216+
* scaling before optimization
216217
*/
217218
HighsStatus run();
218219

@@ -1290,7 +1291,16 @@ class Highs {
12901291
*/
12911292
static void resetGlobalScheduler(bool blocking = false);
12921293

1293-
// Start of advanced methods for HiGHS MIP solver
1294+
// Start of advanced methods: only for internal use!
1295+
1296+
// Nested methods below Highs::run()
1297+
//
1298+
// See highs/HighsRun.md
1299+
HighsStatus optimizeHighs();
1300+
HighsStatus optimizeModel();
1301+
HighsStatus calledOptimizeModel();
1302+
// Used in MIP solver as minimal LP solve
1303+
HighsStatus optimizeLp();
12941304

12951305
const HighsSimplexStats& getSimplexStats() const {
12961306
return ekk_instance_.getSimplexStats();
@@ -1532,7 +1542,6 @@ class Highs {
15321542
HighsRanging ranging_;
15331543
HighsIis iis_;
15341544
std::vector<HighsObjectiveSolution> saved_objective_and_solution_;
1535-
HighsFiles files_;
15361545

15371546
HighsPresolveStatus model_presolve_status_ =
15381547
HighsPresolveStatus::kNotPresolved;
@@ -1561,7 +1570,6 @@ class Highs {
15611570
bool written_log_header_ = false;
15621571

15631572
void reportModelStats() const;
1564-
HighsStatus optimizeModel();
15651573

15661574
void exactResizeModel() {
15671575
this->model_.lp_.exactResize();
@@ -1767,9 +1775,12 @@ class Highs {
17671775
void restoreInfCost(HighsStatus& return_status);
17681776
HighsStatus optionChangeAction();
17691777

1778+
HighsStatus userScale(HighsUserScaleData& data);
1779+
HighsStatus userUnscale(HighsUserScaleData& data);
17701780
HighsStatus userScaleModel(HighsUserScaleData& data);
17711781
HighsStatus userScaleSolution(HighsUserScaleData& data,
17721782
bool update_kkt = false);
1783+
17731784
HighsStatus computeIllConditioning(HighsIllConditioning& ill_conditioning,
17741785
const bool constraint,
17751786
const HighsInt method,
@@ -1789,10 +1800,6 @@ class Highs {
17891800

17901801
bool tryPdlpCleanup(HighsInt& pdlp_cleanup_iteration_limit,
17911802
const HighsInfo& presolved_lp_info) const;
1792-
1793-
bool optionsHasHighsFiles() const;
1794-
void saveHighsFiles();
1795-
void getHighsFiles();
17961803
};
17971804

17981805
// Start of deprecated methods not in the Highs class

highs/HighsRun.md

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
# `Highs::run()`
2+
3+
`Highs::run()` has evolved a great deal since it was first created to
4+
"solve" the LP in the `HighsLp` instance `Highs::lp_`. As well as
5+
solving more problem classes, and using more solvers, features have
6+
been added inelegantly. The simplicity of having a single
7+
`Highs::run()` method, with different features obscured within it will
8+
be replaced by multiple, nested, "solve" methods which are explicit in
9+
their features and very much simpler.
10+
11+
The only refactoring came when the multiple objective code was added:
12+
`Highs::optimizeModel()` inherited the content of `Highs::run()` so
13+
that a single call to`Highs::run()` could perform multiple
14+
optimizations.
15+
16+
Other developments that have been implemented inelegantly are
17+
18+
### Actioning executable run-time options via `Highs::run()`
19+
20+
As [#2269](https://github.com/ERGO-Code/HiGHS/issues/2269)
21+
highlighted, users of `cvxpy` can only execute `Highs::run()`, so the
22+
following actions that were previously in `app/RunHighs.cpp`, are now
23+
performed in `Highs::run()`
24+
25+
- Read from a solution and/or basis file
26+
- Write out the model
27+
- Write out the IIS model
28+
- Write out the solution and/or basis file.
29+
30+
There is still one action in`app/RunHighs.cpp` that should be performed in `Highs::run()`
31+
32+
- Write out the presolved model
33+
34+
These "HiGHS files" actions must only be performed at the "top level"
35+
of `Highs::run()`, and this is acheived by caching the file options in
36+
the `Highs` class and clearing them from options_ so that they aren't
37+
applied at lower level calls to `Highs::run()`. They are then restored
38+
before returning from `Highs::run()`.
39+
40+
### Performing user scaling
41+
42+
User objective and/or bound scaling is performed before assessing
43+
whether there is excessive problem data and suggesting user objective
44+
and bound scaling. These user scaling actions must only be performed
45+
at the "top level" of `Highs::run()`, and this is acheived by caching
46+
the user scaling options in the `Highs` class and clearing them from
47+
options_ so that they aren't applied at lower level calls to
48+
`Highs::run()`. If user scaling has been applied in a call to
49+
`Highs::run()`, it is unapplied and the option values restored before
50+
returning from `Highs::run()`.
51+
52+
### Applying "mods"
53+
54+
The `HighsLp` class contains data values and structures that cannot be handled explicitly by the solvers.
55+
56+
- If a variable has an excessivly large objective cost, this is
57+
interpreted as being infinte, and handled in
58+
`Highs::handleInfCost()` by fixing the variable at its lower or
59+
upper bound (when finite) according to the sign of the cost and the
60+
sense of the optimization, and zeroing the cost. After solving the
61+
problem, the cost and bounds must be restored.
62+
63+
- If a variable is of type `HighsVarType::kSemiContinuous` or
64+
`HighsVarType::kSemiInteger`, it is assessed in
65+
`assessSemiVariables` (in `HighsLpUtils.cpp`) before reformulation
66+
in `withoutSemiVariables` (in `HighsLpUtils.cpp`)
67+
68+
- If it is not strictly "semi" it is set to`HighsVarType::kContinuous` or `HighsVarType::kInteger`
69+
- If its lower bound is not positive, it is deemed to be illegal
70+
- If its upper bound is larger than `kMaxSemiVariableUpper` then,
71+
depending on the lower bound, if it is possible to reformulate it the
72+
upper bound is set to `kMaxSemiVariableUpper` (it is said to be "tightened".
73+
Otherwise, it is deemed to be illegal
74+
75+
These modifications are currently performed in `Highs::run()`, with
76+
very careful code to ensure that they are removed before returning
77+
from `Highs::run()`.
78+
79+
With the plan to allow indicator constraints and SOS as generalised
80+
disjunctive forms that will be reformulated, the handling of "mods"
81+
needs to be refactored!
82+
83+
## Refactoring
84+
85+
The inelegance of `Highs::run()` (and `Highs::optimizeModel()`) was
86+
exposed by
87+
[\#2635](https://github.com/ERGO-Code/HiGHS/issues/2635). Both methods
88+
need to be refactored. Firstly, `Highs::run()` must be refactored into
89+
the following set of nested methods. By calling the appropriate
90+
method, there is no need to "hide" option settings by caching and then
91+
clearing their value.
92+
93+
Refactoring `Highs::optimizeModel()` is trickier. There needs to be a
94+
method where any "mods" are made, so that at the level below the
95+
problem defined by the `HighsModel` class (without semi-variables) is
96+
solved.
97+
98+
### `Highs::run`
99+
100+
This "outer" layer can contain the "HiGHS files" actions that were
101+
previously in `app/RunHighs.cpp`, and user scaling
102+
103+
### `Highs::optimizeHighs()`
104+
105+
The next layer applies any "mods" to the `HighsModel` class, and calls
106+
`Highs::optimizeModel()` or `Highs::multiobjectiveSolve()` if there
107+
are multiple objectives.
108+
109+
### `Highs::optimizeModel()`
110+
111+
The next layer should just optimize what's in the `HighsModel` (without semi-variables)
112+
113+
## Observations
114+
115+
- Refactoring `Highs::optimizeModel()` is tricky, so is is temporarily
116+
renamed `Highs::calledOptimizeModel()`, and `Highs::optimizeModel()`
117+
is a temporary intermediate method to facilitate this is.
118+
119+
- The most obvious place where `Highs::run()` was called at a "lower
120+
level" is in the MIP solver. Since there are no "upper level"
121+
actions to be performed, it can call `Highs::optimizeModel()`. To
122+
emphasise that just an LP is being solved, `Highs::optimizeLp()` has
123+
been created. This is currently a call to `Highs::optimizeModel()`
124+
125+
## ToDo
126+
127+
- Move the code to write out the presolved model from `app/RunHighs.cpp` to `Highs::run()`
128+
129+
- Move the "mods" to `Highs::optimizeHighs()`
130+
131+
- For problems with multiple objectives
132+
133+
- Apply user scaling to the multiple objectives
134+
135+
- Assess the multiple objectives for extreme values
136+
137+
- Accumulate subsystem solve times
138+
139+
- In IIS calculations, accumulate subsystem solve times
140+
141+
142+
143+

highs/lp_data/HStruct.h

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,6 @@
1616

1717
#include "lp_data/HConst.h"
1818

19-
struct HighsFiles {
20-
bool empty = true;
21-
std::string read_solution_file = "";
22-
std::string read_basis_file = "";
23-
std::string write_model_file = "";
24-
std::string write_iis_model_file = "";
25-
std::string write_solution_file = "";
26-
std::string write_basis_file = "";
27-
void clear();
28-
};
29-
3019
struct HighsSolution {
3120
bool value_valid = false;
3221
bool dual_valid = false;
@@ -197,8 +186,8 @@ struct HighsSimplexStats {
197186
};
198187

199188
struct HighsUserScaleData {
200-
HighsInt user_objective_scale;
201-
HighsInt user_bound_scale;
189+
HighsInt user_objective_scale = 0;
190+
HighsInt user_bound_scale = 0;
202191
double infinite_cost;
203192
double infinite_bound;
204193
double small_matrix_value;
@@ -211,7 +200,7 @@ struct HighsUserScaleData {
211200
HighsInt num_large_matrix_values;
212201
HighsInt suggested_user_objective_scale;
213202
HighsInt suggested_user_bound_scale;
214-
bool applied;
203+
bool applied = false;
215204
void initialise(const HighsInt& user_objective_scale_,
216205
const HighsInt& user_bound_scale_,
217206
const double& infinite_cost_, const double& infinite_bound_,

0 commit comments

Comments
 (0)