BornAgain  1.19.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 reflection and scattering
4 //
5 //! @file Fit/Adapter/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 // step size of derivative calculations
22 const double kEps = 1.0E-9;
23 } // namespace
24 
25 using namespace mumufit;
26 
28  const mumufit::Parameters& parameters)
29  : m_datasize(0), m_fcn(func), m_parameters(parameters)
30 {
31  // single call of user function to get dataset size
32  auto residuals = m_fcn(parameters);
33  m_datasize = residuals.size();
34 }
35 
37 {
38  gradient_function_t gradient_fun = [&](const std::vector<double>& pars, unsigned int index,
39  std::vector<double>& gradients) {
40  return element_residual(pars, index, gradients);
41  };
42 
43  scalar_function_t objective_fun = [&](const std::vector<double>& pars) { return chi2(pars); };
44 
45  m_root_objective.reset(
46  new RootResidualFunction(objective_fun, gradient_fun, m_parameters.size(), m_datasize));
47 
48  return m_root_objective.get();
49 }
50 
51 void ResidualFunctionAdapter::calculate_gradients(const std::vector<double>& pars)
52 {
53  m_gradients.clear();
54  m_gradients.resize(pars.size());
55  for (size_t i_par = 0; i_par < pars.size(); ++i_par)
56  m_gradients[i_par].resize(m_datasize, 0.0);
57 
58  auto residuals = get_residuals(pars);
60 
61  for (size_t i_par = 0; i_par < pars.size(); ++i_par) {
62  std::vector<double> pars_deriv = pars; // values of parameters for derivative calculation
63  pars_deriv[i_par] += kEps;
64 
65  auto residuals2 = get_residuals(pars_deriv);
66 
67  for (size_t i_data = 0; i_data < m_datasize; ++i_data)
68  m_gradients[i_par][i_data] = (residuals2[i_data] - residuals[i_data]) / kEps;
69  }
70 }
71 
72 std::vector<double> ResidualFunctionAdapter::get_residuals(const std::vector<double>& pars)
73 {
74  if (pars.size() != m_parameters.size()) {
75  std::ostringstream ostr;
76  ostr << "ResidualFunctionAdapter::residuals() -> Error. Number of fit parameters "
77  << "has changed in the course of minimization. Initially was " << m_parameters.size()
78  << " become " << pars.size() << "\n";
79  throw std::runtime_error(ostr.str());
80  }
81 
82  m_parameters.setValues(pars);
83  auto result = m_fcn(m_parameters);
84 
85  if (result.size() != m_datasize) {
86  std::ostringstream ostr;
87  ostr << "ResidualFunctionAdapter::residuals() -> Error. Size of data "
88  << "has changed in the course of minimization. Initial length " << m_datasize
89  << " new length " << result.size() << "\n";
90  throw std::runtime_error(ostr.str());
91  }
92  return result;
93 }
94 
95 //! Returns residual for given data element index. If gradients vector size is not empty, also
96 //! calculates gradients. Actuall calculation is done for all data elements when index==0.
97 //! If index!=0 - cached value of residuals/gradients will be used.
98 
99 double ResidualFunctionAdapter::element_residual(const std::vector<double>& pars,
100  unsigned int index, std::vector<double>& gradients)
101 {
102  if (index == 0) {
103  m_residuals = get_residuals(pars);
104  }
105 
106  if (!gradients.empty()) {
107  // Non zero size means that minimizer wants to know gradients.
108  if (pars.size() != gradients.size())
109  throw std::runtime_error("ResidualFunctionAdapter::element_residual() -> Error. "
110  "Number of gradients doesn't match number of fit parameters.");
111  if (index == 0)
112  calculate_gradients(pars);
113  for (size_t i_par = 0; i_par < pars.size(); ++i_par)
114  gradients[i_par] = m_gradients[i_par][index];
115  }
116 
117  return m_residuals[index];
118 }
119 
120 double ResidualFunctionAdapter::chi2(const std::vector<double>& pars)
121 {
123 
124  double result(0.0);
125  for (auto x : get_residuals(pars))
126  result += x * x;
127 
128  int fnorm = static_cast<int>(m_datasize) - static_cast<int>(m_parameters.freeParameterCount());
129  if (fnorm <= 0)
130  throw std::runtime_error("ResidualFunctionAdapter::chi2() -> Error. Normalization is 0");
131 
132  return result / fnorm;
133 }
Defines class ResidualFunctionAdapter.
Declares class RootResidualFunction.
std::function< double(const std::vector< double > &)> scalar_function_t
Definition: Types.h:30
std::function< double(const std::vector< double > &, unsigned int, std::vector< double > &)> gradient_function_t
Definition: Types.h:33
std::function< std::vector< double >(const mumufit::Parameters &)> fcn_residual_t
Definition: Types.h:40
Minimizer function with access to single data element residuals, required by Fumili2 and GSLMultiMin ...
A collection of fit parameters.
Definition: Parameters.h:26
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()
std::vector< std::vector< double > > m_gradients
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::unique_ptr< RootResidualFunction > m_root_objective
void calculate_gradients(const std::vector< double > &pars)
size_t m_datasize
Length of vector with residuals, should stay the same during minimization.
double chi2(const std::vector< double > &pars)
Evaluate chi2.
ResidualFunctionAdapter(fcn_residual_t func, const Parameters &parameters)
fcn_residual_t m_fcn
user function to minimize
The multi-library, multi-algorithm fit wrapper library.