CompositeFunction.cpp 28.5 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
const std::string 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
41
    : IFunction(), m_nParams(0), m_nAttributes(0),
      m_iConstraintFunction(false) {
  declareAttribute(ATTNUMDERIV, Attribute(false));
42
43
}

44
/// Function initialization. Declare function parameters in this method.
45
void CompositeFunction::init() {}
46

47
/**
48
49
50
 * Writes itself into a string. Functions derived from CompositeFunction may
 * need to override this method with something like this:
 *   std::string NewFunction::writeToString()const
51
52
53
54
55
56
57
 *   {
 *      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
58
59
 * @param parentLocalAttributesStr :: A preformatted string with parent's local
 * attributes.
60
 *    Can be passed in by a CompositeFunction (eg MultiDomainFunction).
61
 * @return the string representation of the composite function
62
 */
Roman Tolchenov's avatar
Roman Tolchenov committed
63
64
std::string CompositeFunction::writeToString(
    const std::string &parentLocalAttributesStr) const {
65
  std::ostringstream ostr;
66

67
  // if empty just return function name
68
  if (nFunctions() == 0) {
69
70
71
    return "name=" + name();
  }

72
  if (name() != "CompositeFunction" || nAttributes() > 1 ||
73
      getAttribute(ATTNUMDERIV).asBool() || !parentLocalAttributesStr.empty()) {
74
    ostr << "composite=" << name();
75
    std::vector<std::string> attr = this->getAttributeNames();
76
77
    for (const auto &attName : attr) {
      std::string attValue = this->getAttribute(attName).value();
78
79
      if (!attValue.empty()) {
        ostr << ',' << attName << '=' << attValue;
80
81
      }
    }
82
    ostr << parentLocalAttributesStr << ';';
83
  }
84
  const auto localAttr = this->getLocalAttributeNames();
85
  for (size_t i = 0; i < nFunctions(); i++) {
86
    IFunction_sptr fun = getFunction(i);
87
    bool isComp = std::dynamic_pointer_cast<CompositeFunction>(fun) != nullptr;
88
89
    if (isComp)
      ostr << '(';
90
    std::ostringstream localAttributesStr;
91
92
93
94
95
    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
96
97
        localAttributesStr << ',' << '$' << localAttName << '='
                           << localAttValue;
98
99
      }
    }
100
    ostr << fun->writeToString(localAttributesStr.str());
101
102
103
    if (isComp)
      ostr << ')';
    if (i < nFunctions() - 1) {
104
105
106
      ostr << ';';
    }
  }
107
108
109
110
111
112

  // collect non-default constraints
  std::string constraints = writeConstraints();
  // print constraints
  if (!constraints.empty()) {
    ostr << ";constraints=(" << constraints << ")";
113
  }
114
115
116
117

  // collect the non-default ties
  std::string ties = writeTies();
  // print the ties
118
  if (!ties.empty()) {
119
    ostr << ";ties=(" << ties << ")";
120
  }
121

122
123
124
  return ostr.str();
}

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

136
137
138
139
140
141
/**
 * @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.
 */
142
void CompositeFunction::setMatrixWorkspace(
143
144
    std::shared_ptr<const MatrixWorkspace> workspace, size_t wi, double startX,
    double endX) {
145
146
147
  for (size_t iFun = 0; iFun < nFunctions(); ++iFun) {
    m_functions[iFun]->setMatrixWorkspace(workspace, wi, startX, endX);
  }
148
149
}

150
/** Function you want to fit to.
151
 *  @param domain :: An instance of FunctionDomain with the function arguments.
152
153
 *  @param values :: A FunctionValues instance for storing the calculated
 * values.
154
 */
