Skip to content

Commit 87bb486

Browse files
authored
Merge 6bebdb8 into sapling-pr-archive-ktf
2 parents 5601ed2 + 6bebdb8 commit 87bb486

2 files changed

Lines changed: 110 additions & 24 deletions

File tree

Framework/Core/include/Framework/StepTHn.h

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ class StepTHn : public TNamed
5858
virtual Long64_t Merge(TCollection* list) = 0;
5959

6060
TAxis* GetAxis(int i) { return mPrototype->GetAxis(i); }
61-
void Sumw2(){}; // TODO: added for compatibiltiy with registry, but maybe it would be useful also in StepTHn as toggle for error weights
61+
void Sumw2() {}; // TODO: added for compatibiltiy with registry, but maybe it would be useful also in StepTHn as toggle for error weights
6262

6363
protected:
6464
void init();
@@ -67,6 +67,7 @@ class StepTHn : public TNamed
6767
void deleteContainers();
6868

6969
Long64_t getGlobalBinIndex(const Int_t* binIdx);
70+
virtual void updateBin(int iStep, Long64_t bin, double weight) = 0;
7071

7172
Long64_t mNBins; // number of total bins
7273
Int_t mNVars; // number of variables
@@ -81,6 +82,18 @@ class StepTHn : public TNamed
8182
Double_t* mLastVars; //! caching of last used bins (in many loops some vars are the same for a while)
8283
Int_t* mLastBins; //! caching of last used bins (in many loops some vars are the same for a while)
8384

85+
// Fast bin lookup table: for each axis, maps a quantized position to an approximate bin.
86+
static constexpr Int_t kLookupSize = 1024; // number of slots per axis
87+
struct AxisLookup {
88+
Double_t invSlotWidth; // 1.0 / slot width for fast index computation
89+
Double_t xmin; // axis minimum
90+
Double_t xmax; // axis maximum
91+
const Double_t* edges; // pointer to bin edges array (nBins+1 entries)
92+
Int_t nBins; // number of bins
93+
Int_t table[kLookupSize]; // slot -> bin index (1-based, TAxis convention)
94+
};
95+
AxisLookup* mLookup; //! per-axis lookup tables
96+
8497
THnSparse* mPrototype; // not filled used as prototype histogram for axis functionality etc.
8598

8699
ClassDef(StepTHn, 1) // THn like container
@@ -107,6 +120,28 @@ class StepTHnT : public StepTHn
107120
}
108121
}
109122

123+
void updateBin(int iStep, Long64_t bin, double weight) override
124+
{
125+
if (!mValues[iStep]) {
126+
mValues[iStep] = createArray();
127+
LOGF(info, "Created values container for step %d", iStep);
128+
}
129+
130+
if (weight != 1.) {
131+
if (!mSumw2[iStep]) {
132+
mSumw2[iStep] = createArray(mValues[iStep]);
133+
LOGF(info, "Created sumw2 container for step %d", iStep);
134+
}
135+
}
136+
137+
auto* arr = static_cast<TemplateArray*>(mValues[iStep])->GetArray();
138+
arr[bin] += weight;
139+
if (mSumw2[iStep]) {
140+
auto* sw2 = static_cast<TemplateArray*>(mSumw2[iStep])->GetArray();
141+
sw2[bin] += weight * weight;
142+
}
143+
}
144+
110145
ClassDef(StepTHnT, 1) // THn like container
111146
};
112147

Framework/Core/src/StepTHn.cxx

