CompositeFunction.cpp 24.8 KB
Newer Older
1
2
3
//----------------------------------------------------------------------
// Includes
//----------------------------------------------------------------------
4
5
#include "MantidAPI/CompositeFunction.h"
#include "MantidAPI/ParameterTie.h"
6
#include "MantidAPI/IConstraint.h"
7
#include "MantidAPI/FunctionFactory.h"
Gigg, Martyn Anthony's avatar
Gigg, Martyn Anthony committed
8
9
#include "MantidKernel/Exception.h"
#include "MantidKernel/Logger.h"
10
#include "MantidKernel/Strings.h"
Gigg, Martyn Anthony's avatar
Gigg, Martyn Anthony committed
11

12
#include <boost/lexical_cast.hpp>
13
14
#include <boost/shared_array.hpp>
#include <sstream>
15
#include <algorithm>
16

17
18
namespace Mantid {
namespace API {
19

20
21
22
23
namespace {
/// static logger
Kernel::Logger g_log("CompositeFunction");
}
Gigg, Martyn Anthony's avatar
Gigg, Martyn Anthony committed
24

Peterson, Peter's avatar
Peterson, Peter committed
25
26
using std::size_t;

27
DECLARE_FUNCTION(CompositeFunction)
28

29
/// Default constructor
30
31
CompositeFunction::CompositeFunction()
    : IFunction(), m_nParams(0), m_iConstraintFunction(false) {
32
33
34
  declareAttribute("NumDeriv", Attribute(false));
}

35
/// Function initialization. Declare function parameters in this method.
36
void CompositeFunction::init() {}
37

38
/**
39
40
41
42
43
44
45
46
47
48
 * Writes itself into a string. Functions derived from CompositeFunction must
 * override this method with something like this:
 *   std::string NewFunction::asString()const
 *   {
 *      ostr << "composite=" << this->name() << ';';
 *      // write NewFunction's own attributes and parameters
 *      ostr << CompositeFunction::asString();
 *      // write NewFunction's own ties and constraints
 *      // ostr << ";constraints=(" << ... <<")";
 *   }
49
 * @return the string representation of the composite function
50
 */
51
std::string CompositeFunction::asString() const {
52
  std::ostringstream ostr;
53

54
  // if empty just return function name
55
  if (nFunctions() == 0) {
56
57
58
    return "name=" + name();
  }

59
  if (name() != "CompositeFunction" || nAttributes() > 1 ||
60
      getAttribute("NumDeriv").asBool()) {
61
    ostr << "composite=" << name();
62
    std::vector<std::string> attr = this->getAttributeNames();
63
64
    for (const auto &attName : attr) {
      std::string attValue = this->getAttribute(attName).value();
65
66
      if (!attValue.empty()) {
        ostr << ',' << attName << '=' << attValue;
67
68
69
      }
    }
    ostr << ';';
70
  }
71
  for (size_t i = 0; i < nFunctions(); i++) {
72
    const auto localAttr = this->getLocalAttributeNames();
73
    IFunction_sptr fun = getFunction(i);
Hahn, Steven's avatar
Hahn, Steven committed
74
75
    bool isComp =
        boost::dynamic_pointer_cast<CompositeFunction>(fun) != nullptr;
76
77
    if (isComp)
      ostr << '(';
78
    ostr << fun->asString();
79
80
81
82
83
84
85
86
    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
        ostr << ',' << '$' << localAttName << '=' << localAttValue;
      }
    }
87
88
89
    if (isComp)
      ostr << ')';
    if (i < nFunctions() - 1) {
90
91
92
      ostr << ';';
    }
  }
93
94
95
96
97
98

  // collect non-default constraints
  std::string constraints = writeConstraints();
  // print constraints
  if (!constraints.empty()) {
    ostr << ";constraints=(" << constraints << ")";
99
  }
100
101
102
103

  // collect the non-default ties
  std::string ties = writeTies();
  // print the ties
104
  if (!ties.empty()) {
105
    ostr << ";ties=(" << ties << ")";
106
  }
107

108
109
110
  return ostr.str();
}