155
156
void CompositeFunction::function(const FunctionDomain &domain,
                                 FunctionValues &values) const {
157
  FunctionValues tmp(domain);
158
  values.zeroCalculated();
159
160
  for (size_t iFun = 0; iFun < nFunctions(); ++iFun) {
    m_functions[iFun]->function(domain, tmp);
161
162
163
164
165
166
167
168
169
    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.
 */
170
171
void CompositeFunction::functionDeriv(const FunctionDomain &domain,
                                      Jacobian &jacobian) {
172
  if (getAttribute(ATTNUMDERIV).asBool()) {
173
    calNumericalDeriv(domain, jacobian);
174
175
176
177
  } else {
    for (size_t iFun = 0; iFun < nFunctions(); ++iFun) {
      PartialJacobian J(&jacobian, paramOffset(iFun));
      getFunction(iFun)->functionDeriv(domain, J);
178
    }
179
180
181
  }
}

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

195
196
197
198
/** Sets a new description to the i-th parameter.
 *  @param i :: The parameter index
 *  @param description :: The new description
 */
199
200
void CompositeFunction::setParameterDescription(
    size_t i, const std::string &description) {
201
  size_t iFun = functionIndex(i);
202
203
  m_functions[iFun]->setParameterDescription(i - m_paramOffsets[iFun],
                                             description);
204
205
}

206
/** Get the i-th parameter.
207
 *  @param i :: The parameter index
208
 *  @return value of the requested parameter
209
 */
210
double CompositeFunction::getParameter(size_t i) const {
Peterson, Peter's avatar
Peterson, Peter committed
211
  size_t iFun = functionIndex(i);
212
  return m_functions[iFun]->getParameter(i - m_paramOffsets[iFun]);
213
214
}

215
216
217
218
/**
 * 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
219
 */
220
221
bool CompositeFunction::hasParameter(const std::string &name) const {
  try {
222
223
224
225
    auto [parameterName, index] = parseName(name);
    return index < m_functions.size()
               ? m_functions[index]->hasParameter(parameterName)
               : false;
226
227
228
229
230
  } catch (std::invalid_argument &) {
    return false;
  }
}

231
232
/**
 * Sets a new value to a parameter by name.
233
234
 * @param name :: The name of the parameter.
 * @param value :: The new value
235
 * @param explicitlySet :: A boolean flagging the parameter as explicitly set
236
 * (by user)
237
 */
238
239
void CompositeFunction::setParameter(const std::string &name,
                                     const double &value, bool explicitlySet) {
240
241
  auto [parameterName, index] = parseName(name);
  getFunction(index)->setParameter(parameterName, value, explicitlySet);
242
243
}

244
245
246
/**
 * Sets a new description to a parameter by name.
 * @param name :: The name of the parameter.
247
 * @param description :: The new description
248
 */
249
250
void CompositeFunction::setParameterDescription(
    const std::string &name, const std::string &description) {
251
252
  auto [parameterName, index] = parseName(name);
  getFunction(index)->setParameterDescription(parameterName, description);
253
254
}

255
256
/**
 * Parameters by name.
257
 * @param name :: The name of the parameter.
258
 * @return value of the requested named parameter
259
 */
260
double CompositeFunction::getParameter(const std::string &name) const {
261
262
  auto [parameterName, index] = parseName(name);
  return getFunction(index)->getParameter(parameterName);
263
264
265
}

/// Total number of parameters
266
size_t CompositeFunction::nParams() const { return m_nParams; }
267

268
269
// Total number of attributes
size_t CompositeFunction::nAttributes() const { return m_nAttributes; }
270
/**
271
 *
272
 * @param name :: The name of a parameter
273
 * @return index of the requested named parameter
274
 */
275
size_t CompositeFunction::parameterIndex(const std::string &name) const {
276
277
278
  auto [parameterName, index] = parseName(name);
  return getFunction(index)->parameterIndex(parameterName) +
         m_paramOffsets[index];
279
280
}

Nick Draper's avatar
re #100    
Nick Draper committed
281
/// Returns the name of parameter
282
/// @param i :: The index
Nick Draper's avatar
re #100    
Nick Draper committed
283
/// @return The name of the parameter
284
std::string CompositeFunction::parameterName(size_t i) const {
285
  size_t iFun = functionIndex(i);
286
  std::ostringstream ostr;
287
288
  ostr << 'f' << iFun << '.'
       << m_functions[iFun]->parameterName(i - m_paramOffsets[iFun]);
289
  return ostr.str();
290
291
}

292
293
294
295
296
297
298
299
300
301
302
303
304
/// 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 {
  size_t functionIndex = attributeFunctionIndex(index);
  std::cout << "Index is" << functionIndex << std::endl;
  std::cout << "Offset is" << getAttributeOffset(index) << std::endl;
  std::ostringstream ostr;
  ostr << 'f' << functionIndex << '.'
       << m_functions[functionIndex]->attributeName(getAttributeOffset(index));
  return ostr.str();
}

305
306
307
/// Returns the description of parameter
/// @param i :: The index
/// @return The description of the parameter
308
std::string CompositeFunction::parameterDescription(size_t i) const {
309
  size_t iFun = functionIndex(i);
310
  std::ostringstream ostr;
311
  ostr << m_functions[iFun]->parameterDescription(i - m_paramOffsets[iFun]);
312
313
314
  return ostr.str();
}

315
316
317
318
319
320
321
322
323
324
325
326
327
/// Returns a list of attribute names, including the attributes of the child
/// functions
std::vector<std::string> CompositeFunction::getAttributeNames() const {
  std::vector<std::string> attributeNames;
  attributeNames.reserve(nAttributes() + 1);
  // Add global numerical derivative attribute
  attributeNames.insert(attributeNames.end(), ATTNUMDERIV);
  // Add child attributes
  auto names = IFunction::getAttributeNames();
  attributeNames.insert(attributeNames.end(), names.begin(), names.end());
  return attributeNames;
}

328
/**
329
330
331
332
 * Get the fitting error for a parameter
 * @param i :: The index of a parameter
 * @return :: the error
 */
333
double CompositeFunction::getError(size_t i) const {
334
  size_t iFun = functionIndex(i);
335
  return m_functions[iFun]->getError(i - m_paramOffsets[iFun]);
336
337
}

338
/**
339
340
341
342
 * Set the fitting error for a parameter
 * @param i :: The index of a parameter
 * @param err :: The error value to set
 */
343
void CompositeFunction::setError(size_t i, double err) {
344
  size_t iFun = functionIndex(i);
345
  m_functions[iFun]->setError(i - m_paramOffsets[iFun], err);
346
347
}

348
349
350
/// Value of i-th active parameter. Override this method to make fitted
/// parameters different from the declared
double CompositeFunction::activeParameter(size_t i) const {
351
  size_t iFun = functionIndex(i);
352
  return m_functions[iFun]->activeParameter(i - m_paramOffsets[iFun]);
353
354
}

355
356
357
/// Set new value of i-th active parameter. Override this method to make fitted
/// parameters different from the declared
void CompositeFunction::setActiveParameter(size_t i, double value) {
358
  size_t iFun = functionIndex(i);
359
  m_functions[iFun]->setActiveParameter(i - m_paramOffsets[iFun], value);
360
361
362
}

/// Returns the name of active parameter i
363
std::string CompositeFunction::nameOfActive(size_t i) const {
364
  size_t iFun = functionIndex(i);
365
  std::ostringstream ostr;
366
367
  ostr << 'f' << iFun << '.'
       << m_functions[iFun]->nameOfActive(i - m_paramOffsets[iFun]);
368
369
370
371
  return ostr.str();
}

/// Returns the description of active parameter i
372
std::string CompositeFunction::descriptionOfActive(size_t i) const {
373
  size_t iFun = functionIndex(i);
374
  std::ostringstream ostr;
375
  ostr << m_functions[iFun]->descriptionOfActive(i - m_paramOffsets[iFun]);
376
  return ostr.str();
377
378
}

379
/// Change status of parameter
380
381
void CompositeFunction::setParameterStatus(size_t i,
                                           IFunction::ParameterStatus status) {
382
  size_t iFun = functionIndex(i);
383
  m_functions[iFun]->setParameterStatus(i - m_paramOffsets[iFun], status);
384
385
}

386
/// Get status of parameter
387
388
IFunction::ParameterStatus
CompositeFunction::getParameterStatus(size_t i) const {
389
  size_t iFun = functionIndex(i);
390
  return m_functions[iFun]->getParameterStatus(i - m_paramOffsets[iFun]);
391
392
}

393
/** Makes sure that the function is consistent.
394
 */
395
void CompositeFunction::checkFunction() {
396
397
  m_nParams = 0;
  m_paramOffsets.clear();
398
  m_IFunction.clear();
399
  m_attributeIndex.clear();
400

401
  std::vector<IFunction_sptr> functions(m_functions.begin(), m_functions.end());
402
403
  m_functions.clear();

Hahn, Steven's avatar
Hahn, Steven committed
404
  for (auto &f : functions) {
405
    CompositeFunction_sptr cf = std::dynamic_pointer_cast<CompositeFunction>(f);
406
407
    if (cf)
      cf->checkFunction();
408
409
410
411
    addFunction(f);
  }
}

412
/**
413
414
415
416
417
418
419
420
421
 * Remove all member functions
 */
void CompositeFunction::clear() {
  m_nParams = 0;
  m_paramOffsets.clear();
  m_IFunction.clear();
  m_functions.clear();
}

422
/** Add a function
423
 * @param f :: A pointer to the added function
424
 * @return The function index
425
 */
426
427
size_t CompositeFunction::addFunction(IFunction_sptr f) {
  m_IFunction.insert(m_IFunction.end(), f->nParams(), m_functions.size());
428
429
  m_attributeIndex.insert(m_attributeIndex.end(), f->nAttributes(),
                          m_functions.size());
430
  m_functions.emplace_back(f);
431
  if (m_paramOffsets.empty()) {
432
    m_paramOffsets.emplace_back(0);
433
    m_nParams = f->nParams();
434
    m_nAttributes = f->nAttributes();
435
  } else {
436
    m_paramOffsets.emplace_back(m_nParams);
437
    m_nParams += f->nParams();
438
    m_nAttributes += f->nAttributes();
439
  }
440
  return m_functions.size() - 1;
441
442
}

443
/** Remove a function
444
 * @param i :: The index of the function to remove
445
 */
446
void CompositeFunction::removeFunction(size_t i) {
447
448
  if (i >= nFunctions()) {
    throw std::out_of_range("Function index (" + std::to_string(i) +
449
450
                            ") out of range (" + std::to_string(nFunctions()) +
                            ").");
451
  }
452

453
  IFunction_sptr fun = getFunction(i);
454
455
456
457
  // Reduction in parameters and attributes
  m_nParams -= fun->nParams();
  m_nAttributes -= fun->nAttributes();
  // Remove ties which are no longer valid
458
459
460
  for (size_t j = 0; j < nParams();) {
    ParameterTie *tie = getTie(j);
    if (tie && tie->findParametersOf(fun.get())) {
461
      removeTie(j);
462
    } else {
463
464
465
466
      j++;
    }
  }

467
468
469
470
471
472
473
  // 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;
474
    } else {
475
      return false;
476
    }
477
478
479
480
481
482
483
  };
  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());
