Skip to content

Commit 91328cd

Browse files
committed
[HS3] Fix export of self_normalized flag
1 parent f4bb828 commit 91328cd

2 files changed

Lines changed: 88 additions & 2 deletions

File tree

roofit/hs3/src/JSONFactories_RooFitCore.cxx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1016,7 +1016,11 @@ bool importWrapperPdf(RooJSONFactoryWSTool *tool, const JSONNode &node)
10161016

10171017
bool selfNormalized = false;
10181018

1019-
if (auto sn = node.find("selfNormalized"))
1019+
auto sn = node.find("self_normalized");
1020+
// ROOT previously exported this key without an underscore.
1021+
if (!sn)
1022+
sn = node.find("selfnormalized");
1023+
if (sn)
10201024
selfNormalized = sn->val_bool();
10211025

10221026
tool->wsEmplace<RooWrapperPdf>(name, *func, selfNormalized);
@@ -1039,7 +1043,7 @@ bool exportWrapperPdf(RooJSONFactoryWSTool *, const RooAbsArg *arg, JSONNode &no
10391043

10401044
node["function"] << funcProxy->absArg()->GetName();
10411045
if (pdf->selfNormalized())
1042-
node["selfnormalized"] << true;
1046+
node["self_normalized"] << true;
10431047

10441048
return true;
10451049
}

roofit/hs3/test/testRooFitHS3.cxx

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
#include <RooStats/HistFactory/ParamHistFunc.h>
3636
#include <RooStats/HistFactory/FlexibleInterpVar.h>
3737
#include <RooStats/HistFactory/PiecewiseInterpolation.h>
38+
#include <RooWrapperPdf.h>
3839
#include <RooWorkspace.h>
3940

4041
#include <cmath>
@@ -212,6 +213,17 @@ std::size_t countOccurrences(std::string_view haystack, std::string_view needle)
212213
return result;
213214
}
214215

216+
std::string makeWrapperPdfJson(bool selfNormalized)
217+
{
218+
RooRealVar x{"x", "x", 0.5, 0.0, 1.0};
219+
RooFormulaVar function{"function", "1.0 + 0.0 * x", RooArgList{x}};
220+
RooWrapperPdf pdf{"pdf", "pdf", function, selfNormalized};
221+
222+
RooWorkspace workspace;
223+
workspace.import(pdf, RooFit::Silence());
224+
return RooJSONFactoryWSTool{workspace}.exportJSONtoString();
225+
}
226+
215227
// Asserts that exporting `ws` to HS3 throws and logs an error message containing `expectedReason`.
216228
void expectExportThrowsWithError(RooWorkspace &ws, std::string const &expectedReason)
217229
{
@@ -802,6 +814,76 @@ TEST(RooFitHS3, RooGenericPdf)
802814
EXPECT_EQ(status, 0);
803815
}
804816

817+
TEST(RooFitHS3, RooWrapperPdfSelfNormalizedRoundTrip)
818+
{
819+
const std::string json = makeWrapperPdfJson(true);
820+
EXPECT_NE(json.find("\"self_normalized\":true"), std::string::npos) << json;
821+
EXPECT_EQ(json.find("\"selfnormalized\""), std::string::npos) << json;
822+
EXPECT_EQ(json.find("\"selfNormalized\""), std::string::npos) << json;
823+
824+
RooWorkspace imported;
825+
ASSERT_TRUE(RooJSONFactoryWSTool{imported}.importJSONfromString(json));
826+
auto *pdf = dynamic_cast<RooWrapperPdf *>(imported.pdf("pdf"));
827+
ASSERT_NE(pdf, nullptr);
828+
EXPECT_TRUE(pdf->selfNormalized());
829+
830+
const std::string defaultJson = makeWrapperPdfJson(false);
831+
EXPECT_EQ(defaultJson.find("self_normalized"), std::string::npos) << defaultJson;
832+
833+
RooWorkspace importedDefault;
834+
ASSERT_TRUE(RooJSONFactoryWSTool{importedDefault}.importJSONfromString(defaultJson));
835+
auto *defaultPdf = dynamic_cast<RooWrapperPdf *>(importedDefault.pdf("pdf"));
836+
ASSERT_NE(defaultPdf, nullptr);
837+
EXPECT_FALSE(defaultPdf->selfNormalized());
838+
}
839+
840+
TEST(RooFitHS3, RooWrapperPdfSelfNormalizedLegacyCompatibility)
841+
{
842+
std::string legacyJson = makeWrapperPdfJson(true);
843+
const auto canonicalPos = legacyJson.find("self_normalized");
844+
ASSERT_NE(canonicalPos, std::string::npos) << legacyJson;
845+
legacyJson.replace(canonicalPos, std::string{"self_normalized"}.size(), "selfnormalized");
846+
847+
RooWorkspace imported;
848+
ASSERT_TRUE(RooJSONFactoryWSTool{imported}.importJSONfromString(legacyJson));
849+
auto *pdf = dynamic_cast<RooWrapperPdf *>(imported.pdf("pdf"));
850+
ASSERT_NE(pdf, nullptr);
851+
EXPECT_TRUE(pdf->selfNormalized());
852+
853+
const std::string canonicalJson = RooJSONFactoryWSTool{imported}.exportJSONtoString();
854+
EXPECT_NE(canonicalJson.find("\"self_normalized\":true"), std::string::npos) << canonicalJson;
855+
EXPECT_EQ(canonicalJson.find("\"selfnormalized\""), std::string::npos) << canonicalJson;
856+
}
857+
858+
TEST(RooFitHS3, RooWrapperPdfSelfNormalizedCanonicalKeyTakesPrecedence)
859+
{
860+
std::string json = makeWrapperPdfJson(true);
861+
const std::string canonicalField = "\"self_normalized\":true";
862+
const auto canonicalPos = json.find(canonicalField);
863+
ASSERT_NE(canonicalPos, std::string::npos) << json;
864+
json.replace(canonicalPos, canonicalField.size(), "\"self_normalized\":false,\"selfnormalized\":true");
865+
866+
RooWorkspace imported;
867+
ASSERT_TRUE(RooJSONFactoryWSTool{imported}.importJSONfromString(json));
868+
auto *pdf = dynamic_cast<RooWrapperPdf *>(imported.pdf("pdf"));
869+
ASSERT_NE(pdf, nullptr);
870+
EXPECT_FALSE(pdf->selfNormalized());
871+
}
872+
873+
TEST(RooFitHS3, RooWrapperPdfSelfNormalizedCamelCaseKeyIsIgnored)
874+
{
875+
std::string json = makeWrapperPdfJson(true);
876+
const auto canonicalPos = json.find("self_normalized");
877+
ASSERT_NE(canonicalPos, std::string::npos) << json;
878+
json.replace(canonicalPos, std::string{"self_normalized"}.size(), "selfNormalized");
879+
880+
RooWorkspace imported;
881+
ASSERT_TRUE(RooJSONFactoryWSTool{imported}.importJSONfromString(json));
882+
auto *pdf = dynamic_cast<RooWrapperPdf *>(imported.pdf("pdf"));
883+
ASSERT_NE(pdf, nullptr);
884+
EXPECT_FALSE(pdf->selfNormalized());
885+
}
886+
805887
TEST(RooFitHS3, GenericExpressionCleanup)
806888
{
807889
RooRealVar x{"x", "x", 0.5, -1.0, 1.0};

0 commit comments

Comments
 (0)