MultiDomainFunctionTest.h 16 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
  void zero() override {}
85
86
87
};
}

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

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

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

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

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

111
112
113
    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));
114
115
  }

116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
  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);
131
132

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

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

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

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

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

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

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

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

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

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

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

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

    FunctionValues values(domain);

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

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

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

    FunctionValues values(domain);
248
249
250
251
252
253
254
255
256
257
258
    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) {
259
260
261
262
263
      TS_ASSERT_EQUALS(values.getCalculated(i), A + B * d0[i]);
    }

    A = multi.getFunction(1)->getParameter("A");
    B = multi.getFunction(1)->getParameter("B");
264
265
266
267
    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]);
268
269
270
271
    }

    A = multi.getFunction(2)->getParameter("A");
    B = multi.getFunction(2)->getParameter("B");
272
273
274
275
    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]);
276
277
278
    }
  }

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

    FunctionValues values(domain);
286
287
288
289
290
291
292
293
294
295
296
    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) {
297
298
299
300
301
      TS_ASSERT_EQUALS(values.getCalculated(i), A + B * d0[i]);
    }

    A = multi.getFunction(1)->getParameter("A");
    B = multi.getFunction(1)->getParameter("B");
302
303
304
305
    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]);
306
307
308
309
    }

    A = multi.getFunction(2)->getParameter("A");
    B = multi.getFunction(2)->getParameter("B");
310
311
312
313
    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]);
314
315
316
    }
  }

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

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

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

335
336
337
338
339
340
341
342
    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]);
343
344
    }

345
346
347
348
349
350
351
352
    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]);
353
354
355
    }
  }

356
357
358
359
360
361
362
363
  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));
364
365

    FunctionValues values(domain);
366
367
368
369
370
371
372
373
374
375
    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));
376
    double checksum = 0;
377
    for (size_t i = 0; i < 9; ++i) {
378
379
380
      TS_ASSERT_EQUALS(values.getCalculated(i), A + B * d0[i]);
      checksum += values.getCalculated(i);
    }
381
    TS_ASSERT_DIFFERS(checksum, 0);
382
383
384
385

    checksum = 0;
    A = mfun->getFunction(1)->getParameter("A");
    B = mfun->getFunction(1)->getParameter("B");
386
387
388
389
    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]);
390
391
      checksum += values.getCalculated(i);
    }
392
    TS_ASSERT_DIFFERS(checksum, 0);
393
394
395
396

    checksum = 0;
    A = mfun->getFunction(2)->getParameter("A");
    B = mfun->getFunction(2)->getParameter("B");
397
398
399
400
    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]);
401
402
      checksum += values.getCalculated(i);
    }
403
    TS_ASSERT_DIFFERS(checksum, 0);
404
405
  }

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

438
439
440
441
442
443
  void test_clone_preserves_domains() {
    const auto copy = multi.clone();
    TS_ASSERT_EQUALS(copy->getNumberDomains(), multi.getNumberDomains());
  }

  void test_string_representation() {
Roman Tolchenov's avatar
Roman Tolchenov committed
444
445
446
447
448
449
    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)";
450
451
452
453
    TS_ASSERT_EQUALS(multi.asString(), expected);
    TS_ASSERT_EQUALS(multi.asString(), multi.clone()->asString());
  }

Roman Tolchenov's avatar
Roman Tolchenov committed
454
455
456
457
458
459
460
461
462
463
464
  void test_equivalent_functions() {
    std::string ini =
        "composite=MultiDomainFunction;"
        "name=MultiDomainFunctionTest_Function,A=1,B=2,$domains=i;"
        "name=MultiDomainFunctionTest_Function,A=3,B=4,$domains=i;ties=(f1.A="
        "f0.B)";
    auto mfun = boost::dynamic_pointer_cast<CompositeFunction>(
        FunctionFactory::Instance().createInitialized(ini));

    auto eqFuns = mfun->createEquivalentFunctions();
    TS_ASSERT_EQUALS(eqFuns.size(), 2);
465
466
467
468
    TS_ASSERT_EQUALS(eqFuns[0]->asString(),
                     "name=MultiDomainFunctionTest_Function,A=1,B=2");
    TS_ASSERT_EQUALS(eqFuns[1]->asString(),
                     "name=MultiDomainFunctionTest_Function,A=2,B=4");
Roman Tolchenov's avatar
Roman Tolchenov committed
469
470
  }

471
472
473
474
475
476
private:
  MultiDomainFunction multi;
  JointDomain domain;
};

#endif /*MULTIDOMAINFUNCTIONTEST_H_*/