CompositeFunction.cpp 30.7 KB
Newer Older
1
2
3
// Mantid Repository : https://github.com/mantidproject/mantid
//
// Copyright © 2018 ISIS Rutherford Appleton Laboratory UKRI,
4
5
//   NScD Oak Ridge National Laboratory, European Spallation Source,
//   Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS
6
// SPDX - License - Identifier: GPL - 3.0 +
7
8
9
//----------------------------------------------------------------------
// Includes
//----------------------------------------------------------------------
10
#include "MantidAPI/CompositeFunction.h"
11
#include "MantidAPI/FunctionFactory.h"
LamarMoore's avatar
LamarMoore committed
12
13
#include "MantidAPI/IConstraint.h"
#include "MantidAPI/ParameterTie.h"
Gigg, Martyn Anthony's avatar
Gigg, Martyn Anthony committed
14
15
#include "MantidKernel/Exception.h"
#include "MantidKernel/Logger.h"
16
#include "MantidKernel/Strings.h"
Gigg, Martyn Anthony's avatar
Gigg, Martyn Anthony committed
17

LamarMoore's avatar
LamarMoore committed
18
#include <algorithm>
19
#include <boost/lexical_cast.hpp>
20
#include <memory>
21
#include <sstream>
David Fairbrother's avatar
David Fairbrother committed
22
#include <utility>
23

