Repository navigation
Conversation
The importer only checked that `metadata.hs3_version` exists. It now also requires it to be a string, parses the leading `MAJOR.MINOR`, and warns if the document is newer than the version RooFit writes (or if the string cannot be parsed). The exported version remains a single constant. The parsed version is kept available to importers during the import via `RooJSONFactoryWSTool::hs3VersionInput()`. Assisted-by: claude-code:claude-opus-5-5
HS3 requires the names of all objects in a document to be unique. The importer built its function/distribution indices with `emplace`, so a duplicate silently resolved to the first definition. Duplicated names within or across `distributions`, `functions`, `data`, `domains` and `parameter_points` are now an error. RooFit itself named likelihoods after their analysis, and analyses without matching dataset after the pdf, so its own files violate this rule. On export, likelihoods are now called `<analysis>_likelihood`, and an analysis without dataset is named after its ModelConfig. On import, a name clash that involves `likelihoods` or `analyses` only triggers a warning, so that files written by older ROOT versions can still be read. This is unambiguous: analyses are not referenced by name, and likelihoods are only looked up from analyses. Assisted-by: claude-code:claude-opus-5-5
The importer required a domain named exactly `default_domain`, which is not part of the HS3 standard. If it is absent, variables are now created with the range of the first domain that mentions them (or RooFit's default if none does). Export still writes `default_domain`, and documents that contain it are imported exactly as before. Assisted-by: claude-code:claude-opus-5-5
A parameter listed in a parameter point that was not created by any distribution, function or domain used to be ignored silently. It is now created as a `RooRealVar`, constant unless a domain lists it (an explicit `const` flag takes precedence). A string `value` is accepted for categorical parameters if a `RooCategory` of that name exists; otherwise it is ignored with a warning instead of being read as a number. Assisted-by: claude-code:claude-opus-5-5
HS3 forbids `nbins` and `edges` on the axes of unbinned data, but the unbinned-data exporter used the shared binning writer. It now writes only `name`, `min` and `max` for each axis. The binning of the observables is still preserved in the domains. Assisted-by: claude-code:claude-opus-5-5
On export, if a ModelConfig has a snapshot, the analysis now references the parameter point of that snapshot as `init`. On import, the snapshot is re-created under the same name so that repeated round trips are stable, and an `init` that refers to a parameter point that doesn't exist is reported with a warning instead of being skipped silently. (All parameter points are imported before the analyses, so their order in the file does not matter.) Assisted-by: claude-code:claude-opus-5-5
* RooArgusBG is exported as `argus_dist`. `ARGUS_dist` is still imported, with a one-time deprecation warning. * RooBinSamplingPdf is exported as `binsampling_dist`, the key its importer was registered for, so it can be read back. `binsampling` is still imported, with a one-time deprecation warning. * Double-sided RooCrystalBall shapes are exported as `crystalball_dist`. The width is written as `sigma` if both sides share it, otherwise as `sigma_L`/`sigma_R`. The tails are always written as `alpha_L`/`n_L`/`alpha_R`/`n_R`, also when both sides share their parameters, because RooFit reads the symmetric `alpha`/`n` form back as a single-sided RooCBShape. On import, a `crystalball_dist` with the asymmetric tail or width fields creates a RooCrystalBall. Single-sided RooCrystalBall shapes and `crystalball_doublesided_dist` are unchanged. * `rate_extended_dist` uses the HS3 fields `rate` and `dist`. `norm` and `pdf` are still accepted on import. RooFormulaVar and RooGenericPdf were already exported as `generic` and `generic_dist`, with `generic_function` still importable, so they needed no change. Assisted-by: claude-code:claude-opus-5-5
HS3 expresses conditionals as `ifelse(condition, a, b)`, but RooFit wrote the C-style ternary `c ? a : b` that TFormula uses. The generic formula exporter now rewrites ternaries at any nesting level into `ifelse()`. If an expression can't be rewritten safely, a warning is printed and the expression is exported unchanged. TFormula doesn't understand `ifelse()`, so on import it is translated back into a parenthesized ternary. Without this, RooFit could not read its own output. Expressions without ternaries are exported unchanged. Assisted-by: claude-code:claude-opus-5-5
The `extended` field of `mixture_dist` was ignored on import; the RooAddPdf derived its extended mode from the number of coefficients only. If the flag is present, it is now checked against the coefficient count: * `extended: true` with one coefficient fewer than summands is an error (HS3 requires such a mixture to be non-extended); * `extended: false` without coefficients is an error (RooFit would build an extended sum of extended distributions); * `extended: false` with one coefficient per summand is valid HS3, but a RooAddPdf cannot be forced to be non-extendable, so a warning advises not to use the extended term when fitting. Assisted-by: claude-code:claude-opus-5-5
…factors * HS3 spells the log-normal constraint type `LogNormal`. It is now recognized as a constraint type like the older RooFit spelling `Lognormal`, which is kept as an alias. * For a `normfactor` that has both `name` and `parameter`, the RooRealVar was named after `name`, breaking the correlation of normfactors that share a `parameter` but have different labels. The parameter is now named after `parameter`; `name` is only the label of the modifier. Assisted-by: claude-code:claude-opus-5-5
…trained In HS3, a modifier without `constraint` is unconstrained. RooFit instead created a unit Gaussian constraint for `normsys` and `histosys` modifiers that had none of `constraint`, `constraint_name` or `constraint_type`. Now no constraint is created and an info message is printed. The `nom_*` global observable is only created for constrained modifiers. On export, a non-constant normsys/histosys parameter without a recognized constraint used to be an error. Such a modifier is now exported without `constraint`, as long as no other constraint term of the channel depends on its parameter (that case is still an error, because the constraint is not supported). This keeps RooFit able to write the models it reads. The rf515_hfJSON tutorial relied on the implicit Gaussian constraints; its normsys modifiers now request them with `"constraint": "Gauss"`, so the tutorial model is unchanged. For the same reason, the "Constraint" case of the HistFactoryIncompatibleDuplicateModifiers test now requests its two different constraint terms explicitly. Assisted-by: claude-code:claude-opus-5-5
… modifier HS3 gives the constraint type of a `staterror` on the modifier (`Poisson` or `Gauss`), but RooFit ignored it and only read the private channel-level field `histfactory_dist.staterror.constraint_type`, defaulting to Poisson. The export wrote neither, so the choice between Gaussian and Poisson stat constraints was lost. On import, the `constraint` of the staterror modifier is now used, with the channel-level field as fallback. On export, the constraint type is written both on the staterror modifiers and on the channel level, so that the files can still be read by older ROOT versions. Assisted-by: claude-code:claude-opus-5-5
…>= 0.3 HS3 requires `default_interpolation` if a modifier has no `interpolation`, and forbids implementation-specific defaults. RooFit silently fell back to code 4. Documents with `hs3_version` 0.3 or newer that rely on this are now rejected. ROOT 6.38 and earlier never wrote the interpolation of histosys modifiers (or of normsys modifiers with the default code 4), nor a `default_interpolation`. To keep these files readable, documents with an older `hs3_version` (and objects imported without a document, e.g. with `importJSONElement()`) still get code 4, with one deprecation warning per channel. The export already writes `default_interpolation` whenever a modifier omits `interpolation`, so RooFit doesn't produce such documents. The rf515_hfJSON tutorial now states its interpolation explicitly with a `default_interpolation`, which avoids the deprecation warning. Assisted-by: claude-code:claude-opus-5-5
Malformed input could be read out of bounds: * the rows of `multivariate_normal_dist` covariance and correlation matrices are now checked to form a square matrix of the right size; * the `errors` of binned data must have as many entries as `contents`; * the `edges` of binned axes must contain at least two strictly ascending values. Assisted-by: claude-code:claude-opus-5-5
The `min`/`max` of the axes of unbinned data were ignored on import; the observables kept whatever range they had, and entries outside of it were clipped. If an observable got no range from a domain, its range is now set from the axis. If a domain range disagrees with the axis, it is kept and a warning is printed. Assisted-by: claude-code:claude-opus-5-5
* The export keys and factory expressions are no longer central JSON files in `etc/`, but catalogs embedded in `roofit/hs3/src/RooFitHS3_wsexportkeys.cxx` and `roofit/hs3/src/RooFitHS3_wsfactoryexpressions.cxx`. Point to them, and explain how to add entries there or load additional catalogs at runtime. * Describe `importArg()` as the function to override, with `importFunction()`/`importPdf()` as compatibility shims. * Register importers and exporters with the template helpers or with `std::unique_ptr`, as the current API requires, instead of raw `new`. Assisted-by: claude-code:claude-opus-5-5
HS3 PR "json-fixes-0.3" defines `hs3_version` as a `"MAJOR.MINOR"` string and the version of the revised standard as `"0.3"`. RooFit now writes this version. Documents with a newer version are imported with a warning. This commit depends on the HS3 PR and should only be merged together with or after it. Assisted-by: claude-code:claude-opus-5-5
HS3 PR "json-fixes-0.3" renames the analysis keys to `parameters_of_interest` and `domains` (a list of domain names), which is what RooFit already writes, including one entry each for the nuisance parameter, global observable and parameter-of-interest domains. The keys of HS3 v0.2.9, `parameter_of_interest` and `domain` (a single name), are now also read, with a deprecation warning. The plural keys take precedence. Assisted-by: claude-code:claude-opus-5-5
…f writing null RooFit wrote infinite bounds of product-domain axes as JSON `null`, which HS3 doesn't allow. HS3 PR "json-fixes-0.3" makes `min` and `max` of domain axes optional, with an absent bound meaning an unbounded range. Infinite bounds are now omitted on export. Since that PR also requires both bounds for a binning, the default binning is only written for bounded axes. Axes without any bound are still written (with only their `name`), which the HS3 PR keeps valid. RooFit needs them to tell an unbounded RooRealVar from a RooConstVar. On import, an absent bound means -inf/+inf, and `null` is still accepted for files written by older ROOT versions. The RooGaussianConstVarSigmaExport and ProductDomainEntriesExportExplicitBounds tests are updated to expect omitted instead of null bounds. Assisted-by: claude-code:claude-opus-5-5
…eference constraints by name HS3 PR root-project#124 replaces the `parameter` and `constraint` (a constraint type like `Gauss`) of HistFactory modifiers with the lists `parameters` and `constraints`. Each entry of `constraints` names a distribution in the top-level `distributions`, and `null` leaves a parameter unconstrained. `shapesys` loses its `data.vals`, since the uncertainties are encoded in the constraint distributions. HS3 PR root-project#129 states that the constraint terms belong to the prediction, which is how RooFit already builds the model. Import: * `parameters` and `constraints` are read for all modifiers, and take precedence over the legacy `parameter`, `constraint`, `constraint_name` and `constraint_type`, which are still read for older files. * For `staterror`, the named constraint distributions are used instead of creating Poisson terms. Without any constraint information, the gammas are unconstrained in documents with `hs3_version` >= 0.3; older documents still get the Poisson constraints that RooFit used to create implicitly. * The `nom_*` global observable is now only created when RooFit builds a constraint itself (legacy `Gauss`). Export: * All modifiers write `parameters` and, if any parameter is constrained, `constraints` with the names of the constraint distributions, which are exported alongside. `parameter`, `constraint` and `shapesys` `data.vals` are no longer written. * The RooPoisson (or RooGaussian) constraint terms of the staterror gammas are exported as named `poisson_dist` (or `gaussian_dist`) and referenced from the staterror modifiers. root-project#124 recommends a `barlow_beeston_lite_poisson_constraint_dist`, which RooFit doesn't support yet; any distribution type is allowed. * The channel-level `staterror.constraint_type` and the `constraint` type on staterror modifiers (written since the previous HistFactory commits) are no longer written, but still read. Tests that checked the exported `constraint` key are updated to the new form, and a new test covers import and export by name. This commit depends on HS3 PRs root-project#124 and root-project#129 and should only be merged together with or after them. Assisted-by: claude-code:claude-opus-5-5
…max on irregular binned axes RooFit wrote only `edges` for binned axes with an irregular binning. HS3 PR root-project#124 requires `name`, `min`, `max` and exactly one of `nbins` or `edges` on the axes of a `histfactory_dist`, while HS3 PR root-project#135 makes `min`/`max` optional next to `edges`, but allows them if they equal the first and last edge. Writing them satisfies both, so binned axes written via `exportAxis()` (binned data, histograms, ParamHistFunc and HistFactory axes) now always carry `min` and `max`. The `axes` of generic formulas are unchanged, because their reader requires either `edges` or `min`/`max`/`nbins`. Assisted-by: claude-code:claude-opus-5-5
… named constraints in rf515_hfJSON Write the HistFactory modifiers of the rf515_hfJSON tutorial in the form of HS3 PR root-project#124: every modifier names its parameters in `parameters`, and its constraint distributions by name in `constraints`. The constraint terms that RooFit used to create implicitly are now explicit: * unit Gaussians (mean 0, sigma 1) for the normsys parameters, as recommended by root-project#124; * Poisson terms for the two staterror gammas, with the tau values (400 and 100) that follow from the sample errors. root-project#124 recommends a `barlow_beeston_lite_poisson_constraint_dist`, which RooFit doesn't support yet, so `poisson_dist` is used. The gammas are declared in a domain and in the default parameter point, so that the functions referring to them can be imported. The document now declares `hs3_version` 0.3. The likelihood is the same as before up to an additive constant of 1.3e-5 (checked at several parameter points), and the fit result agrees within the minimizer tolerance. Assisted-by: claude-code:claude-opus-5-5
Test Results 23 files 23 suites 3d 22h 31m 33s ⏱️ For more details on these failures, see this check. Results for commit 625d361. |
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This Pull request:
Brings RooFit's HS3 import/export closer to the HS3 standard (
hep-statistics-serialization-standardmasteratcb3e9eb, v0.2.9 → v0.3) with small, independent fixes: renamed type keys, added validation andwarnings, and changed defaults. It also moves HistFactory constraints to the form of HS3 PRs
#124 and #129, which this PR assumes to be merged: constraint distributions
referenced by name. Larger structural items (categorical axes, the expression language, the new
histfactoryfunction of #129) are not touched here; they will be tracked in a separate ROOT issue (to be opened).
Background: the HS3–RooFit compatibility audit (October 2026)
of RooFit's HS3 layer against HS3
cb3e9eb(findings also tracked in HS3 issues #95 "Inconsistencies between ROOT HS3 export and spec",#99 "Potential spec deviations in HS3TestSuite" and #133 "Checklist: RooFit HS3
types/fields not yet in the standard"). Related ROOT PR: #23839
(pinned HS3TestSuite with an expected-failure list).
The PR has three groups of commits:
[after HS3 json-fixes]) follows HS3 PR#135 "Small normative JSON fixes for v0.3" (branch
json-fixes-0.3).[after HS3 #124/#129]) follows HS3 PRs #124"Histfactory Specfication Sync" and #129 "Move HistFactory to an appendix and add the
histfactory function".
If the HS3 PRs are delayed, Group I (or Groups I and II) can be merged on their own.
Each commit builds on its own and passes
testRooFitHS3andtestJSONInterface.Pre-flight findings
Behaviour on
masterbefore this PR:exportChannel()inJSONFactories_HistFactory.cxx): RooFitwrites
"constraint": "<name of the constraint distribution>"(e.g."alpha_syst1Constraint") and exports thatdistribution separately. It does not write
constraint_name/constraint_type(ROOT 6.38 still wroteconstraint_name). On import,constraintis first resolved as a distribution name and then as a legacy type(
Gauss, ...). Groups I and II keep this (Group I only adds thestaterrorconstraint type of the currentstandard); Group III replaces it by the
parameters/constraintslists of HS3 Add support for abi-tags (adapted from Fedora's patch) #124.Domains.cxx,ProductDomain::writeJSON()): RooFit writes infinite bounds as JSONnull. The axis itself is always written; there is no "skip the axis entirely" shortcut. Group II item 3therefore changes
nullto an omitted key.Changes or fixes:
Group I: independent of pending HS3 changes
RooJSONFactoryWSTool.cxx,JSONIOUtils.cxxhs3_versionmust be a string. ParseMAJOR.MINOR, and warn if it is newer than the supported version or can't be parsed. The export value stays a single constant. The version of the document being imported is available viaRooJSONFactoryWSTool::hs3VersionInput().VersionStringHandlingRooJSONFactoryWSTool.cxxdistributions,functions,data,domains,parameter_points. A clash involvinglikelihoodsoranalysesonly warns (legacy ROOT files, see below). Export: likelihoods are named<analysis>_likelihood, and an analysis without dataset is named after its ModelConfig.DuplicateNamesAreRejecteddefault_domainoptionalDomains.cxxdefault_domain, variables take the range of the first domain that mentions them. Still written on export.DefaultDomainIsOptionalRooJSONFactoryWSTool.cxxRooRealVar, constant unless a domain lists it (an explicitconstwins). Stringvaluesets aRooCategoryof that name; otherwise it is ignored with a warning.ParametersOnlyInParameterPointsRooJSONFactoryWSTool.cxxname/min/max.UnbinnedDatasetAxesHaveNoBinninginitin analysesRooJSONFactoryWSTool.cxxanalyses[].init. Import re-creates it under the same name (stable round trips), and warns if the parameter point doesn't exist.AnalysisInitRoundTripJSONFactories_RooFitCore.cxx,RooFitHS3_wsexportkeys.cxx,RooFitHS3_wsfactoryexpressions.cxxargus_dist(ARGUS_distdeprecated alias);binsampling_distfor export and import (binsamplingdeprecated alias); double-sidedRooCrystalBall→crystalball_dist;rate_extended_distusesrate/dist(norm/pdfaccepted).generic/generic_distwere already conformant.TypeKeysMatchTheCatalogifelseJSONFactories_RooFitCore.cxxc ? a : b(nested, in parentheses) toifelse(c,a,b); it warns and leaves the expression unchanged if that isn't safely possible. Also translatesifelseback on import, because TFormula doesn't understand it.TernaryIsExportedAsIfElsemixture_dist.extendedJSONFactories_RooFitCore.cxxextendedagainst the coefficient count; throw on contradictions.MixtureDistExtendedFlagLogNormal, normfactorparameterJSONFactories_HistFactory.cxxLogNormal(keepLognormal). The normfactor parameter is named afterparameter;nameis the label only.HistFactoryNormFactorParameterAndLogNormalJSONFactories_HistFactory.cxxnormsys/histosyswithout a constraint key are unconstrained (info message). Export accepts unconstrained modifiers instead of throwing "cannot find constraint".HistFactoryModifierWithoutConstraintIsUnconstrainedstaterrorconstraintJSONFactories_HistFactory.cxxconstraint(Poisson/Gauss) from the staterror modifier, falling back to the channel-levelstaterror.constraint_type. Export writes both. (Superseded on export by Group III; still read.)HistFactoryStatErrorConstraintJSONFactories_HistFactory.cxxinterpolationand there is nodefault_interpolation, forhs3_version≥ 0.3. Older documents keep code 4, with one deprecation warning per channel. Export already always writesdefault_interpolationwhen needed (verified, unchanged).HistFactoryMissingInterpolationJSONFactories_RooFitCore.cxx,RooJSONFactoryWSTool.cxx,JSONIOUtils.cxxerrorslength =contentslength;edgeshas ≥ 2 strictly ascending values.MalformedArraysAreRejectedRooJSONFactoryWSTool.cxxmin/maxfrom the axis; a disagreeing domain range is kept with a warning.UnbinnedAxisRangesOnImportroofit/doc/developers/roofit_hs3.mdetc/*.json;importArg()as primary API;std::unique_ptr/ template registration. The snippets were compile-checked against the current headers.Deviations from the original plan, and the reasons:
pdf. Throwing on these would make every file written by an existing ROOT release unreadable. Analyses aren't
referenced by name, and likelihoods are only looked up from analyses, so these clashes only warn. New exports
don't produce them.
alpha,n) is what RooFit already uses for thesingle-sided
RooCBShape, and RooFit imports it back asRooCBShape. A symmetricRooCrystalBallistherefore always exported with
alpha_L/n_L/alpha_R/n_R, both sides pointing to the same parameter. Thisis valid HS3 and round-trips exactly. The width is written as
sigmaif shared, otherwise assigma_L/sigma_R. Single-sidedRooCrystalBallcan't be expressed ascrystalball_distand keeps theprevious export (
crystalball_doublesided_dist).ifelse→ ternary translation on import was added, since otherwise RooFit couldn't read its ownoutput.
RooAddPdfcan't be forced to be non-extendable.extended: falsewith one coefficient per summand(valid HS3) therefore only warns; the shape is the same.
histosysmodifiers (or ofnormsysmodifiers withcode 4), nor
default_interpolation. Throwing unconditionally would make all HistFactory JSON from released ROOTversions unreadable, so the strict behaviour is gated on
hs3_version≥ 0.3. Objects imported without adocument (
importJSONElement) are treated as legacy.combined-data path), so no
categoriesbranch was added.Group II: after HS3 PR #135
Checked against HS3 PR #135 at head
0e5d5a3(2026-10-26); the only change after the originally checkedbebff05is a reformatting of the Crystal Ball section. Re-check this table if the PR changes before merging.hs3_version: "0.3".parameters_of_interestanddomains(with NP/GO/POI domains as entries). Now also import the legacyparameter_of_interestanddomain(a string), with a deprecation warning. Test:AnalysisLegacySingularKeys.min/maxof domain axes instead of writingnull, and write a default binning only for bounded axes (the HS3 PR requires both bounds fornbins). Name-only axes are kept (valid per the HS3 PR); RooFit needs them to tell aRooRealVarfrom aRooConstVar.nullis still read.min/maxoptional withedges.errorserrorsoptional.Group III: after HS3 PRs #124 and #129
Checked against #124 at head
bc5f670and #129 at heade76fc4a.JSONFactories_HistFactory.cxxparametersandconstraints(names of distributions,null= unconstrained) for all modifiers; they take precedence over the legacyparameter,constraint,constraint_nameandconstraint_type, which are still read.staterroruses the named constraints instead of creating Poisson terms; without any constraint information its gammas are unconstrained forhs3_version≥ 0.3 (older documents get the implicit Poisson terms as before). Export writesparameters/constraintsand exports the constraint distributions; the staterror RooPoisson terms become namedpoisson_dist.parameter,constraint,shapesysdata.valsand the channel-levelstaterrorare no longer written.HistFactoryConstraintsByName, updatedHistFactoryStatErrorConstraint,HistFactoryConstraintKeyMigration,HistFactoryLegacyConstraintTypeInConstraintKey,HistFactoryModifierWithoutConstraintIsUnconstrainedRooJSONFactoryWSTool.cxxexportAxis()always carrymin/max, also withedges. #124 requires them onhistfactory_distaxes; #135 allows them if they equal the first and last edge.IrregularBinnedAxesHaveRangetutorials/roofit/roofit/rf515_hfJSON.jsonparameters/constraints; the formerly implicit constraints are explicitgaussian_dist(mean 0, sigma 1) andpoisson_dist(staterror, tau 400/100) entries;hs3_version0.3.Notes on Group III:
histfactoryfunction (TMVA: fixes in testsuite #129): not implemented. It is a new primitive; it's left for a follow-up (structuralissue). TMVA: fixes in testsuite #129's statement that the constraint terms belong to the prediction is how RooFit already builds
HistFactory models.
barlow_beeston_lite_poisson_constraint_dist, which RooFit can'tread or write yet. Since Add support for abi-tags (adapted from Fedora's patch) #124 allows any distribution type, the existing RooPoisson terms are exported as
poisson_dist.name: Add support for abi-tags (adapted from Fedora's patch) #124 doesn't list anamefor modifiers. RooFit still writes and reads it as a label.min/maxonhistfactory_distaxes, Do not allow short reads. #135 makes them optional next toedges. Group III item 2 writes them, which is valid under both; the HS3 PRs should be reconciled.its own; Group III replaces its export.
Behaviour changes users may notice
normsys/histosyswithoutconstraintare no longer silently Gaussian-constrained. Hand-written filesthat relied on this must add
"constraint": "Gauss"(Group I) or, in the form of HS3 Add support for abi-tags (adapted from Fedora's patch) #124, reference aconstraint distribution in
constraints(Group III). Therf515_hfJSONtutorial JSON was updated accordingly.After Group I its fit result is identical to
master(min NLL −1043.45938119, same parameters). After Group III(explicit, named constraints) its likelihood equals the old one up to an additive constant of 1.3e-5, checked at
several parameter points; the fit agrees within the minimizer tolerance (
mu1.10955 vs. 1.10947).hs3_version≥ 0.3. Older documents get a deprecationwarning and the old code-4 default.
ARGUS_dist→argus_dist,binsampling→binsampling_dist,crystalball_doublesided_dist→crystalball_dist(double-sided),rate_extended_distfieldspdf/norm→dist/rate.<analysis>_likelihood; analyses without dataset are named after theModelConfig.
errorsor invalidedgesare now rejected.ifelse()instead of?:.hs3_versionis"0.3", and infinite domain bounds are omitted instead ofnull.parameters/constraintsand named constraintdistributions (including
poisson_distterms forstaterror);shapesysdata.valsand the channel-levelstaterrorare gone. In documents withhs3_version≥ 0.3, astaterrorwithoutconstraintsis unconstrained.Binned axes with
edgesalso carrymin/max.Changed tests and reference data
No reference JSON files exist for these tests; expectations are inline. The following existing expectations or
fixtures were changed because of intended behaviour changes:
HistFactoryIncompatibleDuplicateModifiers/Constraint: the two modifiers now request"constraint": "Gauss"explicitly, because the test needs two different constraint terms (previously these were implicit).
RooGaussianConstVarSigmaExport,ProductDomainEntriesExportExplicitBounds(Group II.3): expect omittedinstead of
nullinfinite bounds. The second test now also checks thatnullis still read.tutorials/roofit/roofit/rf515_hfJSON.json: explicit"constraint": "Gauss"on the threenormsys, and adefault_interpolation(Group I); rewritten with named constraint distributions (Group III).HistFactoryConstraintKeyMigration,HistFactoryLegacyConstraintTypeInConstraintKey,HistFactoryModifierWithoutConstraintIsUnconstrained,HistFactoryStatErrorConstraint(Group III): expect theexported
constraintslist instead ofconstraint.HistFactory round trip
roofit/histfactory/test/ref_6.16_example_UsingC_combined_meas_model.root(workspacecombined), exported withmasterand with this branch. Every changed key:analyses[simPdf_obsData].likelihood:"simPdf_obsData"→"simPdf_obsData_likelihood"(same forsimPdf_asimovData)likelihoods[simPdf_obsData]→likelihoods[simPdf_obsData_likelihood](same forsimPdf_asimovData),content unchanged
distributions[model_channel1].samples[background1_…].modifiers[staterror].constraint: absent →"Poisson"(same for
background2_…)distributions[model_channel1].staterror.constraint_type: absent →"Poisson"metadata.hs3_version:"0.2"→"0.3"parameter→parameters(list) andconstraint→constraints(list ofnames; for
staterrorthe names of the gamma constraints instead of"Poisson"); the channel-levelstaterror.constraint_typeis gone; new distributionsgamma_stat_channel1_bin_{0,1}_constraint(
poisson_dist), functionsgamma_stat_channel1_bin_{0,1}_poisMean(product) and their constant parametersgamma_stat_channel1_bin_{0,1}_tauandnom_gamma_stat_channel1_bin_{0,1}.A second export after re-importing differs from the first in the same keys on
masterand on this branch(legacy
binWidth_*variables of the 6.16 workspace,misc.minimization.parameter_stepwidths). That ispre-existing and not touched here.
Checklist:
Tests run (Linux, GCC 13, RelWithDebInfo,
-Droofit=ON -Dtesting=ON):ctest -R 'RooFitHS3|JSONInterface': baseline 71 + 1 tests pass; with this PR 89 + 1 tests pass (18 new). Thetwo test binaries were also built and run for every single commit.
Not run: the PyROOT tests (
bindings/pyroot/pythonizations/test/roofit.py, which uses HistFactory JSON viaimportJSONElement), therf515_hfJSON.pytutorial itself (its model was fitted from C++ instead), and theoptional HS3 test suite (
test_roofit_hs3testsuite). My build haspyroot=OFF.AI assistance: Claude Code (claude-opus-5-5) implemented the changes, the tests and this description from a
written plan, under review of the author. The commits carry
Assisted-by: claude-code:claude-opus-5-5.🤖 Generated with Claude Code