CompositeFunction.cpp 26.1 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");
LamarMoore's avatar
LamarMoore committed
30
} // namespace
Gigg, Martyn Anthony's avatar
Gigg, Martyn Anthony committed
31

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

34
DECLARE_FUNCTION(CompositeFunction)
35

36
/// Default constructor
37
38
CompositeFunction::CompositeFunction()
    : IFunction(), m_nParams(0), m_iConstraintFunction(false) {
39
40
41
  declareAttribute("NumDeriv", Attribute(false));
}

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

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

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

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

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

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

120
121
122
  return ostr.str();
}

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

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

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

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

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

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

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

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

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

258
259
/**
 * Parameters by name.
260
 * @param name :: The name of the parameter.
261
 * @return value of the requested named parameter
262
 */
263
double CompositeFunction::getParameter(const std::string &name) const {
264
  std::string pname;
265
  size_t index;
266
  parseName(name, index, pname);
267
  return getFunction(index)->getParameter(pname);
268
269
270
}

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

273
/**
274
 *
275
 * @param name :: The name of a parameter
276
 * @return index of the requested named parameter
277
 */
278
size_t CompositeFunction::parameterIndex(const std::string &name) const {
279
  std::string pname;
280
  size_t index;
281
  parseName(name, index, pname);
282
  return getFunction(index)->parameterIndex(pname) + m_paramOffsets[index];
283
284
}

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

296
297
298
/// Returns the description of parameter
/// @param i :: The index
/// @return The description of the parameter
299
std::string CompositeFunction::parameterDescription(size_t i) const {
300
  size_t iFun = functionIndex(i);
301
  std::ostringstream ostr;
302
  ostr << m_functions[iFun]->parameterDescription(i - m_paramOffsets[iFun]);
303
304
305
  return ostr.str();
}

306
/**
307
308
309
310
 * Get the fitting error for a parameter
 * @param i :: The index of a parameter
 * @return :: the error
 */
311
double CompositeFunction::getError(size_t i) const {
312
  size_t iFun = functionIndex(i);
313
  return m_functions[iFun]->getError(i - m_paramOffsets[iFun]);
314
315
}

316
/**
317
318
319
320
 * Set the fitting error for a parameter
 * @param i :: The index of a parameter
 * @param err :: The error value to set
 */
321
void CompositeFunction::setError(size_t i, double err) {
322
  size_t iFun = functionIndex(i);
323
  m_functions[iFun]->setError(i - m_paramOffsets[iFun], err);
324
325
}

326
327
328
/// Value of i-th active parameter. Override this method to make fitted
/// parameters different from the declared
double CompositeFunction::activeParameter(size_t i) const {
329
  size_t iFun = functionIndex(i);
330
  return m_functions[iFun]->activeParameter(i - m_paramOffsets[iFun]);
331
332
}

333
334
335
/// 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) {
336
  size_t iFun = functionIndex(i);
337
  m_functions[iFun]->setActiveParameter(i - m_paramOffsets[iFun], value);
338
339
340
}

/// Returns the name of active parameter i
341
std::string CompositeFunction::nameOfActive(size_t i) const {
342
  size_t iFun = functionIndex(i);
343
  std::ostringstream ostr;
344
345
  ostr << 'f' << iFun << '.'
       << m_functions[iFun]->nameOfActive(i - m_paramOffsets[iFun]);
346
347
348
349
  return ostr.str();
}

/// Returns the description of active parameter i
350
std::string CompositeFunction::descriptionOfActive(size_t i) const {
351
  size_t iFun = functionIndex(i);
352
  std::ostringstream ostr;
353
  ostr << m_functions[iFun]->descriptionOfActive(i - m_paramOffsets[iFun]);
354
  return ostr.str();
355
356
}

357
/// Change status of parameter
358
359
void CompositeFunction::setParameterStatus(size_t i,
                                           IFunction::ParameterStatus status) {
360
  size_t iFun = functionIndex(i);
361
  m_functions[iFun]->setParameterStatus(i - m_paramOffsets[iFun], status);
362
363
}

364
/// Get status of parameter
365
366
IFunction::ParameterStatus
CompositeFunction::getParameterStatus(size_t i) const {
367
  size_t iFun = functionIndex(i);
368
  return m_functions[iFun]->getParameterStatus(i - m_paramOffsets[iFun]);
369
370
}

371
/** Makes sure that the function is consistent.
372
 */
373
void CompositeFunction::checkFunction() {
374
375
  m_nParams = 0;
  m_paramOffsets.clear();
376
  m_IFunction.clear();
377

378
  std::vector<IFunction_sptr> functions(m_functions.begin(), m_functions.end());
379
380
  m_functions.clear();

Hahn, Steven's avatar
Hahn, Steven committed
381
  for (auto &f : functions) {
382
    CompositeFunction_sptr cf = std::dynamic_pointer_cast<CompositeFunction>(f);
383
384
    if (cf)
      cf->checkFunction();
385
386
387
388
    addFunction(f);
  }
}

