Helios++
Helios software for LiDAR simulations
ExprTreeTest.h
1 #pragma once
2 
3 #include <BaseTest.h>
4 #include <adt/exprtree/UnivarExprTreeNode.h>
5 #include <adt/exprtree/UnivarExprTreeStringFactory.h>
6 #include <adt/bintree/BinaryTreeFastDepthIterator.h>
7 
8 #include <memory>
9 #include <vector>
10 
11 namespace HeliosTests{
12 
18 class ExprTreeTest : public BaseTest{
19 public:
20  // *** ATTRIBUTES *** //
21  // ******************** //
25  double eps = 0.00001; // Decimal precision for validation purposes
26 
27  // *** CONSTRUCTOR *** //
28  // ******************** //
32  ExprTreeTest() : BaseTest("Expression tree test"){}
33 
34  // *** R U N *** //
35  // *************** //
39  bool run() override;
40 
41  // *** SUB-TESTS *** //
42  // ******************* //
47  bool testUnivarExprTree();
48 
49  // *** INNER METHODS *** //
50  // *********************** //
55  template <typename InputType, typename OutputType>
56  bool validate(
57  std::shared_ptr<IExprTreeNode<InputType, OutputType>> f,
58  std::vector<InputType> const &t,
59  std::vector<OutputType> const &y
60  ){
61  std::vector<OutputType> ft;
62  for(InputType const & ti : t) ft.push_back(f->eval(ti));
63  size_t const m = ft.size();
64  size_t const n = y.size();
65  if(m!=n) return false;
66  for(size_t i = 0 ; i < m ; ++i){
67  double const absDiff = std::fabs(ft[i]-y[i]);
68  if(absDiff > eps) return false;
69  }
70  return true;
71  }
76  template <typename InputType, typename OutputType>
77  bool validate(
78  std::shared_ptr<IExprTreeNode<InputType, OutputType>> f,
79  std::shared_ptr<IExprTreeNode<InputType, OutputType>> g,
80  std::vector<InputType> const &t
81  ){
82  for(double const &ti : t){
83  double const absDiff = std::abs(f->eval(ti)-g->eval(ti));
84  if(absDiff > eps) return false;
85  }
86  return true;
87  }
93  template <typename NumericType>
95  std::shared_ptr<UnivarExprTreeNode<NumericType>> f,
96  size_t const expectedPowCount,
97  size_t const expectedIPowCount
98  ){
99  size_t powCount = 0, ipowCount = 0;
101  f.get()
102  );
103  while(btdi.hasNext()){
104  UnivarExprTreeNode<NumericType> *node = btdi.next();
105  if(!node->isOperator()) continue; // Ignore non-operator nodes
106  if(node->op == node->OP_POW) ++powCount;
107  if(node->op == node->OP_IPOW) ++ipowCount;
108  }
109  return powCount == expectedPowCount && ipowCount == expectedIPowCount;
110  }
116  template <typename NumericType>
118  std::shared_ptr<IExprTreeNode<NumericType, NumericType>> f,
119  size_t const expectedPowCount,
120  size_t const expectedIPowCount
121  ){
122  return validatePowIPow(
123  std::static_pointer_cast<UnivarExprTreeNode<NumericType>>(f),
124  expectedPowCount,
125  expectedIPowCount
126  );
127  }
128 };
129 
130 // *** R U N *** //
131 // *************** //
133  bool passed = true;
134  passed = passed && testUnivarExprTree();
135  return passed;
136 }
137 
138 
139 // *** SUB-TESTS *** //
140 // ******************* //
142  // Prepare tests
144  std::shared_ptr<IExprTreeNode<double, double>> node;
145  std::shared_ptr<IExprTreeNode<double, double>> node2;
146  std::vector<double> t({ // Input domain for all cases
147  -10, -3.14, -2.72, -1.1, -0.34, 0, 0.34, 1.1, 2.72, 3.14, 10
148  });
149  std::vector<double> y; // Expected output (distinct for each case)
150 
151 
152  // Test case 1: 1+(t-1)^2
153  node = uetsf.makeShared("1+(t-1)^2");
154  y = std::vector<double>({
155  122.00000000, 18.13960000, 14.83840000, 5.41000000,
156  2.79560000, 2.00000000, 1.43560000, 1.01000000,
157  3.95840000, 5.57960000, 82.00000000
158  });
159  if(!validate<double, double>(node, t, y)) return false;
160 
161  // Test case 2: exp(t)+3*cos(1+2*t)
162  node = uetsf.makeShared("exp(t)+3*cos(1+2*t)");
163  y = std::vector<double>({
164  2.96615925e+00, 1.65614048e+00, -7.41224555e-01, 1.41994435e+00,
165  3.55947658e+00, 2.62090692e+00, 1.07798733e+00, 9.28169656e-03,
166  1.81435115e+01, 2.47328066e+01, 2.20248226e+04
167  });
168  if(!validate<double, double>(node, t, y)) return false;
169 
170  // Test case 3: ln(t/2)*sin(t)
171  node = uetsf.makeShared("ln(t/2)*sin(t)");
172  y = std::vector<double>({
173  NAN, NAN, NAN, NAN,
174  NAN, NAN, -5.90924735e-01, -5.32796735e-01,
175  1.25827096e-01, 7.18406901e-04, -8.75568201e-01
176  });
177  if(!validate<double, double>(node, t, y)) return false;
178 
179 
180 
181  // Test case 4: acos(1/3)+t/pi
182  node = uetsf.makeShared("acos(1/3)+t/pi");
183  y = std::vector<double>({
184  -1.95213944, 0.23146637, 0.36515653, 0.88081854, 1.12273406,
185  1.23095942, 1.33918478, 1.58110029, 2.09676231, 2.23045246,
186  4.41405828
187  });
188  if(!validate<double, double>(node, t, y)) return false;
189 
190  // Test case 5: 5*t
191  node = uetsf.makeShared("5*t");
192  y = std::vector<double>({
193  -50.00000000, -15.70000000, -13.60000000, -5.50000000,
194  -1.70000000, 0.00000000, 1.70000000, 5.50000000,
195  13.60000000, 15.70000000, 50.00000000
196  });
197  if(!validate<double, double>(node, t, y)) return false;
198 
199 
200  // Test case 6: t
201  node = uetsf.makeShared("t");
202  y = std::vector<double>({
203  -10.00000000, -3.14000000, -2.72000000, -1.10000000,
204  -0.34000000, 0.00000000, 0.34000000, 1.10000000,
205  2.72000000, 3.14000000, 10.00000000
206  });
207  if(!validate<double, double>(node, t, y)) return false;
208 
209  // Test case 7: 7
210  node = uetsf.makeShared("7");
211  y = std::vector<double>({7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7});
212  validate<double, double>(node, t, y);
213 
214  // Test case 8: 4*t^3 + 3*t^2 + 2*t - 1
215  node = uetsf.makeShared("4*t^3 + 3*t^2 + 2*t - 1");
216  y = std::vector<double>({
217  -3.72100000e+03, -1.01537776e+02, -6.47393920e+01, -4.89400000e+00,
218  -1.49041600e+00, -1.00000000e+00, 1.84016000e-01, 1.01540000e+01,
219  1.07129792e+02, 1.58695376e+02, 4.31900000e+03
220  });
221  if(!validate<double, double>(node, t, y)) return false;
222 
223  // Test case 9: 3600 - t^(t+1)
224  node = uetsf.makeShared("3600 - t^(t+1)");
225  y = std::vector<double>({
226  3.60000000e+03, NAN, NAN, NAN,
227  NAN, 3.60000000e+03, 3.59976440e+03, 3.59877841e+03,
228  3.55863850e+03, 3.48589919e+03, -9.99999964e+10
229  });
230  if(!validate<double, double>(node, t, y)) return false;
231 
232  // Test case 10: cos(t)
233  node = uetsf.makeShared("cos(t)");
234  y = std::vector<double>({
235  -0.83907153, -0.99999873, -0.91243836, 0.45359612, 0.94275467,
236  1.00000000, 0.94275467, 0.45359612, -0.91243836, -0.99999873,
237  -0.83907153
238  });
239  if(!validate<double, double>(node, t, y)) return false;
240 
241  // Test case 11: sin(t^2 + 3)
242  node = uetsf.makeShared("sin(t^2 + 3)");
243  y = std::vector<double>({
244  0.62298863, 0.28904527, -0.82692784, -0.87643472, 0.02598973,
245  0.14112001, 0.02598973, -0.87643472, -0.82692784, 0.28904527,
246  0.62298863
247  });
248  if(!validate<double, double>(node, t, y)) return false;
249 
250  // Test case 12: 3 + 5 * 7 + 4
251  node = uetsf.makeShared("3.1 + 5.0 * 7 +4.4");
252  y = std::vector<double>({
253  42.5, 42.5, 42.5, 42.5, 42.5, 42.5, 42.5, 42.5, 42.5, 42.5, 42.5
254  });
255  if(!validate<double, double>(node, t, y)) return false;
256 
257  // Test case 13: -t
258  node = uetsf.makeShared("-t");
259  y = std::vector<double>({
260  10.00000000, 3.14000000, 2.72000000, 1.10000000,
261  0.34000000, -0.00000000, -0.34000000, -1.10000000,
262  -2.72000000, -3.14000000, -10.00000000
263  });
264  if(!validate<double, double>(node, t, y)) return false;
265 
266  // Test case 14: -1
267  node = uetsf.makeShared("-1");
268  y = std::vector<double>({-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1});
269  if(!validate<double, double>(node, t, y)) return false;
270 
271  // Test case 15: -t*7
272  node = uetsf.makeShared("-t*7");
273  y = std::vector<double>({
274  70.00000000, 21.98000000, 19.04000000, 7.70000000,
275  2.38000000, -0.00000000, -2.38000000, -7.70000000,
276  -19.04000000, -21.98000000, -70.00000000
277  });
278  if(!validate<double, double>(node, t, y)) return false;
279 
280  // Test case 16: -1+7
281  node = uetsf.makeShared("-1 +7");
282  y = std::vector<double>({6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6});
283  if(!validate<double, double>(node, t, y)) return false;
284 
285  // Test case 17: ((t-1)*((1+t) / 2))/(t+0.01)^2
286  node = uetsf.makeShared("((t-1)*((1+t) / 2))/(t+0.01)^2");
287  y = std::vector<double>({
288  4.95991487e-01, 4.52163439e-01, 4.35614983e-01, 8.83763993e-02,
289  -4.06060606e+00, -5.00000000e+03, -3.60979592e+00, 8.52203555e-02,
290  4.29255726e-01, 4.46439909e-01, 4.94011483e-01
291  });
292  if(!validate<double, double>(node, t, y)) return false;
293 
294  // Test case 18: (pi/3)^(abs(t)^(e-1))
295  node = uetsf.makeShared("(pi/3)^(abs(t)^(e-1))");
296  y = std::vector<double>({
297  11.14208743, 1.39014251, 1.29353943, 1.05582653, 1.00725079,
298  1.00000000, 1.00725079, 1.05582653, 1.29353943, 1.39014251,
299  11.14208743
300  });
301  if(!validate<double, double>(node, t, y)) return false;
302 
303  // Test case 19: atan2(t, 1) + pi
304  node = uetsf.makeShared("atan2(t, 1) +pi");
305  y = std::vector<double>({
306  1.67046498, 1.87911199, 1.92310505, 2.30861139, 2.81385415,
307  3.14159265, 3.46933116, 3.97457392, 4.36008026, 4.40407332,
308  4.61272033
309  });
310  if(!validate<double, double>(node, t, y)) return false;
311 
312  // Test case 20: t-(-t) = 2*t
313  node = uetsf.makeShared("t-(-t)");
314  node2 = uetsf.makeShared("2*t");
315  if(!validate<double, double>(node, node2, t)) return false;
316 
317  // Test case 21: t+(1)
318  node = uetsf.makeShared("t+(1)");
319  y = std::vector<double>({
320  -9.00000000, -2.14000000, -1.72000000, -0.10000000, 0.66000000,
321  1.00000000, 1.34000000, 2.10000000, 3.72000000, 4.14000000,
322  11.00000000
323  });
324  if(!validate<double, double>(node, t, y)) return false;
325 
326  // Test case 22: (-1)+t
327  node = uetsf.makeShared("(-1)+t");
328  y = std::vector<double>({
329  -11.00000000, -4.14000000, -3.72000000, -2.10000000,
330  -1.34000000, -1.00000000, -0.66000000, 0.10000000,
331  1.72000000, 2.14000000, 9.00000000
332  });
333  if(!validate<double, double>(node, t, y)) return false;
334 
335  // Test case 23: (-1+1)+t
336  node = uetsf.makeShared("(-1+1)+t");
337  if(!validate<double, double>(node, t, t)) return false;
338 
339  // Test case 24: (1-(-1))
340  node = uetsf.makeShared("(1-(-1))");
341  y = std::vector<double>({2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2});
342  if(!validate<double, double>(node, t, y)) return false;
343 
344  // Test case 25: (1-(1))
345  node = uetsf.makeShared("(1-(1))");
346  y = std::vector<double>({0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0});
347  if(!validate<double, double>(node, t, y)) return false;
348 
349  // Test case 26: abs(t) = abs(-t) = abs(-1*t)
350  node = uetsf.makeShared("abs(t)");
351  node2 = uetsf.makeShared("abs(-t)");
352  if(!validate<double, double>(node, node2, t)) return false;
353  node = uetsf.makeShared("abs(-1*t)");
354  if(!validate<double, double>(node, node2, t)) return false;
355 
356  // Test case 27: abs(-t) - abs(-1*t) + abs(t) - abs(-2*t+(t))
357  node = uetsf.makeShared("abs(-t) - abs(-1*t) + abs(t) - abs(-2*t+(t))");
358  y = std::vector<double>({0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0});
359  if(!validate<double, double>(node, t, y)) return false;
360 
361  // Test case 28: 1+atan2(3*t-1, 1)
362  node = uetsf.makeShared("1 + atan2(3*t-1, 1)");
363  y = std::vector<double>({
364  -0.53854944, -0.47512005, -0.46205665, -0.34229969, -0.11111695,
365  0.21460184, 1.01999733, 2.16066899, 2.43202915, 2.4525852 ,
366  2.53632723
367  });
368  if(!validate<double, double>(node, t, y)) return false;
369 
370  // Test case 29: 1+atan2(3*t-1, (t-1)*2)
371  node = uetsf.makeShared("1+atan2(3*t-1, (t-1)*2)");
372  y = std::vector<double>({
373  -1.18798772, -1.24225167, -1.25294846, -1.34443033, -1.49570776,
374  -1.67794504, 4.1264423 , 2.48405799, 2.12291318, 2.10052003,
375  2.01530259
376  });
377  if(!validate<double, double>(node, t, y)) return false;
378 
379  // Test case 30: t*atan2(-1, -t)
380  node = uetsf.makeShared("t*atan2(-1, -t)");
381  y = std::vector<double>({
382  0.99668652, 0.96811118, 0.95827973, 0.81159657,
383  0.42263966, -0. , -0.64550184, -2.64415535,
384  -7.58685229, -8.89648975, -30.41924001
385  });
386  if(!validate<double, double>(node, t, y)) return false;
387 
388  // Test case 31: t+(-(t-1)) = t+(-(-1+t))
389  node = uetsf.makeShared("t+(-(t-1))");
390  node2 = uetsf.makeShared("t+(-(-1+t))");
391  if(!validate<double, double>(node, node2, t)) return false;
392 
393  // Test case 32: -(t-1)*(t^2-2*t)/10^3
394  node = uetsf.makeShared("-(t-1)*(t^2-2*t)/10^3");
395  y = std::vector<double>({
396  1.3200000e+00, 6.6817944e-02, 4.7758848e-02, 7.1610000e-03,
397  1.0661040e-03, 0.0000000e+00, -3.7250400e-04, 9.9000000e-05,
398  -3.3684480e-03, -7.6603440e-03, -7.2000000e-01
399  });
400  if(!validate<double, double>(node, t, y)) return false;
401 
402  // Test case 33: -(-1)*(t^2-2*t)/10^3
403  node = uetsf.makeShared("-(-1)*(t^2-2*t)/10^3");
404  y = std::vector<double>({
405  0.12 , 0.0161396, 0.0128384, 0.00341 , 0.0007956,
406  0. , -0.0005644, -0.00099 , 0.0019584, 0.0035796,
407  0.08
408  });
409  if(!validate<double, double>(node, t, y)) return false;
410 
411  // Test case 34:
412  // (1000+((1-(1+sin(1/2+3*pi/3*t))*(2+t))*abs(t)^(2-1/2)))^(3/2)
413  node = uetsf.makeShared(
414  "(1000+((1-(1+sin(1/2+3*pi/3*t))*(2+t))*abs(t)^(2-1/2)))^(3/2)"
415  );
416  y = std::vector<double>({
417  52714.18945957, 32171.06013344, 31838.59857288, 31637.35056204,
418  31624.96953355, 31622.77660168, 31588.17650985, 31631.21333073,
419  30466.17238908, 31625.49824345, 10196.5206164
420  });
421  if(!validate<double, double>(node, t, y)) return false;
422 
423  // Test case 35: cos(3*t+sin(1+t)-5*(10+tan(t))^(2*sqrt(abs(t))))
424  node = uetsf.makeShared(
425  "cos(3*t+sin(1+t)-5*(10+tan(t))^(2*sqrt(abs(t))))"
426  );
427  y = std::vector<double>({
428  0.91282919, 0.97082196, 0.90930816, -0.9976885 , -0.01534952,
429  -0.52597404, 0.49827694, -0.99743291, 0.3236059 , 0.7942002 ,
430  -0.98567806
431  });
432  if(!validate<double, double>(node, t, y)) return false;
433 
434  // Test case 36:
435  // cos(3*t+atan2(t,1)-5*atan2(1/1+1-1,t/t*t)^(2*sqrt(abs(t))))
436  node = uetsf.makeShared(
437  "cos(3*t+atan2(t,1)-5*atan2(1/1+1-1,t/t*t)^(2*sqrt(abs(t))))"
438  );
439  y = std::vector<double>({
440  -0.45058035, -0.83062706, 0.94696144, -0.49875059, 0.79075789,
441  0.28366219, 0.37464262, 0.0799824 , -0.9787685 , -0.38051728,
442  0.99847693
443  });
444  if(!validate<double, double>(node, t, y)) return false;
445 
446  // Test case 37: (t+10)^2-(t+10)^3
447  node = uetsf.makeShared("(t+10)^2-(t+10)^3");
448  y = std::vector<double>({
449  0. , -275.769256, -332.829952, -625.759,
450  -808.113096 , -900. , -998.591704, -1244.421,
451  -1896.277248, -2096.087544, -7600.
452  });
453  if(!validate<double, double>(node, t, y)) return false;
454  if(!validatePowIPow<double>(node, 0, 2)) return false;
455 
456  // Test case 38: (t+10)^2.0000001
457  node = uetsf.makeShared("(t+10)^2.0000001");
458  y = std::vector<double>({
459  0. , 47.05960906, 52.99841052, 79.21001732,
460  93.31562116, 100.00002303, 106.91562498, 123.21002966,
461  161.79844115, 172.65964447, 400.00011983
462  });
463  if(!validate<double, double>(node, t, y)) return false;
464  if(!validatePowIPow<double>(node, 1, 0)) return false;
465 
466  // Test case 39: (t+10)^2.0000001-(t+10)^2
467  node = uetsf.makeShared("1000*((t+10)^2.0000001-(t+10)^2)");
468  y = std::vector<double>({
469  0. , 0.0090623 , 0.01052088, 0.01731571, 0.02116392,
470  0.02302585, 0.0249757 , 0.02965597, 0.04114818, 0.04447127,
471  0.11982931
472  });
473  if(!validate<double, double>(node, t, y)) return false;
474  if(!validatePowIPow<double>(node, 1, 1)) return false;
475 
476  // Test case 40: 5
477  node = uetsf.makeShared("5");
478  y = std::vector<double>({
479  5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5
480  });
481  if(!validate<double, double>(node, t, y)) return false;
482 
483  // On test passed
484  return true;
485 }
486 
487 
488 }
Fast depth first iterator for binary tree like nodes.
Definition: BinaryTreeFastDepthIterator.h:47
NodeType * next() override
Obtain the next node according to depth iteration criterion.
Definition: BinaryTreeFastDepthIterator.h:119
bool hasNext() const override
Check if the iterator has more nodes to visit (true) or not (false)
Definition: BinaryTreeFastDepthIterator.h:113
BaseTest class.
Definition: BaseTest.h:20
Expression tree test.
Definition: ExprTreeTest.h:18
ExprTreeTest()
Expression tree test constructor.
Definition: ExprTreeTest.h:32
bool validate(std::shared_ptr< IExprTreeNode< InputType, OutputType >> f, std::shared_ptr< IExprTreeNode< InputType, OutputType >> g, std::vector< InputType > const &t)
Validate .
Definition: ExprTreeTest.h:77
double eps
Decimal precision for validation purposes.
Definition: ExprTreeTest.h:25
bool testUnivarExprTree()
Test univariate expression tree.
Definition: ExprTreeTest.h:141
bool validatePowIPow(std::shared_ptr< IExprTreeNode< NumericType, NumericType >> f, size_t const expectedPowCount, size_t const expectedIPowCount)
Wrapper for validatePowIPow method providing automatic pointer casting.
Definition: ExprTreeTest.h:117
bool validatePowIPow(std::shared_ptr< UnivarExprTreeNode< NumericType >> f, size_t const expectedPowCount, size_t const expectedIPowCount)
Validate given tree as UnivarExprTreeNode contains exactly as many POW and IPOW nodes as specified.
Definition: ExprTreeTest.h:94
bool validate(std::shared_ptr< IExprTreeNode< InputType, OutputType >> f, std::vector< InputType > const &t, std::vector< OutputType > const &y)
Validate .
Definition: ExprTreeTest.h:56
bool run() override
Definition: ExprTreeTest.h:132
std::shared_ptr< IExprTreeNode< InputType, OutputType > > makeShared(std::string const &expr)
Like IExprTreeNodeStringFactory::make but returning a shared pointer instead of a raw pointer.
Definition: IExprTreeNodeStringFactory.h:40
Interface extending the Binary Tree node definition to become a Expression Tree node....
Definition: IExprTreeNode.h:19
Class implementing a Univariate Expression Tree Node.
Definition: UnivarExprTreeNode.h:27
OpType op
Operator as node's element.
Definition: UnivarExprTreeNode.h:89
bool isOperator() const
Check whether the node is an operator (true) or not (false)
Definition: UnivarExprTreeNode.h:237
Class implementing a factory that makes univariate expression trees from expressions given as strings...
Definition: UnivarExprTreeStringFactory.h:63