484

485
486
487
  // Shift the parameter offsets down by the total number of i-th function's
  // params
  for (size_t j = i + 1; j < nFunctions(); j++) {
488
    m_paramOffsets[j] -= fun->nParams();
489
  }
490
  m_paramOffsets.erase(m_paramOffsets.begin() + i);
491

492
  m_functions.erase(m_functions.begin() + i);
493
494
}

495
/** Replace a function with a new one. The old function is deleted.
496
 *  The new function must have already its workspace set.
497
 * @param f_old :: The pointer to the function to replace. If it's not
498
 *  a member of this composite function nothing happens
499
 * @param f_new :: A pointer to the new function
500
 */
David Fairbrother's avatar
David Fairbrother committed
501
502
void CompositeFunction::replaceFunctionPtr(const IFunction_sptr &f_old,
                                           const IFunction_sptr &f_new) {
503
504
505
506
  std::vector<IFunction_sptr>::const_iterator it =
      std::find(m_functions.begin(), m_functions.end(), f_old);
  if (it == m_functions.end())
    return;
507
  std::vector<IFunction_sptr>::difference_type iFun = it - m_functions.begin();
508
  replaceFunction(iFun, f_new);
509
510
}

511
/** Replace a function with a new one. The old function is deleted.
512
513
 * @param i :: The index of the function to replace
 * @param f :: A pointer to the new function
514
 */