111
112
113
/**
 * @param ws A pointer to the workspace being fitted
 */
114
void CompositeFunction::setWorkspace(boost::shared_ptr<const Workspace> ws) {
115
116
  // Pass it on to each member
  auto iend = m_functions.end();
117
  for (auto it = m_functions.begin(); it != iend; ++it) {
118
119
120
121
    (*it)->setWorkspace(ws);
  }
}

122
123
124
125
126
127
/**
 * @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.
 */
128
129
130
131
132
133
void CompositeFunction::setMatrixWorkspace(
    boost::shared_ptr<const MatrixWorkspace> workspace, size_t wi,
    double startX, double endX) {
  for (size_t iFun = 0; iFun < nFunctions(); ++iFun) {
    m_functions[iFun]->setMatrixWorkspace(workspace, wi, startX, endX);
  }
134
135
}

136
/** Function you want to fit to.
137
 *  @param domain :: An instance of FunctionDomain with the function arguments.
138
139
 *  @param values :: A FunctionValues instance for storing the calculated
 * values.
140
 */
141
142
void CompositeFunction::function(const FunctionDomain &domain,
                                 FunctionValues &values) const {
143
  FunctionValues tmp(domain);
144
  values.zeroCalculated();
145
146
  for (size_t iFun = 0; iFun < nFunctions(); ++iFun) {
    m_functions[iFun]->function(domain, tmp);
147
148
149
150
151
152
153
154
155
    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.
 */
156
157
158
void CompositeFunction::functionDeriv(const FunctionDomain &domain,
                                      Jacobian &jacobian) {
  if (getAttribute("NumDeriv").asBool()) {
159
    calNumericalDeriv(domain, jacobian);
160
161
162
163
  } else {
    for (size_t iFun = 0; iFun < nFunctions(); ++iFun) {
      PartialJacobian J(&jacobian, paramOffset(iFun));
      getFunction(iFun)->functionDeriv(domain, J);
164
    }
165
166
167
  }
}

168
/** Sets a new value to the i-th parameter.
169
170
 *  @param i :: The parameter index
 *  @param value :: The new value
171
172
 *  @param explicitlySet :: A boolean falgging the parameter as explicitly set
 * (by user)
173
 */
174
175
void CompositeFunction::setParameter(size_t i, const double &value,
                                     bool explicitlySet) {
176
  size_t iFun = functionIndex(i);
177
178
  m_functions[iFun]->setParameter(i - m_paramOffsets[iFun], value,
                                  explicitlySet);
179
180
}

181
182
183
184
/** Sets a new description to the i-th parameter.
 *  @param i :: The parameter index
 *  @param description :: The new description
 */
185
186
void CompositeFunction::setParameterDescription(
    size_t i, const std::string &description) {
187
  size_t iFun = functionIndex(i);
188
189
  m_functions[iFun]->setParameterDescription(i - m_paramOffsets[iFun],
                                             description);
190
191
}

192
/** Get the i-th parameter.
193
 *  @param i :: The parameter index
194
 *  @return value of the requested parameter
195
 */
196
double CompositeFunction::getParameter(size_t i) const {
Peterson, Peter's avatar
Peterson, Peter committed
197
  size_t iFun = functionIndex(i);
198
  return m_functions[iFun]->getParameter(i - m_paramOffsets[iFun]);
199
200
}

201
202
/**
 * Sets a new value to a parameter by name.
203
204
 * @param name :: The name of the parameter.
 * @param value :: The new value
205
206
 * @param explicitlySet :: A boolean falgging the parameter as explicitly set
 * (by user)
207
 */
208
209
void CompositeFunction::setParameter(const std::string &name,
                                     const double &value, bool explicitlySet) {
210
  std::string pname;
211
  size_t index;
212
213
  parseName(name, index, pname);
  getFunction(index)->setParameter(pname, value, explicitlySet);
214
215
}

216
217
218
/**
 * Sets a new description to a parameter by name.
 * @param name :: The name of the parameter.
219
 * @param description :: The new description
220
 */
221
222
void CompositeFunction::setParameterDescription(
    const std::string &name, const std::string &description) {
223
  std::string pname;
224
  size_t index;
225
226
  parseName(name, index, pname);
  getFunction(index)->setParameterDescription(pname, description);
227
228
}

229
230
/**
 * Parameters by name.
231
 * @param name :: The name of the parameter.
232
 * @return value of the requested named parameter
233
 */
234
double CompositeFunction::getParameter(const std::string &name) const {
235
  std::string pname;
236
  size_t index;
237
  parseName(name, index, pname);
238
  return getFunction(index)->getParameter(pname);
239
240
241
}

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

244
/**
245
 *
246
 * @param name :: The name of a parameter
247
 * @return index of the requested named parameter
248
 */
249
size_t CompositeFunction::parameterIndex(const std::string &name) const {
250
  std::string pname;
251
  size_t index;
252
  parseName(name, index, pname);
253
  return getFunction(index)->parameterIndex(pname) + m_paramOffsets[index];
254
255
}

Nick Draper's avatar
re #100    
Nick Draper committed
256
/// Returns the name of parameter
257
/// @param i :: The index
Nick Draper's avatar
re #100    
Nick Draper committed
258
/// @return The name of the parameter
259
std::string CompositeFunction::parameterName(size_t i) const {
260
  size_t iFun = functionIndex(i);
261
  std::ostringstream ostr;
262
263
  ostr << 'f' << iFun << '.'
       << m_functions[iFun]->parameterName(i - m_paramOffsets[iFun]);
264
  return ostr.str();
265
266
}

267
268
269
/// Returns the description of parameter
/// @param i :: The index
/// @return The description of the parameter
270
std::string CompositeFunction::parameterDescription(size_t i) const {
271
  size_t iFun = functionIndex(i);
272
  std::ostringstream ostr;
273
  ostr << m_functions[iFun]->parameterDescription(i - m_paramOffsets[iFun]);
274
275
276
  return ostr.str();
}

277
/**
278
279
280
281
 * Get the fitting error for a parameter
 * @param i :: The index of a parameter
 * @return :: the error
 */
282
double CompositeFunction::getError(size_t i) const {
283
  size_t iFun = functionIndex(i);
284
  return m_functions[iFun]->getError(i - m_paramOffsets[iFun]);
285
286
}

287
/**
288
289
290
291
 * Set the fitting error for a parameter
 * @param i :: The index of a parameter
 * @param err :: The error value to set
 */
292
void CompositeFunction::setError(size_t i, double err) {
293
  size_t iFun = functionIndex(i);
294
  m_functions[iFun]->setError(i - m_paramOffsets[iFun], err);
295
296
}

297
298
299
/// Value of i-th active parameter. Override this method to make fitted
/// parameters different from the declared
double CompositeFunction::activeParameter(size_t i) const {
300
  size_t iFun = functionIndex(i);
301
  return m_functions[iFun]->activeParameter(i - m_paramOffsets[iFun]);
302
303
}

304
305
306
/// 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) {
307
  size_t iFun = functionIndex(i);
308
  m_functions[iFun]->setActiveParameter(i - m_paramOffsets[iFun], value);
309
310
311
}

/// Returns the name of active parameter i
312
std::string CompositeFunction::nameOfActive(size_t i) const {
313
  size_t iFun = functionIndex(i);
314
  std::ostringstream ostr;
315
316
  ostr << 'f' << iFun << '.'
       << m_functions[iFun]->nameOfActive(i - m_paramOffsets[iFun]);
317
318
319
320
  return ostr.str();
}

/// Returns the description of active parameter i
321
std::string CompositeFunction::descriptionOfActive(size_t i) const {
322
  size_t iFun = functionIndex(i);
323
  std::ostringstream ostr;
324
  ostr << m_functions[iFun]->descriptionOfActive(i - m_paramOffsets[iFun]);
325
  return ostr.str();
326
327
}

328
/// Change status of parameter
329
330
void CompositeFunction::setParameterStatus(size_t i,
                                           IFunction::ParameterStatus status) {
331
  size_t iFun = functionIndex(i);
332
  m_functions[iFun]->setParameterStatus(i - m_paramOffsets[iFun], status);
333
334
}

335
/// Get status of parameter
336
337
IFunction::ParameterStatus
CompositeFunction::getParameterStatus(size_t i) const {
338
  size_t iFun = functionIndex(i);
339
  return m_functions[iFun]->getParameterStatus(i - m_paramOffsets[iFun]);
340
341
}

342
/** Makes sure that the function is consistent.
343
 */
344
void CompositeFunction::checkFunction() {
345
346
  m_nParams = 0;
  m_paramOffsets.clear();
347
  m_IFunction.clear();
348

349
  std::vector<IFunction_sptr> functions(m_functions.begin(), m_functions.end());
350
351
  m_functions.clear();

Hahn, Steven's avatar
Hahn, Steven committed
352
  for (auto &f : functions) {
353
354
355
356
    CompositeFunction_sptr cf =
        boost::dynamic_pointer_cast<CompositeFunction>(f);
    if (cf)
      cf->checkFunction();
357
358
359
360
    addFunction(f);
  }
}

361
/**
362
363
364
365
366
367
368
369
370
 * Remove all member functions
 */
void CompositeFunction::clear() {
  m_nParams = 0;
  m_paramOffsets.clear();
  m_IFunction.clear();
  m_functions.clear();
}

371
/** Add a function
372
 * @param f :: A pointer to the added function
373
 * @return The function index
374
 */
375
376
size_t CompositeFunction::addFunction(IFunction_sptr f) {
  m_IFunction.insert(m_IFunction.end(), f->nParams(), m_functions.size());
377
378
  m_functions.push_back(f);
  //?f->init();
379
  if (m_paramOffsets.empty()) {
380
381
    m_paramOffsets.push_back(0);
    m_nParams = f->nParams();
382
  } else {
383
384
385
    m_paramOffsets.push_back(m_nParams);
    m_nParams += f->nParams();
  }
386
  return m_functions.size() - 1;
387
388
}

389
/** Remove a function
390
 * @param i :: The index of the function to remove
391
 */
392
void CompositeFunction::removeFunction(size_t i) {
393
394
  if (i >= nFunctions()) {
    throw std::out_of_range("Function index (" + std::to_string(i) +
395
396
                            ") out of range (" + std::to_string(nFunctions()) +
                            ").");
397
  }
398

399
  IFunction_sptr fun = getFunction(i);
400
  // Reduction in parameters
401
  size_t dnp = fun->nParams();
402

403
404
405
  for (size_t j = 0; j < nParams();) {
    ParameterTie *tie = getTie(j);
    if (tie && tie->findParametersOf(fun.get())) {
406
      removeTie(j);
407
    } else {
408
409
410
411
412
      j++;
    }
  }

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

415
    if (*it == i) {
416
      it = m_IFunction.erase(it);
417
418
    } else {
      if (*it > i) {
419
420
        *it -= 1;
      }
421
      ++it;
422
423
424
425
    }
  }

  m_nParams -= dnp;
426
427
428
  // Shift the parameter offsets down by the total number of i-th function's
  // params
  for (size_t j = i + 1; j < nFunctions(); j++) {
429
430
    m_paramOffsets[j] -= dnp;
  }
431
  m_paramOffsets.erase(m_paramOffsets.begin() + i);
432

433
  m_functions.erase(m_functions.begin() + i);
434
435
}

