GooFit  v2.1.3
AddPdf.cu
Go to the documentation of this file.
1 #include <goofit/Error.h>
2 #include <goofit/PDFs/combine/AddPdf.h>
3 #include <goofit/detail/ThrustOverride.h>
4 
5 #include <thrust/iterator/constant_iterator.h>
6 #include <thrust/transform_reduce.h>
7 
8 #ifdef GOOFIT_MPI
9 #include <mpi.h>
10 #endif
11 
12 namespace GooFit {
13 
14 __device__ fptype device_AddPdfs(fptype *evt, fptype *p, unsigned int *indices) {
15  int numParameters = RO_CACHE(indices[0]);
16  fptype ret = 0;
17  fptype totalWeight = 0;
18 
19  for(int i = 1; i < numParameters - 3; i += 3) {
20  totalWeight += RO_CACHE(p[RO_CACHE(indices[i + 2])]);
21  fptype curr = callFunction(evt, RO_CACHE(indices[i]), RO_CACHE(indices[i + 1]));
22  fptype weight = RO_CACHE(p[RO_CACHE(indices[i + 2])]);
23  ret += weight * curr * normalisationFactors[RO_CACHE(indices[i + 1])];
24 
25  // if ((gpuDebug & 1) && (0 == THREADIDX) && (0 == BLOCKIDX))
26  // if ((1 > (int) floor(0.5 + evt[8])) && (gpuDebug & 1) && (paramIndices + debugParamIndex == indices))
27  // printf("Add comp %i: %f * %f * %f = %f (%f)\n", i, weight, curr, normalisationFactors[indices[i+1]],
28  // weight*curr*normalisationFactors[indices[i+1]], ret);
29  }
30 
31  // numParameters does not count itself. So the array structure for two functions is
32  // nP | F P w | F P
33  // in which nP = 5. Therefore the parameter index for the last function pointer is nP, and the function index is
34  // nP-1.
35  // fptype last = (*(reinterpret_cast<device_function_ptr>(device_function_table[indices[numParameters-1]])))(evt, p,
36  // paramIndices + indices[numParameters]);
37  fptype last = callFunction(evt, RO_CACHE(indices[numParameters - 1]), RO_CACHE(indices[numParameters]));
38  ret += (1 - totalWeight) * last * normalisationFactors[RO_CACHE(indices[numParameters])];
39 
40  // if ((THREADIDX < 50) && (isnan(ret))) printf("NaN final component %f %f\n", last, totalWeight);
41 
42  // if ((gpuDebug & 1) && (0 == THREADIDX) && (0 == BLOCKIDX))
43  // if ((1 > (int) floor(0.5 + evt[8])) && (gpuDebug & 1) && (paramIndices + debugParamIndex == indices))
44  // printf("Add final: %f * %f * %f = %f (%f)\n", (1 - totalWeight), last,
45  // normalisationFactors[indices[numParameters]], (1 - totalWeight) *last*
46  // normalisationFactors[indices[numParameters]], ret);
47 
48  return ret;
49 }
50 
51 __device__ fptype device_AddPdfsExt(fptype *evt, fptype *p, unsigned int *indices) {
52  // numParameters does not count itself. So the array structure for two functions is
53  // nP | F P w | F P w
54  // in which nP = 6.
55 
56  int numParameters = RO_CACHE(indices[0]);
57  fptype ret = 0;
58  fptype totalWeight = 0;
59 
60  for(int i = 1; i < numParameters; i += 3) {
61  // fptype curr = (*(reinterpret_cast<device_function_ptr>(device_function_table[indices[i]])))(evt, p,
62  // paramIndices + indices[i+1]);
63  fptype curr = callFunction(evt, RO_CACHE(indices[i]), RO_CACHE(indices[i + 1]));
64  fptype weight = RO_CACHE(p[RO_CACHE(indices[i + 2])]);
65  ret += weight * curr * normalisationFactors[RO_CACHE(indices[i + 1])];
66 
67  totalWeight += weight;
68  // if ((gpuDebug & 1) && (THREADIDX == 0) && (0 == BLOCKIDX))
69  // if ((1 > (int) floor(0.5 + evt[8])) && (gpuDebug & 1) && (paramIndices + debugParamIndex == indices))
70  // printf("AddExt: %i %E %f %f %f %f %f %f\n", i, curr, weight, ret, totalWeight,
71  // normalisationFactors[indices[i+1]], evt[0], evt[8]);
72  }
73 
74  ret /= totalWeight;
75  // if ((1 > (int) floor(0.5 + evt[8])) && (gpuDebug & 1) && (paramIndices + debugParamIndex == indices))
76  // if ((gpuDebug & 1) && (THREADIDX == 0) && (0 == BLOCKIDX))
77  // printf("AddExt result: %f\n", ret);
78 
79  return ret;
80 }
81 
82 __device__ device_function_ptr ptr_to_AddPdfs = device_AddPdfs;
83 __device__ device_function_ptr ptr_to_AddPdfsExt = device_AddPdfsExt;
84 
85 AddPdf::AddPdf(std::string n, std::vector<Variable> weights, std::vector<PdfBase *> comps)
86  : GooPdf(n)
87  , extended(true) {
88  if(weights.size() != comps.size() && (weights.size() + 1) != comps.size())
89  throw GooFit::GeneralError("Size of weights {} (+1) != comps {}", weights.size(), comps.size());
90 
91  // Indices stores (function index)(function parameter index)(weight index) triplet for each component.
92  // Last component has no weight index unless function is extended.
93  for(PdfBase *p : comps) {
94  components.push_back(p);
95  if(components.back() == nullptr)
96  throw GooFit::GeneralError("Invalid component");
97  }
98 
99  observables = getObservables();
100 
101  std::vector<unsigned int> pindices;
102 
103  for(unsigned int w = 0; w < weights.size(); ++w) {
104  if(components[w] == nullptr)
105  throw GooFit::GeneralError("Invalid component");
106  pindices.push_back(components[w]->getFunctionIndex());
107  pindices.push_back(components[w]->getParameterIndex());
108  pindices.push_back(registerParameter(weights[w]));
109  }
110 
111  if(components.back() == nullptr)
112  throw GooFit::GeneralError("Invalid component");
113 
114  if(weights.size() < components.size()) {
115  pindices.push_back(components.back()->getFunctionIndex());
116  pindices.push_back(components.back()->getParameterIndex());
117  extended = false;
118  }
119 
120  if(extended) {
121  GET_FUNCTION_ADDR(ptr_to_AddPdfsExt);
122  } else {
123  GET_FUNCTION_ADDR(ptr_to_AddPdfs);
124  }
125 
126  initialize(pindices);
127 }
128 
129 AddPdf::AddPdf(std::string n, Variable frac1, PdfBase *func1, PdfBase *func2)
130  : GooPdf(n)
131  , extended(false) {
132  // Special-case constructor for common case of adding two functions.
133  components.push_back(func1);
134  components.push_back(func2);
135  observables = getObservables();
136 
137  std::vector<unsigned int> pindices;
138  pindices.push_back(func1->getFunctionIndex());
139  pindices.push_back(func1->getParameterIndex());
140  pindices.push_back(registerParameter(frac1));
141 
142  pindices.push_back(func2->getFunctionIndex());
143  pindices.push_back(func2->getParameterIndex());
144 
145  GET_FUNCTION_ADDR(ptr_to_AddPdfs);
146 
147  initialize(pindices);
148 }
149 
150 __host__ fptype AddPdf::normalize() const {
151  // if (cpuDebug & 1) std::cout << "Normalising AddPdf " << getName() << std::endl;
152 
153  fptype ret = 0;
154  fptype totalWeight = 0;
155 
156  for(unsigned int i = 0; i < components.size() - 1; ++i) {
157  fptype weight = host_params[host_indices[parameters + 3 * (i + 1)]];
158  totalWeight += weight;
159  fptype curr = components[i]->normalize();
160  ret += curr * weight;
161  }
162 
163  fptype last = components.back()->normalize();
164 
165  if(extended) {
166  fptype lastWeight = host_params[host_indices[parameters + 3 * components.size()]];
167  totalWeight += lastWeight;
168  ret += last * lastWeight;
169  ret /= totalWeight;
170  } else {
171  ret += (1 - totalWeight) * last;
172  }
173 
174  host_normalisation[parameters] = 1.0;
175 
176  if(getSpecialMask() & PdfBase::ForceCommonNorm) {
177  // Want to normalize this as
178  // (f1 A + (1-f1) B) / int (f1 A + (1-f1) B)
179  // instead of default
180  // (f1 A / int A) + ((1-f1) B / int B).
181 
182  for(auto component : components) {
183  host_normalisation[component->getParameterIndex()] = (1.0 / ret);
184  }
185  }
186 
187  // if (cpuDebug & 1) std::cout << getName() << " integral returning " << ret << std::endl;
188  return ret;
189 }
190 
191 __host__ double AddPdf::sumOfNll(int numVars) const {
192  static thrust::plus<double> cudaPlus;
193  thrust::constant_iterator<int> eventSize(numVars);
194  thrust::constant_iterator<fptype *> arrayAddress(dev_event_array);
195  double dummy = 0;
196 
197  thrust::counting_iterator<int> eventIndex(0);
198 
199  double ret;
200 #ifdef GOOFIT_MPI
201 #if THRUST_DEVICE_SYSTEM == THRUST_DEVICE_SYSTEM_CUDA
202  goofit_policy my_policy;
203  double r = thrust::transform_reduce(
204  my_policy,
205  thrust::make_zip_iterator(thrust::make_tuple(eventIndex, arrayAddress, eventSize)),
206  thrust::make_zip_iterator(thrust::make_tuple(eventIndex + m_iEventsPerTask, arrayAddress, eventSize)),
207  *logger,
208  dummy,
209  cudaPlus);
210 #else
211  double r = thrust::transform_reduce(
212  thrust::make_zip_iterator(thrust::make_tuple(eventIndex, arrayAddress, eventSize)),
213  thrust::make_zip_iterator(thrust::make_tuple(eventIndex + m_iEventsPerTask, arrayAddress, eventSize)),
214  *logger,
215  dummy,
216  cudaPlus);
217 #endif
218 
219  MPI_Allreduce(&r, &ret, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD);
220 #else
221 #if THRUST_DEVICE_SYSTEM == THRUST_DEVICE_SYSTEM_CUDA
222  goofit_policy my_policy;
223  ret = thrust::transform_reduce(
224  my_policy,
225  thrust::make_zip_iterator(thrust::make_tuple(eventIndex, arrayAddress, eventSize)),
226  thrust::make_zip_iterator(thrust::make_tuple(eventIndex + numEntries, arrayAddress, eventSize)),
227  *logger,
228  dummy,
229  cudaPlus);
230 #else
231  ret = thrust::transform_reduce(
232  thrust::make_zip_iterator(thrust::make_tuple(eventIndex, arrayAddress, eventSize)),
233  thrust::make_zip_iterator(thrust::make_tuple(eventIndex + numEntries, arrayAddress, eventSize)),
234  *logger,
235  dummy,
236  cudaPlus);
237 #endif
238 #endif
239 
240  if(extended) {
241  fptype expEvents = 0;
242 
243  // std::cout << "Weights:";
244  for(unsigned int i = 0; i < components.size(); ++i) {
245  expEvents += host_params[host_indices[parameters + 3 * (i + 1)]];
246  // std::cout << " " << host_params[host_indices[parameters + 3*(i+1)]];
247  }
248 
249  // Log-likelihood of numEvents with expectation of exp is (-exp + numEvents*ln(exp) - ln(numEvents!)).
250  // The last is constant, so we drop it; and then multiply by minus one to get the negative log-likelihood.
251  ret += (expEvents - numEvents * log(expEvents));
252  // std::cout << " " << expEvents << " " << numEvents << " " << (expEvents - numEvents*log(expEvents)) <<
253  // std::endl;
254  }
255 
256  return ret;
257 }
258 } // namespace GooFit