Skip to content

Commit 84610bb

Browse files
committed
AS analyse and NE structure in parallel
1 parent 3d19148 commit 84610bb

File tree

4 files changed

+78
-34
lines changed

4 files changed

+78
-34
lines changed

highs/ipm/hipo/ipm/FactorHiGHSSolver.cpp

Lines changed: 68 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include "Status.h"
66
#include "ipm/hipo/auxiliary/Auxiliary.h"
77
#include "ipm/hipo/auxiliary/Log.h"
8+
#include "parallel/HighsParallel.h"
89

910
namespace hipo {
1011

@@ -121,8 +122,7 @@ Int FactorHiGHSSolver::buildNEvalues(const HighsSparseMatrix& A,
121122
return kStatusOk;
122123
}
123124

124-
Int FactorHiGHSSolver::buildNEstructure(const HighsSparseMatrix& A,
125-
int64_t nz_limit) {
125+
Int FactorHiGHSSolver::buildNEstructure(const HighsSparseMatrix& A) {
126126
// Build lower triangular structure of AAt.
127127
// This approach uses a column-wise copy of A, a partial row-wise copy and a
128128
// vector of corresponding indices.
@@ -194,10 +194,16 @@ Int FactorHiGHSSolver::buildNEstructure(const HighsSparseMatrix& A,
194194
}
195195
// intersection of row with rows below finished.
196196

197-
// if the total number of nonzeros exceeds the maximum, return error
198-
if ((int64_t)ptrNE_[row] + (int64_t)nz_in_col >= nz_limit)
197+
// if the total number of nonzeros overflows the int type, return OoM
198+
if ((int64_t)ptrNE_[row] + (int64_t)nz_in_col >= kHighsIInf)
199199
return kStatusOoM;
200200

201+
// if the total number of nonzeros exceeds the maximum, return error.
202+
// this is useful when the number of nnz from AS factor is known.
203+
if ((int64_t)ptrNE_[row] + (int64_t)nz_in_col >=
204+
NE_nz_limit_.load(std::memory_order_relaxed))
205+
return kStatusErrorAnalyse;
206+
201207
// update pointers
202208
ptrNE_[row + 1] = ptrNE_[row] + nz_in_col;
203209

@@ -380,7 +386,7 @@ Int FactorHiGHSSolver::analyseAS(Symbolic& S) {
380386
std::vector<Int> ptrLower, rowsLower;
381387
if (Int status = getASstructure(model_.A(), ptrLower, rowsLower))
382388
return status;
383-
if (info_) info_->matrix_structure_time = clock.stop();
389+
if (info_) info_->AS_structure_time = clock.stop();
384390

385391
// create vector of signs of pivots
386392
std::vector<Int> pivot_signs(model_.A().num_col_ + model_.A().num_row_, -1);
@@ -401,16 +407,12 @@ Int FactorHiGHSSolver::analyseAS(Symbolic& S) {
401407
return status ? kStatusErrorAnalyse : kStatusOk;
402408
}
403409

404-
Int FactorHiGHSSolver::analyseNE(Symbolic& S, int64_t nz_limit) {
410+
Int FactorHiGHSSolver::analyseNE(Symbolic& S) {
405411
// Perform analyse phase of augmented system and return symbolic factorisation
406412
// in object S and the status. If building the matrix failed, the status is
407413
// set to OoM.
408414

409-
log_.printDevInfo("Building NE structure\n");
410-
411415
Clock clock;
412-
if (Int status = buildNEstructure(model_.A(), nz_limit)) return status;
413-
if (info_) info_->matrix_structure_time = clock.stop();
414416

415417
// create vector of signs of pivots
416418
std::vector<Int> pivot_signs(model_.A().num_row_, 1);
@@ -441,24 +443,57 @@ Int FactorHiGHSSolver::chooseNla() {
441443

442444
Clock clock;
443445

444-
// Perform analyse phase of augmented system
445-
if (analyseAS(symb_AS)) failure_AS = true;
446-
447-
// Perform analyse phase of normal equations
448-
if (model_.m() > kMinRowsForDensity &&
449-
model_.maxColDensity() > kDenseColThresh) {
450-
// Normal equations would be too expensive because there are dense
451-
// columns, so skip it.
452-
failure_NE = true;
453-
} else {
454-
// If NE has more nonzeros than the factor of AS, then it's likely that AS
455-
// will be preferred, so stop computation of NE.
446+
// Perform analyseAS concurrently with buildNEstructure. Then, perform
447+
// analyseNE. Metis is not thread-safe, so cannot perform the two analyse at
448+
// the same time.
449+
highs::parallel::TaskGroup tg;
450+
451+
tg.spawn([&, this]() {
452+
// Perform analyse phase of augmented system
453+
if (analyseAS(symb_AS)) failure_AS = true;
454+
455+
// Set a multiple of the number of nonzeros in the factor
456+
// of AS as an upper limit for the number of nonzeros of NE.
457+
// This is potentially non-deterministic, because NE_nz_limit_ changes at a
458+
// random moment for function buildNEstructure. However, the most likely
459+
// outcome is that it stops the NE structure when it would not be chosen
460+
// anyway, so it shouldn't have a visible effect. There may be pathological
461+
// cases where this is not true though.
456462
int64_t NE_nz_limit = symb_AS.nz() * kSymbNzMult;
457463
if (failure_AS || NE_nz_limit > kHighsIInf) NE_nz_limit = kHighsIInf;
464+
NE_nz_limit_.store(NE_nz_limit, std::memory_order_relaxed);
465+
});
466+
467+
// Run the two tasks sequentially in debug mode
468+
if (log_.debug(1)) tg.taskWait();
469+
470+
tg.spawn([&, this]() {
471+
if (model_.m() > kMinRowsForDensity &&
472+
model_.maxColDensity() > kDenseColThresh) {
473+
// Normal equations would be too expensive because there are dense
474+
// columns, so skip it.
475+
failure_NE = true;
476+
log_.printDevInfo("NE skipped\n");
477+
} else {
478+
log_.printDevInfo("Building NE structure\n");
479+
480+
Clock clock;
481+
if (Int status = buildNEstructure(model_.A())) {
482+
failure_NE = true;
483+
if (status == kStatusErrorAnalyse)
484+
log_.printDevInfo("NE stopped early\n");
485+
if (status == kStatusOoM) log_.printDevInfo("NE matrix is too large\n");
486+
}
487+
if (info_) info_->NE_structure_time = clock.stop();
488+
}
489+
});
490+
491+
tg.taskWait();
458492

459-
Int NE_status = analyseNE(symb_NE, NE_nz_limit);
493+
// Perform analyse phase of normal equations
494+
if (!failure_NE) {
495+
Int NE_status = analyseNE(symb_NE);
460496
if (NE_status) failure_NE = true;
461-
if (NE_status == kStatusOoM) log_.printDevInfo("NE matrix is too large\n");
462497
}
463498

464499
Int status = kStatusOk;
@@ -529,11 +564,16 @@ Int FactorHiGHSSolver::setNla() {
529564
}
530565

531566
case kOptionNlaNormEq: {
532-
Int status = analyseNE(S_);
533-
if (status == kStatusOoM) {
567+
Clock clock;
568+
Int status = buildNEstructure(model_.A());
569+
if (info_) info_->NE_structure_time = clock.stop();
570+
if (status) {
534571
log_.printe("NE requested, matrix is too large\n");
535-
return kStatusOoM;
536-
} else if (status) {
572+
return kStatusErrorAnalyse;
573+
}
574+
575+
status = analyseNE(S_);
576+
if (status) {
537577
log_.printe("NE requested, failed analyse phase\n");
538578
return kStatusErrorAnalyse;
539579
}

highs/ipm/hipo/ipm/FactorHiGHSSolver.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#define HIPO_FACTORHIGHS_SOLVER_H
33

44
#include <algorithm>
5+
#include <atomic>
56

67
#include "Info.h"
78
#include "IpmData.h"
@@ -24,6 +25,7 @@ class FactorHiGHSSolver : public LinearSolver {
2425
std::vector<double> valNE_;
2526
std::vector<Int> ptrNE_rw_, idxNE_rw_;
2627
std::vector<Int> corr_NE_;
28+
std::atomic<int64_t> NE_nz_limit_{kHighsIInf};
2729

2830
const Regularisation& regul_;
2931

@@ -38,13 +40,12 @@ class FactorHiGHSSolver : public LinearSolver {
3840
Int setNla();
3941
void setParallel();
4042

41-
Int buildNEstructure(const HighsSparseMatrix& A,
42-
int64_t nz_limit = kHighsIInf);
43+
Int buildNEstructure(const HighsSparseMatrix& A);
4344
Int buildNEvalues(const HighsSparseMatrix& A,
4445
const std::vector<double>& scaling);
4546

4647
Int analyseAS(Symbolic& S);
47-
Int analyseNE(Symbolic& S, int64_t nz_limit = kHighsIInf);
48+
Int analyseNE(Symbolic& S);
4849

4950
public:
5051
FactorHiGHSSolver(Options& options, const Model& model,

highs/ipm/hipo/ipm/Info.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,8 @@ struct Info {
4343
double analyse_NE_time{};
4444
double analyse_AS_time{};
4545
double matrix_time{};
46-
double matrix_structure_time{};
46+
double NE_structure_time{};
47+
double AS_structure_time{};
4748
double factor_time{};
4849
double solve_time{};
4950

highs/ipm/hipo/ipm/Solver.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1478,10 +1478,12 @@ void Solver::printSummary() const {
14781478
<< fix(info_.analyse_AS_time, 0, 2) << '\n';
14791479
log_stream << textline("Analyse NE time:")
14801480
<< fix(info_.analyse_NE_time, 0, 2) << '\n';
1481+
log_stream << textline("AS structure time:")
1482+
<< fix(info_.AS_structure_time, 0, 2) << '\n';
1483+
log_stream << textline("NE structure time:")
1484+
<< fix(info_.NE_structure_time, 0, 2) << '\n';
14811485
log_stream << textline("Matrix time:") << fix(info_.matrix_time, 0, 2)
14821486
<< '\n';
1483-
log_stream << textline("Matrix structure time:")
1484-
<< fix(info_.matrix_structure_time, 0, 2) << '\n';
14851487
log_stream << textline("Factorisation time:")
14861488
<< fix(info_.factor_time, 0, 2) << '\n';
14871489
log_stream << textline("Solve time:") << fix(info_.solve_time, 0, 2)

0 commit comments

Comments
 (0)