436
/** Replace a function with a new one. The old function is deleted.
437
 *  The new function must have already its workspace set.
438
 * @param f_old :: The pointer to the function to replace. If it's not
439
 *  a member of this composite function nothing happens
440
 * @param f_new :: A pointer to the new function
441
 */
442
443
444
445
446
447
void CompositeFunction::replaceFunctionPtr(const IFunction_sptr f_old,
                                           IFunction_sptr f_new) {
  std::vector<IFunction_sptr>::const_iterator it =
      std::find(m_functions.begin(), m_functions.end(), f_old);
  if (it == m_functions.end())
    return;
448
  std::vector<IFunction_sptr>::difference_type iFun = it - m_functions.begin();
449
  replaceFunction(iFun, f_new);
450
451
}

452
/** Replace a function with a new one. The old function is deleted.
453
454
 * @param i :: The index of the function to replace
 * @param f :: A pointer to the new function
455
 */
456
void CompositeFunction::replaceFunction(size_t i, IFunction_sptr f) {
457
458
  if (i >= nFunctions()) {
    throw std::out_of_range("Function index (" + std::to_string(i) +
459
460
                            ") out of range (" + std::to_string(nFunctions()) +
                            ").");
461
  }
462

463
  IFunction_sptr fun = getFunction(i);
464
  size_t np_old = fun->nParams();
465

466
  size_t np_new = f->nParams();
467

468
469
  // Modify function indeces: The new function may have different number of
  // parameters
470
  {
471
    auto itFun = std::find(m_IFunction.begin(), m_IFunction.end(), i);
472
    if (itFun != m_IFunction.end()) // functions must have at least 1 parameter
473
    {
474
475
476
477
      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);
478
      }
479
480
    } else if (np_new > 0) // it could happen if the old function is an empty
                           // CompositeFunction
