Skip to content

Commit 2045798

Browse files
committed
Merge branch 'hi-pdlp' into hi-pdlp-jh
2 parents 7d417ff + 49f3a6d commit 2045798

File tree

7 files changed

+156
-40
lines changed

7 files changed

+156
-40
lines changed

check/TestPdlp.cpp

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include "Highs.h"
44
#include "SpecialLps.h"
55
#include "catch.hpp"
6+
#include <chrono>
67

78
const bool dev_run = false;
89
const double double_equal_tolerance = 1e-3;
@@ -333,30 +334,42 @@ TEST_CASE("pdlp-restart-add-row", "[pdlp]") {
333334
}
334335

335336
TEST_CASE("hi-pdlp", "[pdlp]") {
336-
std::string model = "afiro"; //"adlittle";//"afiro";//
337+
std::string model = "adlittle"; //"adlittle";//"afiro";// shell// stair //25fv47 //fit2p
337338
std::string model_file =
338339
std::string(HIGHS_DIR) + "/check/instances/" + model + ".mps";
339340
Highs h;
340341
// h.setOptionValue("output_flag", dev_run);
341342
REQUIRE(h.readModel(model_file) == HighsStatus::kOk);
342343
h.setOptionValue("solver", kHiPdlpString);
343344
h.setOptionValue("kkt_tolerance", kkt_tolerance);
345+
h.setOptionValue("presolve", "off");
344346

345-
HighsInt pdlp_features_off =
346-
// kPdlpScalingOff +
347-
kPdlpRestartOff + kPdlpAdaptiveStepSizeOff;
347+
HighsInt pdlp_features_off =
348+
//kPdlpScalingOff +
349+
//kPdlpRestartOff
350+
kPdlpAdaptiveStepSizeOff
351+
;
348352
h.setOptionValue("pdlp_features_off", pdlp_features_off);
349353

350354
HighsInt pdlp_scaling = // 0;
351355
kPdlpScalingRuiz
352356
//+ kPdlpScalingL2
353357
+ kPdlpScalingPC;
354358
h.setOptionValue("pdlp_scaling_mode", pdlp_scaling);
355-
h.setOptionValue("pdlp_step_size_strategy", 0);
359+
h.setOptionValue("pdlp_step_size_strategy", 1);
356360
h.setOptionValue("pdlp_restart_strategy", 2);
357-
// h.setOptionValue("pdlp_iteration_limit", 15);
361+
h.setOptionValue("pdlp_iteration_limit",3520);
358362
// h.setOptionValue("log_dev_level", kHighsLogDevLevelVerbose);
363+
auto start_hipdlp = std::chrono::high_resolution_clock::now();
359364
HighsStatus run_status = h.run();
365+
auto end_hipdlp = std::chrono::high_resolution_clock::now();
366+
auto duration_hipdlp = std::chrono::duration_cast<std::chrono::milliseconds>(end_hipdlp - start_hipdlp);
367+
std::cout << "\n--- HiPDLP Results ---" << std::endl;
368+
std::cout << "Status: " << h.modelStatusToString(h.getModelStatus()) << std::endl;
369+
std::cout << "Iterations: " << h.getInfo().pdlp_iteration_count << std::endl;
370+
std::cout << "Wall time: " << duration_hipdlp.count() / 1000.0 << " seconds" << std::endl;
371+
std::cout << "Objective: " << h.getInfo().objective_function_value << std::endl;
372+
360373
// REQUIRE(run_status == HighsStatus::kOk);
361374
// REQUIRE(h.getModelStatus() == HighsModelStatus::kOptimal);
362375
// REQUIRE(h.getInfo().pdlp_iteration_count == 11880);
@@ -365,7 +378,15 @@ TEST_CASE("hi-pdlp", "[pdlp]") {
365378
h.clearSolver();
366379
h.setOptionValue("solver", kCuPdlpString);
367380
h.setOptionValue("pdlp_log_level", 2);
381+
auto start_cupdlp = std::chrono::high_resolution_clock::now();
368382
run_status = h.run();
383+
auto end_cupdlp = std::chrono::high_resolution_clock::now();
384+
auto duration_cupdlp = std::chrono::duration_cast<std::chrono::milliseconds>(end_cupdlp - start_cupdlp);
385+
std::cout << "\n--- cuPDLP Results ---" << std::endl;
386+
std::cout << "Status: " << h.modelStatusToString(h.getModelStatus()) << std::endl;
387+
std::cout << "Iterations: " << h.getInfo().pdlp_iteration_count << std::endl;
388+
std::cout << "Wall time: " << duration_cupdlp.count() / 1000.0 << " seconds" << std::endl;
389+
std::cout << "Objective: " << h.getInfo().objective_function_value << std::endl;
369390
}
370391
h.resetGlobalScheduler(true);
371392
}