24
25
namespace Mantid {
namespace API {
26

27
28
29
namespace {
/// static logger
Kernel::Logger g_log("CompositeFunction");
30
constexpr char *ATTNUMDERIV = "NumDeriv";
LamarMoore's avatar
LamarMoore committed
31
} // namespace
Gigg, Martyn Anthony's avatar
Gigg, Martyn Anthony committed
32

Peterson, Peter's avatar
Peterson, Peter committed
33
34
using std::size_t;

35
DECLARE_FUNCTION(CompositeFunction)
36

37
/// Default constructor
38
CompositeFunction::CompositeFunction()
39
40
    : IFunction(), m_nParams(0), m_nAttributes(0),
      m_iConstraintFunction(false) {
41
  createDefaultGlobalAttributes();
Stephen's avatar
Stephen committed
42
43
}

44
45
void CompositeFunction::createDefaultGlobalAttributes() {
  m_globalAttributeNames.clear();
46
  declareAttribute(ATTNUMDERIV, Attribute(false));
47
  m_nAttributes = 1;
48
49
}

50
/// Function initialization. Declare function parameters in this method.
51
void CompositeFunction::init() {}
52

53
/**
54
55
56
 * Writes itself into a string. Functions derived from CompositeFunction may
 * need to override this method with something like this:
 *   std::string NewFunction::writeToString()const
57
58
59
60
61
62
63
 *   {
 *      ostr << "composite=" << this->name() << ';';
 *      // write NewFunction's own attributes and parameters
 *      ostr << CompositeFunction::asString();
 *      // write NewFunction's own ties and constraints
 *      // ostr << ";constraints=(" << ... <<")";
 *   }
Roman Tolchenov's avatar
Roman Tolchenov committed
64
65
 * @param parentLocalAttributesStr :: A preformatted string with parent's local
 * attributes.
66
 *    Can be passed in by a CompositeFunction (eg MultiDomainFunction).
67
 * @return the string representation of the composite function
68
 */
Roman Tolchenov's avatar
Roman Tolchenov committed
69
70
std::string CompositeFunction::writeToString(
    const std::string &parentLocalAttributesStr) const {
71
  std::ostringstream ostr;
72

73
  // if empty just return function name
74
  if (nFunctions() == 0) {
75
76
77
    return "name=" + name();
  }

78
  if (name() != "CompositeFunction" || nAttributes() > nGlobalAttributes() ||
79
      getAttribute(ATTNUMDERIV).asBool() || !parentLocalAttributesStr.empty()) {
80
    ostr << "composite=" << name();
Stephen's avatar
Stephen committed
81
    std::vector<std::string> attr = m_globalAttributeNames;
82
83
    for (const auto &attName : attr) {
      std::string attValue = this->getAttribute(attName).value();
84
85
      if (!attValue.empty()) {
        ostr << ',' << attName << '=' << attValue;
86
87
      }
    }
88
    ostr << parentLocalAttributesStr << ';';
89
  }
90
  const auto localAttr = this->getLocalAttributeNames();
91
  for (size_t i = 0; i < nFunctions(); i++) {
92
    IFunction_sptr fun = getFunction(i);
93
    bool isComp = std::dynamic_pointer_cast<CompositeFunction>(fun) != nullptr;
94
95
    if (isComp)
      ostr << '(';
96
    std::ostringstream localAttributesStr;
97
98
99
100
101
    for (const auto &localAttName : localAttr) {
      const std::string localAttValue =
          this->getLocalAttribute(i, localAttName).value();
      if (!localAttValue.empty()) {
        // local attribute names are prefixed by dollar sign
Roman Tolchenov's avatar
Roman Tolchenov committed
102
103
        localAttributesStr << ',' << '$' << localAttName << '='
                           << localAttValue;
104
105
      }
    }
106
    ostr << fun->writeToString(localAttributesStr.str());
107
108
109
    if (isComp)
      ostr << ')';
    if (i < nFunctions() - 1) {
110
111
112
      ostr << ';';
    }
  }
113
114
115
116
117
118

  // collect non-default constraints
  std::string constraints = writeConstraints();
  // print constraints
  if (!constraints.empty()) {
    ostr << ";constraints=(" << constraints << ")";
119
  }
120
121
122
123

  // collect the non-default ties
  std::string ties = writeTies();
  // print the ties
124
  if (!ties.empty()) {
125
    ostr << ";ties=(" << ties << ")";
126
  }
127

128
129
130
  return ostr.str();
}

131
132
133
/**
 * @param ws A pointer to the workspace being fitted
 */
134
void CompositeFunction::setWorkspace(std::shared_ptr<const Workspace> ws) {
135
136
  // Pass it on to each member
  auto iend = m_functions.end();
137
  for (auto it = m_functions.begin(); it != iend; ++it) {
138
139
140
141
    (*it)->setWorkspace(ws);
  }
}

142
143
144
145
146
147
/**
 * @param workspace :: A workspace to fit to.
 * @param wi :: An index of a spectrum to fit to.
 * @param startX :: A start of the fitting region.
 * @param endX :: An end of the fitting region.
 */
148
void CompositeFunction::setMatrixWorkspace(
149
150
    std::shared_ptr<const MatrixWorkspace> workspace, size_t wi, double startX,
    double endX) {
151
152
153
  for (size_t iFun = 0; iFun < nFunctions(); ++iFun) {
    m_functions[iFun]->setMatrixWorkspace(workspace, wi, startX, endX);
  }
154
155
}

156
/** Function you want to fit to.
157
 *  @param domain :: An instance of FunctionDomain with the function arguments.
158
159
 *  @param values :: A FunctionValues instance for storing the calculated
 * values.
160
 */
161
162
void CompositeFunction::function(const FunctionDomain &domain,
                                 FunctionValues &values) const {
163
  FunctionValues tmp(domain);
164
  values.zeroCalculated();
165
166
  for (size_t iFun = 0; iFun < nFunctions(); ++iFun) {
    m_functions[iFun]->function(domain, tmp);
167
168
169
170
171
172
173
174
175
    values += tmp;
  }
}

/**
 * Derivatives of function with respect to active parameters
 * @param domain :: Function domain to get the arguments from.
 * @param jacobian :: A Jacobian to store the derivatives.
 */
176
177
void CompositeFunction::functionDeriv(const FunctionDomain &domain,
                                      Jacobian &jacobian) {
178
  if (getAttribute(ATTNUMDERIV).asBool()) {
179
    calNumericalDeriv(domain, jacobian);
180
181
182
183
  } else {
    for (size_t iFun = 0; iFun < nFunctions(); ++iFun) {
      PartialJacobian J(&jacobian, paramOffset(iFun));
      getFunction(iFun)->functionDeriv(domain, J);
184
    }
185
186
187
  }
}

188
/** Sets a new value to the i-th parameter.
189
190
 *  @param i :: The parameter index
 *  @param value :: The new value
191
192
 *  @param explicitlySet :: A boolean falgging the parameter as explicitly set
 * (by user)
193
 */
194
195
void CompositeFunction::setParameter(size_t i, const double &value,
                                     bool explicitlySet) {
196
  size_t iFun = functionIndex(i);
197
198
  m_functions[iFun]->setParameter(i - m_paramOffsets[iFun], value,
                                  explicitlySet);
199
200
}

201
202
203
204
/** Sets a new description to the i-th parameter.
 *  @param i :: The parameter index
 *  @param description :: The new description
 */
205
206
void CompositeFunction::setParameterDescription(
    size_t i, const std::string &description) {
207
  size_t iFun = functionIndex(i);
208
209
  m_functions[iFun]->setParameterDescription(i - m_paramOffsets[iFun],
                                             description);
210
211
}

212
/** Get the i-th parameter.
213
 *  @param i :: The parameter index
214
 *  @return value of the requested parameter
215
 */
216
double CompositeFunction::getParameter(size_t i) const {
Peterson, Peter's avatar
Peterson, Peter committed
217
  size_t iFun = functionIndex(i);
218
  return m_functions[iFun]->getParameter(i - m_paramOffsets[iFun]);
219
220
}

221
222
223
224
/**
 * Check if function has a parameter with a particular name.
 * @param name :: A name of a parameter.
 * @return True if the parameter exists.
LamarMoore's avatar
LamarMoore committed
225
 */
226
227
bool CompositeFunction::hasParameter(const std::string &name) const {
  try {
228
229
230
231
    auto [parameterName, index] = parseName(name);
    return index < m_functions.size()
               ? m_functions[index]->hasParameter(parameterName)
               : false;
232
233
234
235
236
  } catch (std::invalid_argument &) {
    return false;
  }
}

Stephen's avatar
Stephen committed
237
238
239
240
241
242
243
244
245
246
247
248
/**
 * Check if function has a attribute with a particular name.
 * @param name :: A name of a attribute.
 * @return True if the parameter exists.
 */
bool CompositeFunction::hasAttribute(const std::string &name) const {
  try {
    if (std::find(m_globalAttributeNames.begin(), m_globalAttributeNames.end(),
                  name) != m_globalAttributeNames.end()) {
      return true;
    }
    auto [attributeName, index] = parseName(name);
249
250
    return m_functions[index]->hasAttribute(attributeName);
  } catch (...) {
Stephen's avatar
Stephen committed
251
252
253
254
    return false;
  }
}

255
256
/**
 * Sets a new value to a parameter by name.
257
258
 * @param name :: The name of the parameter.
 * @param value :: The new value
259
 * @param explicitlySet :: A boolean flagging the parameter as explicitly set
260
 * (by user)
261
 */
262
263
void CompositeFunction::setParameter(const std::string &name,
                                     const double &value, bool explicitlySet) {
264
265
  auto [parameterName, index] = parseName(name);
  getFunction(index)->setParameter(parameterName, value, explicitlySet);
266
267
}

268
269
270
/**
 * Sets a new description to a parameter by name.
 * @param name :: The name of the parameter.
271
 * @param description :: The new description
272
 */
273
274
void CompositeFunction::setParameterDescription(
    const std::string &name, const std::string &description) {
275
276
  auto [parameterName, index] = parseName(name);
  getFunction(index)->setParameterDescription(parameterName, description);
277
278
}

279
280
/**
 * Parameters by name.
281
 * @param name :: The name of the parameter.
282
 * @return value of the requested named parameter
283
 */
284
double CompositeFunction::getParameter(const std::string &name) const {
285
286
  auto [parameterName, index] = parseName(name);
  return getFunction(index)->getParameter(parameterName);
287
288
}

289
290
291
292
293
294
295
296
297
298
299
300
301
/**
 * Return a value of attribute attName
 * @param name :: Returns the named attribute
 */
API::IFunction::Attribute
CompositeFunction::getAttribute(const std::string &name) const {
  try {
    if (std::find(m_globalAttributeNames.begin(), m_globalAttributeNames.end(),
                  name) != m_globalAttributeNames.end()) {
      return IFunction::getAttribute(name);
    }
    auto [attributeName, index] = parseName(name);
    return m_functions[index]->getAttribute(attributeName);
302
  } catch (std::invalid_argument &) {
303
304
305
306
307
308
    throw std::invalid_argument(
        "ParamFunctionAttributeHolder::getAttribute - Unknown attribute '" +
        name + "'");
  }
}

309
/**
Stephen's avatar
Stephen committed
310
 *  Set a value of a named attribute.
311
312
313
314
315
316
317
318
319
320
321
322
323
 *  @param name :: The name of the attribute
 *  @param value :: The value of the attribute
 */
void CompositeFunction::setAttribute(const std::string &name,
                                     const API::IFunction::Attribute &value) {
  if (std::find(m_globalAttributeNames.begin(), m_globalAttributeNames.end(),
                name) != m_globalAttributeNames.end()) {
    return IFunction::setAttribute(name, value);
  }
  auto [attributeName, index] = parseName(name);
  return m_functions[index]->setAttribute(attributeName, value);
}

324
/// Total number of parameters
325
size_t CompositeFunction::nParams() const { return m_nParams; }
326

327
// Total number of attributes
328
329
330
331
332
333
334
size_t CompositeFunction::nAttributes() const {
  size_t numAttributes = nGlobalAttributes();
  for (const auto &func : m_functions) {
    numAttributes += func->nAttributes();
  }
  return numAttributes;
}
335
/**
336
 *
337
 * @param name :: The name of a parameter
338
 * @return index of the requested named parameter
339
 */
340
size_t CompositeFunction::parameterIndex(const std::string &name) const {
341
342
343
  auto [parameterName, index] = parseName(name);
  return getFunction(index)->parameterIndex(parameterName) +
         m_paramOffsets[index];
344
345
}

Nick Draper's avatar
re #100    
Nick Draper committed
346
/// Returns the name of parameter
347
/// @param i :: The index
Nick Draper's avatar
re #100    
Nick Draper committed
348
/// @return The name of the parameter
349
std::string CompositeFunction::parameterName(size_t i) const {
350
  size_t iFun = functionIndex(i);
351
  std::ostringstream ostr;
352
353
  ostr << 'f' << iFun << '.'
       << m_functions[iFun]->parameterName(i - m_paramOffsets[iFun]);
354
  return ostr.str();
355
356
}

357
358
359
360
/// Returns the name of the ith attribute
/// @param index :: The index of the attribute
/// @return The name of the attribute
std::string CompositeFunction::attributeName(size_t index) const {
361
  if (index < nGlobalAttributes())
Stephen's avatar
Stephen committed
362
    return IFunction::attributeName(index);
363
364
365
366

  // Offset the index by the number of global attributes
  auto offsetIndex = index - nGlobalAttributes();
  size_t functionIndex = attributeFunctionIndex(offsetIndex);
367
368
  std::ostringstream ostr;
  ostr << 'f' << functionIndex << '.'
Stephen's avatar
Stephen committed
369
       << m_functions[functionIndex]->attributeName(
370
              getAttributeOffset(offsetIndex));
371
372
373
  return ostr.str();
}

374
375
376
/// Returns the description of parameter
/// @param i :: The index
/// @return The description of the parameter
377
std::string CompositeFunction::parameterDescription(size_t i) const {
378
  size_t iFun = functionIndex(i);
379
  std::ostringstream ostr;
380
  ostr << m_functions[iFun]->parameterDescription(i - m_paramOffsets[iFun]);
381
382
  return ostr.str();
}
383
/**
384
385
386
387
 * Get the fitting error for a parameter
 * @param i :: The index of a parameter
 * @return :: the error
 */
388
double CompositeFunction::getError(size_t i) const {
389
  size_t iFun = functionIndex(i);
390
  return m_functions[iFun]->getError(i - m_paramOffsets[iFun]);
391
392
}

393
/**
394
395
396
397
 * Set the fitting error for a parameter
 * @param i :: The index of a parameter
 * @param err :: The error value to set
 */
398
void CompositeFunction::setError(size_t i, double err) {
399
  size_t iFun = functionIndex(i);
400
  m_functions[iFun]->setError(i - m_paramOffsets[iFun], err);
401
402
}

403
404
405
/// Value of i-th active parameter. Override this method to make fitted
/// parameters different from the declared
double CompositeFunction::activeParameter(size_t i) const {
406
  size_t iFun = functionIndex(i);
407
  return m_functions[iFun]->activeParameter(i - m_paramOffsets[iFun]);
408
409
}

410
411
/// Set new value of i-th active parameter. Override this method to make
/// fitted parameters different from the declared
412
void CompositeFunction::setActiveParameter(size_t i, double value) {
413
  size_t iFun = functionIndex(i);
414
  m_functions[iFun]->setActiveParameter(i - m_paramOffsets[iFun], value);
415
416
417
}

/// Returns the name of active parameter i
418
std::string CompositeFunction::nameOfActive(size_t i) const {
419
  size_t iFun = functionIndex(i);
420
  std::ostringstream ostr;
421
422
  ostr << 'f' << iFun << '.'
       << m_functions[iFun]->nameOfActive(i - m_paramOffsets[iFun]);
423
424
425
426
  return ostr.str();
}

/// Returns the description of active parameter i
427
std::string CompositeFunction::descriptionOfActive(size_t i) const {
428
  size_t iFun = functionIndex(i);
429
  std::ostringstream ostr;
430
  ostr << m_functions[iFun]->descriptionOfActive(i - m_paramOffsets[iFun]);
431
  return ostr.str();
432
433
}

434
/// Change status of parameter
435
436
void CompositeFunction::setParameterStatus(size_t i,
                                           IFunction::ParameterStatus status) {
437
  size_t iFun = functionIndex(i);
438
  m_functions[iFun]->setParameterStatus(i - m_paramOffsets[iFun], status);
439
440
}

441
/// Get status of parameter
442
443
IFunction::ParameterStatus
CompositeFunction::getParameterStatus(size_t i) const {
444
  size_t iFun = functionIndex(i);
445
  return m_functions[iFun]->getParameterStatus(i - m_paramOffsets[iFun]);
446
447
}

448
/** Makes sure that the function is consistent.
449
 */
450
void CompositeFunction::checkFunction() {
451
  m_nParams = 0;
452
  m_nAttributes = nGlobalAttributes();
453
  m_paramOffsets.clear();
454
  m_IFunction.clear();
455
  m_attributeIndex.clear();
456

457
  std::vector<IFunction_sptr> functions(m_functions.begin(), m_functions.end());
458
459
  m_functions.clear();

Hahn, Steven's avatar
Hahn, Steven committed
460
  for (auto &f : functions) {
461
    CompositeFunction_sptr cf = std::dynamic_pointer_cast<CompositeFunction>(f);
462
463
    if (cf)
      cf->checkFunction();
464
465
466
467
    addFunction(f);
  }
}

468
/**
469
470
471
472
 * Remove all member functions
 */
void CompositeFunction::clear() {
  m_nParams = 0;
473
  m_nAttributes = nGlobalAttributes();
474
475
476
  m_paramOffsets.clear();
  m_IFunction.clear();
  m_functions.clear();
Stephen's avatar
Stephen committed
477
  m_attributeIndex.clear();
478
479
}

480
/** Add a function
481
 * @param f :: A pointer to the added function
482
 * @return The function index
483
 */
484
485
size_t CompositeFunction::addFunction(IFunction_sptr f) {
  m_IFunction.insert(m_IFunction.end(), f->nParams(), m_functions.size());
486
487
  m_attributeIndex.insert(m_attributeIndex.end(), f->nAttributes(),
                          m_functions.size());
488
  m_functions.emplace_back(f);
489
  if (m_paramOffsets.empty()) {
490
    m_paramOffsets.emplace_back(0);
491
    m_nParams = f->nParams();
492
    m_nAttributes = f->nAttributes() + nGlobalAttributes();
493
  } else {
494
    m_paramOffsets.emplace_back(m_nParams);
495
    m_nParams += f->nParams();
496
    m_nAttributes += f->nAttributes();
497
  }
498
  return m_functions.size() - 1;
499
500
}

501
/** Remove a function
502
 * @param i :: The index of the function to remove
503
 */
504
void CompositeFunction::removeFunction(size_t i) {
505
506
  if (i >= nFunctions()) {
    throw std::out_of_range("Function index (" + std::to_string(i) +
507
508
                            ") out of range (" + std::to_string(nFunctions()) +
                            ").");
509
  }
510

511
  IFunction_sptr fun = getFunction(i);
512
  // Remove ties which are no longer valid
513
514
515
  for (size_t j = 0; j < nParams();) {
    ParameterTie *tie = getTie(j);
    if (tie && tie->findParametersOf(fun.get())) {
516
      removeTie(j);
517
    } else {
518
519
520
521
      j++;
    }
  }

522
523
524
525
526
527
528
  // Shift down the function indices for parameters and attributes
  auto shiftDown = [&](auto &functionIndex) {
    if (functionIndex == i) {
      return true;
    } else if (functionIndex > i) {
      functionIndex -= 1;
      return false;
529
    } else {
530
      return false;
531
    }
532
  };
Stephen's avatar
Stephen committed
533

534
535
536
537
538
539
  m_IFunction.erase(
      std::remove_if(m_IFunction.begin(), m_IFunction.end(), shiftDown),
      m_IFunction.end());
  m_attributeIndex.erase(std::remove_if(m_attributeIndex.begin(),
                                        m_attributeIndex.end(), shiftDown),
                         m_attributeIndex.end());
540

Stephen's avatar
Stephen committed
541
542
543
544
  // Reduction in parameters and attributes
  m_nParams -= fun->nParams();
  m_nAttributes -= fun->nAttributes();

545
546
547
  // Shift the parameter offsets down by the total number of i-th function's
  // params
  for (size_t j = i + 1; j < nFunctions(); j++) {
548
    m_paramOffsets[j] -= fun->nParams();
549
  }
550
  m_paramOffsets.erase(m_paramOffsets.begin() + i);
551

552
  m_functions.erase(m_functions.begin() + i);
553
554
}

555
/** Replace a function with a new one. The old function is deleted.
556
 *  The new function must have already its workspace set.
557
 * @param f_old :: The pointer to the function to replace. If it's not
558
 *  a member of this composite function nothing happens
559
 * @param f_new :: A pointer to the new function
560
 */
David Fairbrother's avatar
David Fairbrother committed
561
562
void CompositeFunction::replaceFunctionPtr(const IFunction_sptr &f_old,
                                           const IFunction_sptr &f_new) {
563
564
565
566
  std::vector<IFunction_sptr>::const_iterator it =
      std::find(m_functions.begin(), m_functions.end(), f_old);
  if (it == m_functions.end())
    return;
567
  std::vector<IFunction_sptr>::difference_type iFun = it - m_functions.begin();
568
  replaceFunction(iFun, f_new);
569
570
}

571
/** Replace a function with a new one. The old function is deleted.
572
573
 * @param i :: The index of the function to replace
 * @param f :: A pointer to the new function
574
 */
575
576
577
578
void CompositeFunction::replaceFunction(size_t functionIndex,
                                        const IFunction_sptr &f) {
  if (functionIndex >= nFunctions()) {
    throw std::out_of_range("Function index (" + std::to_string(functionIndex) +
579
580
                            ") out of range (" + std::to_string(nFunctions()) +
                            ").");
581
  }
582

583
  IFunction_sptr fun = getFunction(functionIndex);
584
585
  size_t np_old = fun->nParams();
  size_t np_new = f->nParams();
Stephen's avatar
Stephen committed
586
587
  size_t at_old = fun->nAttributes();
  size_t at_new = f->nAttributes();
588

589
590
  // Modify function parameter and attribute indices:
  // The new function may have different number of
591
  {
592
593
594
    auto itFun =
        std::find(m_IFunction.begin(), m_IFunction.end(), functionIndex);

595
    if (itFun != m_IFunction.end()) // functions must have at least 1 parameter
596
    {
597
598
599
      if (np_old > np_new) {
        m_IFunction.erase(itFun, itFun + np_old - np_new);
      } else if (np_old < np_new) {
600
        m_IFunction.insert(itFun, np_new - np_old, functionIndex);
601
      }
602
603
    } else if (np_new > 0) // it could happen if the old function is an empty
                           // CompositeFunction
604
    {
605
      using std::placeholders::_1;
606
607
608
609
      itFun =
          std::find_if(m_IFunction.begin(), m_IFunction.end(),
                       std::bind(std::greater<size_t>(), _1, functionIndex));
      m_IFunction.insert(itFun, np_new, functionIndex);
610
    }
611
612
  }

613
  size_t dnp = np_new - np_old;
Stephen's avatar
Stephen committed
614
  size_t dna = at_new - at_old;
615
  m_nParams += dnp;
Stephen's avatar
Stephen committed
616
  m_nAttributes += dna;
617
618
  // Shift the parameter offsets down by the total number of i-th function's
  // params
619
  for (size_t j = functionIndex + 1; j < nFunctions(); j++) {
620
621
    m_paramOffsets[j] += dnp;
  }
622
  m_functions[functionIndex] = f;
623
624
}

625
/**
626
 * @param i :: The index of the function
627
 * @return function at the requested index
628
 */
629
630
IFunction_sptr CompositeFunction::getFunction(std::size_t i) const {
  if (i >= nFunctions()) {
631
    throw std::out_of_range("Function index (" + std::to_string(i) +
632
633
                            ") out of range (" + std::to_string(nFunctions()) +
                            ").");
634
  }
635
636
637
  return m_functions[i];
}

638
639
/**
 * Get the index of the function to which parameter i belongs
640
 * @param i :: The parameter index
641
 * @return function index of the requested parameter
642
 */
643
644
size_t CompositeFunction::functionIndex(std::size_t i) const {
  if (i >= nParams()) {
645
    throw std::out_of_range("Function parameter index (" + std::to_string(i) +
646
647
                            ") out of range (" + std::to_string(nParams()) +
                            ").");
648
  }
649
  return m_IFunction[i];
650
}
651
652
653
654
655
656
657
658
659
660
661
662
663
/**
 * Get the index of the function to which parameter i belongs
 * @param i :: The parameter index
 * @return function index of the requested parameter
 */
size_t CompositeFunction::attributeFunctionIndex(std::size_t i) const {
  if (i >= nAttributes()) {
    throw std::out_of_range("Function attribute index (" + std::to_string(i) +
                            ") out of range (" + std::to_string(nAttributes()) +
                            ").");
  }
  return m_attributeIndex[i];
}
664
665

/**
LamarMoore's avatar
LamarMoore committed
666
667
668
 * @param varName :: The variable name which may contain function index (
 * [f<index.>]name )
 * @param index :: Receives function index or throws std::invalid_argument
669
 * @param name :: Receives the variable name
LamarMoore's avatar
LamarMoore committed
670
 */
671
672
std::pair<std::string, size_t>
CompositeFunction::parseName(const std::string &varName) {
673
  size_t i = varName.find('.');
674
  if (i == std::string::npos) {
675
    throw std::invalid_argument("Variable " + varName + " not found.");
676
  } else {
677
    if (varName[0] != 'f')
678
      throw std::invalid_argument(
679
          "External function variable name must start with 'f'");
680

681
    std::string sindex = varName.substr(1, i - 1);
682
    size_t index = boost::lexical_cast<size_t>(sindex);
683
684
685
686

    if (i == varName.size() - 1)
      throw std::invalid_argument("Name cannot be empty");

687
    return std::make_pair(varName.substr(i + 1), index);
688
  }
689
690
}

691
/** Returns the index of parameter i as it declared in its function
692
 * @param i :: The parameter index
693
 * @param recursive :: If true call parameterLocalName recursively until
694
 *    a non-composite function is reached.
695
696
 * @return The local index of the parameter
 */
697
size_t CompositeFunction::parameterLocalIndex(size_t i, bool recursive) const {
698
  size_t iFun = functionIndex(i);
699
700
  auto localIndex = i - m_paramOffsets[iFun];
  if (recursive) {
701
    auto cf = dynamic_cast<const CompositeFunction *>(m_functions[iFun].get());
702
703
704
705
706
    if (cf) {
      return cf->parameterLocalIndex(localIndex, recursive);
    }
  }
  return localIndex;
707
708
}

709
/** Returns the name of parameter i as it declared in its function
710
 * @param i :: The parameter index
711
 * @param recursive :: If true call parameterLocalName recursively until
712
 *    a non-composite function is reached.
713
714
 * @return The pure parameter name (without the function identifier f#.)
 */
715
716
std::string CompositeFunction::parameterLocalName(size_t i,
                                                  bool recursive) const {
717
  size_t iFun = functionIndex(i);
718
719
720
  auto localIndex = i - m_paramOffsets[iFun];
  auto localFunction = m_functions[iFun].get();
  if (recursive) {
721
    auto cf = dynamic_cast<const CompositeFunction *>(localFunction);
722
723
724
725
726
    if (cf) {
      return cf->parameterLocalName(localIndex, recursive);
    }
  }
  return localFunction->parameterName(localIndex);
727
728
}

729
/**
730
 * Apply the ties.
731
 */
732
void CompositeFunction::applyTies() {
733
734
735
736
737
738
739
  if (hasOrderedTies()) {
    applyOrderedTies();
  } else {
    for (size_t i = 0; i < nFunctions(); i++) {
      getFunction(i)->applyTies();
    }
    IFunction::applyTies();
740
741
742
743
  }
}

/**
744
 * Clear the ties.
745
 */
746
void CompositeFunction::clearTies() {
747
  IFunction::clearTies();
748
  for (size_t i = 0; i < nFunctions(); i++) {
749
750
751
752
753
    getFunction(i)->clearTies();
  }
}

/** Removes i-th parameter's tie if it is tied or does nothing.
754
 * @param i :: The index of the tied parameter.
755
 * @return True if successful
756
 */
757
bool CompositeFunction::removeTie(size_t i) {
758
759
760
761
762
763
764
  bool foundAndRemovedTie = IFunction::removeTie(i);
  if (!foundAndRemovedTie) {
    size_t iFun = functionIndex(i);
    bool res = m_functions[iFun]->removeTie(i - m_paramOffsets[iFun]);
    return res;
  }
  return foundAndRemovedTie;
765
766
767
}

/** Get the tie of i-th parameter
768
 * @param i :: The parameter index
769
 * @return A pointer to the tie.
770
 */
771
ParameterTie *CompositeFunction::getTie(size_t i) const {
772
773
774
775
776
777
  auto tie = IFunction::getTie(i);
  if (tie == nullptr) {
    size_t iFun = functionIndex(i);
    tie = m_functions[iFun]->getTie(i - m_paramOffsets[iFun]);
  }
  return tie;
778
779
780
}

/**
781
 * Declare a new parameter. To used in the implementation's constructor.
782
783
 * @param name :: The parameter name.
 * @param initValue :: The initial value for the parameter
784
 * @param description :: Parameter documentation
785
 */
786
787
788
789
790
791
792
793
void CompositeFunction::declareParameter(const std::string &name,
                                         double initValue,
                                         const std::string &description) {
  (void)name;        // Avoid compiler warning
  (void)initValue;   // Avoid compiler warning
  (void)description; // Avoid compiler warning
  throw Kernel::Exception::NotImplementedError(
      "CompositeFunction cannot not have its own parameters.");
794
}
795
796
797
798
799
800
801
802
803
804
/**
 * Declares a single (global) attribute on the composite function
 * @param name :: The name of the attribute
 * @param defaultValue :: A default value
 */
void CompositeFunction::declareAttribute(
    const std::string &name, const API::IFunction::Attribute &defaultValue) {
  m_globalAttributeNames.emplace_back(name);
  IFunction::declareAttribute(name, defaultValue);
}
805

806
807
808
/**
 * Prepare the function for a fit.
 */
809
void CompositeFunction::setUpForFit() {
810
  IFunction::setUpForFit();
811
  // set up the member functions
812
  for (size_t i = 0; i < nFunctions(); i++) {
813
    getFunction(i)->setUpForFit();
814
  }
815
816

  // Instead of automatically switching to numeric derivatives
817
  // log a warning about a danger of not using it
818
819
820
821
822
  if (!getAttribute("NumDeriv").asBool()) {
    for (size_t i = 0; i < nParams(); ++i) {
      ParameterTie *tie = getTie(i);
      if (tie && !tie->isConstant()) {
        g_log.warning() << "Numeric derivatives should be used when "