CompositeFunction.cpp 32.3 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";
31
constexpr int NUMDEFAULTATTRIBUTES = 1;
Stephen's avatar
Stephen committed
32
33
34
35
36
37
38
39
40
/**
 * Helper function called when we replace a function within the composite
 * function For example, consider a composite function with 5 attributes, 3
 * (oldSize) belonging to the first function and two to the second i.e. the
 * variableFunctionIndexList is [0, 0, 0, 1, 1]. If we replace the second
 * (functionIndex) function with a new function that has 3 (newSize) attributes
 * we need the variableFunctionIndexList to become [0, 0, 0, 1, 1, 1]. This
 * function performs this operation.
 */
41
42
template <typename T>
void replaceVariableIndexRange(std::vector<T> &variableFunctionIndexList,
Stephen's avatar
Stephen committed
43
                               const size_t oldSize, const size_t newSize,
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
                               const T functionIndex) {
  auto itFun = std::find(variableFunctionIndexList.begin(),
                         variableFunctionIndexList.end(), functionIndex);
  if (itFun != variableFunctionIndexList.end()) {
    if (oldSize > newSize) {
      variableFunctionIndexList.erase(itFun, itFun + oldSize - newSize);
    } else if (oldSize < newSize) {
      variableFunctionIndexList.insert(itFun, newSize - oldSize, functionIndex);
    }
  } else if (newSize > 0) {
    using std::placeholders::_1;
    itFun = std::find_if(variableFunctionIndexList.begin(),
                         variableFunctionIndexList.end(),
                         std::bind(std::greater<size_t>(), _1, functionIndex));
    variableFunctionIndexList.insert(itFun, newSize, functionIndex);
  }
}

LamarMoore's avatar
LamarMoore committed
62
} // namespace
Gigg, Martyn Anthony's avatar
Gigg, Martyn Anthony committed
63

Peterson, Peter's avatar
Peterson, Peter committed
64
65
using std::size_t;

66
DECLARE_FUNCTION(CompositeFunction)
67

68
/// Default constructor
69
CompositeFunction::CompositeFunction()
70
71
    : IFunction(), m_nParams(0), m_nAttributes(0),
      m_iConstraintFunction(false) {
72
  createDefaultGlobalAttributes();
Stephen's avatar
Stephen committed
73
74
}

75
76
void CompositeFunction::createDefaultGlobalAttributes() {
  m_globalAttributeNames.clear();
77
  declareAttribute(ATTNUMDERIV, Attribute(false));
78
  m_nAttributes = NUMDEFAULTATTRIBUTES;
79
80
}

81
/// Function initialization. Declare function parameters in this method.
82
void CompositeFunction::init() {}
83

84
/**
85
86
87
 * Writes itself into a string. Functions derived from CompositeFunction may
 * need to override this method with something like this:
 *   std::string NewFunction::writeToString()const
88
89
90
91
92
93
94
 *   {
 *      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
95
96
 * @param parentLocalAttributesStr :: A preformatted string with parent's local
 * attributes.
97
 *    Can be passed in by a CompositeFunction (eg MultiDomainFunction).
98
 * @return the string representation of the composite function
99
 */
Roman Tolchenov's avatar
Roman Tolchenov committed
100
101
std::string CompositeFunction::writeToString(
    const std::string &parentLocalAttributesStr) const {
102
  std::ostringstream ostr;
103

104
  // if empty just return function name
105
  if (nFunctions() == 0) {
106
107
108
    return "name=" + name();
  }

109
110
  if (name() != "CompositeFunction" ||
      nGlobalAttributes() > NUMDEFAULTATTRIBUTES ||
111
      getAttribute(ATTNUMDERIV).asBool() || !parentLocalAttributesStr.empty()) {
112
    ostr << "composite=" << name();
Stephen's avatar
Stephen committed
113
    std::vector<std::string> attr = m_globalAttributeNames;
114
115
    for (const auto &attName : attr) {
      std::string attValue = this->getAttribute(attName).value();
116
117
      if (!attValue.empty()) {
        ostr << ',' << attName << '=' << attValue;
118
119
      }
    }
120
    ostr << parentLocalAttributesStr << ';';
121
  }
122
  const auto localAttr = this->getLocalAttributeNames();
123
  for (size_t i = 0; i < nFunctions(); i++) {
124
    IFunction_sptr fun = getFunction(i);
125
    bool isComp = std::dynamic_pointer_cast<CompositeFunction>(fun) != nullptr;
126
127
    if (isComp)
      ostr << '(';
128
    std::ostringstream localAttributesStr;
129
130
131
132
133
    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
134
135
        localAttributesStr << ',' << '$' << localAttName << '='
                           << localAttValue;
136
137
      }
    }
