@@ -988,12 +988,11 @@ void RepairSetup::printProgressFooter() const
988988
989989void RepairSetup::resetStagnationTracking ()
990990{
991- wns_history_. assign (wns_stagnation_window_passes_, 0 . 0f );
992- wns_history_head_ = 0 ;
993- wns_history_count_ = 0 ;
991+ // Initialize best_wns_ to the most-negative finite float so the very first
992+ // worstSlack() sample always wins the max().
993+ best_wns_ = std::numeric_limits< float >:: lowest () ;
994994 wns_stagnation_tripped_ = false ;
995995 wns_stagnation_iteration_ = 0 ;
996- wns_stagnation_last_wns_ = 0 ;
997996}
998997
999998std::string RepairSetup::wnsStagnationReport (const int iteration) const
@@ -1003,21 +1002,26 @@ std::string RepairSetup::wnsStagnationReport(const int iteration) const
10031002 }
10041003 const int digits = 3 ;
10051004 return fmt::format (
1006- " repair_timing: obviously futile - WNS stuck at {} after {} passes "
1007- " (initial {}). This is probably an exploration run, not a timing "
1008- " closure run. Aborting with best-effort result." ,
1009- delayAsString (wns_stagnation_last_wns_ , digits, sta_),
1005+ " repair_timing: obviously futile - WNS only reached {} after {} passes "
1006+ " (initial {}, improvement {} ). This is probably an exploration run, "
1007+ " not a timing closure run. Aborting with best-effort result." ,
1008+ delayAsString (best_wns_ , digits, sta_),
10101009 wns_stagnation_iteration_,
1011- delayAsString (initial_wns_, digits, sta_));
1010+ delayAsString (initial_wns_, digits, sta_),
1011+ delayAsString (best_wns_ - initial_wns_, digits, sta_));
10121012}
10131013
10141014// Terminate progress if incremental fix rate within an opto interval falls
10151015// below the threshold. Bump up the threshold after each large opto
10161016// interval.
1017- // A second, independent "WNS-stagnation" gate also returns true when the
1018- // best-so-far WNS has failed to improve by a deterministic threshold over
1019- // the last wns_stagnation_window_passes_ passes. This catches
1020- // "obviously futile" designs where TNS keeps twitching but WNS is pinned.
1017+ // A second, independent "WNS-stagnation" gate also returns true when, after
1018+ // the warmup, the best-so-far WNS has failed to improve by a deterministic
1019+ // threshold relative to the initial WNS. That detects designs where WNS
1020+ // essentially never moves from its starting value — the real signature of
1021+ // an "obviously futile" run. Plateaus on legitimate designs (WNS drops by
1022+ // orders of magnitude early, then flat-lines while TNS keeps improving) do
1023+ // not trip this gate, because best_wns_ has already moved far from
1024+ // initial_wns_ by the time the plateau starts.
10211025bool RepairSetup::terminateProgress (const int iteration,
10221026 const float initial_tns,
10231027 float & prev_tns,
@@ -1028,48 +1032,33 @@ bool RepairSetup::terminateProgress(const int iteration,
10281032 const char * phase_name,
10291033 const char phase_marker)
10301034{
1031- // WNS-stagnation gate. Sampled every call so the ring buffer advances at
1032- // the same rate as the caller's inner loop (deterministic).
1033- const sta::Slack wns_now = sta_->worstSlack (max_);
1034- if (!wns_history_.empty ()) {
1035- const size_t window = wns_history_.size ();
1036- wns_history_[wns_history_head_] = wns_now;
1037- wns_history_head_ = (wns_history_head_ + 1 ) % window;
1038- if (wns_history_count_ < window) {
1039- wns_history_count_++;
1040- } else if (iteration > wns_stagnation_warmup_iterations_) {
1041- // Window is full; compare oldest sample (the one we are about to
1042- // overwrite on the next call) to the best (largest, i.e. least
1043- // negative) slack observed in the window.
1044- sta::Slack window_best
1045- = *std::max_element (wns_history_.begin (), wns_history_.end ());
1046- const sta::Slack window_oldest = wns_history_[wns_history_head_];
1047- const float threshold
1048- = std::max (wns_stagnation_abs_tol_,
1049- wns_stagnation_rel_tol_ * std::fabs (initial_wns_));
1050- if (window_best - window_oldest < threshold) {
1051- debugPrint (
1052- logger_,
1053- RSZ ,
1054- " repair_setup" ,
1055- 1 ,
1056- " {}{} Phase: Exiting at iteration {} because WNS best-in-window "
1057- " {} only improved {} over last {} passes (threshold {}) "
1058- " [endpoint {}/{}]" ,
1059- phase_name,
1060- phase_marker,
1061- iteration,
1062- delayAsString (window_best, 3 , sta_),
1063- delayAsString (window_best - window_oldest, 3 , sta_),
1064- window,
1065- delayAsString (threshold, 3 , sta_),
1066- endpt_index,
1067- num_endpts);
1068- wns_stagnation_tripped_ = true ;
1069- wns_stagnation_iteration_ = iteration;
1070- wns_stagnation_last_wns_ = wns_now;
1071- return true ;
1072- }
1035+ // WNS-stagnation gate. Sampled every call so the "best so far" tracker
1036+ // advances at the same rate as the caller's inner loop (deterministic).
1037+ best_wns_ = std::max (best_wns_, sta_->worstSlack (max_));
1038+ if (iteration > wns_stagnation_warmup_iterations_) {
1039+ const float threshold
1040+ = std::max (wns_stagnation_abs_tol_,
1041+ wns_stagnation_rel_tol_ * std::fabs (initial_wns_));
1042+ if (best_wns_ - initial_wns_ < threshold) {
1043+ debugPrint (logger_,
1044+ RSZ ,
1045+ " repair_setup" ,
1046+ 1 ,
1047+ " {}{} Phase: Exiting at iteration {} because WNS best-so-far "
1048+ " {} only improved {} from initial {} (threshold {}) "
1049+ " [endpoint {}/{}]" ,
1050+ phase_name,
1051+ phase_marker,
1052+ iteration,
1053+ delayAsString (best_wns_, 3 , sta_),
1054+ delayAsString (best_wns_ - initial_wns_, 3 , sta_),
1055+ delayAsString (initial_wns_, 3 , sta_),
1056+ delayAsString (threshold, 3 , sta_),
1057+ endpt_index,
1058+ num_endpts);
1059+ wns_stagnation_tripped_ = true ;
1060+ wns_stagnation_iteration_ = iteration;
1061+ return true ;
10731062 }
10741063 }
10751064
0 commit comments