481
    {
482
483
484
      itFun = std::find_if(m_IFunction.begin(), m_IFunction.end(),
                           std::bind2nd(std::greater<size_t>(), i));
      m_IFunction.insert(itFun, np_new, i);
485
    }
486
487
  }

488
  size_t dnp = np_new - np_old;
489
  m_nParams += dnp;
490
491
492
  // Shift the parameter offsets down by the total number of i-th function's
  // params
  for (size_t j = i + 1; j < nFunctions(); j++) {
493
494
495
496
497
498
    m_paramOffsets[j] += dnp;
  }

  m_functions[i] = f;
}

499
/**
500
 * @param i :: The index of the function
501
 * @return function at the requested index
502
 */
503
504
IFunction_sptr CompositeFunction::getFunction(std::size_t i) const {
  if (i >= nFunctions()) {
505
    throw std::out_of_range("Function index (" + std::to_string(i) +
506
507
                            ") out of range (" + std::to_string(nFunctions()) +
                            ").");
508
  }
509
510
511
  return m_functions[i];
}

512
513
/**
 * Get the index of the function to which parameter i belongs
514
 * @param i :: The parameter index
515
 * @return function index of the requested parameter
516
 */
517
518
size_t CompositeFunction::functionIndex(std::size_t i) const {
  if (i >= nParams()) {
519
    throw std::out_of_range("Function parameter index (" + std::to_string(i) +
520
521
                            ") out of range (" + std::to_string(nParams()) +
                            ").");
522
  }
523
  return m_IFunction[i];
524
525
526
}