138
    ostr << fun->writeToString(localAttributesStr.str());
139
140
141
    if (isComp)
      ostr << ')';
    if (i < nFunctions() - 1) {
142
143
144
      ostr << ';';
    }
  }
145
146
147
148
149
150

  // collect non-default constraints
  std::string constraints = writeConstraints();
  // print constraints
  if (!constraints.empty()) {
    ostr << ";constraints=(" << constraints << ")";
151
  }
152
153
154
155

  // collect the non-default ties
  std::string ties = writeTies();
  // print the ties
156
  if (!ties.empty()) {
157
    ostr << ";ties=(" << ties << ")";
158
  }
159

160
161
162
  return ostr.str();
}

163
164
165
/**
 * @param ws A pointer to the workspace being fitted
 */
166
void CompositeFunction::setWorkspace(std::shared_ptr<const Workspace> ws) {
167
168
  // Pass it on to each member
  auto iend = m_functions.end();
169
  for (auto it = m_functions.begin(); it != iend; ++it) {
170
171
172
173
    (*it)->setWorkspace(ws);
  }
}

174
175
176
177
178
179
/**
 * @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.
 */
180
void CompositeFunction::setMatrixWorkspace(
181
182
    std::shared_ptr<const MatrixWorkspace> workspace, size_t wi, double startX,
    double endX) {
183
184
185
  for (size_t iFun = 0; iFun < nFunctions(); ++iFun) {
    m_functions[iFun]->setMatrixWorkspace(workspace, wi, startX, endX);
  }
186
187
}

188
/** Function you want to fit to.
189
 *  @param domain :: An instance of FunctionDomain with the function arguments.
190
191
 *  @param values :: A FunctionValues instance for storing the calculated
 * values.
192
 */
