CompositeFunction.cpp 24.7 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
10
#include "MantidKernel/Exception.h"
#include "MantidKernel/Logger.h"

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

16
17
namespace Mantid {
namespace API {
18

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

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

26
DECLARE_FUNCTION(CompositeFunction)
27

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

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

37
/**
38
39
40
41
42
43
44
45
46
47
 * 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=(" << ... <<")";
 *   }
48
 * @return the string representation of the composite function
49
 */
50
std::string CompositeFunction::asString() const {
51
  std::ostringstream ostr;
52

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

58
  if (name() != "CompositeFunction" || nAttributes() > 1 ||
59
      getAttribute("NumDeriv").asBool()) {
60
    ostr << "composite=" << name();
61
    std::vector<std::string> attr = this->getAttributeNames();
62
63
    for (const auto &attName : attr) {
      std::string attValue = this->getAttribute(attName).value();
64
65
      if (!attValue.empty()) {
        ostr << ',' << attName << '=' << attValue;
66
67
68
      }
    }
    ostr << ';';
69
  }
70
  for (size_t i = 0; i < nFunctions(); i++) {
71
    const auto localAttr = this->getLocalAttributeNames();
72
    IFunction_sptr fun = getFunction(i);
Hahn, Steven's avatar
Hahn, Steven committed
73
74
    bool isComp =
        boost::dynamic_pointer_cast<CompositeFunction>(fun) != nullptr;
75
76
    if (isComp)
      ostr << '(';
77
    ostr << fun->asString();
78
79
80
81
82
83
84
85
    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;
      }
    }
86
87
88
    if (isComp)
      ostr << ')';
    if (i < nFunctions() - 1) {
89
90
91
92
      ostr << ';';
    }
  }
  std::string ties;
93
94
95
  for (size_t i = 0; i < nParams(); i++) {
    const ParameterTie *tie = getTie(i);
    if (tie) {
96
97
      IFunction_sptr fun = getFunction(functionIndex(i));
      std::string tmp = tie->asString(fun.get());
98
      if (tmp.empty()) {
99
        tmp = tie->asString(this);
100
101
        if (!tmp.empty()) {
          if (!ties.empty()) {
102
103
104
105
106
107
108
            ties += ",";
          }
          ties += tmp;
        }
      }
    }
  }
109
  if (!ties.empty()) {
110
    ostr << ";ties=(" << ties << ")";
111
112
113
114
  }
  return ostr.str();
}

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

126
127
128
129
130
131
/**
 * @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.
 */
132
133
134
135
136
137
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);
  }
138
139
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

308
309
310
/// 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) {
311
  size_t iFun = functionIndex(i);
312
  m_functions[iFun]->setActiveParameter(i - m_paramOffsets[iFun], value);
313
314
315
}

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

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

332
333
334
335
336
/**
 * query to see in the function is active
 * @param i :: The index of a declared parameter
 * @return true if parameter i is active
 */
337
bool CompositeFunction::isActive(size_t i) const {
338
  size_t iFun = functionIndex(i);
339
  return m_functions[iFun]->isActive(i - m_paramOffsets[iFun]);
340
341
}

342
/**
343
 * query to see in the function is active
344
 * @param i :: The index of a declared parameter
345
 * @return true if parameter i is active
346
 */
347
bool CompositeFunction::isFixed(size_t i) const {
348
  size_t iFun = functionIndex(i);
349
  return m_functions[iFun]->isFixed(i - m_paramOffsets[iFun]);
350
351
352
}

/**
353
 * @param i :: A declared parameter index to be removed from active
354
 */
355
void CompositeFunction::fix(size_t i) {
356
  size_t iFun = functionIndex(i);
357
  m_functions[iFun]->fix(i - m_paramOffsets[iFun]);
358
359
}

360
/** Makes a parameter active again. It doesn't change the parameter's tie.
361
 * @param i :: A declared parameter index to be restored to active
362
 */
363
void CompositeFunction::unfix(size_t i) {
364
  size_t iFun = functionIndex(i);
365
  m_functions[iFun]->unfix(i - m_paramOffsets[iFun]);
366
367
}

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

375
  std::vector<IFunction_sptr> functions(m_functions.begin(), m_functions.end());
376
377
  m_functions.clear();

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

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

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

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

425
  IFunction_sptr fun = getFunction(i);
426

427
  size_t dnp = fun->nParams();
428

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

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

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

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

459
  m_functions.erase(m_functions.begin() + i);
460
461
}

462
/** Replace a function with a new one. The old function is deleted.
463
 *  The new function must have already its workspace set.
464
 * @param f_old :: The pointer to the function to replace. If it's not
465
 *  a member of this composite function nothing happens
466
 * @param f_new :: A pointer to the new function
467
 */
468
469
470
471
472
473
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;
474
  std::vector<IFunction_sptr>::difference_type iFun = it - m_functions.begin();
