419 QPFunc& fqp,
double frequency0,
double left_limit,
double right_limit,
422 std::vector<RootCandidate>* accepted_roots_out =
nullptr,
423 std::vector<RootCandidate>* rejected_roots_out =
nullptr,
424 bool use_brent =
false) {
425 struct SamplePoIndex {
431 std::vector<RootCandidate> accepted_roots;
432 std::vector<RootCandidate> rejected_roots;
434 if (left_limit >= right_limit) {
435 if (wdiag !=
nullptr) {
438 if (accepted_roots_out !=
nullptr) {
439 *accepted_roots_out = accepted_roots;
441 if (rejected_roots_out !=
nullptr) {
442 *rejected_roots_out = rejected_roots;
453 double center = frequency0;
455 if (gw_sc_iteration == 0) {
457 const double df0 = fqp.deriv(frequency0);
458 if (std::isfinite(f0) && std::isfinite(df0) && std::abs(df0) > 1
e-6) {
459 const double w_lin = frequency0 - f0 / df0;
460 if (std::isfinite(w_lin) && w_lin >= left_limit && w_lin <= right_limit) {
466 center = std::max(left_limit, std::min(right_limit, center));
471 const double max_shell_reach =
472 std::max(center - left_limit, right_limit - center);
473 const Index n_shells =
474 static_cast<Index>(std::ceil(max_shell_reach / shell_width));
476 auto refine_and_store = [&](
double a,
double fa,
double b,
double fb,
486 struct LocalBracket {
490 double f_right = 0.0;
491 double midpoint()
const {
return 0.5 * (left + right); }
494 const Index local_substeps = 12;
495 std::vector<LocalBracket> local_brackets;
496 local_brackets.reserve(
static_cast<std::size_t
>(local_substeps));
499 const double dx = (b - a) /
static_cast<double>(local_substeps);
504 for (
Index i = 1; i <= local_substeps; ++i) {
505 const double x_curr =
506 (i == local_substeps) ? b : (a +
static_cast<double>(i) * dx);
507 const double f_curr =
511 if ((f_prev < 0.0 && f_curr > 0.0) || (f_prev > 0.0 && f_curr < 0.0)) {
512 local_brackets.push_back({x_prev, f_prev, x_curr, f_curr});
516 if (std::abs(f_prev) <= opt.
g_sc_limit && x_prev < x_curr) {
517 local_brackets.push_back({x_prev, f_prev, x_curr, f_curr});
521 if (std::abs(f_curr) <= opt.
g_sc_limit && x_prev < x_curr) {
522 local_brackets.push_back({x_prev, f_prev, x_curr, f_curr});
532 if (!local_brackets.empty()) {
533 auto best_it = local_brackets.begin();
534 double best_dist = std::abs(best_it->midpoint() - center);
536 for (
auto it = local_brackets.begin() + 1; it != local_brackets.end();
538 const double dist = std::abs(it->midpoint() - center);
539 if (dist < best_dist - 1
e-14 ||
540 (std::abs(dist - best_dist) <= 1
e-14 && it->left < best_it->left)) {
547 fa = best_it->f_left;
549 fb = best_it->f_right;
568 accepted_roots.push_back(cand);
570 rejected_roots.push_back(cand);
576 bool left_active =
true;
577 bool right_active =
true;
578 SamplePoIndex left_prev = center_pt;
579 SamplePoIndex right_prev = center_pt;
581 for (
Index shell = 1; shell <= n_shells; ++shell) {
583 bool added_this_shell =
false;
584 const double delta = double(shell) * shell_width;
592 const double omega_left = center - delta;
593 if (omega_left >= left_limit) {
594 SamplePoIndex left_curr{omega_left,
596 added_this_shell =
true;
598 if (left_prev.fval * left_curr.fval < 0.0) {
599 refine_and_store(left_curr.omega, left_curr.fval, left_prev.omega,
600 left_prev.fval, shell);
602 left_prev = left_curr;
609 const double omega_right = center + delta;
610 if (omega_right <= right_limit) {
611 SamplePoIndex right_curr{omega_right,
613 added_this_shell =
true;
615 if (right_prev.fval * right_curr.fval < 0.0) {
616 refine_and_store(right_prev.omega, right_prev.fval, right_curr.omega,
617 right_curr.fval, shell);
619 right_prev = right_curr;
621 right_active =
false;
625 if (!added_this_shell && !left_active && !right_active) {
630 if (left_prev.omega > left_limit + 1
e-12) {
631 SamplePoIndex left_end{left_limit, fqp.value(left_limit,
EvalStage::Scan)};
632 if (left_end.fval * left_prev.fval < 0.0) {
633 refine_and_store(left_end.omega, left_end.fval, left_prev.omega,
638 if (right_prev.omega < right_limit - 1
e-12) {
639 SamplePoIndex right_end{right_limit,
641 if (right_prev.fval * right_end.fval < 0.0) {
642 refine_and_store(right_prev.omega, right_prev.fval, right_end.omega,
653 if (!accepted_roots.empty()) {
655 std::max_element(accepted_roots.begin(), accepted_roots.end(),
657 return ScoreRoot(a) < ScoreRoot(b);
661 std::llround(std::abs(best->omega - center) / shell_width));
663 if (wdiag !=
nullptr) {
666 if (accepted_roots_out !=
nullptr) {
667 *accepted_roots_out = accepted_roots;
669 if (rejected_roots_out !=
nullptr) {
670 *rejected_roots_out = rejected_roots;
675 if (!rejected_roots.empty()) {
677 std::max_element(rejected_roots.begin(), rejected_roots.end(),
679 return ScoreRoot(a) < ScoreRoot(b);
683 std::llround(std::abs(least_bad->omega - center) / shell_width));
685 if (wdiag !=
nullptr) {
688 if (accepted_roots_out !=
nullptr) {
689 *accepted_roots_out = accepted_roots;
691 if (rejected_roots_out !=
nullptr) {
692 *rejected_roots_out = rejected_roots;
694 return least_bad->omega;
697 if (wdiag !=
nullptr) {
700 if (accepted_roots_out !=
nullptr) {
701 *accepted_roots_out = accepted_roots;
703 if (rejected_roots_out !=
nullptr) {
704 *rejected_roots_out = rejected_roots;