MultiDomainFunctionTest.h 15.2 KB
Newer Older
1
2
3
4
5
6
7
8
9
#ifndef MULTIDOMAINFUNCTIONTEST_H_
#define MULTIDOMAINFUNCTIONTEST_H_

#include "MantidAPI/FunctionDomain1D.h"
#include "MantidAPI/FunctionValues.h"
#include "MantidAPI/MultiDomainFunction.h"
#include "MantidAPI/JointDomain.h"
#include "MantidAPI/IFunction1D.h"
#include "MantidAPI/ParamFunction.h"
10
#include "MantidAPI/FunctionFactory.h"
11
12
13
14
15
16
17
18

#include <cxxtest/TestSuite.h>
#include <boost/make_shared.hpp>
#include <algorithm>

using namespace Mantid;
using namespace Mantid::API;

19
20
class MultiDomainFunctionTest_Function : public IFunction1D,
                                         public ParamFunction {
21
public:
22
23
24
  MultiDomainFunctionTest_Function() : IFunction1D(), ParamFunction() {
    this->declareParameter("A", 0);
    this->declareParameter("B", 0);
25
  }
26
  std::string name() const override {
27
28
29
    return "MultiDomainFunctionTest_Function";
  }

30
protected:
31
32
  void function1D(double *out, const double *xValues,
                  const size_t nData) const override {
33
34
35
    const double A = getParameter(0);
    const double B = getParameter(1);

36
    for (size_t i = 0; i < nData; ++i) {
37
38
39
40
      double x = xValues[i];
      out[i] = A + B * x;
    }
  }
41
42
  void functionDeriv1D(Jacobian *out, const double *xValues,
                       const size_t nData) override {
43
    for (size_t i = 0; i < nData; ++i) {
44
      double x = xValues[i];
45
46
      out->set(i, 0, x);
      out->set(i, 1, 1.0);
47
48
49
50
    }
  }
};

51
DECLARE_FUNCTION(MultiDomainFunctionTest_Function)
52

53
54
namespace {

55
class JacobianToTestNumDeriv : public Jacobian {
56
57
  size_t n[3];
  size_t np;
58

59
60
public:
  double off_diag;
61
  JacobianToTestNumDeriv() : np(2), off_diag(0.0) {
62
    // sizes of the three domains
63
    n[0] = 9;
64
65
66
    n[1] = 10;
    n[2] = 11;
  }
67
  void set(size_t iY, size_t iP, double value) override {
68
69
70
    // domain index the data point #iY comes from
    size_t jY = 2;
    size_t size = 0;
71
    for (size_t k = 0; k < 3; ++k) {
72
      size += n[k];
73
      if (iY < size) {
74
75
76
77
78
79
        jY = k;
        break;
      }
    }
    // domain index of function that has parameter #iP
    auto jP = iP / np;
80
81
    if (jY != jP)
      off_diag += value;
82
  }
83
  double get(size_t, size_t) override { return 0.0; }
84
85
86
};
}