515
516
517
518
void CompositeFunction::replaceFunction(size_t functionIndex,
                                        const IFunction_sptr &f) {
  if (functionIndex >= nFunctions()) {
    throw std::out_of_range("Function index (" + std::to_string(functionIndex) +
519
520
                            ") out of range (" + std::to_string(nFunctions()) +
                            ").");
521
  }
522

523
  IFunction_sptr fun = getFunction(functionIndex);
524
525
  size_t np_old = fun->nParams();
  size_t np_new = f->nParams();
526

527
528
  // Modify function parameter and attribute indices:
  // The new function may have different number of
529
  {
530
531
532
    auto itFun =
        std::find(m_IFunction.begin(), m_IFunction.end(), functionIndex);

533
    if (itFun != m_IFunction.end()) // functions must have at least 1 parameter
534
    {
535
536
537
      if (np_old > np_new) {
        m_IFunction.erase(itFun, itFun + np_old - np_new);
      } else if (np_old < np_new) {
538
        m_IFunction.insert(itFun, np_new - np_old, functionIndex);
539
      }
540
541
    } else if (np_new > 0) // it could happen if the old function is an empty
                           // CompositeFunction
542
    {
543
      using std::placeholders::_1;
544
545
546
547
      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);
548
    }
549
550
  }

