BornAgain  1.19.79
Open-source research software to simulate and fit neutron and x-ray reflectometry and grazing-incidence small-angle scattering
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 <memory>
19 #include <sstream>
20 #include <utility>
21 
22 namespace {
23 
24 // step size of derivative calculations
25 const double kEps = 1.0E-9;
26 
27 } // namespace
28 
29 using namespace mumufit;
30 
32  const mumufit::Parameters& parameters)
33  : m_datasize(0)
34  , m_fcn(std::move(func))
35  , m_parameters(parameters)
36 {
37  // single call of user function to get dataset size
38  auto residuals = m_fcn(parameters);
39  m_datasize = residuals.size();
40 }
41 
43 {
44  gradient_function_t gradient_fun = [&](const std::vector<double>& pars, unsigned int index,
45  std::vector<double>& gradients) {
46  return element_residual(pars, index, gradients);
47  };
48 
49  scalar_function_t objective_fun = [&](const std::vector<double>& pars) { return chi2(pars); };
50 
51  m_root_objective = std::make_unique<RootResidualFunction>(objective_fun, gradient_fun,
53 
54  return m_root_objective.get();
55 }
56 
57 void ResidualFunctionAdapter::calculate_gradients(const std::vector<double>& pars)
58 {
59  m_gradients.clear();
60  m_gradients.resize(pars.size());
61  for (size_t i_par = 0; i_par < pars.size(); ++i_par)
62  m_gradients[i_par].resize(m_datasize, 0.0);
63 
64  auto residuals = get_residuals(pars);
66 
67  for (size_t i_par = 0; i_par < pars.size(); ++i_par) {
68  std::vector<double> pars_deriv = pars; // values of parameters for derivative calculation
69  pars_deriv[i_par] += kEps;
70 
71  auto residuals2 = get_residuals(pars_deriv);
72 
73  for (size_t i_data = 0; i_data < m_datasize; ++i_data)
74  m_gradients[i_par][i_data] = (residuals2[i_data] - residuals[i_data]) / kEps;
75  }
76 }
77 
78 std::vector<double> ResidualFunctionAdapter::get_residuals(const std::vector<double>& pars)
79 {
80  if (pars.size() != m_parameters.size()) {
81  std::ostringstream ostr;
82  ostr << "ResidualFunctionAdapter::residuals() -> Error. Number of fit parameters "
83  << "has changed in the course of minimization. Initially was " << m_parameters.size()
84  << " become " << pars.size() << "\n";
85  throw std::runtime_error(ostr.str());
86  }
87 
88  m_parameters.setValues(pars);
89  auto result = m_fcn(m_parameters);
90 
91  if (result.size() != m_datasize) {
92  std::ostringstream ostr;
93  ostr << "ResidualFunctionAdapter::residuals() -> Error. Size of data "
94  << "has changed in the course of minimization. Initial length " << m_datasize
95  << " new length " << result.size() << "\n";
96  throw std::runtime_error(ostr.str());
97  }
98  return result;
99 }
100 
101 //! Returns residual for given data element index. If gradients vector size is not empty, also
102 //! calculates gradients. Actuall calculation is done for all data elements when index==0.
103 //! If index!=0 - cached value of residuals/gradients will be used.
104 
105 double ResidualFunctionAdapter::element_residual(const std::vector<double>& pars,
106  unsigned int index, std::vector<double>& gradients)
107 {
108  if (index == 0)
109  m_residuals = get_residuals(pars);
110 
111  if (!gradients.empty()) {
112  // Non zero size means that minimizer wants to know gradients.
113  if (pars.size() != gradients.size())
114  throw std::runtime_error("ResidualFunctionAdapter::element_residual() -> Error. "
115  "Number of gradients doesn't match number of fit parameters.");
116  if (index == 0)
117  calculate_gradients(pars);
118  for (size_t i_par = 0; i_par < pars.size(); ++i_par)
119  gradients[i_par] = m_gradients[i_par][index];
120  }
121 
122  return m_residuals[index];
123 }
124 
125 double ResidualFunctionAdapter::chi2(const std::vector<double>& pars)
126 {
128 
129  double result(0.0);
130  for (auto x : get_residuals(pars))
131  result += x * x;
132 
133  int fnorm = static_cast<int>(m_datasize) - static_cast<int>(m_parameters.freeParameterCount());
134  if (fnorm <= 0)
135  throw std::runtime_error("ResidualFunctionAdapter::chi2() -> Error. Normalization is 0");
136 
137  return result / fnorm;
138 }
std::function< double(const std::vector< double > &)> scalar_function_t
Definition: Types.h:31
std::function< double(const std::vector< double > &, unsigned int, std::vector< double > &)> gradient_function_t
Definition: Types.h:34
std::function< std::vector< double >(const mumufit::Parameters &)> fcn_residual_t
Definition: Types.h:41
Defines class ResidualFunctionAdapter.
Declares class RootResidualFunction.
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.