Mantid
Loading...
Searching...
No Matches
Fit.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 +
7//----------------------------------------------------------------------
8// Includes
9//----------------------------------------------------------------------
13
20#include "MantidAPI/TableRow.h"
22
28
29#include <memory>
30
32
33// Register the class into the algorithm factory
35
36
37Fit::Fit() : IFittingAlgorithm(), m_maxIterations() {}
38
41void Fit::initConcrete() {
42
44 getPointerToProperty("Ties")->setDocumentation("Math expressions defining ties between parameters of "
45 "the fitting function.");
46 declareProperty("Constraints", "", Kernel::Direction::Input);
47 getPointerToProperty("Constraints")->setDocumentation("List of constraints");
48 auto mustBePositive = std::make_shared<Kernel::BoundedValidator<int>>();
49 mustBePositive->setLower(0);
50 declareProperty("MaxIterations", 500, mustBePositive->clone(),
51 "Stop after this number of iterations if a good fit is not found");
52 declareProperty("OutputStatus", "", Kernel::Direction::Output);
53 getPointerToProperty("OutputStatus")->setDocumentation("Whether the fit was successful");
54 declareProperty("OutputChi2overDoF", 0.0, "Returns the goodness of the fit", Kernel::Direction::Output);
55
56 std::vector<std::string> minimizerOptions = API::FuncMinimizerFactory::Instance().getKeys();
57 Kernel::IValidator_sptr minimizerValidator = std::make_shared<Kernel::StartsWithValidator>(minimizerOptions);
58
59 declareProperty("Minimizer", "Levenberg-Marquardt", minimizerValidator, "Minimizer to use for fitting.");
60
61 std::vector<std::string> costFuncOptions = API::CostFunctionFactory::Instance().getKeys();
62 // select only CostFuncFitting variety
63 for (auto &costFuncOption : costFuncOptions) {
64 auto costFunc = std::dynamic_pointer_cast<CostFunctions::CostFuncFitting>(
65 API::CostFunctionFactory::Instance().create(costFuncOption));
66 if (!costFunc) {
67 costFuncOption = "";
68 }
69 }
70 Kernel::IValidator_sptr costFuncValidator = std::make_shared<Kernel::ListValidator<std::string>>(costFuncOptions);
71 declareProperty("CostFunction", "Least squares", costFuncValidator,
72 "The cost function to be used for the fit, default is Least squares", Kernel::Direction::InOut);
73 declareProperty("CreateOutput", false,
74 "Set to true to create output workspaces with the results of the fit"
75 "(default is false).");
76 declareProperty("Output", "",
77 "A base name for the output workspaces (if not "
78 "given default names will be created). The "
79 "default is to use the name of the original data workspace as prefix "
80 "followed by suffixes _Workspace, _Parameters, etc.");
81 declareProperty("CalcErrors", false,
82 "Set to true to calcuate errors when output isn't created "
83 "(default is false).");
84 declareProperty("OutputCompositeMembers", false,
85 "If true and CreateOutput is true then the value of each "
86 "member of a Composite Function is also output.");
87 declareProperty(std::make_unique<Kernel::PropertyWithValue<bool>>("ConvolveMembers", false),
88 "If true and OutputCompositeMembers is true members of any "
89 "Convolution are output convolved\n"
90 "with corresponding resolution");
91 declareProperty("OutputParametersOnly", false,
92 "Set to true to output only the parameters and not "
93 "workspace(s) with the calculated values\n"
94 "(default is false, ignored if CreateOutput is false and "
95 "Output is an empty string).");
96 declareProperty("CustomStepSizes", std::vector<double>{}, "Custom step sizes for numerical derivatives.");
97 setPropertySettings("CustomStepSizes",
98 std::make_unique<Kernel::EnabledWhenProperty>(
100}
101
102std::map<std::string, std::string> Fit::validateInputs() {
103 std::map<std::string, std::string> issues;
104
105 const auto &possibleOperators = Mantid::API::Expression::DEFAULT_OPS_STR;
106 std::string constraints = getPropertyValue("Constraints");
107 if (constraints.size() > 0) {
108 auto operatorPresent = false;
109 for (const auto &op : possibleOperators) {
110 const auto it = constraints.find_first_of(op);
111 if (it <= constraints.size()) {
112 operatorPresent = true;
113 break;
114 }
115 }
116 if (!operatorPresent) {
117 issues["Constraints"] = "No operator is present in the constraint.";
118 }
119 }
120
121 const std::string stepSizeMethod = getPropertyValue("StepSizeMethod");
122 const std::vector<double> customStepSizes = getProperty("CustomStepSizes");
123 if (stepSizeMethod == CUSTOM_STEP_SIZE && customStepSizes.empty()) {
124 issues["CustomStepSizes"] = "CustomStepSizes must be provided when StepSizeMethod is set to Custom.";
125 }
126 if (stepSizeMethod != CUSTOM_STEP_SIZE && !customStepSizes.empty()) {
127 issues["CustomStepSizes"] = "CustomStepSizes can only be provided when StepSizeMethod is set to Custom.";
128 }
129
130 return issues;
131}
132
134void Fit::readProperties() {
135 std::string ties = getPropertyValue("Ties");
136 if (!ties.empty()) {
137 m_function->addTies(ties);
138 }
139 std::string constraints = getPropertyValue("Constraints");
140 if (!constraints.empty()) {
141 m_function->addConstraints(constraints);
142 }
143 m_function->registerFunctionUsage(isChild());
144
145 // Try to retrieve optional properties
146 int intMaxIterations = getProperty("MaxIterations");
147 m_maxIterations = static_cast<size_t>(intMaxIterations);
148
149 const std::string stepSizeMethod = getPropertyValue("StepSizeMethod");
150 if (stepSizeMethod == CUSTOM_STEP_SIZE) {
151 const std::vector<double> customStepSizes = getProperty("CustomStepSizes");
152 const size_t nParams = m_function->nParams();
153 if (customStepSizes.size() != nParams) {
154 throw std::invalid_argument(
155 "The 'CustomStepSizes' list must be the same length as the number of parameters. The list should contain " +
156 std::to_string(nParams) + " values.\n");
157 }
158 m_function->setCustomStepSizes(customStepSizes);
159 for (size_t i = 0; i < nParams; i++) {
160 g_log.debug() << "The step size of " << m_function->parameterName(i) << " has been set to " << customStepSizes[i]
161 << "\n";
162 }
163 }
164}
165
168void Fit::initializeMinimizer(size_t maxIterations) {
169 const bool unrollComposites = getProperty("OutputCompositeMembers");
170 bool convolveMembers = existsProperty("ConvolveMembers");
171 if (convolveMembers) {
172 convolveMembers = getProperty("ConvolveMembers");
173 }
174 m_domainCreator->separateCompositeMembersInOutput(unrollComposites, convolveMembers);
176 std::string minimizerName = getPropertyValue("Minimizer");
177 m_minimizer = API::FuncMinimizerFactory::Instance().createMinimizer(minimizerName);
178 m_minimizer->initialize(m_costFunction, maxIterations);
180}
181
186void Fit::copyMinimizerOutput(const API::IFuncMinimizer &minimizer) {
187 const auto &properties = minimizer.getProperties();
188 for (auto property : properties) {
189 if ((*property).direction() == Kernel::Direction::Output && (*property).isValid().empty()) {
190 auto clonedProperty = std::unique_ptr<Kernel::Property>((*property).clone());
191 declareProperty(std::move(clonedProperty));
192 }
193 }
194}
195
198size_t Fit::runMinimizer() {
199 const int64_t nsteps = m_maxIterations * m_function->estimateNoProgressCalls();
200 auto prog = std::make_shared<API::Progress>(this, 0.0, 1.0, nsteps);
201 m_function->setProgressReporter(prog);
202
203 // do the fitting until success or iteration limit is reached
204 size_t iter = 0;
205 bool isFinished = false;
206 g_log.debug("Starting minimizer iteration\n");
207 while (iter < m_maxIterations) {
208 g_log.debug() << "Starting iteration " << iter << "\n";
209 try {
210 // Perform a single iteration. isFinished is set when minimizer wants to
211 // quit.
212 m_function->iterationStarting();
213 isFinished = !m_minimizer->iterate(iter);
214 m_function->iterationFinished();
216 // This is an attempt to recover after the function changes its number of
217 // parameters or ties during the iteration.
218 if (auto cf = dynamic_cast<API::CompositeFunction *>(m_function.get())) {
219 // Make sure the composite function is valid.
220 cf->checkFunction();
221 }
222 // Re-create the cost function and minimizer.
224 }
225
226 prog->report();
227 ++iter;
228 if (isFinished) {
229 // It was the last iteration. Break out of the loop and return the number
230 // of finished iterations.
231 break;
232 }
233 }
234 g_log.debug() << "Number of minimizer iterations=" << iter << "\n";
235 return iter;
236}
237
240void Fit::finalizeMinimizer(size_t nIterations) {
241 m_minimizer->finalize();
242
243 auto errorString = m_minimizer->getError();
244 g_log.debug() << "Iteration stopped. Minimizer status string=" << errorString << "\n";
245
246 if (nIterations >= m_maxIterations) {
247 if (!errorString.empty()) {
248 errorString += '\n';
249 }
250 errorString += "Failed to converge after " + std::to_string(m_maxIterations) + " iterations.";
251 }
252
253 if (errorString.empty()) {
254 errorString = API::MinimizerStatus::SUCCESS;
255 }
256
257 // return the status flag
258 setPropertyValue("OutputStatus", errorString);
259 if (!this->isChild()) {
260 auto &logStream = errorString == API::MinimizerStatus::SUCCESS ? g_log.notice() : g_log.warning();
261 logStream << "Fit status: " << errorString << '\n';
262 logStream << "Stopped after " << nIterations << " iterations" << '\n';
263 }
264}
265
267void Fit::createOutput() {
268
269 // degrees of freedom
270 size_t dof = m_costFunction->getDomain()->size() - m_costFunction->nParams();
271 if (dof == 0)
272 dof = 1;
273 double rawcostfuncval = m_minimizer->costFunctionVal();
274 double finalCostFuncVal = rawcostfuncval / double(dof);
275
276 setProperty("OutputChi2overDoF", finalCostFuncVal);
277
278 bool doCreateOutput = getProperty("CreateOutput");
279 std::string baseName = getPropertyValue("Output");
280 if (!baseName.empty()) {
281 doCreateOutput = true;
282 }
283 bool doCalcErrors = getProperty("CalcErrors");
284 if (doCreateOutput) {
285 doCalcErrors = true;
286 }
287 if (m_costFunction->nParams() == 0) {
288 doCalcErrors = false;
289 }
290
291 EigenMatrix covar;
292 if (doCalcErrors) {
293 // Calculate the covariance matrix and the errors.
294 m_costFunction->calCovarianceMatrix(covar);
295 m_costFunction->calFittingErrors(covar, rawcostfuncval);
296 }
297
298 if (doCreateOutput) {
300
301 // get the workspace
302 API::Workspace_const_sptr ws = getProperty("InputWorkspace");
303
304 if (baseName.empty()) {
305 baseName = ws->getName();
306 if (baseName.empty()) {
307 baseName = "Output";
308 }
309 }
310 baseName += "_";
311
312 declareProperty(std::make_unique<API::WorkspaceProperty<API::ITableWorkspace>>("OutputNormalisedCovarianceMatrix",
314 "The name of the TableWorkspace in which to store the final covariance "
315 "matrix");
316 setPropertyValue("OutputNormalisedCovarianceMatrix", baseName + "NormalisedCovarianceMatrix");
317
319 Mantid::API::WorkspaceFactory::Instance().createTable("TableWorkspace");
320 covariance->addColumn("str", "Name");
321 // set plot type to Label = 6
322 covariance->getColumn(covariance->columnCount() - 1)->setPlotType(6);
323 for (size_t i = 0; i < m_function->nParams(); i++) {
324 if (m_function->isActive(i)) {
325 covariance->addColumn("double", m_function->parameterName(i));
326 }
327 }
328
329 size_t nParams = m_function->nParams();
330 size_t ia = 0;
331 for (size_t i = 0; i < nParams; i++) {
332 if (!m_function->isActive(i))
333 continue;
334 Mantid::API::TableRow row = covariance->appendRow();
335 row << m_function->parameterName(i);
336 size_t ja = 0;
337 for (size_t j = 0; j < nParams; j++) {
338 if (!m_function->isActive(j))
339 continue;
340 if (j == i)
341 row << 100.0;
342 else {
343 if (!covar.inspector().data()) {
344 throw std::runtime_error("There was an error while allocating the covariance "
345 "matrix "
346 "which is needed to produce fitting error results.");
347 }
348 row << 100.0 * covar.get(ia, ja) / sqrt(covar.get(ia, ia) * covar.get(ja, ja));
349 }
350 ++ja;
351
352 if (ja >= covar.size2())
353 break;
354 }
355 ++ia;
356
357 if (ia >= covar.size1())
358 break;
359 }
360
361 setProperty("OutputNormalisedCovarianceMatrix", covariance);
362
363 // create output parameter table workspace to store final fit parameters
364 // including error estimates if derivative of fitting function defined
365
366 declareProperty(std::make_unique<API::WorkspaceProperty<API::ITableWorkspace>>("OutputParameters", "",
368 "The name of the TableWorkspace in which to store the "
369 "final fit parameters");
370
371 setPropertyValue("OutputParameters", baseName + "Parameters");
372
373 Mantid::API::ITableWorkspace_sptr result = Mantid::API::WorkspaceFactory::Instance().createTable("TableWorkspace");
374 result->addColumn("str", "Name");
375 // set plot type to Label = 6
376 result->getColumn(result->columnCount() - 1)->setPlotType(6);
377 result->addColumn("double", "Value");
378 result->addColumn("double", "Error");
379 // yErr = 5
380 result->getColumn(result->columnCount() - 1)->setPlotType(5);
381
382 for (size_t i = 0; i < m_function->nParams(); i++) {
383 Mantid::API::TableRow row = result->appendRow();
384 row << m_function->parameterName(i) << m_function->getParameter(i) << m_function->getError(i);
385 }
386 // Add chi-squared value at the end of parameter table
387 Mantid::API::TableRow row = result->appendRow();
388
389 std::string costfuncname = getPropertyValue("CostFunction");
390 if (costfuncname == "Rwp")
391 row << "Cost function value" << rawcostfuncval;
392 else
393 row << "Cost function value" << finalCostFuncVal;
394
395 setProperty("OutputParameters", result);
396 bool outputParametersOnly = getProperty("OutputParametersOnly");
397
398 if (!outputParametersOnly) {
399 m_domainCreator->createOutputWorkspace(baseName, m_function, m_costFunction->getDomain(),
400 m_costFunction->getValues());
401 }
402 }
403}
404
405/*
406Register usage of the minimizer and cost function with the UsageService
407*/
408void Fit::registerMinimizerAndCostFuncUsage() {
409 std::stringstream ss;
410 ss << m_minimizer->name() << " Minimizer";
411 Kernel::UsageService::Instance().registerFeatureUsage(Kernel::FeatureType::Function, ss.str(), false);
412 ss.str("");
413 ss << m_costFunction->name() << " Cost Function";
414 Kernel::UsageService::Instance().registerFeatureUsage(Kernel::FeatureType::Function, ss.str(), false);
415}
416
421void Fit::execConcrete() {
422
423 // Read Fit's own properties
425
426 // Get the minimizer
428
429 // Run the minimizer
430 auto nIterations = runMinimizer();
431
432 // Finilize the minimizer.
433 finalizeMinimizer(nIterations);
434
435 // fit ended, creating output
436 createOutput();
437
438 progress(1.0);
439}
440
441} // namespace Mantid::CurveFitting::Algorithms
#define DECLARE_ALGORITHM(classname)
Definition Algorithm.h:542
void declareProperty(std::unique_ptr< Kernel::Property > p, const std::string &doc="") override
Add a property to the list of managed properties.
Kernel::Property * getPointerToProperty(const std::string &name) const override
Get a property by name.
bool existsProperty(const std::string &name) const override
Checks whether the named property is already in the list of managed property.
std::string getPropertyValue(const std::string &name) const override
Get the value of a property as a string.
TypedValue getProperty(const std::string &name) const override
Get the value of a property.
bool isChild() const override
To query whether algorithm is a child.
Kernel::Logger & g_log
Definition Algorithm.h:423
void progress(double p, const std::string &msg="", double estimatedTime=0.0, int progressPrecision=0)
Sends ProgressNotification.
void setPropertyValue(const std::string &name, const std::string &value) override
Set the value of a property by string N.B.
A composite function is a function containing other functions.
static const std::vector< std::string > DEFAULT_OPS_STR
Definition Expression.h:119
An interface for function minimizers.
TableRow represents a row in a TableWorkspace.
Definition TableRow.h:39
A property class for workspaces.
A generic fitting algorithm.
Definition Fit.h:78
void finalizeMinimizer(size_t nIterations)
Finalize the minimizer.
Definition Fit.cpp:240
std::shared_ptr< CostFunctions::CostFuncFitting > m_costFunction
The cost function.
Definition Fit.h:104
void copyMinimizerOutput(const API::IFuncMinimizer &minimizer)
Copy all output workspace properties from the minimizer to Fit algorithm.
Definition Fit.cpp:186
size_t m_maxIterations
Max number of iterations.
Definition Fit.h:108
size_t runMinimizer()
Run the minimizer's iteration loop.
Definition Fit.cpp:198
void createOutput()
Create algorithm output workspaces.
Definition Fit.cpp:267
std::shared_ptr< API::IFuncMinimizer > m_minimizer
The minimizer.
Definition Fit.h:106
void readProperties()
Read in the properties specific to Fit.
Definition Fit.cpp:134
void initializeMinimizer(size_t maxIterations)
Initialize the minimizer for this fit.
Definition Fit.cpp:168
A wrapper around Eigen::Matrix.
Definition EigenMatrix.h:33
double get(size_t i, size_t j) const
Get an element.
size_t size1() const
First size of the matrix.
size_t size2() const
Second size of the matrix.
const map_type inspector() const
Get a const copy of the Eigen matrix.
Definition EigenMatrix.h:58
A base class for fitting algorithms.
std::shared_ptr< API::IDomainCreator > m_domainCreator
Pointer to a domain creator.
std::shared_ptr< CostFunctions::CostFuncFitting > getCostFunctionInitialized() const
Create a cost function from the "CostFunction" property and make it ready for evaluation.
std::shared_ptr< API::IFunction > m_function
Pointer to the fitting function.
Exception thrown when a fitting function changes number of parameters during fit.
Definition Exception.h:336
IPropertyManager * setProperty(const std::string &name, const T &value)
Templated method to set the value of a PropertyWithValue.
void setPropertySettings(const std::string &name, std::unique_ptr< IPropertySettings const > settings)
Add a PropertySettings instance to the chain of settings for a given property.
void debug(const std::string &msg)
Logs at debug level.
Definition Logger.cpp:145
void notice(const std::string &msg)
Logs at notice level.
Definition Logger.cpp:126
void warning(const std::string &msg)
Logs at warning level.
Definition Logger.cpp:117
const std::vector< Property * > & getProperties() const override
Get the list of managed properties.
The concrete, templated class for properties.
void setDocumentation(const std::string &documentation)
Sets the user level description of the property.
Definition Property.cpp:149
static T & Instance()
Return a reference to the Singleton instance, creating it if it does not already exist Creation is do...
const std::string SUCCESS
Reported when a minimizer has fully converged.
std::shared_ptr< ITableWorkspace > ITableWorkspace_sptr
shared pointer to Mantid::API::ITableWorkspace
std::shared_ptr< const Workspace > Workspace_const_sptr
shared pointer to Mantid::API::Workspace (const version)
const std::string CUSTOM_STEP_SIZE
std::unique_ptr< T > create(const P &parent, const IndexArg &indexArg, const HistArg &histArg)
This is the create() method that all the other create() methods call.
std::shared_ptr< IValidator > IValidator_sptr
A shared_ptr to an IValidator.
Definition IValidator.h:26
std::string to_string(const wide_integer< Bits, Signed > &n)
@ InOut
Both an input & output workspace.
Definition Property.h:55
@ Input
An input workspace.
Definition Property.h:53
@ Output
An output workspace.
Definition Property.h:54