Logo ROOT   6.18/05
Reference Guide
RooJohnson.cxx
Go to the documentation of this file.
1// Author: Stephan Hageboeck, CERN, May 2019
2/*****************************************************************************
3 * Project: RooFit *
4 * Package: RooFitModels *
5 * @(#)root/roofit:$Id$
6 * Authors: *
7 * WV, Wouter Verkerke, UC Santa Barbara, verkerke@slac.stanford.edu *
8 * DK, David Kirkby, UC Irvine, dkirkby@uci.edu *
9 * *
10 * Copyright (c) 2000-2005, Regents of the University of California *
11 * and Stanford University. All rights reserved. *
12 * *
13 * Redistribution and use in source and binary forms, *
14 * with or without modification, are permitted according to the terms *
15 * listed in LICENSE (http://roofit.sourceforge.net/license.txt) *
16 *****************************************************************************/
17
18/** \class RooJohnson
19 \ingroup Roofit
20
21Johnson's \f$ S_{U} \f$ distribution.
22
23This PDF results from transforming a normally distributed variable \f$ x \f$ to this form:
24\f[
25 z = \gamma + \delta \sinh^{-1}\left( \frac{x - \mu}{\lambda} \right)
26\f]
27The resulting PDF is
28\f[
29 \mathrm{PDF}[\mathrm{Johnson}\ S_U] = \frac{\delta}{\lambda\sqrt{2\pi}}
30 \frac{1}{\sqrt{1 + \left( \frac{x-\mu}{\lambda} \right)^2}}
31 \;\exp\left[-\frac{1}{2} \left(\gamma + \delta \sinh^{-1}\left(\frac{x-\mu}{\lambda}\right) \right)^2\right].
32\f]
33
34It is often used to fit a mass difference for charm decays, and therefore the variable \f$ x \f$ is called
35"mass" in the implementation. A mass threshold allows to set the PDF to zero to the left of the threshold.
36
37###References:
38Johnson, N. L. (1949). *Systems of Frequency Curves Generated by Methods of Translation*. Biometrika **36(1/2)**, 149–176. [doi:10.2307/2332539](https://doi.org/10.2307%2F2332539)
39
40\image html RooJohnson_plot.png
41
42**/
43
44#include "RooJohnson.h"
45
46#include "RooRandom.h"
47
48#include <cmath>
49#include "TMath.h"
50
52
53////////////////////////////////////////////////////////////////////////////////
54/// Construct a new Johnson PDF.
55///
56/// \param name Name that identifies the PDF in computations
57/// \param title Title for plotting
58/// \param mass The variable of the PDF. Often this is a mass.
59/// \param mu Location parameter of the Gaussian component.
60/// \param lambda Width parameter of the Gaussian component.
61/// \param gamma Shape parameter that distorts distribution to left/right.
62/// \param delta Shape parameter (>0) that determines strength of Gaussian-like component.
63/// \param massThreshold Set PDF to zero below this threshold.
64RooJohnson::RooJohnson(const char *name, const char *title,
65 RooAbsReal& mass, RooAbsReal& mu, RooAbsReal& lambda,
67 double massThreshold) :
68 RooAbsPdf(name,title),
69 _mass("mass", "Mass observable", this, mass),
70 _mu("mu", "Location parameter of the underlying normal distribution.", this, mu),
71 _lambda("lambda", "Width parameter of the underlying normal distribution (=2 lambda)", this, lambda),
72 _gamma("gamma", "Shift of transformation", this, gamma),
73 _delta("delta", "Scale of transformation", this, delta),
74 _massThreshold(massThreshold)
75{
76
77}
78
79
80////////////////////////////////////////////////////////////////////////////////
81/// Copy a Johnson PDF.
82RooJohnson::RooJohnson(const RooJohnson& other, const char* newName) :
83 RooAbsPdf(other, newName),
84 _mass("Mass", this, other._mass),
85 _mu("mean", this, other._mu),
86 _lambda("lambda", this, other._lambda),
87 _gamma("gamma", this, other._gamma),
88 _delta("delta", this, other._delta),
89 _massThreshold(other._massThreshold)
90{
91
92}
93
94
95////////////////////////////////////////////////////////////////////////////////
96
98{
100 return 0.;
101
102 const double arg = (_mass-_mu)/_lambda;
103 const double expo = _gamma + _delta * asinh(arg);
104
105 const double result = _delta
106 / sqrt(TMath::TwoPi())
107 / (_lambda * sqrt(1. + arg*arg))
108 * exp(-0.5 * expo * expo);
109
110 return result;
111}
112
113////////////////////////////////////////////////////////////////////////////////
114
115int RooJohnson::getAnalyticalIntegral(RooArgSet& allVars, RooArgSet& analVars, const char* /*rangeName*/) const
116{
117 if (matchArgs(allVars, analVars, _mass)) return kMass;
118 if (matchArgs(allVars, analVars, _mu)) return kMean;
119 if (matchArgs(allVars, analVars, _lambda)) return kLambda;
120 if (matchArgs(allVars, analVars, _gamma)) return kGamma;
121 if (matchArgs(allVars, analVars, _delta)) return kDelta;
122 //TODO write integral for others
123 return 0;
124}
125
126////////////////////////////////////////////////////////////////////////////////
127
128double RooJohnson::analyticalIntegral(Int_t code, const char* rangeName) const
129{
130 //The normalisation constant is left out in evaluate().
131 //Therefore, the integral is scaled up by that amount to make RooFit normalise
132 //correctly.
133 const double globalNorm = 1.;
134// const double globalNorm = sqrt(TMath::TwoPi());
135
136 //Here everything is scaled and shifted such that we only need to compute CDF(Gauss):
137 double min = -1.E300;
138 double max = 1.E300;
139 if (kMass <= code && code <= kLambda) {
140 double argMin, argMax;
141
142 if (code == kMass) {
143 argMin = (_mass.min(rangeName)-_mu)/_lambda;
144 argMax = (_mass.max(rangeName)-_mu)/_lambda;
145 } else if (code == kMean) {
146 argMin = (_mass-_mu.min(rangeName))/_lambda;
147 argMax = (_mass-_mu.max(rangeName))/_lambda;
148 } else {
149 assert(code == kLambda);
150 argMin = (_mass-_mu)/_lambda.min(rangeName);
151 argMax = (_mass-_mu)/_lambda.max(rangeName);
152 }
153
154 min = _gamma + _delta * asinh(argMin);
155 max = _gamma + _delta * asinh(argMax);
156 } else if (code == kGamma) {
157 const double arg = (_mass-_mu)/_lambda;
158 min = _gamma.min(rangeName) + _delta * asinh(arg);
159 max = _gamma.max(rangeName) + _delta * asinh(arg);
160 } else if (code == kDelta) {
161 const double arg = (_mass-_mu)/_lambda;
162 min = _gamma + _delta.min(rangeName) * asinh(arg);
163 max = _gamma + _delta.max(rangeName) * asinh(arg);
164 } else {
165 assert(false);
166 }
167
168
169
170 //Here we go for maximum precision: We compute all integrals in the UPPER
171 //tail of the Gaussian, because erfc has the highest precision there.
172 //Therefore, the different cases for range limits in the negative hemisphere are mapped onto
173 //the equivalent points in the upper hemisphere using erfc(-x) = 2. - erfc(x)
174 const double ecmin = std::erfc(std::abs(min/sqrt(2.)));
175 const double ecmax = std::erfc(std::abs(max/sqrt(2.)));
176
177 const double result = 0.5 * (
178 min*max < 0.0 ? 2.0 - (ecmin + ecmax)
179 : max <= 0. ? ecmax - ecmin : ecmin - ecmax
180 );
181
182 // Now, include the global norm that may be missing in evaluate and return
183 return globalNorm * (result != 0. ? result : 1.E-300);
184}
185
186
187
188////////////////////////////////////////////////////////////////////////////////
189/// Advertise which kind of direct event generation is supported.
190///
191/// So far, only generating mass values is supported.
192Int_t RooJohnson::getGenerator(const RooArgSet& directVars, RooArgSet &generateVars, Bool_t /*staticInitOK*/) const
193{
194 if (matchArgs(directVars, generateVars, _mass)) return 1 ;
195// if (matchArgs(directVars, generateVars, _mu)) return 2 ;
196 return 0 ;
197}
198
199
200
201////////////////////////////////////////////////////////////////////////////////
202/// Generate events based code obtained by getGenerator.
203///
204/// So far, only generating mass values is supported. Others will have to be generated
205/// by the slower accept/reject method.
207{
208 if (code == 1) {
209 while (true) {
210 const double gauss = RooRandom::randomGenerator()->Gaus(0., 1.);
211 const double mass = _lambda * sinh((gauss - _gamma)/_delta) + _mu;
212 if (_mass.min() <= mass && mass <= _mass.max() && _massThreshold <= mass) {
213 _mass = mass;
214 break;
215 }
216 }
217 } else {
218 std::cerr << "Generation in other variables not yet implemented." << std::endl;
219 assert(false);
220 }
221}
int Int_t
Definition: RtypesCore.h:41
bool Bool_t
Definition: RtypesCore.h:59
#define ClassImp(name)
Definition: Rtypes.h:365
char name[80]
Definition: TGX11.cxx:109
double sinh(double)
double sqrt(double)
double exp(double)
RooAbsReal is the common abstract base class for objects that represent a real value and implements f...
Definition: RooAbsReal.h:53
Bool_t matchArgs(const RooArgSet &allDeps, RooArgSet &numDeps, const RooArgProxy &a) const
Utility function for use in getAnalyticalIntegral().
RooArgSet is a container object that can hold multiple RooAbsArg objects.
Definition: RooArgSet.h:28
Johnson's distribution.
Definition: RooJohnson.h:26
RooJohnson()=default
Double_t evaluate() const override
Evaluate this PDF / function / constant. Needs to be overridden by all derived classes.
Definition: RooJohnson.cxx:97
Double_t analyticalIntegral(Int_t code, const char *rangeName=0) const override
Implements the actual analytical integral(s) advertised by getAnalyticalIntegral.
Definition: RooJohnson.cxx:128
RooRealProxy _mass
Definition: RooJohnson.h:52
RooRealProxy _delta
Definition: RooJohnson.h:57
RooRealProxy _gamma
Definition: RooJohnson.h:56
void generateEvent(Int_t code) override
Generate events based code obtained by getGenerator.
Definition: RooJohnson.cxx:206
Int_t getGenerator(const RooArgSet &directVars, RooArgSet &generateVars, Bool_t staticInitOK=kTRUE) const override
Advertise which kind of direct event generation is supported.
Definition: RooJohnson.cxx:192
RooRealProxy _mu
Definition: RooJohnson.h:53
RooRealProxy _lambda
Definition: RooJohnson.h:54
double _massThreshold
Definition: RooJohnson.h:59
Int_t getAnalyticalIntegral(RooArgSet &allVars, RooArgSet &analVars, const char *rangeName=0) const override
Interface function getAnalyticalIntergral advertises the analytical integrals that are supported.
Definition: RooJohnson.cxx:115
static TRandom * randomGenerator()
Return a pointer to a singleton random-number generator implementation.
Definition: RooRandom.cxx:54
Double_t min(const char *rname=0) const
Definition: RooRealProxy.h:56
Double_t max(const char *rname=0) const
Definition: RooRealProxy.h:57
virtual Double_t Gaus(Double_t mean=0, Double_t sigma=1)
Samples a random number from the standard Normal (Gaussian) Distribution with the given mean and sigm...
Definition: TRandom.cxx:263
double erfc(double x)
Complementary error function.
double gamma(double x)
static constexpr double gauss
constexpr Double_t TwoPi()
Definition: TMath.h:45