Skip to content

Commit 614d82a

Browse files
committed
refactor scaling&preprocess
1 parent 187b74d commit 614d82a

File tree

5 files changed

+198
-230
lines changed

5 files changed

+198
-230
lines changed

highs/pdlp/HiPdlpWrapper.cpp

Lines changed: 6 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -49,79 +49,36 @@ HighsStatus solveLpHiPdlp(const HighsOptions& options, HighsTimer& timer,
4949
if (!log_filename.empty()) logger.set_log_file(log_filename);
5050
logger.print_header();
5151

52-
// --- Initialize Parameters with Defaults ---
53-
PrimalDualParams params{};
54-
55-
getHiPpdlpParamsFromOptions(options, timer, params);
56-
5752
Timer total_timer;
5853
/*** Order of operations
59-
* Presolve with HiGHS
6054
* Preprocess with HiPdlp
6155
* Scale with HiPdlp
6256
* Solve with HiPdlp
6357
* Unscale with HiPdlp
6458
* Postprocess with HiPDLP
65-
* Postsolve with HiGHS
6659
* ***/
67-
// 1. Presolve with HiGHS
68-
Highs highs;
69-
highs.passModel(lp);
70-
HighsLp presolved_lp;
71-
HighsSolution presolve_solution;
72-
bool do_presolve = (options.presolve != "off");
73-
74-
if (do_presolve) {
75-
HighsStatus presolve_status = highs.presolve();
76-
if (presolve_status != HighsStatus::kOk) {
77-
return HighsStatus::kError; // Handle presolve errors
78-
}
79-
80-
// If the problem is solved during presolve, get the solution and return.
81-
if (highs.getModelStatus() == HighsModelStatus::kOptimal ||
82-
highs.getModelStatus() == HighsModelStatus::kInfeasible ||
83-
highs.getModelStatus() == HighsModelStatus::kUnbounded) {
84-
presolve_solution = highs.getSolution();
85-
highs_info.pdlp_iteration_count = 0;
86-
highs_solution = presolve_solution;
87-
model_status = highs.getModelStatus();
88-
return HighsStatus::kOk;
89-
}
90-
91-
// Get the presolved LP for the next steps.
92-
presolved_lp = highs.getLp();
93-
} else {
94-
// If presolve is off, the problem to solve is the original LP.
95-
presolved_lp = lp;
96-
}
97-
9860
// 2. Preprocess with HiPdlp
9961
PDLPSolver pdlp(logger);
62+
pdlp.setParams(options, timer);
10063
HighsLp preprocessed_lp;
64+
pdlp.passLp(&lp);
10165
//logger_.info("Preprocessing LP to handle ranged constraints...");
102-
pdlp.PreprocessLp(presolved_lp, preprocessed_lp);
66+
pdlp.PreprocessLp();
10367

10468
// 3. Scale with HiPdlp
105-
pdlp.scaling_.ScaleProblem(preprocessed_lp, params);
69+
pdlp.scaling_.ScaleProblem();
10670

10771
// 4. Solve with HiPdlp
10872
std::vector<double> x, y;
109-
pdlp.Solve(preprocessed_lp, params, x, y);
73+
//pdlp.Solve(preprocessed_lp, params, x, y);
11074

11175
// 5. Unscale with HiPdlp
11276
pdlp.scaling_.UnscaleSolution(x, y);
11377

11478
// 6. Postprocess with HiPDLP
11579
HighsSolution pdlp_solution;
116-
pdlp.Postsolve(presolved_lp, preprocessed_lp, x, y, pdlp_solution);
80+
//pdlp.Postsolve(presolved_lp, preprocessed_lp, x, y, pdlp_solution);
11781

118-
// 7. Postsolve with HiGHS
119-
if (do_presolve) {
120-
logger.info("Postsolving with HiGHS...");
121-
highs.postsolve(pdlp_solution);
122-
} else {
123-
pdlp_solution = pdlp_solution; // No presolve, so nothing to do
124-
}
12582

12683
// --- Print Summary ---
12784
logger.print_summary(pdlp.GetResults(), pdlp.GetIterationCount(),
@@ -185,68 +142,7 @@ HighsStatus solveLpHiPdlp(const HighsOptions& options, HighsTimer& timer,
185142
return HighsStatus::kOk;
186143
}
187144

188-
void getHiPpdlpParamsFromOptions(const HighsOptions& options, HighsTimer& timer,
189-
PrimalDualParams& params) {
190-
params.initialise();
191-
// params.eta = 0; Not set in parse_options_file
192-
// params.omega = 0; Not set in parse_options_file
193-
params.tolerance = options.pdlp_optimality_tolerance;
194-
if (options.kkt_tolerance != kDefaultKktTolerance) {
195-
params.tolerance = options.kkt_tolerance;
196-
}
197-
params.max_iterations = options.pdlp_iteration_limit;
198-
params.device_type = Device::CPU;
199-
// HiPDLP has its own timer, so set its time limit according to
200-
// the time remaining with respect to the HiGHS time limit (if
201-
// finite)
202-
double time_limit = options.time_limit;
203-
if (time_limit < kHighsInf) {
204-
time_limit -= timer.read();
205-
time_limit = std::max(0.0, time_limit);
206-
}
207-
params.time_limit = time_limit;
208-
209-
params.scaling_method = ScalingMethod::NONE;
210-
params.use_ruiz_scaling = false;
211-
params.use_pc_scaling = false;
212-
params.use_l2_scaling = false;
213-
if ((options.pdlp_features_off & kPdlpScalingOff) == 0) {
214-
// Use scaling: now see which
215-
params.use_ruiz_scaling = options.pdlp_scaling_mode & kPdlpScalingRuiz;
216-
params.use_pc_scaling = options.pdlp_scaling_mode & kPdlpScalingPC;
217-
params.use_l2_scaling = options.pdlp_scaling_mode & kPdlpScalingL2;
218-
}
219-
params.ruiz_iterations = options.pdlp_ruiz_iterations;
220-
// params.ruiz_norm = INFINITY; Not set in parse_options_file
221-
// params.pc_alpha = 1.0; Not set in parse_options_file
222-
223-
// Restart strategy maps 0/1/2 to RestartStrategy
224-
params.restart_strategy = RestartStrategy::NO_RESTART;
225-
if ((options.pdlp_features_off & kPdlpRestartOff) == 0) {
226-
// Use restart: now see which
227-
if (options.pdlp_restart_strategy == kPdlpRestartStrategyFixed) {
228-
params.restart_strategy = RestartStrategy::FIXED_RESTART;
229-
} else if (options.pdlp_restart_strategy == kPdlpRestartStrategyAdaptive) {
230-
params.restart_strategy = RestartStrategy::ADAPTIVE_RESTART;
231-
}
232-
}
233-
// params.fixed_restart_interval = 0; Not set in parse_options_file
234-
// params.use_halpern_restart = false; Not set in parse_options_file
235145

236-
params.step_size_strategy = StepSizeStrategy::FIXED;
237-
if ((options.pdlp_features_off & kPdlpAdaptiveStepSizeOff) == 0) {
238-
// Use adaptive step size: now see which
239-
if (options.pdlp_step_size_strategy == kPdlpStepSizeStrategyAdaptive) {
240-
params.step_size_strategy = StepSizeStrategy::ADAPTIVE;
241-
} else if (options.pdlp_step_size_strategy ==
242-
kPdlpStepSizeStrategyMalitskyPock) {
243-
params.step_size_strategy = StepSizeStrategy::MALITSKY_POCK;
244-
}
245-
}
246-
// params.malitsky_pock_params.initialise(); Not set in parse_options_file
247-
// params.adaptive_linesearch_params.initialise(); Not set in
248-
// parse_options_file
249-
}
250146

251147
void PrimalDualParams::initialise() {
252148
this->eta = 0;

highs/pdlp/hipdlp/pdhg.cc

Lines changed: 86 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,15 @@ using namespace std;
1616

1717
int PDLPSolver::GetIterationCount() const { return final_iter_count_; }
1818

19-
void PDLPSolver::PreprocessLp(const HighsLp& original_lp,
20-
HighsLp& processed_lp) {
19+
void PDLPSolver::PreprocessLp() {
2120
logger_.info(
2221
"Preprocessing LP using cupdlp formulation (slack variables for "
2322
"bounds)...");
2423

25-
int nRows_orig = original_lp.num_row_;
26-
int nCols_orig = original_lp.num_col_;
24+
HighsLp& processed_lp = lp_;
25+
26+
int nRows_orig = original_lp_->num_row_;
27+
int nCols_orig = original_lp_->num_col_;
2728

2829
int num_new_cols = 0;
2930
int nEqs = 0;
@@ -32,11 +33,11 @@ void PDLPSolver::PreprocessLp(const HighsLp& original_lp,
3233

3334
// 1. First pass: Classify constraints and count slack variables needed
3435
for (int i = 0; i < nRows_orig; ++i) {
35-
bool has_lower = original_lp.row_lower_[i] > -kHighsInf;
36-
bool has_upper = original_lp.row_upper_[i] < kHighsInf;
36+
bool has_lower = original_lp_->row_lower_[i] > -kHighsInf;
37+
bool has_upper = original_lp_->row_upper_[i] < kHighsInf;
3738

3839
if (has_lower && has_upper) {
39-
if (original_lp.row_lower_[i] == original_lp.row_upper_[i]) {
40+
if (original_lp_->row_lower_[i] == original_lp_->row_upper_[i]) {
4041
constraint_types_[i] = EQ;
4142
} else {
4243
constraint_types_[i] = BOUND;
@@ -67,17 +68,17 @@ void PDLPSolver::PreprocessLp(const HighsLp& original_lp,
6768

6869
// 4. Populate costs and bounds for original and new slack variables
6970
for (int i = 0; i < nCols_orig; ++i) {
70-
processed_lp.col_cost_[i] = original_lp.col_cost_[i];
71-
processed_lp.col_lower_[i] = original_lp.col_lower_[i];
72-
processed_lp.col_upper_[i] = original_lp.col_upper_[i];
71+
processed_lp.col_cost_[i] = original_lp_->col_cost_[i];
72+
processed_lp.col_lower_[i] = original_lp_->col_lower_[i];
73+
processed_lp.col_upper_[i] = original_lp_->col_upper_[i];
7374
}
7475

7576
int current_slack_col = nCols_orig;
7677
for (int i = 0; i < nRows_orig; ++i) {
7778
if (constraint_types_[i] == BOUND || constraint_types_[i] == FREE) {
7879
processed_lp.col_cost_[current_slack_col] = 0.0;
79-
processed_lp.col_lower_[current_slack_col] = original_lp.row_lower_[i];
80-
processed_lp.col_upper_[current_slack_col] = original_lp.row_upper_[i];
80+
processed_lp.col_lower_[current_slack_col] = original_lp_->row_lower_[i];
81+
processed_lp.col_upper_[current_slack_col] = original_lp_->row_upper_[i];
8182
current_slack_col++;
8283
}
8384
}
@@ -86,7 +87,7 @@ void PDLPSolver::PreprocessLp(const HighsLp& original_lp,
8687
//
8788
// Take a copy of the original LP's constraint matrix since we
8889
// need it rowwise and it's const
89-
HighsSparseMatrix original_matrix = original_lp.a_matrix_;
90+
HighsSparseMatrix original_matrix = original_lp_->a_matrix_;
9091
original_matrix.ensureRowwise();
9192
// Set up the processed constraint matrix as an empty row-wise
9293
// matrix that can have nCols_orig columns
@@ -133,16 +134,16 @@ void PDLPSolver::PreprocessLp(const HighsLp& original_lp,
133134
for (int i = 0; i < nRows_orig; ++i) {
134135
switch (constraint_types_[i]) {
135136
case EQ:
136-
processed_lp.row_lower_[i] = original_lp.row_lower_[i];
137-
processed_lp.row_upper_[i] = original_lp.row_upper_[i];
137+
processed_lp.row_lower_[i] = original_lp_->row_lower_[i];
138+
processed_lp.row_upper_[i] = original_lp_->row_upper_[i];
138139
break;
139140
case GEQ:
140-
processed_lp.row_lower_[i] = original_lp.row_lower_[i];
141+
processed_lp.row_lower_[i] = original_lp_->row_lower_[i];
141142
processed_lp.row_upper_[i] = kHighsInf;
142143
break;
143144
case LEQ:
144145
// Becomes -Ax >= -b
145-
processed_lp.row_lower_[i] = -original_lp.row_upper_[i];
146+
processed_lp.row_lower_[i] = -original_lp_->row_upper_[i];
146147
processed_lp.row_upper_[i] = kHighsInf;
147148
break;
148149
case BOUND:
@@ -187,25 +188,25 @@ void PDLPSolver::Postsolve(const HighsLp& original_lp, HighsLp& processed_lp,
187188

188189
// 2. Resize solution object to original dimensions
189190
solution.col_value.resize(original_num_col_);
190-
solution.row_value.resize(original_lp.num_row_);
191+
solution.row_value.resize(original_lp_->num_row_);
191192
solution.col_dual.resize(original_num_col_);
192-
solution.row_dual.resize(original_lp.num_row_);
193+
solution.row_dual.resize(original_lp_->num_row_);
193194

194195
// 3. Recover Primal Column Values (x)
195196
// This is the easy part: just take the first 'original_num_col_' elements.
196197
for (int i = 0; i < original_num_col_; ++i) {
197198
solution.col_value[i] = x_unscaled[i];
198199
}
199200

200-
double final_primal_objective = original_lp.offset_;
201+
double final_primal_objective = original_lp_->offset_;
201202
for (int i = 0; i < original_num_col_; ++i) {
202-
final_primal_objective += original_lp.col_cost_[i] * solution.col_value[i];
203+
final_primal_objective += original_lp_->col_cost_[i] * solution.col_value[i];
203204
}
204205
results_.primal_obj = final_primal_objective;
205206

206207
// 4. Recover Dual Row Values (y)
207208
// This requires reversing the sign flip for LEQ constraints.
208-
for (int i = 0; i < original_lp.num_row_; ++i) {
209+
for (int i = 0; i < original_lp_->num_row_; ++i) {
209210
if (constraint_types_[i] == LEQ) {
210211
solution.row_dual[i] = -y_unscaled[i];
211212
} else {
@@ -235,7 +236,7 @@ void PDLPSolver::Postsolve(const HighsLp& original_lp, HighsLp& processed_lp,
235236
linalg::Ax(unscaled_processed_lp, x_unscaled, ax_unscaled);
236237

237238
int slack_variable_idx = original_num_col_;
238-
for (int i = 0; i < original_lp.num_row_; ++i) {
239+
for (int i = 0; i < original_lp_->num_row_; ++i) {
239240
if (constraint_types_[i] == BOUND || constraint_types_[i] == FREE) {
240241
solution.row_value[i] = x_unscaled[slack_variable_idx++];
241242
} else if (constraint_types_[i] == LEQ) {
@@ -948,3 +949,65 @@ HighsStatus PDLPSolver::PowerMethod(HighsLp& lp, double& op_norm_sq) {
948949
// If the method did not converge within max_iter
949950
return HighsStatus::kWarning;
950951
}
952+
953+
void PDLPSolver::setParams(const HighsOptions& options, HighsTimer& timer) {
954+
params_.initialise();
955+
// params.eta = 0; Not set in parse_options_file
956+
// params.omega = 0; Not set in parse_options_file
957+
params_.tolerance = options.pdlp_optimality_tolerance;
958+
if (options.kkt_tolerance != kDefaultKktTolerance) {
959+
params_.tolerance = options.kkt_tolerance;
960+
}
961+
params_.max_iterations = options.pdlp_iteration_limit;
962+
params_.device_type = Device::CPU;
963+
// HiPDLP has its own timer, so set its time limit according to
964+
// the time remaining with respect to the HiGHS time limit (if
965+
// finite)
966+
double time_limit = options.time_limit;
967+
if (time_limit < kHighsInf) {
968+
time_limit -= timer.read();
969+
time_limit = std::max(0.0, time_limit);
970+
}
971+
params_.time_limit = time_limit;
972+
973+
params_.scaling_method = ScalingMethod::NONE;
974+
params_.use_ruiz_scaling = false;
975+
params_.use_pc_scaling = false;
976+
params_.use_l2_scaling = false;
977+
if ((options.pdlp_features_off & kPdlpScalingOff) == 0) {
978+
// Use scaling: now see which
979+
params_.use_ruiz_scaling = options.pdlp_scaling_mode & kPdlpScalingRuiz;
980+
params_.use_pc_scaling = options.pdlp_scaling_mode & kPdlpScalingPC;
981+
params_.use_l2_scaling = options.pdlp_scaling_mode & kPdlpScalingL2;
982+
}
983+
params_.ruiz_iterations = options.pdlp_ruiz_iterations;
984+
// params_.ruiz_norm = INFINITY; Not set in parse_options_file
985+
// params_.pc_alpha = 1.0; Not set in parse_options_file
986+
987+
// Restart strategy maps 0/1/2 to RestartStrategy
988+
params_.restart_strategy = RestartStrategy::NO_RESTART;
989+
if ((options.pdlp_features_off & kPdlpRestartOff) == 0) {
990+
// Use restart: now see which
991+
if (options.pdlp_restart_strategy == kPdlpRestartStrategyFixed) {
992+
params_.restart_strategy = RestartStrategy::FIXED_RESTART;
993+
} else if (options.pdlp_restart_strategy == kPdlpRestartStrategyAdaptive) {
994+
params_.restart_strategy = RestartStrategy::ADAPTIVE_RESTART;
995+
}
996+
}
997+
// params_.fixed_restart_interval = 0; Not set in parse_options_file
998+
// params_.use_halpern_restart = false; Not set in parse_options_file
999+
1000+
params_.step_size_strategy = StepSizeStrategy::FIXED;
1001+
if ((options.pdlp_features_off & kPdlpAdaptiveStepSizeOff) == 0) {
1002+
// Use adaptive step size: now see which
1003+
if (options.pdlp_step_size_strategy == kPdlpStepSizeStrategyAdaptive) {
1004+
params_.step_size_strategy = StepSizeStrategy::ADAPTIVE;
1005+
} else if (options.pdlp_step_size_strategy ==
1006+
kPdlpStepSizeStrategyMalitskyPock) {
1007+
params_.step_size_strategy = StepSizeStrategy::MALITSKY_POCK;
1008+
}
1009+
}
1010+
// params_.malitsky_pock_params.initialise(); Not set in parse_options_file
1011+
// params_.adaptive_linesearch_params.initialise(); Not set in
1012+
// parse_options_file
1013+
}

0 commit comments

Comments
 (0)