/**
527
528
* @param varName :: The variable name which may contain function index (
* [f<index.>]name )
529
* @param index :: Receives function index or throws std::invalid_argument
530
* @param name :: Receives the parameter name
531
*/
532
533
void CompositeFunction::parseName(const std::string &varName, size_t &index,
                                  std::string &name) {
534
  size_t i = varName.find('.');
535
  if (i == std::string::npos) {
536
    throw std::invalid_argument("Parameter " + varName + " not found.");
537
  } else {
538
    if (varName[0] != 'f')
539
540
      throw std::invalid_argument(
          "External function parameter name must start with 'f'");
541

542
    std::string sindex = varName.substr(1, i - 1);
543
544
545
546
547
    index = boost::lexical_cast<int>(sindex);

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

548
    name = varName.substr(i + 1);
549
  }
550
551
}

552
/** Returns the index of parameter i as it declared in its function
553
 * @param i :: The parameter index
554
555
 * @param recursive :: If true call parameterLocalName recusively until
 *    a non-composite function is reached.
556
557
 * @return The local index of the parameter
 */
558
size_t CompositeFunction::parameterLocalIndex(size_t i, bool recursive) const {
559
  size_t iFun = functionIndex(i);
560
561
  auto localIndex = i - m_paramOffsets[iFun];
  if (recursive) {
562
    auto cf = dynamic_cast<const CompositeFunction *>(m_functions[iFun].get());
563
564
565
566
567
    if (cf) {
      return cf->parameterLocalIndex(localIndex, recursive);
    }
  }
  return localIndex;
568
569
}

