Skip to content

Commit ae2896a

Browse files
committed
Add logic to choose ordering
1 parent 0d84127 commit ae2896a

File tree

3 files changed

+102
-31
lines changed

3 files changed

+102
-31
lines changed

highs/ipm/hipo/ipm/FactorHiGHSSolver.cpp

Lines changed: 97 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -379,6 +379,101 @@ void FactorHiGHSSolver::getReg(std::vector<double>& reg) {
379379
return FH_.getRegularisation(reg);
380380
}
381381

382+
Int FactorHiGHSSolver::chooseOrdering(const std::vector<Int>& rows,
383+
const std::vector<Int>& ptr,
384+
const std::vector<Int>& signs,
385+
Symbolic& S) {
386+
// Run analyse phase.
387+
// - If ordering is "amd", "metis", "rcm" run only the ordering requested.
388+
// - If ordering is "choose", run "amd", "metis", and choose the best.
389+
390+
Clock clock;
391+
392+
// select which fill-reducing orderings should be tried
393+
std::vector<std::string> orderings_to_try;
394+
if (options_.ordering != kHighsChooseString)
395+
orderings_to_try.push_back(options_.ordering);
396+
else {
397+
orderings_to_try.push_back("amd");
398+
orderings_to_try.push_back("metis");
399+
// rcm is much worse in general, so no point in trying for now
400+
}
401+
402+
std::vector<Symbolic> symbolics(orderings_to_try.size(), S);
403+
std::vector<bool> status(orderings_to_try.size(), 0);
404+
Int num_success = 0;
405+
406+
for (Int i = 0; i < orderings_to_try.size(); ++i) {
407+
clock.start();
408+
status[i] =
409+
FH_.analyse(symbolics[i], rows, ptr, signs, orderings_to_try[i]);
410+
if (info_) info_->analyse_AS_time += clock.stop();
411+
412+
if (status[i] && log_.debug(2)) {
413+
log_.print("Failed symbolic:");
414+
symbolics[i].print(log_, true);
415+
}
416+
417+
if (!status[i]) ++num_success;
418+
}
419+
420+
if (orderings_to_try.size() < 2) {
421+
S = std::move(symbolics[0]);
422+
423+
} else if (orderings_to_try.size() == 2) {
424+
// if there's only one success, obvious choice
425+
if (status[0] && !status[1])
426+
S = std::move(symbolics[1]);
427+
else if (!status[0] && status[1])
428+
S = std::move(symbolics[0]);
429+
430+
else if (num_success > 1) {
431+
// need to choose the better ordering
432+
433+
const double flops_0 = symbolics[0].flops();
434+
const double flops_1 = symbolics[1].flops();
435+
const double sn_avg_0 = symbolics[0].size() / symbolics[0].sn();
436+
const double sn_avg_1 = symbolics[1].size() / symbolics[1].sn();
437+
const double bytes_0 = symbolics[0].storage();
438+
const double bytes_1 = symbolics[1].storage();
439+
440+
Int chosen = -1;
441+
442+
// selection rule:
443+
// - if flops have a clear winner (+/- 20%), then choose it.
444+
// - otherwise, choose the one with larger supernodes.
445+
446+
if (flops_0 > kFlopsOrderingThresh * flops_1)
447+
chosen = 1;
448+
else if (flops_1 > kFlopsOrderingThresh * flops_0)
449+
chosen = 0;
450+
else if (sn_avg_0 > sn_avg_1)
451+
chosen = 0;
452+
else
453+
chosen = 1;
454+
455+
// fix selection if one or more require too much memory
456+
const double bytes_thresh = kLargeStorageGB * 1024 * 1024 * 1024;
457+
if (bytes_0 > bytes_thresh || bytes_1 > bytes_thresh) {
458+
if (bytes_0 > bytes_1)
459+
chosen = 1;
460+
else
461+
chosen = 0;
462+
}
463+
464+
assert(chosen == 0 || chosen == 1);
465+
466+
S = std::move(symbolics[chosen]);
467+
}
468+
469+
} else {
470+
// only two orderings tried for now
471+
assert(0 == 1);
472+
}
473+
474+
return num_success > 0 ? kStatusOk : kStatusErrorAnalyse;
475+
}
476+
382477
Int FactorHiGHSSolver::analyseAS(Symbolic& S) {
383478
// Perform analyse phase of augmented system and return symbolic factorisation
384479
// in object S and the status.
@@ -399,19 +494,7 @@ Int FactorHiGHSSolver::analyseAS(Symbolic& S) {
399494

400495
log_.printDevInfo("Performing AS analyse phase\n");
401496

402-
std::string ordering = options_.ordering;
403-
if (ordering == kHighsChooseString) ordering = kHipoMetisString;
404-
405-
clock.start();
406-
Int status = FH_.analyse(S, rowsLower, ptrLower, pivot_signs, ordering);
407-
if (info_) info_->analyse_AS_time = clock.stop();
408-
409-
if (status && log_.debug(2)) {
410-
log_.print("Failed augmented system:");
411-
S.print(log_, true);
412-
}
413-
414-
return status ? kStatusErrorAnalyse : kStatusOk;
497+
return chooseOrdering(rowsLower, ptrLower, pivot_signs, S);
415498
}
416499

417500
void FactorHiGHSSolver::freeNEmemory() {
@@ -441,19 +524,7 @@ Int FactorHiGHSSolver::analyseNE(Symbolic& S, Int64 nz_limit) {
441524

442525
log_.printDevInfo("Performing NE analyse phase\n");
443526

444-
std::string ordering = options_.ordering;
445-
if (ordering == kHighsChooseString) ordering = kHipoMetisString;
446-
447-
clock.start();
448-
Int status = FH_.analyse(S, rowsNE_, ptrNE_, pivot_signs, ordering);
449-
if (info_) info_->analyse_NE_time = clock.stop();
450-
451-
if (status && log_.debug(2)) {
452-
log_.print("Failed normal equations:");
453-
S.print(log_, true);
454-
}
455-
456-
return status ? kStatusErrorAnalyse : kStatusOk;
527+
return chooseOrdering(rowsNE_, ptrNE_, pivot_signs, S);
457528
}
458529

459530
Int FactorHiGHSSolver::chooseNla() {
@@ -518,11 +589,6 @@ Int FactorHiGHSSolver::chooseNla() {
518589
status = kStatusErrorAnalyse;
519590

520591
log_.printe("Both NE and AS failed analyse phase\n");
521-
if ((symb_AS.fillin() > kLargeFillin || symb_NE.fillin() > kLargeFillin) &&
522-
options_.ordering == "metis" && !options_.metis_no2hop)
523-
log_.print(
524-
"Large fill-in in factorisation. Consider setting the "
525-
"hipo_metis_no2hop option to true\n");
526592
} else {
527593
// Total number of operations, given by dense flops and sparse indexing
528594
// operations, weighted with an empirical factor

highs/ipm/hipo/ipm/FactorHiGHSSolver.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ class FactorHiGHSSolver : public LinearSolver {
3838
Int chooseNla();
3939
Int setNla();
4040
void setParallel();
41+
Int chooseOrdering(const std::vector<Int>& rows, const std::vector<Int>& ptr,
42+
const std::vector<Int>& signs, Symbolic& S);
4143

4244
Int buildNEstructure(const HighsSparseMatrix& A, Int64 nz_limit = kHighsIInf);
4345
Int buildNEvalues(const HighsSparseMatrix& A,

highs/ipm/hipo/ipm/Parameters.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ const Int kMinNumberSn = 10;
3535
const double kLargeStorageGB = 20.0;
3636
const double kLargeFillin = 50.0;
3737

38+
// parameters for choice of ordering
39+
const double kFlopsOrderingThresh = 1.2;
40+
3841
// parameters for dense columns
3942
const double kDenseColThresh = 0.5;
4043
const Int kMinRowsForDensity = 2000;

0 commit comments

Comments
 (0)