389
/**
390
391
392
393
394
395
396
397
398
 * Remove all member functions
 */
void CompositeFunction::clear() {
  m_nParams = 0;
  m_paramOffsets.clear();
  m_IFunction.clear();
  m_functions.clear();
}

399
/** Add a function
400
 * @param f :: A pointer to the added function
401
 * @return The function index
402
 */
403
404
size_t CompositeFunction::addFunction(IFunction_sptr f) {
  m_IFunction.insert(m_IFunction.end(), f->nParams(), m_functions.size());
405
  m_functions.emplace_back(f);
406
  //?f->init();
407
  if (m_paramOffsets.empty()) {
408
    m_paramOffsets.emplace_back(0);
409
    m_nParams = f->nParams();
410
  } else {
411
    m_paramOffsets.emplace_back(m_nParams);
412
413
    m_nParams += f->nParams();
  }
414
  return m_functions.size() - 1;
415
416
}

417
/** Remove a function
418
 * @param i :: The index of the function to remove
419
 */
420
void CompositeFunction::removeFunction(size_t i) {
421
422
  if (i >= nFunctions()) {
    throw std::out_of_range("Function index (" + std::to_string(i) +
423
424
                            ") out of range (" + std::to_string(nFunctions()) +
                            ").");
425
  }
426

427
  IFunction_sptr fun = getFunction(i);
428
  // Reduction in parameters
429
  size_t dnp = fun->nParams();
430

431
432
433
  for (size_t j = 0; j < nParams();) {
    ParameterTie *tie = getTie(j);
    if (tie && tie->findParametersOf(fun.get())) {
434
      removeTie(j);
435
    } else {
436
437
438
439
440
      j++;
    }
  }

  // Shift down the function indeces for parameters
441
  for (auto it = m_IFunction.begin(); it != m_IFunction.end();) {
442

443
    if (*it == i) {
444
      it = m_IFunction.erase(it);
445
446
    } else {
      if (*it > i) {
447
448
        *it -= 1;
      }
449
      ++it;
450
451
452
453
    }
  }

  m_nParams -= dnp;
454
455
456
  // Shift the parameter offsets down by the total number of i-th function's
  // params
  for (size_t j = i + 1; j < nFunctions(); j++) {
457
458
    m_paramOffsets[j] -= dnp;
  }
459
  m_paramOffsets.erase(m_paramOffsets.begin() + i);
460

461
  m_functions.erase(m_functions.begin() + i);
462
463
}

464
/** Replace a function with a new one. The old function is deleted.
465
 *  The new function must have already its workspace set.
466
 * @param f_old :: The pointer to the function to replace. If it's not
467
 *  a member of this composite function nothing happens
468
 * @param f_new :: A pointer to the new function
469
 */
David Fairbrother's avatar
David Fairbrother committed
470
471
void CompositeFunction::replaceFunctionPtr(const IFunction_sptr &f_old,
                                           const IFunction_sptr &f_new) {
472
473
474
475
  std::vector<IFunction_sptr>::const_iterator it =
      std::find(m_functions.begin(), m_functions.end(), f_old);
  if (it == m_functions.end())
    return;
476
  std::vector<IFunction_sptr>::difference_type iFun = it - m_functions.begin();
David Fairbrother's avatar
David Fairbrother committed
477
  replaceFunction(iFun, std::move(f_new));
478
479
}

480
/** Replace a function with a new one. The old function is deleted.
481
482
 * @param i :: The index of the function to replace
 * @param f :: A pointer to the new function
483
 */
David Fairbrother's avatar
David Fairbrother committed
484
void CompositeFunction::replaceFunction(size_t i, const IFunction_sptr &f) {
485
486
  if (i >= nFunctions()) {
    throw std::out_of_range("Function index (" + std::to_string(i) +
487
488
                            ") out of range (" + std::to_string(nFunctions()) +
                            ").");
489
  }
490

491
  IFunction_sptr fun = getFunction(i);
492
  size_t np_old = fun->nParams();
493

494
  size_t np_new = f->nParams();
495

496
497
  // Modify function indeces: The new function may have different number of
  // parameters
498
  {
499
    auto itFun = std::find(m_IFunction.begin(), m_IFunction.end(), i);
500
    if (itFun != m_IFunction.end()) // functions must have at least 1 parameter
501
    {
502
503
504
505
      if (np_old > np_new) {
        m_IFunction.erase(itFun, itFun + np_old - np_new);
      } else if (np_old < np_new) {
        m_IFunction.insert(itFun, np_new - np_old, i);
506
      }
507
508
    } else if (np_new > 0) // it could happen if the old function is an empty
                           // CompositeFunction
509
    {
510
      using std::placeholders::_1;
511
      itFun = std::find_if(m_IFunction.begin(), m_IFunction.end(),
512
                           std::bind(std::greater<size_t>(), _1, i));
513
      m_IFunction.insert(itFun, np_new, i);
514
    }
515
516
  }

517
  size_t dnp = np_new - np_old;
518
  m_nParams += dnp;
519
520
521
  // Shift the parameter offsets down by the total number of i-th function's
  // params
  for (size_t j = i + 1; j < nFunctions(); j++) {
522
523
524
525
526
527
    m_paramOffsets[j] += dnp;
  }

  m_functions[i] = f;
}

