@@ -516,40 +516,110 @@ template <bool WrtLog = false, typename T_y, typename T_a, typename T_w,
516516inline auto wiener5_grad_w (const T_y& y, const T_a& a, const T_v& v,
517517 const T_w& w, const T_sv& sv,
518518 T_err log_err = log(1e-12 )) noexcept {
519- const auto two_log_a = 2.0 * log (a) ;
520- const auto log_y_asq = log (y) - two_log_a;
521- const auto log_error_term = wiener5_compute_log_error_term (y, a, v, w, sv );
522- const auto one_m_w = 1.0 - w;
519+ using ret_t = return_type_t <T_y, T_a, T_w, T_v, T_sv, T_err> ;
520+
521+ const auto y_asq = y / square (a );
522+ const auto q = 1.0 - w;
523523 const auto sv_sqr = square (sv);
524524 const auto one_plus_svsqr_y = 1.0 + sv_sqr * y;
525- const auto density_part_one
526- = (v * a + sv_sqr * square (a) * one_m_w) / one_plus_svsqr_y;
527- const auto log_error = (log_err - log_error_term);
528525
529- const auto n_terms_small_t
530- = wiener5_n_terms_small_t <GradientCalc::OFF , GradientCalc::ON >(y, a, w,
531- log_error);
532- const auto n_terms_large_t
533- = wiener5_gradient_large_reaction_time_terms<GradientCalc::ON >(y, a, w,
534- log_error);
535- auto wiener_res = wiener5_log_sum_exp<GradientCalc::OFF , GradientCalc::ON >(
536- y, a, w, n_terms_small_t , n_terms_large_t );
537- auto && result = wiener_res.first ;
538- auto && newsign = wiener_res.second ;
539- const auto log_density = wiener5_density<GradientCalc::OFF >(
540- y, a, v, w, sv, log_err - log (fabs (density_part_one)));
541- if (2.0 * n_terms_small_t < n_terms_large_t ) {
542- auto ans = -(density_part_one
543- - newsign
544- * exp (result - (log_density - log_error_term)
545- - 2.5 * log_y_asq - 0.5 * LOG_TWO - 0.5 * LOG_PI ));
546- return WrtLog ? ans * exp (log_density) : ans;
526+ const auto log_error_term = wiener5_compute_log_error_term (y, a, v, w, sv);
527+ const auto log_error = log_err - log_error_term;
528+
529+ // d/dw of
530+ //
531+ // -2 log(a)
532+ // - 0.5 log(1 + sv^2 y)
533+ // + [-v^2 y + 2 a v (1-w) + a^2 (1-w)^2 sv^2]
534+ // / [2 (1 + sv^2 y)].
535+ const auto pref_grad_w
536+ = (-a * v - square (a) * sv_sqr * q) / one_plus_svsqr_y;
537+
538+ // Use the density branch decision. The derivative is the derivative of
539+ // the same scalar value, so do not let the w-gradient path switch to a
540+ // different underconverged representation.
541+ const auto n_small_density
542+ = wiener5_n_terms_small_t <true , GradientCalc::OFF >(y, a, w, log_error);
543+ const auto n_large_density
544+ = wiener5_density_large_reaction_time_terms (y, a, w, log_error);
545+
546+ const auto n_small_grad
547+ = wiener5_n_terms_small_t <false , GradientCalc::ON >(y, a, w, log_error);
548+ const auto n_large_grad
549+ = wiener5_gradient_large_reaction_time_terms<GradientCalc::ON >(
550+ y, a, w, log_error);
551+
552+ const int n_small = static_cast <int >(
553+ fmax (value_of_rec (n_small_density), value_of_rec (n_small_grad)));
554+ const int n_large = static_cast <int >(
555+ fmax (value_of_rec (n_large_density), value_of_rec (n_large_grad)));
556+
557+ ret_t series_grad_w = 0.0 ;
558+
559+ if (2.0 * n_small_density <= n_large_density) {
560+ // Small-time representation.
561+ //
562+ // R_s = sum_{k=-K}^{K} z_k exp(-z_k^2 / (2 t*)),
563+ // z_k = 1 - w + 2k.
564+ //
565+ // dR_s/dw = sum_k (z_k^2 / t* - 1)
566+ // exp(-z_k^2 / (2 t*)).
567+ ret_t max_log = NEGATIVE_INFTY ;
568+
569+ for (int k = -n_small; k <= n_small; ++k) {
570+ const double kd = static_cast <double >(k);
571+ const auto z = q + 2.0 * kd;
572+ const auto log_e = -square (z) / (2.0 * y_asq);
573+ max_log = fmax (max_log, log_e);
574+ }
575+
576+ ret_t raw = 0.0 ;
577+ ret_t draw_dw = 0.0 ;
578+
579+ for (int k = -n_small; k <= n_small; ++k) {
580+ const double kd = static_cast <double >(k);
581+ const auto z = q + 2.0 * kd;
582+ const auto e = exp (-square (z) / (2.0 * y_asq) - max_log);
583+
584+ raw += z * e;
585+ draw_dw += (square (z) / y_asq - 1.0 ) * e;
586+ }
587+
588+ series_grad_w = draw_dw / raw;
547589 } else {
548- auto ans = -(
549- density_part_one
550- + newsign
551- * exp (result - (log_density - log_error_term) + 2.0 * LOG_PI ));
552- return WrtLog ? ans * exp (log_density) : ans;
590+ // Large-time representation in the same upper-bound coordinate used
591+ // by the density code: q = 1 - w.
592+ //
593+ // R_l = sum_{k=1}^{K}
594+ // k sin(k pi q)
595+ // exp(-(k^2 - 1) pi^2 t* / 2).
596+ //
597+ // dR_l/dw = -sum_{k=1}^{K}
598+ // k^2 pi cos(k pi q)
599+ // exp(-(k^2 - 1) pi^2 t* / 2).
600+ ret_t raw = 0.0 ;
601+ ret_t draw_dw = 0.0 ;
602+
603+ const auto half_pi2_y = 0.5 * square (pi ()) * y_asq;
604+
605+ for (int k = 1 ; k <= n_large; ++k) {
606+ const double kd = static_cast <double >(k);
607+ const auto exp_term = exp (-(square (kd) - 1.0 ) * half_pi2_y);
608+ const auto angle = kd * pi () * q;
609+
610+ raw += kd * sin (angle) * exp_term;
611+ draw_dw += -square (kd) * pi () * cos (angle) * exp_term;
612+ }
613+
614+ series_grad_w = draw_dw / raw;
615+ }
616+
617+ const auto ans = pref_grad_w + series_grad_w;
618+
619+ if constexpr (WrtLog) {
620+ return ans * wiener5_density<true >(y, a, v, w, sv, log_err);
621+ } else {
622+ return ans;
553623 }
554624}
555625
0 commit comments