@@ -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
636568void Analyse::afterRelaxSn () {
0 commit comments