528
/**
529
 * @param i :: The index of the function
530
 * @return function at the requested index
531
 */
532
533
IFunction_sptr CompositeFunction::getFunction(std::size_t i) const {
  if (i >= nFunctions()) {
534
    throw std::out_of_range("Function index (" + std::to_string(i) +
535
536
                            ") out of range (" + std::to_string(nFunctions()) +
                            ").");
537
  }
538
539
540
  return m_functions[i];
}

541
542
/**
 * Get the index of the function to which parameter i belongs
543
 * @param i :: The parameter index
544
 * @return function index of the requested parameter
545
 */
546
547
size_t CompositeFunction::functionIndex(std::size_t i) const {
  if (i >= nParams()) {
548
    throw std::out_of_range("Function parameter index (" + std::to_string(i) +
549
550
                            ") out of range (" + std::to_string(nParams()) +
                            ").");
551
  }
552
  return m_IFunction[i];
553
554
555
}

/**
LamarMoore's avatar
LamarMoore committed
556
557
558
559
560
 * @param varName :: The variable name which may contain function index (
 * [f<index.>]name )
 * @param index :: Receives function index or throws std::invalid_argument
 * @param name :: Receives the parameter name
 */
561
562
void CompositeFunction::parseName(const std::string &varName, size_t &index,
                                  std::string &name) {
563
  size_t i = varName.find('.');
564
  if (i == std::string::npos) {
565
    throw std::invalid_argument("Parameter " + varName + " not found.");
566
  } else {
567
    if (varName[0] != 'f')
568
569
      throw std::invalid_argument(
          "External function parameter name must start with 'f'");
570

571
    std::string sindex = varName.substr(1, i - 1);
572
573
574
575
576
    index = boost::lexical_cast<int>(sindex);

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

577
    name = varName.substr(i + 1);
578
  }
579
580
}

581
/** Returns the index of parameter i as it declared in its function
582
 * @param i :: The parameter index
583
584
 * @param recursive :: If true call parameterLocalName recusively until
 *    a non-composite function is reached.
585
586
 * @return The local index of the parameter
 */
587
size_t CompositeFunction::parameterLocalIndex(size_t i, bool recursive) const {
588
  size_t iFun = functionIndex(i);
589
590
  auto localIndex = i - m_paramOffsets[iFun];
  if (recursive) {
591
    auto cf = dynamic_cast<const CompositeFunction *>(m_functions[iFun].get());
592
593
594
595
596
    if (cf) {
      return cf->parameterLocalIndex(localIndex, recursive);
    }
  }
  return localIndex;
597
598
}

599
/** Returns the name of parameter i as it declared in its function
600
 * @param i :: The parameter index
601
602
 * @param recursive :: If true call parameterLocalName recusively until
 *    a non-composite function is reached.
603
604
 * @return The pure parameter name (without the function identifier f#.)
 */
605
606
std::string CompositeFunction::parameterLocalName(size_t i,
                                                  bool recursive) const {
607
  size_t iFun = functionIndex(i);
608
609
610
  auto localIndex = i - m_paramOffsets[iFun];
  auto localFunction = m_functions[iFun].get();
  if (recursive) {
611
    auto cf = dynamic_cast<const CompositeFunction *>(localFunction);
612
613
614
615
616
    if (cf) {
      return cf->parameterLocalName(localIndex, recursive);
    }
  }
  return localFunction->parameterName(localIndex);
617
618
}

619
/**
620
 * Apply the ties.
621
 */
622
void CompositeFunction::applyTies() {
623
624
625
626
627
628
629
  if (hasOrderedTies()) {
    applyOrderedTies();
  } else {
    for (size_t i = 0; i < nFunctions(); i++) {
      getFunction(i)->applyTies();
    }
    IFunction::applyTies();
630
631
632
633
  }
}

/**
634
 * Clear the ties.
635
 */
636
void CompositeFunction::clearTies() {
637
  IFunction::clearTies();
638
  for (size_t i = 0; i < nFunctions(); i++) {
639
640
641
642
643
    getFunction(i)->clearTies();
  }
}

/** Removes i-th parameter's tie if it is tied or does nothing.
644
 * @param i :: The index of the tied parameter.
645
646
 * @return True if successfull
 */
