Mantid
Loading...
Searching...
No Matches
MultiDomainFunction.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//----------------------------------------------------------------------
14
15#include <algorithm>
16#include <boost/lexical_cast.hpp>
17#include <set>
18
19namespace Mantid::API {
20
21DECLARE_FUNCTION(MultiDomainFunction)
22
23MultiDomainFunction::MultiDomainFunction() : m_nDomains(0), m_maxIndex(0) { setAttributeValue("NumDeriv", true); }
24
31void MultiDomainFunction::setDomainIndex(size_t funIndex, size_t domainIndex) {
32 m_domains[funIndex] = std::vector<size_t>(1, domainIndex);
34}
35
44void MultiDomainFunction::setDomainIndices(size_t funIndex, const std::vector<size_t> &domainIndices) {
45 m_domains[funIndex] = domainIndices;
47}
48
53 std::set<size_t> dSet;
54 for (auto &domain : m_domains) {
55 if (!domain.second.empty()) {
56 dSet.insert(domain.second.begin(), domain.second.end());
57 }
58 }
59 m_nDomains = dSet.size();
60 m_maxIndex = dSet.empty() ? 0 : *dSet.rbegin();
61}
62
67 m_valueOffsets.clear();
68 m_valueOffsets.emplace_back(0);
69 for (size_t i = 0; i < domain.getNParts(); ++i) {
70 const FunctionDomain &d = domain.getDomain(i);
71 m_valueOffsets.emplace_back(m_valueOffsets.back() + d.size());
72 }
73}
74
80
87void MultiDomainFunction::getDomainIndices(size_t funIndex, size_t nDomains, std::vector<size_t> &domains) const {
88 auto it = m_domains.find(funIndex);
89 if (it == m_domains.end()) { // apply to all domains
90 domains.resize(nDomains);
91 for (size_t i = 0; i < domains.size(); ++i) {
92 domains[i] = i;
93 }
94 } else { // apply to selected domains
95 domains.assign(it->second.begin(), it->second.end());
96 }
97}
98
101
107 // works only on CompositeDomain
108 if (!dynamic_cast<const CompositeDomain *>(&domain)) {
109 // make exception if a single member function is defined
110 if (m_maxIndex == 0 && nFunctions() == 1) {
111 getFunction(0)->function(domain, values);
112 return;
113 }
114 throw std::invalid_argument("Non-CompositeDomain passed to MultiDomainFunction.");
115 }
116 const auto &cd = dynamic_cast<const CompositeDomain &>(domain);
117 // domain must not have less parts than m_maxIndex
118 if (cd.getNParts() <= m_maxIndex) {
119 throw std::invalid_argument("CompositeDomain has too few parts (" + std::to_string(cd.getNParts()) +
120 ") for MultiDomainFunction (max index " + std::to_string(m_maxIndex) + ").");
121 }
122 // domain and values must be consistent
123 if (cd.size() != values.size()) {
124 throw std::invalid_argument("MultiDomainFunction: domain and values have different sizes.");
125 }
126
128 // evaluate member functions
129 values.zeroCalculated();
130 for (size_t iFun = 0; iFun < nFunctions(); ++iFun) {
131 // find the domains member function must be applied to
132 std::vector<size_t> domains;
133 getDomainIndices(iFun, cd.getNParts(), domains);
134
135 for (auto const &dom : domains) {
136 const FunctionDomain &d = cd.getDomain(dom);
138 getFunction(iFun)->function(d, tmp);
139 values.addToCalculated(m_valueOffsets[dom], tmp);
140 }
141 }
142}
143
146 // works only on CompositeDomain
147 if (!dynamic_cast<const CompositeDomain *>(&domain)) {
148 // make exception if a single member function is defined
149 if (m_maxIndex == 0 && nFunctions() == 1) {
150 getFunction(0)->functionDeriv(domain, jacobian);
151 return;
152 }
153 throw std::invalid_argument("Non-CompositeDomain passed to MultiDomainFunction.");
154 }
155
156 if (getAttribute("NumDeriv").asBool()) {
157 calNumericalDeriv(domain, jacobian);
158 } else {
159 const auto &cd = dynamic_cast<const CompositeDomain &>(domain);
160 // domain must not have less parts than m_maxIndex
161 if (cd.getNParts() < m_maxIndex) {
162 throw std::invalid_argument("CompositeDomain has too few parts (" + std::to_string(cd.getNParts()) +
163 ") for MultiDomainFunction (max index " + std::to_string(m_maxIndex) + ").");
164 }
165
166 jacobian.zero();
168 // evaluate member functions derivatives
169 for (size_t iFun = 0; iFun < nFunctions(); ++iFun) {
170 // find the domains member function must be applied to
171 std::vector<size_t> domains;
172 getDomainIndices(iFun, cd.getNParts(), domains);
173
174 for (auto const &dom : domains) {
175 const FunctionDomain &d = cd.getDomain(dom);
176 PartialJacobian J(&jacobian, m_valueOffsets[dom], paramOffset(iFun));
177 getFunction(iFun)->functionDeriv(d, J);
178 }
179 }
180 }
181}
182
188 for (size_t iFun = 0; iFun < nFunctions(); ++iFun) {
189 getFunction(iFun)->iterationStarting();
190 }
191}
192
197 for (size_t iFun = 0; iFun < nFunctions(); ++iFun) {
198 getFunction(iFun)->iterationFinished();
199 }
200}
201
203IFunction::Attribute MultiDomainFunction::getLocalAttribute(size_t funIndex, const std::string &attName) const {
204 if (attName != "domains") {
205 throw std::invalid_argument("MultiDomainFunction does not have attribute " + attName);
206 }
207 if (funIndex >= nFunctions()) {
208 throw std::out_of_range("Function index is out of range.");
209 }
210 try {
211 auto it = m_domains.at(funIndex);
212 if (it.size() == 1 && it.front() == funIndex) {
213 return IFunction::Attribute("i");
214 } else if (!it.empty()) {
215 auto out = std::to_string(it.front());
216 for (auto i = it.begin() + 1; i != it.end(); ++i) {
217 out += "," + std::to_string(*i);
218 }
219 return IFunction::Attribute(out);
220 }
221 } catch (const std::out_of_range &) {
222 return IFunction::Attribute("All");
223 }
224 return IFunction::Attribute("");
225}
226
255void MultiDomainFunction::setLocalAttribute(size_t funIndex, const std::string &attName,
256 const IFunction::Attribute &att) {
257 if (attName != "domains") {
258 throw std::invalid_argument("MultiDomainFunction does not have attribute " + attName);
259 }
260 if (funIndex >= nFunctions()) {
261 throw std::out_of_range("Function index is out of range.");
262 }
263 std::string value = att.asString();
264 auto it = m_domains.find(funIndex);
265
266 if (value == "All") { // fit to all domains
267 if (it != m_domains.end()) {
268 m_domains.erase(it);
269 }
270 return;
271 } else if (value == "i") { // fit to domain with the same index as the function
272 setDomainIndex(funIndex, funIndex);
273 return;
274 } else if (value.empty()) { // do not fit to any domain
275 setDomainIndices(funIndex, std::vector<size_t>());
276 return;
277 }
278
279 // fit to a selection of domains
280 std::vector<size_t> indx;
281 Expression list;
282 list.parse(value);
283 if (list.name() == "+") {
284 if (list.size() != 2 || list.terms()[1].operator_name() != "-") {
285 throw std::runtime_error("MultiDomainFunction: attribute \"domains\" "
286 "expects two integers separated by a \"-\"");
287 }
288 // value looks like "a - b". a and b must be ints and define a range of
289 // domain indices
290 auto start = boost::lexical_cast<size_t>(list.terms()[0].str());
291 size_t end = boost::lexical_cast<size_t>(list.terms()[1].str()) + 1;
292 if (start >= end) {
293 throw std::runtime_error("MultiDomainFunction: attribute \"domains\": wrong range limits.");
294 }
295 indx.resize(end - start);
296 for (size_t i = start; i < end; ++i) {
297 indx[i - start] = i;
298 }
299 } else {
300 // value must be either an int or a list of ints: "a,b,c,..."
301 list.toList();
302 indx.reserve(list.size());
303 std::transform(list.begin(), list.end(), std::back_inserter(indx),
304 [](const Mantid::API::Expression &k) { return boost::lexical_cast<size_t>(k.name()); });
305 }
306 setDomainIndices(funIndex, indx);
307}
308
315std::vector<IFunction_sptr> MultiDomainFunction::createEquivalentFunctions() const {
316 size_t nDomains = m_maxIndex + 1;
317 std::vector<CompositeFunction_sptr> compositeFunctions(nDomains);
318 for (size_t iFun = 0; iFun < nFunctions(); ++iFun) {
319 // find the domains member function must be applied to
320 std::vector<size_t> domains;
321 getDomainIndices(iFun, nDomains, domains);
322
323 for (auto domainIndex : domains) {
324 CompositeFunction_sptr cf = compositeFunctions[domainIndex];
325 if (!cf) {
326 // create a composite function for each domain
328 compositeFunctions[domainIndex] = cf;
329 }
330 // add copies of all functions applied to j-th domain to a single
331 // compositefunction
332 cf->addFunction(FunctionFactory::Instance().createInitialized(getFunction(iFun)->asString()));
333 }
334 }
335 std::vector<IFunction_sptr> outFunctions(nDomains);
336 // fill in the output vector
337 // check functions containing a single member and take it out of the composite
338 for (size_t i = 0; i < compositeFunctions.size(); ++i) {
339 auto fun = compositeFunctions[i];
340 if (!fun || fun->nFunctions() == 0) {
341 throw std::runtime_error("There is no function for domain " + std::to_string(i));
342 }
343 if (fun->nFunctions() > 1) {
344 outFunctions[i] = fun;
345 } else {
346 outFunctions[i] = fun->getFunction(0);
347 }
348 }
349 return outFunctions;
350}
351
352} // namespace Mantid::API
gsl_vector * tmp
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.
Base class for a composite domain.
virtual const FunctionDomain & getDomain(size_t i) const =0
Return i-th domain.
virtual size_t getNParts() const =0
Return the number of parts in the domain.
size_t paramOffset(size_t i) const
std::size_t nFunctions() const override
Number of functions.
Attribute getAttribute(const std::string &name) const override
Return a value of attribute attName.
CompositeFunction()
Default constructor.
IFunction_sptr getFunction(std::size_t i) const override
Returns the pointer to i-th function.
This class represents an expression made up of names, binary operators and brackets.
Definition Expression.h:36
const std::vector< Expression > & terms() const
Returns the top level terms of the expression (function arguments).
Definition Expression.h:77
void parse(const std::string &str)
Parse a string and create an expression.
const std::string & name() const
Returns the name of the expression which is a function or variable name.
Definition Expression.h:71
iterator end() const
An iterator pointing to the end of the expressions.
Definition Expression.h:86
iterator begin() const
An iterator pointing to the start of the expressions.
Definition Expression.h:84
void toList(const std::string &sep=",")
Make sure the expression is a list of expression separated by sep, eg "term1,term2,...
size_t size() const
Returns the number of argumens.
Definition Expression.h:79
Base class that represents the domain of a function.
A class to store values calculated by a function.
void addToCalculated(size_t i, double value)
Add a number to a calculated value.
void zeroCalculated()
Set all calculated values to zero.
size_t size() const
Return the number of values.
Attribute is a non-fitting parameter.
Definition IFunction.h:285
std::string asString() const
Returns string value if attribute is a string, throws exception otherwise.
std::string asString() const
Writes itself into a string.
void calNumericalDeriv(const FunctionDomain &domain, Jacobian &jacobian)
Calculate numerical derivatives.
Represents the Jacobian in IFitFunction::functionDeriv.
Definition Jacobian.h:22
virtual void zero()=0
Zero all matrix elements.
A composite function defined on a CompositeDomain.
size_t getMaxIndex() const
Get the largest domain index.
void setLocalAttribute(size_t funIndex, const std::string &attName, const Attribute &) override
Set a value to attribute attName.
Attribute getLocalAttribute(size_t funIndex, const std::string &attName) const override
Return a value of attribute attName.
void countValueOffsets(const CompositeDomain &domain) const
Count value offsets for each member domain in a CompositeDomain.
void countNumberOfDomains()
Counts number of the domains.
void iterationFinished() override
Called at the end of an iteration.
void functionDeriv(const FunctionDomain &domain, Jacobian &jacobian) override
Derivatives of function with respect to active parameters.
std::map< size_t, std::vector< size_t > > m_domains
Domain index map: finction -> domain.
void getDomainIndices(size_t funIndex, size_t nDomains, std::vector< size_t > &domains) const
Get domain indices for a member function.
size_t getNumberDomains() const override
Get number of domains required by this function.
void iterationStarting() override
Called at the start of each iteration.
void setDomainIndices(size_t funIndex, const std::vector< size_t > &domainIndices)
Associate a function and a list of domains.
void clearDomainIndices()
Clear all domain indices.
void setDomainIndex(size_t funIndex, size_t domainIndex)
Associate a function and a domain.
size_t m_nDomains
Number of domains this MultiDomainFunction operates on.
std::vector< IFunction_sptr > createEquivalentFunctions() const override
Create a list of equivalent functions.
size_t m_maxIndex
Maximum domain index.
void function(const FunctionDomain &domain, FunctionValues &values) const override
Function you want to fit to.
A Jacobian for individual functions.
std::shared_ptr< CompositeFunction > CompositeFunction_sptr
shared pointer to the composite function base class
std::string to_string(const wide_integer< Bits, Signed > &n)