IFunction.cpp 50.2 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
#include "MantidAPI/IFunction.h"
8
#include "MantidAPI/Axis.h"
9
#include "MantidAPI/ConstraintFactory.h"
10
#include "MantidAPI/Expression.h"
11
#include "MantidAPI/FunctionFactory.h"
12
13
14
#include "MantidAPI/IConstraint.h"
#include "MantidAPI/IFunctionWithLocation.h"
#include "MantidAPI/Jacobian.h"
15
#include "MantidAPI/MatrixWorkspace.h"
16
#include "MantidAPI/MultiDomainFunction.h"
17
#include "MantidAPI/ParameterTie.h"
18
#include "MantidAPI/SpectrumInfo.h"
19
#include "MantidGeometry/Instrument.h"
20
21
#include "MantidGeometry/Instrument/Component.h"
#include "MantidGeometry/Instrument/DetectorGroup.h"
22
#include "MantidGeometry/Instrument/DetectorInfo.h"
23
#include "MantidGeometry/Instrument/FitParameter.h"
24
#include "MantidGeometry/Instrument/ParameterMap.h"
25
26
#include "MantidGeometry/muParser_Silent.h"
#include "MantidKernel/Exception.h"
27
#include "MantidKernel/IPropertyManager.h"
28
#include "MantidKernel/Logger.h"
29
#include "MantidKernel/MultiThreaded.h"
30
#include "MantidKernel/ProgressBase.h"
31
32
#include "MantidKernel/Strings.h"
#include "MantidKernel/UnitFactory.h"
33

34
35
#include <boost/lexical_cast.hpp>

36
#include "MantidKernel/StringTokenizer.h"
37

LamarMoore's avatar
LamarMoore committed
38
#include <algorithm>
39
#include <limits>
40
#include <sstream>
David Fairbrother's avatar
David Fairbrother committed
41
#include <utility>
42