475
  replaceFunction(iFun, f_new);
476
477
}

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

489
  IFunction_sptr fun = getFunction(i);
490
  size_t np_old = fun->nParams();
491

492
  size_t np_new = f->nParams();
493

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

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

  m_functions[i] = f;
}

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

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

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

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

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

574
    name = varName.substr(i + 1);
575
  }
576
577
}

578
/** Returns the index of parameter i as it declared in its function
579
 * @param i :: The parameter index
580
581
 * @return The local index of the parameter
 */
582
size_t CompositeFunction::parameterLocalIndex(size_t i) const {
583
  size_t iFun = functionIndex(i);
584
585
586
  return i - m_paramOffsets[iFun];
}

587
/** Returns the name of parameter i as it declared in its function
588
 * @param i :: The parameter index
589
590
 * @return The pure parameter name (without the function identifier f#.)
 */
591
std::string CompositeFunction::parameterLocalName(size_t i) const {
592
  size_t iFun = functionIndex(i);
593
  return m_functions[iFun]->parameterName(i - m_paramOffsets[iFun]);
594
595
}

596
/**
597
 * Apply the ties.
598
 */
599
600
void CompositeFunction::applyTies() {
  for (size_t i = 0; i < nFunctions(); i++) {
601
602
603
604
605
    getFunction(i)->applyTies();
  }
}

/**
606
 * Clear the ties.
607
 */
608
609
void CompositeFunction::clearTies() {
  for (size_t i = 0; i < nFunctions(); i++) {
610
611
612
613
614
    getFunction(i)->clearTies();
  }
}

/** Removes i-th parameter's tie if it is tied or does nothing.
615
 * @param i :: The index of the tied parameter.
616
617
 * @return True if successfull
 */
618
bool CompositeFunction::removeTie(size_t i) {
619
  size_t iFun = functionIndex(i);
620
  bool res = m_functions[iFun]->removeTie(i - m_paramOffsets[iFun]);
621
622
623
624
  return res;
}

/** 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
  size_t iFun = functionIndex(i);
630
  return m_functions[iFun]->getTie(i - m_paramOffsets[iFun]);
631
632
633
634
}

/**
 * Attaches a tie to this function. The attached tie is owned by the function.
635
 * @param tie :: A pointer to a new tie
636
 */
637
void CompositeFunction::addTie(std::unique_ptr<ParameterTie> tie) {
638
639
  size_t i = getParameterIndex(*tie);
  size_t iFun = functionIndex(i);
640
  m_functions[iFun]->addTie(std::move(tie));
641
642
643
644
}

/**
 * Declare a new parameter. To used in the implementation'c constructor.
645
646
 * @param name :: The parameter name.
 * @param initValue :: The initial value for the parameter
647
 * @param description :: Parameter documentation
648
 */
649
650
651
652
653
654
655
656
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.");
657
658
659
}

/** Add a constraint
660
 *  @param ic :: Pointer to a constraint.
661
 */
662
void CompositeFunction::addConstraint(std::unique_ptr<IConstraint> ic) {
663
664
  size_t i = getParameterIndex(*ic);
  size_t iFun = functionIndex(i);
665
  getFunction(iFun)->addConstraint(std::move(ic));
666
667
}

668
669
670
/**
 * Prepare the function for a fit.
 */
671
void CompositeFunction::setUpForFit() {
672
  // set up the member functions
673
  for (size_t i = 0; i < nFunctions(); i++) {
674
    getFunction(i)->setUpForFit();
675
  }
676
677
678
679
  // 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
  //
680
  // if parameters have non-constant ties enable numerical derivatives
681
  // for(size_t i = 0; i < nParams(); ++i)
682
683
684
685
686
687
688
689
690
691
692
  //{
  //  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
693
694
695
696
697
  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 "
698
                           "non-constant ties defined.\n";
699
700
        break;
      }
701
    }
702
703
704
  }
}

Nick Draper's avatar
re #100    
Nick Draper committed
705
/// Get constraint
706
/// @param i :: the index
Nick Draper's avatar
re #100    
Nick Draper committed
707
/// @return A pointer to the constraint
708
IConstraint *CompositeFunction::getConstraint(size_t i) const {
709
  size_t iFun = functionIndex(i);
710
  return m_functions[iFun]->getConstraint(i - m_paramOffsets[iFun]);
711
712
}

713
/** Remove a constraint
714
 * @param parName :: The name of a parameter which constarint to remove.
715
 */
716
void CompositeFunction::removeConstraint(const std::string &parName) {
717
718
  size_t iPar = parameterIndex(parName);
  size_t iFun = functionIndex(iPar);
719
720
721
  getFunction(iFun)->removeConstraint(parameterLocalName(iPar));
}

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

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

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

768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
/// 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
789
790
std::vector<IFunction_sptr>
CompositeFunction::createEquivalentFunctions() const {
791
792
793
794
795
796
797
798
799
  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);