highs/pdlp/HiPdlpWrapper.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,11 @@ HighsStatus solveLpHiPdlp(const HighsOptions& options, HighsTimer& timer,
5656
std::vector<double> x, y;
5757
x.resize(pdlp.getnCol(),0.0);
5858
y.resize(pdlp.getnRow(), 0.0);
59+
auto solve_start = std::chrono::high_resolution_clock::now();
5960
pdlp.solve(x, y);
61+
auto solve_end = std::chrono::high_resolution_clock::now();
62+
pdlp.timings_.total_time = std::chrono::duration<double>(solve_end - solve_start).count();
63+
pdlp.timings_.print("HiPdlp :");
6064

6165
// 5. Unscale with HiPdlp
6266
pdlp.unscaleSolution(x, y);

highs/pdlp/cupdlp/cupdlp_solver.c

Lines changed: 18 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1003,24 +1003,23 @@ cupdlp_retcode PDHG_Solve(const cupdlp_int* has_variables, CUPDLPwork *pdhg) {
10031003
}
10041004

10051005
if (PDHG_Check_Termination_Average(pdhg, termination_print)) {
1006-
// cupdlp_printf("Optimal average solution.\n");
1007-
1008-
cupdlp_int iter = pdhg->timers->nIter;
1009-
CUPDLPvec *x = iterates->x[iter % 2];
1010-
CUPDLPvec *y = iterates->y[iter % 2];
1011-
CUPDLPvec *ax = iterates->ax[iter % 2];
1012-
CUPDLPvec *aty = iterates->aty[iter % 2];
1013-
1014-
CUPDLP_COPY_VEC(x->data, iterates->xAverage->data, cupdlp_float, problem->nCols);
1015-
CUPDLP_COPY_VEC(y->data, iterates->yAverage->data, cupdlp_float, problem->nRows);
1016-
CUPDLP_COPY_VEC(ax->data, iterates->axAverage->data, cupdlp_float, problem->nRows);
1017-
CUPDLP_COPY_VEC(aty->data, iterates->atyAverage->data, cupdlp_float, problem->nCols);
1018-
CUPDLP_COPY_VEC(resobj->dSlackPos, resobj->dSlackPosAverage, cupdlp_float, problem->nCols);
1019-
CUPDLP_COPY_VEC(resobj->dSlackNeg, resobj->dSlackNegAverage, cupdlp_float, problem->nCols);
1020-
1021-
resobj->termIterate = AVERAGE_ITERATE;
1022-
resobj->termCode = OPTIMAL;
1023-
break;
1006+
// cupdlp_printf("Optimal average solution.\n");
1007+
cupdlp_int iter = pdhg->timers->nIter;
1008+
CUPDLPvec *x = iterates->x[iter % 2];
1009+
CUPDLPvec *y = iterates->y[iter % 2];
1010+
CUPDLPvec *ax = iterates->ax[iter % 2];
1011+
CUPDLPvec *aty = iterates->aty[iter % 2];
1012+
1013+
CUPDLP_COPY_VEC(x->data, iterates->xAverage->data, cupdlp_float, problem->nCols);
1014+
CUPDLP_COPY_VEC(y->data, iterates->yAverage->data, cupdlp_float, problem->nRows);
1015+
CUPDLP_COPY_VEC(ax->data, iterates->axAverage->data, cupdlp_float, problem->nRows);
1016+
CUPDLP_COPY_VEC(aty->data, iterates->atyAverage->data, cupdlp_float, problem->nCols);
1017+
CUPDLP_COPY_VEC(resobj->dSlackPos, resobj->dSlackPosAverage, cupdlp_float, problem->nCols);
1018+
CUPDLP_COPY_VEC(resobj->dSlackNeg, resobj->dSlackNegAverage, cupdlp_float, problem->nCols);
1019+
1020+
resobj->termIterate = AVERAGE_ITERATE;
1021+
resobj->termCode = OPTIMAL;
1022+
break;
10241023
}
10251024

10261025
if (PDHG_Check_Infeasibility(pdhg, 0) == INFEASIBLE_OR_UNBOUNDED) {
@@ -1061,8 +1060,7 @@ cupdlp_retcode PDHG_Solve(const cupdlp_int* has_variables, CUPDLPwork *pdhg) {
10611060
double debug_pdlp_data_aty_norm = 0.0;
10621061
cupdlp_twoNorm(pdhg, problem->nCols, aty->data, &debug_pdlp_data_aty_norm);
10631062
pdhg->debug_pdlp_data_.aty_norm = debug_pdlp_data_aty_norm;
1064-
1065-
1063+
10661064
// CUPDLP_CALL(PDHG_Update_Iterate(pdhg));
10671065
if (PDHG_Update_Iterate(pdhg) == RETCODE_FAILED) {
10681066
// cupdlp_printf("Time limit reached.\n");

highs/pdlp/hipdlp/defs.hpp

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,4 +162,37 @@ namespace pdlp_iterate_ops {
162162
// Compute z_new = z_old -
163163
};
164164

165+
struct DetailedTimings {
166+
double total_time = 0.0;
167+
double iterate_update_time = 0.0;
168+
double matrix_multiply_time = 0.0; // Ax and ATy
169+
double convergence_check_time = 0.0;
170+
double restart_check_time = 0.0;
171+
double average_iterate_time = 0.0;
172+
double projection_time = 0.0;
173+
double step_size_adjustment_time = 0.0;
174+
double other_time = 0.0;
175+
176+
void print(const std::string& solver_name) const {
177+
std::cout << "\n=== " << solver_name << " Detailed Timings ===" << std::endl;
178+
std::cout << "Total time: " << total_time << " s" << std::endl;
179+
std::cout << "Iterate update: " << iterate_update_time
180+
<< " s (" << (iterate_update_time/total_time*100) << "%)" << std::endl;
181+
std::cout << " - Matrix multiply: " << matrix_multiply_time
182+
<< " s (" << (matrix_multiply_time/total_time*100) << "%)" << std::endl;
183+
std::cout << " - Projection: " << projection_time
184+
<< " s (" << (projection_time/total_time*100) << "%)" << std::endl;
185+
std::cout << " - Step size adjust: " << step_size_adjustment_time
186+
<< " s (" << (step_size_adjustment_time/total_time*100) << "%)" << std::endl;
187+
std::cout << "Convergence check: " << convergence_check_time
188+
<< " s (" << (convergence_check_time/total_time*100) << "%)" << std::endl;
189+
std::cout << "Restart check: " << restart_check_time
190+
<< " s (" << (restart_check_time/total_time*100) << "%)" << std::endl;
191+
std::cout << "Average iterate comp: " << average_iterate_time
192+
<< " s (" << (average_iterate_time/total_time*100) << "%)" << std::endl;
193+
std::cout << "Other: " << other_time
194+
<< " s (" << (other_time/total_time*100) << "%)" << std::endl;
195+
}
196+
};
197+
165198
#endif

highs/pdlp/hipdlp/pdhg.cc

Lines changed: 69 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -460,13 +460,15 @@ PostSolveRetcode PDLPSolver::postprocess(HighsSolution& solution) {
460460
}
461461

462462
void PDLPSolver::solve(std::vector<double>& x, std::vector<double>& y) {
463+
auto solve_start = std::chrono::high_resolution_clock::now();
463464
Timer solver_timer;
464465
const HighsLp& lp = lp_;
465466

466467
debug_pdlp_log_file_ = fopen("HiPDLP.log", "w");
467468
assert(debug_pdlp_log_file_);
468469

469470
// --- 0. Using PowerMethod to estimate the largest eigenvalue ---
471+
auto init_start = std::chrono::high_resolution_clock::now();
470472
InitializeStepSizes();
471473

472474
PrimalDualParams working_params = params_;
@@ -511,6 +513,9 @@ void PDLPSolver::solve(std::vector<double>& x, std::vector<double>& y) {
511513

512514
logger_.print_iteration_header();
513515

516+
auto init_end = std::chrono::high_resolution_clock::now();
517+
timings_.other_time += std::chrono::duration<double>(init_end - init_start).count();
518+
514519
// --- 2. Main PDHG Loop ---
515520
debugPdlpIterHeaderLog(debug_pdlp_log_file_);
516521
debugPdlpDataInitialise(&debug_pdlp_data_);
@@ -538,14 +543,19 @@ void PDLPSolver::solve(std::vector<double>& x, std::vector<double>& y) {
538543

539544
bool_checking = (bool_checking || iter % PDHG_CHECK_INTERVAL == 0);
540545
if (bool_checking) {
546+
auto avg_start = std::chrono::high_resolution_clock::now();
541547
ComputeAverageIterate(Ax_avg, ATy_avg);
548+
auto avg_end = std::chrono::high_resolution_clock::now();
549+
timings_.average_iterate_time += std::chrono::duration<double>(avg_end - avg_start).count();
550+
542551
// Reset the average iterate accumulation
543552
int inner_iter = iter - restart_scheme_.GetLastRestartIter();
544553

545554
// Compute residuals and convergence metrics
546555
SolverResults current_results;
547556
SolverResults average_results;
548557

558+
auto conv_start = std::chrono::high_resolution_clock::now();
549559
// Compute residuals for current iterate
550560
bool current_converged = CheckConvergence(
551561
iter, x_current_, y_current_, Ax_cache_, ATy_cache_,
@@ -555,6 +565,8 @@ void PDLPSolver::solve(std::vector<double>& x, std::vector<double>& y) {
555565
bool average_converged =
556566
CheckConvergence(iter, x_avg_, y_avg_, Ax_avg, ATy_avg,
557567
params_.tolerance, average_results, "[A]");
568+
auto conv_end = std::chrono::high_resolution_clock::now();
569+
timings_.convergence_check_time += std::chrono::duration<double>(conv_end - conv_start).count();
558570

559571
debugPdlpIterHeaderLog(debug_pdlp_log_file_);
560572

@@ -585,6 +597,7 @@ void PDLPSolver::solve(std::vector<double>& x, std::vector<double>& y) {
585597
}
586598

587599
// --- 4. Restart Check (using computed results) ---
600+
auto restart_start = std::chrono::high_resolution_clock::now();
588601
RestartInfo restart_info =
589602
restart_scheme_.Check(iter, current_results, average_results);
590603

@@ -618,17 +631,21 @@ void PDLPSolver::solve(std::vector<double>& x, std::vector<double>& y) {
618631
sum_weights_ = 0.0;
619632

620633
restart_scheme_.last_restart_iter_ = iter;
621-
634+
auto matvec_start = std::chrono::high_resolution_clock::now();
622635
// Recompute Ax and ATy for the restarted iterates
623636
linalg::Ax(lp, x_current_, Ax_cache_);
624637
linalg::ATy(lp, y_current_, ATy_cache_);
638+
auto matvec_end = std::chrono::high_resolution_clock::now();
639+
timings_.matrix_multiply_time += std::chrono::duration<double>(matvec_end - matvec_start).count();
640+
625641
restart_scheme_.SetLastRestartIter(iter);
626-
627-
//continue; //same logic as cupdlp
628642
}
643+
auto restart_end = std::chrono::high_resolution_clock::now();
644+
timings_.restart_check_time += std::chrono::duration<double>(restart_end - restart_start).count();
629645
}
630646

631647
// --- 5. Core PDHG Update Step ---
648+
auto update_start = std::chrono::high_resolution_clock::now();
632649
bool step_success = true;
633650

634651
// Store current iterates before update (for next iteration's x_current_,
@@ -661,15 +678,25 @@ void PDLPSolver::solve(std::vector<double>& x, std::vector<double>& y) {
661678
return;
662679
}
663680
}
681+
auto update_end = std::chrono::high_resolution_clock::now();
682+
timings_.iterate_update_time += std::chrono::duration<double>(update_end - update_start).count();
664683

665684
// Compute ATy for the new iterate
666685
Ax_cache_ = Ax_next_;
686+
auto aty_start = std::chrono::high_resolution_clock::now();
667687
linalg::ATy(lp, y_next_, ATy_cache_);
688+
auto aty_end = std::chrono::high_resolution_clock::now();
689+
timings_.matrix_multiply_time += std::chrono::duration<double>(aty_end - aty_start).count();
690+
668691

669692
// --- 6. Update Average Iterates ---
670693
// The number of iterations since the last restart
671694
int inner_iter = iter - restart_scheme_.GetLastRestartIter();
672-
UpdateAverageIterates(x_next_, y_next_, working_params, inner_iter);;
695+
auto avg_update_start = std::chrono::high_resolution_clock::now();
696+
UpdateAverageIterates(x_next_, y_next_, working_params, inner_iter);
697+
auto avg_update_end = std::chrono::high_resolution_clock::now();
698+
timings_.average_iterate_time += std::chrono::duration<double>(avg_update_end - avg_update_start).count();
699+
673700

674701
// --- 7. Prepare for next iteration ---
675702
x_current_ = x_next_;
@@ -686,6 +713,8 @@ void PDLPSolver::solve(std::vector<double>& x, std::vector<double>& y) {
686713
y = y_avg_;
687714

688715
results_.term_code = TerminationStatus::TIMEOUT;
716+
auto solve_end = std::chrono::high_resolution_clock::now();
717+
timings_.total_time = std::chrono::duration<double>(solve_end - solve_start).count();
689718
return;
690719
}
691720

@@ -1352,12 +1381,24 @@ std::vector<double> PDLPSolver::UpdateY(const std::vector<double> &y, const std:
13521381
}
13531382

13541383
void PDLPSolver::UpdateIteratesFixed() {
1355-
x_next_ = UpdateX(x_current_, ATy_cache_,stepsize_.primal_step);
1384+
auto proj_start = std::chrono::high_resolution_clock::now();
1385+
x_next_ = UpdateX(x_current_, ATy_cache_, stepsize_.primal_step);
1386+
auto proj_end = std::chrono::high_resolution_clock::now();
1387+
timings_.projection_time += std::chrono::duration<double>(proj_end - proj_start).count();
1388+
1389+
auto ax_start = std::chrono::high_resolution_clock::now();
13561390
linalg::Ax(lp_, x_next_, Ax_next_);
1357-
y_next_ = UpdateY(y_current_, Ax_cache_,Ax_next_, stepsize_.dual_step);
1391+
auto ax_end = std::chrono::high_resolution_clock::now();
1392+
timings_.matrix_multiply_time += std::chrono::duration<double>(ax_end - ax_start).count();
1393+
1394+
auto proj_y_start = std::chrono::high_resolution_clock::now();
1395+
y_next_ = UpdateY(y_current_, Ax_cache_, Ax_next_, stepsize_.dual_step);
1396+
auto proj_y_end = std::chrono::high_resolution_clock::now();
1397+
timings_.projection_time += std::chrono::duration<double>(proj_y_end - proj_y_start).count();
13581398
}
13591399

13601400
void PDLPSolver::UpdateIteratesAdaptive() {
1401+
auto step_adjust_start = std::chrono::high_resolution_clock::now();
13611402
const double MIN_ETA = 1e-6;
13621403
const double MAX_ETA = 1.0;
13631404

@@ -1393,12 +1434,26 @@ void PDLPSolver::UpdateIteratesAdaptive() {
13931434
double dual_step_update = dStepSizeUpdate * std::sqrt(stepsize_.beta);
13941435

13951436
// Primal update
1396-
xupdate = UpdateX(x_candidate, aty_candidate,primal_step_update); //need to take aty
1397-
linalg::Ax(lp_, xupdate , axupdate);
1398-
1399-
// Dual update
1400-
yupdate = UpdateY(y_candidate, ax_candidate,axupdate, dual_step_update);
1437+
auto proj_start = std::chrono::high_resolution_clock::now();
1438+
xupdate = UpdateX(x_candidate, aty_candidate, primal_step_update);
1439+
auto proj_end = std::chrono::high_resolution_clock::now();
1440+
timings_.projection_time += std::chrono::duration<double>(proj_end - proj_start).count();
1441+
1442+
auto ax_start = std::chrono::high_resolution_clock::now();
1443+
linalg::Ax(lp_, xupdate, axupdate);
1444+
auto ax_end = std::chrono::high_resolution_clock::now();
1445+
timings_.matrix_multiply_time += std::chrono::duration<double>(ax_end - ax_start).count();
1446+
1447+
// Dual update with timing
1448+
auto proj_y_start = std::chrono::high_resolution_clock::now();
1449+
yupdate = UpdateY(y_candidate, ax_candidate, axupdate, dual_step_update);
1450+
auto proj_y_end = std::chrono::high_resolution_clock::now();
1451+
timings_.projection_time += std::chrono::duration<double>(proj_y_end - proj_y_start).count();
1452+
1453+
auto aty_start = std::chrono::high_resolution_clock::now();
14011454
linalg::ATy(lp_, yupdate, atyupdate);
1455+
auto aty_end = std::chrono::high_resolution_clock::now();
1456+
timings_.matrix_multiply_time += std::chrono::duration<double>(aty_end - aty_start).count();
14021457

14031458
// Compute deltas
14041459
std::vector<double> delta_x(lp_.num_col_);
@@ -1473,6 +1528,9 @@ void PDLPSolver::UpdateIteratesAdaptive() {
14731528
current_eta_ = dStepSizeUpdate;
14741529
stepsize_.primal_step = dStepSizeUpdate / std::sqrt(stepsize_.beta);
14751530
stepsize_.dual_step = dStepSizeUpdate * std::sqrt(stepsize_.beta);
1531+
1532+
auto step_adjust_end = std::chrono::high_resolution_clock::now();
1533+
timings_.step_size_adjustment_time += std::chrono::duration<double>(step_adjust_end - step_adjust_start).count();
14761534
}
14771535

14781536
bool PDLPSolver::UpdateIteratesMalitskyPock(

highs/pdlp/hipdlp/pdhg.hpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,13 @@ class PDLPSolver {
4646
TerminationStatus getTerminationCode() const { return results_.term_code; }
4747
int getIterationCount() const { return final_iter_count_; }
4848
int getnCol() const { return lp_.num_col_; }
49-
int getnRow() const { return lp_.num_row_; }
49+
int getnRow() const { return lp_.num_row_; }
5050

5151
// --- Debugging ---
5252
FILE* debug_pdlp_log_file_ = nullptr;
5353
DebugPdlpData debug_pdlp_data_;
54+
DetailedTimings timings_;
55+
const DetailedTimings& getTimings() const { return timings_; }
5456

5557
private:
5658
// --- Core Algorithm Logic ---

0 commit comments

Comments
 (0)