570
/** Returns the name of parameter i as it declared in its function
571
 * @param i :: The parameter index
572
573
 * @param recursive :: If true call parameterLocalName recusively until
 *    a non-composite function is reached.
574
575
 * @return The pure parameter name (without the function identifier f#.)
 */
576
577
std::string CompositeFunction::parameterLocalName(size_t i,
                                                  bool recursive) const {
578
  size_t iFun = functionIndex(i);
579
580
581
  auto localIndex = i - m_paramOffsets[iFun];
  auto localFunction = m_functions[iFun].get();
  if (recursive) {
582
    auto cf = dynamic_cast<const CompositeFunction *>(localFunction);
583
584
585
586
587
    if (cf) {
      return cf->parameterLocalName(localIndex, recursive);
    }
  }
  return localFunction->parameterName(localIndex);
588
589
}

590
/**
591
 * Apply the ties.
592
 */
593
594
void CompositeFunction::applyTies() {
  for (size_t i = 0; i < nFunctions(); i++) {
595
596
    getFunction(i)->applyTies();
  }
597
  IFunction::applyTies();
598
599
600
}

/**
601
 * Clear the ties.
602
 */
603
void CompositeFunction::clearTies() {
604
  IFunction::clearTies();
605
  for (size_t i = 0; i < nFunctions(); i++) {
606
607
608
609
610
    getFunction(i)->clearTies();
  }
}

/** Removes i-th parameter's tie if it is tied or does nothing.
611
 * @param i :: The index of the tied parameter.
612
613
 * @return True if successfull
 */
614
bool CompositeFunction::removeTie(size_t i) {
615
616
617
618
619
620
621
  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;
622
623
624
}

/** Get the tie of i-th parameter
625
 * @param i :: The parameter index
626
 * @return A pointer to the tie.
627
 */
628
ParameterTie *CompositeFunction::getTie(size_t i) const {
629
630
631
632
633
634
  auto tie = IFunction::getTie(i);
  if (tie == nullptr) {
    size_t iFun = functionIndex(i);
    tie = m_functions[iFun]->getTie(i - m_paramOffsets[iFun]);
  }
  return tie;
635
636
637
638
}

/**
 * Declare a new parameter. To used in the implementation'c constructor.
639
640
 * @param name :: The parameter name.
 * @param initValue :: The initial value for the parameter
641
 * @param description :: Parameter documentation
642
 */
643
644
645
646
647
648
649
650
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.");
651
652
}

653
654
655
/**
 * Prepare the function for a fit.
 */
656
void CompositeFunction::setUpForFit() {
657
  IFunction::setUpForFit();
658
  // set up the member functions
659
  for (size_t i = 0; i < nFunctions(); i++) {
660
    getFunction(i)->setUpForFit();
661
  }
662
663
664
665
  // 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
  //
666
  // if parameters have non-constant ties enable numerical derivatives
667
  // for(size_t i = 0; i < nParams(); ++i)
668
669
670
671
672
673
674
675
676
677
678
  //{
  //  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
679
680
681
682
683
  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 "
684
                           "non-constant ties defined.\n";
685
686
        break;
      }
687
    }
688
689
690
  }
}

Nick Draper's avatar
re #100    
Nick Draper committed
691
/// Get constraint
692
/// @param i :: the index
Nick Draper's avatar
re #100    
Nick Draper committed
693
/// @return A pointer to the constraint
694
IConstraint *CompositeFunction::getConstraint(size_t i) const {
695
696
697
698
699
700
  auto constraint = IFunction::getConstraint(i);
  if (constraint == nullptr) {
    size_t iFun = functionIndex(i);
    constraint = m_functions[iFun]->getConstraint(i - m_paramOffsets[iFun]);
  }
  return constraint;
701
702
}

703
/** Remove a constraint
704
 * @param parName :: The name of a parameter which constarint to remove.
705
 */
