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//----------------------------------------------------------------------
12
19#include "MantidAPI/TableRow.h"
21
26
27#include <memory>
28
30
31// Register the class into the algorithm factory
33
34
35Fit::Fit() : IFittingAlgorithm(), m_maxIterations() {}
36
39void Fit::initConcrete() {
40
42 getPointerToProperty("Ties")->setDocumentation("Math expressions defining ties between parameters of "
43 "the fitting function.");
44 declareProperty("Constraints", "", Kernel::Direction::Input);
45 getPointerToProperty("Constraints")->setDocumentation("List of constraints");
46 auto mustBePositive = std::make_shared<Kernel::BoundedValidator<int>>();
47 mustBePositive->setLower(0);
48 declareProperty("MaxIterations", 500, mustBePositive->clone(),
49 "Stop after this number of iterations if a good fit is not found");
50 declareProperty("OutputStatus", "", Kernel::Direction::Output);
51 getPointerToProperty("OutputStatus")->setDocumentation("Whether the fit was successful");
52 declareProperty("OutputChi2overDoF", 0.0, "Returns the goodness of the fit", Kernel::Direction::Output);
53
54 std::vector<std::string> minimizerOptions = API::FuncMinimizerFactory::Instance().getKeys();
55 Kernel::IValidator_sptr minimizerValidator = std::make_shared<Kernel::StartsWithValidator>(minimizerOptions);
56
57 declareProperty("Minimizer", "Levenberg-Marquardt", minimizerValidator, "Minimizer to use for fitting.");
58
59 std::vector<std::string> costFuncOptions = API::CostFunctionFactory::Instance().getKeys();
60 // select only CostFuncFitting variety
61 for (auto &costFuncOption : costFuncOptions) {
62 auto costFunc = std::dynamic_pointer_cast<CostFunctions::CostFuncFitting>(
63 API::CostFunctionFactory::Instance().create(costFuncOption));
64 if (!costFunc) {
65 costFuncOption = "";
66 }
67 }
68 Kernel::IValidator_sptr costFuncValidator = std::make_shared<Kernel::ListValidator<std::string>>(costFuncOptions);
69 declareProperty("CostFunction", "Least squares", costFuncValidator,
70 "The cost function to be used for the fit, default is Least squares", Kernel::Direction::InOut);
71 declareProperty("CreateOutput", false,
72 "Set to true to create output workspaces with the results of the fit"
73 "(default is false).");
74 declareProperty("Output", "",
75 "A base name for the output workspaces (if not "
76 "given default names will be created). The "
77 "default is to use the name of the original data workspace as prefix "
78 "followed by suffixes _Workspace, _Parameters, etc.");
79 declareProperty("CalcErrors", false,
80 "Set to true to calcuate errors when output isn't created "
81 "(default is false).");
82 declareProperty("OutputCompositeMembers", false,
83 "If true and CreateOutput is true then the value of each "
84 "member of a Composite Function is also output.");
85 declareProperty(std::make_unique<Kernel::PropertyWithValue<bool>>("ConvolveMembers", false),
86 "If true and OutputCompositeMembers is true members of any "
87 "Convolution are output convolved\n"
88 "with corresponding resolution");
89 declareProperty("OutputParametersOnly", false,
90 "Set to true to output only the parameters and not "
91 "workspace(s) with the calculated values\n"
92 "(default is false, ignored if CreateOutput is false and "
93 "Output is an empty string).");
94}
95
96std::map<std::string, std::string> Fit::validateInputs() {
97 std::map<std::string, std::string> issues;
98
99 const auto &possibleOperators = Mantid::API::Expression::DEFAULT_OPS_STR;
100 std::string constraints = getPropertyValue("Constraints");
101 if (constraints.size() > 0) {
102 auto operatorPresent = false;
103 for (const auto &op : possibleOperators) {
104 const auto it = constraints.find_first_of(op);
105 if (it <= constraints.size()) {
106 operatorPresent = true;
107 break;
108 }
109 }
110 if (!operatorPresent) {
111 issues["Constraints"] = "No operator is present in the constraint.";
112 }
113 }
114
115 return issues;
116}
117
119void Fit::readProperties() {
120 std::string ties = getPropertyValue("Ties");
121 if (!ties.empty()) {
122 m_function->addTies(ties);
123 }
124 std::string constraints = getPropertyValue("Constraints");
125 if (!constraints.empty()) {
126 m_function->addConstraints(constraints);
127 }
128 m_function->registerFunctionUsage(isChild());
129
130 // Try to retrieve optional properties
131 int intMaxIterations = getProperty("MaxIterations");
132 m_maxIterations = static_cast<size_t>(intMaxIterations);
133}
134
137void Fit::initializeMinimizer(size_t maxIterations) {
138 const bool unrollComposites = getProperty("OutputCompositeMembers");
139 bool convolveMembers = existsProperty("ConvolveMembers");
140 if (convolveMembers) {
141 convolveMembers = getProperty("ConvolveMembers");
142 }
143 m_domainCreator->separateCompositeMembersInOutput(unrollComposites, convolveMembers);
145 std::string minimizerName = getPropertyValue("Minimizer");
146 m_minimizer = API::FuncMinimizerFactory::Instance().createMinimizer(minimizerName);
147 m_minimizer->initialize(m_costFunction, maxIterations);
149}
150
155void Fit::copyMinimizerOutput(const API::IFuncMinimizer &minimizer) {
156 const auto &properties = minimizer.getProperties();
157 for (auto property : properties) {
158 if ((*property).direction() == Kernel::Direction::Output && (*property).isValid().empty()) {
159 auto clonedProperty = std::unique_ptr<Kernel::Property>((*property).clone());
160 declareProperty(std::move(clonedProperty));
161 }
162 }
163}
164
167size_t Fit::runMinimizer() {
168 const int64_t nsteps = m_maxIterations * m_function->estimateNoProgressCalls();
169 auto prog = std::make_shared<API::Progress>(this, 0.0, 1.0, nsteps);
170 m_function->setProgressReporter(prog);
171
172 // do the fitting until success or iteration limit is reached
173 size_t iter = 0;
174 bool isFinished = false;
175 g_log.debug("Starting minimizer iteration\n");
176 while (iter < m_maxIterations) {
177 g_log.debug() << "Starting iteration " << iter << "\n";
178 try {
179 // Perform a single iteration. isFinished is set when minimizer wants to
180 // quit.
181 m_function->iterationStarting();
182 isFinished = !m_minimizer->iterate(iter);
183 m_function->iterationFinished();
185 // This is an attempt to recover after the function changes its number of
186 // parameters or ties during the iteration.
187 if (auto cf = dynamic_cast<API::CompositeFunction *>(m_function.get())) {
188 // Make sure the composite function is valid.
189 cf->checkFunction();
190 }
191 // Re-create the cost function and minimizer.
193 }
194
195 prog->report();
196 ++iter;
197 if (isFinished) {
198 // It was the last iteration. Break out of the loop and return the number
199 // of finished iterations.
200 break;
201 }
202 }
203 g_log.debug() << "Number of minimizer iterations=" << iter << "\n";
204 return iter;
205}
206
209void Fit::finalizeMinimizer(size_t nIterations) {
210 m_minimizer->finalize();
211
212 auto errorString = m_minimizer->getError();
213 g_log.debug() << "Iteration stopped. Minimizer status string=" << errorString << "\n";
214
215 if (nIterations >= m_maxIterations) {
216 if (!errorString.empty()) {
217 errorString += '\n';
218 }
219 errorString += "Failed to converge after " + std::to_string(m_maxIterations) + " iterations.";
220 }
221
222 if (errorString.empty()) {
223 errorString = "success";
224 }
225
226 // return the status flag
227 setPropertyValue("OutputStatus", errorString);
228 if (!this->isChild()) {
229 auto &logStream = errorString == "success" ? g_log.notice() : g_log.warning();
230 logStream << "Fit status: " << errorString << '\n';
231 logStream << "Stopped after " << nIterations << " iterations" << '\n';
232 }
233}
234
236void Fit::createOutput() {
237
238 // degrees of freedom
239 size_t dof = m_costFunction->getDomain()->size() - m_costFunction->nParams();
240 if (dof == 0)
241 dof = 1;
242 double rawcostfuncval = m_minimizer->costFunctionVal();
243 double finalCostFuncVal = rawcostfuncval / double(dof);
244
245 setProperty("OutputChi2overDoF", finalCostFuncVal);
246
247 bool doCreateOutput = getProperty("CreateOutput");
248 std::string baseName = getPropertyValue("Output");
249 if (!baseName.empty()) {
250 doCreateOutput = true;
251 }
252 bool doCalcErrors = getProperty("CalcErrors");
253 if (doCreateOutput) {
254 doCalcErrors = true;
255 }
256 if (m_costFunction->nParams() == 0) {
257 doCalcErrors = false;
258 }
259
260 EigenMatrix covar;
261 if (doCalcErrors) {
262 // Calculate the covariance matrix and the errors.
263 m_costFunction->calCovarianceMatrix(covar);
264 m_costFunction->calFittingErrors(covar, rawcostfuncval);
265 }
266
267 if (doCreateOutput) {
269
270 // get the workspace
271 API::Workspace_const_sptr ws = getProperty("InputWorkspace");
272
273 if (baseName.empty()) {
274 baseName = ws->getName();
275 if (baseName.empty()) {
276 baseName = "Output";
277 }
278 }
279 baseName += "_";
280
281 declareProperty(std::make_unique<API::WorkspaceProperty<API::ITableWorkspace>>("OutputNormalisedCovarianceMatrix",
283 "The name of the TableWorkspace in which to store the final covariance "
284 "matrix");
285 setPropertyValue("OutputNormalisedCovarianceMatrix", baseName + "NormalisedCovarianceMatrix");
286
288 Mantid::API::WorkspaceFactory::Instance().createTable("TableWorkspace");
289 covariance->addColumn("str", "Name");
290 // set plot type to Label = 6
291 covariance->getColumn(covariance->columnCount() - 1)->setPlotType(6);
292 for (size_t i = 0; i < m_function->nParams(); i++) {
293 if (m_function->isActive(i)) {
294 covariance->addColumn("double", m_function->parameterName(i));
295 }
296 }
297
298 size_t np = m_function->nParams();
299 size_t ia = 0;
300 for (size_t i = 0; i < np; i++) {
301 if (!m_function->isActive(i))
302 continue;
303 Mantid::API::TableRow row = covariance->appendRow();
304 row << m_function->parameterName(i);
305 size_t ja = 0;
306 for (size_t j = 0; j < np; j++) {
307 if (!m_function->isActive(j))
308 continue;
309 if (j == i)
310 row << 100.0;
311 else {
312 if (!covar.inspector().data()) {
313 throw std::runtime_error("There was an error while allocating the covariance "
314 "matrix "
315 "which is needed to produce fitting error results.");
316 }
317 row << 100.0 * covar.get(ia, ja) / sqrt(covar.get(ia, ia) * covar.get(ja, ja));
318 }
319 ++ja;
320
321 if (ja >= covar.size2())
322 break;
323 }
324 ++ia;
325
326 if (ia >= covar.size1())
327 break;
328 }
329
330 setProperty("OutputNormalisedCovarianceMatrix", covariance);
331
332 // create output parameter table workspace to store final fit parameters
333 // including error estimates if derivative of fitting function defined
334
335 declareProperty(std::make_unique<API::WorkspaceProperty<API::ITableWorkspace>>("OutputParameters", "",
337 "The name of the TableWorkspace in which to store the "
338 "final fit parameters");
339
340 setPropertyValue("OutputParameters", baseName + "Parameters");
341
342 Mantid::API::ITableWorkspace_sptr result = Mantid::API::WorkspaceFactory::Instance().createTable("TableWorkspace");
343 result->addColumn("str", "Name");
344 // set plot type to Label = 6
345 result->getColumn(result->columnCount() - 1)->setPlotType(6);
346 result->addColumn("double", "Value");
347 result->addColumn("double", "Error");
348 // yErr = 5
349 result->getColumn(result->columnCount() - 1)->setPlotType(5);
350
351 for (size_t i = 0; i < m_function->nParams(); i++) {
352 Mantid::API::TableRow row = result->appendRow();
353 row << m_function->parameterName(i) << m_function->getParameter(i) << m_function->getError(i);
354 }
355 // Add chi-squared value at the end of parameter table
356 Mantid::API::TableRow row = result->appendRow();
357
358 std::string costfuncname = getPropertyValue("CostFunction");
359 if (costfuncname == "Rwp")
360 row << "Cost function value" << rawcostfuncval;
361 else
362 row << "Cost function value" << finalCostFuncVal;
363
364 setProperty("OutputParameters", result);
365 bool outputParametersOnly = getProperty("OutputParametersOnly");
366
367 if (!outputParametersOnly) {
368 m_domainCreator->createOutputWorkspace(baseName, m_function, m_costFunction->getDomain(),
369 m_costFunction->getValues());
370 }
371 }
372}
373
374/*
375Register usage of the minimizer and cost function with the UsageService
376*/
377void Fit::registerMinimizerAndCostFuncUsage() {
378 std::stringstream ss;
379 ss << m_minimizer->name() << " Minimizer";
380 Kernel::UsageService::Instance().registerFeatureUsage(Kernel::FeatureType::Function, ss.str(), false);
381 ss.str("");
382 ss << m_costFunction->name() << " Cost Function";
383 Kernel::UsageService::Instance().registerFeatureUsage(Kernel::FeatureType::Function, ss.str(), false);
384}
385
390void Fit::execConcrete() {
391
392 // Read Fit's own properties
394
395 // Get the minimizer
397
398 // Run the minimizer
399 auto nIterations = runMinimizer();
400
401 // Finilize the minimizer.
402 finalizeMinimizer(nIterations);
403
404 // fit ended, creating output
405 createOutput();
406
407 progress(1.0);
408}
409
410} // namespace Mantid::CurveFitting::Algorithms
#define DECLARE_ALGORITHM(classname)
Definition Algorithm.h:538
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:422
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:209
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:155
size_t m_maxIterations
Max number of iterations.
Definition Fit.h:108
size_t runMinimizer()
Run the minimizer's iteration loop.
Definition Fit.cpp:167
void createOutput()
Create algorithm output workspaces.
Definition Fit.cpp:236
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:119
void initializeMinimizer(size_t maxIterations)
Initialize the minimizer for this fit.
Definition Fit.cpp:137
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 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:146
static T & Instance()
Return a reference to the Singleton instance, creating it if it does not already exist Creation is do...
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)
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