87
class MultiDomainFunctionTest : public CxxTest::TestSuite {
88
public:
89
90
  // This pair of boilerplate methods prevent the suite being created statically
  // This means the constructor isn't called when running other tests
91
92
93
94
  static MultiDomainFunctionTest *createSuite() {
    return new MultiDomainFunctionTest();
  }
  static void destroySuite(MultiDomainFunctionTest *suite) { delete suite; }
95

96
  MultiDomainFunctionTest() {
97
98
99
100
    multi.addFunction(boost::make_shared<MultiDomainFunctionTest_Function>());
    multi.addFunction(boost::make_shared<MultiDomainFunctionTest_Function>());
    multi.addFunction(boost::make_shared<MultiDomainFunctionTest_Function>());

101
102
    multi.getFunction(0)->setParameter("A", 0);
    multi.getFunction(0)->setParameter("B", 1);
103

104
105
    multi.getFunction(1)->setParameter("A", 1);
    multi.getFunction(1)->setParameter("B", 2);
106

107
108
    multi.getFunction(2)->setParameter("A", 2);
    multi.getFunction(2)->setParameter("B", 3);
109

110
111
112
    domain.addDomain(boost::make_shared<FunctionDomain1DVector>(0, 1, 9));
    domain.addDomain(boost::make_shared<FunctionDomain1DVector>(1, 2, 10));
    domain.addDomain(boost::make_shared<FunctionDomain1DVector>(2, 3, 11));
113
114
  }

115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
  void test_calc_domain0_only() {
    multi.setDomainIndex(0, 0);
    multi.setDomainIndices(1, std::vector<size_t>());
    multi.setDomainIndices(2, std::vector<size_t>());
    // multi.setDomainIndex(1,1);
    // multi.setDomainIndex(2,2);

    // std::vector<size_t> ii;
    // ii.push_back(0);
    // ii.push_back(1);
    // multi.setDomainIndices(1,ii);
    // ii.clear();
    // ii.push_back(0);
    // ii.push_back(2);
    // multi.setDomainIndices(2,ii);
130
131

    FunctionValues values(domain);
132
    multi.function(domain, values);
133
134
135

    const double A = multi.getFunction(0)->getParameter("A");
    const double B = multi.getFunction(0)->getParameter("B");
136
137
138
    const FunctionDomain1D &d =
        static_cast<const FunctionDomain1D &>(domain.getDomain(0));
    for (size_t i = 0; i < 9; ++i) {
139
140
      TS_ASSERT_EQUALS(values.getCalculated(i), A + B * d[i]);
    }
141
    for (size_t i = 9; i < 19; ++i) {
142
143
      TS_ASSERT_EQUALS(values.getCalculated(i), 0);
    }
144
    for (size_t i = 19; i < 30; ++i) {
145
146
147
148
      TS_ASSERT_EQUALS(values.getCalculated(i), 0);
    }
  }

149
150
151
152
  void test_calc_domain1_only() {
    multi.setDomainIndex(0, 1);
    multi.setDomainIndices(1, std::vector<size_t>());
    multi.setDomainIndices(2, std::vector<size_t>());
153
154

    FunctionValues values(domain);
155
    multi.function(domain, values);
156
157
158

    const double A = multi.getFunction(0)->getParameter("A");
    const double B = multi.getFunction(0)->getParameter("B");
159
160
161
    const FunctionDomain1D &d =
        static_cast<const FunctionDomain1D &>(domain.getDomain(1));
    for (size_t i = 0; i < 9; ++i) {
162
163
      TS_ASSERT_EQUALS(values.getCalculated(i), 0);
    }
164
165
    for (size_t i = 9; i < 19; ++i) {
      TS_ASSERT_EQUALS(values.getCalculated(i), A + B * d[i - 9]);
166
    }
167
    for (size_t i = 19; i < 30; ++i) {
168
169
170
171
      TS_ASSERT_EQUALS(values.getCalculated(i), 0);
    }
  }

172
173
174
175
  void test_calc_domain2_only() {
    multi.setDomainIndex(0, 2);
    multi.setDomainIndices(1, std::vector<size_t>());
    multi.setDomainIndices(2, std::vector<size_t>());
176
177

    FunctionValues values(domain);
178
    multi.function(domain, values);
179
180
181

    const double A = multi.getFunction(0)->getParameter("A");
    const double B = multi.getFunction(0)->getParameter("B");
182
183
184
    const FunctionDomain1D &d =
        static_cast<const FunctionDomain1D &>(domain.getDomain(2));
    for (size_t i = 0; i < 9; ++i) {
185
186
      TS_ASSERT_EQUALS(values.getCalculated(i), 0);
    }
187
    for (size_t i = 9; i < 19; ++i) {
188
189
      TS_ASSERT_EQUALS(values.getCalculated(i), 0);
    }
190
191
    for (size_t i = 19; i < 30; ++i) {
      TS_ASSERT_EQUALS(values.getCalculated(i), A + B * d[i - 19]);
192
193
194
    }
  }

195
  void test_calc_all_domains() {
196
    multi.clearDomainIndices();
197
198
    multi.setDomainIndices(1, std::vector<size_t>());
    multi.setDomainIndices(2, std::vector<size_t>());
199
200

    FunctionValues values(domain);
201
    multi.function(domain, values);
202
203
204

    const double A = multi.getFunction(0)->getParameter("A");
    const double B = multi.getFunction(0)->getParameter("B");
205
206
207
    const FunctionDomain1D &d0 =
        static_cast<const FunctionDomain1D &>(domain.getDomain(0));
    for (size_t i = 0; i < 9; ++i) {
208
209
      TS_ASSERT_EQUALS(values.getCalculated(i), A + B * d0[i]);
    }
210
211
212
213
    const FunctionDomain1D &d1 =
        static_cast<const FunctionDomain1D &>(domain.getDomain(1));
    for (size_t i = 9; i < 19; ++i) {
      TS_ASSERT_EQUALS(values.getCalculated(i), A + B * d1[i - 9]);
214
    }
215
216
217
218
    const FunctionDomain1D &d2 =
        static_cast<const FunctionDomain1D &>(domain.getDomain(2));
    for (size_t i = 19; i < 30; ++i) {
      TS_ASSERT_EQUALS(values.getCalculated(i), A + B * d2[i - 19]);
219
220
221
    }
  }

222
223
224
  void test_set_wrong_index() {
    multi.setDomainIndices(1, std::vector<size_t>());
    multi.setDomainIndices(2, std::vector<size_t>());
225
226
227

    FunctionValues values(domain);

228
229
    multi.setDomainIndex(0, 3);
    TS_ASSERT_THROWS(multi.function(domain, values), std::invalid_argument);
230

231
232
    multi.setDomainIndex(0, 4);
    TS_ASSERT_THROWS(multi.function(domain, values), std::invalid_argument);
233
234
  }

235
236
  void test_calc() {
    multi.setDomainIndex(0, 0);
237
238
239
    std::vector<size_t> ii;
    ii.push_back(0);
    ii.push_back(1);
240
    multi.setDomainIndices(1, ii);
241
242
243
    ii.clear();
    ii.push_back(0);
    ii.push_back(2);
244
    multi.setDomainIndices(2, ii);
245
246

    FunctionValues values(domain);
247
248
249
250
251
252
253
254
255
256
257
    multi.function(domain, values);

    double A = multi.getFunction(0)->getParameter("A") +
               multi.getFunction(1)->getParameter("A") +
               multi.getFunction(2)->getParameter("A");
    double B = multi.getFunction(0)->getParameter("B") +
               multi.getFunction(1)->getParameter("B") +
               multi.getFunction(2)->getParameter("B");
    const FunctionDomain1D &d0 =
        static_cast<const FunctionDomain1D &>(domain.getDomain(0));
    for (size_t i = 0; i < 9; ++i) {
258
259
260
261
262
      TS_ASSERT_EQUALS(values.getCalculated(i), A + B * d0[i]);
    }

    A = multi.getFunction(1)->getParameter("A");
    B = multi.getFunction(1)->getParameter("B");
263
264
265
266
    const FunctionDomain1D &d1 =
        static_cast<const FunctionDomain1D &>(domain.getDomain(1));
    for (size_t i = 9; i < 19; ++i) {
      TS_ASSERT_EQUALS(values.getCalculated(i), A + B * d1[i - 9]);
267
268
269
270
    }

    A = multi.getFunction(2)->getParameter("A");
    B = multi.getFunction(2)->getParameter("B");
271
272
273
274
    const FunctionDomain1D &d2 =
        static_cast<const FunctionDomain1D &>(domain.getDomain(2));
    for (size_t i = 19; i < 30; ++i) {
      TS_ASSERT_EQUALS(values.getCalculated(i), A + B * d2[i - 19]);
275
276
277
    }
  }

278
  void test_attribute() {
279
    multi.clearDomainIndices();
280
281
282
    multi.setLocalAttributeValue(0, "domains", "i");
    multi.setLocalAttributeValue(1, "domains", "0,1");
    multi.setLocalAttributeValue(2, "domains", "0,2");
283
284

    FunctionValues values(domain);
285
286
287
288
289
290
291
292
293
294
295
    multi.function(domain, values);

    double A = multi.getFunction(0)->getParameter("A") +
               multi.getFunction(1)->getParameter("A") +
               multi.getFunction(2)->getParameter("A");
    double B = multi.getFunction(0)->getParameter("B") +
               multi.getFunction(1)->getParameter("B") +
               multi.getFunction(2)->getParameter("B");
    const FunctionDomain1D &d0 =
        static_cast<const FunctionDomain1D &>(domain.getDomain(0));
    for (size_t i = 0; i < 9; ++i) {
296
297
298
299
300
      TS_ASSERT_EQUALS(values.getCalculated(i), A + B * d0[i]);
    }

    A = multi.getFunction(1)->getParameter("A");
    B = multi.getFunction(1)->getParameter("B");
301
302
303
304
    const FunctionDomain1D &d1 =
        static_cast<const FunctionDomain1D &>(domain.getDomain(1));
    for (size_t i = 9; i < 19; ++i) {
      TS_ASSERT_EQUALS(values.getCalculated(i), A + B * d1[i - 9]);
305
306
307
308
    }

    A = multi.getFunction(2)->getParameter("A");
    B = multi.getFunction(2)->getParameter("B");
309
310
311
312
    const FunctionDomain1D &d2 =
        static_cast<const FunctionDomain1D &>(domain.getDomain(2));
    for (size_t i = 19; i < 30; ++i) {
      TS_ASSERT_EQUALS(values.getCalculated(i), A + B * d2[i - 19]);
313
314
315
    }
  }

316
  void test_attribute_domain_range() {
317
    multi.clearDomainIndices();
318
    multi.setLocalAttributeValue(0, "domains", "0-2");
319
    return;
320
321
    multi.setLocalAttributeValue(1, "domains", "i");
    multi.setLocalAttributeValue(2, "domains", "i");
322
323

    FunctionValues values(domain);
324
    multi.function(domain, values);
325
326
327

    double A = multi.getFunction(0)->getParameter("A");
    double B = multi.getFunction(0)->getParameter("B");
328
329
330
    const FunctionDomain1D &d0 =
        static_cast<const FunctionDomain1D &>(domain.getDomain(0));
    for (size_t i = 0; i < 9; ++i) {
331
332
333
      TS_ASSERT_EQUALS(values.getCalculated(i), A + B * d0[i]);
    }

334
335
336
337
338
339
340
341
    A = multi.getFunction(0)->getParameter("A") +
        multi.getFunction(1)->getParameter("A");
    B = multi.getFunction(0)->getParameter("B") +
        multi.getFunction(1)->getParameter("B");
    const FunctionDomain1D &d1 =
        static_cast<const FunctionDomain1D &>(domain.getDomain(1));
    for (size_t i = 9; i < 19; ++i) {
      TS_ASSERT_EQUALS(values.getCalculated(i), A + B * d1[i - 9]);
342
343
    }

344
345
346
347
348
349
350
351
    A = multi.getFunction(0)->getParameter("A") +
        multi.getFunction(2)->getParameter("A");
    B = multi.getFunction(0)->getParameter("B") +
        multi.getFunction(2)->getParameter("B");
    const FunctionDomain1D &d2 =
        static_cast<const FunctionDomain1D &>(domain.getDomain(2));
    for (size_t i = 19; i < 30; ++i) {
      TS_ASSERT_EQUALS(values.getCalculated(i), A + B * d2[i - 19]);
352
353
354
    }
  }

355
356
357
358
359
360
361
362
  void test_attribute_in_FunctionFactory() {
    std::string ini =
        "composite=MultiDomainFunction;"
        "name=MultiDomainFunctionTest_Function,A=0,B=1,$domains=i;"
        "name=MultiDomainFunctionTest_Function,A=1,B=2,$domains=(0,1);"
        "name=MultiDomainFunctionTest_Function,A=2,B=3,$domains=(0,2)";
    auto mfun = boost::dynamic_pointer_cast<CompositeFunction>(
        FunctionFactory::Instance().createInitialized(ini));
363
364

    FunctionValues values(domain);
365
366
367
368
369
370
371
372
373
374
    mfun->function(domain, values);

    double A = mfun->getFunction(0)->getParameter("A") +
               mfun->getFunction(1)->getParameter("A") +
               mfun->getFunction(2)->getParameter("A");
    double B = mfun->getFunction(0)->getParameter("B") +
               mfun->getFunction(1)->getParameter("B") +
               mfun->getFunction(2)->getParameter("B");
    const FunctionDomain1D &d0 =
        static_cast<const FunctionDomain1D &>(domain.getDomain(0));
375
    double checksum = 0;
376
    for (size_t i = 0; i < 9; ++i) {
377
378
379
      TS_ASSERT_EQUALS(values.getCalculated(i), A + B * d0[i]);
      checksum += values.getCalculated(i);
    }
380
    TS_ASSERT_DIFFERS(checksum, 0);
381
382
383
384

    checksum = 0;
    A = mfun->getFunction(1)->getParameter("A");
    B = mfun->getFunction(1)->getParameter("B");
385
386
387
388
    const FunctionDomain1D &d1 =
        static_cast<const FunctionDomain1D &>(domain.getDomain(1));
    for (size_t i = 9; i < 19; ++i) {
      TS_ASSERT_EQUALS(values.getCalculated(i), A + B * d1[i - 9]);
389
390
      checksum += values.getCalculated(i);
    }
391
    TS_ASSERT_DIFFERS(checksum, 0);
392
393
394
395

    checksum = 0;
    A = mfun->getFunction(2)->getParameter("A");
    B = mfun->getFunction(2)->getParameter("B");
396
397
398
399
    const FunctionDomain1D &d2 =
        static_cast<const FunctionDomain1D &>(domain.getDomain(2));
    for (size_t i = 19; i < 30; ++i) {
      TS_ASSERT_EQUALS(values.getCalculated(i), A + B * d2[i - 19]);
400
401
      checksum += values.getCalculated(i);
    }
402
    TS_ASSERT_DIFFERS(checksum, 0);
403
404
  }

405
  void test_derivatives_for_tied_parameters() {
406
    multi.clearDomainIndices();
407
408
409
    multi.setDomainIndex(0, 0);
    multi.setDomainIndex(1, 1);
    multi.setDomainIndex(2, 2);
410
411
    {
      JacobianToTestNumDeriv jacobian;
412
413
      multi.functionDeriv(domain, jacobian);
      TS_ASSERT_EQUALS(jacobian.off_diag, 0.0);
414
    }
415
    multi.setAttributeValue("NumDeriv", true);
416
417
    {
      JacobianToTestNumDeriv jacobian;
418
419
      multi.functionDeriv(domain, jacobian);
      TS_ASSERT_EQUALS(jacobian.off_diag, 0.0);
420
    }
421
422
423
    multi.tie("f1.A", "f0.A");
    multi.tie("f2.A", "f0.A");
    multi.setAttributeValue("NumDeriv", false);
424
425
    {
      JacobianToTestNumDeriv jacobian;
426
427
      multi.functionDeriv(domain, jacobian);
      TS_ASSERT_EQUALS(jacobian.off_diag, 0.0);
428
    }
429
    multi.setAttributeValue("NumDeriv", true);
430
431
    {
      JacobianToTestNumDeriv jacobian;
432
433
      multi.functionDeriv(domain, jacobian);
      TS_ASSERT_DIFFERS(jacobian.off_diag, 0.0);
434
435
436
    }
  }

437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
  void test_clone_preserves_domains() {
    const auto copy = multi.clone();
    TS_ASSERT_EQUALS(copy->getNumberDomains(), multi.getNumberDomains());
  }

  void test_string_representation() {
    const std::string expected =
        "composite=MultiDomainFunction,NumDeriv=true;"
        "name=MultiDomainFunctionTest_Function,A=0,B=1,$domains=i;"
        "name=MultiDomainFunctionTest_Function,A=0,B=2,$domains=i;"
        "name=MultiDomainFunctionTest_Function,A=0,B=3,$domains=i;ties=(f1.A="
        "f0.A,f2.A=f0.A)";
    TS_ASSERT_EQUALS(multi.asString(), expected);
    TS_ASSERT_EQUALS(multi.asString(), multi.clone()->asString());
  }

453
454
455
456
457
458
private:
  MultiDomainFunction multi;
  JointDomain domain;
};

#endif /*MULTIDOMAINFUNCTIONTEST_H_*/