396 QPFunc& fqp,
double frequency0,
double left_limit,
double right_limit,
399 std::vector<RootCandidate>* accepted_roots_out =
nullptr,
400 std::vector<RootCandidate>* rejected_roots_out =
nullptr,
401 bool use_brent =
false) {
402 struct SamplePoIndex {
408 std::vector<RootCandidate> accepted_roots;
409 std::vector<RootCandidate> rejected_roots;
411 if (left_limit >= right_limit) {
412 if (wdiag !=
nullptr) {
415 if (accepted_roots_out !=
nullptr) {
416 *accepted_roots_out = accepted_roots;
418 if (rejected_roots_out !=
nullptr) {
419 *rejected_roots_out = rejected_roots;
430 double center = frequency0;
432 if (gw_sc_iteration == 0) {
434 const double df0 = fqp.deriv(frequency0);
435 if (std::isfinite(f0) && std::isfinite(df0) && std::abs(df0) > 1
e-6) {
436 const double w_lin = frequency0 - f0 / df0;
437 if (std::isfinite(w_lin) && w_lin >= left_limit && w_lin <= right_limit) {
443 center = std::max(left_limit, std::min(right_limit, center));
448 const double max_shell_reach =
449 std::max(center - left_limit, right_limit - center);
450 const Index n_shells =
451 static_cast<Index>(std::ceil(max_shell_reach / shell_width));
453 auto refine_and_store = [&](
double a,
double fa,
double b,
double fb,
463 struct LocalBracket {
467 double f_right = 0.0;
468 double midpoint()
const {
return 0.5 * (left + right); }
471 const Index local_substeps = 12;
472 std::vector<LocalBracket> local_brackets;
473 local_brackets.reserve(
static_cast<std::size_t
>(local_substeps));
476 const double dx = (b - a) /
static_cast<double>(local_substeps);
481 for (
Index i = 1; i <= local_substeps; ++i) {
482 const double x_curr =
483 (i == local_substeps) ? b : (a +
static_cast<double>(i) * dx);
484 const double f_curr =
488 if ((f_prev < 0.0 && f_curr > 0.0) || (f_prev > 0.0 && f_curr < 0.0)) {
489 local_brackets.push_back({x_prev, f_prev, x_curr, f_curr});
493 if (std::abs(f_prev) <= opt.
g_sc_limit && x_prev < x_curr) {
494 local_brackets.push_back({x_prev, f_prev, x_curr, f_curr});
498 if (std::abs(f_curr) <= opt.
g_sc_limit && x_prev < x_curr) {
499 local_brackets.push_back({x_prev, f_prev, x_curr, f_curr});
509 if (!local_brackets.empty()) {
510 auto best_it = local_brackets.begin();
511 double best_dist = std::abs(best_it->midpoint() - center);
513 for (
auto it = local_brackets.begin() + 1; it != local_brackets.end();
515 const double dist = std::abs(it->midpoint() - center);
516 if (dist < best_dist - 1
e-14 ||
517 (std::abs(dist - best_dist) <= 1
e-14 && it->left < best_it->left)) {
524 fa = best_it->f_left;
526 fb = best_it->f_right;
545 accepted_roots.push_back(cand);
547 rejected_roots.push_back(cand);
553 bool left_active =
true;
554 bool right_active =
true;
555 SamplePoIndex left_prev = center_pt;
556 SamplePoIndex right_prev = center_pt;
558 for (
Index shell = 1; shell <= n_shells; ++shell) {
560 bool added_this_shell =
false;
561 const double delta = double(shell) * shell_width;
569 const double omega_left = center - delta;
570 if (omega_left >= left_limit) {
571 SamplePoIndex left_curr{omega_left,
573 added_this_shell =
true;
575 if (left_prev.fval * left_curr.fval < 0.0) {
576 refine_and_store(left_curr.omega, left_curr.fval, left_prev.omega,
577 left_prev.fval, shell);
579 left_prev = left_curr;
586 const double omega_right = center + delta;
587 if (omega_right <= right_limit) {
588 SamplePoIndex right_curr{omega_right,
590 added_this_shell =
true;
592 if (right_prev.fval * right_curr.fval < 0.0) {
593 refine_and_store(right_prev.omega, right_prev.fval, right_curr.omega,
594 right_curr.fval, shell);
596 right_prev = right_curr;
598 right_active =
false;
602 if (!added_this_shell && !left_active && !right_active) {
607 if (left_prev.omega > left_limit + 1
e-12) {
608 SamplePoIndex left_end{left_limit, fqp.value(left_limit,
EvalStage::Scan)};
609 if (left_end.fval * left_prev.fval < 0.0) {
610 refine_and_store(left_end.omega, left_end.fval, left_prev.omega,
615 if (right_prev.omega < right_limit - 1
e-12) {
616 SamplePoIndex right_end{right_limit,
618 if (right_prev.fval * right_end.fval < 0.0) {
619 refine_and_store(right_prev.omega, right_prev.fval, right_end.omega,
630 if (!accepted_roots.empty()) {
632 std::max_element(accepted_roots.begin(), accepted_roots.end(),
634 return ScoreRoot(a) < ScoreRoot(b);
638 std::llround(std::abs(best->omega - center) / shell_width));
640 if (wdiag !=
nullptr) {
643 if (accepted_roots_out !=
nullptr) {
644 *accepted_roots_out = accepted_roots;
646 if (rejected_roots_out !=
nullptr) {
647 *rejected_roots_out = rejected_roots;
652 if (!rejected_roots.empty()) {
654 std::max_element(rejected_roots.begin(), rejected_roots.end(),
656 return ScoreRoot(a) < ScoreRoot(b);
660 std::llround(std::abs(least_bad->omega - center) / shell_width));
662 if (wdiag !=
nullptr) {
665 if (accepted_roots_out !=
nullptr) {
666 *accepted_roots_out = accepted_roots;
668 if (rejected_roots_out !=
nullptr) {
669 *rejected_roots_out = rejected_roots;
671 return least_bad->omega;
674 if (wdiag !=
nullptr) {
677 if (accepted_roots_out !=
nullptr) {
678 *accepted_roots_out = accepted_roots;
680 if (rejected_roots_out !=
nullptr) {
681 *rejected_roots_out = rejected_roots;