UCKDE.cc
1 #include "UCKDE.h"
2 #include "TMath.h"
3 
4 ClassImp(UCorrelator::KDE2D);
5 
6 
7 static double dist2(double x0, double y0, double x1, double y1)
8 {
9 
10  return (x0-x1)*(x0-x1) + (y0-y1)*(y0-y1);
11 
12 }
13 
14 //naive algorithm for now... probably very slow!
15 static int nearest_neighbors(int n, double x, double y, int Npts, const double * vx,
16  const double * vy, int * nearest_i, double * nearest_d2)
17 {
18 
19  int nfound = 0;
20 
21  for (int i = 0; i < Npts; i++)
22  {
23 
24  double d2 = dist2(x,y,vx[i],vy[i]);
25 
26  if (nfound == 0)
27  {
28  nearest_d2[0]=d2;
29  nearest_i[0] = i;
30  nfound++;
31  }
32  else if (d2 < nearest_d2[nfound-1])
33  {
34 
35  int last = nfound-1;
36  //if we are below capacity, increase capacity!
37  if (nfound < n) nfound++;
38 
39  int new_index = last;
40  for (int ii = 0 ; ii < last-1; ii++)
41  {
42  if (d2 < nearest_d2[ii])
43  {
44  new_index = ii;
45  break;
46  }
47  }
48 
49  //now shift things over 1
50 
51  for (int ii = nfound-1; ii > new_index; ii--)
52  {
53  nearest_d2[ii] = nearest_d2[ii-1];
54  nearest_i[ii] = nearest_i[ii-1];
55  }
56 
57  nearest_d2[new_index] = d2;
58  nearest_i[new_index] = i;
59  }
60  }
61  return nfound;
62 }
63 
64 
65 
66 UCorrelator::KDE2D::KDE2D(int n, const double *xin, const double * yin, const double * weights, KDE2DOptions opt)
67  : x(xin, xin+n), y(yin,yin+n)
68 {
69  if (weights)
70  {
71  w.insert(w.end(), weights, weights+n);
72  }
73 
74  double sx = opt.sigma_x ? opt.sigma_x: pow(n,-1./6)*TMath::RMS(n,xin);
75  double sy = opt.sigma_y ? opt.sigma_y: pow(n,-1./6)*TMath::RMS(n,yin);
76 
77  // sigma_x/y will be of size 1 if not adaptive.
78 
79  if (opt.adaptiveness == 0)
80  {
81 
82  sigma_x.push_back(sx);
83  sigma_y.push_back(sy);
84  rho.push_back(opt.corr);
85  double sum_of_weights = n;
86  if (w.size() > 0)
87  {
88  sum_of_weights = 0;
89  for (unsigned i = 0; i < w.size(); i++) sum_of_weights += w[i];
90  }
91  W = 1./( 2*sum_of_weights*M_PI * sx*sy*sqrt(1-opt.corr*opt.corr)); //check this
92  }
93 
94  else
95  {
96  sigma_x.reserve(n);
97  sigma_y.reserve(n);
98  rho.reserve(n);
99 
100  std::vector<double> nearest_d2(opt.adaptiveness+1);
101  std::vector<int> nearest_i(opt.adaptiveness+1);
102  double Winv = 0;
103  for (int i = 0; i < n; i++)
104  {
105  int found = nearest_neighbors(opt.adaptiveness+1, xin[i],yin[i], n, xin, yin, &nearest_i[0], &nearest_d2[0]);
106 
107  double d2 =nearest_d2[found-1];
108  double d = sqrt(d2);
109  sigma_x.push_back(sx * d);
110  sigma_y.push_back(sy * d);
111  rho.push_back(opt.corr * d);
112  double this_w = w.size() ? w[i] : 1;
113  Winv += this_w/( 2*d2*M_PI * sx*sy*sqrt(1-d2*opt.corr*opt.corr)); //check this
114  }
115  W = 1./Winv;
116  }
117 }
118 
119 
120 //TODO vectorize this
121 double UCorrelator::KDE2D::operator()(double xt, double yt) const
122 {
123 
124  double sxinv = 1./sigma_x[0];
125  double syinv = 1./sigma_y[0];
126  double sxinv2 = sxinv*sxinv;
127  double syinv2 = syinv*syinv;
128  double r = rho[0];
129  double one_minus_r_inv = 1./(1-r);
130  double sxyinv = r == 0 ? 0 : sxinv*syinv;
131 
132  double sum = 0;
133  for (unsigned i = 0; i < x.size(); i++)
134  {
135  if (sigma_x.size() > 1 && i > 0)
136  {
137  sxinv = 1./sigma_x[i];
138  syinv = 1./sigma_y[i];
139  sxinv2 = sxinv*sxinv;
140  syinv2 = syinv*syinv;
141  r = rho[i];
142  one_minus_r_inv = 1./(1-r);
143  sxyinv = r == 0 ? 0 : sxinv*syinv;
144  }
145 
146  double xd = ( x[i] - xt);
147  double yd = ( y[i] - yt);
148  double z = xd*xd*sxinv2 + yd*yd*syinv2;
149  if (r!=0) z -= 2*r*xd*yd*sxyinv;
150  double this_w = w.size() == 0 ? 1 : w[i];
151  sum += this_w * (r == 0 ? exp ( - 0.5* z ) : exp(-0.5*z*one_minus_r_inv));
152  }
153 
154  return sum *W;
155 }
156 
157 
158 void UCorrelator::KDE2D::getNSigmaBounds(double nsig, double & xmin, double & xmax, double & ymin, double & ymax) const
159 {
160 
161  double sig_x = sigma_x[0];
162  double sig_y = sigma_y[0];
163 
164  xmin = x[0] - sig_x * nsig;
165  xmax = x[0] + sig_x * nsig;
166  ymin = y[0] - sig_y * nsig;
167  ymax = y[0] + sig_y * nsig;
168 
169  for (unsigned i = 1; i < x.size() ; i++)
170  {
171  if (sigma_x.size() > 1)
172  {
173  sig_x = sigma_x[i];
174  sig_y = sigma_y[i];
175  }
176 
177  double test_xmin = x[i] - sig_x * nsig;
178  double test_xmax = x[i] + sig_x * nsig;
179  double test_ymin = y[i] - sig_y * nsig;
180  double test_ymax = y[i] + sig_y * nsig;
181 
182 
183  if (test_xmin < xmin) xmin = test_xmin;
184  if (test_ymin < ymin) ymin = test_ymin;
185  if (test_xmax > xmax) xmax = test_xmax;
186  if (test_ymax > ymax) ymax = test_ymax;
187  }
188 }
189 
190 
191 TF2 * UCorrelator::KDE2D::makeTF2(const char * name, double nsigma_bounds) const
192 {
193 
194  double xmin,xmax,ymin,ymax;
195  getNSigmaBounds(nsigma_bounds,xmin,xmax,ymin,ymax);
196 
197  return new TF2(name, this, xmin,xmax,ymin,ymax, 0,2);
198 }
199 
200 TH2 * UCorrelator::KDE2D::makeTH2(double binwidth_x, double binwidth_y, const char * name, const char * title, double nsigma_bounds, double auto_factor) const
201 {
202 
203  double xmin,xmax,ymin,ymax;
204  getNSigmaBounds(nsigma_bounds,xmin,xmax,ymin,ymax);
205 
206  if (!binwidth_x)
207  {
208  binwidth_x = sigma_x[0] * auto_factor;
209  for (unsigned i = 1; i < sigma_x.size(); i++) binwidth_x = TMath::Min(binwidth_x, sigma_x[i]*auto_factor);
210  }
211 
212  if (!binwidth_y)
213  {
214  binwidth_y = sigma_y[0] * auto_factor;
215  for (unsigned i = 1; i < sigma_y.size(); i++) binwidth_y = TMath::Min(binwidth_y, sigma_y[i]*auto_factor);
216  }
217 
218  int nbinsx = ceil((xmax-xmin)/binwidth_x);
219  int nbinsy = ceil((ymax-ymin)/binwidth_x);
220  xmax = xmin + nbinsx * binwidth_x;
221  ymax = ymin + nbinsy * binwidth_y;
222 
223  TH2 * h = new TH2D ( name,title, nbinsx, xmin, xmax, nbinsy, ymin, ymax);
224 
225  h->SetEntries(x.size());
226  for (int i = 1; i <= h->GetNbinsX(); i++)
227  {
228  for (int j = 1; j <= h->GetNbinsY(); j++)
229  {
230  h->SetBinContent(i,j, this->operator()(h->GetXaxis()->GetBinCenter(i), h->GetYaxis()->GetBinCenter(j)));
231  }
232  }
233  return h;
234 }
TH2 * makeTH2(double binwidth_x=0, double binwidth_y=0, const char *name="hkde2d", const char *title="Kernel Density Estimator", double n_sigma_bound=3, double auto_factor=0.25) const
Definition: UCKDE.cc:200