193
194
void CompositeFunction::function(const FunctionDomain &domain,
                                 FunctionValues &values) const {
195
  FunctionValues tmp(domain);
196
  values.zeroCalculated();
197
198
  for (size_t iFun = 0; iFun < nFunctions(); ++iFun) {
    m_functions[iFun]->function(domain, tmp);
199
200
201
202
203
204
205
206
207
    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.
 */
208
209
void CompositeFunction::functionDeriv(const FunctionDomain &domain,
                                      Jacobian &jacobian) {
210
  if (getAttribute(ATTNUMDERIV).asBool()) {
211
    calNumericalDeriv(domain, jacobian);
212
213
214
215
  } else {
    for (size_t iFun = 0; iFun < nFunctions(); ++iFun) {
      PartialJacobian J(&jacobian, paramOffset(iFun));
      getFunction(iFun)->functionDeriv(domain, J);
216
    }
217
218
219
  }
}

220
/** Sets a new value to the i-th parameter.
221
222
 *  @param i :: The parameter index
 *  @param value :: The new value
223
224
 *  @param explicitlySet :: A boolean falgging the parameter as explicitly set
 * (by user)
225
 */
226
227
void CompositeFunction::setParameter(size_t i, const double &value,
                                     bool explicitlySet) {
228
  size_t iFun = functionIndex(i);
229
230
  m_functions[iFun]->setParameter(i - m_paramOffsets[iFun], value,
                                  explicitlySet);
231
232
}

233
234
235
236
/** Sets a new description to the i-th parameter.
 *  @param i :: The parameter index
 *  @param description :: The new description
 */
237
238
void CompositeFunction::setParameterDescription(
    size_t i, const std::string &description) {
239
  size_t iFun = functionIndex(i);
240
241
  m_functions[iFun]->setParameterDescription(i - m_paramOffsets[iFun],
                                             description);
242
243
}

244
/** Get the i-th parameter.
245
 *  @param i :: The parameter index
246
 *  @return value of the requested parameter
247
 */
248
double CompositeFunction::getParameter(size_t i) const {
Peterson, Peter's avatar
Peterson, Peter committed
249
  size_t iFun = functionIndex(i);
250
  return m_functions[iFun]->getParameter(i - m_paramOffsets[iFun]);
251
252
}

253
254
255
256
/**
 * 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
257
 */
258
259
bool CompositeFunction::hasParameter(const std::string &name) const {
  try {
Stephen's avatar
Stephen committed
260
    const auto [parameterName, index] = parseName(name);
261
262
263
    return index < m_functions.size()
               ? m_functions[index]->hasParameter(parameterName)
               : false;
264
265
266
267
268
  } catch (std::invalid_argument &) {
    return false;
  }
}

Stephen's avatar
Stephen committed
269
270
271
272
273
274
275
276
277
278
279
/**
 * 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;
    }
Stephen's avatar
Stephen committed
280
    const auto [attributeName, index] = parseName(name);
281
    return m_functions[index]->hasAttribute(attributeName);
282
  } catch (std::invalid_argument &) {
Stephen's avatar
Stephen committed
283
284
285
286
    return false;
  }
}

287
288
/**
 * Sets a new value to a parameter by name.
289
290
 * @param name :: The name of the parameter.
 * @param value :: The new value
291
 * @param explicitlySet :: A boolean flagging the parameter as explicitly set
292
 * (by user)
293
 */
294
295
void CompositeFunction::setParameter(const std::string &name,
                                     const double &value, bool explicitlySet) {
Stephen's avatar
Stephen committed
296
  const auto [parameterName, index] = parseName(name);
297
  getFunction(index)->setParameter(parameterName, value, explicitlySet);
298
299
}

300
301
302
/**
 * Sets a new description to a parameter by name.
 * @param name :: The name of the parameter.
303
 * @param description :: The new description
304
 */
305
306
void CompositeFunction::setParameterDescription(
    const std::string &name, const std::string &description) {
Stephen's avatar
Stephen committed
307
  const auto [parameterName, index] = parseName(name);
308
  getFunction(index)->setParameterDescription(parameterName, description);
309
310
}

311
312
/**
 * Parameters by name.
313
 * @param name :: The name of the parameter.
314
 * @return value of the requested named parameter
315
 */
316
double CompositeFunction::getParameter(const std::string &name) const {
Stephen's avatar
Stephen committed
317
  const auto [parameterName, index] = parseName(name);
318
  return getFunction(index)->getParameter(parameterName);
319
320
}

321
322
323
324
325
326
327
328
329
330
331
/**
 * 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);
    }
Stephen's avatar
Stephen committed
332
    const auto [attributeName, index] = parseName(name);
333
    return m_functions[index]->getAttribute(attributeName);
334
  } catch (std::invalid_argument &) {
335
336
337
338
339
340
    throw std::invalid_argument(
        "ParamFunctionAttributeHolder::getAttribute - Unknown attribute '" +
        name + "'");
  }
}

341
/**
Stephen's avatar
Stephen committed
342
 *  Set a value of a named attribute.
343
344
345
346
347
348
349
350
351
 *  @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);
  }
Stephen's avatar
Stephen committed
352
  const auto [attributeName, index] = parseName(name);
353
354
355
  return m_functions[index]->setAttribute(attributeName, value);
}

356
/// Total number of parameters
357
size_t CompositeFunction::nParams() const { return m_nParams; }
358

359
// Total number of attributes
360
361
362
363
364
365
366
size_t CompositeFunction::nAttributes() const {
  size_t numAttributes = nGlobalAttributes();
  for (const auto &func : m_functions) {
    numAttributes += func->nAttributes();
  }
  return numAttributes;
}
367
/**
368
 *
369
 * @param name :: The name of a parameter
370
 * @return index of the requested named parameter
371
 */
372
size_t CompositeFunction::parameterIndex(const std::string &name) const {
Stephen's avatar
Stephen committed
373
  const auto [parameterName, index] = parseName(name);
374
375
  return getFunction(index)->parameterIndex(parameterName) +
         m_paramOffsets[index];
376
377
}

Nick Draper's avatar
re #100    
Nick Draper committed
378
/// Returns the name of parameter
379
/// @param i :: The index
Nick Draper's avatar
re #100    
Nick Draper committed
380
/// @return The name of the parameter
381
std::string CompositeFunction::parameterName(size_t i) const {
Stephen's avatar
Stephen committed
382
  const size_t iFun = functionIndex(i);
383
  std::ostringstream ostr;
384
385
  ostr << 'f' << iFun << '.'
       << m_functions[iFun]->parameterName(i - m_paramOffsets[iFun]);
386
  return ostr.str();
387
388
}

389
390
391
392
/// 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 {
393
  if (index < nGlobalAttributes())
Stephen's avatar
Stephen committed
394
    return IFunction::attributeName(index);
395
396

  // Offset the index by the number of global attributes
Stephen's avatar
Stephen committed
397
  const size_t offsetIndex = index - nGlobalAttributes();
398
  size_t functionIndex = attributeFunctionIndex(offsetIndex);
399
400
  std::ostringstream ostr;
  ostr << 'f' << functionIndex << '.'
Stephen's avatar
Stephen committed
401
       << m_functions[functionIndex]->attributeName(
402
              getAttributeOffset(offsetIndex));
403
404
405
  return ostr.str();
}

406
407
408
/// Returns the description of parameter
/// @param i :: The index
/// @return The description of the parameter
409
std::string CompositeFunction::parameterDescription(size_t i) const {
Stephen's avatar
Stephen committed
410
  const size_t iFun = functionIndex(i);
411
  std::ostringstream ostr;
412
  ostr << m_functions[iFun]->parameterDescription(i - m_paramOffsets[iFun]);
413
414
  return ostr.str();
}
415
/**
416
417
418
419
 * Get the fitting error for a parameter
 * @param i :: The index of a parameter
 * @return :: the error
 */
420
double CompositeFunction::getError(size_t i) const {
421
  size_t iFun = functionIndex(i);
422
  return m_functions[iFun]->getError(i - m_paramOffsets[iFun]);
423
424
}

425
426
427
428
429
430
431
432
433
434
/**
 * Get the fitting error for a parameter by name.
 * @param name :: The name of the parameter.
 * @return value of the requested named parameter
 */
double CompositeFunction::getError(const std::string &name) const {
  const auto [parameterName, index] = parseName(name);
  return getFunction(index)->getError(parameterName);
}

435
/**
436
437
438
439
 * Set the fitting error for a parameter
 * @param i :: The index of a parameter
 * @param err :: The error value to set
 */
440
void CompositeFunction::setError(size_t i, double err) {
441
  size_t iFun = functionIndex(i);
442
  m_functions[iFun]->setError(i - m_paramOffsets[iFun], err);
443
444
}

445
446
447
448
449
450
451
452
453
454
/**
 * Sets the fitting error to a parameter by name.
 * @param name :: The name of the parameter.
 * @param err :: The error value to set
 */
void CompositeFunction::setError(const std::string &name, double err) {
  auto [parameterName, index] = parseName(name);
  getFunction(index)->setError(parameterName, err);
}

455
456
457
/// Value of i-th active parameter. Override this method to make fitted
/// parameters different from the declared
double CompositeFunction::activeParameter(size_t i) const {
458
  size_t iFun = functionIndex(i);
459
  return m_functions[iFun]->activeParameter(i - m_paramOffsets[iFun]);
460
461
}

462
463
/// Set new value of i-th active parameter. Override this method to make
/// fitted parameters different from the declared
464
void CompositeFunction::setActiveParameter(size_t i, double value) {
465
  size_t iFun = functionIndex(i);
466
  m_functions[iFun]->setActiveParameter(i - m_paramOffsets[iFun], value);
467
468
469
}

/// Returns the name of active parameter i
470
std::string CompositeFunction::nameOfActive(size_t i) const {
471
  size_t iFun = functionIndex(i);
472
  std::ostringstream ostr;
473
474
  ostr << 'f' << iFun << '.'
       << m_functions[iFun]->nameOfActive(i - m_paramOffsets[iFun]);
475
476
477
478
  return ostr.str();
}

/// Returns the description of active parameter i
479
std::string CompositeFunction::descriptionOfActive(size_t i) const {
480
  size_t iFun = functionIndex(i);
481
  std::ostringstream ostr;
482
  ostr << m_functions[iFun]->descriptionOfActive(i - m_paramOffsets[iFun]);
483
  return ostr.str();
484
485
}

486
/// Change status of parameter
487
488
void CompositeFunction::setParameterStatus(size_t i,
                                           IFunction::ParameterStatus status) {
489
  size_t iFun = functionIndex(i);
490
  m_functions[iFun]->setParameterStatus(i - m_paramOffsets[iFun], status);
491
492
}

493
/// Get status of parameter
494
495
IFunction::ParameterStatus
CompositeFunction::getParameterStatus(size_t i) const {
496
  size_t iFun = functionIndex(i);
497
  return m_functions[iFun]->getParameterStatus(i - m_paramOffsets[iFun]);
498
499
}

500
/** Makes sure that the function is consistent.
501
 */
502
void CompositeFunction::checkFunction() {
503
  m_nParams = 0;
504
  m_nAttributes = nGlobalAttributes();
505
  m_paramOffsets.clear();
506
  m_IFunction.clear();
507
  m_attributeIndex.clear();
508

509
  std::vector<IFunction_sptr> functions(m_functions.begin(), m_functions.end());
510
511
  m_functions.clear();

Hahn, Steven's avatar
Hahn, Steven committed
512
  for (auto &f : functions) {
513
    CompositeFunction_sptr cf = std::dynamic_pointer_cast<CompositeFunction>(f);
514
515
    if (cf)
      cf->checkFunction();
516
517
518
519
    addFunction(f);
  }
}

520
/**
521
522
523
524
 * Remove all member functions
 */
void CompositeFunction::clear() {
  m_nParams = 0;
525
  m_nAttributes = nGlobalAttributes();
526
527
528
  m_paramOffsets.clear();
  m_IFunction.clear();
  m_functions.clear();
Stephen's avatar
Stephen committed
529
  m_attributeIndex.clear();
530
531
}

532
/** Add a function
533
 * @param f :: A pointer to the added function
534
 * @return The function index
535
 */
536
537
size_t CompositeFunction::addFunction(IFunction_sptr f) {
  m_IFunction.insert(m_IFunction.end(), f->nParams(), m_functions.size());
538
539
  m_attributeIndex.insert(m_attributeIndex.end(), f->nAttributes(),
                          m_functions.size());
540
  m_functions.emplace_back(f);
541
  if (m_paramOffsets.empty()) {
542
    m_paramOffsets.emplace_back(0);
543
    m_nParams = f->nParams();
544
    m_nAttributes = f->nAttributes() + nGlobalAttributes();
545
  } else {
546
    m_paramOffsets.emplace_back(m_nParams);
547
    m_nParams += f->nParams();
548
    m_nAttributes += f->nAttributes();
549
  }
550
  return m_functions.size() - 1;
551
552
}

553
/** Remove a function
554
 * @param i :: The index of the function to remove
555
 */
556
void CompositeFunction::removeFunction(size_t i) {
557
558
  if (i >= nFunctions()) {
    throw std::out_of_range("Function index (" + std::to_string(i) +
559
560
                            ") out of range (" + std::to_string(nFunctions()) +
                            ").");
561
  }
562

Stephen's avatar
Stephen committed
563
  const IFunction_sptr fun = getFunction(i);
564
  // Remove ties which are no longer valid
565
566
567
  for (size_t j = 0; j < nParams();) {
    ParameterTie *tie = getTie(j);
    if (tie && tie->findParametersOf(fun.get())) {
568
      removeTie(j);
569
    } else {
570
571
572
573
      j++;
    }
  }

574
575
576
577
578
579
580
  // 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;
581
    } else {
582
      return false;
583
    }
584
  };