Lines changed: 74 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ StepTHn::StepTHn() : mNBins(0),
3939
mNbinsCache(nullptr),
4040
mLastVars(nullptr),
4141
mLastBins(nullptr),
42+
mLookup(nullptr),
4243
mPrototype(nullptr)
4344
{
4445
// Default constructor (for streaming)
@@ -55,6 +56,7 @@ StepTHn::StepTHn(const Char_t* name, const Char_t* title, const Int_t nSteps, co
5556
mNbinsCache(nullptr),
5657
mLastVars(nullptr),
5758
mLastBins(nullptr),
59+
mLookup(nullptr),
5860
mPrototype(nullptr)
5961
{
6062
// Constructor
@@ -130,6 +132,7 @@ StepTHn::StepTHn(const StepTHn& c) : mNBins(c.mNBins),
130132
mNbinsCache(nullptr),
131133
mLastVars(nullptr),
132134
mLastBins(nullptr),
135+
mLookup(nullptr),
133136
mPrototype(nullptr)
134137
{
135138
//
@@ -152,6 +155,7 @@ StepTHn::~StepTHn()
152155
delete[] mNbinsCache;
153156
delete[] mLastVars;
154157
delete[] mLastBins;
158+
delete[] mLookup;
155159
delete mPrototype;
156160
}
157161

@@ -392,13 +396,35 @@ void StepTHn::Fill(int iStep, int nParams, double positionAndWeight[])
392396
LOGF(fatal, "Fill called with invalid number of parameters (%d vs %d)", mNVars, nParams);
393397
}
394398

395-
// fill axis cache
399+
// fill axis cache and build lookup tables on first call
396400
if (!mAxisCache) {
397401
mAxisCache = new TAxis*[mNVars];
398402
mNbinsCache = new Int_t[mNVars];
403+
mLookup = new AxisLookup[mNVars];
399404
for (Int_t i = 0; i < mNVars; i++) {
400405
mAxisCache[i] = mPrototype->GetAxis(i);
401406
mNbinsCache[i] = mAxisCache[i]->GetNbins();
407+
408+
// Build lookup table for this axis
409+
auto& lut = mLookup[i];
410+
lut.nBins = mNbinsCache[i];
411+
lut.edges = mAxisCache[i]->GetXbins()->GetArray();
412+
lut.xmin = mAxisCache[i]->GetXmin();
413+
lut.xmax = mAxisCache[i]->GetXmax();
414+
415+
if (lut.edges && lut.xmax > lut.xmin) {
416+
// Variable-width binning: build slot->bin mapping
417+
Double_t slotWidth = (lut.xmax - lut.xmin) / kLookupSize;
418+
lut.invSlotWidth = 1.0 / slotWidth;
419+
for (Int_t s = 0; s < kLookupSize; s++) {
420+
Double_t x = lut.xmin + (s + 0.5) * slotWidth;
421+
lut.table[s] = mAxisCache[i]->FindBin(x);
422+
}
423+
} else {
424+
// Uniform binning or degenerate axis: direct formula, no table needed
425+
lut.edges = nullptr;
426+
lut.invSlotWidth = lut.xmax > lut.xmin ? lut.nBins / (lut.xmax - lut.xmin) : 0;
427+
}
402428
}
403429

404430
mLastVars = new Double_t[mNVars];
@@ -420,11 +446,54 @@ void StepTHn::Fill(int iStep, int nParams, double positionAndWeight[])
420446
if (mLastVars[i] == positionAndWeight[i]) {
421447
tmpBin = mLastBins[i];
422448
} else {
423-
tmpBin = mAxisCache[i]->FindBin(positionAndWeight[i]);
449+
const auto& lut = mLookup[i];
450+
Double_t x = positionAndWeight[i];
451+
452+
if (!lut.edges) {
453+
// Uniform binning: direct computation.
454+
// Use floor instead of truncation to match TAxis::FindBin rounding,
455+
// then verify against the exact edge to handle values that land
456+
// precisely on a bin boundary (where float arithmetic can round
457+
// either way).
458+
Double_t rawBin = (x - lut.xmin) * lut.invSlotWidth;
459+
tmpBin = 1 + static_cast<Int_t>(std::floor(rawBin));
460+
if (tmpBin >= 1 && tmpBin <= lut.nBins) {
461+
// Compute the exact upper edge of this bin the same way TAxis does:
462+
// edge = xmin + tmpBin * binWidth
463+
// If x is at or above it, move to the next bin.
464+
Double_t binWidth = (lut.xmax - lut.xmin) / lut.nBins;
465+
Double_t upperEdge = lut.xmin + tmpBin * binWidth;
466+
if (x >= upperEdge && tmpBin < lut.nBins) {
467+
tmpBin++;
468+
}
469+
}
470+
if (tmpBin < 1) {
471+
tmpBin = 0; // underflow
472+
} else if (tmpBin > lut.nBins) {
473+
tmpBin = lut.nBins + 1; // overflow
474+
}
475+
} else {
476+
// Variable-width binning: lookup table + refine
477+
Int_t slot = static_cast<Int_t>((x - lut.xmin) * lut.invSlotWidth);
478+
if (slot < 0 || slot >= kLookupSize) {
479+
tmpBin = (slot < 0) ? 0 : lut.nBins + 1; // under/overflow
480+
} else {
481+
tmpBin = lut.table[slot];
482+
// Refine: the lookup gives an approximate bin, check edges
483+
// Move left if x is below the lower edge of tmpBin
484+
while (tmpBin > 1 && x < lut.edges[tmpBin - 1]) {
485+
tmpBin--;
486+
}
487+
// Move right if x is at or above the upper edge of tmpBin
488+
while (tmpBin < lut.nBins && x >= lut.edges[tmpBin]) {
489+
tmpBin++;
490+
}
491+
}
492+
}
493+
424494
mLastBins[i] = tmpBin;
425-
mLastVars[i] = positionAndWeight[i];
495+
mLastVars[i] = x;
426496
}
427-
//Printf("%d", tmpBin);
428497

429498
// under/overflow not supported
430499
if (tmpBin < 1 || tmpBin > mNbinsCache[i]) {
@@ -433,27 +502,9 @@ void StepTHn::Fill(int iStep, int nParams, double positionAndWeight[])
433502

434503
// bins start from 0 here
435504
bin += tmpBin - 1;
436-
// Printf("%lld", bin);
437-
}
438-
439-
if (!mValues[iStep]) {
440-
mValues[iStep] = createArray();
441-
LOGF(info, "Created values container for step %d", iStep);
442505
}
443506

444-
if (weight != 1.) {
445-
// initialize with already filled entries (which have been filled with weight == 1), in this case mSumw2 := mValues
446-
if (!mSumw2[iStep]) {
447-
mSumw2[iStep] = createArray(mValues[iStep]);
448-
LOGF(info, "Created sumw2 container for step %d", iStep);
449-
}
450-
}
451-
452-
// TODO probably slow; add StepTHnT::add ?
453-
mValues[iStep]->SetAt(mValues[iStep]->GetAt(bin) + weight, bin);
454-
if (mSumw2[iStep]) {
455-
mSumw2[iStep]->SetAt(mSumw2[iStep]->GetAt(bin) + weight * weight, bin);
456-
}
507+
updateBin(iStep, bin, weight);
457508
}
458509

459510
template class StepTHnT<TArrayF>;

0 commit comments

Comments
 (0)