diff --git a/inc/LauAbsRValue.hh b/inc/LauAbsRValue.hh index 477fe36..d8c21a5 100644 --- a/inc/LauAbsRValue.hh +++ b/inc/LauAbsRValue.hh @@ -1,149 +1,215 @@ /* Copyright 2014 University of Warwick Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ /* Laura++ package authors: John Back Paul Harrison Thomas Latham */ /*! \file LauAbsRValue.hh \brief File containing declaration of LauAbsRValue class. */ /*! \class LauAbsRValue \brief Pure abstract base class for defining a parameter containing an R value Pure abstract base class for defining a parameter containing an R value, either a LauParameter or a LauFormulaPar */ #ifndef LAU_ABSRVALUE #define LAU_ABSRVALUE -#include +#include "LauJsonTools.hh" #include "Rtypes.h" +#include "TString.h" + +#include + +#include +#include class LauParameter; class LauAbsRValue { public: //! Constructor LauAbsRValue() = default; //! Destructor virtual ~LauAbsRValue() = default; //! Copy constructor LauAbsRValue(const LauAbsRValue& rhs) = default; //! Copy assignment operator LauAbsRValue& operator=(const LauAbsRValue& rhs) = default; //! Move constructor LauAbsRValue(LauAbsRValue&& rhs) = default; //! Move assignment operator LauAbsRValue& operator=(LauAbsRValue&& rhs) = default; //! Return the name of the parameter /*! \return the name of the parameter */ virtual const TString& name() const =0; //! Set the parameter name /*! \param [in] newName the name of the parameter */ virtual void name(const TString& newName) =0; //! Return the value of the parameter /*! \return the value of the parameter */ virtual Double_t value() const =0; //! The unblinded value of the parameter /*! \return the unblinded value of the parameter */ virtual Double_t unblindValue() const =0; //! The value generated for the parameter /*! \return the value generated for the parameter */ virtual Double_t genValue() const =0; //! The initial value of the parameter /*! \return the initial value of the parameter given to the fitter */ virtual Double_t initValue() const =0; //! Check whether a Gaussian constraints is applied /*! \return the boolean flag true/false whether a Gaussian constraint is applied */ virtual Bool_t gaussConstraint() const =0; //! The mean of the Gaussian constraint /*! \return the mean value of the Gaussian constraint */ virtual Double_t constraintMean() const =0; //! The width of the Gaussian constraint /*! \return the width of the Gaussian constraint */ virtual Double_t constraintWidth() const =0; //! Return the list of LauParameters on which the LauAbsRValue depends /*! \return the list of LauParameters */ virtual std::vector getPars() =0; //! Is the parameter also an L value or not /*! \return whether the parameter is also an L value */ virtual Bool_t isLValue() const =0; //! Check is the parameter is fixed or floated /*! \return the boolean flag whether the parameter is fixed */ virtual Bool_t fixed() const =0; //! The blinding state /*! \return the blinding state: kTRUE means that it is blinded, kFALSE that it is not blinded */ virtual Bool_t blind() const =0; - private: + //! Write state to a JSON record + /*! + \param [in,out] j the JSON record to write to + */ + virtual void serialiseToJson( nlohmann::json& j ) const; + + //! Construct a collection of parameter objects based on values read from a JSON array + /*! + \param [in] array the JSON array + \return the collection of newly constructed parameters + */ + static std::vector> readFromJson( const nlohmann::json& array ); + //! Construct a collection of parameter objects based on values read from a JSON file + /*! + \param [in] fileName the name of the file from which the JSON should be read + \param [in] elementName the optional name of the JSON element that contains the array of parameter definitions (defaults to using the root record) + \return the collection of newly constructed parameters + */ + static std::vector> readFromJson( const TString& fileName, const TString& elementName = "" ); + + //! Write a collection of parameter objects to a JSON file + /*! + \param [in] params the collection of parameters to be written out (the collection should contain either smart or raw pointers to LauAbsRValue or derived types) + \param [in] fileName the name of the file to which the JSON should be written + \param [in] elementName the (optional) name of the JSON element to which the parameters should be written + \param [in] append if true, append the spline to the existing JSON within the file - if using this option it is then mandatory to provide elementName + \param [in] indent the indentation level to use in the output in number of spaces (defaults to 4) + */ + template + static void writeToJson( const Collection& params, const TString& fileName, const TString& elementName = "", const bool append = false, const int indent = 4 ); + + private: ClassDef(LauAbsRValue,1) // Abstract base class for R parameters }; +template +void LauAbsRValue::writeToJson( const Collection& params, const TString& fileName, const TString& elementName, const bool append, const int indent ) +{ + using nlohmann::json; + + json j = json::array(); + + for ( auto& param : params ) { + j.push_back( *param ); + } + + const bool writeOK { LauJsonTools::writeJsonFile( j, fileName.Data(), elementName.Data(), append, indent ) }; + if ( ! writeOK ) { + std::cerr << "ERROR in LauAbsRValue::writeToJson : could not successfully write to file \"" << fileName << std::endl; + } +} + +//! \cond DOXYGEN_IGNORE +namespace nlohmann { + template <> + struct adl_serializer { + static void to_json(json& j, const LauAbsRValue& t) + { + t.serialiseToJson(j); + } + }; +} +//! \endcond DOXYGEN_IGNORE + #endif diff --git a/inc/LauBlind.hh b/inc/LauBlind.hh index da59757..4fd64ad 100644 --- a/inc/LauBlind.hh +++ b/inc/LauBlind.hh @@ -1,114 +1,114 @@ /* Copyright 2015 University of Warwick Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ /* Laura++ package authors: John Back Paul Harrison Thomas Latham */ /*! \file LauBlind.hh \brief File containing declaration of LauBlind class. */ /*! \class LauBlind \brief Class for blinding and unblinding a number based on a blinding string. Numbers are blinded by applying an offset. The blinding string is converted to an integer using TMath::Hash and this integer is used to seed a TRandom3. The offset is sampled from a Gaussian of defined width using the seeded TRandom3. */ #ifndef LAU_BLIND #define LAU_BLIND #include "TString.h" #include class LauBlind final { public: //! Default constructor /*! This is purely to allow I/O to succeed, should not generally be used */ LauBlind() = default; //! Constructor /*! \param [in] blindingStr the blinding string \param [in] width the width of the Gaussian for sampling the offset \param [in] flipSign activate possible random sign flip (off by default) */ LauBlind(const TString& blindingStr, const Double_t width, const Bool_t flipSign = kFALSE); //! Obtain the blinded value /*! \param [in] val the unblinded value \return the blinded value */ inline Double_t blind(const Double_t val) const { return flipFactor_[flip_] * val + offset_; } //! Obtain the unblinded value /*! \param [in] val the blinded value \return the unblinded value */ inline Double_t unblind(const Double_t val) const { return flipFactor_[flip_] * (val - offset_); } //! Obtain the blinding string /*! \return the blinding string */ inline const TString& blindingString() const { return blindingString_; } //! Obtain the Gaussian width /*! \return the Gaussian width */ inline Double_t blindingWidth() const { return blindingWidth_; } //! Obtain the sign-flip setting /*! \return the sign-flip setting */ inline Bool_t flipSign() const { return flipSign_; } private: //! The blinding string - const TString blindingString_; + TString blindingString_; //! The Gaussian width - const Double_t blindingWidth_{0.0}; + Double_t blindingWidth_{0.0}; //! The flip sign setting - const Bool_t flipSign_{kFALSE}; + Bool_t flipSign_{kFALSE}; //! Array to hold +1/-1 factors for efficient implementation of sign flip - const std::array flipFactor_{1.0,-1.0}; + std::array flipFactor_{1.0,-1.0}; //! The offset used to blind the value Double_t offset_{0.0}; //! Whether to flip sign in blinding Bool_t flip_{kFALSE}; ClassDef(LauBlind, 1) }; #endif diff --git a/inc/LauFormulaPar.hh b/inc/LauFormulaPar.hh index eee8125..9f337ea 100644 --- a/inc/LauFormulaPar.hh +++ b/inc/LauFormulaPar.hh @@ -1,183 +1,181 @@ /* Copyright 2014 University of Warwick Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ /* Laura++ package authors: John Back Paul Harrison Thomas Latham */ /*! \file LauFormulaPar.hh \brief File containing declaration of LauFormulaPar class. */ /*! \class LauFormulaPar \brief Class for defining combinations of fit parameter objects. Allows for combinations of LauParameters to be passed to the fit models. Equations of the form [0]*2 + 3*[1] are accepted, with a vector of LauParameters to define inputs [0] and [1]. The parameter for [0] must be stored first in the vector, and so on. */ #ifndef LAU_FORMULAPAR #define LAU_FORMULAPAR -#include -#include +#include "LauAbsRValue.hh" #include "TString.h" #include "TFormula.h" -#include "LauAbsRValue.hh" -#include "LauParameter.hh" + +#include + +class LauParameter; class LauFormulaPar : public LauAbsRValue { public: //! Constructor /*! \param [in] forName the name of the formula \param [in] formula the desired expression, using TFormula syntax \param [in] params a vector of LauParameters used in the formula */ LauFormulaPar(const TString& forName, const TString& formula, const std::vector& params); - // Destructor - virtual ~LauFormulaPar(); - - //! Copy constructor - LauFormulaPar(const LauFormulaPar& rhs); - - //! Copy assignment operator - LauFormulaPar& operator=(const LauFormulaPar& rhs); - //! Return the value of the LauFormalaPar /*! \return the value of the formula */ - Double_t value() const; + Double_t value() const override; //! The unblinded value of the parameter /*! \return the unblinded value of the parameter */ - Double_t unblindValue() const; + Double_t unblindValue() const override; //! The value generated for the parameter /*! \return the value generated for the parameter */ - Double_t genValue() const; + Double_t genValue() const override; //! The initial value of the parameter /*! \return the initial value of the parameter given to the fitter */ - Double_t initValue() const; + Double_t initValue() const override; //! The parameter name /*! \return the name of the parameter */ - inline const TString& name() const {return name_;} + const TString& name() const override {return name_;} //! Set the parameter name /*! \param [in] newName the name of the parameter */ - inline void name(const TString& newName) {name_ = newName;}; + void name(const TString& newName) override {name_ = newName;}; //! Get the LauParameters used in LauFormulaPar /*! \return the list of LauParameters */ - std::vector getPars() {return paramVec_;} + std::vector getPars() override {return params_;} //! Boolean to say it is not an L value /*! \return kFALSE, LauFormulaPars are not L values */ - inline Bool_t isLValue() const {return kFALSE;} + Bool_t isLValue() const override {return kFALSE;} //! Boolean to say if the LauFormulaPar is fixed /*! \return kFALSE unless all LauParameters in the formula are fixed */ - Bool_t fixed() const; + Bool_t fixed() const override; //! The blinding state /*! \return kTRUE if any of the LauParameters in the formula are blinded, kFALSE otherwise */ - Bool_t blind() const; + Bool_t blind() const override; //! Check whether a Gaussian constraints is applied /*! \return the boolean flag true/false whether a Gaussian constraint is applied */ - inline Bool_t gaussConstraint() const {return gaussConstraint_;} + Bool_t gaussConstraint() const override {return gaussConstraint_;} //! The mean of the Gaussian constraint /*! \return the mean value of the Gaussian constraint */ - inline Double_t constraintMean() const {return constraintMean_;} + Double_t constraintMean() const override {return constraintMean_;} //! The width of the Gaussian constraint /*! \return the width of the Gaussian constraint */ - inline Double_t constraintWidth() const {return constraintWidth_;} + Double_t constraintWidth() const override {return constraintWidth_;} //! Add a Gaussian constraint (or modify an existing one) /*! \param [in] newGaussMean the new value of the Gaussian constraint mean \param [in] newGaussWidth the new value of the Gaussian constraint width */ - void addGaussianConstraint(Double_t newGaussMean, Double_t newGaussWidth); + void addGaussianConstraint(const Double_t newGaussMean, const Double_t newGaussWidth); //! Remove the Gaussian constraint void removeGaussianConstraint(); + //! Write state to a JSON record + /*! + \param [in,out] j the JSON record to write to + */ + void serialiseToJson( nlohmann::json& j ) const override; + protected: private: //! The parameter name TString name_; //! The formula - mutable TFormula formula_; + TFormula formula_; //! Vector of LauParameters in the formula - std::vector paramVec_; + std::vector params_; - //! Array to hold parameter values to pass to formula - Double_t* paramArray_; + //! Vector of parameter values to pass to formula + mutable std::vector paramVals_; //! Choice to use Gaussian constraint - Bool_t gaussConstraint_; + Bool_t gaussConstraint_{kFALSE}; //! Mean value of the Gaussian constraint - Double_t constraintMean_; + Double_t constraintMean_{0.0}; //! Width of the Gaussian constraint - Double_t constraintWidth_; + Double_t constraintWidth_{0.0}; - ClassDef(LauFormulaPar, 0) + ClassDefOverride(LauFormulaPar, 0) }; #endif diff --git a/inc/LauJsonTools.hh b/inc/LauJsonTools.hh index 69f88bd..5cd9eb2 100644 --- a/inc/LauJsonTools.hh +++ b/inc/LauJsonTools.hh @@ -1,162 +1,181 @@ /* Copyright 2023 University of Warwick Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ /* Laura++ package authors: John Back Paul Harrison Thomas Latham */ #ifndef LAU_JSON_TOOLS_HH #define LAU_JSON_TOOLS_HH +#include "TString.h" + #include #include #include /*! \file LauJsonTools.hh \brief File containing LauJsonTools namespace */ /*! \namespace LauJsonTools \brief Namespace containing tools for reading/writing JSON files */ namespace LauJsonTools { /*! \enum JsonType \brief Define types that can be found in JSON fields and that we can handle */ enum class JsonType { Null, //!< JSON value is null Object, //!< JSON value is an object Array, //!< JSON value is an array String, //!< JSON value is a string Boolean, //!< JSON value is a boolean Number_Integer, //!< JSON value is an integer (signed or unsigned) Number_Unsigned, //!< JSON value is an unsigned integer Number_Float, //!< JSON value is a floating point value Number, //!< JSON value is any number (integer, unsigned or float) Primitive, //!< JSON value is any primative type (null, string, boolean, integer, unsigned, float) Structured, //!< JSON value is any structured type (object, array) Any //!< JSON value is any of the above }; //! Typedef to define a combination of a JSON element's name and type using ElementNameType = std::pair< std::string, JsonType >; //! Exception object to be thrown in case of a missing element class MissingJsonElement : public std::runtime_error { public: //! Constructor /*! \param [in] what the message explaining the precise error that has occurred */ MissingJsonElement(const std::string& what) : std::runtime_error(what) {} }; //! Exception object to be thrown in case of malformed JSON class InvalidJson : public std::runtime_error { public: //! Constructor /*! \param [in] what the message explaining the precise error that has occurred */ InvalidJson(const std::string& what) : std::runtime_error(what) {} }; //! Deserialise a JSON file to a JSON value /*! \param [in] fileName the name of the file to read \param [in] elementName the optional name of the element within the root object to retrieve - by default retrieve the root structure \param [in] expectedType the expected type of the value */ nlohmann::json readJsonFile(const std::string& fileName, const std::string& elementName = "", const JsonType expectedType = JsonType::Any); //! Serialise a JSON value to a JSON file /*! \param [in] value the JSON value to serialise \param [in] fileName the name of the file to be written \param [in] elementName the (optional) name of the JSON element to which the coefficients should be written \param [in] append if true, append the spline to the existing JSON within the file - if using this option it is then mandatory to provide elementName \param [in] indent the indentation level to use in the output in number of spaces (defaults to 4)\n From the nlohmann::json::dump documentation:\n If indent is nonnegative, then array elements and object members will be pretty-printed with that indent level. An indent level of 0 will only insert newlines. A value of -1 selects the most compact representation. \return true if file successfully written, false otherwise */ bool writeJsonFile(const nlohmann::json& value, const std::string& fileName, const std::string& elementName = "", const bool append = false, const int indent = 4); //! Check that the type of a JSON value is as expected /*! \param [in] value the JSON value to check \param [in] expectedType the expected type of the value */ bool checkValueType(const nlohmann::json& value, const JsonType expectedType); //! Check that the expected JSON elements are present in the supplied object /*! \param [in] obj the JSON object to check \param [in] expectedElements the elements (names and types) that are expected \return true if all expected elements are present, false otherwise */ bool checkObjectElements( const nlohmann::json& obj, const std::vector& expectedElements ); //! Access the value of an element that should be present in an object (no checking is done) /*! + \tparam T the type of the element to be retrieved \param [in] obj the JSON object to access \param [in] elementName the name of the element within the object to retrieve \return the value of the element */ template T getValue( const nlohmann::json& obj, const std::string& elementName ) { return obj.at( elementName ).get(); } //! Access an element that may or may not be present in an object /*! \param [in] obj the JSON object to access \param [in] elementName the name of the element within the object to retrieve \param [in] expectedType the expected type of the element \return a pointer to the element, or nullptr if it is not present */ const nlohmann::json* getOptionalElement( const nlohmann::json& obj, const std::string& elementName, const JsonType expectedType = JsonType::Any ); //! Access the value of an element that may or may not be present in an object /*! + \tparam T the type of the element to be retrieved \param [in] obj the JSON object to access \param [in] elementName the name of the element within the object to retrieve \param [in] expectedType the expected type of the element \return the value of the element as a std::optional */ template std::optional getOptionalValue( const nlohmann::json& obj, const std::string& elementName, const JsonType expectedType = JsonType::Any ); } template std::optional LauJsonTools::getOptionalValue( const nlohmann::json& obj, const std::string& elementName, const JsonType expectedType ) { auto elem { getOptionalElement( obj, elementName, expectedType ) }; if ( ! elem ) { return {}; } return elem->get(); } +//! \cond DOXYGEN_IGNORE +namespace nlohmann { + template <> + struct adl_serializer { + static void to_json(json& j, const TString& t) + { + j = t.Data(); + } + static TString from_json(const json& j) + { + return j.get(); + } + }; +} +//! \endcond DOXYGEN_IGNORE #endif diff --git a/inc/LauParameter.hh b/inc/LauParameter.hh index 891993e..b8fab3e 100644 --- a/inc/LauParameter.hh +++ b/inc/LauParameter.hh @@ -1,590 +1,601 @@ /* Copyright 2006 University of Warwick Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ /* Laura++ package authors: John Back Paul Harrison Thomas Latham */ /*! \file LauParameter.hh \brief File containing declaration of LauParameter class. */ /*! \class LauParameter \brief Class for defining the fit parameter objects. Holds all relevant information for the parameters for both generation and fitting step: current, initial and generated value, maximum and minimum range, error, asymmetric error, fix and float and etc. */ #ifndef LAU_PARAMETER #define LAU_PARAMETER #include #include #include #include #include "TObject.h" #include "TString.h" #include "LauAbsRValue.hh" #include "LauBlind.hh" class LauParameter final : public TObject, public LauAbsRValue { public: //! Default constructor LauParameter() = default; //! Constructor for named parameter /*! \param [in] parName the parameter name */ explicit LauParameter(const TString& parName); //! Constructor for parameter value /*! \param [in] parValue the parameter value */ explicit LauParameter(const Double_t parValue); //! Constructor double limit parameter /*! \param [in] parValue the parameter value \param [in] min the minimum value of the parameter \param [in] max the maximum value of the parameter */ LauParameter(const Double_t parValue, const Double_t min, const Double_t max); //! Constructor double limit fixed parameter /*! \param [in] parValue the parameter value \param [in] min the minimum value of the parameter \param [in] max the maximum value of the parameter \param [in] parFixed boolean flag to fix or float parameter */ LauParameter(const Double_t parValue, const Double_t min, const Double_t max, const Bool_t parFixed); //! Constructor for double error and limit parameter /*! \param [in] parValue the parameter value \param [in] parError the parameter error \param [in] min the minimum value of the parameter \param [in] max the maximum value of the parameter */ LauParameter(const Double_t parValue, const Double_t parError, const Double_t min, const Double_t max); //! Constructor for parameter value and name /*! \param [in] parName the parameter name \param [in] parValue the parameter value */ LauParameter(const TString& parName, const Double_t parValue); //! Constructor double limit parameter and name /*! \param [in] parName the parameter name \param [in] parValue the parameter value \param [in] min the minimum value of the parameter \param [in] max the maximum value of the parameter */ LauParameter(const TString& parName, const Double_t parValue, const Double_t min, const Double_t max); //! Constructor double limit fixed parameter and name /*! \param [in] parName the parameter name \param [in] parValue the parameter value \param [in] min the minimum value of the parameter \param [in] max the maximum value of the parameter \param [in] parFixed boolean flag to fix (kTRUE) or float (kFALSE) the parameter */ LauParameter(const TString& parName, const Double_t parValue, const Double_t min, const Double_t max, const Bool_t parFixed); //! Constructor double error and limit parameter and name /*! \param [in] parName the parameter name \param [in] parValue the parameter value \param [in] parError the parameter error \param [in] min the minimum value of the parameter \param [in] max the maximum value of the parameter */ LauParameter(const TString& parName, const Double_t parValue, const Double_t parError, const Double_t min, const Double_t max); // Destructor virtual ~LauParameter() noexcept; //! Copy constructor /*! \param [in] rhs the parameter to be copied */ - LauParameter(const LauParameter& rhs); + LauParameter(const LauParameter& rhs) = default; //! Copy assignment operator /*! \param [in] rhs the parameter to be copied */ - LauParameter& operator=(const LauParameter& rhs); + LauParameter& operator=(const LauParameter& rhs) = default; //! Move constructor /*! \param [in] rhs the parameter to be moved from */ LauParameter(LauParameter&& rhs) = default; //! Move assignment operator /*! \param [in] rhs the parameter to be moved from */ LauParameter& operator=(LauParameter&& rhs) = default; // the simple accessor functions //! The parameter name /*! \return the name of the parameter */ const TString& name() const override {return name_;} //! The blinding state /*! \return the blinding state: kTRUE means that it is blinded, kFALSE that it is not blinded */ - Bool_t blind() const override {return (blinder_ != nullptr);} + Bool_t blind() const override {return blinder_.has_value();} //! Access the blinder object /*! \return the blinder */ - const LauBlind* blinder() const {return blinder_.get();} + const LauBlind* blinder() const {return blinder_.has_value() ? &blinder_.value() : nullptr;} //! The value of the parameter /*! \return the value of the parameter */ Double_t value() const override {return value_;} //! The unblinded value of the parameter /*! \return the unblinded value of the parameter */ - Double_t unblindValue() const override {return (blinder_==nullptr) ? value_ : blinder_->unblind(value_);} + Double_t unblindValue() const override {return this->blind() ? blinder_->unblind(value_) : value_;} //! The error on the parameter /*! \return the error on the parameter */ Double_t error() const {return error_;} //! The lower error on the parameter /*! \return the lower error on the parameter */ Double_t negError() const {return negError_;} //! The upper error on the parameter /*! \return the upper error on the parameter */ Double_t posError() const {return posError_;} //! The value generated for the parameter /*! \return the value generated for the parameter */ Double_t genValue() const override {return genValue_;} //! The initial value of the parameter /*! \return the initial value of the parameter given to the fitter */ Double_t initValue() const override {return initValue_;} //! The minimum value allowed for the parameter /*! \return the minimum value allowed for the parameter */ Double_t minValue() const {return minValue_;} //! The maximum value allowed for the parameter /*! \return the maximum value allowed for the parameter */ Double_t maxValue() const {return maxValue_;} //! The range allowed for the parameter /*! \return the range allowed for the parameters, defined as the difference between the max and min value */ Double_t range() const {return this->maxValue() - this->minValue();} //! Check whether the parameter is fixed or floated /*! \return the boolean flag true/false whether the parameter is fixed */ Bool_t fixed() const override {return fixed_;} //! Check whether the parameter should be floated only in the second stage of a two stage fit /*! \return the boolean flag true/false whether it floats only in the second stage */ Bool_t secondStage() const {return secondStage_;} //! Check whether a Gaussian constraints is applied /*! \return the boolean flag true/false whether a Gaussian constraint is applied */ Bool_t gaussConstraint() const override {return gaussConstraint_;} //! The mean of the Gaussian constraint /*! \return the mean value of the Gaussian constraint */ Double_t constraintMean() const override {return constraintMean_;} //! The width of the Gaussian constraint /*! \return the width of the Gaussian constraint */ Double_t constraintWidth() const override {return constraintWidth_;} //! The parameter global correlation coefficient /*! \return the global correlation coefficient */ Double_t globalCorrelationCoeff() const {return gcc_;} //! The bias in the parameter /*! \return the bias in the parameter, defined as the difference between the value and the generated value */ Double_t bias() const {return bias_;} //! The pull value for the parameter /*! \return the pull value for the parameter, defined as the bias divided by the error */ Double_t pull() const {return pull_;} //! Boolean to say it is an L value /*! \return kTRUE, LauParameters are L values */ Bool_t isLValue() const override {return kTRUE;} //! Get the LauParameter itself /*! \return a vector of the LauParameter */ std::vector getPars() override; // the simple "setter" functions //! Set the parameter name /*! \param [in] newName the name of the parameter */ void name(const TString& newName) override; //! Set the value of the parameter /*! \param [in] newValue the value of the parameter */ void value(const Double_t newValue); //! Set the error on the parameter /*! \param [in] newError the error on the parameter */ void error(const Double_t newError); //! Set the lower error on the parameter /*! \param [in] newNegError the lower error on the parameter */ void negError(const Double_t newNegError); //! Set the upper error on the parameter /*! \param [in] newPosError the upper error on the parameter */ void posError(const Double_t newPosError); //! Set the error values on the parameter /*! \param [in] newError the error on the parameter \param [in] newNegError the lower error on the parameter \param [in] newPosError the upper error on the parameter */ void errors(const Double_t newError, const Double_t newNegError, const Double_t newPosError); //! Set the value and errors on the parameter /*! \param [in] newValue the value of the parameter \param [in] newError the error on the parameter \param [in] newNegError the lower error on the parameter (default set to zero) \param [in] newPosError the upper error on the parameter (default set to zero) */ void valueAndErrors(const Double_t newValue, const Double_t newError, const Double_t newNegError = 0.0, const Double_t newPosError = 0.0); //! Set the global correlation coefficient /*! \param [in] newGCCValue the value of the coefficient */ void globalCorrelationCoeff(const Double_t newGCCValue); //! Set the generated value for the parameter /*! \param [in] newGenValue the generated value for the parameter */ void genValue(const Double_t newGenValue); //! Set the inital value for the parameter /*! \param [in] newInitValue the initial value for the parameter */ void initValue(const Double_t newInitValue); //! Set the minimum value for the parameter /*! \param [in] newMinValue the minimum value for the parameter */ void minValue(const Double_t newMinValue); //! Set the maximum value for the parameter /*! \param [in] newMaxValue the maximum value for the parameter */ void maxValue(const Double_t newMaxValue); //! Set the range for the parameter /*! \param [in] newMinValue the minimum value for the parameter \param [in] newMaxValue the maximum value for the parameter */ void range(const Double_t newMinValue, const Double_t newMaxValue); //! Set the value and range for the parameter /*! \param [in] newValue the value of the parameter \param [in] newMinValue the minimum value for the parameter \param [in] newMaxValue the maximum value for the parameter */ void valueAndRange(const Double_t newValue, const Double_t newMinValue, const Double_t newMaxValue); //! Fix or float the given parameter /*! \param [in] parFixed boolean flag to fix or float the parameter */ void fixed(const Bool_t parFixed); //! Set parameter as second-stage or not of the fit /*! \param [in] secondStagePar boolean flag to check whether is a second-stage parameter */ void secondStage(const Bool_t secondStagePar); //! Add a Gaussian constraint (or modify an existing one) /*! \param [in] newGaussMean the new value of the Gaussian constraint mean \param [in] newGaussWidth the new value of the Gaussian constraint width */ void addGaussianConstraint(const Double_t newGaussMean, const Double_t newGaussWidth); //! Remove the Gaussian constraint void removeGaussianConstraint(); //! Blind the parameter /*! See LauBlind documentation for details of blinding procedure \param [in] blindingString the unique blinding string used to seed the random number generator \param [in] width the width of the Gaussian from which the offset should be sampled \param [in] flipSign activate possible random sign flip (off by default) */ void blindParameter(const TString& blindingString, const Double_t width, const Bool_t flipSign = kFALSE); // functions for the cloning mechanism //! Check whether is a clone or not /*! \return true/false whether is a clone */ - Bool_t clone() const {return clone_;} + Bool_t clone() const {return (parent_ != nullptr);} //! Method to create a clone from the parent parameter using the copy constructor /*! \param [in] constFactor the optional constant factor by which the clone shold be multiplied \return the cloned parameter */ LauParameter* createClone(const Double_t constFactor = 1.0); //! Method to create a clone from the parent parameter using the copy constructor and setting a new name /*! \param [in] newName the new name of the cloned parameter \param [in] constFactor the optional constant factor by which the clone shold be multiplied \return the cloned parameter */ LauParameter* createClone(const TString& newName, const Double_t constFactor = 1.0); //! The parent parameter /*! \return the parent parameter */ LauParameter* parent() const {return parent_;} //! Call to update the bias and pull values void updatePull(); //! Randomise the value of the parameter (if it is floating). /*! The pre-defined parameter range is used as the randomisation range. */ void randomiseValue(); //! Randomise the value of the parameter (if it is floating). /*! Use the given range unless either of the given values are outside the range of the parameter, in which case that value will be altered to the current max or min. \param [in] minVal the minimum value for the parameter \param [in] maxVal the maximum value for the parameter */ void randomiseValue(const Double_t minVal, const Double_t maxVal); + //! Write state to a JSON record + /*! + \param [in,out] j the JSON record to write to + */ + void serialiseToJson( nlohmann::json& j ) const override; + protected: //! Method to check whether value provided is within the range and that the minimum and maximum limits make sense /*! \param [in] val the value of the parameter \param [in] minVal the minimum value allowed \param [in] maxVal the maximum value allowed */ void checkRange(const Double_t val, const Double_t minVal, const Double_t maxVal); //! Method to check whether value provided is whithin the range and that the minimum and maximum limits make sense void checkRange() { this->checkRange(this->value(),this->minValue(),this->maxValue()); } //! Mark this as a clone of the given parent /*! \param theparent the parent parameter */ void clone(LauParameter* theparent) { parent_ = theparent; - clone_ = (parent_==nullptr) ? kFALSE : kTRUE; } //! Method to remove a clone from the list of clones /*! This is used in the destructor to allow a clone to inform its parent it is no longer around \param [in] clone the clone to be removed from the list */ void removeFromCloneList(LauParameter* clone) { auto iter = clones_.find( clone ); if ( iter != clones_.end() ) { clones_.erase( iter ); } } //! Method to clear the clone parameters void wipeClones() {clones_.clear();} //! Method to update clone values /*! \param [in] justValue boolean flag to determine whether it is necessary to update all the parameter settings or only its value. */ void updateClones(const Bool_t justValue); private: //! LauFitNtuple is a friend class friend class LauFitNtuple; //! The parameter name TString name_; //! The parameter value Double_t value_{0.0}; //! The error on the parameter Double_t error_{0.0}; //! The lower error on the parameter Double_t negError_{0.0}; //! The upper error on the parameter Double_t posError_{0.0}; //! Toy generation value Double_t genValue_{0.0}; //! Initial fit value Double_t initValue_{0.0}; //! Minimum value for the parameter Double_t minValue_{0.0}; //! Maximum value for the parameter Double_t maxValue_{0.0}; //! Fix/float option for parameter Bool_t fixed_{kTRUE}; //! Flag whether it is floated only in the second stage of the fit Bool_t secondStage_{kFALSE}; //! Choice to use Gaussian constraint Bool_t gaussConstraint_{kFALSE}; //! Mean value of the Gaussian constraint Double_t constraintMean_{0.0}; //! Width of the Gaussian constraint Double_t constraintWidth_{0.0}; //! Global correlation coefficient Double_t gcc_{0.0}; //! Parameter bias Double_t bias_{0.0}; //! Parameter pull Double_t pull_{0.0}; - //! Flag whether the parameter is a clone - Bool_t clone_{kFALSE}; - //! The parent parameter LauParameter* parent_{nullptr}; //! The clones of this parameter std::map clones_; //! The blinding engine - std::unique_ptr blinder_; + std::optional blinder_; - ClassDefOverride(LauParameter, 4) + ClassDefOverride(LauParameter, 5) }; //! Output stream operator std::ostream& operator << (std::ostream& stream, const LauParameter& par); //! Type to define an array of parameters typedef std::vector< std::vector > LauParArray; +//! \cond DOXYGEN_IGNORE +namespace nlohmann { + template <> + struct adl_serializer { + static LauParameter from_json(const json& j); + }; +} +//! \endcond DOXYGEN_IGNORE + #endif diff --git a/src/LauAbsRValue.cc b/src/LauAbsRValue.cc new file mode 100644 index 0000000..95e5e1c --- /dev/null +++ b/src/LauAbsRValue.cc @@ -0,0 +1,188 @@ + +/* +Copyright 2023 University of Warwick + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +/* +Laura++ package authors: +John Back +Paul Harrison +Thomas Latham +*/ + +/*! \file LauAbsRValue.cc + \brief File containing implementation of LauAbsRValue class. +*/ + +#include "LauAbsRValue.hh" +#include "LauFormulaPar.hh" +#include "LauJsonTools.hh" +#include "LauParameter.hh" + +ClassImp(LauAbsRValue); + +void LauAbsRValue::serialiseToJson( nlohmann::json& j ) const +{ + j["name"] = this->name(); + j["isLValue"] = this->isLValue(); +} + +std::vector> LauAbsRValue::readFromJson( const TString& fileName, const TString& elementName ) +{ + // NB deliberately not using uniform initialisation here because of this issue: + // https://json.nlohmann.me/home/faq/#brace-initialization-yields-arrays + const nlohmann::json j = LauJsonTools::readJsonFile( fileName.Data(), elementName.Data(), LauJsonTools::JsonType::Array ); + if ( ! j.is_array() ) { + if ( elementName != "" ) { + std::cerr << "ERROR in LauAbsRValue::readFromJson : unable to retrieve JSON array from element \"" << elementName << "\" in file \"" << fileName << "\"" << std::endl; + } else { + std::cerr << "ERROR in LauAbsRValue::readFromJson : unable to retrieve JSON array from root element of file \"" << fileName << "\"" << std::endl; + } + return {}; + } + + return readFromJson( j ); +} + +std::vector> LauAbsRValue::readFromJson( const nlohmann::json& array ) +{ + using nlohmann::json; + using LauJsonTools::InvalidJson; + using LauJsonTools::JsonType; + using LauJsonTools::ElementNameType; + using LauJsonTools::checkObjectElements; + using LauJsonTools::getValue; + + if ( ! array.is_array() ) { + std::cerr << "ERROR in LauAbsRValue::readFromJson : supplied value is not an array" << std::endl; + return {}; + } + + std::vector mandatoryElements { + std::make_pair("name", JsonType::String), + std::make_pair("isLValue", JsonType::Boolean) + }; + + Bool_t allOK{kTRUE}; + for ( auto& par : array ) { + allOK &= checkObjectElements( par, mandatoryElements ); + } + if ( ! allOK ) { + std::cerr << "ERROR in LauAbsRValue::readFromJson : aborting processing due to mis-formatted elements" << std::endl; + return {}; + } + + const auto nPars { array.size() }; + + std::vector> pars; + std::vector formulaPars; + std::vector clonedPars; + + pars.reserve( nPars ); + formulaPars.reserve( nPars ); + clonedPars.reserve( nPars ); + + for ( auto& par : array ) { + + // If it's a LauFormulaPar, we save it for later + if ( ! getValue( par, "isLValue" ) ) { + formulaPars.emplace_back( par ); + continue; + } + + // If it's a cloned LauParameter, we save it for later + if ( getValue( par, "clone" ) ) { + clonedPars.emplace_back( par ); + continue; + } + + // Otherwise store an instance constructed from the JSON record + pars.emplace_back( std::make_unique( par.get() ) ); + } + + // Now construct the clones + mandatoryElements = { + std::make_pair("parent", JsonType::String), + std::make_pair("constFactor", JsonType::Number) + }; + + allOK = kTRUE; + for ( auto& par : clonedPars ) { + allOK &= checkObjectElements( par, mandatoryElements ); + } + if ( ! allOK ) { + std::cerr << "ERROR in LauAbsRValue::readFromJson : aborting processing due to mis-formatted elements" << std::endl; + return {}; + } + + for ( auto& par : clonedPars ) { + const auto name { getValue( par, "name" ) }; + const auto parentName { getValue( par, "parent" ) }; + + // Find the parent of this coefficient set + auto parent = std::find_if( pars.begin(), pars.end(), [&parentName](const std::unique_ptr& c){ return c->name() == parentName; } ); + if ( parent == pars.end() ) { + const TString errMsg {"Cannot locate parent \"" + parentName + "\" for cloned parameter \"" + name + "\""}; + throw InvalidJson{errMsg.Data()}; + } + + const auto constFactor { getValue( par, "constFactor" ) }; + + // Create a clone from the parent + LauParameter* lpar { dynamic_cast( parent->get() ) }; + pars.emplace_back( lpar->createClone( name, constFactor ) ); + } + + // Now construct the LauFormulaPar's + mandatoryElements = { + std::make_pair("formula", JsonType::String), + std::make_pair("parameters", JsonType::Array) + }; + + allOK = kTRUE; + for ( auto& par : formulaPars ) { + allOK &= checkObjectElements( par, mandatoryElements ); + } + if ( ! allOK ) { + std::cerr << "ERROR in LauAbsRValue::readFromJson : aborting processing due to mis-formatted elements" << std::endl; + return {}; + } + + for ( auto& par : formulaPars ) { + const auto name { getValue( par, "name" ) }; + const auto formula { getValue( par, "formula" ) }; + const auto parNames { getValue>( par, "parameters" ) }; + + // Find the parameters on which this formula depends + std::vector params; + params.reserve( parNames.size() ); + + for ( const auto& parName : parNames ) { + auto param = std::find_if( pars.begin(), pars.end(), [&parName](const std::unique_ptr& c){ return c->name() == parName; } ); + if ( param == pars.end() ) { + const TString errMsg {"Cannot locate parameter \"" + parName + "\" on which the formula \"" + name + "\" depends"}; + throw InvalidJson{errMsg.Data()}; + } + + LauParameter* lpar { dynamic_cast( param->get() ) }; + params.push_back( lpar ); + } + + // Create a clone from the parent + pars.emplace_back( std::make_unique( name, formula, params ) ); + } + + return pars; +} diff --git a/src/LauFormulaPar.cc b/src/LauFormulaPar.cc index 03883c7..6017d56 100644 --- a/src/LauFormulaPar.cc +++ b/src/LauFormulaPar.cc @@ -1,197 +1,144 @@ /* Copyright 2014 University of Warwick Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ /* Laura++ package authors: John Back Paul Harrison Thomas Latham */ /*! \file LauFormulaPar.cc \brief File containing implementation of LauFormulaPar class. */ -#include -#include -#include -#include +#include "LauFormulaPar.hh" + +#include "LauJsonTools.hh" +#include "LauParameter.hh" -#include "TRandom.h" -#include "TMessage.h" #include "TSystem.h" -#include "LauFormulaPar.hh" -#include "LauParameter.hh" -#include "LauRandom.hh" +#include +#include +#include +#include +#include ClassImp(LauFormulaPar) LauFormulaPar::LauFormulaPar(const TString& forName, const TString& formula, const std::vector& params) : - name_(forName), - formula_(forName,formula), - paramVec_(params), - paramArray_(nullptr), - gaussConstraint_(kFALSE), - constraintMean_(0.0), - constraintWidth_(0.0) + name_{forName}, + formula_{forName,formula}, + params_{params} { // Check length of vector matches number of parameter in the formula - Int_t nPars = paramVec_.size(); + Int_t nPars = params_.size(); if (formula_.GetNpar() != nPars){ std::cerr<<"ERROR in LauFormulaPar::evaluate : Number of parameters in the formula is : "<Exit(EXIT_FAILURE); } if (formula_.GetNdim() != 0){ std::cerr<<"ERROR in LauFormulaPar::evaluate : Given formula of dimension: "<Exit(EXIT_FAILURE); } - // Array of input parameters - paramArray_ = new Double_t[nPars]; -} - -LauFormulaPar::~LauFormulaPar() -{ - delete[] paramArray_; -} - -LauFormulaPar::LauFormulaPar(const LauFormulaPar& rhs) : LauAbsRValue(rhs), - name_(rhs.name_), - formula_(rhs.formula_), - paramVec_(rhs.paramVec_), - paramArray_(nullptr), - gaussConstraint_(rhs.gaussConstraint_), - constraintMean_(rhs.constraintMean_), - constraintWidth_(rhs.constraintWidth_) -{ - // Check length of vector matches number of parameter in the formula - Int_t nPars = paramVec_.size(); - if (formula_.GetNpar() != nPars){ - std::cerr<<"ERROR in LauFormulaPar::evaluate : Number of parameters in the formula is : "<Exit(EXIT_FAILURE); - } - - if (formula_.GetNdim() != 0){ - std::cerr<<"ERROR in LauFormulaPar::evaluate : Given formula of dimension: "<Exit(EXIT_FAILURE); - } - - // Array of input parameters - paramArray_ = new Double_t[nPars]; -} - -LauFormulaPar& LauFormulaPar::operator=(const LauFormulaPar& rhs) -{ - if ( &rhs != this ) { - name_ = rhs.name_; - formula_ = rhs.formula_; - - Int_t nOldPars = paramVec_.size(); - Int_t nNewPars = rhs.paramVec_.size(); - - paramVec_ = rhs.paramVec_; - if ( nOldPars != nNewPars ) { - delete [] paramArray_; - paramArray_ = new Double_t[nNewPars]; - } - - gaussConstraint_ = rhs.gaussConstraint_; - constraintMean_ = rhs.constraintMean_; - constraintWidth_ = rhs.constraintWidth_; - } - return *this; + paramVals_.resize( params_.size() ); } Double_t LauFormulaPar::value() const { - //Assign vector values to array - Int_t nPars = paramVec_.size(); + static auto assignValue = [](const LauParameter* par){return par->value();}; - for(Int_t i=0; ivalue(); - } + std::transform( params_.begin(), params_.end(), paramVals_.begin(), assignValue ); - return formula_.EvalPar(nullptr,paramArray_); + return formula_.EvalPar( nullptr, paramVals_.data() ); } Double_t LauFormulaPar::unblindValue() const { - //Assign vector values to array - Int_t nPars = paramVec_.size(); + static auto assignValue = [](const LauParameter* par){return par->unblindValue();}; - for(Int_t i=0; iunblindValue(); - } + std::transform( params_.begin(), params_.end(), paramVals_.begin(), assignValue ); - return formula_.EvalPar(nullptr,paramArray_); + return formula_.EvalPar( nullptr, paramVals_.data() ); } Double_t LauFormulaPar::genValue() const { - //Assign vector values to array - Int_t nPars = paramVec_.size(); + static auto assignValue = [](const LauParameter* par){return par->genValue();}; - for(Int_t i=0; igenValue(); - } + std::transform( params_.begin(), params_.end(), paramVals_.begin(), assignValue ); - return formula_.EvalPar(nullptr,paramArray_); + return formula_.EvalPar( nullptr, paramVals_.data() ); } Double_t LauFormulaPar::initValue() const { - //Assign vector values to array - Int_t nPars = paramVec_.size(); + static auto assignValue = [](const LauParameter* par){return par->initValue();}; - for(Int_t i=0; iinitValue(); - } + std::transform( params_.begin(), params_.end(), paramVals_.begin(), assignValue ); - return formula_.EvalPar(nullptr,paramArray_); + return formula_.EvalPar( nullptr, paramVals_.data() ); } Bool_t LauFormulaPar::fixed() const { - for ( std::vector::const_iterator iter = paramVec_.begin(); iter != paramVec_.end(); ++iter ) { - if ( !(*iter)->fixed() ) { return kFALSE; } - } - return kTRUE; + static auto isFixed = [](const LauParameter* par){return par->fixed();}; + + return std::all_of( params_.begin(), params_.end(), isFixed ); } Bool_t LauFormulaPar::blind() const { - for ( std::vector::const_iterator iter = paramVec_.begin(); iter != paramVec_.end(); ++iter ) { - if ( (*iter)->blind() ) { return kTRUE; } - } - return kFALSE; + static auto isBlind = [](const LauParameter* par){return par->blind();}; + + return std::any_of( params_.begin(), params_.end(), isBlind ); } -void LauFormulaPar::addGaussianConstraint(Double_t newGaussMean, Double_t newGaussWidth) +void LauFormulaPar::addGaussianConstraint(const Double_t newGaussMean, const Double_t newGaussWidth) { gaussConstraint_ = kTRUE; constraintMean_ = newGaussMean; constraintWidth_ = newGaussWidth; } void LauFormulaPar::removeGaussianConstraint() { gaussConstraint_ = kFALSE; } +void LauFormulaPar::serialiseToJson( nlohmann::json& j ) const +{ + LauAbsRValue::serialiseToJson( j ); + + j["formula"] = formula_.GetExpFormula().Data(); + + j["parameters"] = nlohmann::json::array(); + for ( const LauParameter* par : params_ ) { + j["parameters"].push_back( par->name().Data() ); + } + + const Bool_t gaussCons { this->gaussConstraint() }; + j["gaussConstraint"] = gaussCons; + if ( gaussCons ) { + j["constraintMean"] = this->constraintMean(); + j["constraintWidth"] = this->constraintWidth(); + } +} diff --git a/src/LauParameter.cc b/src/LauParameter.cc index 6746547..6e896ad 100644 --- a/src/LauParameter.cc +++ b/src/LauParameter.cc @@ -1,614 +1,744 @@ /* Copyright 2006 University of Warwick Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ /* Laura++ package authors: John Back Paul Harrison Thomas Latham */ /*! \file LauParameter.cc \brief File containing implementation of LauParameter class. */ -#include -#include +#include "LauParameter.hh" + +#include "LauJsonTools.hh" +#include "LauRandom.hh" #include "TRandom.h" -#include "LauParameter.hh" -#include "LauRandom.hh" +#include +#include ClassImp(LauParameter) LauParameter::LauParameter(const TString& parName) : name_{parName} { } LauParameter::LauParameter(const Double_t parValue) : value_{parValue}, genValue_{parValue}, initValue_{parValue}, minValue_{parValue-1e-6}, maxValue_{parValue+1e-6} { } LauParameter::LauParameter(const TString& parName, const Double_t parValue) : name_{parName}, value_{parValue}, genValue_{parValue}, initValue_{parValue}, minValue_{parValue-1e-6}, maxValue_{parValue+1e-6} { } LauParameter::LauParameter(const Double_t parValue, const Double_t min, const Double_t max) : value_{parValue}, genValue_{parValue}, initValue_{parValue}, minValue_{min}, maxValue_{max} { this->checkRange(); } LauParameter::LauParameter(const Double_t parValue, const Double_t parError, const Double_t min, const Double_t max) : value_{parValue}, error_{parError}, genValue_{parValue}, initValue_{parValue}, minValue_{min}, maxValue_{max} { this->checkRange(); } LauParameter::LauParameter(const Double_t parValue, const Double_t min, const Double_t max, const Bool_t parFixed) : value_{parValue}, genValue_{parValue}, initValue_{parValue}, minValue_{min}, maxValue_{max}, fixed_{parFixed} { this->checkRange(); } LauParameter::LauParameter(const TString& parName, const Double_t parValue, const Double_t min, const Double_t max) : name_{parName}, value_{parValue}, genValue_{parValue}, initValue_{parValue}, minValue_{min}, maxValue_{max} { this->checkRange(); } LauParameter::LauParameter(const TString& parName, const Double_t parValue, const Double_t min, const Double_t max, const Bool_t parFixed) : name_{parName}, value_{parValue}, genValue_{parValue}, initValue_{parValue}, minValue_{min}, maxValue_{max}, fixed_{parFixed} { this->checkRange(); } LauParameter::LauParameter(const TString& parName, const Double_t parValue, const Double_t parError, const Double_t min, const Double_t max) : name_{parName}, value_{parValue}, error_{parError}, genValue_{parValue}, initValue_{parValue}, minValue_{min}, maxValue_{max} { this->checkRange(); } +/* LauParameter::LauParameter(const LauParameter& rhs) : TObject(rhs), LauAbsRValue(rhs), name_{rhs.name_}, value_{rhs.value_}, error_{rhs.error_}, negError_{rhs.negError_}, posError_{rhs.posError_}, genValue_{rhs.genValue_}, initValue_{rhs.initValue_}, minValue_{rhs.minValue_}, maxValue_{rhs.maxValue_}, fixed_{rhs.fixed_}, secondStage_{rhs.secondStage_}, gaussConstraint_{rhs.gaussConstraint_}, constraintMean_{rhs.constraintMean_}, constraintWidth_{rhs.constraintWidth_}, gcc_{rhs.gcc_}, bias_{rhs.bias_}, pull_{rhs.pull_}, - clone_{rhs.clone_}, parent_{rhs.parent_}, clones_{rhs.clones_}, blinder_{(rhs.blinder_) ? std::make_unique(*(rhs.blinder_)) : nullptr} { } LauParameter& LauParameter::operator=(const LauParameter& rhs) { if (&rhs != this) { TObject::operator=(rhs); LauAbsRValue::operator=(rhs); name_ = rhs.name_; value_ = rhs.value_; error_ = rhs.error_; negError_ = rhs.negError_; posError_ = rhs.posError_; genValue_ = rhs.genValue_; initValue_ = rhs.initValue_; minValue_ = rhs.minValue_; maxValue_ = rhs.maxValue_; fixed_ = rhs.fixed_; secondStage_ = rhs.secondStage_; gaussConstraint_ = rhs.gaussConstraint_; constraintMean_ = rhs.constraintMean_; constraintWidth_ = rhs.constraintWidth_; gcc_ = rhs.gcc_; bias_ = rhs.bias_; pull_ = rhs.pull_; - clone_ = rhs.clone_; parent_ = rhs.parent_; clones_ = rhs.clones_; blinder_.reset(); if ( rhs.blinder_ ) { blinder_ = std::make_unique(*(rhs.blinder_)); } } return *this; } +*/ LauParameter::~LauParameter() noexcept { // if we're a clone, we just need to inform our parent of our demise if ( this->clone() ) { parent_->removeFromCloneList(this); return; } // if we have no clones there's nothing to do if ( clones_.empty() ) { return; } // otherwise if we have clones we need to make one of them the new parent and inform the rest of the change // let's (arbitrarily) make the first parameter in the map the new parent auto iter = clones_.begin(); auto [ newParent, constFactor ] { *iter }; // remove that entry in the map clones_.erase(iter); // for all the other entries, we need to tell them they are clones of the new parent // and also rescale the constants so that they are relative to the new parent for ( auto& [ theClone, theFactor ] : clones_ ) { theClone->clone(newParent); theFactor /= constFactor; } // transfer the list of clones to the new parent newParent->clones_ = std::move(clones_); // finally, the new parent has to be told that it isn't a clone anymore newParent->clone(nullptr); } std::vector LauParameter::getPars() { std::vector list; list.push_back(this); return list; } void LauParameter::value(const Double_t newValue) { if (this->clone()) { parent_->value(newValue); } else { this->checkRange(newValue,this->minValue(),this->maxValue()); this->updateClones(kTRUE); } } void LauParameter::error(const Double_t newError) { if (this->clone()) { parent_->error(newError); } else { error_ = TMath::Abs(newError); this->updateClones(kFALSE); } } void LauParameter::negError(const Double_t newNegError) { if (this->clone()) { parent_->negError(newNegError); } else { negError_ = TMath::Abs(newNegError); this->updateClones(kFALSE); } } void LauParameter::posError(const Double_t newPosError) { if (this->clone()) { parent_->posError(newPosError); } else { posError_ = TMath::Abs(newPosError); this->updateClones(kFALSE); } } void LauParameter::errors(const Double_t newError, const Double_t newNegError, const Double_t newPosError) { if (this->clone()) { parent_->errors(newError,newNegError,newPosError); } else { error_ = TMath::Abs(newError); negError_ = TMath::Abs(newNegError); posError_ = TMath::Abs(newPosError); this->updateClones(kFALSE); } } void LauParameter::valueAndErrors(const Double_t newValue, const Double_t newError, const Double_t newNegError, const Double_t newPosError) { if (this->clone()) { parent_->valueAndErrors(newValue,newError,newNegError,newPosError); } else { this->checkRange(newValue,this->minValue(),this->maxValue()); error_ = TMath::Abs(newError); negError_ = TMath::Abs(newNegError); posError_ = TMath::Abs(newPosError); this->updateClones(kFALSE); } } void LauParameter::globalCorrelationCoeff(const Double_t newGCCValue) { if (this->clone()) { parent_->globalCorrelationCoeff(newGCCValue); } else { gcc_ = newGCCValue; this->updateClones(kFALSE); } } void LauParameter::genValue(const Double_t newGenValue) { if (this->clone()) { parent_->genValue(newGenValue); } else { genValue_ = newGenValue; this->updateClones(kFALSE); } } void LauParameter::initValue(const Double_t newInitValue) { if (this->clone()) { parent_->initValue(newInitValue); } else { initValue_ = newInitValue; this->updateClones(kFALSE); } } void LauParameter::minValue(const Double_t newMinValue) { if (this->clone()) { parent_->minValue(newMinValue); } else { this->checkRange(this->value(),newMinValue,this->maxValue()); this->updateClones(kFALSE); } } void LauParameter::maxValue(const Double_t newMaxValue) { if (this->clone()) { parent_->maxValue(newMaxValue); } else { this->checkRange(this->value(),this->minValue(),newMaxValue); this->updateClones(kFALSE); } } void LauParameter::range(const Double_t newMinValue, const Double_t newMaxValue) { if (this->clone()) { parent_->range(newMinValue,newMaxValue); } else { this->checkRange(this->value(),newMinValue,newMaxValue); this->updateClones(kFALSE); } } void LauParameter::valueAndRange(const Double_t newValue, const Double_t newMinValue, const Double_t newMaxValue) { if (this->clone()) { parent_->valueAndRange(newValue,newMinValue,newMaxValue); } else { this->checkRange(newValue,newMinValue,newMaxValue); this->updateClones(kFALSE); } } void LauParameter::name(const TString& newName) { // no need to update clones here // clones are allowed to have different names name_ = newName; } void LauParameter::fixed(const Bool_t parFixed) { if (this->clone()) { parent_->fixed(parFixed); } else { fixed_ = parFixed; this->updateClones(kFALSE); } } void LauParameter::secondStage(const Bool_t secondStagePar) { if (this->clone()) { parent_->secondStage(secondStagePar); } else { secondStage_ = secondStagePar; this->updateClones(kFALSE); } } void LauParameter::addGaussianConstraint(const Double_t newGaussMean, const Double_t newGaussWidth) { if (this->clone()) { parent_->addGaussianConstraint(newGaussMean,newGaussWidth); } else { gaussConstraint_ = kTRUE; constraintMean_ = newGaussMean; constraintWidth_ = newGaussWidth; this->updateClones(kFALSE); } } void LauParameter::removeGaussianConstraint() { if (this->clone()) { parent_->removeGaussianConstraint(); } else { gaussConstraint_ = kFALSE; this->updateClones(kFALSE); } } void LauParameter::blindParameter(const TString& blindingString, const Double_t width, const Bool_t flipSign) { if (this->clone()) { parent_->blindParameter(blindingString,width,flipSign); return; } - if ( blinder_ ) { + if ( blinder_.has_value() ) { std::cerr << "WARNING in LauParameter::blindParameter : blinding has already been set up for this parameter" << std::endl; return; } - blinder_ = std::make_unique(blindingString,width,flipSign); + blinder_.emplace( blindingString, width, flipSign ); for ( auto& [ clonePar, _ ] : clones_ ) { - if ( clonePar->blinder_ != nullptr ) { + if ( clonePar->blinder_.has_value() ) { std::cerr << "WARNING in LauParameter::blindParameter : blinding has already been set up for a clone of this parameter - it will be replaced!" << std::endl; clonePar->blinder_.reset(); } - clonePar->blinder_ = std::make_unique(*blinder_); + clonePar->blinder_ = blinder_; } } void LauParameter::updatePull() { if (this->clone()) { parent_->updatePull(); return; } // calculate the bias bias_ = value_ - genValue_; // if we have errors calculated then calculate // the pull using the best error available if ((bias_ > 0.0) && (negError_ > 1e-10)) { pull_ = bias_ / negError_; } else if ((bias_ < 0.0) && (posError_ > 1e-10)) { pull_ = bias_ / posError_; } else if (error_ > 1e-10) { pull_ = bias_ / error_; } else { pull_ = 0.0; } this->updateClones(kFALSE); } void LauParameter::checkRange(const Double_t val, const Double_t minVal, const Double_t maxVal) { // first check that min is less than max (or they are the same - this is allowed) if (minVal > maxVal) { std::cerr<<"ERROR in LauParameter::checkRange : minValue: "< maxValue_) { minValue_ = maxValue_; std::cerr<<" : Setting both to "< maxVal)) { if (name_ != "") { std::cerr<<"ERROR in LauParameter::checkRange : value: "<clone()) { LauParameter* clonePar = parent_->createClone(constFactor); clonePar->name(this->name()); return clonePar; } // clone ourselves using the copy-constructor LauParameter* clonePar = new LauParameter(*this); Double_t newValue = clonePar->value() * constFactor; clonePar->value( newValue ); clonePar->wipeClones(); clonePar->clone(this); clones_.insert( std::make_pair( clonePar, constFactor ) ); return clonePar; } LauParameter* LauParameter::createClone(const TString& newName, const Double_t constFactor) { // self message to create the clone LauParameter* clonePar = this->createClone(constFactor); // set the new name clonePar->name(newName); // and return return clonePar; } void LauParameter::updateClones(const Bool_t justValue) { // if we don't have any clones then there's nothing to do if ( clones_.empty() ) { return; } // we have to set the values directly rather than using member functions because otherwise we'd get into an infinite loop if (justValue) { for ( auto& [ clonePar, constFactor ] : clones_ ) { clonePar->value_ = constFactor*value_; } } else { for ( auto& [ clonePar, constFactor ] : clones_ ) { clonePar->value_ = constFactor*value_; clonePar->error_ = constFactor*error_; clonePar->negError_ = constFactor*negError_; clonePar->posError_ = constFactor*posError_; clonePar->genValue_ = constFactor*genValue_; clonePar->initValue_ = constFactor*initValue_; clonePar->minValue_ = constFactor*minValue_; clonePar->maxValue_ = constFactor*maxValue_; clonePar->fixed_ = fixed_; clonePar->secondStage_ = secondStage_; clonePar->gaussConstraint_ = gaussConstraint_; clonePar->constraintMean_ = constraintMean_; clonePar->constraintWidth_ = constraintWidth_; clonePar->gcc_ = gcc_; clonePar->bias_ = bias_; clonePar->pull_ = pull_; } } } void LauParameter::randomiseValue() { this->randomiseValue(this->minValue(), this->maxValue()); } void LauParameter::randomiseValue(const Double_t minVal, const Double_t maxVal) { // if we're fixed then do nothing if (this->fixed()) { return; } // check supplied values are sensible if (maxVal < minVal) { std::cerr<<"ERROR in LauParameter::randomiseValue : Supplied maximum value smaller than minimum value."<Uniform( TMath::Max( minVal, this->minValue() ), TMath::Min( maxVal, this->maxValue() ) ) }; this->initValue(val); } +void LauParameter::serialiseToJson( nlohmann::json& j ) const +{ + using nlohmann::json; + + LauAbsRValue::serialiseToJson( j ); + + // First check whether this is a clone because there's much less to do if it is + const Bool_t cloned { this->clone() }; + j["clone"] = cloned; + if ( cloned ) { + j["parent"] = parent_->name(); + auto me {const_cast(this)}; + j["constFactor"] = parent_->clones_.at(me); + return; + } + + // TODO - do we need to write this? + // - I guess it's maybe useful for a human reader but we don't use it at all in deserialisation + if ( ! clones_.empty() ) { + j["clones"] = json::array(); + for ( auto& [ par, factor ] : clones_ ) { + json obj = json::object( { {"name", par->name()}, {"factor", factor} } ); + j["clones"].push_back( obj ); + } + } + + j["value"] = value_; + j["genValue"] = genValue_; + j["initValue"] = initValue_; + + j["error"] = error_; + j["negError"] = negError_; + j["posError"] = posError_; + + j["minValue"] = minValue_; + j["maxValue"] = maxValue_; + + j["secondStage"] = secondStage_; + + j["gcc"] = gcc_; + j["bias"] = bias_; + j["pull"] = pull_; + + j["fixed"] = fixed_; + + const Bool_t blind { this->blind() }; + j["blind"] = blind; + if ( blind ) { + j["blindingString"] = blinder_->blindingString(); + j["blindingWidth"] = blinder_->blindingWidth(); + j["blindingFlip"] = blinder_->flipSign(); + } + + const Bool_t gaussCons { this->gaussConstraint() }; + j["gaussConstraint"] = gaussCons; + if ( gaussCons ) { + j["constraintMean"] = this->constraintMean(); + j["constraintWidth"] = this->constraintWidth(); + } +} + // ostream operator std::ostream& operator<<(std::ostream& stream, const LauParameter& par) { stream << par.name() << " : "; stream << par.value(); if ( par.negError() > 1e-10 && par.posError() > 1e-10 ) { stream << " +/- (" << par.negError() << ", " << par.posError() << ")"; } else if ( par.error() > 1e-10 ) { stream << " +/- " << par.error(); } if ( par.fixed() ) { stream << " C"; } stream << " L(" << par.minValue() << ", " << par.maxValue() << ")"; if ( par.blind() ) { stream << " BLIND(" << par.blinder()->blindingString() << ", " << par.blinder()->blindingWidth() << ", " << std::boolalpha << par.blinder()->flipSign() << ")"; } return stream; } + +//! \cond DOXYGEN_IGNORE +LauParameter nlohmann::adl_serializer::from_json(const json& j) +{ + using LauJsonTools::getValue; + + const auto isLValue { getValue( j, "isLValue" ) }; + if ( ! isLValue ) { + throw LauJsonTools::InvalidJson{"Cannot create LauParameter from non L-value"}; + } + + const auto clone { getValue( j, "clone" ) }; + if ( clone ) { + throw LauJsonTools::InvalidJson{"Cannot create cloned LauParameter standalone"}; + } + + // Get everything we need to construct the parameter + const auto name { getValue( j, "name" ) }; + + const auto value { getValue( j, "value" ) }; + + const auto minValue { getValue( j, "minValue" ) }; + const auto maxValue { getValue( j, "maxValue" ) }; + + const auto fixed { getValue( j, "fixed" ) }; + + LauParameter par { name, value, minValue, maxValue, fixed }; + + // Then get any additional information and use the appropriate functions to set the values + + const auto genValue { getValue( j, "genValue" ) }; + par.genValue( genValue ); + + const auto initValue { getValue( j, "initValue" ) }; + par.initValue( initValue ); + + const auto error { getValue( j, "error" ) }; + const auto negError { getValue( j, "negError" ) }; + const auto posError { getValue( j, "posError" ) }; + par.errors( error, negError, posError ); + + par.updatePull(); + + const auto secondStage { getValue( j, "secondStage" ) }; + par.secondStage( secondStage ); + + const auto gcc { getValue( j, "gcc" ) }; + par.globalCorrelationCoeff( gcc ); + + const auto gaussCons { getValue( j, "gaussConstraint" ) }; + if ( gaussCons ) { + const auto mean { getValue( j, "constraintMean" ) }; + const auto width { getValue( j, "constraintWidth" ) }; + par.addGaussianConstraint( mean, width ); + } + + const auto blind { getValue( j, "blind" ) }; + if ( blind ) { + const auto blindingStr { getValue( j, "blindingString" ) }; + const auto blindingWidth { getValue( j, "blindingWidth" ) }; + const auto blindingFlip { getValue( j, "blindingFlip" ) }; + par.blindParameter( blindingStr, blindingWidth, blindingFlip ); + } + + return par; +} +//! \endcond DOXYGEN_IGNORE diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 9f13296..f7a6dc4 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,26 +1,28 @@ list(APPEND TEST_SOURCES TestCovariant TestCovariant2 TestNewKinematicsMethods TestFitSplineToTH1 TestFitDoubleSplineToTH1 TestSplineDTAdivision TestWriteCoeffSetToJson + TestWriteParametersToJson TestWriteSplineToJson TestWriteVetoesToJson TestReadCoeffSetFromJson TestReadDPModelFromJson + TestReadParametersFromJson TestReadSplineFromJson TestReadVetoesFromJson TestSplineFindMax ) foreach( _test ${TEST_SOURCES}) add_executable(${_test} ${_test}.cc) target_link_libraries(${_test} PRIVATE Laura++) install(TARGETS ${_test} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) endforeach() install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/test-model.json ${CMAKE_CURRENT_SOURCE_DIR}/test-coeffset.json DESTINATION ${CMAKE_INSTALL_BINDIR}) diff --git a/test/TestReadParametersFromJson.cc b/test/TestReadParametersFromJson.cc new file mode 100644 index 0000000..60bb599 --- /dev/null +++ b/test/TestReadParametersFromJson.cc @@ -0,0 +1,43 @@ +#include "LauAbsRValue.hh" +#include "LauFormulaPar.hh" +#include "LauParameter.hh" + +#include + +#include +#include +#include + +int main() +{ + std::cout << std::boolalpha; + + TString fileName { "kstar-masses.json" }; + std::vector> params { LauAbsRValue::readFromJson( fileName ) }; + + std::cout << "Read " << params.size() << " params from file: kstar-masses.json\n" << std::endl; + + for ( const auto& param : params ) { + if ( param->isLValue() ) { + auto par { static_cast( param.get() ) }; + std::cout << *par << std::endl; + } + } + + + fileName = "b-mass-shift.json"; + params = LauAbsRValue::readFromJson( fileName ); + + std::cout << "\nRead " << params.size() << " params from file b-mass-shift.json\n" << std::endl; + + for ( const auto& param : params ) { + if ( param->isLValue() ) { + auto par { static_cast( param.get() ) }; + std::cout << *par << std::endl; + } else { + std::cout << param->name() << " = " << param->value() << std::endl; + } + } + + +} diff --git a/test/TestWriteParametersToJson.cc b/test/TestWriteParametersToJson.cc new file mode 100644 index 0000000..bc6f5a4 --- /dev/null +++ b/test/TestWriteParametersToJson.cc @@ -0,0 +1,42 @@ +#include "LauAbsRValue.hh" +#include "LauFormulaPar.hh" +#include "LauParameter.hh" + +#include + +#include +#include +#include +#include +#include +#include + +int main() +{ + std::vector> params; + + auto kstarp_mass { std::make_unique("Kstarp_MASS", 0.892, 0.70, 1.10, kFALSE) }; + kstarp_mass->blindParameter("hellothere", 0.2); + kstarp_mass->secondStage(kTRUE); + auto kstarm_mass { kstarp_mass->createClone("Kstarm_MASS") }; + params.emplace_back( std::move(kstarp_mass) ); + params.emplace_back( std::move(kstarm_mass) ); + + LauAbsRValue::writeToJson( params, "kstar-masses.json" ); + + params.clear(); + + auto mc_mass { std::make_unique("MC_mass", 5.279) }; + auto data_mc_mass_shift { std::make_unique("data_mc_mass_shift", 0.002, -0.020, 0.020, kFALSE) }; + + std::vector pars { mc_mass.get(), data_mc_mass_shift.get() }; + + auto data_mass { std::make_unique("data_mass", "[0] + [1]", pars) }; + + params.emplace_back( std::move(mc_mass) ); + params.emplace_back( std::move(data_mc_mass_shift) ); + params.emplace_back( std::move(data_mass) ); + + LauAbsRValue::writeToJson( params, "b-mass-shift.json" ); + +}