Stephen's avatar
Stephen committed
585

586
587
588
589
590
591
  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());
592

Stephen's avatar
Stephen committed
593
594
595
596
  // Reduction in parameters and attributes
  m_nParams -= fun->nParams();
  m_nAttributes -= fun->nAttributes();

597
598
599
  // Shift the parameter offsets down by the total number of i-th function's
  // params
  for (size_t j = i + 1; j < nFunctions(); j++) {
600
    m_paramOffsets[j] -= fun->nParams();
601
  }
602
  m_paramOffsets.erase(m_paramOffsets.begin() + i);
603

604
  m_functions.erase(m_functions.begin() + i);
605
606
}

607
/** Replace a function with a new one. The old function is deleted.
608
 *  The new function must have already its workspace set.
609
 * @param f_old :: The pointer to the function to replace. If it's not
610
 *  a member of this composite function nothing happens
611
 * @param f_new :: A pointer to the new function
612
 */
David Fairbrother's avatar
David Fairbrother committed
613
614
void CompositeFunction::replaceFunctionPtr(const IFunction_sptr &f_old,
                                           const IFunction_sptr &f_new) {
615
616
617
618
  std::vector<IFunction_sptr>::const_iterator it =
      std::find(m_functions.begin(), m_functions.end(), f_old);
  if (it == m_functions.end())
    return;
619
  std::vector<IFunction_sptr>::difference_type iFun = it - m_functions.begin();
620
  replaceFunction(iFun, f_new);
621
622
}

