BornAgain  1.19.0
Simulate and fit neutron and x-ray scattering at grazing incidence
ObjectiveMetric.cpp
Go to the documentation of this file.
1 // ************************************************************************************************
2 //
3 // BornAgain: simulate and fit reflection and scattering
4 //
5 //! @file Core/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 
18 #include "Device/Data/OutputData.h"
19 #include <cmath>
20 #include <limits>
21 
22 namespace {
23 const double double_max = std::numeric_limits<double>::max();
24 const double double_min = std::numeric_limits<double>::min();
25 const double ln10 = std::log(10.0);
26 
27 template <class T> T* copyMetric(const T& metric)
28 {
29  auto* result = new T;
30  result->setNorm(metric.norm());
31  return result;
32 }
33 
34 void checkIntegrity(const std::vector<double>& sim_data, const std::vector<double>& exp_data,
35  const std::vector<double>& weight_factors)
36 {
37  const size_t sim_size = sim_data.size();
38  if (sim_size != exp_data.size() || sim_size != weight_factors.size())
39  throw std::runtime_error("Error in ObjectiveMetric: input arrays have different sizes");
40 
41  for (size_t i = 0; i < sim_size; ++i)
42  if (sim_data[i] < 0.0)
43  throw std::runtime_error(
44  "Error in ObjectiveMetric: simulation data array contains negative values");
45 }
46 
47 void checkIntegrity(const std::vector<double>& sim_data, const std::vector<double>& exp_data,
48  const std::vector<double>& uncertainties,
49  const std::vector<double>& weight_factors)
50 {
51  if (sim_data.size() != uncertainties.size())
52  throw std::runtime_error("Error in ObjectiveMetric: input arrays have different sizes");
53 
54  checkIntegrity(sim_data, exp_data, weight_factors);
55 }
56 } // namespace
57 
58 ObjectiveMetric::ObjectiveMetric(std::function<double(double)> norm) : m_norm(std::move(norm)) {}
59 
60 double ObjectiveMetric::compute(const SimDataPair& data_pair, bool use_weights) const
61 {
62  if (use_weights && !data_pair.containsUncertainties())
63  throw std::runtime_error("Error in ObjectiveMetric::compute: the metric is weighted, but "
64  "the simulation-data pair does not contain uncertainties");
65 
66  if (use_weights)
67  return computeFromArrays(data_pair.simulation_array(), data_pair.experimental_array(),
68  data_pair.uncertainties_array(), data_pair.user_weights_array());
69  else
70  return computeFromArrays(data_pair.simulation_array(), data_pair.experimental_array(),
71  data_pair.user_weights_array());
72 }
73 
74 void ObjectiveMetric::setNorm(std::function<double(double)> norm)
75 {
76  m_norm = std::move(norm);
77 }
78 
79 // ----------------------- Chi2 metric ---------------------------
80 
82 
84 {
85  return copyMetric(*this);
86 }
87 
88 double Chi2Metric::computeFromArrays(std::vector<double> sim_data, std::vector<double> exp_data,
89  std::vector<double> uncertainties,
90  std::vector<double> weight_factors) const
91 {
92  checkIntegrity(sim_data, exp_data, uncertainties, weight_factors);
93 
94  double result = 0.0;
95  auto norm_fun = norm();
96  for (size_t i = 0, sim_size = sim_data.size(); i < sim_size; ++i)
97  if (exp_data[i] >= 0.0 && weight_factors[i] > 0.0 && uncertainties[i] > 0.0)
98  result += norm_fun((exp_data[i] - sim_data[i]) / uncertainties[i]) * weight_factors[i];
99 
100  return std::isfinite(result) ? result : double_max;
101 }
102 
103 double Chi2Metric::computeFromArrays(std::vector<double> sim_data, std::vector<double> exp_data,
104  std::vector<double> weight_factors) const
105 {
106  checkIntegrity(sim_data, exp_data, weight_factors);
107 
108  auto norm_fun = norm();
109  double result = 0.0;
110  for (size_t i = 0, sim_size = sim_data.size(); i < sim_size; ++i)
111  if (exp_data[i] >= 0.0 && weight_factors[i] > 0.0)
112  result += norm_fun(exp_data[i] - sim_data[i]) * weight_factors[i];
113 
114  return std::isfinite(result) ? result : double_max;
115 }
116 
117 // ----------------------- Poisson-like metric ---------------------------
118 
120 
122 {
123  return copyMetric(*this);
124 }
125 
126 double PoissonLikeMetric::computeFromArrays(std::vector<double> sim_data,
127  std::vector<double> exp_data,
128  std::vector<double> weight_factors) const
129 {
130  checkIntegrity(sim_data, exp_data, weight_factors);
131 
132  double result = 0.0;
133  auto norm_fun = norm();
134  for (size_t i = 0, sim_size = sim_data.size(); i < sim_size; ++i) {
135  if (weight_factors[i] <= 0.0 || exp_data[i] < 0.0)
136  continue;
137  const double variance = std::max(1.0, sim_data[i]);
138  const double value = (sim_data[i] - exp_data[i]) / std::sqrt(variance);
139  result += norm_fun(value) * weight_factors[i];
140  }
141 
142  return std::isfinite(result) ? result : double_max;
143 }
144 
145 // ----------------------- Log metric ---------------------------
146 
148 
150 {
151  return copyMetric(*this);
152 }
153 
154 double LogMetric::computeFromArrays(std::vector<double> sim_data, std::vector<double> exp_data,
155  std::vector<double> uncertainties,
156  std::vector<double> weight_factors) const
157 {
158  checkIntegrity(sim_data, exp_data, uncertainties, weight_factors);
159 
160  double result = 0.0;
161  auto norm_fun = norm();
162  for (size_t i = 0, sim_size = sim_data.size(); i < sim_size; ++i) {
163  if (weight_factors[i] <= 0.0 || exp_data[i] < 0.0 || uncertainties[i] <= 0.0)
164  continue;
165  const double sim_val = std::max(double_min, sim_data[i]);
166  const double exp_val = std::max(double_min, exp_data[i]);
167  double value = std::log10(sim_val) - std::log10(exp_val);
168  value *= exp_val * ln10 / uncertainties[i];
169  result += norm_fun(value) * weight_factors[i];
170  }
171 
172  return std::isfinite(result) ? result : double_max;
173 }
174 
175 double LogMetric::computeFromArrays(std::vector<double> sim_data, std::vector<double> exp_data,
176  std::vector<double> weight_factors) const
177 {
178  checkIntegrity(sim_data, exp_data, weight_factors);
179 
180  double result = 0.0;
181  auto norm_fun = norm();
182  for (size_t i = 0, sim_size = sim_data.size(); i < sim_size; ++i) {
183  if (weight_factors[i] <= 0.0 || exp_data[i] < 0.0)
184  continue;
185  const double sim_val = std::max(double_min, sim_data[i]);
186  const double exp_val = std::max(double_min, exp_data[i]);
187  result += norm_fun(std::log10(sim_val) - std::log10(exp_val)) * weight_factors[i];
188  }
189 
190  return std::isfinite(result) ? result : double_max;
191 }
192 
193 // ----------------------- Relative difference ---------------------------
194 
196 
198 {
199  return copyMetric(*this);
200 }
201 
202 double RelativeDifferenceMetric::computeFromArrays(std::vector<double> sim_data,
203  std::vector<double> exp_data,
204  std::vector<double> weight_factors) const
205 {
206  checkIntegrity(sim_data, exp_data, weight_factors);
207 
208  double result = 0.0;
209  auto norm_fun = norm();
210  for (size_t i = 0, sim_size = sim_data.size(); i < sim_size; ++i) {
211  if (weight_factors[i] <= 0.0 || exp_data[i] < 0.0)
212  continue;
213  const double sim_val = std::max(double_min, sim_data[i]);
214  const double exp_val = std::max(double_min, exp_data[i]);
215  result += norm_fun((exp_val - sim_val) / (exp_val + sim_val)) * weight_factors[i];
216  }
217 
218  return std::isfinite(result) ? result : double_max;
219 }
220 
221 // ----------------------- RQ4 metric ---------------------------
222 
224 
226 {
227  return copyMetric(*this);
228 }
229 
230 double RQ4Metric::compute(const SimDataPair& data_pair, bool use_weights) const
231 {
232  if (use_weights)
233  return Chi2Metric::compute(data_pair, use_weights);
234 
235  // fetching data in RQ4 form
236  auto sim_data = data_pair.simulationResult().data(Axes::Units::RQ4);
237  auto exp_data = data_pair.experimentalData().data(Axes::Units::RQ4);
238 
239  return computeFromArrays(sim_data->getRawDataVector(), exp_data->getRawDataVector(),
240  data_pair.user_weights_array());
241 }
Defines ObjectiveMetric utilities and corresponding namespace.
Defines ObjectiveMetric classes.
Defines and implements templated class OutputData.
Defines class SimDataPair.
Implementation of the standard metric derived from maximum likelihood with Gaussian uncertainties.
Chi2Metric * clone() const override
double computeFromArrays(std::vector< double > sim_data, std::vector< double > exp_data, std::vector< double > uncertainties, std::vector< double > weight_factors) const override
Computes metric value from data arrays.
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 > uncertainties, std::vector< double > weight_factors) const override
Computes metric value from data arrays.
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.
virtual double computeFromArrays(std::vector< double > sim_data, std::vector< double > exp_data, std::vector< double > uncertainties, std::vector< double > weight_factors) const =0
Computes metric value from data arrays.
std::function< double(double)> m_norm
auto norm() const
Returns a copy of the normalization function used.
ObjectiveMetric(std::function< double(double)> norm)
void setNorm(std::function< double(double)> norm)
Implementation of metric with standard deviation , where is the simulated intensity.
PoissonLikeMetric * clone() const override
double computeFromArrays(std::vector< double > sim_data, std::vector< double > exp_data, std::vector< double > uncertainties, std::vector< double > weight_factors) const override
Computes metric value from data arrays.
Implementation of relative difference metric.
double compute(const SimDataPair &data_pair, bool use_weights) const override
Computes metric value from SimDataPair object.
RQ4Metric * clone() const override
Implementation of relative difference metric.
double computeFromArrays(std::vector< double > sim_data, std::vector< double > exp_data, std::vector< double > uncertainties, std::vector< double > weight_factors) const override
Computes metric value from data arrays.
RelativeDifferenceMetric * 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.
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
Definition: SimDataPair.cpp:86
SimulationResult simulationResult() const
Returns the result of last computed simulation.
Definition: SimDataPair.cpp:96
std::unique_ptr< OutputData< double > > data(Axes::Units units=Axes::Units::DEFAULT) const
Utility functions related to class ObjectiveMetric.
const std::function< double(double)> l2Norm()
Returns L2 normalization function.
Definition: filesystem.h:81