/*************************************************************************** * GOptimizerPar.cpp - Optimizer parameter class * * ----------------------------------------------------------------------- * * copyright (C) 2013-2015 by Juergen Knoedlseder * * ----------------------------------------------------------------------- * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * * * ***************************************************************************/ /** * @file GOptimizerPar.cpp * @brief Optimizer parameter class implementation * @author Juergen Knoedlseder */ /* __ Includes ___________________________________________________________ */ #ifdef HAVE_CONFIG_H #include #endif #include "GException.hpp" #include "GOptimizerPar.hpp" #include "GTools.hpp" /* __ Method name definitions ____________________________________________ */ #define G_CONSTRUCT "GOptimizerPar::GOptimizerPar(std::string&, double&,"\ " double&)" #define G_FACTOR_VALUE "GOptimizerPar::factor_value(double&)" #define G_FACTOR_MIN "GOptimizerPar::factor_min(double&)" #define G_FACTOR_MAX "GOptimizerPar::factor_max(double&)" #define G_SCALE "GOptimizerPar::scale(double&)" /* __ Macros _____________________________________________________________ */ /* __ Coding definitions _________________________________________________ */ /* __ Debug definitions __________________________________________________ */ /*========================================================================== = = = Constructors/destructors = = = ==========================================================================*/ /***********************************************************************//** * @brief Void constructor ***************************************************************************/ GOptimizerPar::GOptimizerPar(void) { // Initialise members init_members(); // Return return; } /***********************************************************************//** * @brief Parameter constructor * * @param[in] name Parameter name. * @param[in] value Parameter value. * * Constructs a parameter from a parameter @p name and a parameter @p value. * * The parameter is auto-scaled, which for a @p value that differs from zero * sets the scale factor to @p value and the @p factor_value to unity. For a * @p value of zero, the scale factor will be set to unity and the * @p factor_value will be set to @p value. ***************************************************************************/ GOptimizerPar::GOptimizerPar(const std::string& name, const double& value) { // Initialise members init_members(); // Set name attribute m_name = name; // Set auto-scaled factor and scale attributes if (value != 0.0) { m_factor_value = 1.0; m_scale = value; } else { m_factor_value = value; m_scale = 1.0; } // Return return; } /***********************************************************************//** * @brief Parameter constructor * * @param[in] name Parameter name. * @param[in] factor Parameter value factor. * @param[in] scale Parameter scaling (non-zero value). * * @exception GException::invalid_argument * Sacle factor of 0 specified. * * Constructs a parameter from a parameter @p name, value @p factor * and @p scale factor. The @p scale factor needs to be a non-zero value. * If the @p scale factor is zero, an exception is thrown. ***************************************************************************/ GOptimizerPar::GOptimizerPar(const std::string& name, const double& factor, const double& scale) { // Make sure that scale is not zero if (scale == 0.0) { std::string msg = "Specified a parameter scale factor of 0.\n" "Parameters need a non-zero scale factor."; throw GException::invalid_argument(G_CONSTRUCT, msg); } // Initialise members init_members(); // Set attributes m_name = name; m_factor_value = factor; m_scale = scale; // Return return; } /***********************************************************************//** * @brief Copy constructor * * @param[in] par Function parameter. ***************************************************************************/ GOptimizerPar::GOptimizerPar(const GOptimizerPar& par) { // Initialise members init_members(); // Copy members copy_members(par); // Return return; } /***********************************************************************//** * @brief Destructor ***************************************************************************/ GOptimizerPar::~GOptimizerPar(void) { // Free members free_members(); // Return return; } /*========================================================================== = = = Operators = = = ==========================================================================*/ /***********************************************************************//** * @brief Assignment operator * * @param[in] par Function parameter. * @return Function parameter. ***************************************************************************/ GOptimizerPar& GOptimizerPar::operator=(const GOptimizerPar& par) { // Execute only if object is not identical if (this != &par) { // Free members free_members(); // Initialise members init_members(); // Copy members copy_members(par); } // endif: object was not identical // Return return *this; } /*========================================================================== = = = Public methods = = = ==========================================================================*/ /***********************************************************************//** * @brief Clear parameter * * Resets parameter to a clean initial state. ***************************************************************************/ void GOptimizerPar::clear(void) { // Free class members free_members(); // Initialise members init_members(); // Return return; } /***********************************************************************//** * @brief Clone parameter * * @return Pointer to deep copy of parameter. ***************************************************************************/ GOptimizerPar* GOptimizerPar::clone(void) const { // Clone parameter return new GOptimizerPar(*this); } /***********************************************************************//** * @brief Set parameter value * * @param[in] value Parameter value. * * Sets the parameter value. The method stores the value factor which is * obtained by dividing the @p value by the scale factor. * * The method calls factor_value() for assigning the value factor, and this * method will verify that the value lies within the specified boundaries. ***************************************************************************/ void GOptimizerPar::value(const double& value) { // Set value factor. The GOptimizerPar class makes sure that m_scale is // never 0, so no test is needed here factor_value(value / m_scale); // Return return; } /***********************************************************************//** * @brief Set parameter error * * @param[in] error Parameter error. * * Sets the parameter error. The method stores the error factor which is * obtained by dividing the @p error by the scale factor. ***************************************************************************/ void GOptimizerPar::error(const double& error) { // Set error factor. The GOptimizerPar class makes sure that m_scale is // never 0, so no test is needed here factor_error(error / m_scale); // Return return; } /***********************************************************************//** * @brief Set parameter gradient * * @param[in] gradient Parameter gradient. * * Sets the parameter gradient. The method stores the gradient factor which * is obtained by multiplying @p gradient by the scale factor. ***************************************************************************/ void GOptimizerPar::gradient(const double& gradient) { // Set gradient factor. factor_gradient(gradient * m_scale); // Return return; } /***********************************************************************//** * @brief Set minimum parameter boundary * * @param[in] min Parameter minimum. * * Sets the minimum parameter boundary. The method stores the minimum * boundary factor which is obtained by dividing @p min by the scale factor. ***************************************************************************/ void GOptimizerPar::min(const double& min) { // Set minimum boundary factor. The GOptimizerPar class makes sure that // m_scale is never 0, so no test is needed here factor_min(min / m_scale); // Return return; } /***********************************************************************//** * @brief Set maximum parameter boundary * * @param[in] max Parameter maximum. * * Sets the maximum parameter boundary. The method stores the maximum * boundary factor which is obtained by dividing @p max by the scale factor. ***************************************************************************/ void GOptimizerPar::max(const double& max) { // Set maximum boundary factor. The GOptimizerPar class makes sure that // m_scale is never 0, so no test is needed here factor_max(max / m_scale); // Return return; } /***********************************************************************//** * @brief Set minimum and maximum parameter boundaries * * @param[in] min Parameter minimum. * @param[in] max Parameter maximum. * * Sets the minimum and maximum parameter boundaries. The method calls the * min() and max() methods to set the boundaries. ***************************************************************************/ void GOptimizerPar::range(const double& min, const double& max) { // Set minimum and maximum this->min(min); this->max(max); // Return return; } /***********************************************************************//** * @brief Set parameter value factor * * @param[in] value Value factor. * * @exception GException::invalid_argument * Parameter @p value outside [min,max] boundaries. * * Sets the value factor of the parameter. The method makes sure that * none of the boundaries is violated. Otherwise, exceptions are thrown. ***************************************************************************/ void GOptimizerPar::factor_value(const double& value) { // If there is a minimum boundary and if value is below this boundary // then throw an exception if (m_has_min && value < m_factor_min) { std::string msg = "Specified value factor "+gammalib::str(value)+ " is smaller than the minimum boundary "+ gammalib::str(m_factor_min)+"."; throw GException::invalid_argument(G_FACTOR_VALUE, msg); } // If there is a maximum boundary and if value is above this boundary // then throw an exception if (m_has_max && value > m_factor_max) { std::string msg = "Specified value factor "+gammalib::str(value)+ " is larger than the maximum boundary "+ gammalib::str(m_factor_max)+"."; throw GException::invalid_argument(G_FACTOR_VALUE, msg); } // Assign value m_factor_value = value; // Return return; } /***********************************************************************//** * @brief Set parameter minimum factor * * @param[in] min Minimum factor. * * @exception GException::invalid_argument * Parameter @p min larger than value factor. * * Sets the minimum boundary factor of the parameter. The method makes * sure that the minimum is not larger than the actual value factor. * Otherwise, an exception is thrown. ***************************************************************************/ void GOptimizerPar::factor_min(const double& min) { // Check if minimum is larger than value if (min > m_factor_value) { std::string msg = "Specified minimum factor "+gammalib::str(min)+ " is larger than the value factor "+ gammalib::str(m_factor_value)+"."; throw GException::invalid_argument(G_FACTOR_VALUE, msg); } // Assign minimum factor m_factor_min = min; // Flag that minimum was set m_has_min = true; // Return return; } /***********************************************************************//** * @brief Set parameter maximum factor * * @param[in] max Maximum factor. * * @exception GException::invalid_argument * Parameter @p max smaller than value factor. * * Sets the maximum boundary factor of the parameter. The method makes * sure that the maximum is not smaller than the actual value factor. * Otherwise, an exception is thrown. ***************************************************************************/ void GOptimizerPar::factor_max(const double& max) { // Check if maximum is smaller than value if (max < m_factor_value) { std::string msg = "Specified maximum factor "+gammalib::str(max)+ " is smaller than the value factor "+ gammalib::str(m_factor_value)+"."; throw GException::invalid_argument(G_FACTOR_VALUE, msg); } // Assign maximum m_factor_max = max; // Flag that maximum was set m_has_max = true; // Return return; } /***********************************************************************//** * @brief Set parameter minimum and maximum factors * * @param[in] min Minimum factor. * @param[in] max Maximum factor. * * Sets the minimum and maximum boundary factors. The method calls the * factor_min() and factor_max() methods which perform validity checking * of the arguments. ***************************************************************************/ void GOptimizerPar::factor_range(const double& min, const double& max) { // Set minimum and maximum factor_min(min); factor_max(max); // Return return; } /***********************************************************************//** * @brief Set scale factor * * @param[in] scale Scale factor. * * @exception GException::invalid_argument * Sacle factor of 0 specified. * * Sets the scale factor of the parameter. All parameter attributes are * rescaled accordingly. * * Special care is taken for value boundaries, that need swaping if the scale * factor changes its sign. In case of sign change the following logic applies: * - if minimum and maximum boundaries are set they are swapped * - if only a minimum boundary exists it will be replaced by a maximum boundary * - if only a maximum boundary exists it will be replaced by a minimum boundary * * An exception is thrown if a scale factor of 0 is specified. ***************************************************************************/ void GOptimizerPar::scale(const double& scale) { // Make sure that scale is not zero if (scale == 0.0) { std::string msg = "Specified parameter scale factor of 0.\n" "Parameters need a non-zero scale factor."; throw GException::invalid_argument(G_SCALE, msg); } // Set rescaling double rescale = m_scale/scale; // Set new scale factor m_scale = scale; // Set values, error, gradient, min and max m_factor_value *= rescale; m_factor_error *= rescale; m_factor_gradient *= rescale; if (m_has_min) { m_factor_min *= rescale; } if (m_has_max) { m_factor_max *= rescale; } // Takes care of boundaries in case of sign change if (rescale < 0.0) { if (m_has_min && m_has_max) { double swap = m_factor_min; m_factor_min = m_factor_max; m_factor_max = swap; } else if (m_has_min) { m_factor_max = m_factor_min; m_has_max = true; m_factor_min = 0.0; m_has_min = false; } else if (m_has_max) { m_factor_min = m_factor_max; m_has_min = true; m_factor_max = 0.0; m_has_max = false; } } // Return return; } /***********************************************************************//** * @brief Autoscale parameter * * Sets the value factor to unity and the scale factor to the real value * of the parameter. The method will also adjust the error factor and * gradient factor, as well as the minimum and maximum factors if they * exist. * * Special care is taken for value boundaries, that need swaping if the scale * factor changes its sign. In case of sign change the following logic applies: * - if minimum and maximum boundaries are set they are swapped * - if only a minimum boundary exists it will be replaced by a maximum boundary * - if only a maximum boundary exists it will be replaced by a minimum boundary * * The method does nothing if the actual value factor is zero. ***************************************************************************/ void GOptimizerPar::autoscale(void) { // Get value double value = this->value(); // Continue only if the value is non-zero if (value != 0.0) { // Get the renormalization factor double renormalization = m_scale / value; // Set the new scale factor to the actual value m_scale = value; // Renormalize values, error, gradient, min and max m_factor_value *= renormalization; m_factor_error *= renormalization; m_factor_gradient /= renormalization; if (m_has_min) { m_factor_min *= renormalization; } if (m_has_max) { m_factor_max *= renormalization; } // Takes care of boundaries in case of sign change if (renormalization < 0.0) { if (m_has_min && m_has_max) { double swap = m_factor_min; m_factor_min = m_factor_max; m_factor_max = swap; } else if (m_has_min) { m_factor_max = m_factor_min; m_has_max = true; m_factor_min = 0.0; m_has_min = false; } else if (m_has_max) { m_factor_min = m_factor_max; m_has_min = true; m_factor_max = 0.0; m_has_max = false; } } } // endif: value was non-zero // Return return; } /***********************************************************************//** * @brief Print parameter information * * @param[in] chatter Chattiness (defaults to NORMAL). * @return String with parameter information. ***************************************************************************/ std::string GOptimizerPar::print(const GChatter& chatter) const { // Initialise result string std::string result; // Continue only if chatter is not silent if (chatter != SILENT) { // Append parameter name result.append(gammalib::parformat(" "+name())); // Append value result.append(gammalib::str(value())); // For free parameters, append statistical uncertainty if (m_free) { result.append(" +/- "+gammalib::str(error())); } // Append parameter limites if they exist if (m_has_min && m_has_max) { result.append(" ["+gammalib::str(min()) + ","+gammalib::str(max())+"]"); } else if (m_has_min) { result.append(" ["+gammalib::str(min()) + ",infty["); } else if (m_has_max) { result.append(" ]-infty,"+gammalib::str(max())+"]"); } // Append parameter unit result.append(" "+m_unit); // Signal if parameter was free or fixed if (m_free) { result.append(" (free"); } else { result.append(" (fixed"); } // Append parameter scale result.append(",scale="+gammalib::str(m_scale)); // Signal if parameter has analytic gradient if (m_has_grad) { result.append(",gradient)"); } else { result.append(")"); } } // endif: chatter was not silent // Return result return result; } /*========================================================================== = = = Private methods = = = ==========================================================================*/ /***********************************************************************//** * @brief Initialise class members ***************************************************************************/ void GOptimizerPar::init_members(void) { // Initialise members m_name = ""; m_unit = ""; m_factor_value = 0.0; m_factor_error = 0.0; m_factor_gradient = 0.0; m_factor_min = 0.0; m_factor_max = 0.0; m_scale = 1.0; m_free = true; m_has_min = false; m_has_max = false; m_has_grad = false; // Return return; } /***********************************************************************//** * @brief Copy class members * * @param[in] par Model parameter. ***************************************************************************/ void GOptimizerPar::copy_members(const GOptimizerPar& par) { // Copy members m_name = par.m_name; m_unit = par.m_unit; m_factor_value = par.m_factor_value; m_factor_error = par.m_factor_error; m_factor_gradient = par.m_factor_gradient; m_factor_min = par.m_factor_min; m_factor_max = par.m_factor_max; m_scale = par.m_scale; m_free = par.m_free; m_has_min = par.m_has_min; m_has_max = par.m_has_max; m_has_grad = par.m_has_grad; // Return return; } /***********************************************************************//** * @brief Delete class members ***************************************************************************/ void GOptimizerPar::free_members(void) { // Return return; }