623
/** Replace a function with a new one. The old function is deleted.
624
 * @param functionIndex :: The index of the function to replace
625
 * @param f :: A pointer to the new function
626
 */
627
628
629
630
void CompositeFunction::replaceFunction(size_t functionIndex,
                                        const IFunction_sptr &f) {
  if (functionIndex >= nFunctions()) {
    throw std::out_of_range("Function index (" + std::to_string(functionIndex) +
631
632
                            ") out of range (" + std::to_string(nFunctions()) +
                            ").");
633
  }
634

Stephen's avatar
Stephen committed
635
636
637
638
639
  const IFunction_sptr fun = getFunction(functionIndex);
  const size_t np_old = fun->nParams();
  const size_t np_new = f->nParams();
  const size_t at_old = fun->nAttributes();
  const size_t at_new = f->nAttributes();
640

641
  // Modify function parameter and attribute indices:
642
  replaceVariableIndexRange(m_IFunction, np_old, np_new, functionIndex);
Stephen's avatar
Stephen committed
643
  replaceVariableIndexRange(m_attributeIndex, at_old, at_new, functionIndex);
644

645
  // Decrement attribute and parameter counts
Stephen's avatar
Stephen committed
646
647
  const size_t dnp = np_new - np_old;
  const size_t dna = at_new - at_old;
648
  m_nParams += dnp;
Stephen's avatar
Stephen committed
649
  m_nAttributes += dna;
650

651
652
  // Shift the parameter offsets down by the total number of i-th function's
  // params
653
  for (size_t j = functionIndex + 1; j < nFunctions(); j++) {
654
655
    m_paramOffsets[j] += dnp;
  }
656
  m_functions[functionIndex] = f;
657
658
}