43
44
45
namespace Mantid {
namespace API {
using namespace Geometry;
Gigg, Martyn Anthony's avatar
Gigg, Martyn Anthony committed
46

47
48
49
namespace {
/// static logger
Kernel::Logger g_log("IFunction");
50
51
52

/// Struct that helps sort ties in correct order of application.
struct TieNode {
53
  // Index of the tied parameter
54
55
56
57
58
  size_t left;
  // Indices of parameters on the right-hand-side of the expression
  std::vector<size_t> right;
  // This tie must be applied before the other if the RHS of the other
  // contains this (left) parameter.
59
  bool operator<(TieNode const &other) const {
60
61
62
63
    return std::find(other.right.begin(), other.right.end(), left) !=
           other.right.end();
  }
};
LamarMoore's avatar
LamarMoore committed
64
} // namespace
65
66
67
68

/**
 * Destructor
 */
Sam Jenkins's avatar
Sam Jenkins committed
69
IFunction::~IFunction() { m_attrs.clear(); }
70

71
72
73
74
/**
 * Virtual copy constructor
 */
boost::shared_ptr<IFunction> IFunction::clone() const {
75
76
77
  auto clonedFunction =
      FunctionFactory::Instance().createInitialized(this->asString());
  for (size_t i = 0; i < this->nParams(); i++) {
78
79
80
81
    double error = this->getError(i);
    clonedFunction->setError(i, error);
  }
  return clonedFunction;
82
}
83

84
85
/**
 * Attach a progress reporter
86
87
 * @param reporter :: A pointer to a progress reporter that can be called during
 * function evaluation
88
 */
89
90
void IFunction::setProgressReporter(
    boost::shared_ptr<Kernel::ProgressBase> reporter) {
David Fairbrother's avatar
David Fairbrother committed
91
  m_progReporter = std::move(reporter);
92
93
94
95
96
97
98
  m_progReporter->setNotifyStep(0.01);
}

/**
 * If a reporter object is set, reports progress with an optional message
 * @param msg :: A message to display (default = "")
 */
99
100
void IFunction::reportProgress(const std::string &msg) const {
  if (m_progReporter) {
Roman Tolchenov's avatar
Roman Tolchenov committed
101
    const_cast<Kernel::ProgressBase *>(m_progReporter.get())->report(msg);
102
103
104
105
106
  }
}

/**
 *
107
108
 * @returns true if a progress reporter is set & evalaution has been requested
 *to stop
109
 */
110
111
112
113
114
bool IFunction::cancellationRequestReceived() const {
  if (m_progReporter)
    return m_progReporter->hasCancellationBeenRequested();
  else
    return false;
115
116
}

117
/** Base class implementation calculates the derivatives numerically.
118
 * @param domain :: The domain of the function
119
120
 * @param jacobian :: A Jacobian matrix. It is expected to have dimensions of
 * domain.size() by nParams().
121
 */
122
123
void IFunction::functionDeriv(const FunctionDomain &domain,
                              Jacobian &jacobian) {
124
  calNumericalDeriv(domain, jacobian);
125
126
}

127
128
129
130
/** Check if an active parameter i is actually active
 * @param i :: Index of a parameter.
 */
bool IFunction::isActive(size_t i) const {
131
132
133
134
135
136
  return getParameterStatus(i) == Active;
}

/**
 * Query if the parameter is fixed
 * @param i :: The index of a declared parameter
137
 * @return true if parameter i is fixed
138
139
 */
bool IFunction::isFixed(size_t i) const {
140
141
  auto status = getParameterStatus(i);
  return status == Fixed || status == FixedByDefault;
142
143
}

144
145
146
147
148
149
150
/// Check if a parameter i is fixed by default (not by user).
/// @param i :: The index of a parameter
/// @return true if parameter i is fixed by default
bool IFunction::isFixedByDefault(size_t i) const {
  return getParameterStatus(i) == FixedByDefault;
}

151
152
153
154
155
/// This method doesn't create a tie
/// @param i :: A declared parameter index to be fixed
/// @param isDefault :: If true fix it by default
///
void IFunction::fix(size_t i, bool isDefault) {
156
157
158
159
160
  auto status = getParameterStatus(i);
  if (status == Tied) {
    throw std::runtime_error("Cannot fix parameter " + std::to_string(i) +
                             " (" + parameterName(i) + "): it has a tie.");
  }
161
162
163
164
165
  if (isDefault) {
    setParameterStatus(i, FixedByDefault);
  } else {
    setParameterStatus(i, Fixed);
  }
166
167
168
169
170
171
172
173
174
175
176
177
}

/** Makes a parameter active again. It doesn't change the parameter's tie.
 * @param i :: A declared parameter index to be restored to active
 */
void IFunction::unfix(size_t i) {
  auto status = getParameterStatus(i);
  if (status == Tied) {
    throw std::runtime_error("Cannot unfix parameter " + std::to_string(i) +
                             " (" + parameterName(i) + "): it has a tie.");
  }
  setParameterStatus(i, Active);
178
179
}

180
181
182
/**
 * Ties a parameter to other parameters
 * @param parName :: The name of the parameter to tie.
183
 * @param expr :: A math expression
184
185
 * @param isDefault :: Flag to mark as default the value of an object associated
 * with this reference: a tie or a constraint.
186
187
 * @return newly ties parameters
 */
Roman Tolchenov's avatar
Roman Tolchenov committed
188
189
void IFunction::tie(const std::string &parName, const std::string &expr,
                    bool isDefault) {
190
  auto ti = std::make_unique<ParameterTie>(this, parName, expr, isDefault);
191
  if (!isDefault && ti->isConstant()) {
192
    setParameter(parName, ti->eval());
193
    fix(getParameterIndex(*ti));
194
  } else {
195
    addTie(std::move(ti));
196
  }
197
198
}

199
200
/**
 * Add ties to the function.
201
202
 * @param ties :: Comma-separated list of name=value pairs where name is a
 *parameter name and value
203
 *  is a math expression tying the parameter to other parameters or a constant.
204
205
 * @param isDefault :: Flag to mark as default the value of an object associated
 *with this reference: a tie or a constraint.
206
 *
207
 */
208
void IFunction::addTies(const std::string &ties, bool isDefault) {
209
210
211
  Expression list;
  list.parse(ties);
  list.toList();
212
213
214
215
  for (const auto &t : list) {
    if (t.name() == "=" && t.size() >= 2) {
      size_t n = t.size() - 1;
      const std::string value = t[n].str();
216
      for (size_t i = n; i != 0;) {
217
        --i;
218
        this->tie(t[i].name(), value, isDefault);
219
220
221
      }
    }
  }
222
  applyTies();
223
224
}

225
226
227
228
/** Removes the tie off a parameter. The parameter becomes active
 * This method can be used when constructing and editing the IFunction in a GUI
 * @param parName :: The name of the parameter which ties will be removed.
 */
229
void IFunction::removeTie(const std::string &parName) {
230
231
232
233
  size_t i = parameterIndex(parName);
  this->removeTie(i);
}

234
/// Write all parameter ties owned by this function to a string
235
/// @return A tie string for the parameter.
236
std::string IFunction::writeTies() const {
237
  std::ostringstream tieStream;
238
  bool first = true;
239
240
241
  for (auto &tie : m_ties) {
    if (tie->isDefault())
      continue;
242
243
244
245
    if (!first) {
      tieStream << ',';
    } else {
      first = false;
246
    }
247
    tieStream << tie->asString(this);
248
  }
249
  return tieStream.str();
250
251
}

252
253
254
255
256
257
/**
 * Attaches a tie to this ParamFunction. The attached tie is owned by the
 * ParamFunction.
 * @param tie :: A pointer to a new tie
 */
void IFunction::addTie(std::unique_ptr<ParameterTie> tie) {
258

259
260
261
262
263
264
265
266
267
268
269
  auto iPar = getParameterIndex(*tie);
  bool found = false;
  for (auto &m_tie : m_ties) {
    auto mPar = getParameterIndex(*m_tie);
    if (mPar == iPar) {
      found = true;
      m_tie = std::move(tie);
      break;
    }
  }
  if (!found) {
270
    m_ties.emplace_back(std::move(tie));
271
    setParameterStatus(iPar, Tied);
272
273
274
  }
}

Roman Tolchenov's avatar
Roman Tolchenov committed
275
bool IFunction::hasOrderedTies() const { return !m_orderedTies.empty(); }
276

Roman Tolchenov's avatar
Roman Tolchenov committed
277
void IFunction::applyOrderedTies() {
278
279
280
281
282
  for (auto &&tie : m_orderedTies) {
    tie->eval();
  }
}

283
284
285
286
/**
 * Apply the ties.
 */
void IFunction::applyTies() {
287
288
289
290
291
292
  if (hasOrderedTies()) {
    applyOrderedTies();
  } else {
    for (auto &tie : m_ties) {
      tie->eval();
    }
293
294
295
296
297
298
299
300
301
302
303
304
305
306
  }
}

/**
 * Used to find ParameterTie for a parameter i
 */
class ReferenceEqual {
  /// The function that has the tie
  const IFunction &m_fun;
  /// index to find
  const size_t m_i;

public:
  /// Constructor
307
308
  explicit ReferenceEqual(const IFunction &fun, size_t i)
      : m_fun(fun), m_i(i) {}
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
  /// Bracket operator
  /// @param p :: the element you are looking for
  /// @return True if found
  template <class T> bool operator()(const std::unique_ptr<T> &p) {
    return m_fun.getParameterIndex(*p) == m_i;
  }
};

/** Removes i-th parameter's tie if it is tied or does nothing.
 * @param i :: The index of the tied parameter.
 * @return True if successfull
 */
bool IFunction::removeTie(size_t i) {
  if (i >= nParams()) {
    throw std::out_of_range("Function parameter index out of range.");
  }
325
326
  auto it =
      std::find_if(m_ties.begin(), m_ties.end(), ReferenceEqual(*this, i));
327
328
  if (it != m_ties.end()) {
    m_ties.erase(it);
329
    setParameterStatus(i, Active);
330
331
332
333
334
335
336
337
338
339
340
    return true;
  }
  unfix(i);
  return false;
}

/** Get tie of parameter number i
 * @param i :: The index of a declared parameter.
 * @return A pointer to the tie
 */
ParameterTie *IFunction::getTie(size_t i) const {
341
342
  auto it =
      std::find_if(m_ties.cbegin(), m_ties.cend(), ReferenceEqual(*this, i));
343
344
345
346
347
348
349
350
351
352
  if (it != m_ties.cend()) {
    return it->get();
  }
  return nullptr;
}

/** Remove all ties
 */
void IFunction::clearTies() {
  for (size_t i = 0; i < nParams(); ++i) {
353
    setParameterStatus(i, Active);
354
355
356
357
358
359
360
361
  }
  m_ties.clear();
}

/** Add a constraint
 *  @param ic :: Pointer to a constraint.
 */
void IFunction::addConstraint(std::unique_ptr<IConstraint> ic) {
362
  size_t iPar = ic->parameterIndex();
363
364
  bool found = false;
  for (auto &constraint : m_constraints) {
365
    if (constraint->parameterIndex() == iPar) {
366
367
368
369
370
371
      found = true;
      constraint = std::move(ic);
      break;
    }
  }
  if (!found) {
372
    m_constraints.emplace_back(std::move(ic));
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
  }
}

/** Get constraint of parameter number i
 * @param i :: The index of a declared parameter.
 * @return A pointer to the constraint or NULL
 */
IConstraint *IFunction::getConstraint(size_t i) const {
  auto it = std::find_if(m_constraints.cbegin(), m_constraints.cend(),
                         ReferenceEqual(*this, i));
  if (it != m_constraints.cend()) {
    return it->get();
  }
  return nullptr;
}

/** Remove a constraint
 * @param parName :: The name of a parameter which constarint to remove.
 */
void IFunction::removeConstraint(const std::string &parName) {
  size_t iPar = parameterIndex(parName);
  for (auto it = m_constraints.begin(); it != m_constraints.end(); ++it) {
395
    if (iPar == (**it).getLocalIndex()) {
396
397
398
399
400
401
      m_constraints.erase(it);
      break;
    }
  }
}

402
403
404
405
/** Set a constraint penalty
 * @param parName :: The name of a constraint
 * @param c :: The penalty
 */
406
407
void IFunction::setConstraintPenaltyFactor(const std::string &parName,
                                           const double &c) {
408
  size_t iPar = parameterIndex(parName);
Tom Titcombe's avatar
Tom Titcombe committed
409
410
411
  for (auto &constraint : m_constraints) {
    if (iPar == constraint->getLocalIndex()) {
      constraint->setPenaltyFactor(c);
412
413
414
      return;
    }
  }
415
416
417
418
  g_log.warning()
      << parName
      << " does not have constraint so setConstraintPenaltyFactor failed"
      << "\n";
419
420
}

421
/// Remove all constraints.
422
void IFunction::clearConstraints() { m_constraints.clear(); }
423
424
425
426
427
428
429

void IFunction::setUpForFit() {
  for (auto &constraint : m_constraints) {
    constraint->setParamToSatisfyConstraint();
  }
}

430
/// Write all parameter constraints owned by this function to a string
431
/// @return A constraint string for the parameter.
432
433
434
std::string IFunction::writeConstraints() const {
  std::ostringstream stream;
  bool first = true;
435
436
437
  for (auto &constrint : m_constraints) {
    if (constrint->isDefault())
      continue;
438
439
440
441
    if (!first) {
      stream << ',';
    } else {
      first = false;
442
    }
443
    stream << constrint->asString();
444
  }
445
  return stream.str();
446
447
448
}

/**
449
 * Writes a string that can be used in FunctionFunctory to create a copy of this
450
451
452
 * IFunction
 * @return string representation of the function
 */
Roman Tolchenov's avatar
Roman Tolchenov committed
453
std::string IFunction::asString() const { return writeToString(); }
454
455
456
457
458
459
460
461
462
463

/**
 * Writes this function into a string.
 * @param parentLocalAttributesStr :: A preformatted string with local
 * attributes of a parent composite function. Can be passed in by a
 * CompositeFunction (eg MultiDomainFunction).
 * @return string representation of the function
 */
std::string
IFunction::writeToString(const std::string &parentLocalAttributesStr) const {
464
465
466
467
468
469
470
471
472
473
  std::ostringstream ostr;
  ostr << "name=" << this->name();
  // print the attributes
  std::vector<std::string> attr = this->getAttributeNames();
  for (const auto &attName : attr) {
    std::string attValue = this->getAttribute(attName).value();
    if (!attValue.empty() && attValue != "\"\"") {
      ostr << ',' << attName << '=' << attValue;
    }
  }
474
  std::vector<std::string> ties;
475
  // print the parameters
476
  for (size_t i = 0; i < nParams(); i++) {
477
478
    std::ostringstream paramOut;
    paramOut << parameterName(i) << '=' << getParameter(i);
479
480
481
    ostr << ',' << paramOut.str();
    // Output non-default ties only.
    if (getParameterStatus(i) == Fixed) {
482
      ties.emplace_back(paramOut.str());
483
484
    }
  }
485
486

  // collect non-default constraints
487
  std::string constraints = writeConstraints();
488
489
  // print constraints
  if (!constraints.empty()) {
490
    ostr << ",constraints=(" << constraints << ")";
491
492
493
  }

  // collect the non-default ties
494
495
  auto tiesString = writeTies();
  if (!tiesString.empty()) {
496
    ties.emplace_back(tiesString);
497
498
499
500
501
502
  }
  // print the ties
  if (!ties.empty()) {
    ostr << ",ties=(" << Kernel::Strings::join(ties.begin(), ties.end(), ",")
         << ")";
  }
503
  // "local" attributes of a parent composite function
504
  ostr << parentLocalAttributesStr;
505
506
507
  return ostr.str();
}

508
/** Add a list of constraints from a string
509
 * @param str :: A comma-separated list of constraint expressions.
510
511
 * @param isDefault :: Flag to mark as default the value of an object associated
 *with this reference.
512
 *
513
 */
514
void IFunction::addConstraints(const std::string &str, bool isDefault) {
515
516
517
  Expression list;
  list.parse(str);
  list.toList();
518
519
  for (auto it = list.begin(); it != list.end(); ++it) {
    auto expr = (*it);
Sullivan, Brendan T's avatar
Sullivan, Brendan T committed
520
    if (expr.terms()[0].str().compare("penalty") == 0) {
521
522
      continue;
    }
Sullivan, Brendan T's avatar
Sullivan, Brendan T committed
523
524
525
526
527
528
529
530
531
    if ((it + 1) != list.end()) {
      auto next_expr = *(it + 1);
      if (next_expr.terms()[0].str().compare("penalty") == 0) {
        auto c = std::unique_ptr<IConstraint>(
            ConstraintFactory::Instance().createInitialized(this, expr,
                                                            isDefault));
        double penalty_factor = std::stof(next_expr.terms()[1].str(), NULL);
        c->setPenaltyFactor(penalty_factor);
        this->addConstraint(std::move(c));
532
533
      } else {
        auto c = std::unique_ptr<IConstraint>(
Sullivan, Brendan T's avatar
Sullivan, Brendan T committed
534
535
            ConstraintFactory::Instance().createInitialized(this, expr,
                                                            isDefault));
536
        this->addConstraint(std::move(c));
Sullivan, Brendan T's avatar
Sullivan, Brendan T committed
537
538
539
540
541
542
      }
    } else {
      auto c = std::unique_ptr<IConstraint>(
          ConstraintFactory::Instance().createInitialized(this, expr,
                                                          isDefault));
      this->addConstraint(std::move(c));
543
    }
544
545
546
547
548
549
  }
}

/**
 * Return a vector with all parameter names.
 */
550
std::vector<std::string> IFunction::getParameterNames() const {
551
  std::vector<std::string> out;
552
  for (size_t i = 0; i < nParams(); ++i) {
553
    out.emplace_back(parameterName(i));
554
555
556
557
  }
  return out;
}

558
559
560
/** Set a function handler
 * @param handler :: A new handler
 */
561
562
void IFunction::setHandler(std::unique_ptr<FunctionHandler> handler) {
  m_handler = std::move(handler);
563
  if (handler && handler->function().get() != this) {
564
565
566
567
568
    throw std::runtime_error("Function handler points to a different function");
  }
  m_handler->init();
}

569
/// Function to return all of the categories that contain this function
570
const std::vector<std::string> IFunction::categories() const {
571
572
573
574
  Mantid::Kernel::StringTokenizer tokenizer(
      category(), categorySeparator(),
      Mantid::Kernel::StringTokenizer::TOK_TRIM |
          Mantid::Kernel::StringTokenizer::TOK_IGNORE_EMPTY);
575
  return tokenizer.asVector();
576
577
}

578
579
580
581
582
/**
 * Operator <<
 * @param ostr :: The output stream
 * @param f :: The IFunction
 */
583
std::ostream &operator<<(std::ostream &ostr, const IFunction &f) {
584
585
586
587
  ostr << f.asString();
  return ostr;
}

588
589
590
591
592
593
594
namespace {
/**
 * Const attribute visitor returning the type of the attribute
 */
class AttType : public IFunction::ConstAttributeVisitor<std::string> {
protected:
  /// Apply if string
595
  std::string apply(const std::string & /*str*/) const override {
596
597
    return "std::string";
  }
598
  /// Apply if int
599
  std::string apply(const int & /*i*/) const override { return "int"; }
600
  /// Apply if double
601
  std::string apply(const double & /*d*/) const override { return "double"; }
602
  /// Apply if bool
603
  std::string apply(const bool & /*i*/) const override { return "bool"; }
604
  /// Apply if vector
605
  std::string apply(const std::vector<double> & /*unused*/) const override {
606
607
608
    return "std::vector<double>";
  }
};
LamarMoore's avatar
LamarMoore committed
609
} // namespace
610

611
std::string IFunction::Attribute::type() const {
612
613
614
615
  AttType tmp;
  return apply(tmp);
}

616
617
618
619
620
621
namespace {
/**
 * Const attribute visitor returning the value of the attribute as a string
 */
class AttValue : public IFunction::ConstAttributeVisitor<std::string> {
public:
622
  explicit AttValue(bool quoteString = false)
623
624
625
626
627
      : IFunction::ConstAttributeVisitor<std::string>(),
        m_quoteString(quoteString) {}

protected:
  /// Apply if string
628
  std::string apply(const std::string &str) const override {
629
630
631
    return (m_quoteString) ? std::string("\"" + str + "\"") : str;
  }
  /// Apply if int
632
  std::string apply(const int &i) const override { return std::to_string(i); }
633
  /// Apply if double
634
  std::string apply(const double &d) const override {
635
636
637
    return boost::lexical_cast<std::string>(d);
  }
  /// Apply if bool
638
639
640
  std::string apply(const bool &b) const override {
    return b ? "true" : "false";
  }
641
  /// Apply if vector
642
  std::string apply(const std::vector<double> &v) const override {
643
    std::string res = "(";
644
    if (!v.empty()) {
645
646
647
648
      for (size_t i = 0; i < v.size() - 1; ++i) {
        res += boost::lexical_cast<std::string>(v[i]) + ",";
      }
      res += boost::lexical_cast<std::string>(v.back());
649
    }
650
651
652
    res += ")";
    return res;
  }
653

654
655
656
657
private:
  /// Flag to quote a string value returned
  bool m_quoteString;
};
LamarMoore's avatar
LamarMoore committed
658
} // namespace
659

660
661
/// Copy assignment. Do not copy m_quoteValue flag.
/// @param attr :: The attribute to copy from.
662
IFunction::Attribute &IFunction::Attribute::operator=(const Attribute &attr) {
663
664
665
666
  m_data = attr.m_data;
  return *this;
}

667
std::string IFunction::Attribute::value() const {
668
669
670
671
  AttValue tmp(m_quoteValue);
  return apply(tmp);
}

672
673
674
/**
 * Return the attribute as a string if it is a string.
 */
675
676
677
678
679
std::string IFunction::Attribute::asString() const {
  if (m_quoteValue)
    return asQuotedString();

  try {
680
    return boost::get<std::string>(m_data);
681
  } catch (...) {
LamarMoore's avatar
LamarMoore committed
682
683
684
    throw std::runtime_error("Trying to access a " + type() +
                             " attribute "
                             "as string");
685
686
687
  }
}

688
689
690
/**
 * Return the attribute as a quoted string if it is a string.
 */
691
std::string IFunction::Attribute::asQuotedString() const {
692
693
  std::string attr;

694
  try {
695
    attr = boost::get<std::string>(m_data);
696
  } catch (...) {
LamarMoore's avatar
LamarMoore committed
697
698
699
    throw std::runtime_error("Trying to access a " + type() +
                             " attribute "
                             "as string");
700
  }
701

702
703
  if (attr.empty())
    return "\"\"";
704

705
  std::string quoted(attr);
706
707
708
709
  if (*(attr.begin()) != '\"')
    quoted = "\"" + attr;
  if (*(quoted.end() - 1) != '\"')
    quoted += "\"";
710
711
712
713

  return quoted;
}

714
715
716
/**
 * Return the attribute as an unquoted string if it is a string.
 */
717
std::string IFunction::Attribute::asUnquotedString() const {
718
719
  std::string attr;

720
  try {
721
    attr = boost::get<std::string>(m_data);
722
  } catch (...) {
LamarMoore's avatar
LamarMoore committed
723
724
725
    throw std::runtime_error("Trying to access a " + type() +
                             " attribute "
                             "as string");
726
727
  }
  std::string unquoted(attr);
728
729
730
731
732
733
734
  if (attr.empty())
    return "";
  if (*(attr.begin()) == '\"')
    unquoted = std::string(attr.begin() + 1, attr.end() - 1);
  if (*(unquoted.end() - 1) == '\"')
    unquoted = std::string(unquoted.begin(), unquoted.end() - 1);

735
736
737
  return unquoted;
}

738
739
740
/**
 * Return the attribute as an int if it is a int.
 */
741
742
int IFunction::Attribute::asInt() const {
  try {
743
    return boost::get<int>(m_data);
744
  } catch (...) {
LamarMoore's avatar
LamarMoore committed
745
746
747
    throw std::runtime_error("Trying to access a " + type() +
                             " attribute "
                             "as int");
748
749
750
  }
}

751
752
753
/**
 * Return the attribute as a double if it is a double.
 */
754
755
double IFunction::Attribute::asDouble() const {
  try {
756
    return boost::get<double>(m_data);
757
  } catch (...) {
LamarMoore's avatar
LamarMoore committed
758
759
760
    throw std::runtime_error("Trying to access a " + type() +
                             " attribute "
                             "as double");
761
762
763
  }
}

764
765
766
/**
 * Return the attribute as a bool if it is a bool.
 */
767
768
bool IFunction::Attribute::asBool() const {
  try {
769
    return boost::get<bool>(m_data);
770
  } catch (...) {
LamarMoore's avatar
LamarMoore committed
771
772
773
    throw std::runtime_error("Trying to access a " + type() +
                             " attribute "
                             "as bool");
774
  }
775
776
777
778
779
}

/**
 * Return the attribute as a bool if it is a vector.
 */
780
781
782
783
std::vector<double> IFunction::Attribute::asVector() const {
  try {
    return boost::get<std::vector<double>>(m_data);
  } catch (...) {
LamarMoore's avatar
LamarMoore committed
784
785
786
    throw std::runtime_error("Trying to access a " + type() +
                             " attribute "
                             "as vector");
787
  }
788
789
}

790
/** Sets new value if attribute is a string. If the type is wrong
791
792
793
 * throws an exception
 * @param str :: The new value
 */
794
795
void IFunction::Attribute::setString(const std::string &str) {
  try {
796
    boost::get<std::string>(m_data) = str;
797
  } catch (...) {
LamarMoore's avatar
LamarMoore committed
798
799
800
    throw std::runtime_error("Trying to access a " + type() +
                             " attribute "
                             "as string");
801
802
803
  }
}

804
/** Sets new value if attribute is a double. If the type is wrong
805
806
807
 * throws an exception
 * @param d :: The new value
 */
808
809
void IFunction::Attribute::setDouble(const double &d) {
  try {
810
    boost::get<double>(m_data) = d;
811
  } catch (...) {
LamarMoore's avatar
LamarMoore committed
812
813
814
    throw std::runtime_error("Trying to access a " + type() +
                             " attribute "
                             "as double");
815
816
817
  }
}

818
/** Sets new value if attribute is an int. If the type is wrong
819
820
821
 * throws an exception
 * @param i :: The new value
 */
822
823
void IFunction::Attribute::setInt(const int &i) {
  try {
824
    boost::get<int>(m_data) = i;
825
  } catch (...) {
LamarMoore's avatar
LamarMoore committed
826
827
828
    throw std::runtime_error("Trying to access a " + type() +
                             " attribute "
                             "as int");
829
830
831
  }
}

832
/** Sets new value if attribute is an bool. If the type is wrong
833
834
 * throws an exception
 * @param b :: The new value
835
 */
836
837
void IFunction::Attribute::setBool(const bool &b) {
  try {
838
    boost::get<bool>(m_data) = b;
839
  } catch (...) {
LamarMoore's avatar
LamarMoore committed
840
841
842
    throw std::runtime_error("Trying to access a " + type() +
                             " attribute "
                             "as bool");
843
  }
844
845
846
847
848
849
850
}

/**
 * Sets new value if attribute is a vector. If the type is wrong
 * throws an exception
 * @param v :: The new value
 */
851
852
853
854
855
void IFunction::Attribute::setVector(const std::vector<double> &v) {
  try {
    auto &value = boost::get<std::vector<double>>(m_data);
    value.assign(v.begin(), v.end());
  } catch (...) {
LamarMoore's avatar
LamarMoore committed
856
857
858
    throw std::runtime_error("Trying to access a " + type() +
                             " attribute "
                             "as vector");
859
  }
860
861
}

862
863
864
865
866
867
868
869
870
871
/// Check if a string attribute is empty
bool IFunction::Attribute::isEmpty() const {
  try {
    return boost::get<std::string>(m_data).empty();
  } catch (...) {
    throw std::runtime_error("Trying to access a " + type() +
                             " attribute as string");
  }
}

872
873
874
875
876
877
namespace {
/**
 * Attribute visitor setting new value to an attribute
 */
class SetValue : public IFunction::AttributeVisitor<> {
public:
878
  /**
879
880
   * Constructor
   * @param value :: The value to set
881
   */
882
  explicit SetValue(const std::string &value) : m_value(value) {}
883
884
885

protected:
  /// Apply if string
886
  void apply(std::string &str) const override { str = m_value; }
887
  /// Apply if int
888
  void apply(int &i) const override {
889
890
891
892
893
894
895
896
    std::istringstream istr(m_value + " ");
    istr >> i;
    if (!istr.good())
      throw std::invalid_argument("Failed to set int attribute "
                                  "from string " +
                                  m_value);
  }
  /// Apply if double
897
  void apply(double &d) const override {
898
899
900
901
902
903
904
905
    std::istringstream istr(m_value + " ");
    istr >> d;
    if (!istr.good())
      throw std::invalid_argument("Failed to set double attribute "
                                  "from string " +
                                  m_value);
  }
  /// Apply if bool
906
  void apply(bool &b) const override {
907
908
909
    b = (m_value == "true" || m_value == "TRUE" || m_value == "1");
  }
  /// Apply if vector
910
  void apply(std::vector<double> &v) const override {
911
    if (m_value.empty() || m_value == "EMPTY") {
912
913
      v.clear();
      return;
914
    }
915
916
    if (m_value.size() > 2) {
      // check if the value is in barckets (...)
917
      if (m_value.front() == '(' && m_value.back() == ')') {
918
919
920
        m_value.erase(0, 1);
        m_value.erase(m_value.size() - 1);
      }
921
    }
922
923
    Mantid::Kernel::StringTokenizer tokenizer(
        m_value, ",", Mantid::Kernel::StringTokenizer::TOK_TRIM);
924
925
926
    v.resize(tokenizer.count());
    for (size_t i = 0; i < v.size(); ++i) {
      v[i] = boost::lexical_cast<double>(tokenizer[i]);
927
    }
928
  }
929

930
private:
LamarMoore's avatar
LamarMoore committed
931
  mutable std::string m_value; ///< the value as a string
932
};
LamarMoore's avatar
LamarMoore committed
933
} // namespace
934
935
936
937

/** Set value from a string. Throws exception if the string has wrong format
 * @param str :: String representation of the new value
 */
938
void IFunction::Attribute::fromString(const std::string &str) {
939
940
941
942
  SetValue tmp(str);
  apply(tmp);
}

943
944
945
/// Value of i-th active parameter. Override this method to make fitted
/// parameters different from the declared
double IFunction::activeParameter(size_t i) const {
946
947
  if (!isActive(i)) {
    throw std::runtime_error("Attempt to use an inactive parameter " +
948
                             parameterName(i));
949
950
951
952
  }
  return getParameter(i);
}

953
954
955
956
/// Set new value of i-th active parameter. Override this method to make fitted
/// parameters different from the declared
void IFunction::setActiveParameter(size_t i, double value) {
  if (!isActive(i)) {
957
958
    throw std::runtime_error("Attempt to use an inactive parameter " +
                             parameterName(i));
959
  }
960
  setParameter(i, value);
961
962
}

963
964
965
966
/**
 * Returns the name of an active parameter.
 * @param i :: Index of a parameter. The parameter must be active.
 */
967
968
std::string IFunction::nameOfActive(size_t i) const {
  if (!isActive(i)) {
969
970
    throw std::runtime_error("Attempt to use an inactive parameter " +
                             parameterName(i));
971
972
973
974
  }
  return parameterName(i);
}

975
976
977
978
/**
 * Returns the description of an active parameter.
 * @param i :: Index of a parameter. The parameter must be active.
 */
979
980
std::string IFunction::descriptionOfActive(size_t i) const {
  if (!isActive(i)) {
981
982
    throw std::runtime_error("Attempt to use an inactive parameter " +
                             parameterName(i));
983
984
985
986
  }
  return parameterDescription(i);
}

987
/** Calculate numerical derivatives.
988
 * @param domain :: The domain of the function
989
990
 * @param jacobian :: A Jacobian matrix. It is expected to have dimensions of
 * domain.size() by nParams().
991
 */
992
993
void IFunction::calNumericalDeriv(const FunctionDomain &domain,
                                  Jacobian &jacobian) {
994
995
996
997
998
999
1000
  /*
   * There is a similar more specialized method for 1D functions in IFunction1D
   * but the method takes different parameters and uses slightly different
   * function calls in places making it difficult to share code. Please also
   * consider that method when updating this.
   */