BornAgain  1.19.79
Open-source research software to simulate and fit neutron and x-ray reflectometry and grazing-incidence small-angle scattering
ObjectiveMetric.cpp
Go to the documentation of this file.
1 // ************************************************************************************************
2 //
3 // BornAgain: simulate and fit reflection and scattering
4 //
5 //! @file Sim/Fitting/ObjectiveMetric.cpp
6 //! @brief Implements ObjectiveMetric classes.
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 
16 #include "Device/Data/Datafield.h"
19 #include <cmath>
20 #include <limits>
21 #include <stdexcept>
22 
23 namespace {
24 
25 const double double_max = std::numeric_limits<double>::max();
26 const double double_min = std::numeric_limits<double>::min();
27 const double ln10 = std::log(10.0);
28 
29 template <class T>
30 T* copyMetric(const T& metric)
31 {
32  auto* result = new T;
33  result->setNorm(metric.norm());
34  return result;
35 }
36 
37 void checkIntegrity(const std::vector<double>& sim_data, const std::vector<double>& exp_data,
38  const std::vector<double>& weight_factors)
39 {
40  const size_t sim_size = sim_data.size();
41  if (sim_size != exp_data.size() || sim_size != weight_factors.size())
42  throw std::runtime_error("Error in ObjectiveMetric: input arrays have different sizes");
43 
44  for (size_t i = 0; i < sim_size; ++i)
45  if (sim_data[i] < 0.0)
46  throw std::runtime_error(
47  "Error in ObjectiveMetric: simulation data array contains negative values");
48 }
49 
50 void checkIntegrity(const std::vector<double>& sim_data, const std::vector<double>& exp_data,
51  const std::vector<double>& exp_stdv, const std::vector<double>& weight_factors)
52 {
53  if (sim_data.size() != exp_stdv.size())
54  throw std::runtime_error("Error in ObjectiveMetric: input arrays have different sizes");
55 
56  checkIntegrity(sim_data, exp_data, weight_factors);
57 }
58 
59 } // namespace
60 
61 ObjectiveMetric::ObjectiveMetric(std::function<double(double)> norm)
62  : m_norm(std::move(norm))
63 {
64 }
65 
66 double ObjectiveMetric::compute(const SimDataPair& data_pair, bool use_weights) const
67 {
68  if (use_weights && !data_pair.containsUncertainties())
69  throw std::runtime_error("Error in ObjectiveMetric::compute: the metric is weighted, but "
70  "the simulation-data pair does not contain uncertainties");
71 
72  if (use_weights)
73  return computeFromArrays(data_pair.simulation_array(), data_pair.experimental_array(),
74  data_pair.uncertainties_array(), data_pair.user_weights_array());
75  return computeFromArrays(data_pair.simulation_array(), data_pair.experimental_array(),
76  data_pair.user_weights_array());
77 }
78 
79 void ObjectiveMetric::setNorm(std::function<double(double)> norm)
80 {
81  m_norm = std::move(norm);
82 }
83 
84 // ----------------------- Chi2 metric ---------------------------
85 
88 {
89 }
90 
92 {
93  return copyMetric(*this);
94 }
95 
96 double Chi2Metric::computeFromArrays(std::vector<double> sim_data, std::vector<double> exp_data,
97  std::vector<double> exp_stdv,
98  std::vector<double> weight_factors) const
99 {
100  checkIntegrity(sim_data, exp_data, exp_stdv, weight_factors);
101 
102  double result = 0.0;
103  auto norm_fun = norm();
104  for (size_t i = 0, sim_size = sim_data.size(); i < sim_size; ++i)
105  if (exp_data[i] >= 0.0 && weight_factors[i] > 0.0 && exp_stdv[i] > 0.0)
106  result += norm_fun((exp_data[i] - sim_data[i]) / exp_stdv[i]) * weight_factors[i];
107 
108  return std::isfinite(result) ? result : double_max;
109 }
110 
111 double Chi2Metric::computeFromArrays(std::vector<double> sim_data, std::vector<double> exp_data,
112  std::vector<double> weight_factors) const
113 {
114  checkIntegrity(sim_data, exp_data, weight_factors);
115 
116  auto norm_fun = norm();
117  double result = 0.0;
118  for (size_t i = 0, sim_size = sim_data.size(); i < sim_size; ++i)
119  if (exp_data[i] >= 0.0 && weight_factors[i] > 0.0)
120  result += norm_fun(exp_data[i] - sim_data[i]) * weight_factors[i];
121 
122  return std::isfinite(result) ? result : double_max;
123 }
124 
125 // ----------------------- Poisson-like metric ---------------------------
126 
128  : Chi2Metric()
129 {
130 }
131 
133 {
134  return copyMetric(*this);
135 }
136 
137 double PoissonLikeMetric::computeFromArrays(std::vector<double> sim_data,
138  std::vector<double> exp_data,
139  std::vector<double> weight_factors) const
140 {
141  checkIntegrity(sim_data, exp_data, weight_factors);
142 
143  double result = 0.0;
144  auto norm_fun = norm();
145  for (size_t i = 0, sim_size = sim_data.size(); i < sim_size; ++i) {
146  if (weight_factors[i] <= 0.0 || exp_data[i] < 0.0)
147  continue;
148  const double variance = std::max(1.0, sim_data[i]);
149  const double value = (sim_data[i] - exp_data[i]) / std::sqrt(variance);
150  result += norm_fun(value) * weight_factors[i];
151  }
152 
153  return std::isfinite(result) ? result : double_max;
154 }
155 
156 // ----------------------- Log metric ---------------------------
157 
160 {
161 }
162 
164 {
165  return copyMetric(*this);
166 }
167 
168 double LogMetric::computeFromArrays(std::vector<double> sim_data, std::vector<double> exp_data,
169  std::vector<double> exp_stdv,
170  std::vector<double> weight_factors) const
171 {
172  checkIntegrity(sim_data, exp_data, exp_stdv, weight_factors);
173 
174  double result = 0.0;
175  auto norm_fun = norm();
176  for (size_t i = 0, sim_size = sim_data.size(); i < sim_size; ++i) {
177  if (weight_factors[i] <= 0.0 || exp_data[i] < 0.0 || exp_stdv[i] <= 0.0)
178  continue;
179  const double sim_val = std::max(double_min, sim_data[i]);
180  const double exp_val = std::max(double_min, exp_data[i]);
181  double value = std::log10(sim_val) - std::log10(exp_val);
182  value *= exp_val * ln10 / exp_stdv[i];
183  result += norm_fun(value) * weight_factors[i];
184  }
185 
186  return std::isfinite(result) ? result : double_max;
187 }
188 
189 double LogMetric::computeFromArrays(std::vector<double> sim_data, std::vector<double> exp_data,
190  std::vector<double> weight_factors) const
191 {
192  checkIntegrity(sim_data, exp_data, weight_factors);
193 
194  double result = 0.0;
195  auto norm_fun = norm();
196  for (size_t i = 0, sim_size = sim_data.size(); i < sim_size; ++i) {
197  if (weight_factors[i] <= 0.0 || exp_data[i] < 0.0)
198  continue;
199  const double sim_val = std::max(double_min, sim_data[i]);
200  const double exp_val = std::max(double_min, exp_data[i]);
201  result += norm_fun(std::log10(sim_val) - std::log10(exp_val)) * weight_factors[i];
202  }
203 
204  return std::isfinite(result) ? result : double_max;
205 }
206 
207 // ----------------------- Relative difference ---------------------------
208 
210  : Chi2Metric()
211 {
212 }
213 
215 {
216  return copyMetric(*this);
217 }
218 
219 double meanRelativeDifferenceMetric::computeFromArrays(std::vector<double> sim_data,
220  std::vector<double> exp_data,
221  std::vector<double> weight_factors) const
222 {
223  checkIntegrity(sim_data, exp_data, weight_factors);
224 
225  double result = 0.0;
226  auto norm_fun = norm();
227  for (size_t i = 0, sim_size = sim_data.size(); i < sim_size; ++i) {
228  if (weight_factors[i] <= 0.0 || exp_data[i] < 0.0)
229  continue;
230  const double sim_val = std::max(double_min, sim_data[i]);
231  const double exp_val = std::max(double_min, exp_data[i]);
232  result += norm_fun((exp_val - sim_val) / (exp_val + sim_val)) * weight_factors[i];
233  }
234 
235  return std::isfinite(result) ? result : double_max;
236 }
237 
238 // ----------------------- RQ4 metric ---------------------------
239 
241  : Chi2Metric()
242 {
243 }
244 
246 {
247  return copyMetric(*this);
248 }
249 
250 double RQ4Metric::compute(const SimDataPair& data_pair, bool use_weights) const
251 {
252  if (use_weights)
253  return Chi2Metric::compute(data_pair, use_weights);
254 
255  // fetching data in RQ4 form
256  const std::vector<double> sim_vec = data_pair.simulationResult().flatVector(Coords::RQ4);
257  const std::vector<double> exp_vec = data_pair.experimentalData().flatVector(Coords::RQ4);
258 
259  return computeFromArrays(sim_vec, exp_vec, data_pair.user_weights_array());
260 }
Defines and implements templated class Datafield.
Defines ObjectiveMetric utilities and corresponding namespace.
Defines ObjectiveMetric classes.
Defines class SimDataPair.
Implementation of the standard metric derived from maximum likelihood with Gaussian uncertainties....
double computeFromArrays(std::vector< double > sim_data, std::vector< double > exp_data, std::vector< double > exp_stdv, std::vector< double > weight_factors) const override
Computes metric value from data arrays. Negative values in exp_data are ignored as well as non-positi...
Chi2Metric * clone() const override
Implementation of the standard metric with intensity and experimental data being replaced by and ...
double computeFromArrays(std::vector< double > sim_data, std::vector< double > exp_data, std::vector< double > exp_stdv, std::vector< double > weight_factors) const override
Computes metric value from data arrays. Negative values in exp_data are ignored as well as non-positi...
LogMetric * clone() const override
Base class for metric implementations.
virtual double compute(const SimDataPair &data_pair, bool use_weights) const
Computes metric value from SimDataPair object. Calls computeFromArrays internally.
std::function< double(double)> m_norm
auto norm() const
Returns a copy of the normalization function used.
virtual double computeFromArrays(std::vector< double > sim_data, std::vector< double > exp_data, std::vector< double > exp_stdv, std::vector< double > weight_factors) const =0
Computes metric value from data arrays. Negative values in exp_data are ignored as well as non-positi...
ObjectiveMetric(std::function< double(double)> norm)
void setNorm(std::function< double(double)> norm)
Implementation of metric with standard deviation , where is the simulated intensity....
double computeFromArrays(std::vector< double > sim_data, std::vector< double > exp_data, std::vector< double > exp_stdv, std::vector< double > weight_factors) const override
Computes metric value from data arrays. Negative values in exp_data are ignored as well as non-positi...
PoissonLikeMetric * clone() const override
Implementation of relative difference metric. With default L2 norm and weighting off corresponds to t...
double compute(const SimDataPair &data_pair, bool use_weights) const override
Computes metric value from SimDataPair object. Calls computeFromArrays internally.
RQ4Metric * clone() const override
Holds pair of simulation/experimental data to fit.
Definition: SimDataPair.h:30
std::vector< double > experimental_array() const
Returns the flattened experimental data cut to the ROI area.
std::vector< double > user_weights_array() const
Returns a flat array of user weights cut to the ROI area.
std::vector< double > uncertainties_array() const
Returns the flattened experimental uncertainties cut to the ROI area. If no uncertainties are availab...
std::vector< double > simulation_array() const
Returns the flattened simulated intensities cut to the ROI area.
SimulationResult experimentalData() const
Returns the experimental data cut to the ROI area.
bool containsUncertainties() const
SimulationResult simulationResult() const
Returns the result of last computed simulation.
std::vector< double > flatVector(Coords units=Coords::UNDEFINED) const
Implementation of relative difference metric. With default L2 norm and weighting off corresponds to t...
double computeFromArrays(std::vector< double > sim_data, std::vector< double > exp_data, std::vector< double > exp_stdv, std::vector< double > weight_factors) const override
Computes metric value from data arrays. Negative values in exp_data are ignored as well as non-positi...
meanRelativeDifferenceMetric * clone() const override
Utility functions related to class ObjectiveMetric.
std::function< double(double)> l2Norm()
Returns L2 normalization function.