551
  size_t dnp = np_new - np_old;
552
  m_nParams += dnp;
553
554
  // Shift the parameter offsets down by the total number of i-th function's
  // params
555
  for (size_t j = functionIndex + 1; j < nFunctions(); j++) {
556
557
    m_paramOffsets[j] += dnp;
  }
558
  m_functions[functionIndex] = f;
559
560
}

561
/**
562
 * @param i :: The index of the function
563
 * @return function at the requested index
564
 */
565
566
IFunction_sptr CompositeFunction::getFunction(std::size_t i) const {
  if (i >= nFunctions()) {
567
    throw std::out_of_range("Function index (" + std::to_string(i) +
568
569
                            ") out of range (" + std::to_string(nFunctions()) +
                            ").");
570
  }
571
572
573
  return m_functions[i];
}

574
575
/**
 * Get the index of the function to which parameter i belongs
576
 * @param i :: The parameter index
577
 * @return function index of the requested parameter
578
 */
579
580
size_t CompositeFunction::functionIndex(std::size_t i) const {
  if (i >= nParams()) {
581
    throw std::out_of_range("Function parameter index (" + std::to_string(i) +
582
583
                            ") out of range (" + std::to_string(nParams()) +
                            ").");
584
  }
585
  return m_IFunction[i];
586
}
587
588
589
590
591
592
593
594
595
596
597
598
599
/**
 * 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];
}
600
601

/**
LamarMoore's avatar
LamarMoore committed
602
603
604
 * @param varName :: The variable name which may contain function index (
 * [f<index.>]name )
 * @param index :: Receives function index or throws std::invalid_argument
605
 * @param name :: Receives the variable name
LamarMoore's avatar
LamarMoore committed
606
 */
607
608
std::pair<std::string, size_t>
CompositeFunction::parseName(const std::string &varName) {
609
  size_t i = varName.find('.');
610
  if (i == std::string::npos) {
611
    throw std::invalid_argument("Parameter " + varName + " not found.");
612
  } else {
613
    std::string name;
614
    if (varName[0] != 'f')
615
616
      throw std::invalid_argument(
          "External function parameter name must start with 'f'");
617

618
    std::string sindex = varName.substr(1, i - 1);
619
    size_t index = boost::lexical_cast<size_t>(sindex);
620
621
622
623

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

624
    return std::make_pair(varName.substr(i + 1), index);
625
  }
626
627
}

628
/** Returns the index of parameter i as it declared in its function
629
 * @param i :: The parameter index
630
 * @param recursive :: If true call parameterLocalName recursively until
631
 *    a non-composite function is reached.
632
633
 * @return The local index of the parameter
 */
634
size_t CompositeFunction::parameterLocalIndex(size_t i, bool recursive) const {
635
  size_t iFun = functionIndex(i);
636
637
  auto localIndex = i - m_paramOffsets[iFun];
  if (recursive) {
638
    auto cf = dynamic_cast<const CompositeFunction *>(m_functions[iFun].get());
639
640
641
642
643
    if (cf) {
      return cf->parameterLocalIndex(localIndex, recursive);
    }
  }
  return localIndex;
644
645
}

646
/** Returns the name of parameter i as it declared in its function
647
 * @param i :: The parameter index
648
 * @param recursive :: If true call parameterLocalName recursively until
649
 *    a non-composite function is reached.
650
651
 * @return The pure parameter name (without the function identifier f#.)
 */
652
653
std::string CompositeFunction::parameterLocalName(size_t i,
                                                  bool recursive) const {
654
  size_t iFun = functionIndex(i);
655
656
657
  auto localIndex = i - m_paramOffsets[iFun];
  auto localFunction = m_functions[iFun].get();
  if (recursive) {
658
    auto cf = dynamic_cast<const CompositeFunction *>(localFunction);
659
660
661
662
663
    if (cf) {
      return cf->parameterLocalName(localIndex, recursive);
    }
  }
  return localFunction->parameterName(localIndex);
664
665
}

666
/**
667
 * Apply the ties.
668
 */
669
void CompositeFunction::applyTies() {
670
671
672
673
674
675
676
  if (hasOrderedTies()) {
    applyOrderedTies();
  } else {
    for (size_t i = 0; i < nFunctions(); i++) {
      getFunction(i)->applyTies();
    }
    IFunction::applyTies();
677
678
679
680
  }
}

