Mantid
Loading...
Searching...
No Matches
MaxentCalculator.cpp
Go to the documentation of this file.
1// Mantid Repository : https://github.com/mantidproject/mantid
2//
3// Copyright © 2018 ISIS Rutherford Appleton Laboratory UKRI,
4// NScD Oak Ridge National Laboratory, European Spallation Source,
5// Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS
6// SPDX - License - Identifier: GPL - 3.0 +
8#include <cmath>
9#include <utility>
10
11namespace Mantid::Algorithms {
12
21 : m_data(), m_errors(), m_image(), m_dataCalc(), m_background(1.0), m_angle(-1.), m_chisq(-1.), m_directionsIm(),
22 m_coeffs(), m_entropy(std::move(entropy)), m_transform(std::move(transform)) {}
23
29std::vector<double> MaxentCalculator::calculateChiGrad() const {
30
31 // Calculates the gradient of Chi
32 // CGrad_i = -2 * [ data_i - dataCalc_i ] / [ error_i ]^2
33
34 if ((m_data.size() != m_errors.size()) || (m_dataCalc.size() % m_data.size())) {
35 // Data and errors must have the same number of data points
36 // but the reconstructed (calculated) data may contain more points
37 throw std::runtime_error("Cannot compute gradient of Chi");
38 }
39
40 // We only consider the experimental data points to calculate chi grad
41 size_t sizeDat = m_data.size();
42 // The number of calculated data points can be bigger than the number of
43 // experimental data points I am not sure how we should deal with this
44 // situation. On the one hand one can only consider real data and errors to
45 // calculate chi-square, but on the other hand this method should return a
46 // vector of size equal to the size of the calculated data, so I am just
47 // setting the 'leftovers' to zero. This is what is done in the original
48 // muon code.
49 size_t sizeDatCalc = m_dataCalc.size();
50 std::vector<double> cgrad(sizeDatCalc, 0.);
51 auto dpoints = static_cast<double>(sizeDat);
52 for (size_t i = 0; i < sizeDat; i++) {
53 if (m_errors[i] != 0)
54 cgrad[i] = -2. * (m_data[i] - m_dataCalc[i]) / m_errors[i] / m_errors[i] / dpoints;
55 }
56
57 return cgrad;
58}
59
64std::vector<double> MaxentCalculator::getReconstructedData() const {
65
66 if (m_dataCalc.empty()) {
67 // If it is empty it means we didn't load valid data
68 throw std::runtime_error("No data were loaded");
69 }
70 return m_dataCalc;
71}
72
77std::vector<double> MaxentCalculator::getImage() const {
78
79 if (m_image.empty()) {
80 // If it is empty it means we didn't load valid data
81 throw std::runtime_error("No image was loaded");
82 }
83 return m_image;
84}
85
90std::vector<std::vector<double>> MaxentCalculator::getSearchDirections() const {
91
92 if (m_directionsIm.empty()) {
93 throw std::runtime_error("Search directions have not been calculated");
94 }
95 return m_directionsIm;
96}
97
103
104 if (!m_coeffs.c1.size().first) {
105 // This means that none of the coefficients were calculated
106 throw std::runtime_error("Quadratic coefficients have not been calculated");
107 }
108 return m_coeffs;
109}
110
117
118 if (m_angle == -1) {
119 throw std::runtime_error("Angle has not been calculated");
120 }
121 return m_angle;
122}
123
129
130 if (m_chisq == -1.) {
131 throw std::runtime_error("Chisq has not been calculated");
132 }
133 return m_chisq;
134}
135
136std::vector<double> MaxentCalculator::calculateData(const std::vector<double> &image) const {
137 return m_transform->imageToData(image);
138}
139
140std::vector<double> MaxentCalculator::calculateImage(const std::vector<double> &data) const {
141 return m_transform->dataToImage(data);
142}
143
155void MaxentCalculator::iterate(const std::vector<double> &data, const std::vector<double> &errors,
156 const std::vector<double> &image, double background,
157 const std::vector<double> &linearAdjustments,
158 const std::vector<double> &constAdjustments) {
159 // Some checks
160 if (data.empty() || errors.empty() || (data.size() != errors.size())) {
161 throw std::invalid_argument("Cannot calculate quadratic coefficients: invalid data");
162 }
163 if (image.empty()) {
164 throw std::invalid_argument("Cannot calculate quadratic coefficients: invalid image");
165 }
166 if (background == 0) {
167 throw std::invalid_argument("Cannot calculate quadratic coefficients: invalid background");
168 }
169 m_data = data;
170 m_errors = errors;
171 m_image = m_entropy->correctValues(image, background);
172 m_background = background;
173 m_dataCalc = m_transform->imageToData(image);
174
175 // Set to -1, these will be calculated later
176 m_angle = -1.;
177 m_chisq = -1.;
178
179 // adjust calculated data, if required
180 if (!linearAdjustments.empty()) {
181 if (linearAdjustments.size() < m_dataCalc.size()) {
182 throw std::invalid_argument("Cannot adjust calculated data: too few linear adjustments");
183 }
184 for (size_t j = 0; j < m_dataCalc.size() / 2; ++j) {
185 double yr = m_dataCalc[2 * j];
186 double yi = m_dataCalc[2 * j + 1];
187 m_dataCalc[2 * j] = yr * linearAdjustments[2 * j] - yi * linearAdjustments[2 * j + 1];
188 m_dataCalc[2 * j + 1] = yi * linearAdjustments[2 * j] + yr * linearAdjustments[2 * j + 1];
189 }
190 }
191 if (!constAdjustments.empty()) {
192 if (constAdjustments.size() < m_dataCalc.size()) {
193 throw std::invalid_argument("Cannot adjust calculated data: too few constant adjustments");
194 }
195 for (size_t i = 0; i < m_dataCalc.size(); ++i) {
196 m_dataCalc[i] += constAdjustments[i];
197 }
198 }
199
200 // Two search directions
201 const size_t dim = 2;
202
203 size_t npoints = m_image.size();
204
205 // Gradient of chi (in image space)
206 std::vector<double> cgrad = m_transform->dataToImage(calculateChiGrad());
207 // Gradient of entropy
208 std::vector<double> sgrad = m_entropy->derivative(m_image, m_background);
209 // Metric (second derivative of the entropy)
210 std::vector<double> metric = m_entropy->secondDerivative(m_image, m_background);
211
212 if (cgrad.size() != npoints || sgrad.size() != npoints || metric.size() != npoints)
213 throw std::runtime_error("Cannot calculate quadratic coefficients: invalid image space");
214
215 double cnorm = 0.;
216 double snorm = 0.;
217 double csnorm = 0.;
218
219 // Here we calculate:
220 // SB. eq 22 -> |grad S|, |grad C|
221 // SB. eq 37 -> test
222 for (size_t i = 0; i < npoints; i++) {
223 auto metric2 = metric[i] * metric[i];
224 cnorm += cgrad[i] * cgrad[i] * metric2;
225 snorm += sgrad[i] * sgrad[i] * metric2;
226 csnorm += cgrad[i] * sgrad[i] * metric2;
227 }
228 cnorm = sqrt(cnorm);
229 snorm = sqrt(snorm);
230
231 if (cnorm == 0) {
232 cnorm = 1.;
233 }
234 if (snorm == 0) {
235 snorm = 1.;
236 }
237
238 m_angle = sqrt(0.5 * (1. - csnorm / snorm / cnorm));
239 // csnorm could be greater than snorm * cnorm due to rounding issues
240 // so check for nan
241 if (!std::isfinite(m_angle)) {
242 m_angle = 0.;
243 }
244
245 // Calculate the search directions
246
247 // Search directions (image space)
248 m_directionsIm = std::vector<std::vector<double>>(2, std::vector<double>(npoints, 0.));
249
250 for (size_t i = 0; i < npoints; i++) {
251 m_directionsIm[0][i] = metric[i] * cgrad[i] / cnorm;
252 m_directionsIm[1][i] = metric[i] * sgrad[i] / snorm;
253 }
254
255 // Search directions (data space)
256 // Not needed outside this method
257 auto directionsDat = std::vector<std::vector<double>>(2, std::vector<double>(npoints, 0.));
258 directionsDat[0] = m_transform->imageToData(m_directionsIm[0]);
259 directionsDat[1] = m_transform->imageToData(m_directionsIm[1]);
260
262 double factor = getChisq() * double(npoints) / 2;
263 auto resolutionFactor = static_cast<double>(m_dataCalc.size() / m_data.size());
264
265 // Calculate the quadratic coefficients SB. eq 24
266
267 // First compute s1, c1
268 m_coeffs.s1 = Kernel::DblMatrix(dim, 1);
269 m_coeffs.c1 = Kernel::DblMatrix(dim, 1);
270 for (size_t k = 0; k < dim; k++) {
271 m_coeffs.c1[k][0] = m_coeffs.s1[k][0] = 0.;
272 for (size_t i = 0; i < npoints; i++) {
273 m_coeffs.s1[k][0] += m_directionsIm[k][i] * sgrad[i];
274 m_coeffs.c1[k][0] += m_directionsIm[k][i] * cgrad[i];
275 }
276 m_coeffs.c1[k][0] /= factor;
277 }
278
279 // Then s2
280 m_coeffs.s2 = Kernel::DblMatrix(dim, dim);
281 for (size_t k = 0; k < dim; k++) {
282 for (size_t l = 0; l < k + 1; l++) {
283 m_coeffs.s2[k][l] = 0.;
284 for (size_t i = 0; i < npoints; i++) {
285 m_coeffs.s2[k][l] -= m_directionsIm[k][i] * m_directionsIm[l][i] / metric[i];
286 }
287 }
288 }
289 // Then c2
290 npoints = m_errors.size();
291 m_coeffs.c2 = Kernel::DblMatrix(dim, dim);
292 for (size_t k = 0; k < dim; k++) {
293 for (size_t l = 0; l < k + 1; l++) {
294 m_coeffs.c2[k][l] = 0.;
295 for (size_t i = 0; i < npoints; i++) {
296 if (m_errors[i] != 0)
297 m_coeffs.c2[k][l] += directionsDat[k][i] * directionsDat[l][i] / m_errors[i] / m_errors[i];
298 }
299 m_coeffs.c2[k][l] *= 2.0 / factor * resolutionFactor;
300 }
301 }
302
303 // Symmetrise s2, c2: reflect accross the diagonal
304 for (size_t k = 0; k < dim; k++) {
305 for (size_t l = k + 1; l < dim; l++) {
306 m_coeffs.s2[k][l] = m_coeffs.s2[l][k];
307 m_coeffs.c2[k][l] = m_coeffs.c2[l][k];
308 }
309 }
310}
311
316 if (m_data.empty() || m_errors.empty() || m_dataCalc.empty()) {
317 throw std::runtime_error("Cannot calculate chi-square");
318 }
320}
321
324double MaxentCalculator::calculateChiSquared(const std::vector<double> &data) const {
325 size_t npoints = m_data.size();
326 auto dpoints = static_cast<double>(npoints);
327
328 double chisq = 0;
329 for (size_t i = 0; i < npoints; i++) {
330 double term = (m_data[i] - data[i]) / m_errors[i];
331 chisq += term * term;
332 }
333 return chisq / dpoints;
334}
335
336} // namespace Mantid::Algorithms
const std::vector< Type > & m_data
Definition: TableColumn.h:417
std::vector< std::vector< double > > getSearchDirections() const
Returns the search directions (in image space)
void calculateChisq()
Calculates chi-square.
std::vector< double > calculateData(const std::vector< double > &image) const
double getAngle() const
Returns the angle between the gradient of chi-square and the gradient of the entropy (calculated and ...
std::vector< double > getReconstructedData() const
Returns the reconstructed (calculated) data.
std::vector< std::vector< double > > m_directionsIm
QuadraticCoefficients getQuadraticCoefficients() const
Returns the quadratic coefficients.
std::vector< double > calculateChiGrad() const
Calculates the gradient of chi-square using the experimental data, calculated data and errors.
void iterate(const std::vector< double > &data, const std::vector< double > &errors, const std::vector< double > &image, double background, const std::vector< double > &linearAdjustments, const std::vector< double > &constAdjustments)
Performs an iteration and calculates everything: search directions (SB.
double getChisq()
Returns chi-square.
double calculateChiSquared(const std::vector< double > &data) const
Calculate ChiSq = 1 / N sum_i [ data_i - dataCalc_i ]^2 / [ error_i ]^2.
std::vector< double > getImage() const
Returns the (reconstructed) image.
std::vector< double > calculateImage(const std::vector< double > &data) const
std::pair< size_t, size_t > size() const
Access matrix sizes.
Definition: Matrix.h:141
std::shared_ptr< MaxentTransform > MaxentTransform_sptr
std::shared_ptr< MaxentEntropy > MaxentEntropy_sptr
Definition: MaxentEntropy.h:33
Mantid::Kernel::Matrix< double > DblMatrix
Definition: Matrix.h:206
STL namespace.
Mantid::Kernel::DblMatrix s2
Mantid::Kernel::DblMatrix s1
Mantid::Kernel::DblMatrix c1
Mantid::Kernel::DblMatrix c2