Mantid
Loading...
Searching...
No Matches
ComptonScatteringCountRate.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 +
11#include "MantidKernel/Logger.h"
13
14#include <sstream>
15
17
18using namespace CurveFitting;
19using Kernel::Logger;
20
21namespace {
23const char *CONSTRAINT_MATRIX_NAME = "IntensityConstraints";
25const char *BKGD_ORDER_ATTR_NAME = "BackgroundOrderAttr";
27Logger g_log("ComptonScatteringCountRate");
28} // namespace
29
30DECLARE_FUNCTION(ComptonScatteringCountRate)
31
32//----------------------------------------------------------------------------------------------
36 : CompositeFunction(), m_profiles(), m_fixedParamIndices(), m_cmatrix(), m_eqMatrix(), m_bkgdOrderAttr("n"),
37 m_bkgdPolyN(0), m_dataErrorRatio() {
38 // Must be a string to be able to be passed through Fit
39 declareAttribute(CONSTRAINT_MATRIX_NAME, IFunction::Attribute("", true));
40 declareAttribute(BKGD_ORDER_ATTR_NAME, IFunction::Attribute(m_bkgdOrderAttr));
41}
42
43//----------------------------------------------------------------------------------------------
44// Private methods
45//----------------------------------------------------------------------------------------------
46
53 if (name == CONSTRAINT_MATRIX_NAME)
54 parseIntensityConstraintMatrix(value.asUnquotedString());
55 else if (name == BKGD_ORDER_ATTR_NAME)
56 m_bkgdOrderAttr = value.asString();
57}
58
68 if (value.empty())
69 throw std::invalid_argument("ComptonScatteringCountRate - Empty string not allowed.");
70 std::istringstream is(value);
72}
73
74namespace {
78struct NoBkgdNorm2 {
80 NoBkgdNorm2(const Kernel::DblMatrix &cmatrix, const std::vector<double> &data)
81 : cm(cmatrix), nrows(cmatrix.numRows()), ncols(cmatrix.numCols()), rhs(data) {}
82
83 double eval(const std::vector<double> &xpt) const {
84 double norm2(0.0);
85 for (size_t i = 0; i < nrows; ++i) {
86 const double *cmRow = cm[i];
87 double cx(0.0);
88 for (size_t j = 0; j < ncols; ++j) {
89 cx += cmRow[j] * xpt[j];
90 }
91 cx -= rhs[i];
92 norm2 += cx * cx;
93 }
94 return 0.5 * norm2;
95 }
97 size_t nrows;
98 size_t ncols;
99 const std::vector<double> &rhs;
100};
104struct BkgdNorm2 {
106 BkgdNorm2(const Kernel::DblMatrix &cmatrix, const std::vector<double> &data)
107 : cm(cmatrix), nrows(cmatrix.numRows()), ncols(cmatrix.numCols()), rhs(data) {}
108
109 double eval(const size_t n, const double *xpt) const {
110 double norm2(0.0);
111 for (size_t i = 0; i < nrows; ++i) {
112 const double *cmRow = cm[i];
113 double cx(0.0);
114 for (size_t j = 0; j < n; ++j) {
115 cx += cmRow[j] * xpt[j];
116 }
117 cx *= -1.0; // our definition of cm has been multiplied by -1
118 cx -= rhs[i];
119 norm2 += cx * cx;
120 }
121 return 0.5 * norm2;
122 }
123 const Kernel::DblMatrix &cm;
124 size_t nrows;
125 size_t ncols;
126 const std::vector<double> &rhs;
127};
128} // namespace
129
151 const size_t nParams(m_cmatrix.numCols());
152
153 // Compute minimization with of Cx where the amplitudes are set to 1.0
154 std::vector<double> x0(nParams, 1);
156 // Compute the constraint matrix
157 this->updateCMatrixValues();
158
159 if (m_bkgdPolyN > 0) {
160 BkgdNorm2 objf(m_cmatrix, m_dataErrorRatio);
161 using namespace std::placeholders;
162 AugmentedLagrangianOptimizer::ObjFunction objfunc = std::bind(&BkgdNorm2::eval, objf, _1, _2);
164 lsqmin.minimize(x0);
165 // Set the parameters for the 'real' function calls
167 } else {
168 NoBkgdNorm2 objfunc(m_cmatrix, m_dataErrorRatio);
170 auto res = lsqmin.minimize(x0);
171 // Set the parameters for the 'real' function calls
173 }
174}
175
180void ComptonScatteringCountRate::setFixedParameterValues(const std::vector<double> &values) {
181 assert(values.size() == m_fixedParamIndices.size());
182
183 const size_t nParams = values.size();
184 for (size_t i = 0; i < nParams; ++i) {
185 this->setParameter(m_fixedParamIndices[i], values[i], true);
186 }
187
188 if (g_log.is(Logger::Priority::PRIO_DEBUG)) {
189 g_log.debug() << "--- New Intensity Parameters ---\n";
190 for (size_t i = 0; i < nParams; ++i) {
191 g_log.debug() << "x_" << i << "=" << values[i] << "\n";
192 }
193 }
194}
195
200 // -- Compute constraint matrix from each "member function" --
201 const size_t nprofiles = m_profiles.size();
202 for (size_t i = 0, start = 0; i < nprofiles; ++i) {
203 auto const *profile = m_profiles[i];
204 const size_t numFilled = profile->fillConstraintMatrix(m_cmatrix, start, m_hist->e());
205 start += numFilled;
206 }
207 // Using different minimizer for background constraints as the original is not
208 // stable
209 // The background one requires the contraints are specified as <= 0
210 if (m_bkgdPolyN > 0) {
211 m_cmatrix *= -1.0;
212 }
213
214 if (g_log.is(Logger::Priority::PRIO_DEBUG)) {
215 g_log.debug() << "--- CM ---\n";
216 for (size_t i = 0; i < m_cmatrix.numRows(); ++i) {
217 for (size_t j = 0; j < m_cmatrix.numCols(); ++j) {
218 g_log.debug() << m_cmatrix[i][j] << " ";
219 }
220 g_log.debug() << "\n";
221 }
222 }
223}
224
232void ComptonScatteringCountRate::setMatrixWorkspace(std::shared_ptr<const API::MatrixWorkspace> matrix, size_t wsIndex,
233 double startX, double endX) {
234 CompositeFunction::setMatrixWorkspace(matrix, wsIndex, startX, endX);
235
236 this->m_hist = std::make_shared<HistogramData::Histogram>(matrix->histogram(wsIndex));
237 this->wsIndex = wsIndex;
238 const auto &values = m_hist->y();
239 const auto &errors = m_hist->e();
240 // Keep the errors for the constraint calculation
241 m_dataErrorRatio.resize(errors.size());
242 std::transform(values.begin(), values.end(), errors.begin(), m_dataErrorRatio.begin(), std::divides<double>());
243
244 if (g_log.is(Kernel::Logger::Priority::PRIO_DEBUG)) {
245 g_log.debug() << "-- data/error --\n";
246 for (size_t i = 0; i < errors.size(); ++i) {
247 g_log.debug() << m_dataErrorRatio[i] << "\n";
248 }
249 }
250
253}
254
255/*
256 * Casts the pointers to ComptonProfiles to avoid overhead during fit. Also
257 * stores the indices, within the composite, of all of the intensity parameters
258 * and
259 * background parameters
260 */
262 // Cache ptrs cast to ComptonProfile functions to that we can compute the
263 // constraint matrix
264 const size_t nfuncs = this->nFunctions();
265 m_profiles.reserve(nfuncs); // won't include background
266 m_fixedParamIndices.reserve(nfuncs + 3); // guess at max size
267 bool foundBkgd(false);
268
269 for (size_t i = 0; i < nfuncs; ++i) {
270 auto func = this->getFunction(i);
271 const size_t paramsOffset = this->paramOffset(i); // offset for ith function inside composite
272
273 if (auto profile = std::dynamic_pointer_cast<ComptonProfile>(func)) {
274 this->cacheComptonProfile(profile, paramsOffset);
275 continue;
276 }
277
278 auto function1D = std::dynamic_pointer_cast<API::IFunction1D>(func);
279 if (!foundBkgd) {
280 this->cacheBackground(function1D, paramsOffset);
281 foundBkgd = true;
282 } else {
283 std::ostringstream os;
284 os << "ComptonScatteringCountRate - Invalid function member at index '" << i << "'. "
285 << "All members must be of type ComptonProfile and at most a single "
286 "1D function";
287 throw std::runtime_error(os.str());
288 }
289 }
290}
291
297void ComptonScatteringCountRate::cacheComptonProfile(const std::shared_ptr<ComptonProfile> &profile,
298 const size_t paramsOffset) {
299 m_profiles.emplace_back(profile.get());
300 auto fixedParams = profile->intensityParameterIndices();
301 for (auto fixedParam : fixedParams) {
302 const size_t indexOfFixed = paramsOffset + fixedParam;
303 this->setParameterStatus(indexOfFixed, Tied);
304 m_fixedParamIndices.emplace_back(indexOfFixed);
305 }
306}
307
313void ComptonScatteringCountRate::cacheBackground(const API::IFunction1D_sptr &function1D, const size_t paramsOffset) {
314 // Check for the order attribute
315 if (function1D->hasAttribute(m_bkgdOrderAttr)) {
316 m_bkgdPolyN = function1D->getAttribute(m_bkgdOrderAttr).asInt();
317 const auto npars = static_cast<size_t>(m_bkgdPolyN + 1); // + constant term
318 // we assume the parameters are at index 0->N on the background so we need
319 // to reverse them
320 for (size_t i = npars; i > 0; --i) // i = from npars->1
321 {
322 const size_t indexOfFixed = paramsOffset + (i - 1);
323 this->setParameterStatus(indexOfFixed, Tied);
324 m_fixedParamIndices.emplace_back(indexOfFixed);
325 }
326 } else {
327 std::ostringstream os;
328 os << "ComptonScatteringCountRate - Background function does not have "
329 "attribute named '"
330 << m_bkgdOrderAttr << "' that specifies its order. Use the '" << BKGD_ORDER_ATTR_NAME
331 << "' attribute to specify the name of the order attribute.";
332 throw std::runtime_error(os.str());
333 }
334}
335
336/*
337 * The equality constraint matrix is padded out to allow for any
338 * additional intensity constraints required each specific Compton profile.
339 * Also creates the inequality matrix
340 * @param xValues The xdata from the workspace
341 */
343 const size_t nmasses = m_profiles.size();
344
345 // Sanity check that equality constraints matrix has the same number of
346 // columns as masses or is zero-sized
347 if (m_eqMatrix.numCols() > 0 && m_eqMatrix.numCols() != nmasses) {
348 std::ostringstream os;
349 os << "ComptonScatteringCountRate - Equality constraint matrix (Aeq) has "
350 "incorrect number of columns ("
351 << m_eqMatrix.numCols() << "). The number of columns should match the number of masses (" << nmasses << ")";
352 throw std::invalid_argument(os.str());
353 }
354
356 createEqualityCM(nmasses);
357
358 if (g_log.is(Logger::Priority::PRIO_DEBUG)) {
359 g_log.debug() << "\n--- aeq ---\n";
360 for (size_t i = 0; i < m_eqMatrix.numRows(); ++i) {
361 for (size_t j = 0; j < m_eqMatrix.numCols(); ++j) {
362 g_log.debug() << m_eqMatrix[i][j] << " ";
363 }
364 g_log.debug() << "\n";
365 }
366 }
367}
368
370 // -- Constraint matrix for J(y) > 0 --
371 // The first N columns are filled with J(y) for each mass + N_h for the first
372 // mass hermite
373 // terms included + (n+1) for each termn the background of order n
374 // The background columns are filled with x^j/error where j=(1,n+1)
375 const auto &xValues = m_hist->x();
376 const auto &errors = m_hist->e();
377
378 const size_t nrows(xValues.size());
379 size_t nColsCMatrix(m_fixedParamIndices.size());
380 m_cmatrix = Kernel::DblMatrix(nrows, nColsCMatrix);
381
382 // Fill background values as they don't change at all
383 if (m_bkgdPolyN > 0) {
384 size_t polyStartCol = nColsCMatrix - m_bkgdPolyN - 1;
385 for (size_t i = 0; i < nrows; ++i) // rows
386 {
387 double *row = m_cmatrix[i];
388 const double &xi = xValues[i];
389 const double &erri = errors[i];
390 size_t polyN = m_bkgdPolyN;
391 for (size_t j = polyStartCol; j < nColsCMatrix; ++j) // cols
392 {
393 row[j] = std::pow(xi, static_cast<double>(polyN)) / erri;
394 --polyN;
395 }
396 }
397 }
398}
399
404 // -- Equality constraints --
405 // The user-specified equality matrix needs to be padded on the left with the
406 // copies of the first column
407 // until the number of cols matches the number of fixed parameters to account
408 // for the extra coefficients
409 // for the hermite term.
410 // It then needs to be padded on the right with zeroes to account for the
411 // background terms
412 auto userMatrix = m_eqMatrix; // copy original
413 const size_t nconstr = userMatrix.numRows();
414
415 const size_t nColsCMatrix(m_cmatrix.numCols());
416 size_t nFixedProfilePars(m_fixedParamIndices.size());
417 // skip background for lhs padding
418 if (m_bkgdPolyN > 0) {
419 nFixedProfilePars -= (m_bkgdPolyN + 1);
420 }
421 const size_t nExtraColsLeft = (nFixedProfilePars - nmasses);
422
423 m_eqMatrix = Kernel::DblMatrix(nconstr, nColsCMatrix); // all zeroed by default.
424 for (size_t i = 0; i < nconstr; ++i) {
425 const double *userRow = userMatrix[i];
426 double *destRow = m_eqMatrix[i];
427 for (size_t j = 0; j < nFixedProfilePars; ++j) {
428 destRow[j] = (j < nExtraColsLeft) ? userRow[0] : userRow[j - nExtraColsLeft];
429 }
430 }
431}
432
433} // namespace Mantid::CurveFitting::Functions
std::string name
Definition Run.cpp:60
const Kernel::DblMatrix & cm
const std::vector< double > & rhs
double value
The value of the point.
Definition FitMW.cpp:51
#define DECLARE_FUNCTION(classname)
Macro for declaring a new type of function to be used with the FunctionFactory.
A composite function is a function containing other functions.
void setParameter(size_t, const double &value, bool explicitlySet=true) override
Set i-th parameter.
size_t paramOffset(size_t i) const
std::size_t nFunctions() const override
Number of functions.
size_t nParams() const override
Total number of parameters.
void setMatrixWorkspace(std::shared_ptr< const API::MatrixWorkspace > workspace, size_t wi, double startX, double endX) override
Set matrix workspace.
IFunction_sptr getFunction(std::size_t i) const override
Returns the pointer to i-th function.
void setAttribute(const std::string &name, const API::IFunction::Attribute &value) override
Set a value of a named attribute.
void setParameterStatus(size_t i, ParameterStatus status) override
Change status of parameter.
Attribute is a non-fitting parameter.
Definition IFunction.h:286
Implements the Augmented Lagrangian optimization method of Birgin & Martinez.
boost::function< double(const size_t, const double *)> ObjFunction
Function type.
void minimize(std::vector< double > &xv) const
Perform the minimization.
Implements a specialized function that encapsulates the combination of ComptonProfile functions that ...
Kernel::DblMatrix m_cmatrix
Positivity constraints on J(y)
void setFixedParameterValues(const std::vector< double > &values)
Set the fixed parameters to the given values.
void cacheBackground(const API::IFunction1D_sptr &function1D, const size_t paramsOffset)
Cache parameters positions for background function.
std::string m_bkgdOrderAttr
Name of order attribute on background function.
void updateCMatrixValues() const
Refresh the values of the C matrix for this evaluation.
Kernel::DblMatrix m_eqMatrix
Intensity equality constraints.
std::vector< double > m_dataErrorRatio
Ratio of data & errors.
void cacheFunctions()
Cache ptrs to the individual profiles and their parameters.
std::vector< ComptonProfile * > m_profiles
Holder for non-owning functions cast as ComptonProfiles.
void parseIntensityConstraintMatrix(const std::string &value)
Takes the string & constructs the constraint matrix.
void setMatrixWorkspace(std::shared_ptr< const API::MatrixWorkspace > matrix, size_t wsIndex, double startX, double endX) override
Cache reference to workspace for use in setupForFit.
void setAttribute(const std::string &name, const Attribute &value) override
Set an attribute value (and possibly cache its value)
void cacheComptonProfile(const std::shared_ptr< ComptonProfile > &profile, const size_t paramsOffset)
Cache ptr to the individual profile and its parameters.
std::shared_ptr< HistogramData::Histogram > m_hist
The histogram of the matrix workspace being cached for use.
std::vector< size_t > m_fixedParamIndices
Store parameter indices of intensity parameters that are fixed.
void createEqualityCM(const size_t nmasses)
Set up equality constraint matrix.
void iterationStarting() override
Called by the framework just before an iteration is starting.
void debug(const std::string &msg)
Logs at debug level.
Definition Logger.cpp:145
bool is(int level) const
Returns true if at least the given log level is set.
Definition Logger.cpp:177
Minimize an objective function using the SLSQP optimization subroutine originally implemented by Diet...
std::vector< double > minimize(const std::vector< double > &x0) const
Perform the minimization.
size_t numRows() const
Return the number of rows in the matrix.
Definition Matrix.h:143
size_t numCols() const
Return the number of columns in the matrix.
Definition Matrix.h:146
Kernel::Logger g_log("ExperimentInfo")
static logger object
std::shared_ptr< IFunction1D > IFunction1D_sptr
Definition IFunction1D.h:76
double MANTID_CURVEFITTING_DLL norm2(const DoubleFortranVector &v)
Compute the 2-norm of a vector which is a square root of the sum of squares of its elements.
void fillFromStream(std::istream &, Kernel::Matrix< T > &, const char)
Fill a Matrix from a stream using the given separator.
Definition Matrix.cpp:1628
Mantid::Kernel::Matrix< double > DblMatrix
Definition Matrix.h:205