Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
CladDerivator.h
Go to the documentation of this file.
1/// \file CladDerivator.h
2///
3/// \brief The file is a bridge between ROOT and clad automatic differentiation
4/// plugin.
5///
6/// \author Vassil Vassilev <vvasilev@cern.ch>
7///
8/// \date July, 2018
9
10/*************************************************************************
11 * Copyright (C) 1995-2018, Rene Brun and Fons Rademakers. *
12 * All rights reserved. *
13 * *
14 * For the licensing terms see $ROOTSYS/LICENSE. *
15 * For the list of contributors see $ROOTSYS/README/CREDITS. *
16 *************************************************************************/
17
18#ifndef CLAD_DERIVATOR
19#define CLAD_DERIVATOR
20
21#ifndef __CLING__
22#error "This file must not be included by compiled programs."
23#endif //__CLING__
24
25#include <plugins/include/clad/Differentiator/Differentiator.h>
26#include "TMath.h"
30
31#include <stdexcept>
32
33namespace clad {
34namespace custom_derivatives {
35namespace TMath {
36template <typename T>
38{
39 return {::TMath::Abs(x), ((x < 0) ? -1 : 1) * d_x};
40}
41
42template <typename T>
44{
45 return {::TMath::ACos(x), (-1. / ::TMath::Sqrt(1 - x * x)) * d_x};
46}
47
48template <typename T>
50{
51 return {::TMath::ACosH(x), (1. / ::TMath::Sqrt(x * x - 1)) * d_x};
52}
53
54template <typename T>
56{
57 return {::TMath::ASin(x), (1. / ::TMath::Sqrt(1 - x * x)) * d_x};
58}
59
60template <typename T>
62{
63 return {::TMath::ASinH(x), (1. / ::TMath::Sqrt(x * x + 1)) * d_x};
64}
65
66template <typename T>
68{
69 return {::TMath::ATan(x), (1. / (x * x + 1)) * d_x};
70}
71
72template <typename T>
74{
75 return {::TMath::ATanH(x), (1. / (1 - x * x)) * d_x};
76}
77
78template <typename T>
83
84template <typename T>
89
90template <typename T>
95
96template <typename T>
98{
99 return {::TMath::Erfc(x), -Erf_pushforward(x, d_x).pushforward};
100}
101
102template <typename T>
104{
105 return {::TMath::LnGamma(z), ::clad::custom_derivatives::std::clad_digamma(z) * d_z};
106}
107
108template <typename T>
113
114template <typename T>
119
120template <typename T, typename U>
121void Hypot_pullback(T x, T y, U p, T *d_x, T *d_y)
122{
123 T h = ::TMath::Hypot(x, y);
124 *d_x += x / h * p;
125 *d_y += y / h * p;
126}
127
128template <typename T>
130{
131 return {::TMath::Log(x), (1. / x) * d_x};
132}
133
134template <typename T>
136{
137 return {::TMath::Log10(x), (1.0 / (x * ::TMath::Ln10())) * d_x};
138}
139
140template <typename T>
142{
143 return {::TMath::Log2(x), (1.0 / (x * ::TMath::Log(2.0))) * d_x};
144}
145
146template <typename T, typename U>
148{
149 T pushforward = y * ::TMath::Power(x, y - 1) * d_x;
150 if (d_y) {
152 }
153 return {::TMath::Power(x, y), pushforward};
154}
155
156template <typename T, typename U, typename V>
157void Power_pullback(T x, U y, V p, T *d_x, U *d_y)
158{
159 auto t = pow_pushforward(x, y, 1, 0);
160 *d_x += t.pushforward * p;
161 t = pow_pushforward(x, y, 0, 1);
162 *d_y += t.pushforward * p;
163}
164
165template <typename T>
170
171template <typename T>
176
177template <typename T>
179{
180 return {::TMath::Sq(x), 2 * x * d_x};
181}
182
183template <typename T>
188
189template <typename T>
194
195template <typename T>
200
201} // namespace TMath
202
203namespace ROOT {
204namespace Math {
205
206/// Evaluate the polynomial c[0] + c[1]*x + c[2]*x^2 + ... in Horner form,
207/// matching the evaluation order of the CERNLIB Landau routines below.
208template <unsigned N>
209double horner(const double (&c)[N], double x)
210{
211 double r = c[N - 1];
212 for (unsigned i = N - 1; i > 0; --i)
213 r = c[i - 1] + r * x;
214 return r;
215}
216
217/// Derivative of horner(c, x) with respect to x.
218template <unsigned N>
219double horner_deriv(const double (&c)[N], double x)
220{
221 double r = (N - 1) * c[N - 1];
222 for (unsigned i = N - 1; i > 1; --i)
223 r = (i - 1) * c[i - 1] + r * x;
224 return r;
225}
226
227/// Derivative of the rational function horner(p, x) / horner(q, x) with
228/// respect to x.
229template <unsigned N, unsigned M>
230double rational_deriv(const double (&p)[N], const double (&q)[M], double x)
231{
232 const double den = horner(q, x);
233 return (horner_deriv(p, x) - horner(p, x) / den * horner_deriv(q, x)) / den;
234}
235
236/// First derivative of the standardized Landau density
237/// p(v) = ROOT::Math::landau_pdf(v) with respect to v, obtained by
238/// differentiating each branch of the CERNLIB DENLAN rational approximation.
239/// The branch structure and the coefficient tables mirror landau_pdf() in
240/// PdfFuncMathCore.cxx. Used by landau_pdf_pullback() and by the
241/// second-derivative helpers further down.
242inline double landau_pdf_dv(double v)
243{
244 // clang-format off
245 static constexpr double p1[5] = {0.4259894875,-0.1249762550, 0.03984243700, -0.006298287635, 0.001511162253};
246 static constexpr double q1[5] = {1.0 ,-0.3388260629, 0.09594393323, -0.01608042283, 0.003778942063};
247
248 static constexpr double p2[5] = {0.1788541609, 0.1173957403, 0.01488850518, -0.001394989411, 0.0001283617211};
249 static constexpr double q2[5] = {1.0 , 0.7428795082, 0.3153932961, 0.06694219548, 0.008790609714};
250
251 static constexpr double p3[5] = {0.1788544503, 0.09359161662,0.006325387654, 0.00006611667319,-0.000002031049101};
252 static constexpr double q3[5] = {1.0 , 0.6097809921, 0.2560616665, 0.04746722384, 0.006957301675};
253
254 static constexpr double p4[5] = {0.9874054407, 118.6723273, 849.2794360, -743.7792444, 427.0262186};
255 static constexpr double q4[5] = {1.0 , 106.8615961, 337.6496214, 2016.712389, 1597.063511};
256
257 static constexpr double p5[5] = {1.003675074, 167.5702434, 4789.711289, 21217.86767, -22324.94910};
258 static constexpr double q5[5] = {1.0 , 156.9424537, 3745.310488, 9834.698876, 66924.28357};
259
260 static constexpr double p6[5] = {1.000827619, 664.9143136, 62972.92665, 475554.6998, -5743609.109};
261 static constexpr double q6[5] = {1.0 , 651.4101098, 56974.73333, 165917.4725, -2815759.939};
262
263 static constexpr double a1[3] = {0.04166666667,-0.01996527778, 0.02709538966};
264
265 static constexpr double a2[2] = {-1.845568670,-4.284640743};
266 // clang-format on
267 if (v < -5.5) {
268 const double u = ::std::exp(v + 1.);
269 if (u < 1e-10)
270 return 0.;
271 // p = C * exp(-1/u) / sqrt(u) * A(u) with u = exp(v + 1), so
272 // dp/dv = p * (1/u - 1/2) + C * exp(-1/u) * sqrt(u) * A'(u)
273 const double eu = 0.3989422803 * ::std::exp(-1. / u);
274 const double val = eu / ::std::sqrt(u) * (1 + (a1[0] + (a1[1] + a1[2] * u) * u) * u);
275 const double dA = a1[0] + (2 * a1[1] + 3 * a1[2] * u) * u;
276 return val * (1. / u - 0.5) + eu * ::std::sqrt(u) * dA;
277 } else if (v < -1) {
278 // p = exp(-u) * sqrt(u) * P(v)/Q(v) with u = exp(-v - 1); v acts both
279 // directly and through u, and d(exp(-u) * sqrt(u))/dv comes out as
280 // exp(-u) * sqrt(u) * (u - 1/2)
281 const double u = ::std::exp(-v - 1);
282 const double eu = ::std::exp(-u) * ::std::sqrt(u);
283 const double q = horner(q1, v);
284 const double r = horner(p1, v) / q;
285 return eu * ((u - 0.5) * r + (horner_deriv(p1, v) - r * horner_deriv(q1, v)) / q);
286 } else if (v < 1) {
287 return rational_deriv(p2, q2, v);
288 } else if (v < 5) {
289 return rational_deriv(p3, q3, v);
290 } else if (v < 12) {
291 // p = u^2 * P(u)/Q(u) with u = 1/v, du/dv = -u^2
292 const double u = 1 / v;
293 return -u * u * (2 * u * horner(p4, u) / horner(q4, u) + u * u * rational_deriv(p4, q4, u));
294 } else if (v < 50) {
295 const double u = 1 / v;
296 return -u * u * (2 * u * horner(p5, u) / horner(q5, u) + u * u * rational_deriv(p5, q5, u));
297 } else if (v < 300) {
298 const double u = 1 / v;
299 return -u * u * (2 * u * horner(p6, u) / horner(q6, u) + u * u * rational_deriv(p6, q6, u));
300 } else {
301 // p = u^2 * B(u) with u = 1/w, w = v - v*log(v)/(v + 1)
302 const double lv = ::std::log(v);
303 const double u = 1 / (v - v * lv / (v + 1));
304 const double dw = 1 - lv / (v + 1) - 1 / (v + 1) + v * lv / ((v + 1) * (v + 1));
305 const double dB = a2[0] + 2 * a2[1] * u;
306 return -u * u * dw * (2 * u * (1 + (a2[0] + a2[1] * u) * u) + u * u * dB);
307 }
308}
309
310inline void landau_pdf_pullback(double x, double xi, double x0, double d_out, double *d_x, double *d_xi, double *d_x0)
311{
312 if (xi <= 0) {
313 return;
314 }
315 // The pdf is p(v) / xi with v = (x - x0) / xi.
316 const double v = (x - x0) / xi;
317 const double p = ::ROOT::Math::landau_pdf(v);
318 const double dp = landau_pdf_dv(v);
319 *d_x += d_out * dp / (xi * xi);
320 *d_x0 += -d_out * dp / (xi * xi);
321 *d_xi += -d_out * (v * dp + p) / (xi * xi);
322}
323
324/// Derivative with respect to v of the CERNLIB DISLAN rational approximation
325/// of the standardized Landau cumulative distribution, obtained by
326/// differentiating each branch of landau_cdf() in ProbFuncMathCore.cxx, whose
327/// branch structure and coefficient tables this function mirrors. Since
328/// DISLAN is an approximation of its own, this is not identical to
329/// landau_pdf() (they are consistent to about 1e-7 relative).
330inline double landau_cdf_dv(double v)
331{
332 // clang-format off
333 static constexpr double p1[5] = {0.2514091491e+0,-0.6250580444e-1, 0.1458381230e-1,-0.2108817737e-2, 0.7411247290e-3};
334 static constexpr double q1[5] = {1.0 ,-0.5571175625e-2, 0.6225310236e-1,-0.3137378427e-2, 0.1931496439e-2};
335
336 static constexpr double p2[4] = {0.2868328584e+0, 0.3564363231e+0, 0.1523518695e+0, 0.2251304883e-1};
337 static constexpr double q2[4] = {1.0 , 0.6191136137e+0, 0.1720721448e+0, 0.2278594771e-1};
338
339 static constexpr double p3[4] = {0.2868329066e+0, 0.3003828436e+0, 0.9950951941e-1, 0.8733827185e-2};
340 static constexpr double q3[4] = {1.0 , 0.4237190502e+0, 0.1095631512e+0, 0.8693851567e-2};
341
342 static constexpr double p4[4] = {0.1000351630e+1, 0.4503592498e+1, 0.1085883880e+2, 0.7536052269e+1};
343 static constexpr double q4[4] = {1.0 , 0.5539969678e+1, 0.1933581111e+2, 0.2721321508e+2};
344
345 static constexpr double p5[4] = {0.1000006517e+1, 0.4909414111e+2, 0.8505544753e+2, 0.1532153455e+3};
346 static constexpr double q5[4] = {1.0 , 0.5009928881e+2, 0.1399819104e+3, 0.4200002909e+3};
347
348 static constexpr double p6[4] = {0.1000000983e+1, 0.1329868456e+3, 0.9162149244e+3,-0.9605054274e+3};
349 static constexpr double q6[4] = {1.0 , 0.1339887843e+3, 0.1055990413e+4, 0.5532224619e+3};
350
351 static constexpr double a1[4] = {0 ,-0.4583333333e+0, 0.6675347222e+0,-0.1641741416e+1};
352 static constexpr double a2[4] = {0 , 1.0 ,-0.4227843351e+0,-0.2043403138e+1};
353 // clang-format on
354 if (v < -5.5) {
355 // F = C * exp(-1/u) * sqrt(u) * A(u) with u = exp(v + 1), so
356 // dF/dv = F * (1/u + 1/2) + C * exp(-1/u) * sqrt(u) * u * A'(u)
357 const double u = ::std::exp(v + 1);
358 const double eu = 0.3989422803 * ::std::exp(-1. / u) * ::std::sqrt(u);
359 const double val = eu * (1 + (a1[1] + (a1[2] + a1[3] * u) * u) * u);
360 const double dA = a1[1] + (2 * a1[2] + 3 * a1[3] * u) * u;
361 return val * (1. / u + 0.5) + eu * u * dA;
362 } else if (v < -1) {
363 // F = exp(-u) / sqrt(u) * P(v)/Q(v) with u = exp(-v - 1); v acts both
364 // directly and through u, and d(exp(-u) / sqrt(u))/dv comes out as
365 // exp(-u) / sqrt(u) * (u + 1/2)
366 const double u = ::std::exp(-v - 1);
367 const double eu = ::std::exp(-u) / ::std::sqrt(u);
368 const double q = horner(q1, v);
369 const double r = horner(p1, v) / q;
370 return eu * ((u + 0.5) * r + (horner_deriv(p1, v) - r * horner_deriv(q1, v)) / q);
371 } else if (v < 1) {
372 return rational_deriv(p2, q2, v);
373 } else if (v < 4) {
374 return rational_deriv(p3, q3, v);
375 } else if (v < 12) {
376 // F = P(u)/Q(u) with u = 1/v, du/dv = -u^2
377 const double u = 1. / v;
378 return -u * u * rational_deriv(p4, q4, u);
379 } else if (v < 50) {
380 const double u = 1. / v;
381 return -u * u * rational_deriv(p5, q5, u);
382 } else if (v < 300) {
383 const double u = 1. / v;
384 return -u * u * rational_deriv(p6, q6, u);
385 } else {
386 // F = 1 - G(u) with u = 1/w, w = v - v*log(v)/(v + 1)
387 const double lv = ::std::log(v);
388 const double u = 1. / (v - v * lv / (v + 1));
389 const double dw = 1 - lv / (v + 1) - 1 / (v + 1) + v * lv / ((v + 1) * (v + 1));
390 return horner_deriv(a2, u) * u * u * dw;
391 }
392}
393
394inline void landau_cdf_pullback(double x, double xi, double x0, double d_out, double *d_x, double *d_xi, double *d_x0)
395{
396 // The cdf is a function of v = (x - x0) / xi alone.
397 const double v = (x - x0) / xi;
398 const double dcdf = landau_cdf_dv(v);
399 *d_x += d_out * dcdf / xi;
400 *d_x0 += -d_out * dcdf / xi;
401 *d_xi += -d_out * dcdf * v / xi;
402}
403
404inline void inc_gamma_c_pullback(double a, double x, double _d_y, double *_d_a, double *_d_x);
405
406inline void inc_gamma_pullback(double a, double x, double _d_y, double *_d_a, double *_d_x)
407{
408 // Synced with SpecFuncCephes.h
409 constexpr double kMACHEP = 1.11022302462515654042363166809e-16;
410 constexpr double kMAXLOG = 709.782712893383973096206318587;
411
412 double _d_ans = 0, _d_ax = 0, _d_c = 0, _d_r = 0;
413 double _t1;
414 double _t2;
415 double _t3;
416 double _t4;
417 double _t5;
418 clad::tape<double> _t7 = {};
419 clad::tape<double> _t8 = {};
420 clad::tape<double> _t9 = {};
421 double ans, ax, c, r;
422 if (a <= 0)
423 return;
424 if (x <= 0)
425 return;
426 if ((x > 1.) && (x > a)) {
427 double _r0 = 0;
428 double _r1 = 0;
430 *_d_a += _r0;
431 *_d_x += _r1;
432 return;
433 }
434 _t1 = ::std::log(x);
435 ax = a * _t1 - x - ::std::lgamma(a);
436 if (ax < -kMAXLOG) {
437 *_d_x += (a * _d_ax / x) - _d_ax;
438 *_d_a += _d_ax * (_t1 - ::clad::custom_derivatives::std::clad_digamma(
439 a)); // numerical_diff::forward_central_difference(::std::lgamma, a, 0, 0, a);
440 _d_ax = 0.;
441 return;
442 }
443 _t2 = ax;
444 ax = ::std::exp(ax);
445 _t3 = r;
446 r = a;
447 _t4 = c;
448 c = 1.;
449 _t5 = ans;
450 ans = 1.;
451 unsigned long _t6 = 0;
452 do {
453 _t6++;
454 clad::push(_t7, r);
455 r += 1.;
456 clad::push(_t8, c);
457 c *= x / r;
458 clad::push(_t9, ans);
459 ans += c;
460 } while (c / ans > kMACHEP);
461 {
462 _d_ans += _d_y / a * ax;
463 _d_ax += ans * _d_y / a;
464 double _r6 = _d_y * -(ans * ax / (a * a));
465 *_d_a += _r6;
466 }
467 do {
468 {
469 {
470 ans = clad::pop(_t9);
471 double _r_d7 = _d_ans;
472 _d_c += _r_d7;
473 }
474 {
475 c = clad::pop(_t8);
476 double _r_d6 = _d_c;
477 _d_c -= _r_d6;
478 _d_c += _r_d6 * x / r;
479 *_d_x += c * _r_d6 / r;
480 double _r5 = c * _r_d6 * -(x / (r * r));
481 _d_r += _r5;
482 }
483 {
484 r = clad::pop(_t7);
485 double _r_d5 = _d_r;
486 }
487 }
488 _t6--;
489 } while (_t6);
490 {
491 ans = _t5;
492 double _r_d4 = _d_ans;
493 _d_ans -= _r_d4;
494 }
495 {
496 c = _t4;
497 double _r_d3 = _d_c;
498 _d_c -= _r_d3;
499 }
500 {
501 r = _t3;
502 double _r_d2 = _d_r;
503 _d_r -= _r_d2;
504 *_d_a += _r_d2;
505 }
506 {
507 ax = _t2;
508 double _r_d1 = _d_ax;
509 _d_ax -= _r_d1;
510 double _r4 = 0;
511 _r4 += _r_d1 * ::std::exp(ax);
512 _d_ax += _r4;
513 }
514 {
515 *_d_x += (a * _d_ax / x) - _d_ax;
516 *_d_a += _d_ax * (_t1 - ::clad::custom_derivatives::std::clad_digamma(
517 a)); // numerical_diff::forward_central_difference(::std::lgamma, a, 0, 0, a);
518 _d_ax = 0.;
519 }
520}
521
522inline void inc_gamma_c_pullback(double a, double x, double _d_y, double *_d_a, double *_d_x)
523{
524 // Synced with SpecFuncCephes.h
525 constexpr double kMACHEP = 1.11022302462515654042363166809e-16;
526 constexpr double kMAXLOG = 709.782712893383973096206318587;
527 constexpr double kBig = 4.503599627370496e15;
528 constexpr double kBiginv = 2.22044604925031308085e-16;
529
530 double _d_ans = 0, _d_ax = 0, _d_c = 0, _d_yc = 0, _d_r = 0, _d_y0 = 0, _d_z = 0;
531 double _d_pk = 0, _d_pkm1 = 0, _d_pkm2 = 0, _d_qk = 0, _d_qkm1 = 0, _d_qkm2 = 0;
532 double _t1;
533 double _t2;
534 double _t3;
535 double _t4;
536 double _t5;
537 double _t6;
538 double _t7;
539 double _t8;
540 double _t9;
541 double _t10;
542 unsigned long _t11;
543 clad::tape<double> _t12 = {};
544 clad::tape<double> _t13 = {};
545 clad::tape<double> _t14 = {};
546 clad::tape<double> _t15 = {};
547 clad::tape<double> _t16 = {};
548 clad::tape<double> _t17 = {};
549 clad::tape<double> _t19 = {};
550 clad::tape<double> _t20 = {};
551 clad::tape<double> _t22 = {};
552 clad::tape<double> _t24 = {};
553 clad::tape<double> _t25 = {};
554 clad::tape<double> _t26 = {};
555 clad::tape<double> _t27 = {};
556 clad::tape<bool> _t29 = {};
557 clad::tape<double> _t30 = {};
558 clad::tape<double> _t31 = {};
559 clad::tape<double> _t32 = {};
560 clad::tape<double> _t33 = {};
561 double ans, ax, c, yc, r, t, y, z;
562 double pk, pkm1, pkm2, qk, qkm1, qkm2;
563 if (a <= 0)
564 return;
565 if (x <= 0)
566 return;
567 if ((x < 1.) || (x < a)) {
568 double _r0 = 0;
569 double _r1 = 0;
571 *_d_a += _r0;
572 *_d_x += _r1;
573 return;
574 }
575 _t1 = ::std::log(x);
576 ax = a * _t1 - x - ::std::lgamma(a);
577 if (ax < -kMAXLOG) {
578 *_d_x += a * _d_ax / x - _d_ax;
579 *_d_a += _d_ax * (_t1 - ::clad::custom_derivatives::std::clad_digamma(
580 a)); // numerical_diff::forward_central_difference(::std::lgamma, a, 0, 0, a);
581 _d_ax = 0.;
582 return;
583 }
584 _t2 = ax;
585 ax = ::std::exp(ax);
586 _t3 = y;
587 y = 1. - a;
588 _t4 = z;
589 z = x + y + 1.;
590 _t5 = c;
591 c = 0.;
592 _t6 = pkm2;
593 pkm2 = 1.;
594 _t7 = qkm2;
595 qkm2 = x;
596 _t8 = pkm1;
597 pkm1 = x + 1.;
598 _t9 = qkm1;
599 qkm1 = z * x;
600 _t10 = ans;
601 ans = pkm1 / qkm1;
602 _t11 = 0;
603 do {
604 _t11++;
605 clad::push(_t12, c);
606 c += 1.;
607 clad::push(_t13, y);
608 y += 1.;
609 clad::push(_t14, z);
610 z += 2.;
611 clad::push(_t15, yc);
612 yc = y * c;
613 clad::push(_t16, pk);
614 pk = pkm1 * z - pkm2 * yc;
615 clad::push(_t17, qk);
616 qk = qkm1 * z - qkm2 * yc;
617 double _t18 = qk;
618 {
619 if (_t18) {
620 clad::push(_t20, r);
621 r = pk / qk;
622 t = ::std::abs((ans - r) / r);
623 clad::push(_t22, ans);
624 ans = r;
625 } else {
626 t = 1.;
627 }
628 clad::push(_t19, _t18);
629 }
630 clad::push(_t24, pkm2);
631 pkm2 = pkm1;
632 clad::push(_t25, pkm1);
633 pkm1 = pk;
634 clad::push(_t26, qkm2);
635 qkm2 = qkm1;
636 clad::push(_t27, qkm1);
637 qkm1 = qk;
638 bool _t28 = ::std::abs(pk) > kBig;
639 {
640 if (_t28) {
641 clad::push(_t30, pkm2);
642 pkm2 *= kBiginv;
643 clad::push(_t31, pkm1);
644 pkm1 *= kBiginv;
645 clad::push(_t32, qkm2);
646 qkm2 *= kBiginv;
647 clad::push(_t33, qkm1);
648 qkm1 *= kBiginv;
649 }
650 clad::push(_t29, _t28);
651 }
652 } while (t > kMACHEP);
653 {
654 _d_ans += _d_y * ax;
655 _d_ax += ans * _d_y;
656 }
657 do {
658 {
659 if (clad::pop(_t29)) {
660 {
661 qkm1 = clad::pop(_t33);
662 double _r_d27 = _d_qkm1;
663 _d_qkm1 -= _r_d27;
664 _d_qkm1 += _r_d27 * kBiginv;
665 }
666 {
667 qkm2 = clad::pop(_t32);
668 double _r_d26 = _d_qkm2;
669 _d_qkm2 -= _r_d26;
670 _d_qkm2 += _r_d26 * kBiginv;
671 }
672 {
673 pkm1 = clad::pop(_t31);
674 double _r_d25 = _d_pkm1;
675 _d_pkm1 -= _r_d25;
676 _d_pkm1 += _r_d25 * kBiginv;
677 }
678 {
679 pkm2 = clad::pop(_t30);
680 double _r_d24 = _d_pkm2;
681 _d_pkm2 -= _r_d24;
682 _d_pkm2 += _r_d24 * kBiginv;
683 }
684 }
685 {
686 qkm1 = clad::pop(_t27);
687 double _r_d23 = _d_qkm1;
688 _d_qkm1 -= _r_d23;
689 _d_qk += _r_d23;
690 }
691 {
692 qkm2 = clad::pop(_t26);
693 double _r_d22 = _d_qkm2;
694 _d_qkm2 -= _r_d22;
695 _d_qkm1 += _r_d22;
696 }
697 {
698 pkm1 = clad::pop(_t25);
699 double _r_d21 = _d_pkm1;
700 _d_pkm1 -= _r_d21;
701 _d_pk += _r_d21;
702 }
703 {
704 pkm2 = clad::pop(_t24);
705 double _r_d20 = _d_pkm2;
706 _d_pkm2 -= _r_d20;
707 _d_pkm1 += _r_d20;
708 }
709 // t only controls the loop exit, so its adjoint is identically zero
710 // and it needs neither a tape nor a restore.
711 if (clad::pop(_t19)) {
712 {
713 ans = clad::pop(_t22);
714 double _r_d18 = _d_ans;
715 _d_ans -= _r_d18;
716 _d_r += _r_d18;
717 }
718 {
719 r = clad::pop(_t20);
720 double _r_d16 = _d_r;
721 _d_r -= _r_d16;
722 _d_pk += _r_d16 / qk;
723 double _r6 = _r_d16 * -(pk / (qk * qk));
724 _d_qk += _r6;
725 }
726 }
727 {
728 qk = clad::pop(_t17);
729 double _r_d15 = _d_qk;
730 _d_qk -= _r_d15;
731 _d_qkm1 += _r_d15 * z;
732 _d_z += qkm1 * _r_d15;
733 _d_qkm2 += -_r_d15 * yc;
734 _d_yc += qkm2 * -_r_d15;
735 }
736 {
737 pk = clad::pop(_t16);
738 double _r_d14 = _d_pk;
739 _d_pk -= _r_d14;
740 _d_pkm1 += _r_d14 * z;
741 _d_z += pkm1 * _r_d14;
742 _d_pkm2 += -_r_d14 * yc;
743 _d_yc += pkm2 * -_r_d14;
744 }
745 {
746 yc = clad::pop(_t15);
747 double _r_d13 = _d_yc;
748 _d_yc -= _r_d13;
749 _d_y0 += _r_d13 * c;
750 _d_c += y * _r_d13;
751 }
752 {
753 z = clad::pop(_t14);
754 double _r_d12 = _d_z;
755 }
756 {
757 y = clad::pop(_t13);
758 double _r_d11 = _d_y0;
759 }
760 {
761 c = clad::pop(_t12);
762 double _r_d10 = _d_c;
763 }
764 }
765 _t11--;
766 } while (_t11);
767 {
768 ans = _t10;
769 double _r_d9 = _d_ans;
770 _d_ans -= _r_d9;
771 _d_pkm1 += _r_d9 / qkm1;
772 double _r5 = _r_d9 * -(pkm1 / (qkm1 * qkm1));
773 _d_qkm1 += _r5;
774 }
775 {
776 qkm1 = _t9;
777 double _r_d8 = _d_qkm1;
778 _d_qkm1 -= _r_d8;
779 _d_z += _r_d8 * x;
780 *_d_x += z * _r_d8;
781 }
782 {
783 pkm1 = _t8;
784 double _r_d7 = _d_pkm1;
785 _d_pkm1 -= _r_d7;
786 *_d_x += _r_d7;
787 }
788 {
789 qkm2 = _t7;
790 double _r_d6 = _d_qkm2;
791 _d_qkm2 -= _r_d6;
792 *_d_x += _r_d6;
793 }
794 {
795 pkm2 = _t6;
796 double _r_d5 = _d_pkm2;
797 _d_pkm2 -= _r_d5;
798 }
799 {
800 c = _t5;
801 double _r_d4 = _d_c;
802 _d_c -= _r_d4;
803 }
804 {
805 z = _t4;
806 double _r_d3 = _d_z;
807 _d_z -= _r_d3;
808 *_d_x += _r_d3;
809 _d_y0 += _r_d3;
810 }
811 {
812 y = _t3;
813 double _r_d2 = _d_y0;
814 _d_y0 -= _r_d2;
815 *_d_a += -_r_d2;
816 }
817 {
818 ax = _t2;
819 double _r_d1 = _d_ax;
820 _d_ax -= _r_d1;
821 double _r4 = _r_d1 * ::std::exp(ax);
822 _d_ax += _r4;
823 }
824 {
825 *_d_x += a * _d_ax / x - _d_ax;
826 *_d_a += _d_ax * (_t1 - ::clad::custom_derivatives::std::clad_digamma(
827 a)); // numerical_diff::forward_central_difference(::std::lgamma, a, 0, 0, a);
828 _d_ax = 0.;
829 }
830}
831
832/// Derivative of the normalized lower incomplete gamma function P(a, x) with
833/// respect to x. This is the integrand of P(a, x), i.e. the gamma
834/// distribution density: x^(a-1) * exp(-x) / Gamma(a).
835inline double inc_gamma_dx(double a, double x)
836{
837 if (a <= 0 || x <= 0)
838 return 0.;
839 return ::std::exp((a - 1.) * ::std::log(x) - x - ::std::lgamma(a));
840}
841
842/// Pullback of inc_gamma_dx(), using the closed forms of the second
843/// derivatives of P(a, x):
844///
845/// d2P/dx2 = inc_gamma_dx(a, x) * ((a - 1) / x - 1)
846/// d2P/dxda = inc_gamma_dx(a, x) * (log(x) - digamma(a))
847inline void inc_gamma_dx_pullback(double a, double x, double _d_y, double *_d_a, double *_d_x)
848{
849 if (a <= 0 || x <= 0)
850 return;
851 const double g = inc_gamma_dx(a, x);
852 *_d_a += _d_y * g * (::std::log(x) - ::clad::custom_derivatives::std::clad_digamma(a));
853 *_d_x += _d_y * g * ((a - 1.) / x - 1.);
854}
855
856/// Derivative of the normalized lower incomplete gamma function P(a, x) with
857/// respect to a. It has no closed form, but inc_gamma_pullback() computes it
858/// exactly by differentiating through the algorithm that evaluates P(a, x).
859inline double inc_gamma_da(double a, double x)
860{
861 double da = 0.;
862 double dx = 0.;
863 inc_gamma_pullback(a, x, 1., &da, &dx);
864 return da;
865}
866
867/// Pullback of inc_gamma_da(). The mixed second derivative is known in
868/// closed form (it is the same as the a-derivative of inc_gamma_dx(), see
869/// inc_gamma_dx_pullback()). For d2P/da2 there is no closed form, so it is
870/// approximated by a central difference of the exact first derivative.
871///
872/// For a <= h, the lower stencil point leaves the domain (inc_gamma_da()
873/// returns zero for non-positive a), so d2P/da2 is unreliable there. This is
874/// acceptable because RooFit never differentiates with respect to a, which is
875/// data there.
876inline void inc_gamma_da_pullback(double a, double x, double _d_y, double *_d_a, double *_d_x)
877{
878 if (a <= 0 || x <= 0)
879 return;
880 *_d_x += _d_y * inc_gamma_dx(a, x) * (::std::log(x) - ::clad::custom_derivatives::std::clad_digamma(a));
881 // A first-order central difference of the exact derivative is much more
882 // accurate than a second-order finite difference of P(a, x) itself. The
883 // step size balances truncation and roundoff error (~ cbrt of the machine
884 // epsilon).
885 const double h = 6e-6 * ::std::max(1., ::std::abs(a));
886 *_d_a += _d_y * (inc_gamma_da(a + h, x) - inc_gamma_da(a - h, x)) / (2. * h);
887}
888
889/// Pushforward of ROOT::Math::inc_gamma. Besides forward-mode differentiation,
890/// this enables second derivatives (e.g. clad::hessian): clad differentiates
891/// this function in reverse mode, and all derivatives it needs for that are
892/// provided by custom pullbacks.
893inline clad::ValueAndPushforward<double, double> inc_gamma_pushforward(double a, double x, double d_a, double d_x)
894{
896}
897
898/// Pushforward of ROOT::Math::inc_gamma_c, which is 1 - inc_gamma. See
899/// inc_gamma_pushforward().
900inline clad::ValueAndPushforward<double, double> inc_gamma_c_pushforward(double a, double x, double d_a, double d_x)
901{
903}
904
905/// Pullback of landau_pdf_dv(). The second derivative of the standardized
906/// Landau density has no closed form, so it is approximated by a central
907/// difference of the exact first derivative (see also inc_gamma_da_pullback()).
908///
909/// The CERNLIB DENLAN approximation of the density is piecewise rational, and
910/// its first derivative has small jumps at the branch boundaries. Dividing
911/// such a jump by the step size would ruin the difference quotient (up to
912/// ~50 % error right at v = 1), so when the stencil would straddle a boundary
913/// it is shifted sideways to keep both points on the branch that contains v.
914/// The off-center evaluation costs one order in h, which is insignificant at
915/// this step size.
916inline void landau_pdf_dv_pullback(double v, double _d_y, double *_d_v)
917{
918 const double h = 6e-6 * ::std::max(1., ::std::abs(v));
919 double lo = v - h;
920 double hi = v + h;
921 // Branch boundaries of landau_pdf(); each branch covers v < seam.
922 constexpr double seams[] = {-5.5, -1., 1., 5., 12., 50., 300.};
923 for (double seam : seams) {
924 if (lo < seam && seam <= hi) {
925 if (v < seam) {
926 lo -= h;
927 hi -= h;
928 } else {
929 lo += h;
930 hi += h;
931 }
932 break;
933 }
934 }
935 *_d_v += _d_y * (landau_pdf_dv(hi) - landau_pdf_dv(lo)) / (2. * h);
936}
937
938/// Pushforward of ROOT::Math::landau_pdf, which is p((x - x0) / xi) / xi in
939/// terms of the standardized Landau density p. Like for
940/// inc_gamma_pushforward(), all derivatives that clad needs to differentiate
941/// this function in reverse mode (e.g. for clad::hessian) are provided by
942/// custom pullbacks.
943inline clad::ValueAndPushforward<double, double>
944landau_pdf_pushforward(double x, double xi, double x0, double d_x, double d_xi, double d_x0)
945{
946 if (xi <= 0.)
947 return {0., 0.};
948 const double v = (x - x0) / xi;
949 const double p = ::ROOT::Math::landau_pdf(v);
950 const double vDot = (d_x - d_x0 - v * d_xi) / xi;
951 return {p / xi, landau_pdf_dv(v) * vDot / xi - p / (xi * xi) * d_xi};
952}
953
954/// Pushforward of ROOT::Math::landau_cdf, which is the cumulative
955/// distribution function of the standardized Landau density evaluated at
956/// (x - x0) / xi. Its derivative in x is landau_pdf, so unlike for the
957/// density itself, all second derivatives are known in closed form.
958///
959/// That identity is exact only for the mathematical Landau distribution: ROOT
960/// implements the cdf (CERNLIB DISLAN) and the density (CERNLIB DENLAN) as
961/// independent rational approximations that are consistent with each other to
962/// about 1e-7 relative. The derivatives returned here therefore differ at
963/// that level both from the exact derivative of the implemented cdf and from
964/// landau_cdf_pullback(), which differentiates the DISLAN algorithm itself.
965inline clad::ValueAndPushforward<double, double>
966landau_cdf_pushforward(double x, double xi, double x0, double d_x, double d_xi, double d_x0)
967{
968 const double v = (x - x0) / xi;
969 const double vDot = (d_x - d_x0 - v * d_xi) / xi;
971}
972
973} // namespace Math
974} // namespace ROOT
975
976} // namespace custom_derivatives
977} // namespace clad
978
979#endif // CLAD_DERIVATOR
#define c(i)
Definition RSha256.hxx:101
#define g(i)
Definition RSha256.hxx:105
#define a(i)
Definition RSha256.hxx:99
#define h(i)
Definition RSha256.hxx:106
#define e(i)
Definition RSha256.hxx:103
#define kMACHEP
#define kMAXLOG
ROOT::Detail::TRangeCast< T, true > TRangeDynCast
TRangeDynCast is an adapter class that allows the typed iteration through a TCollection.
#define N
winID h TVirtualViewer3D TVirtualGLPainter p
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t r
#define hi
float * q
double landau_pdf(double x, double xi=1, double x0=0)
Probability density function of the Landau distribution:
double landau_cdf(double x, double xi=1, double x0=0)
Cumulative distribution function of the Landau distribution (lower tail).
double inc_gamma_c(double a, double x)
Calculates the normalized (regularized) upper incomplete gamma function (upper integral)
double inc_gamma(double a, double x)
Calculates the normalized (regularized) lower incomplete gamma function (lower integral)
Double_t y[n]
Definition legend1.C:17
Double_t x[n]
Definition legend1.C:17
Namespace for new Math classes and functions.
TMath.
Definition TMathBase.h:35
Double_t CosH(Double_t)
Returns the hyperbolic cosine of x.
Definition TMath.h:625
Double_t ACos(Double_t)
Returns the principal value of the arc cosine of x, expressed in radians.
Definition TMath.h:645
Double_t ASin(Double_t)
Returns the principal value of the arc sine of x, expressed in radians.
Definition TMath.h:637
Double_t Log2(Double_t x)
Returns the binary (base-2) logarithm of x.
Definition TMath.cxx:107
Double_t Exp(Double_t x)
Returns the base-e exponential function of x, which is e raised to the power x.
Definition TMath.h:722
Double_t Erf(Double_t x)
Computation of the error function erf(x).
Definition TMath.cxx:190
Double_t ATan(Double_t)
Returns the principal value of the arc tangent of x, expressed in radians.
Definition TMath.h:653
Double_t ASinH(Double_t)
Returns the area hyperbolic sine of x.
Definition TMath.cxx:67
Double_t TanH(Double_t)
Returns the hyperbolic tangent of x.
Definition TMath.h:631
Double_t ACosH(Double_t)
Returns the nonnegative area hyperbolic cosine of x.
Definition TMath.cxx:81
Double_t Log(Double_t x)
Returns the natural logarithm of x.
Definition TMath.h:769
Double_t Erfc(Double_t x)
Computes the complementary error function erfc(x).
Definition TMath.cxx:199
Double_t Sq(Double_t x)
Returns x*x.
Definition TMath.h:669
Double_t Sqrt(Double_t x)
Returns the square root of x.
Definition TMath.h:675
LongDouble_t Power(LongDouble_t x, LongDouble_t y)
Returns x raised to the power y.
Definition TMath.h:734
constexpr Double_t Ln10()
Natural log of 10 (to convert log to ln)
Definition TMath.h:103
Double_t Hypot(Double_t x, Double_t y)
Returns sqrt(x*x + y*y)
Definition TMath.cxx:59
Double_t Cos(Double_t)
Returns the cosine of an angle of x radians.
Definition TMath.h:607
constexpr Double_t Pi()
Definition TMath.h:40
Double_t LnGamma(Double_t z)
Computation of ln[gamma(z)] for all z.
Definition TMath.cxx:509
Double_t Sin(Double_t)
Returns the sine of an angle of x radians.
Definition TMath.h:601
Double_t Tan(Double_t)
Returns the tangent of an angle of x radians.
Definition TMath.h:613
Double_t ATanH(Double_t)
Returns the area hyperbolic tangent of x.
Definition TMath.cxx:95
Double_t Log10(Double_t x)
Returns the common (base-10) logarithm of x.
Definition TMath.h:775
Short_t Abs(Short_t d)
Returns the absolute value of parameter Short_t d.
Definition TMathBase.h:122
Double_t SinH(Double_t)
Returns the hyperbolic sine of x.
Definition TMath.h:619
double landau_pdf_dv(double v)
First derivative of the standardized Landau density p(v) = ROOT::Math::landau_pdf(v) with respect to ...
clad::ValueAndPushforward< double, double > inc_gamma_c_pushforward(double a, double x, double d_a, double d_x)
Pushforward of ROOT::Math::inc_gamma_c, which is 1 - inc_gamma.
void landau_pdf_pullback(double x, double xi, double x0, double d_out, double *d_x, double *d_xi, double *d_x0)
double inc_gamma_da(double a, double x)
Derivative of the normalized lower incomplete gamma function P(a, x) with respect to a.
double landau_cdf_dv(double v)
Derivative with respect to v of the CERNLIB DISLAN rational approximation of the standardized Landau ...
clad::ValueAndPushforward< double, double > landau_cdf_pushforward(double x, double xi, double x0, double d_x, double d_xi, double d_x0)
Pushforward of ROOT::Math::landau_cdf, which is the cumulative distribution function of the standardi...
double horner(const double(&c)[N], double x)
Evaluate the polynomial c[0] + c[1]*x + c[2]*x^2 + ... in Horner form, matching the evaluation order ...
clad::ValueAndPushforward< double, double > inc_gamma_pushforward(double a, double x, double d_a, double d_x)
Pushforward of ROOT::Math::inc_gamma.
void inc_gamma_c_pullback(double a, double x, double _d_y, double *_d_a, double *_d_x)
clad::ValueAndPushforward< double, double > landau_pdf_pushforward(double x, double xi, double x0, double d_x, double d_xi, double d_x0)
Pushforward of ROOT::Math::landau_pdf, which is p((x - x0) / xi) / xi in terms of the standardized La...
void inc_gamma_da_pullback(double a, double x, double _d_y, double *_d_a, double *_d_x)
Pullback of inc_gamma_da().
void landau_cdf_pullback(double x, double xi, double x0, double d_out, double *d_x, double *d_xi, double *d_x0)
void inc_gamma_dx_pullback(double a, double x, double _d_y, double *_d_a, double *_d_x)
Pullback of inc_gamma_dx(), using the closed forms of the second derivatives of P(a,...
double horner_deriv(const double(&c)[N], double x)
Derivative of horner(c, x) with respect to x.
double inc_gamma_dx(double a, double x)
Derivative of the normalized lower incomplete gamma function P(a, x) with respect to x.
void inc_gamma_pullback(double a, double x, double _d_y, double *_d_a, double *_d_x)
void landau_pdf_dv_pullback(double v, double _d_y, double *_d_v)
Pullback of landau_pdf_dv().
double rational_deriv(const double(&p)[N], const double(&q)[M], double x)
Derivative of the rational function horner(p, x) / horner(q, x) with respect to x.
ValueAndPushforward< T, T > CosH_pushforward(T x, T d_x)
ValueAndPushforward< T, T > Abs_pushforward(T x, T d_x)
void Power_pullback(T x, U y, V p, T *d_x, U *d_y)
ValueAndPushforward< T, T > Sq_pushforward(T x, T d_x)
void Hypot_pullback(T x, T y, U p, T *d_x, T *d_y)
ValueAndPushforward< T, T > Erf_pushforward(T x, T d_x)
ValueAndPushforward< T, T > Erfc_pushforward(T x, T d_x)
ValueAndPushforward< T, T > Sin_pushforward(T x, T d_x)
ValueAndPushforward< T, T > Hypot_pushforward(T x, T y, T d_x, T d_y)
ValueAndPushforward< T, T > ASinH_pushforward(T x, T d_x)
ValueAndPushforward< T, T > LnGamma_pushforward(T z, T d_z)
ValueAndPushforward< T, T > ACosH_pushforward(T x, T d_x)
ValueAndPushforward< T, T > ASin_pushforward(T x, T d_x)
ValueAndPushforward< T, T > Power_pushforward(T x, U y, T d_x, U d_y)
ValueAndPushforward< T, T > Cos_pushforward(T x, T d_x)
ValueAndPushforward< T, T > Sqrt_pushforward(T x, T d_x)
ValueAndPushforward< T, T > Tan_pushforward(T x, T d_x)
ValueAndPushforward< T, T > Log_pushforward(T x, T d_x)
ValueAndPushforward< T, T > Log10_pushforward(T x, T d_x)
ValueAndPushforward< T, T > TanH_pushforward(T x, T d_x)
ValueAndPushforward< T, T > ACos_pushforward(T x, T d_x)
ValueAndPushforward< T, T > SinH_pushforward(T x, T d_x)
ValueAndPushforward< T, T > Exp_pushforward(T x, T d_x)
ValueAndPushforward< T, T > Log2_pushforward(T x, T d_x)
ValueAndPushforward< T, T > ATanH_pushforward(T x, T d_x)
ValueAndPushforward< T, T > ATan_pushforward(T x, T d_x)
TLine lv
Definition textalign.C:5