diff --git a/App/inc/ROOTMinimizer.h b/App/inc/ROOTMinimizer.h index 5bb0683365fcbccff18bf39378040facd6912a4d..3c7fe5c0492827d3b08b32f01a2f45ce0d90da5e 100644 --- a/App/inc/ROOTMinimizer.h +++ b/App/inc/ROOTMinimizer.h @@ -14,10 +14,12 @@ //! @author Scientific Computing Group at FRM II //! @date 05.10.2012 + #include "IMinimizer.h" #include "OutputData.h" #include "Exceptions.h" #include "ROOTMinimizerFunction.h" +#include "FitSuiteParameters.h" #include <string> // from ROOT #include "Math/Minimizer.h" @@ -35,14 +37,15 @@ public: ROOTMinimizer(const std::string &minimizer_name, const std::string &algo_type=std::string()); virtual ~ROOTMinimizer(); - virtual void setVariable(int index, const FitParameter *par) ; + virtual void setParameter(size_t index, const FitParameter *par); + virtual void setParameters(const FitSuiteParameters ¶meters); + - virtual void setFunction(function_t fcn, int ndims, element_function_t element_fcn, int nelements); -// virtual void setElementFunction(element_function_t element_fcn, int ndims, int nelements) { m_element_fcn = element_fcn, m_ndims = ndims; m_nelements = nelements; } + virtual void setFunction(function_chi2_t fun_chi2, size_t nparameters, function_gradient_t fun_gradient, size_t ndatasize); virtual void minimize(); - //! return pointer to created minimizer + //! return created minimizer ROOT::Math::Minimizer *getROOTMinimizer() { return m_root_minimizer; } //! get number of variables to fit diff --git a/App/inc/ROOTMinimizerFunction.h b/App/inc/ROOTMinimizerFunction.h index 8b25d21d045ff9b1fb5762aed13e89294847f149..26e69a76e47f9eb7230356127e5d4fefdbedc2c0 100644 --- a/App/inc/ROOTMinimizerFunction.h +++ b/App/inc/ROOTMinimizerFunction.h @@ -29,9 +29,9 @@ class ROOTMinimizerFunction : public ROOT::Math::Functor { public: - ROOTMinimizerFunction(IMinimizer::function_t fcn, int ndims ) : ROOT::Math::Functor(fcn, ndims), m_fcn(fcn) {} + ROOTMinimizerFunction(IMinimizer::function_chi2_t fcn, int ndims ) : ROOT::Math::Functor(fcn, ndims), m_fcn(fcn) {} virtual ~ROOTMinimizerFunction(){} - IMinimizer::function_t m_fcn; + IMinimizer::function_chi2_t m_fcn; }; @@ -45,7 +45,7 @@ class ROOTMinimizerElementFunction : public ROOT::Math::FitMethodFunction public: typedef ROOT::Math::BasicFitMethodFunction<ROOT::Math::IMultiGenFunction>::Type_t Type_t; - ROOTMinimizerElementFunction(IMinimizer::function_t fcn, size_t ndims, IMinimizer::element_function_t element_fcn, size_t nelements) + ROOTMinimizerElementFunction(IMinimizer::function_chi2_t fcn, size_t ndims, IMinimizer::function_gradient_t element_fcn, size_t nelements) : ROOT::Math::FitMethodFunction(ndims, nelements) , m_fcn(fcn) , m_element_fcn(element_fcn) @@ -105,8 +105,8 @@ public: private: - IMinimizer::function_t m_fcn; - IMinimizer::element_function_t m_element_fcn; + IMinimizer::function_chi2_t m_fcn; + IMinimizer::function_gradient_t m_element_fcn; size_t m_ndims; size_t m_nelements; mutable std::vector<double > m_parameter_values; diff --git a/App/src/ROOTMinimizer.cpp b/App/src/ROOTMinimizer.cpp index 5881a8aac375c47437985915565d98fa73b713ba..5ad39ab83b7bec5003a68a49495e8aad9b6c8716 100644 --- a/App/src/ROOTMinimizer.cpp +++ b/App/src/ROOTMinimizer.cpp @@ -1,5 +1,6 @@ #include "ROOTMinimizer.h" #include "Exceptions.h" +#include "FitSuiteParameters.h" #include "Utils.h" #include "ROOTMinimizerFunction.h" #include <iomanip> @@ -20,8 +21,12 @@ ROOTMinimizer::ROOTMinimizer(const std::string &minimizer_name, const std::strin if( !isValidNames(m_minimizer_name, m_algo_type) ) throw LogicErrorException("ROOTMinimizer::ROOTMinimizer() -> Error! Wrong minimizer initialization parameters."); -// m_root_minimizer = ROOT::Math::Factory::CreateMinimizer(minimizer_name, algo_type ); + // see http://root.cern.ch/phpBB3/viewtopic.php?f=15&t=14230&p=61216&hilit=minimizer+precision#p61216 + // see http://root.cern.ch/phpBB3/viewtopic.php?f=3&t=9181&hilit=precision+tolerance + //ROOT::Math::MinimizerOptions::SetDefaultTolerance(0.1); + if( m_minimizer_name == "GSLMultiFit") { + // hacked version of ROOT's GSL Levenberg-Marquardt minimizer m_root_minimizer = new ROOT::Patch::GSLNLSMinimizer(); } else { m_root_minimizer = ROOT::Math::Factory::CreateMinimizer(minimizer_name, algo_type ); @@ -30,6 +35,9 @@ ROOTMinimizer::ROOTMinimizer(const std::string &minimizer_name, const std::strin m_root_minimizer->SetMaxFunctionCalls(20000); m_root_minimizer->SetMaxIterations(20000); +// m_root_minimizer->SetPrintLevel(4); +// m_root_minimizer->SetTolerance(0.01); +// m_root_minimizer->SetPrecision(1e-6); } @@ -57,13 +65,11 @@ bool ROOTMinimizer::isValidNames(const std::string &minimizer_name, const std::s algoTypes["GSLSimAn"] = boost::assign::list_of(""); algoTypes["Genetic"] = boost::assign::list_of(""); - // check if given minimizer names are valid + // check minimizers names algotypes_t::iterator it = algoTypes.find(minimizer_name); if(it != algoTypes.end() ) { - // if algo type exists - for(size_t i=0; i<it->second.size(); ++i ) { - if(it->second[i] == algo_type ) return true; - } + // check minimizer's algorithm type + for(size_t i=0; i<it->second.size(); ++i ) if(it->second[i] == algo_type ) return true; } // if not, print complaining and return false @@ -93,7 +99,22 @@ bool ROOTMinimizer::isGradientBasedAgorithm() } -void ROOTMinimizer::setVariable(int index, const FitParameter *par) +void ROOTMinimizer::setParameters(const FitSuiteParameters ¶meters) +{ + size_t index(0); + for(FitSuiteParameters::const_iterator it=parameters.begin(); it!=parameters.end(); ++it) { + setParameter(index++, (*it) ); + } + if( parameters.size() != getNumberOfVariables()) { + std::ostringstream ostr; + ostr << "ROOTMinimizer::setParameters() -> Error! Number of variables defined in minimizer (" << getNumberOfVariables() << ") "; + ostr << "doesn't coincide with number of FitSuite's parameters (" << parameters.size() << ")"; + throw LogicErrorException(ostr.str()); + } +} + + +void ROOTMinimizer::setParameter(size_t index, const FitParameter *par) { bool success; if( par->isFixed() ) { @@ -129,18 +150,18 @@ void ROOTMinimizer::minimize() // set fcn function for minimizer /* ************************************************************************* */ // FIXME ROOTMinimizer::setFunction Implement Multiple inheretiance in ROOTMinimizerElementFunction ;) -void ROOTMinimizer::setFunction(function_t fcn, int ndims, element_function_t element_fcn, int nelements) +void ROOTMinimizer::setFunction(function_chi2_t fun_chi2, size_t nparameters, function_gradient_t fun_gradient, size_t ndatasize) { if( isGradientBasedAgorithm() ) { std::cout << " ROOTMinimizer::setFunction() -> XXX 1.1 making ROOTMinimizerElementFunction " << std::endl; delete m_minfunc_element; - m_minfunc_element = new ROOTMinimizerElementFunction(fcn, ndims, element_fcn, nelements); + m_minfunc_element = new ROOTMinimizerElementFunction(fun_chi2, nparameters, fun_gradient, ndatasize); m_root_minimizer->SetFunction(*m_minfunc_element); } else { std::cout << " ROOTMinimizer::setFunction() -> XXX 1.2 making ROOTMinimizerFunction" << std::endl; delete m_minfunc; - m_minfunc = new ROOTMinimizerFunction(fcn, ndims); + m_minfunc = new ROOTMinimizerFunction(fun_chi2, nparameters); m_root_minimizer->SetFunction(*m_minfunc); } } diff --git a/App/src/ROOTMinimizerFunction.cpp b/App/src/ROOTMinimizerFunction.cpp index e16c5f641f0c016e0692df5ee53f2ea076646e2d..f0c5c65bdfa0a5920eab38c60424c1227e7f7233 100644 --- a/App/src/ROOTMinimizerFunction.cpp +++ b/App/src/ROOTMinimizerFunction.cpp @@ -33,26 +33,6 @@ double ROOTMinimizerElementFunction::DataElement(const double *pars, unsigned in std::cout << " gradient " << std::endl; if(i_selected_element == 0 || parameters_changed) { std::cout << "g!=0 parameters_changed " << parameters_changed << std::endl; - const double kEps = 1.0E-4; - for(size_t i_par=0; i_par<m_ndims; ++i_par ) { - std::vector<double > pars_deriv; // values of parameters for derivative calculation - pars_deriv.resize(m_ndims); - std::copy(pars, pars+m_ndims, pars_deriv.begin()); - pars_deriv[i_par] += kEps; - m_fcn(&pars_deriv.front()); - for(size_t j=0; j<pars_deriv.size(); ++j) { - std::cout << "i_par:" << i_par << " j:" << j << " " << pars[j] << " " << pars_deriv[j] << std::endl; - } - std::vector<double> m_residuals2; - m_residuals2.resize(m_nelements); - for(size_t i_element=0; i_element<m_nelements; ++i_element) { - m_residuals2[i_element] = m_element_fcn(&pars_deriv.front(), i_element, gradient); - } - - for(size_t i_element=0; i_element <m_nelements; ++i_element) { - m_gradients[i_par][i_element] = (m_residuals2[i_element] - m_residuals[i_element])/kEps; - } - } } for(size_t i_par=0; i_par<m_ndims; ++i_par) { gradient[i_par] = m_gradients[i_par][i_selected_element]; diff --git a/App/src/TestToyExperiment.cpp b/App/src/TestToyExperiment.cpp index 5fe84ee96cf6c214884b5c10f8d1eb195e5d2b28..77deb9e8e7238f48ed97ae8e6b3ae9c59a1733eb 100644 --- a/App/src/TestToyExperiment.cpp +++ b/App/src/TestToyExperiment.cpp @@ -97,12 +97,12 @@ void TestToyExperiment::execute() // setting up fitSuite FitSuite *m_fitSuite = new FitSuite(); - //m_fitSuite->setMinimizer( new ROOTMinimizer("Minuit2", "Migrad") ); + m_fitSuite->setMinimizer( new ROOTMinimizer("Minuit2", "Migrad") ); //m_fitSuite->setMinimizer( new ROOTMinimizer("Minuit2", "Fumili") ); + //m_fitSuite->setMinimizer( new ROOTMinimizer("GSLMultiFit") ); - m_fitSuite->setMinimizer( new ROOTMinimizer("GSLMultiFit") ); m_fitSuite->attachObserver( new FitSuiteObserverPrint() ); - m_fitSuite->attachObserver( new FitSuiteObserverDraw() ); +// m_fitSuite->attachObserver( new FitSuiteObserverDraw() ); m_fitSuite->addFitParameter("*/par0", 1.0, 0.01); m_fitSuite->addFitParameter("*/par1", 0.0, 0.01); diff --git a/Core/Algorithms/inc/ISquaredFunction.h b/Core/Algorithms/inc/ISquaredFunction.h index 9c2c28bd37a7b3cfb7696742e0d920f233df0570..8696daf492761564b7a3bd92e16cb3f0c16e813f 100644 --- a/Core/Algorithms/inc/ISquaredFunction.h +++ b/Core/Algorithms/inc/ISquaredFunction.h @@ -15,6 +15,7 @@ //! @date Jul 20, 2012 #include "Numeric.h" +#include "Exceptions.h" #include <cmath> #include <iostream> @@ -27,6 +28,7 @@ public: virtual ISquaredFunction *clone() const=0; virtual double calculateSquaredDifference(double real_value, double simulated_value) const=0; + virtual double calculateSquaredError(double real_value, double simulated_value = 0.0) const { (void)real_value; (void)simulated_value; throw NotImplementedException("ISquaredFunction::calculateError() -> Error! Not implemented."); } }; @@ -43,9 +45,15 @@ public: if (diff_squared < Numeric::double_epsilon) { return 0.0; } - double sigma = std::max(real_value,1.0); - return diff_squared/sigma; + double sigma_squared = std::max(real_value,1.0); + return diff_squared/sigma_squared; + } + + virtual inline double calculateSquaredError(double real_value, double /* simulated_value */) const + { + return std::max(real_value,1.0); } + }; @@ -85,6 +93,11 @@ public: sigma_squared = std::max(sigma_squared, 1.0); return diff_squared/sigma_squared; } + virtual inline double calculateSquaredError(double real_value, double /* simulated_value */) const + { + return std::max(std::fabs(real_value) + (m_epsilon*real_value)*(m_epsilon*real_value),1.0); + } + private: double m_epsilon; }; @@ -103,6 +116,11 @@ public: double sigma_squared = m_sigma*m_sigma; return diff_squared/sigma_squared; } + virtual inline double calculateSquaredError(double /* real_value */, double /* simulated_value */) const + { + return m_sigma*m_sigma; + } + private: double m_sigma; }; diff --git a/Core/Core.pro b/Core/Core.pro index 07bf98a14df574d47929a353964fcdceb047b8c4..991b9f2eeea2ec13e50e4dc5557e1cfc53e146b6 100644 --- a/Core/Core.pro +++ b/Core/Core.pro @@ -117,7 +117,8 @@ SOURCES += \ Tools/src/StochasticGaussian.cpp \ Tools/src/StochasticSampledParameter.cpp \ Tools/src/Types.cpp \ - Tools/src/Utils.cpp + Tools/src/Utils.cpp \ + Tools/src/FitSuiteFunctions.cpp HEADERS += \ Algorithms/inc/Beam.h \ @@ -264,7 +265,8 @@ HEADERS += \ Tools/inc/Types.h \ Tools/inc/Units.h \ Tools/inc/Utils.h \ - Tools/inc/CoreOptionsDescription.h + Tools/inc/CoreOptionsDescription.h \ + Tools/inc/FitSuiteFunctions.h INCLUDEPATH += ./Algorithms/inc ./FormFactors/inc ./Geometry/inc ./Samples/inc ./Tools/inc DEPENDPATH += ./Algorithms/inc ./FormFactors/inc ./Geometry/inc ./Samples/inc ./Tools/inc diff --git a/Core/Tools/inc/FitObject.h b/Core/Tools/inc/FitObject.h index 16516287facbded668cca87aac964a8ee7b01d20..50cf2477fd896e27e67c6d6ce4ed70b7347defdb 100644 --- a/Core/Tools/inc/FitObject.h +++ b/Core/Tools/inc/FitObject.h @@ -60,6 +60,9 @@ public: //! return weight of data set in chi2 calculations double getWeight() const { return m_weight; } + //! return size of data + size_t getSizeOfData() const { return m_real_data->getAllocatedSize(); } + protected: //! initialize pool parameters, i.e. register some of class members for later access via parameter pool virtual void init_parameters(); diff --git a/Core/Tools/inc/FitParameter.h b/Core/Tools/inc/FitParameter.h index d9415908ff142c5300b7453938875fcdd3e1cf7f..a7cf10be3094f8d9c78961dfa082fe2127a6cb8e 100644 --- a/Core/Tools/inc/FitParameter.h +++ b/Core/Tools/inc/FitParameter.h @@ -29,7 +29,7 @@ class FitParameter : public INamed, public AttLimits public: FitParameter(); FitParameter(const AttLimits &limits); - FitParameter(const std::string &name, double value, double step=0.0, const AttLimits &limits=AttLimits::limitless()); + FitParameter(const std::string &name, double value, double step=0.0, const AttLimits &limits=AttLimits::limitless(), double error=0.0); virtual ~FitParameter(){} //! set value of parameter diff --git a/Core/Tools/inc/FitParameterLinked.h b/Core/Tools/inc/FitParameterLinked.h index bff3515ed1afae82ea2d77178f980b0e9dbd2b38..2482ed781f031bc3c3257a379ae50c919c7f2849 100644 --- a/Core/Tools/inc/FitParameterLinked.h +++ b/Core/Tools/inc/FitParameterLinked.h @@ -33,7 +33,7 @@ public: typedef std::vector<ParameterPool::parameter_t > PoolParameterColl_t; FitParameterLinked(); - FitParameterLinked(const std::string &name, double value, double step, const AttLimits &attlim=AttLimits::limitless()); + FitParameterLinked(const std::string &name, double value, double step, const AttLimits &attlim=AttLimits::limitless(), double error=0.0); virtual ~FitParameterLinked(){} //! set given value for all binded parameters diff --git a/Core/Tools/inc/FitSuite.h b/Core/Tools/inc/FitSuite.h index f55fba07feba3153632092514906b8b80f337ec9..b27a1c4df5038b1d0fcb337ac76fe717bf76dde2 100644 --- a/Core/Tools/inc/FitSuite.h +++ b/Core/Tools/inc/FitSuite.h @@ -24,6 +24,7 @@ #include "FitSuiteParameters.h" #include "IMinimizer.h" #include "ChiSquaredModule.h" +#include "FitSuiteFunctions.h" #include <string> class Experiment; @@ -50,7 +51,7 @@ public: void addExperimentAndRealData(const Experiment &experiment, const OutputData<double > &real_data, const IChiSquaredModule &chi2_module=ChiSquaredModule()); //! add fit parameter - void addFitParameter(const std::string &name, double value, double step, const AttLimits &attlim=AttLimits::limitless()); + void addFitParameter(const std::string &name, double value, double step, const AttLimits &attlim=AttLimits::limitless(), double error=0.0); //! add fit strategy void addFitStrategy(IFitSuiteStrategy *strategy); @@ -70,10 +71,10 @@ public: virtual void runFit(); //! function to minimize - double functionToMinimize(const double *pars_current_values); + double fittingChiSquaredFunction(const double *pars_current_values); //! provides minimizer with gradients wrt parameters for single data element - double elementFunction(const double *pars_current_values, unsigned int index, double *deriv); + double fittingGradientFunction(const double *pars_current_values, unsigned int index, double *deriv); //! return reference to the kit with data FitSuiteObjects *getFitObjects() { return &m_fit_objects; } @@ -85,7 +86,8 @@ public: bool isLastIteration() { return m_is_last_iteration; } //! get current number of minimization function calls - int getNCall() { return m_n_call; } + //int getNCall() { return m_n_call; } + int getNCall() { return m_function_chi2.getNCall(); } //! get the number of current strategy int getNStrategy() { return m_n_strategy; } @@ -106,6 +108,8 @@ private: bool m_is_last_iteration; //! set to true after last iteration complete int m_n_call; //! current number of minimization function call int m_n_strategy; //! current number of fit strategy + + FitSuiteChiSquaredFunction m_function_chi2; }; #endif // FITSUITE_H diff --git a/Core/Tools/inc/FitSuiteFunctions.h b/Core/Tools/inc/FitSuiteFunctions.h new file mode 100644 index 0000000000000000000000000000000000000000..2d75c61ac586c08f0bdd04fed111e548c723c910 --- /dev/null +++ b/Core/Tools/inc/FitSuiteFunctions.h @@ -0,0 +1,80 @@ +#ifndef FITSUITEFUNCTIONS_H +#define FITSUITEFUNCTIONS_H +// ******************************************************************** +// * The BornAgain project * +// * Simulation of neutron and x-ray scattering at grazing incidence * +// * * +// * LICENSE AND DISCLAIMER * +// * Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris * +// * eget quam orci. Quisque porta varius dui, quis posuere nibh * +// * mollis quis. Mauris commodo rhoncus porttitor. * +// ******************************************************************** +//! @file FitSuiteFunctions.h +//! @brief Definition of FitSuiteFunctions classes +//! @author Scientific Computing Group at FRM II +//! @date 20.12.2012 + + +#include <vector> + +class FitSuite; + +//- ------------------------------------------------------------------- +//! @class IFitSuiteFunction +//! @brief Fitting functions interface to be used by Minimizer. +//- ------------------------------------------------------------------- +class IFitSuiteFunction +{ +public: + IFitSuiteFunction() : m_fit_suite(0), m_ncall(0) {} + virtual ~IFitSuiteFunction(){} + virtual void init(FitSuite *fit_suite) { m_fit_suite = fit_suite; } + virtual size_t getNCall() const { return m_ncall; } +protected: + FitSuite *m_fit_suite; + size_t m_ncall; +}; + + + +//- ------------------------------------------------------------------- +//! @class FitSuiteChiSquaredFunction +//! @brief Chi squared fitting function for minimizer +//- ------------------------------------------------------------------- +class FitSuiteChiSquaredFunction : public IFitSuiteFunction +{ +public: + FitSuiteChiSquaredFunction(){} + virtual ~FitSuiteChiSquaredFunction(){} + //! evaluate method for chi2 value called directly from the minimizer + double evaluate(const double *pars); +}; + + +//- ------------------------------------------------------------------- +//! @class FitSuiteChiSquaredFunction +//! @brief Gradient fitting function for minimizer +//- ------------------------------------------------------------------- +class FitSuiteGradientFunction : public IFitSuiteFunction +{ +public: + FitSuiteGradientFunction() : m_npars(0), m_ndatasize(0), m_prev_index(-1) {} + virtual ~FitSuiteGradientFunction(){} + //! evaluate method for gradients and residuals called directly from the minimizer + double evaluate(const double *pars, unsigned int index, double *deriv); +private: + void verify_arrays(); + void verify_minimizer_logic(bool parameters_have_changed, int current_index); + void calculate_residuals(const double *pars); + void calculate_gradients(const double *pars); + + size_t m_npars; + size_t m_ndatasize; + int m_prev_index; + std::vector<double > m_residuals; // [m_ndatasize] + std::vector<std::vector<double> > m_gradients; // [m_npars][m_ndatasize] +}; + + + +#endif // FITSUITEFUNCTIONS_H diff --git a/Core/Tools/inc/FitSuiteObjects.h b/Core/Tools/inc/FitSuiteObjects.h index bf8be6a2d3fb2f8705eda63a7c2efcbbb92fa0f3..26221a887781f2f7fdf97943e1f694ff09220849 100644 --- a/Core/Tools/inc/FitSuiteObjects.h +++ b/Core/Tools/inc/FitSuiteObjects.h @@ -38,7 +38,7 @@ public: //! clear all data void clear(); - //! return number of items + //! return number of fit items size_t size() const { return m_fit_objects.size(); } //! add to kit pair of (experiment, real data) for consecutive simulation and chi2 module @@ -47,11 +47,15 @@ public: //! loop through all defined experiments and run they simulation void runSimulation(); + //! get total number of data points + size_t getSizeOfDataSet() const; + //! get sum of chi squared values for all fit objects double getChiSquaredValue(int n_free_fit_parameters = 0); //! get residuals for single data element - double getResidualValue(int index); + //! @pars global_index index accross all OutputData defined + double getResidualValue(int global_index); //! get experiment const Experiment *getExperiment(size_t i_item = 0) const { return m_fit_objects[check_index(i_item)]->getExperiment(); } @@ -86,6 +90,9 @@ protected: //! calculate maximum intensity in simulated data double getSimulationMaxIntensity(); + //! return object and calculate index of data element for given global data element index + const FitObject *getObjectForGlobalDataIndex(size_t global_index, size_t &local_index); + private: //! disabled copy constructor and assignment operator FitSuiteObjects &operator=(const FitSuiteObjects &); diff --git a/Core/Tools/inc/FitSuiteParameters.h b/Core/Tools/inc/FitSuiteParameters.h index 6a0a3711da7e963b1132c2bd46d64d66f4a9ef48..d1bb7407912361534ea1b3a6167462012ceb0bf8 100644 --- a/Core/Tools/inc/FitSuiteParameters.h +++ b/Core/Tools/inc/FitSuiteParameters.h @@ -39,7 +39,7 @@ public: void clear(); //! add fit parameter - void addParameter(const std::string &name, double value, double step, const AttLimits &attlim); + void addParameter(const std::string &name, double value, double step, const AttLimits &attlim, double error=0.0); //! return fit parameter with given name const FitParameter *getParameter(const std::string &name) const; @@ -75,6 +75,9 @@ public: //! return number of free parameters size_t getNfreeParameters() const; + //! return true if parameters have already given values + bool valuesAreDifferrent(const double *pars_valuers, double tolerance_factor=1.0) const; + private: //! disabled copy constructor and assignment operator FitSuiteParameters &operator=(const FitSuiteParameters &other); @@ -82,6 +85,8 @@ private: inline size_t check_index(size_t index) const { return (index < m_parameters.size() ? index : throw OutOfBoundsException("FitSuiteParameters::check_index() -> Index out of bounds") ); } parameters_t m_parameters; //! collection of fit parameters + + static double m_default_parameter_error; }; #endif // FITSUITEPARAMETERS_H diff --git a/Core/Tools/inc/IMinimizer.h b/Core/Tools/inc/IMinimizer.h index 7df17f8678ff098e121528e3af44c6b68a56e952..885a8ff8d810c14fd16cb2a25d41f0b1032e2f5a 100644 --- a/Core/Tools/inc/IMinimizer.h +++ b/Core/Tools/inc/IMinimizer.h @@ -16,6 +16,7 @@ #include "FitParameter.h" +#include "FitSuiteParameters.h" #include <boost/function.hpp> #include <map> #include "Exceptions.h" @@ -29,20 +30,19 @@ class IMinimizer { public: //! signature of function to minimize - typedef boost::function<double(const double *)> function_t; - //! signature of function to minimize with acess to single element residual - typedef boost::function<double(const double *, unsigned int, double *)> element_function_t; + typedef boost::function<double(const double *)> function_chi2_t; + //! signature of function to minimize with acess to single element residual and gradient + typedef boost::function<double(const double *, unsigned int, double *)> function_gradient_t; IMinimizer(){} virtual ~IMinimizer(){} - //! set variable - virtual void setVariable(int index, const FitParameter *par) = 0; + //! set parameter + virtual void setParameter(size_t index, const FitParameter *par) = 0; + virtual void setParameters(const FitSuiteParameters ¶meters) = 0; //! set function to minimize -// virtual void setFunction(function_t fcn, int ndims) = 0; -// virtual void setElementFunction(element_function_t fcn, int ndims, int nelements) = 0; - virtual void setFunction(function_t fcn, int ndims, element_function_t element_fcn = element_function_t(), int nelements = 0) = 0; + virtual void setFunction(function_chi2_t fun_chi2, size_t nparameters, function_gradient_t fun_gradient = function_gradient_t(), size_t ndatasize = 0) = 0; //! run minimization virtual void minimize() = 0; @@ -80,13 +80,11 @@ public: virtual ~TestMinimizer(){} //! set variable - virtual void setVariable(int index, const FitParameter *par) { m_values[index] = par->getValue(); } + virtual void setParameter(size_t index, const FitParameter *par) { m_values[index] = par->getValue(); } + virtual void setParameters(const FitSuiteParameters &/*parameters */) { throw NotImplementedException("TestMinimizer::setParameters() -> Error! Not implemented."); } //! set function to minimize - //virtual void setFunction(function_t fcn, int ndims) { m_fcn = fcn; (void)ndims; } - virtual void setFunction(function_t fcn, int /* ndims */, element_function_t /* element_fcn */, int /* nelements */ ) { m_fcn = fcn; } - -// virtual void setElementFunction(element_function_t /* fcn */, int /* ndims */, int /* nelements */) { throw NotImplementedException("TestMinimizer::setGradientFunction"); } + virtual void setFunction(function_chi2_t fun_chi2, size_t /* nparameters */, function_gradient_t /* fun_gradient */, size_t /* ndatasize */ ) { m_fcn = fun_chi2; } //! run minimization virtual void minimize() @@ -129,7 +127,7 @@ public: private: std::map<int, double > m_values; - function_t m_fcn; + function_chi2_t m_fcn; }; diff --git a/Core/Tools/inc/Numeric.h b/Core/Tools/inc/Numeric.h index 33864f1871be15340a4903e4c7cce24f5ba7c028..0ede1834387d1d8c2f3657b3a95e70acb4a29aef 100644 --- a/Core/Tools/inc/Numeric.h +++ b/Core/Tools/inc/Numeric.h @@ -16,6 +16,7 @@ //! @date 10.05.2012 #include <limits> +#include <cmath> namespace Numeric { @@ -26,6 +27,9 @@ static double double_min = std::numeric_limits<double>::min(); static const double probthreshold = 0.0000000001; //!< threshold on probability value during calculation of weighted form factor +//! compare two doubles +inline bool areAlmostEqual(double a, double b, double tolerance_factor=1.0) { return std::abs(a-b) < tolerance_factor*Numeric::double_epsilon; } + } #endif /* NUMERIC_H_ */ diff --git a/Core/Tools/src/FitObject.cpp b/Core/Tools/src/FitObject.cpp index 1ceb4844f05156705f496c949ed480929df16a46..66b178e13731e30e3577074209633492dba78ef0 100644 --- a/Core/Tools/src/FitObject.cpp +++ b/Core/Tools/src/FitObject.cpp @@ -22,6 +22,9 @@ FitObject::FitObject(const Experiment &experiment, const OutputData<double > &re FitObject::~FitObject() { + delete m_experiment; + delete m_real_data; + delete m_chi2_module; } diff --git a/Core/Tools/src/FitParameter.cpp b/Core/Tools/src/FitParameter.cpp index 00ac4cc8914d8f35201b4dd1f2d283b5becbb620..19b2911c544e93b291dc923de666cdcc24daa1cc 100644 --- a/Core/Tools/src/FitParameter.cpp +++ b/Core/Tools/src/FitParameter.cpp @@ -3,21 +3,15 @@ #include <iomanip> FitParameter::FitParameter() : m_value(0), m_step(0), m_error(0) -{ - -} - +{ } -FitParameter::FitParameter(const std::string &name, double value, double step, const AttLimits &attlimits) +FitParameter::FitParameter(const std::string &name, double value, double step, const AttLimits &attlimits, double error) : INamed(name) , AttLimits(attlimits) , m_value(value) , m_step(step) - , m_error(0.0) -{ - -} - + , m_error(error) +{ } void FitParameter::print(std::ostream &ostr) const { diff --git a/Core/Tools/src/FitParameterLinked.cpp b/Core/Tools/src/FitParameterLinked.cpp index ae1944e1c0dc4e9237e63c0ea915c9f92b74dce9..27d4382e781949cf97b8d812f482aacae1cba88e 100644 --- a/Core/Tools/src/FitParameterLinked.cpp +++ b/Core/Tools/src/FitParameterLinked.cpp @@ -8,7 +8,7 @@ FitParameterLinked::FitParameterLinked() } -FitParameterLinked::FitParameterLinked(const std::string &name, double value, double step, const AttLimits &attlim) : FitParameter(name, value, step, attlim) +FitParameterLinked::FitParameterLinked(const std::string &name, double value, double step, const AttLimits &attlim, double error) : FitParameter(name, value, step, attlim, error) { } diff --git a/Core/Tools/src/FitSuite.cpp b/Core/Tools/src/FitSuite.cpp index 7ebdbcab1913717673fb9a3b437631f9aea8c4e7..7cb7753081b3af1eca246ef6f9f65438a4759dfa 100644 --- a/Core/Tools/src/FitSuite.cpp +++ b/Core/Tools/src/FitSuite.cpp @@ -10,6 +10,7 @@ FitSuite::FitSuite() : m_minimizer(0), m_is_last_iteration(false), m_n_call(0), m_n_strategy(0) { + m_function_chi2.init(this); } @@ -48,9 +49,9 @@ void FitSuite::addExperimentAndRealData(const Experiment &experiment, const Outp /* ************************************************************************* */ // add fit parameter /* ************************************************************************* */ -void FitSuite::addFitParameter(const std::string &name, double value, double step, const AttLimits &attlim) +void FitSuite::addFitParameter(const std::string &name, double value, double step, const AttLimits &attlim, double error) { - m_fit_parameters.addParameter(name, value, step, attlim); + m_fit_parameters.addParameter(name, value, step, attlim, error); } @@ -83,25 +84,15 @@ void FitSuite::link_fit_parameters() /* ************************************************************************* */ void FitSuite::minimize() { - // initializing minimizer with fcn function belonging to given class - IMinimizer::function_t fcn = boost::bind(&FitSuite::functionToMinimize, this, _1); - //m_minimizer->setFunction( fcn, (int)m_fit_parameters.size() ); - // FIXME: FitSuite::minimize() where to get number of elements? - int nelements = m_fit_objects.getRealData()->getAllocatedSize(); - IMinimizer::element_function_t element_fcn = boost::bind(&FitSuite::elementFunction, this, _1, _2, _3); - m_minimizer->setFunction( fcn, (int)m_fit_parameters.size(), element_fcn, nelements ); - - // propagating values of local fit parameters to the minimizer's internal parameters - for(size_t i_par = 0; i_par<m_fit_parameters.size(); i_par++) { - std::cout << " i_par " << i_par << std::endl; - m_minimizer->setVariable((int)i_par, m_fit_parameters[i_par] ); - } - if( m_fit_parameters.size() != m_minimizer->getNumberOfVariables()) { - std::ostringstream ostr; - ostr << "FitSuite::minimize() -> Error! Number of variables defined in minimizer (" << m_minimizer->getNumberOfVariables() << ") "; - ostr << "doesn't coincide with number of FitSuite's parameters (" << m_fit_parameters.size() << ")"; - throw LogicErrorException(ostr.str()); - } + // initializing minimizer with fitting functions + //IMinimizer::function_chi2_t fun_chi2 = boost::bind(&FitSuite::fittingChiSquaredFunction, this, _1); + IMinimizer::function_chi2_t fun_chi2 = boost::bind(&FitSuiteChiSquaredFunction::evaluate, &m_function_chi2, _1); + + IMinimizer::function_gradient_t fun_gradient = boost::bind(&FitSuite::fittingGradientFunction, this, _1, _2, _3); + m_minimizer->setFunction( fun_chi2, m_fit_parameters.size(), fun_gradient, m_fit_objects.getSizeOfDataSet() ); + + // initializing minimizer's parameters with the list of local fit parameters + m_minimizer->setParameters(m_fit_parameters); // minimizing m_minimizer->minimize(); @@ -158,7 +149,7 @@ void FitSuite::runFit() /* ************************************************************************* */ // function to minimize /* ************************************************************************* */ -double FitSuite::functionToMinimize(const double *pars_current_values) +double FitSuite::fittingChiSquaredFunction(const double *pars_current_values) { std::cout << "FitSuite::functionToMinimize() -> Info" << std::endl; // set fitting parameters to values suggested by the minimizer @@ -179,8 +170,11 @@ double FitSuite::functionToMinimize(const double *pars_current_values) /* ************************************************************************* */ // provides minimizer with gradients wrt parameters for single data element /* ************************************************************************* */ -double FitSuite::elementFunction(const double *pars_current_values, unsigned int index, double *deriv) +double FitSuite::fittingGradientFunction(const double *pars_current_values, unsigned int index, double *deriv) { +// bool parameters_changed = m_fit_parameters.parametersDiffer(pars_current_values); + + throw 1; (void)pars_current_values; (void) deriv; // if(index % 10 == 0) std::cout << " elementFunction " << index << std::endl; diff --git a/Core/Tools/src/FitSuiteFunctions.cpp b/Core/Tools/src/FitSuiteFunctions.cpp new file mode 100644 index 0000000000000000000000000000000000000000..377354c21207602568c54266fd9adc88101691f9 --- /dev/null +++ b/Core/Tools/src/FitSuiteFunctions.cpp @@ -0,0 +1,123 @@ +#include "FitSuiteFunctions.h" +#include "FitSuite.h" + + +/* ************************************************************************* */ +// evaluate chi squared value +/* ************************************************************************* */ +double FitSuiteChiSquaredFunction::evaluate(const double *pars) +{ + std::cout << "FitSuiteChiSquaredFunction::evaluate() -> Info" << std::endl; + assert(m_fit_suite != NULL); + + // set fitting parameters to values suggested by the minimizer + m_fit_suite->getFitParameters()->setValues(pars); + + // run simulations + m_fit_suite->getFitObjects()->runSimulation(); + + // caclulate chi2 value + int n_free_pars = m_fit_suite->getFitParameters()->getNfreeParameters(); + double chi_squared = m_fit_suite->getFitObjects()->getChiSquaredValue(n_free_pars); + + m_fit_suite->notifyObservers(); + m_ncall++; + return chi_squared; +} + + +/* ************************************************************************* */ +// evaluate residual and derivative for given data element +/* ************************************************************************* */ +double FitSuiteGradientFunction::evaluate(const double *pars, unsigned int index, double *deriv) +{ + std::cout << "FitSuiteGradientFunction::evaluate() -> Info" << std::endl; + assert(m_fit_suite != NULL); + + bool parameters_changed = m_fit_suite->getFitParameters()->valuesAreDifferrent(pars); + verify_arrays(); + verify_minimizer_logic(parameters_changed, (int)index); + + if(parameters_changed) calculate_residuals(pars); + + if(deriv) { + if(index == 0 || parameters_changed ) calculate_gradients(pars); + for(size_t i_par=0; i_par<m_npars; ++i_par) { + deriv[i_par] = m_gradients[i_par][index]; + } + } + + m_ncall++; + return m_residuals[index]; +} + +void FitSuiteGradientFunction::verify_arrays() +{ + if( m_npars != m_fit_suite->getFitParameters()->size() || m_ndatasize != m_fit_suite->getFitObjects()->getSizeOfDataSet() ) { + std::cout << "FitSuiteGradientFunction::verify_arrays() -> Info. " << std::endl; + m_npars = m_fit_suite->getFitParameters()->size(); + m_ndatasize = m_fit_suite->getFitObjects()->getSizeOfDataSet(); + m_residuals.clear(); + m_residuals.resize(m_ndatasize, 0.0); + m_gradients.clear(); + m_gradients.resize(m_npars); + for(size_t i_par=0; i_par<m_npars; ++i_par) { + m_gradients[i_par].resize(m_ndatasize, 0.0); + } + } +} + +void FitSuiteGradientFunction::verify_minimizer_logic(bool parameters_have_changed, int current_index) +{ + int index_difference = current_index - m_prev_index; + if(index_difference != 1 && (current_index!=0 && int(m_prev_index)!= int(m_ndatasize-1) ) ) { + std::cout << "FitSuiteGradientFunction::verify_minimizer_logic() -> Warning! Non sequential access to elements."; + std::cout << " current_index:" << current_index << " prev_index:" << m_prev_index << std::endl; + } + if(parameters_have_changed && current_index != 0) { + std::cout << "FitSuiteGradientFunction::verify_minimizer_logic() -> Warning! Parameters have changed while current_index!=0" << std::endl; + std::cout << " current_index:" << current_index << " prev_index:" << m_prev_index << std::endl; + } + if(parameters_have_changed && current_index == m_prev_index) { + std::cout << "FitSuiteGradientFunction::verify_minimizer_logic() -> Warning! Parameters have changed while index remained the same" << std::endl; + std::cout << " current_index:" << current_index << " prev_index:" << m_prev_index << std::endl; + } +} + +void FitSuiteGradientFunction::calculate_residuals(const double *pars) +{ + std::cout << " FitSuiteGradientFunction::calculate_residuals() -> Info. " << std::endl; + m_fit_suite->getFitParameters()->setValues(pars); + m_fit_suite->getFitObjects()->runSimulation(); + for(size_t i_data=0; i_data<m_ndatasize; ++i_data) { + m_residuals[i_data] = m_fit_suite->getFitObjects()->getResidualValue(i_data); + } + +} + +void FitSuiteGradientFunction::calculate_gradients(const double *pars) +{ + std::cout << " FitSuiteGradientFunction::calculate_gradients() -> Info. " << std::endl; + const double kEps = 1.0E-4; + for(size_t i_par=0; i_par<m_npars; ++i_par ) { + std::vector<double > pars_deriv; // values of parameters for derivative calculation + pars_deriv.resize(m_npars); + std::copy(pars, pars+m_npars, pars_deriv.begin()); + pars_deriv[i_par] += kEps; + + m_fit_suite->getFitParameters()->setValues(pars_deriv); + m_fit_suite->getFitObjects()->runSimulation(); + + std::vector<double> residuals2; + residuals2.resize(m_ndatasize); + for(size_t i_data=0; i_data<m_ndatasize; ++i_data) { + residuals2[i_data] = m_fit_suite->getFitObjects()->getResidualValue(i_data); + } + + for(size_t i_data=0; i_data <m_ndatasize; ++i_data) { + m_gradients[i_par][i_data] = (m_residuals[i_data] - residuals2[i_data])/kEps; + } + } + +} + diff --git a/Core/Tools/src/FitSuiteObjects.cpp b/Core/Tools/src/FitSuiteObjects.cpp index 393e7043f22997f4e00a6ec070059c916b2fd92b..8ee0fc980d70f72d9ecfd0a507fd29fd7de97486 100644 --- a/Core/Tools/src/FitSuiteObjects.cpp +++ b/Core/Tools/src/FitSuiteObjects.cpp @@ -41,6 +41,19 @@ void FitSuiteObjects::runSimulation() } +/* ************************************************************************* */ +// get total number of data points +/* ************************************************************************* */ +size_t FitSuiteObjects::getSizeOfDataSet() const +{ + size_t result(0); + for(FitObjects_t::const_iterator it = m_fit_objects.begin(); it!= m_fit_objects.end(); ++it) { + result += (*it)->getSizeOfData(); + } + return result; +} + + /* ************************************************************************* */ // get sum of chi squared values for all fit objects // FIXME: refactor FitSuiteObjects::getChiSquaredValue() (the main problem is duplication calculateChiSquared() for ChiSquaredModule and FitObject) @@ -66,22 +79,30 @@ double FitSuiteObjects::getChiSquaredValue(int n_free_fit_parameters) } -double FitSuiteObjects::getResidualValue(int index) +const FitObject *FitSuiteObjects::getObjectForGlobalDataIndex(size_t global_index, size_t &local_index) { - double residual_sum(0); - for(FitObjects_t::iterator it = m_fit_objects.begin(); it!= m_fit_objects.end(); ++it) { - IChiSquaredModule *chi = (*it)->getChiSquaredModule(); - const OutputData<double> *data_real = (*it)->getRealData(); - const OutputData<double> *data_simu = (*it)->getSimulationData(); - double value_real = (*data_real)[index]; - double value_simu = (*data_simu)[index]; - double squared_difference = chi->getSquaredFunction()->calculateSquaredDifference(value_real, value_simu); - double weight = (*it)->getWeight()/m_total_weight; - double residual(0); - (squared_difference > 0 ? residual = weight*std::sqrt(squared_difference) : 0.0); - residual_sum += residual; + local_index = global_index; + for(FitObjects_t::const_iterator it = m_fit_objects.begin(); it!= m_fit_objects.end(); ++it) { + if(local_index < (*it)->getSizeOfData() ) { + //std::cout << "FitSuiteObjects::getObjectForGlobalDataIndex() -> Found index " << global_index << " " << local_index << std::endl; + return (*it); + } else if(local_index == (*it)->getSizeOfData() ) { + local_index -= (*it)->getSizeOfData(); + } } - return residual_sum; + throw LogicErrorException("FitSuiteObjects::getObjectForGlobalDataIndex() -> Error! Can't find fit object for given data index"); +} + + +double FitSuiteObjects::getResidualValue(int global_index) +{ + size_t index(0); + const FitObject *fitObject = getObjectForGlobalDataIndex(global_index, index); + double value_real = (*fitObject->getRealData())[index]; + double value_simu = (*fitObject->getSimulationData())[index]; + double squared_error = fitObject->getChiSquaredModule()->getSquaredFunction()->calculateSquaredError(value_real, value_simu); + double residual = (value_real - value_simu)*(fitObject->getWeight()/m_total_weight)/std::sqrt(squared_error); + return residual; } @@ -91,13 +112,13 @@ double FitSuiteObjects::getResidualValue(int index) /* ************************************************************************* */ double FitSuiteObjects::getSimulationMaxIntensity() { - double max_intensity(0); + double result(0); for(FitObjects_t::iterator it = m_fit_objects.begin(); it!= m_fit_objects.end(); ++it) { const OutputData<double > *data = (*it)->getExperiment()->getOutputData(); OutputData<double >::const_iterator cit = std::max_element(data->begin(), data->end()); - max_intensity = std::max(max_intensity, *cit); + result = std::max(result, *cit); } - return max_intensity; + return result; } @@ -109,7 +130,7 @@ std::string FitSuiteObjects::addParametersToExternalPool(std::string path, { (void)copy_number; // add own parameters - // so far it is top object in our chain, and its without parameters, lets exclude its name from path + // so far it is top object in our chain, and its without parameters, lets not include its name in path //std::string new_path = IParameterized::addParametersToExternalPool(path, external_pool, copy_number); std::string new_path = path; diff --git a/Core/Tools/src/FitSuiteParameters.cpp b/Core/Tools/src/FitSuiteParameters.cpp index 7543a1f52a9c2403216ce960f1eca4a490e8dee7..645b291dce6870c5570d7eaa8f05b0e1d0a5ad70 100644 --- a/Core/Tools/src/FitSuiteParameters.cpp +++ b/Core/Tools/src/FitSuiteParameters.cpp @@ -2,6 +2,7 @@ #include "Experiment.h" +double FitSuiteParameters::m_default_parameter_error=0.001; FitSuiteParameters::FitSuiteParameters() { @@ -25,12 +26,14 @@ void FitSuiteParameters::clear() // add fit parameter -void FitSuiteParameters::addParameter(const std::string &name, double value, double step, const AttLimits &attlim) +void FitSuiteParameters::addParameter(const std::string &name, double value, double step, const AttLimits &attlim, double error) { for(parameters_t::const_iterator it = m_parameters.begin(); it!=m_parameters.end(); ++it) { if( (*it)->getName() == name ) throw LogicErrorException("FitSuiteParameters:addtFitParameter() -> Error. Existing parameter '"+name+"'"); } - m_parameters.push_back(new FitParameterLinked(name, value, step, attlim) ); + // defining default parameter error + if(error == 0.0) error = value*m_default_parameter_error; + m_parameters.push_back(new FitParameterLinked(name, value, step, attlim, error) ); } @@ -54,9 +57,14 @@ FitParameter *FitSuiteParameters::getParameter(const std::string &name) // set values for all defined parameters +// FIXME FitSuiteParameters::setValues remove check for attempt to set parameter values void FitSuiteParameters::setValues(const double *pars_values) { - int index(0); + if( !valuesAreDifferrent(pars_values) ) { + std::cout << "FitSuiteParameters::setValues() -> Warning! Small or absent variation of parameters" << std::endl; + for(size_t i=0; i<m_parameters.size(); i++) std::cout << (m_parameters[i]->getValue() -pars_values[i]) << " " << Numeric::areAlmostEqual(m_parameters[i]->getValue(), pars_values[i]) << std::endl; + } + size_t index(0); for(parameters_t::iterator it=m_parameters.begin(); it!=m_parameters.end(); ++it) (*it)->setValue(pars_values[index++]); } @@ -69,24 +77,23 @@ void FitSuiteParameters::setValues(const std::vector<double> &pars_values) std::vector<double > FitSuiteParameters::getValues() const { - std::vector<double > buff; - buff.resize(m_parameters.size(), 0); + std::vector<double > result; for(parameters_t::const_iterator it=m_parameters.begin(); it!=m_parameters.end(); ++it) { - buff.push_back((*it)->getValue()); + result.push_back((*it)->getValue()); } - return buff; + return result; } size_t FitSuiteParameters::getNfreeParameters() const { - size_t n_free(0); + size_t result(0); for(parameters_t::const_iterator it=m_parameters.begin(); it!=m_parameters.end(); ++it) { - if( !(*it)->isFixed() ) n_free++; + if( !(*it)->isFixed() ) result++; } - return n_free; + return result; } @@ -106,3 +113,14 @@ void FitSuiteParameters::link_to_pool(const ParameterPool *pool) } +bool FitSuiteParameters::valuesAreDifferrent(const double *pars_values, double tolerance_factor) const +{ + size_t index(0); + for(parameters_t::const_iterator it=m_parameters.begin(); it!=m_parameters.end(); ++it) { + if( !Numeric::areAlmostEqual(pars_values[index++], (*it)->getValue(), tolerance_factor )) return true; + } + return false; +} + + + diff --git a/UnitTests/TestCore/TestCore.pro b/UnitTests/TestCore/TestCore.pro index 0a3d5b89534291669e5d7a531443d418b24e2cb1..5a857e7d3a54d4ae19bbc41635aecd2aa1b675fe 100644 --- a/UnitTests/TestCore/TestCore.pro +++ b/UnitTests/TestCore/TestCore.pro @@ -72,4 +72,4 @@ for(dep, MY_DEPENDENCY_LIB) { ############################################################################### # runs automatically tests right after linking ############################################################################### -QMAKE_POST_LINK = $(TARGET) 2> /dev/null +QMAKE_POST_LINK = $$PWD/$(TARGET) 2> /dev/null