BornAgain  1.18.0
Simulate and fit neutron and x-ray scattering at grazing incidence
ResidualFunctionAdapter.cpp
Go to the documentation of this file.
1 // ************************************************************************** //
2 //
3 // BornAgain: simulate and fit scattering at grazing incidence
4 //
5 //! @file Fit/RootAdapter/ResidualFunctionAdapter.cpp
6 //! @brief Implements class ResidualFunctionAdapter.
7 //!
8 //! @homepage http://www.bornagainproject.org
9 //! @license GNU General Public License v3 or higher (see COPYING)
10 //! @copyright Forschungszentrum Jülich GmbH 2018
11 //! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS)
12 //
13 // ************************************************************************** //
14 
17 #include <cassert>
18 #include <sstream>
19 
20 namespace
21 {
22 // step size of derivative calculations
23 const double kEps = 1.0E-9;
24 } // namespace
25 
26 using namespace Fit;
27 
29  const Fit::Parameters& parameters)
30  : m_datasize(0), m_fcn(func), m_parameters(parameters)
31 {
32  // single call of user function to get dataset size
33  auto residuals = m_fcn(parameters);
34  m_datasize = residuals.size();
35 }
36 
38 {
39  gradient_function_t gradient_fun = [&](const std::vector<double>& pars, unsigned int index,
40  std::vector<double>& gradients) {
41  return element_residual(pars, index, gradients);
42  };
43 
44  scalar_function_t objective_fun = [&](const std::vector<double>& pars) { return chi2(pars); };
45 
46  m_root_objective.reset(
47  new RootResidualFunction(objective_fun, gradient_fun, m_parameters.size(), m_datasize));
48 
49  return m_root_objective.get();
50 }
51 
52 void ResidualFunctionAdapter::calculate_gradients(const std::vector<double>& pars)
53 {
54  m_gradients.clear();
55  m_gradients.resize(pars.size());
56  for (size_t i_par = 0; i_par < pars.size(); ++i_par)
57  m_gradients[i_par].resize(m_datasize, 0.0);
58 
59  auto residuals = get_residuals(pars);
61 
62  for (size_t i_par = 0; i_par < pars.size(); ++i_par) {
63  std::vector<double> pars_deriv = pars; // values of parameters for derivative calculation
64  pars_deriv[i_par] += kEps;
65 
66  auto residuals2 = get_residuals(pars_deriv);
67 
68  for (size_t i_data = 0; i_data < m_datasize; ++i_data)
69  m_gradients[i_par][i_data] = (residuals2[i_data] - residuals[i_data]) / kEps;
70  }
71 }
72 
73 std::vector<double> ResidualFunctionAdapter::get_residuals(const std::vector<double>& pars)
74 {
75  if (pars.size() != m_parameters.size()) {
76  std::ostringstream ostr;
77  ostr << "ResidualFunctionAdapter::residuals() -> Error. Number of fit parameters "
78  << "has changed in the course of minimization. Initially was " << m_parameters.size()
79  << " become " << pars.size() << "\n";
80  throw std::runtime_error(ostr.str());
81  }
82 
83  m_parameters.setValues(pars);
84  auto result = m_fcn(m_parameters);
85 
86  if (result.size() != m_datasize) {
87  std::ostringstream ostr;
88  ostr << "ResidualFunctionAdapter::residuals() -> Error. Size of data "
89  << "has changed in the course of minimization. Initial length " << m_datasize
90  << " new length " << result.size() << "\n";
91  throw std::runtime_error(ostr.str());
92  }
93  return result;
94 }
95 
96 //! Returns residual for given data element index. If gradients vector size is not empty, also
97 //! calculates gradients. Actuall calculation is done for all data elements when index==0.
98 //! If index!=0 - cached value of residuals/gradients will be used.
99 
100 double ResidualFunctionAdapter::element_residual(const std::vector<double>& pars,
101  unsigned int index, std::vector<double>& gradients)
102 {
103  if (index == 0) {
104  m_residuals = get_residuals(pars);
105  }
106 
107  if (!gradients.empty()) {
108  // Non zero size means that minimizer wants to know gradients.
109  if (pars.size() != gradients.size())
110  throw std::runtime_error("ResidualFunctionAdapter::element_residual() -> Error. "
111  "Number of gradients doesn't match number of fit parameters.");
112  if (index == 0)
113  calculate_gradients(pars);
114  for (size_t i_par = 0; i_par < pars.size(); ++i_par)
115  gradients[i_par] = m_gradients[i_par][index];
116  }
117 
118  return m_residuals[index];
119 }
120 
121 double ResidualFunctionAdapter::chi2(const std::vector<double>& pars)
122 {
124 
125  double result(0.0);
126  for (auto x : get_residuals(pars))
127  result += x * x;
128 
129  int fnorm = static_cast<int>(m_datasize) - static_cast<int>(m_parameters.freeParameterCount());
130  if (fnorm <= 0)
131  throw std::runtime_error("ResidualFunctionAdapter::chi2() -> Error. Normalization is 0");
132 
133  return result / fnorm;
134 }
std::function< double(const std::vector< double > &)> scalar_function_t
Definition: KernelTypes.h:28
std::function< std::vector< double >(const Fit::Parameters &)> fcn_residual_t
Definition: KernelTypes.h:38
std::function< double(const std::vector< double > &, unsigned int, std::vector< double > &)> gradient_function_t
Definition: KernelTypes.h:31
Defines class ResidualFunctionAdapter.
Declares class RootResidualFunction.
A collection of fit parameters.
Definition: Parameters.h:28
void setValues(const std::vector< double > &values)
Definition: Parameters.cpp:64
size_t freeParameterCount() const
Returns number of free parameters.
Definition: Parameters.cpp:132
size_t size() const
Definition: Parameters.cpp:51
const RootResidualFunction * rootResidualFunction()
fcn_residual_t m_fcn
user function to minimize
double element_residual(const std::vector< double > &pars, unsigned int index, std::vector< double > &gradients)
evaluate method for gradients and residuals called directly from the minimizer
std::vector< double > get_residuals(const std::vector< double > &pars)
std::vector< std::vector< double > > m_gradients
void calculate_gradients(const std::vector< double > &pars)
double chi2(const std::vector< double > &pars)
Evaluate chi2.
ResidualFunctionAdapter(fcn_residual_t func, const Parameters &parameters)
size_t m_datasize
Length of vector with residuals, should stay the same during minimization.
std::unique_ptr< RootResidualFunction > m_root_objective
Minimizer function with access to single data element residuals, required by Fumili2 and GSLMultiMin ...
Objective function types.