647
bool CompositeFunction::removeTie(size_t i) {
648
649
650
651
652
653
654
  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;
655
656
657
}

/** Get the tie of i-th parameter
658
 * @param i :: The parameter index
659
 * @return A pointer to the tie.
660
 */
661
ParameterTie *CompositeFunction::getTie(size_t i) const {
662
663
664
665
666
667
  auto tie = IFunction::getTie(i);
  if (tie == nullptr) {
    size_t iFun = functionIndex(i);
    tie = m_functions[iFun]->getTie(i - m_paramOffsets[iFun]);
  }
  return tie;
668
669
670
671
}

/**
 * Declare a new parameter. To used in the implementation'c constructor.
672
673
 * @param name :: The parameter name.
 * @param initValue :: The initial value for the parameter
674
 * @param description :: Parameter documentation
675
 */
676
677
678
679
680
681
682
683
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.");
684
685
}

686
687
688
/**
 * Prepare the function for a fit.
 */
689
void CompositeFunction::setUpForFit() {
690
  IFunction::setUpForFit();
691
  // set up the member functions
692
  for (size_t i = 0; i < nFunctions(); i++) {
693
    getFunction(i)->setUpForFit();
694
  }
695
696
697
698
  // unfortuately the code below breaks some system tests (IRISFuryAndFuryFit)
  // it looks as if using numeric derivatives can give different fit results
  // to fit with analytical ones
  //
699
  // if parameters have non-constant ties enable numerical derivatives
700
  // for(size_t i = 0; i < nParams(); ++i)
701
702
703
704
705
706
707
708
709
710
711
  //{
  //  ParameterTie* tie = getTie( i );
  //  if ( tie && !tie->isConstant() )
  //  {
  //    useNumericDerivatives( true );
  //    break;
  //  }
  //}

  // instead of automatically switching to numeric derivatives
  // log a warning about a danger of not using it
712
713
714
715
716
  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 "
717
                           "non-constant ties defined.\n";
718
719
        break;
      }
720
    }
721
722
723
  }
}

Nick Draper's avatar
re #100    
Nick Draper committed
724
/// Get constraint
725
/// @param i :: the index
Nick Draper's avatar
re #100    
Nick Draper committed
726
/// @return A pointer to the constraint
727
IConstraint *CompositeFunction::getConstraint(size_t i) const {
728
729
730
731
732
733
  auto constraint = IFunction::getConstraint(i);
  if (constraint == nullptr) {
    size_t iFun = functionIndex(i);
    constraint = m_functions[iFun]->getConstraint(i - m_paramOffsets[iFun]);
  }
  return constraint;
734
735
}

736
/** Remove a constraint
737
 * @param parName :: The name of a parameter which constarint to remove.
738
 */
739
void CompositeFunction::removeConstraint(const std::string &parName) {
740
741
742
743
744
745
746
747
748
  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));
  }
749
750
}

751
/** Checks if a constraint has been explicitly set
752
 *  @param i :: The parameter index
753
 *  @return true if the function is explicitly set
754
 */
755
bool CompositeFunction::isExplicitlySet(size_t i) const {
756
  size_t iFun = functionIndex(i);
757
  return m_functions[iFun]->isExplicitlySet(i - m_paramOffsets[iFun]);
758
759
}

760
/**
761
762
 * Returns the index of parameter if the ref points to one of the member
 * function
763
 * @param ref :: A reference to a parameter
764
 * @return Parameter index or number of nParams() if parameter not found
765
 */
766
767
size_t
CompositeFunction::getParameterIndex(const ParameterReference &ref) const {
768
769
  if (ref.getLocalFunction() == this && ref.getLocalIndex() < nParams()) {
    return ref.getLocalIndex();
770
  }
771
  for (size_t iFun = 0; iFun < nFunctions(); iFun++) {
772
    IFunction_sptr fun = getFunction(iFun);
773
    size_t iLocalIndex = fun->getParameterIndex(ref);
774
    if (iLocalIndex < fun->nParams()) {
775
776
777
      return m_paramOffsets[iFun] + iLocalIndex;
    }
  }
778
  return nParams();
779
780
}

781
/**
782
 * Returns the shrared pointer to the function conataining a parameter
783
 * @param ref :: The reference
784
785
 * @return A function containing parameter pointed to by ref
 */
786
787
788
IFunction_sptr
CompositeFunction::getContainingFunction(const ParameterReference &ref) const {
  for (size_t iFun = 0; iFun < nFunctions(); iFun++) {
789
    IFunction_sptr fun = getFunction(iFun);
790
    if (fun->getParameterIndex(ref) < fun->nParams()) {
791
      return fun;
792
793
    }
  }
794
795
796
  return IFunction_sptr();
}

797
798
799
800
801