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 
83 
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 
121 
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 
149 
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 
197 
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 
225 
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.
Defines ObjectiveMetric classes.
Defines and implements template 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: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
bool containsUncertainties() const
Definition: SimDataPair.cpp:81
SimulationResult simulationResult() const
Returns the result of last computed simulation.
Definition: SimDataPair.cpp:91
std::unique_ptr< OutputData< double > > data(Axes::Units units=Axes::Units::DEFAULT) const
const std::function< double(double)> l2Norm()
Returns L2 normalization function.
void checkIntegrity(const std::vector< double > &sim_data, const std::vector< double > &exp_data, const std::vector< double > &uncertainties, const std::vector< double > &weight_factors)