@@ -132,6 +132,20 @@ int validate(RooAbsArg const &arg, bool exact = true)
132132 return validate (ws, arg.GetName (), exact);
133133}
134134
135+ std::string parameterStepWidthsNode (std::string const &json)
136+ {
137+ const std::string key = " \" parameter_stepwidths\" :[" ;
138+ const auto begin = json.find (key);
139+ if (begin == std::string::npos) {
140+ return " " ;
141+ }
142+ const auto end = json.find (" ]" , begin);
143+ if (end == std::string::npos) {
144+ return " " ;
145+ }
146+ return json.substr (begin, end - begin + 1 );
147+ }
148+
135149} // namespace
136150
137151// Test that the IO of attributes and string attributes works.
@@ -165,6 +179,128 @@ TEST(RooFitHS3, AttributesIO)
165179 EXPECT_STREQ (pdf.getStringAttribute (" key1" ), nullptr ) << " unexpected string attribute found!" ;
166180}
167181
182+ TEST (RooFitHS3, ParameterStepWidthsModelConfigRoundTrip)
183+ {
184+ RooWorkspace ws1{" workspace" };
185+ ws1.factory (" Gaussian::sig(x[-5, 5], mu[0, -10, 10], sigma[1, 0.1, 10])" );
186+ ws1.factory (" Polynomial::bkg(x, {theta[0, -1, 1]})" );
187+ ws1.factory (" SUM::model(fsig[0.5, 0, 1] * sig, bkg)" );
188+
189+ RooRealVar &x = *ws1.var (" x" );
190+ RooDataSet data{" data" , " data" , RooArgSet{x}};
191+ for (double val : {-1.0 , 0.5 , 1.5 }) {
192+ x.setVal (val);
193+ data.add (RooArgSet{x});
194+ }
195+ ws1.import (data);
196+
197+ ws1.var (" x" )->setError (9.0 );
198+ ws1.var (" mu" )->setError (0.12 );
199+ ws1.var (" theta" )->setError (0.33 );
200+ ws1.var (" sigma" )->setError (0.20 );
201+ ws1.var (" sigma" )->setAsymError (-0.18 , 0.25 );
202+
203+ RooFit::ModelConfig mc{" mc" , &ws1};
204+ mc.SetPdf (*ws1.pdf (" model" ));
205+ mc.SetObservables (" x" );
206+ mc.SetParametersOfInterest (" mu" );
207+ mc.SetNuisanceParameters (" sigma" );
208+ ws1.import (mc);
209+
210+ const std::string json = RooJSONFactoryWSTool{ws1}.exportJSONtoString ();
211+ const std::string parameterStepWidths = parameterStepWidthsNode (json);
212+ ASSERT_FALSE (parameterStepWidths.empty ()) << json;
213+ EXPECT_NE (parameterStepWidths.find (" \" name\" :\" mu\" " ), std::string::npos) << parameterStepWidths;
214+ EXPECT_NE (parameterStepWidths.find (" \" name\" :\" sigma\" " ), std::string::npos) << parameterStepWidths;
215+ EXPECT_NE (parameterStepWidths.find (" \" name\" :\" theta\" " ), std::string::npos) << parameterStepWidths;
216+ EXPECT_NE (parameterStepWidths.find (" \" step_width\" :0.12" ), std::string::npos) << parameterStepWidths;
217+ EXPECT_NE (parameterStepWidths.find (" \" step_width\" :0.2" ), std::string::npos) << parameterStepWidths;
218+ EXPECT_EQ (parameterStepWidths.find (" \" error_lo\" " ), std::string::npos) << parameterStepWidths;
219+ EXPECT_EQ (parameterStepWidths.find (" \" error_hi\" " ), std::string::npos) << parameterStepWidths;
220+ EXPECT_EQ (parameterStepWidths.find (" \" name\" :\" x\" " ), std::string::npos) << parameterStepWidths;
221+
222+ RooWorkspace ws2{" workspace2" };
223+ ASSERT_TRUE (RooJSONFactoryWSTool{ws2}.importJSONfromString (json));
224+
225+ ASSERT_NE (ws2.var (" mu" ), nullptr );
226+ ASSERT_NE (ws2.var (" theta" ), nullptr );
227+ ASSERT_NE (ws2.var (" sigma" ), nullptr );
228+ ASSERT_NE (ws2.var (" x" ), nullptr );
229+ EXPECT_TRUE (ws2.var (" mu" )->hasError ());
230+ EXPECT_DOUBLE_EQ (ws2.var (" mu" )->getError (), 0.12 );
231+ EXPECT_TRUE (ws2.var (" theta" )->hasError ());
232+ EXPECT_DOUBLE_EQ (ws2.var (" theta" )->getError (), 0.33 );
233+ EXPECT_TRUE (ws2.var (" sigma" )->hasError ());
234+ EXPECT_DOUBLE_EQ (ws2.var (" sigma" )->getError (), 0.20 );
235+ EXPECT_FALSE (ws2.var (" sigma" )->hasAsymError ());
236+ EXPECT_FALSE (ws2.var (" x" )->hasError ());
237+ }
238+
239+ TEST (RooFitHS3, ParameterStepWidthsFallbackExcludesDataAxes)
240+ {
241+ RooWorkspace ws1{" workspace" };
242+ ws1.factory (" Gaussian::model(x[-5, 5], mu[0, -10, 10], sigma[1, 0.1, 10])" );
243+
244+ RooRealVar &x = *ws1.var (" x" );
245+ RooDataSet data{" data" , " data" , RooArgSet{x}};
246+ for (double val : {-1.0 , 0.5 , 1.5 }) {
247+ x.setVal (val);
248+ data.add (RooArgSet{x});
249+ }
250+ ws1.import (data);
251+
252+ ws1.var (" x" )->setError (9.0 );
253+ ws1.var (" mu" )->setError (0.12 );
254+ ws1.var (" sigma" )->setError (0.20 );
255+
256+ const std::string json = RooJSONFactoryWSTool{ws1}.exportJSONtoString ();
257+ const std::string parameterStepWidths = parameterStepWidthsNode (json);
258+ ASSERT_FALSE (parameterStepWidths.empty ()) << json;
259+ EXPECT_NE (parameterStepWidths.find (" \" name\" :\" mu\" " ), std::string::npos) << parameterStepWidths;
260+ EXPECT_NE (parameterStepWidths.find (" \" name\" :\" sigma\" " ), std::string::npos) << parameterStepWidths;
261+ EXPECT_EQ (parameterStepWidths.find (" \" name\" :\" x\" " ), std::string::npos) << parameterStepWidths;
262+
263+ RooWorkspace ws2{" workspace2" };
264+ ASSERT_TRUE (RooJSONFactoryWSTool{ws2}.importJSONfromString (json));
265+
266+ ASSERT_NE (ws2.var (" mu" ), nullptr );
267+ ASSERT_NE (ws2.var (" sigma" ), nullptr );
268+ ASSERT_NE (ws2.var (" x" ), nullptr );
269+ EXPECT_DOUBLE_EQ (ws2.var (" mu" )->getError (), 0.12 );
270+ EXPECT_DOUBLE_EQ (ws2.var (" sigma" )->getError (), 0.20 );
271+ EXPECT_FALSE (ws2.var (" x" )->hasError ());
272+ }
273+
274+ TEST (RooFitHS3, ParameterStepWidthsImportAfterDefaultSnapshot)
275+ {
276+ const std::string json = R"( {
277+ "metadata": {"hs3_version": "0.1.90"},
278+ "parameter_points": [
279+ {
280+ "name": "default_values",
281+ "parameters": [
282+ {"name": "mu", "value": 0.0, "err": 0.01}
283+ ]
284+ }
285+ ],
286+ "misc": {
287+ "minimization": {
288+ "parameter_stepwidths": [
289+ {"name": "mu", "step_width": 0.42},
290+ {"name": "missing", "step_width": 1.0}
291+ ]
292+ }
293+ }
294+ })" ;
295+
296+ RooWorkspace ws{" workspace" };
297+ ASSERT_TRUE (RooJSONFactoryWSTool{ws}.importJSONfromString (json));
298+
299+ ASSERT_NE (ws.var (" mu" ), nullptr );
300+ EXPECT_TRUE (ws.var (" mu" )->hasError ());
301+ EXPECT_DOUBLE_EQ (ws.var (" mu" )->getError (), 0.42 );
302+ }
303+
168304TEST (RooFitHS3, RooAddPdf)
169305{
170306 int status = validate ({" Gaussian::sig(x[5.20, 5.30], sigmean[5.28, 5.20, 5.30], sigwidth[0.0027, 0.001, 1.])" ,
0 commit comments