When generating toy data from RooMultiPdf with extended PDFs, the category proportions are not weighted by the yields of the component PDFs, whereas RooSimultaneous.generate() does produce category splits proportional to yields.
The reason seems to be that RooSimultaneous implements a specialized generator context (RooSimGenContext) that computes the relative category probabilities from the extended PDF yields and samples the category accordingly. RooMultiPdf does not override genContext()/autoGenContext(), so it falls back to the base RooAbsPdf::generate() behavior and the index category is treated as a fixed parameter rather than a sampled observable.
My question is whether this is intended or not. Either RooMultiPdf is deliberately not meant for category-aware generation, or the missing generator context is an oversight that could be addressed. I'd appreciate clarification on which of the two it is.
The example below reproduces the difference: with yields 100 and 200, RooSimultaneous gives a ~1:2 split while RooMultiPdf does not.
Reproducer
import ROOT
x = ROOT.RooRealVar("x","x",-10,10)
y = ROOT.RooRealVar("y","y",-10,10)
gauss = ROOT.RooGaussian("gauss","gauss", x, 0,1)
gauss1 = ROOT.RooGaussian("gauss1","gauss1", y, 0,1)
lam = ROOT.RooRealVar("lam","lam",-1.,-10.,0.)
exp = ROOT.RooExponential("exp","exp",y,lam)
cat = ROOT.RooCategory("cat","cat")
cat.defineType("gauss",0)
cat.defineType("gauss1",1)
nGauss = ROOT.RooRealVar("nGauss","expected events (gauss)",100,0,1e6)
nGauss1 = ROOT.RooRealVar("nGauss1","expected events (gauss)",200,0,1e6)
gaussExt = ROOT.RooExtendPdf("gaussExt","extended gauss",gauss,nGauss)
gaussExt1 = ROOT.RooExtendPdf("gaussExt1","extended gauss",gauss1,nGauss1)
simPdf = ROOT.RooSimultaneous("simPdf","simultaneous pdf",cat)
simPdf.addPdf(gaussExt,"gauss")
simPdf.addPdf(gaussExt1,"gauss1")
obs_sim = ROOT.RooArgSet(x,y,cat)
simData = simPdf.generate(obs_sim, 1_000)
pdfCat = ROOT.RooCategory("pdfCat","pdfCat")
pdfCat.defineType("gauss",0)
pdfCat.defineType("gauss1",1)
pdfList = ROOT.RooArgList(gaussExt, gaussExt1)
multiPdf = ROOT.RooMultiPdf("multiPdf","multi pdf",pdfCat,pdfList)
obs_multi = ROOT.RooArgSet(x,y,pdfCat)
multiData = multiPdf.generate(obs_multi, 1_000)
simSubData = simData.split(cat)
multiSubData = multiData.split(pdfCat)
print("x-mean, simData:",simSubData[0].to_pandas()["x"].mean())
print("x-mean, multiData:",multiSubData[0].to_pandas()["x"].mean())
print("y-mean, simData:",simSubData[1].to_pandas()["y"].mean())
print("y-mean, multiData:",multiSubData[1].to_pandas()["y"].mean())
n_simData = [ds.numEntries() for ds in simSubData]
n_simData = [n/sum(n_simData) for n in n_simData]
n_multiData = [ds.numEntries() for ds in multiSubData]
n_multiData = [n/sum(n_multiData) for n in n_multiData]
print(f"SimData split: {100*n_simData[0]:.2f}|{100*n_simData[1]:.2f} ")
print(f"MultiData split: {100*n_multiData[0]:.2f}|{100*n_multiData[1]:.2f} ")
Version
ROOT v6.41.01
Built for linuxarm64 on Jun 22 2026, 09:59:52
From heads/master@6bf5355c
With c++ (Ubuntu 13.3.0-6ubuntu2~24.04.1) 13.3.0 std201703
Binary directory: /usr/share/root/bin
build from source in Ubuntu 24.04 docker container (arm64) on macOS.
When generating toy data from RooMultiPdf with extended PDFs, the category proportions are not weighted by the yields of the component PDFs, whereas RooSimultaneous.generate() does produce category splits proportional to yields.
The reason seems to be that RooSimultaneous implements a specialized generator context (RooSimGenContext) that computes the relative category probabilities from the extended PDF yields and samples the category accordingly. RooMultiPdf does not override genContext()/autoGenContext(), so it falls back to the base RooAbsPdf::generate() behavior and the index category is treated as a fixed parameter rather than a sampled observable.
My question is whether this is intended or not. Either RooMultiPdf is deliberately not meant for category-aware generation, or the missing generator context is an oversight that could be addressed. I'd appreciate clarification on which of the two it is.
The example below reproduces the difference: with yields 100 and 200, RooSimultaneous gives a ~1:2 split while RooMultiPdf does not.
Reproducer
Version
build from source in Ubuntu 24.04 docker container (arm64) on macOS.