Skip to content

Commit b38424f

Browse files
committed
Fix relaxSupernodes
1 parent ba20d45 commit b38424f

File tree

3 files changed

+100
-168
lines changed

3 files changed

+100
-168
lines changed

highs/ipm/hipo/factorhighs/Analyse.cpp

Lines changed: 98 additions & 166 deletions
Original file line numberDiff line numberDiff line change
@@ -396,157 +396,9 @@ void Analyse::fundamentalSupernodes() {
396396
sn_parent_.back() = -1;
397397
}
398398

399-
void Analyse::relaxSupernodes() {
400-
// Child which produces smallest number of fake nonzeros is merged if
401-
// resulting sn has fewer than max_artificial_nz fake nonzeros.
402-
// Multiple values of max_artificial_nz are tried, chosen with bisection
403-
// method, until the percentage of artificial nonzeros is in the range [1,2]%.
404-
405-
Int max_artificial_nz = kStartThreshRelax;
406-
Int largest_below = -1;
407-
Int smallest_above = -1;
408-
409-
for (Int iter = 0; iter < kMaxIterRelax; ++iter) {
410-
// =================================================
411-
// Build information about supernodes
412-
// =================================================
413-
std::vector<Int> sn_size(sn_count_);
414-
std::vector<Int> clique_size(sn_count_);
415-
fake_nz_.assign(sn_count_, 0);
416-
for (Int i = 0; i < sn_count_; ++i) {
417-
sn_size[i] = sn_start_[i + 1] - sn_start_[i];
418-
clique_size[i] = col_count_[sn_start_[i]] - sn_size[i];
419-
fake_nz_[i] = 0;
420-
}
421-
422-
// build linked lists of children
423-
std::vector<Int> first_child, next_child;
424-
childrenLinkedList(sn_parent_, first_child, next_child);
425-
426-
// =================================================
427-
// Merge supernodes
428-
// =================================================
429-
merged_into_.assign(sn_count_, -1);
430-
merged_sn_ = 0;
431-
432-
for (Int sn = 0; sn < sn_count_; ++sn) {
433-
// keep iterating through the children of the supernode, until there's no
434-
// more child to merge with
435-
436-
while (true) {
437-
Int child = first_child[sn];
438-
439-
// info for first criterion
440-
int64_t nz_fakenz = kHighsIInf;
441-
Int size_fakenz = 0;
442-
Int child_fakenz = -1;
443-
444-
while (child != -1) {
445-
// how many zero rows would become nonzero
446-
const int64_t rows_filled =
447-
sn_size[sn] + clique_size[sn] - clique_size[child];
448-
449-
// how many zero entries would become nonzero
450-
const int64_t nz_added = rows_filled * sn_size[child];
451-
452-
// how many artificial nonzeros would the merged supernode have
453-
const int64_t total_art_nz =
454-
nz_added + fake_nz_[sn] + fake_nz_[child];
455-
456-
// Save child with smallest number of artificial zeros created.
457-
// Ties are broken based on size of child.
458-
if (total_art_nz < nz_fakenz ||
459-
(total_art_nz == nz_fakenz && size_fakenz < sn_size[child])) {
460-
nz_fakenz = total_art_nz;
461-
size_fakenz = sn_size[child];
462-
child_fakenz = child;
463-
}
464-
465-
child = next_child[child];
466-
}
467-
468-
if (nz_fakenz <= max_artificial_nz) {
469-
// merging creates fewer nonzeros than the maximum allowed
470-
471-
// update information of parent
472-
sn_size[sn] += size_fakenz;
473-
fake_nz_[sn] = nz_fakenz;
474-
475-
// count number of merged supernodes
476-
++merged_sn_;
477-
478-
// save information about merging of supernodes
479-
merged_into_[child_fakenz] = sn;
480-
481-
// remove child from linked list of children
482-
child = first_child[sn];
483-
if (child == child_fakenz) {
484-
// child_smallest is the first child
485-
first_child[sn] = next_child[child_fakenz];
486-
} else {
487-
while (next_child[child] != child_fakenz) {
488-
child = next_child[child];
489-
}
490-
// now child is the previous child of child_smallest
491-
next_child[child] = next_child[child_fakenz];
492-
}
493-
494-
} else {
495-
// no more children can be merged with parent
496-
break;
497-
}
498-
}
499-
}
500-
501-
// compute total number of artificial nonzeros and artificial ops for this
502-
// value of max_artificial_nz
503-
double temp_art_nz{};
504-
double temp_art_ops{};
505-
for (Int sn = 0; sn < sn_count_; ++sn) {
506-
if (merged_into_[sn] == -1) {
507-
temp_art_nz += fake_nz_[sn];
508-
509-
const double nn = sn_size[sn];
510-
const double cc = clique_size[sn];
511-
temp_art_ops += (nn + cc) * (nn + cc) * nn - (nn + cc) * nn * (nn + 1) +
512-
nn * (nn + 1) * (2 * nn + 1) / 6;
513-
}
514-
}
515-
temp_art_ops -= dense_ops_norelax_;
516-
517-
// if enough fake nz or ops have been added, stop.
518-
const double ratio_fake =
519-
temp_art_ops / (temp_art_ops + dense_ops_norelax_);
520-
521-
// try to find ratio in interval [0.01,0.02] using bisection
522-
if (ratio_fake < kLowerRatioRelax) {
523-
// ratio too small
524-
largest_below = max_artificial_nz;
525-
if (smallest_above == -1) {
526-
max_artificial_nz *= 2;
527-
} else {
528-
max_artificial_nz = (largest_below + smallest_above) / 2;
529-
}
530-
} else if (ratio_fake > kUpperRatioRelax) {
531-
// ratio too large
532-
smallest_above = max_artificial_nz;
533-
if (largest_below == -1) {
534-
max_artificial_nz /= 2;
535-
} else {
536-
max_artificial_nz = (largest_below + smallest_above) / 2;
537-
}
538-
} else {
539-
// good ratio
540-
return;
541-
}
542-
}
543-
}
544-
545-
void Analyse::relaxSupernodesSize() {
546-
// Smallest child is merged with parent, if child is small enough.
547-
399+
double Analyse::doRelaxSupernodes(int64_t max_artificial_nz) {
548400
// =================================================
549-
// build information about supernodes
401+
// Build information about supernodes
550402
// =================================================
551403
std::vector<Int> sn_size(sn_count_);
552404
std::vector<Int> clique_size(sn_count_);
@@ -575,9 +427,9 @@ void Analyse::relaxSupernodesSize() {
575427
Int child = first_child[sn];
576428

577429
// info for first criterion
578-
Int size_smallest = kHighsIInf;
579-
Int child_smallest = -1;
580-
int64_t nz_smallest = 0;
430+
int64_t nz_fakenz = kHighsIInf;
431+
Int size_fakenz = 0;
432+
Int child_fakenz = -1;
581433

582434
while (child != -1) {
583435
// how many zero rows would become nonzero
@@ -590,39 +442,42 @@ void Analyse::relaxSupernodesSize() {
590442
// how many artificial nonzeros would the merged supernode have
591443
const int64_t total_art_nz = nz_added + fake_nz_[sn] + fake_nz_[child];
592444

593-
if (sn_size[child] < size_smallest) {
594-
size_smallest = sn_size[child];
595-
child_smallest = child;
596-
nz_smallest = total_art_nz;
445+
// Save child with smallest number of artificial zeros created.
446+
// Ties are broken based on size of child.
447+
if (total_art_nz < nz_fakenz ||
448+
(total_art_nz == nz_fakenz && size_fakenz < sn_size[child])) {
449+
nz_fakenz = total_art_nz;
450+
size_fakenz = sn_size[child];
451+
child_fakenz = child;
597452
}
598453

599454
child = next_child[child];
600455
}
601456

602-
if (size_smallest < kSnSizeRelax && sn_size[sn] < kSnSizeRelax) {
603-
// smallest supernode is small enough to be merged with parent
457+
if (nz_fakenz <= max_artificial_nz) {
458+
// merging creates fewer nonzeros than the maximum allowed
604459

605460
// update information of parent
606-
sn_size[sn] += size_smallest;
607-
fake_nz_[sn] = nz_smallest;
461+
sn_size[sn] += size_fakenz;
462+
fake_nz_[sn] = nz_fakenz;
608463

609464
// count number of merged supernodes
610465
++merged_sn_;
611466

612467
// save information about merging of supernodes
613-
merged_into_[child_smallest] = sn;
468+
merged_into_[child_fakenz] = sn;
614469

615470
// remove child from linked list of children
616471
child = first_child[sn];
617-
if (child == child_smallest) {
472+
if (child == child_fakenz) {
618473
// child_smallest is the first child
619-
first_child[sn] = next_child[child_smallest];
474+
first_child[sn] = next_child[child_fakenz];
620475
} else {
621-
while (next_child[child] != child_smallest) {
476+
while (next_child[child] != child_fakenz) {
622477
child = next_child[child];
623478
}
624479
// now child is the previous child of child_smallest
625-
next_child[child] = next_child[child_smallest];
480+
next_child[child] = next_child[child_fakenz];
626481
}
627482

628483
} else {
@@ -631,6 +486,83 @@ void Analyse::relaxSupernodesSize() {
631486
}
632487
}
633488
}
489+
490+
// compute total number of artificial nonzeros and artificial ops for this
491+
// value of max_artificial_nz
492+
double temp_art_nz{};
493+
double temp_art_ops{};
494+
for (Int sn = 0; sn < sn_count_; ++sn) {
495+
if (merged_into_[sn] == -1) {
496+
temp_art_nz += fake_nz_[sn];
497+
498+
const double nn = sn_size[sn];
499+
const double cc = clique_size[sn];
500+
temp_art_ops += (nn + cc) * (nn + cc) * nn - (nn + cc) * nn * (nn + 1) +
501+
nn * (nn + 1) * (2 * nn + 1) / 6;
502+
}
503+
}
504+
temp_art_ops -= dense_ops_norelax_;
505+
506+
// if enough fake nz or ops have been added, stop.
507+
const double ratio_fake = temp_art_ops / (temp_art_ops + dense_ops_norelax_);
508+
509+
return ratio_fake;
510+
}
511+
512+
void Analyse::relaxSupernodes() {
513+
// Child which produces smallest number of fake nonzeros is merged if
514+
// resulting sn has fewer than max_artificial_nz fake nonzeros.
515+
// Multiple values of max_artificial_nz are tried, chosen with bisection
516+
// method, until the percentage of artificial nonzeros is in the range [1,2]%.
517+
518+
int64_t max_artificial_nz = kStartThreshRelax;
519+
int64_t largest_below = -1;
520+
int64_t smallest_above = -1;
521+
522+
double best_dist_ratio = kHighsInf;
523+
int64_t best_max_art_nz = -1;
524+
525+
for (Int iter = 0; iter < kMaxIterRelax; ++iter) {
526+
// relax the supernodes and obtain the ratio of how many new ops have been
527+
// added with the current value of max_artificial_nz
528+
const double ratio_fake = doRelaxSupernodes(max_artificial_nz);
529+
530+
// store the best ratio, in case a good ratio is never found
531+
double dist_ratio_fake = std::min(std::abs(ratio_fake - kLowerRatioRelax),
532+
std::abs(ratio_fake - kUpperRatioRelax));
533+
if (dist_ratio_fake < best_dist_ratio) {
534+
best_dist_ratio = dist_ratio_fake;
535+
best_max_art_nz = max_artificial_nz;
536+
}
537+
538+
// try to find ratio in interval [0.01,0.02] using bisection
539+
if (ratio_fake < kLowerRatioRelax) {
540+
// ratio too small
541+
largest_below = max_artificial_nz;
542+
if (smallest_above == -1) {
543+
max_artificial_nz *= 2;
544+
} else {
545+
max_artificial_nz = (largest_below + smallest_above) / 2;
546+
}
547+
} else if (ratio_fake > kUpperRatioRelax) {
548+
// ratio too large
549+
smallest_above = max_artificial_nz;
550+
if (largest_below == -1) {
551+
max_artificial_nz /= 2;
552+
} else {
553+
max_artificial_nz = (largest_below + smallest_above) / 2;
554+
}
555+
} else {
556+
// good ratio
557+
return;
558+
}
559+
}
560+
561+
// If reach here, no good ratio was found within kMaxIterRelax
562+
// To avoid having a catastrophically bad ratio in pathological problems,
563+
// choose the best ratio found
564+
565+
doRelaxSupernodes(best_max_art_nz);
634566
}
635567

636568
void Analyse::afterRelaxSn() {

highs/ipm/hipo/factorhighs/Analyse.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ class Analyse {
9191
void colCount();
9292
void fundamentalSupernodes();
9393
void relaxSupernodes();
94-
void relaxSupernodesSize();
94+
double doRelaxSupernodes(int64_t max_artificial_nz);
9595
void afterRelaxSn();
9696
void snPattern();
9797
void relativeIndCols();

highs/ipm/hipo/factorhighs/FactorHiGHSSettings.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ namespace hipo {
3636
const Int kStartThreshRelax = 256;
3737
const double kUpperRatioRelax = 0.02;
3838
const double kLowerRatioRelax = 0.01;
39-
const Int kMaxIterRelax = 10;
39+
const Int kMaxIterRelax = 20;
4040
const Int kSnSizeRelax = 16;
4141

4242
// dense factorisation

0 commit comments

Comments
 (0)