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