659
/**
660
 * @param i :: The index of the function
661
 * @return function at the requested index
662
 */
663
664
IFunction_sptr CompositeFunction::getFunction(std::size_t i) const {
  if (i >= nFunctions()) {
665
    throw std::out_of_range("Function index (" + std::to_string(i) +
666
667
                            ") out of range (" + std::to_string(nFunctions()) +
                            ").");
668
  }
669
670
671
  return m_functions[i];
}

672
673
/**
 * Get the index of the function to which parameter i belongs
674
 * @param i :: The parameter index
675
 * @return function index of the requested parameter
676
 */
677
678
size_t CompositeFunction::functionIndex(std::size_t i) const {
  if (i >= nParams()) {
679
    throw std::out_of_range("Function parameter index (" + std::to_string(i) +
680
681
                            ") out of range (" + std::to_string(nParams()) +
                            ").");
682
  }
683
  return m_IFunction[i];
684
}
685
686
687
688
689
690
691
692
693
694
695
696
697
/**
 * 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];
}
698
699

/**
LamarMoore's avatar
LamarMoore committed
700
701
 * @param varName :: The variable name which may contain function index (
 * [f<index.>]name )
702
 * @return pair containing the unprefixed variable name and functionIndex
LamarMoore's avatar
LamarMoore committed
703
 */