/**
681
 * Clear the ties.
682
 */
683
void CompositeFunction::clearTies() {
684
  IFunction::clearTies();
685
  for (size_t i = 0; i < nFunctions(); i++) {
686
687
688
689
690
    getFunction(i)->clearTies();
  }
}

/** Removes i-th parameter's tie if it is tied or does nothing.
691
 * @param i :: The index of the tied parameter.
692
 * @return True if successful
693
 */
694
bool CompositeFunction::removeTie(size_t i) {
695
696
697
698
699
700
701
  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;
702
703
704
}

/** Get the tie of i-th parameter
705
 * @param i :: The parameter index
706
 * @return A pointer to the tie.
707
 */
708
ParameterTie *CompositeFunction::getTie(size_t i) const {
709
710
711
712
713
714
  auto tie = IFunction::getTie(i);
  if (tie == nullptr) {
    size_t iFun = functionIndex(i);
    tie = m_functions[iFun]->getTie(i - m_paramOffsets[iFun]);
  }
  return tie;
715
716
717
}

/**
718
 * Declare a new parameter. To used in the implementation's constructor.
719
720
 * @param name :: The parameter name.
 * @param initValue :: The initial value for the parameter
721
 * @param description :: Parameter documentation
722
 */
723
724
725
726
727
728
729
730
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.");
731
732
}

733
734
735
/**
 * Prepare the function for a fit.
 */
736
void CompositeFunction::setUpForFit() {
737
  IFunction::setUpForFit();
738
  // set up the member functions
739
  for (size_t i = 0; i < nFunctions(); i++) {
740
    getFunction(i)->setUpForFit();
741
  }
742
743

  // Instead of automatically switching to numeric derivatives
744
  // log a warning about a danger of not using it
745
746
747
748
749
  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 "
750
                           "non-constant ties defined.\n";
751
752
        break;
      }
753
    }
754
755
756
  }
}

Nick Draper's avatar
re #100    
Nick Draper committed
757
/// Get constraint
758
/// @param i :: the index
Nick Draper's avatar
re #100    
Nick Draper committed
759
/// @return A pointer to the constraint
760
IConstraint *CompositeFunction::getConstraint(size_t i) const {
761
762
763
764
765
766
  auto constraint = IFunction::getConstraint(i);
  if (constraint == nullptr) {
    size_t iFun = functionIndex(i);
    constraint = m_functions[iFun]->getConstraint(i - m_paramOffsets[iFun]);
  }
  return constraint;
767
768
}

769
/** Remove a constraint
770
 * @param parName :: The name of a parameter which constraint to remove.
771
 */
772
void CompositeFunction::removeConstraint(const std::string &parName) {
773
774
775
776
777
778
779
780
781
  auto i = parameterIndex(parName);
  auto constraint = IFunction::getConstraint(i);
  if (constraint != nullptr) {
    IFunction::removeConstraint(parName);
  } else {
    size_t iPar = parameterIndex(parName);
    size_t iFun = functionIndex(iPar);
    getFunction(iFun)->removeConstraint(parameterLocalName(iPar));
  }
782
783
}

784
/** Checks if a constraint has been explicitly set
785
 *  @param i :: The parameter index
786
 *  @return true if the function is explicitly set
787
 */
788
bool CompositeFunction::isExplicitlySet(size_t i) const {
789
  size_t iFun = functionIndex(i);
790
  return m_functions[iFun]->isExplicitlySet(i - m_paramOffsets[iFun]);
791
792
}

793
/**
794
795
 * Returns the index of parameter if the ref points to one of the member
 * function
796
 * @param ref :: A reference to a parameter
797
 * @return Parameter index or number of nParams() if parameter not found
798
 */
799
800
size_t
CompositeFunction::getParameterIndex(const ParameterReference &ref) const {
801
802
  if (ref.getLocalFunction() == this && ref.getLocalIndex() < nParams()) {
    return ref.getLocalIndex();
803
  }
804
  for (size_t iFun = 0; iFun < nFunctions(); iFun++) {
805
    IFunction_sptr fun = getFunction(iFun);
806
    size_t iLocalIndex = fun->getParameterIndex(ref);
807
    if (iLocalIndex < fun->nParams()) {
808
809
810
      return m_paramOffsets[iFun] + iLocalIndex;
    }
  }
811
  return nParams();