Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 2 additions & 5 deletions roofit/xroofit/inc/RooFit/xRooFit/xRooHypoSpace.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,8 @@

BEGIN_XROOFIT_NAMESPACE

class xRooHypoSpace : public xRooNLLVar::xRooHypoSpace {
public:
using xRooNLLVar::xRooHypoSpace::xRooHypoSpace;
ClassDefOverride(xRooHypoSpace, 0)
};
// xRooHypoSpace class is now defined in xRooNLLVar.h,
// to avoid incomplete type errors in an #include "xRooNLLVar.h" call

END_XROOFIT_NAMESPACE

Expand Down
9 changes: 8 additions & 1 deletion roofit/xroofit/inc/RooFit/xRooFit/xRooNLLVar.h
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,7 @@ class xRooNLLVar : public std::shared_ptr<RooAbsReal> {
xValueWithError pX_toys(bool alt, double nSigma = std::numeric_limits<double>::quiet_NaN());
size_t addToys(bool alt, int nToys, int initialSeed = 0, double target = std::numeric_limits<double>::quiet_NaN(),
double target_nSigma = std::numeric_limits<double>::quiet_NaN(), bool targetCLs = false,
double relErrThreshold = 2., size_t maxToys = 10000);
double relErrThreshold = 2., size_t maxToys = -1);
};

// use alt_value = nan to skip the asimov calculations
Expand Down Expand Up @@ -512,6 +512,13 @@ class xRooNLLVar : public std::shared_ptr<RooAbsReal> {
bool kReuseNLL = true;
};

// define xRooHypoSpace class here, since inherits from above so need declaration
class xRooHypoSpace : public xRooNLLVar::xRooHypoSpace {
public:
using xRooNLLVar::xRooHypoSpace::xRooHypoSpace;
ClassDefOverride(xRooHypoSpace, 0)
};

END_XROOFIT_NAMESPACE


Expand Down
45 changes: 42 additions & 3 deletions roofit/xroofit/src/xRooFit.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -362,7 +362,12 @@ xRooFit::generateFrom(RooAbsPdf &pdf, const RooFitResult &_fr, bool expected, in
std::abs(boundaries.back() - rr) > 1e-5 * boundaries.back())
boundaries.push_back(rr);
} // sometimes get virtual duplicates of boundaries
r->setBinning(RooBinning(boundaries.size() - 1, &boundaries[0]));

// only replace the binning if necessary, i.e. if binning changed ...
// this ensures we don't replace a RooUniformBinning with a RooBinning, even if should be compatible
if(boundaries.size() < res->size()) {
r->setBinning(RooBinning(boundaries.size() - 1, &boundaries[0]));
}
delete res;
} else if (r->numBins(r->getBinning().GetName()) == 0 && expected) {
// no bins ... in order to generate expected we need to have some bins
Expand Down Expand Up @@ -482,6 +487,10 @@ std::shared_ptr<RooLinkedList> xRooFit::defaultNLLOptions()
delete l;
});
sDefaultNLLOptions->Add(RooFit::Offset().Clone());
#if ROOT_VERSION_CODE < ROOT_VERSION(6, 41, 00)
// disable const-optimization at the construction step ... can happen in the minimization though
sDefaultNLLOptions->Add(RooFit::Optimize(0).Clone());
#endif
return sDefaultNLLOptions;
}