704
705
std::pair<std::string, size_t>
CompositeFunction::parseName(const std::string &varName) {
Stephen's avatar
Stephen committed
706
  const size_t i = varName.find('.');
707
  if (i == std::string::npos) {
708
    throw std::invalid_argument("Variable " + varName + " not found.");
709
  } else {
710
    if (varName[0] != 'f')
711
      throw std::invalid_argument(
712
          "External function variable name must start with 'f'");
713

Stephen's avatar
Stephen committed
714
715
    const std::string sindex = varName.substr(1, i - 1);
    const size_t index = boost::lexical_cast<size_t>(sindex);
716
717
718
719

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

720
    return std::make_pair(varName.substr(i + 1), index);
721
  }
722
723
}

724
/** Returns the index of parameter i as it declared in its function
725
 * @param i :: The parameter index
726
 * @param recursive :: If true call parameterLocalName recursively until
727
 *    a non-composite function is reached.
728
729
 * @return The local index of the parameter
 */
730
size_t CompositeFunction::parameterLocalIndex(size_t i, bool recursive) const {
731
  size_t iFun = functionIndex(i);
732
733
  auto localIndex = i - m_paramOffsets[iFun];
  if (recursive) {
734
    auto cf = dynamic_cast<const CompositeFunction *>(m_functions[iFun].get());
735
736
737
738
739
    if (cf) {
      return cf->parameterLocalIndex(localIndex, recursive);
    }
  }
  return localIndex;
740
741
}

742
/** Returns the name of parameter i as it declared in its function
743
 * @param i :: The parameter index
744
 * @param recursive :: If true call parameterLocalName recursively until
745
 *    a non-composite function is reached.
746
747
 * @return The pure parameter name (without the function identifier f#.)
 */
748
749
std::string CompositeFunction::parameterLocalName(size_t i,
                                                  bool recursive) const {
750
  size_t iFun = functionIndex(i);
751
752
753
  auto localIndex = i - m_paramOffsets[iFun];
  auto localFunction = m_functions[iFun].get();
  if (recursive) {
754
    auto cf = dynamic_cast<const CompositeFunction *>(localFunction);
755
756
757
758
759
    if (cf) {
      return cf->parameterLocalName(localIndex, recursive);
    }
  }
  return localFunction->parameterName(localIndex);
760
761
}

762
/**
763
 * Apply the ties.
764
 */
765
void CompositeFunction::applyTies() {
766
767
768
769
770
771
772
  if (hasOrderedTies()) {
    applyOrderedTies();
  } else {
    for (size_t i = 0; i < nFunctions(); i++) {
      getFunction(i)->applyTies();
    }
    IFunction::applyTies();
773
774
775
776
  }
}

/**
777
 * Clear the ties.
778
 */
779
void CompositeFunction::clearTies() {
780
  IFunction::clearTies();
781
  for (size_t i = 0; i < nFunctions(); i++) {
782
783
784
785
786
    getFunction(i)->clearTies();
  }
}

/** Removes i-th parameter's tie if it is tied or does nothing.
787
 * @param i :: The index of the tied parameter.
788
 * @return True if successful
789
 */
790
bool CompositeFunction::removeTie(size_t i) {
791
792
793
794
795
796
797
  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;
798
799
800
}

/** Get the tie of i-th parameter
801
 * @param i :: The parameter index
802
 * @return A pointer to the tie.
803
 */
804
ParameterTie *CompositeFunction::getTie(size_t i) const {
805
806
807
808
809
810
  auto tie = IFunction::getTie(i);
  if (tie == nullptr) {
    size_t iFun = functionIndex(i);
    tie = m_functions[iFun]->getTie(i - m_paramOffsets[iFun]);
  }
  return tie;
811
812
813
}

/**
814
 * Declare a new parameter. To used in the implementation's constructor.
815
816
 * @param name :: The parameter name.
 * @param initValue :: The initial value for the parameter
817
 * @param description :: Parameter documentation
818
 */
819
820
821
822
823
824
825
826
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.");
827
}
828
829
830
831
832
833
834
835
836
837
/**
 * 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