706
void CompositeFunction::removeConstraint(const std::string &parName) {
707
708
709
710
711
712
713
714
715
  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));
  }
716
717
}

718
/** Checks if a constraint has been explicitly set
719
 *  @param i :: The parameter index
720
 *  @return true if the function is explicitly set
721
 */
722
bool CompositeFunction::isExplicitlySet(size_t i) const {
723
  size_t iFun = functionIndex(i);
724
  return m_functions[iFun]->isExplicitlySet(i - m_paramOffsets[iFun]);
725
726
}

727
/**
728
729
 * Returns the index of parameter if the ref points to one of the member
 * function
730
 * @param ref :: A reference to a parameter
731
 * @return Parameter index or number of nParams() if parameter not found
732
 */
733
734
size_t
CompositeFunction::getParameterIndex(const ParameterReference &ref) const {
735
736
  if (ref.getLocalFunction() == this && ref.getLocalIndex() < nParams()) {
    return ref.getLocalIndex();
737
  }
738
  for (size_t iFun = 0; iFun < nFunctions(); iFun++) {
739
    IFunction_sptr fun = getFunction(iFun);
740
    size_t iLocalIndex = fun->getParameterIndex(ref);
741
    if (iLocalIndex < fun->nParams()) {
742
743
744
      return m_paramOffsets[iFun] + iLocalIndex;
    }
  }
745
  return nParams();
746
747
}

748
/**
749
 * Returns the shrared pointer to the function conataining a parameter
750
 * @param ref :: The reference
751
752
 * @return A function containing parameter pointed to by ref
 */
753
754
755
IFunction_sptr
CompositeFunction::getContainingFunction(const ParameterReference &ref) const {
  for (size_t iFun = 0; iFun < nFunctions(); iFun++) {
756
    IFunction_sptr fun = getFunction(iFun);
757
    if (fun->getParameterIndex(ref) < fun->nParams()) {
758
      return fun;
759
760
    }
  }
761
762
763
  return IFunction_sptr();
}

764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
/// Get number of domains required by this function
size_t CompositeFunction::getNumberDomains() const {
  auto n = nFunctions();
  if (n == 0) {
    return 1;
  }
  size_t nd = getFunction(0)->getNumberDomains();
  for (size_t iFun = 1; iFun < n; ++iFun) {
    if (getFunction(0)->getNumberDomains() != nd) {
      throw std::runtime_error("CompositeFunction has members with "
                               "inconsistent domain numbers.");
    }
  }
  return nd;
}

/// Split this function (if needed) into a list of independent functions.
/// The number of functions must be the number of domains this function is
/// working on (== getNumberDomains()). The result of evaluation of the
/// created functions on their domains must be the same as if this function
/// was evaluated on the composition of those domains.
Roman Tolchenov's avatar
Roman Tolchenov committed
785
786
std::vector<IFunction_sptr>
CompositeFunction::createEquivalentFunctions() const {
787
788
789
790
791
792
793
794
795
  auto nd = getNumberDomains();
  if (nd == 1) {
    return std::vector<IFunction_sptr>(
        1, FunctionFactory::Instance().createInitialized(asString()));
  }

  auto nf = nFunctions();
  std::vector<std::vector<IFunction_sptr>> equiv;
  equiv.reserve(nf);
Roman Tolchenov's avatar
Roman Tolchenov committed
796
  for (size_t i = 0; i < nf; ++i) {
797
798
799
800
801
    equiv.push_back(getFunction(i)->createEquivalentFunctions());
  }

  std::vector<IFunction_sptr> funs;
  funs.reserve(nd);
Roman Tolchenov's avatar
Roman Tolchenov committed
802
  for (size_t i = 0; i < nd; ++i) {
803
804
    auto comp = new CompositeFunction;
    funs.push_back(IFunction_sptr(comp));
Roman Tolchenov's avatar
Roman Tolchenov committed
805
    for (size_t j = 0; j < nf; ++j) {
806
807
808
809
810
811
      comp->addFunction(equiv[j][i]);
    }
  }
  return funs;
}

812
813
} // namespace API
} // namespace Mantid