Expand Down Expand Up @@ -512,6 +521,10 @@ std::shared_ptr<ROOT::Fit::FitConfig> xRooFit::defaultFitConfig()
fitConfig.MinimizerOptions().SetExtraOptions(ROOT::Math::GenAlgoOptions());
// have to const cast to set extra options
auto extraOpts = const_cast<ROOT::Math::IOptions *>(fitConfig.MinimizerOptions().ExtraOptions());
#if ROOT_VERSION_CODE < ROOT_VERSION(6, 41, 00)
extraOpts->SetValue("OptimizeConst", 2); // if 0 will disable constant term optimization and cache-and-track of the
// NLL. 1 = just caching, 2 = cache and track
#endif
#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 29, 00)
extraOpts->SetValue(
"StrategySequence",
Expand Down Expand Up @@ -619,7 +632,12 @@ class ProgressMonitor : public RooAbsReal {
{
fFunc->printMultiline(os, contents, verbose, indent);
}

#if ROOT_VERSION_CODE < ROOT_VERSION(6, 41, 00)
void constOptimizeTestStatistic(ConstOpCode opcode, bool doAlsoTrackingOpt) override
{
fFunc->constOptimizeTestStatistic(opcode, doAlsoTrackingOpt);
}
#endif
double evaluate() const override
{
if (fInterrupt) {
Expand Down Expand Up @@ -1012,7 +1030,27 @@ std::shared_ptr<const RooFitResult> xRooFit::minimize(RooAbsReal &nll,
TString actualFirstMinimizer = _minimizer->fitter()->Config().MinimizerType();

int status = 0;

#if ROOT_VERSION_CODE < ROOT_VERSION(6, 41, 00)
int constOptimize = 2;
_minimizer->fitter()->Config().MinimizerOptions().ExtraOptions()->GetValue("OptimizeConst", constOptimize);
if (constOptimize) {
_minimizer->optimizeConst(constOptimize);
// for safety force a refresh of the cache (and tracking) in the nll
// DO NOT do a ConfigChange ... this is just a deactivate-reactivate of caching
// but it seems like doing this breaks the const optimization and function is badly behaved
// so once its turned on never turn it off.
// nll.constOptimizeTestStatistic(RooAbsArg::ConfigChange, constOptimize>1 /* do tracking too if >1 */); //
// trigger a re-evaluate of which nodes to cache-and-track
// the next line seems safe to do but wont bother doing it because not bothering with above
// need to understand why turning the cache off and on again breaks it??
// nll.constOptimizeTestStatistic(RooAbsArg::ValueChange, constOptimize>1); // update the cache values -- is
// this needed??
} else {
// disable const optimization
// warning - if the nll was previously activated then it seems like deactivating may break it.
nll.constOptimizeTestStatistic(RooAbsArg::DeActivate);
}
#endif
int sIdx = -1;
TString minim = _minimizer->fitter()->Config().MinimizerType();
TString algo = _minimizer->fitter()->Config().MinimizerAlgoType();
Expand Down Expand Up @@ -1303,6 +1341,7 @@ std::shared_ptr<const RooFitResult> xRooFit::minimize(RooAbsReal &nll,
}
}


// signal(SIGINT,gOldHandlerr);
out = std::unique_ptr<RooFitResult>{_minimizer->save(fitName, resultTitle)};

Expand Down
4 changes: 2 additions & 2 deletions roofit/xroofit/src/xRooFitVersion.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,5 @@

#pragma once

#define GIT_COMMIT_HASH "v0.0.4"
#define GIT_COMMIT_DATE "2026-06-19 16:32:00 +0100"
#define GIT_COMMIT_HASH "v0.0.4-29-g67d433e"
#define GIT_COMMIT_DATE "2026-10-23 11:19:53 +0100"
60 changes: 46 additions & 14 deletions roofit/xroofit/src/xRooHypoSpace.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -424,16 +424,18 @@ int xRooNLLVar::xRooHypoSpace::scan(const char *type, size_t nPoints, double low
}
}
} else if (sType.Contains("ts")) {
// add a point at the current poi value
// use that to determine ufit value and use uncertainty to set an approx range
AddPoint(TString::Format("%s=%g", poi().first()->GetName(), poi().getRealValue(poi().first()->GetName())));
graphs(sType); // triggers computation
if(size()==0) {
// add a point at the current poi value
// use that to determine ufit value and use uncertainty to set an approx range
AddPoint(TString::Format("%s=%g", poi().first()->GetName(), poi().getRealValue(poi().first()->GetName())));
graphs(sType); // triggers computation
}
if (back().status() != 0) {
out += 1;
} else {
// fit was ok, so use the values to determine an appropriate range
low = std::max(low, back().mu_hat().getVal() - back().mu_hat().getError() * 3);
high = std::min(high, back().mu_hat().getVal() + back().mu_hat().getError() * 3);
low = std::max(std::isnan(low) ? p->getMin() : low, back().mu_hat().getVal() - back().mu_hat().getError() * 3);
high = std::min(std::isnan(high) ? p->getMax() : high, back().mu_hat().getVal() + back().mu_hat().getError() * 3);
nPoints = 20;
double step = (high - low) / (nPoints - 1);
for (size_t i = 0; i < nPoints; i++) {
Expand Down Expand Up @@ -938,6 +940,27 @@ std::shared_ptr<TGraphErrors> xRooNLLVar::xRooHypoSpace::graph(
TStopwatch s;
s.Start();
size_t nDone = 0;

size_t nToys = 0;
size_t nToysAlt = 0;
if(sOpt.Contains("toys")) {
if (sOpt.Contains("toys=")) {
// extract number of toys required ... format is "nullToys.altToysFraction" if altToysFraction=0 then use
// same for both, unless explicitly set (i.e. N.0) then means we want no alt toys
// e.g. if doing just pnull significance
TString toyNum = sOpt(sOpt.Index("toys=") + 5, sOpt.Length());
nToys = toyNum.Atoi();
nToysAlt = (toyNum.Atof() - nToys) * nToys;
if (nToysAlt == 0 && !toyNum.Contains('.') && !sOpt.Contains("pnull"))
nToysAlt = nToys;
} else {
nToys = gEnv->GetValue("XRooFit.MaxToys",10000);
nToysAlt = 100;
// should really also set nToysAlt??
}
}


for (auto &p : *this) {
if (s.RealTime() > 5) {
if (visualize) {
Expand Down Expand Up @@ -967,7 +990,11 @@ std::shared_ptr<TGraphErrors> xRooNLLVar::xRooHypoSpace::graph(
auto pval = const_cast<xRooHypoPoint &>(p).getVal(sOpt);
auto idx = out->GetN() - nPointsDown;

if (std::isnan(pval.first)) {
double target=0.05; double relErrThreshold = 2;
double diff = (target < 0) ? pval.first : std::abs(pval.first - target);
// need logic that will treat points as bad where if running not readonly would get more toys

if (std::isnan(pval.first) || (pval.second != 0 && (p.nullToys.size() < nToys || p.altToys.size() < nToysAlt) && (sOpt.Contains("toys=") || (sOpt.Contains("toys") && !sOpt.Contains("toys=") && doCLs && pval.second > 1e-4 && diff <= relErrThreshold * pval.second))) ) {
if (p.status() != 0) { // if status is 0 then bad pval is really just absence of fits, not bad fits
badPoints()->SetPoint(badPoints()->GetN(), _x, 0);
}
Expand All @@ -980,7 +1007,8 @@ std::shared_ptr<TGraphErrors> xRooNLLVar::xRooHypoSpace::graph(
TString sOpt2 = sOpt;
sOpt2.ReplaceAll("exp", "exp-");
pval = const_cast<xRooHypoPoint &>(p).getVal(sOpt2);
if (std::isnan(pval.first)) {
diff = (target < 0) ? pval.first : std::abs(pval.first - target);
if (std::isnan(pval.first) || (pval.second != 0 && (p.nullToys.size() < nToys || p.altToys.size() < nToysAlt) && (sOpt.Contains("toys=") || (sOpt.Contains("toys") && !sOpt.Contains("toys=") && doCLs && pval.second > 1e-4 && diff <= relErrThreshold * pval.second))) ) {
if (p.status() != 0) { // if status is 0 then bad pval is really just absence of fits, not bad fits
badPoints()->SetPoint(badPoints()->GetN(), _x, 0);
}
Expand Down Expand Up @@ -1248,12 +1276,15 @@ xRooNLLVar::xValueWithError xRooNLLVar::xRooHypoSpace::GetLimit(const TGraph &pV
(!above && gr->GetPointY(gr->GetN() - 1) >= gr->GetPointY(0))) {
// extrapolating above based on last two points
// in fact, if 2nd last point is a p=1 (log(p)=0) then go back
// also go back if m <= 0
int offset = 2;
while (offset < gr->GetN() && gr->GetPointY(gr->GetN() - offset) == 0)
double x1,y1,m;
do {
x1 = gr->GetPointX(gr->GetN() - offset);
y1 = gr->GetPointY(gr->GetN() - offset);
m = (gr->GetPointY(gr->GetN() - 1) - y1) / (gr->GetPointX(gr->GetN() - 1) - x1);
offset++;
double x1 = gr->GetPointX(gr->GetN() - offset);
double y1 = gr->GetPointY(gr->GetN() - offset);
double m = (gr->GetPointY(gr->GetN() - 1) - y1) / (gr->GetPointX(gr->GetN() - 1) - x1);
} while(offset < gr->GetN() && (y1==0 || m<=0) );
if (m == 0.)
return std::pair(std::numeric_limits<double>::infinity(), std::numeric_limits<double>::infinity());
return std::pair((alpha - y1) / m + x1, std::numeric_limits<double>::infinity());
Expand Down Expand Up @@ -1287,7 +1318,8 @@ xRooNLLVar::xRooHypoSpace::findlimit(const char *opt, double relUncert, unsigned
sOpt.ReplaceAll("visualize", "");
std::shared_ptr<TGraphErrors> gr = graph(sOpt + " readonly");
if (visualize) {
auto gra = graphs(sOpt.Contains("toys") ? "pcls readonly toys" : "pcls readonly");
TString toyConf = sOpt.Contains("toys") ? sOpt(sOpt.Index("toys"),(sOpt.Index(" ",sOpt.Index("toys"))==-1 ? sOpt.Length() : sOpt.Index(" ",sOpt.Index("toys")))-sOpt.Index("toys")) : TString("");
auto gra = graphs(TString("pcls readonly") + toyConf);
if (gra) {
if (!gPad)
gra->Draw(); // in 6.28 DrawClone wont make the gPad defined :( ... so Draw then clear and Draw Clone
Expand Down Expand Up @@ -1324,7 +1356,7 @@ xRooNLLVar::xRooHypoSpace::findlimit(const char *opt, double relUncert, unsigned
if (!gr || gr->GetN() < 1) {
if (maxTries == 0 || std::isnan(AddPoint(TString::Format("%s=%g", v->GetName(), muMin)).getVal(sOpt).first)) {
// first point failed ... give up
::Error("findlimit", "Problem evaluating First Point %s @ %s=%g", sOpt.Data(), v->GetName(), muMin);
::Error("findlimit", "Problem evaluating First Point %s @ %s=%g (maxTries=%d)", sOpt.Data(), v->GetName(), muMin,maxTries);
return std::pair(std::numeric_limits<double>::quiet_NaN(), 0.);
}
gr.reset();
Expand Down
Loading
Loading