xref: /oneTBB/test/tbb/test_composite_node.cpp (revision 155a9af9)
1 /*
2     Copyright (c) 2005-2021 Intel Corporation
3 
4     Licensed under the Apache License, Version 2.0 (the "License");
5     you may not use this file except in compliance with the License.
6     You may obtain a copy of the License at
7 
8         http://www.apache.org/licenses/LICENSE-2.0
9 
10     Unless required by applicable law or agreed to in writing, software
11     distributed under the License is distributed on an "AS IS" BASIS,
12     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13     See the License for the specific language governing permissions and
14     limitations under the License.
15 */
16 
17 #if __INTEL_COMPILER && _MSC_VER
18 #pragma warning(disable : 2586) // decorated name length exceeded, name was truncated
19 #endif
20 
21 #include "common/config.h"
22 
23 #include "tbb/flow_graph.h"
24 
25 #include "common/test.h"
26 #include "common/utils.h"
27 #include "common/graph_utils.h"
28 
29 #include <tuple>
30 #include <cmath>
31 #include <vector>
32 
33 
34 //! \file test_composite_node.cpp
35 //! \brief Test for [flow_graph.composite_node] specification
36 
37 
38 struct passthru_body {
39     int operator()( int i ) {
40         return i;
41     }
42 };
43 
44 class my_input_body{
45     int start;
46     int finish;
47     int step;
48 public:
49     my_input_body(int f, int s) : start(1), finish(f), step(s) {}
50     int operator()(tbb::flow_control& fc) {
51        int a = start;
52        if (start <= finish) {
53            a = start;
54            start+=step;
55            return a;
56        }
57        else {
58            fc.stop();
59            return int();
60        };
61    }
62 };
63 
64 struct m_fxn_body{
65     void operator()(int, tbb::flow::multifunction_node<int, std::tuple<int,int> >::output_ports_type ) {}
66 };
67 
68 struct ct_body {
69 ct_body(){}
70     void operator()(tbb::flow::continue_msg){}
71 };
72 
73 struct seq_body {
74 std::size_t operator()(int i) { return i; }
75 };
76 
77 template<int N, typename T1, typename T2>
78 struct compare {
79     static void compare_refs(T1 tuple1, T2 tuple2) {
80     CHECK_MESSAGE( ( &std::get<N>(tuple1) == &std::get<N>(tuple2)), "ports not set correctly");
81     compare<N-1, T1, T2>::compare_refs(tuple1, tuple2);
82     }
83 };
84 
85 template<typename T1, typename T2>
86 struct compare<1, T1, T2> {
87     static void compare_refs(T1 tuple1, T2 tuple2) {
88     CHECK_MESSAGE( (&std::get<0>(tuple1) == &std::get<0>(tuple2)), "port 0 not correctly set");
89     }
90 };
91 
92 struct tiny_node : public tbb::flow::composite_node< std::tuple< int >, std::tuple< int > > {
93     tbb::flow::function_node< int, int > f1;
94     tbb::flow::function_node< int, int > f2;
95     typedef tbb::flow::composite_node< std::tuple< int >, std::tuple< int > > base_type;
96 
97 public:
98     tiny_node(tbb::flow::graph &g, bool hidden = false) : base_type(g), f1(g, tbb::flow::unlimited, passthru_body() ), f2(g, tbb::flow::unlimited, passthru_body() ) {
99         tbb::flow::make_edge(f1, f2);
100 
101         std::tuple<tbb::flow::function_node< int, int >& > input_tuple(f1);
102         std::tuple<tbb::flow::function_node< int, int >& > output_tuple(f2);
103         base_type::set_external_ports(input_tuple, output_tuple);
104 
105         if(hidden)
106             base_type::add_nodes(f1, f2);
107         else
108             base_type::add_visible_nodes(f1, f2);
109 
110     }
111 };
112 
113 int test_tiny(bool hidden = false) {
114     tbb::flow::graph g;
115     tbb::flow::function_node< int, int > f0( g, tbb::flow::unlimited, passthru_body() );
116     tiny_node t(g, hidden);
117     CHECK_MESSAGE( (&tbb::flow::input_port<0>(t) == &t.f1), "f1 not bound to input port 0 in composite_node t");
118     CHECK_MESSAGE( (&tbb::flow::output_port<0>(t) == &t.f2), "f2 not bound to output port 0 in composite_node t");
119 
120     tiny_node t1(g, hidden);
121     CHECK_MESSAGE( (&std::get<0>(t1.input_ports()) == &t1.f1), "f1 not bound to input port 0 in composite_node t1");
122     CHECK_MESSAGE( (&std::get<0>(t1.output_ports()) == &t1.f2), "f2 not bound to output port 0 in composite_node t1");
123 
124     test_input_ports_return_ref(t1);
125     test_output_ports_return_ref(t1);
126 
127     tiny_node t2(g, hidden);
128     CHECK_MESSAGE( (&tbb::flow::input_port<0>(t2) == &t2.f1), "f1 not bound to input port 0 in composite_node t2");
129     CHECK_MESSAGE( (&tbb::flow::output_port<0>(t2) == &t2.f2), "f2 not bound to output port 0 in composite_node t2");
130 
131     tbb::flow::function_node< int, int > f3( g, tbb::flow::unlimited, passthru_body() );
132     tbb::flow::make_edge( f0, t );
133     tbb::flow::make_edge( t, t1 );
134     tbb::flow::make_edge( t1, t2 );
135     tbb::flow::make_edge( t2 , f3 );
136     tbb::flow::queue_node<int> q(g);
137     tbb::flow::make_edge(f3, q);
138     f0.try_put(1);
139     g.wait_for_all();
140 
141     int i, j =0;
142     q.try_get(i);
143     CHECK_MESSAGE( ( i == 1), "item did not go through graph");
144     q.try_get(j);
145     CHECK_MESSAGE( ( !j), "unexpected item in graph");
146     g.wait_for_all();
147 
148     tbb::flow::remove_edge(f3, q);
149     tbb::flow::remove_edge(t2, f3);
150     tbb::flow::remove_edge(t1, t2);
151 
152     tbb::flow::make_edge( t1 , f3 );
153     tbb::flow::make_edge(f3, q);
154 
155     f0.try_put(2);
156     g.wait_for_all();
157 
158     q.try_get(i);
159     CHECK_MESSAGE( ( i == 2), "item did not go through graph after removal of edge");
160     q.try_get(j);
161     CHECK_MESSAGE( ( !j), "unexpected item in graph after removal of edge");
162 
163     return 0;
164 }
165 
166 class adder_node : public tbb::flow::composite_node< std::tuple< int, int >, std::tuple< int > > {
167 public:
168     tbb::flow::join_node< std::tuple< int, int >, tbb::flow::queueing > j;
169     tbb::flow::function_node< std::tuple< int, int >, int > f;
170 private:
171     typedef tbb::flow::composite_node< std::tuple< int, int >, std::tuple< int > > base_type;
172 
173     struct f_body {
174         int operator()( const std::tuple< int, int > &t ) {
175             return std::get<0>(t) + std::get<1>(t);
176         }
177     };
178 
179 public:
180     adder_node(tbb::flow::graph &g, bool hidden = false) : base_type(g), j(g), f(g, tbb::flow::unlimited, f_body() ) {
181         tbb::flow::make_edge( j, f );
182 
183         base_type::set_external_ports(base_type::input_ports_type(tbb::flow::input_port<0>(j), tbb::flow::input_port<1>(j)), base_type::output_ports_type(f));
184 
185         if (hidden)
186             base_type::add_nodes(j, f);
187         else
188             base_type::add_visible_nodes(j, f);
189 
190     }
191 };
192 
193 struct square_body { int operator()(int v) { return v*v; } };
194 struct cube_body { int operator()(int v) { return v*v*v; } };
195 int adder_sum(int i) {
196     return (int)(pow(3*pow(i,3) + pow(i, 2),2));
197 }
198 int test_adder(bool hidden = false) {
199     tbb::flow::graph g;
200     tbb::flow::function_node<int,int> s(g, tbb::flow::unlimited, square_body());
201     tbb::flow::function_node<int,int> c(g, tbb::flow::unlimited, cube_body());
202     tbb::flow::function_node<int,int> p(g, tbb::flow::unlimited, passthru_body());
203 
204     adder_node a0(g, hidden);
205     CHECK_MESSAGE( (&tbb::flow::input_port<0>(a0) == &tbb::flow::input_port<0>(a0.j)), "input_port 0 of j not bound to input port 0 in composite_node a0");
206     CHECK_MESSAGE( (&tbb::flow::input_port<1>(a0) == &tbb::flow::input_port<1>(a0.j)), "input_port 1 of j not bound to input port 1 in composite_node a0");
207     CHECK_MESSAGE( (&tbb::flow::output_port<0>(a0) == &a0.f), "f not bound to output port 0 in composite_node a0");
208 
209     adder_node a1(g, hidden);
210     CHECK_MESSAGE( (&std::get<0>(a0.input_ports()) == &tbb::flow::input_port<0>(a0.j)), "input_port 0 of j not bound to input port 0 in composite_node a1");
211     CHECK_MESSAGE( (&std::get<1>(a0.input_ports()) == &tbb::flow::input_port<1>(a0.j)), "input_port1 of j not bound to input port 1 in composite_node a1");
212     CHECK_MESSAGE( (&std::get<0>(a0.output_ports()) == &a0.f), "f not bound to output port 0 in composite_node a1");
213 
214     adder_node a2(g, hidden);
215     CHECK_MESSAGE( (&tbb::flow::input_port<0>(a2) == &tbb::flow::input_port<0>(a2.j)), "input_port 0 of j not bound to input port 0 in composite_node a2");
216     CHECK_MESSAGE( (&tbb::flow::input_port<1>(a2) == &tbb::flow::input_port<1>(a2.j)), "input_port 1 of j not bound to input port 1 in composite_node a2");
217     CHECK_MESSAGE( (&tbb::flow::output_port<0>(a2) == &a2.f), "f not bound to output port 0 in composite_node a2");
218 
219     adder_node a3(g, hidden);
220     CHECK_MESSAGE( (&std::get<0>(a3.input_ports()) == &tbb::flow::input_port<0>(a3.j)), "input_port 0 of j not bound to input port 0 in composite_node a3");
221     CHECK_MESSAGE( (&std::get<1>(a3.input_ports()) == &tbb::flow::input_port<1>(a3.j)), "input_port1 of j not bound to input port 1 in composite_node a3");
222     CHECK_MESSAGE( (&std::get<0>(a3.output_ports()) == &a3.f), "f not bound to output port 0 in composite_node a3");
223 
224     tbb::flow::function_node<int,int> s2(g, tbb::flow::unlimited, square_body());
225     tbb::flow::queue_node<int> q(g);
226 
227     tbb::flow::make_edge( s, tbb::flow::input_port<0>(a0) );
228     tbb::flow::make_edge( c, tbb::flow::input_port<1>(a0) );
229 
230     tbb::flow::make_edge( c, tbb::flow::input_port<0>(a1) );
231     tbb::flow::make_edge( c, tbb::flow::input_port<1>(a1) );
232 
233     tbb::flow::make_edge( tbb::flow::output_port<0>(a0), tbb::flow::input_port<0>(a2) );
234     tbb::flow::make_edge( tbb::flow::output_port<0>(a1), tbb::flow::input_port<1>(a2) );
235 
236     tbb::flow::make_edge( tbb::flow::output_port<0>(a2), s2 );
237     tbb::flow::make_edge( s2, q );
238 
239     int sum_total=0;
240     int result=0;
241     for ( int i = 1; i < 4; ++i ) {
242         s.try_put(i);
243         c.try_put(i);
244         sum_total += adder_sum(i);
245         g.wait_for_all();
246     }
247 
248     int j;
249     for ( int i = 1; i < 4; ++i ) {
250         q.try_get(j);
251         result += j;
252     }
253     g.wait_for_all();
254     CHECK_MESSAGE( (result == sum_total), "the sum from the graph does not match the calculated value");
255 
256     tbb::flow::remove_edge(s2, q);
257     tbb::flow::remove_edge( a2, s2 );
258     tbb::flow::make_edge( a0, a3 );
259     tbb::flow::make_edge( a1, tbb::flow::input_port<1>(a3) );
260     tbb::flow::make_edge( a3, s2 );
261     tbb::flow::make_edge( s2, q );
262 
263     sum_total=0;
264     result=0;
265     for ( int i = 10; i < 20; ++i ) {
266         s.try_put(i);
267         c.try_put(i);
268         sum_total += adder_sum(i);
269         g.wait_for_all();
270     }
271 
272     for ( int i = 10; i < 20; ++i ) {
273         q.try_get(j);
274         result += j;
275     }
276     g.wait_for_all();
277     CHECK_MESSAGE( (result == sum_total), "the new sum after the replacement of the nodes does not match the calculated value");
278 
279     return 0;
280 }
281 
282 /*
283                                               outer composite node (outer_node)
284                                      |-------------------------------------------------------------------|
285                                      |                                                                   |
286                                      |  |------------------|  |------------------|  |------------------| |
287              |---------------------| |--| inner composite  | /| inner composite  | /| inner composite  | | |-------------------|
288              |broadcast node(input)|/|  | node             |/ | node             |/ | node             |-+-| queue node(output)|
289              |---------------------|\|  |(inner_node1)     |\ | (inner_node2)    |\ | (inner_node3)    | | |-------------------|
290                                      |--|                  | \|                  | \|                  | |
291                                      |  |------------------|  |------------------|  |------------------| |
292                                      |                                                                   |
293                                      |-------------------------------------------------------------------|
294 
295 */
296 int test_nested_adder(bool hidden=false) {
297     tbb::flow::graph g;
298     tbb::flow::composite_node<std::tuple<int, int>, std::tuple<int> > outer_node(g);
299     typedef tbb::flow::composite_node<std::tuple<int, int>, std::tuple<int> > base_type;
300     tbb::flow::broadcast_node<int> input(g);
301     tbb::flow::queue_node<int> output(g);
302 
303     adder_node inner_node1(g, hidden);
304     adder_node inner_node2(g, hidden);
305     adder_node inner_node3(g, hidden);
306 
307     outer_node.set_external_ports(base_type::input_ports_type(tbb::flow::input_port<0>(inner_node1), tbb::flow::input_port<1>(inner_node1)), base_type::output_ports_type(tbb::flow::output_port<0>(inner_node3)));
308 
309     CHECK_MESSAGE( (&tbb::flow::input_port<0>(outer_node) == &tbb::flow::input_port<0>(inner_node1)), "input port 0 of inner_node1 not bound to input port 0 in outer_node");
310     CHECK_MESSAGE( (&tbb::flow::input_port<1>(outer_node) == &tbb::flow::input_port<1>(inner_node1)), "input port 1 of inner_node1 not bound to input port 1 in outer_node");
311     CHECK_MESSAGE( (&tbb::flow::output_port<0>(outer_node) == &tbb::flow::output_port<0>(inner_node3)), "output port 0 of inner_node3 not bound to output port 0 in outer_node");
312 
313     tbb::flow::make_edge(input, tbb::flow::input_port<0>(outer_node)/*inner_node1*/);
314     tbb::flow::make_edge(input, tbb::flow::input_port<1>(outer_node)/*inner_node1*/);
315 
316     tbb::flow::make_edge(inner_node1, tbb::flow::input_port<0>(inner_node2));
317     tbb::flow::make_edge(inner_node1, tbb::flow::input_port<1>(inner_node2));
318 
319     tbb::flow::make_edge(inner_node2, tbb::flow::input_port<0>(inner_node3));
320     tbb::flow::make_edge(inner_node2, tbb::flow::input_port<1>(inner_node3));
321 
322     tbb::flow::make_edge(outer_node/*inner_node3*/, output);
323 
324     if(hidden)
325         outer_node.add_nodes(inner_node1, inner_node2, inner_node3);
326     else
327         outer_node.add_visible_nodes(inner_node1, inner_node2, inner_node3);
328 
329     int out;
330     for (int i = 1; i < 200000; ++i) {
331         input.try_put(i);
332         g.wait_for_all();
333         output.try_get(out);
334         CHECK_MESSAGE( (tbb::flow::output_port<0>(outer_node).try_get(out) == output.try_get(out)), "output from outer_node does not match output from graph");
335         CHECK_MESSAGE( (out == 8*i), "output from outer_node not correct");
336     }
337     g.wait_for_all();
338 
339     return 0;
340 }
341 
342 template< typename T >
343 class prefix_node : public tbb::flow::composite_node< std::tuple< T, T, T, T, T >, std::tuple< T, T, T, T, T > > {
344     typedef std::tuple< T, T, T, T, T > my_tuple_t;
345 public:
346     tbb::flow::join_node< my_tuple_t, tbb::flow::queueing > j;
347     tbb::flow::split_node< my_tuple_t > s;
348 private:
349     tbb::flow::function_node< my_tuple_t, my_tuple_t > f;
350     typedef tbb::flow::composite_node< my_tuple_t, my_tuple_t > base_type;
351 
352     struct f_body {
353         my_tuple_t operator()( const my_tuple_t &t ) {
354             return my_tuple_t( std::get<0>(t),
355                                std::get<0>(t) + std::get<1>(t),
356                                std::get<0>(t) + std::get<1>(t) + std::get<2>(t),
357                                std::get<0>(t) + std::get<1>(t) + std::get<2>(t) + std::get<3>(t),
358                                std::get<0>(t) + std::get<1>(t) + std::get<2>(t) + std::get<3>(t) + std::get<4>(t) );
359         }
360     };
361 
362 public:
363     prefix_node(tbb::flow::graph &g, bool hidden = false ) : base_type(g), j(g), s(g), f(g, tbb::flow::serial, f_body() ) {
364         tbb::flow::make_edge( j, f );
365         tbb::flow::make_edge( f, s );
366 
367     typename base_type::input_ports_type input_tuple(tbb::flow::input_port<0>(j), tbb::flow::input_port<1>(j), tbb::flow::input_port<2>(j), tbb::flow::input_port<3>(j), tbb::flow::input_port<4>(j));
368 
369     typename base_type::output_ports_type output_tuple(tbb::flow::output_port<0>(s), tbb::flow::output_port<1>(s), tbb::flow::output_port<2>(s), tbb::flow::output_port<3>(s), tbb::flow::output_port<4>(s));
370 
371     base_type::set_external_ports(input_tuple, output_tuple);
372 
373         if(hidden)
374             base_type::add_nodes(j,s,f);
375         else
376             base_type::add_visible_nodes(j,s,f);
377 
378     }
379 };
380 
381 int test_prefix(bool hidden = false) {
382     tbb::flow::graph g;
383     prefix_node<double> p(g, hidden);
384 
385     CHECK_MESSAGE( (&std::get<0>(p.input_ports()) == &tbb::flow::input_port<0>(p.j)), "input port 0 of j is not bound to input port 0 of composite node p");
386     CHECK_MESSAGE( (&tbb::flow::input_port<1>(p.j) == &tbb::flow::input_port<1>(p.j)), "input port 1 of j is not bound to input port 1 of composite node p");
387     CHECK_MESSAGE( (&std::get<2>(p.input_ports()) == &tbb::flow::input_port<2>(p.j)), "input port 2 of j is not bound to input port 2 of composite node p");
388     CHECK_MESSAGE( (&tbb::flow::input_port<3>(p.j) == &tbb::flow::input_port<3>(p.j)), "input port 3 of j is not bound to input port 3 of composite node p");
389     CHECK_MESSAGE( (&std::get<4>(p.input_ports()) == &tbb::flow::input_port<4>(p.j)), "input port 4 of j is not bound to input port 4 of composite node p");
390 
391 
392     CHECK_MESSAGE( (&std::get<0>(p.output_ports()) == &tbb::flow::output_port<0>(p.s)), "output port 0 of s is not bound to output port 0 of composite node p");
393     CHECK_MESSAGE( (&tbb::flow::output_port<1>(p.s) == &tbb::flow::output_port<1>(p.s)), "output port 1 of s is not bound to output port 1 of composite node p");
394     CHECK_MESSAGE( (&std::get<2>(p.output_ports()) == &tbb::flow::output_port<2>(p.s)), "output port 2 of s is not bound to output port 2 of composite node p");
395     CHECK_MESSAGE( (&tbb::flow::output_port<3>(p.s) == &tbb::flow::output_port<3>(p.s)), "output port 3 of s is not bound to output port 3 of composite node p");
396     CHECK_MESSAGE( (&std::get<4>(p.output_ports()) == &tbb::flow::output_port<4>(p.s)), "output port 4 of s is not bound to output port 4 of composite node p");
397 
398     std::vector< tbb::flow::queue_node<double> > v( 5, tbb::flow::queue_node<double>(g) );
399     tbb::flow::make_edge( tbb::flow::output_port<0>(p), v[0] );
400     tbb::flow::make_edge( tbb::flow::output_port<1>(p), v[1] );
401     tbb::flow::make_edge( tbb::flow::output_port<2>(p), v[2] );
402     tbb::flow::make_edge( tbb::flow::output_port<3>(p), v[3] );
403     tbb::flow::make_edge( tbb::flow::output_port<4>(p), v[4] );
404 
405     for(  double offset = 1; offset < 10000; offset *= 10 ) {
406         tbb::flow::input_port<0>(p).try_put( offset );
407         tbb::flow::input_port<1>(p).try_put( offset + 1 );
408         tbb::flow::input_port<2>(p).try_put( offset + 2 );
409         tbb::flow::input_port<3>(p).try_put( offset + 3 );
410         tbb::flow::input_port<4>(p).try_put( offset + 4 );
411     }
412     g.wait_for_all();
413 
414     double x;
415     while ( v[0].try_get(x) ) {
416         g.wait_for_all();
417         for ( int i = 1; i < 5; ++i ) {
418             v[i].try_get(x);
419             g.wait_for_all();
420         }
421     }
422     return 0;
423 }
424 
425 struct input_only_output_only_seq {
426     std::size_t operator()(int i) {
427         CHECK(i > 0);
428         return std::size_t((i + 3) / 4 - 1);
429     }
430 };
431 
432 void input_only_output_only_composite(bool hidden) {
433     tbb::flow::graph g;
434 
435     tbb::flow::composite_node<std::tuple<int>, std::tuple<int> > input_output(g);
436 
437     typedef tbb::flow::composite_node<std::tuple<int>, std::tuple<> > input_only_composite;
438     typedef tbb::flow::composite_node<std::tuple<>, std::tuple<int> > output_only_composite;
439 
440     typedef tbb::flow::input_node<int> src_type;
441     typedef tbb::flow::queue_node<int> q_type;
442     typedef tbb::flow::function_node<int, int> f_type;
443     typedef tbb::flow::sequencer_node<int> sequencer_type;
444 
445     int num = 0;
446     int finish=1000;
447     int step = 4;
448 
449     input_only_composite a_in(g);
450     output_only_composite a_out(g);
451 
452     src_type src(g, my_input_body(finish, step));
453     q_type que(g);
454     f_type f(g, 1, passthru_body());
455 
456     // Sequencer_node is needed, because serial function_node guarantees only serial body execution,
457     // not a sequential order of messages dispatch
458     sequencer_type seq(g, input_only_output_only_seq());
459 
460     std::tuple<f_type& > input_tuple(f);
461     a_in.set_external_ports(input_tuple);
462     CHECK_MESSAGE( (&std::get<0>(a_in.input_ports()) == &f), "f not bound to input port 0 in composite_node a_in");
463 
464     std::tuple<src_type&> output_tuple(src);
465     a_out.set_external_ports(output_tuple);
466     CHECK_MESSAGE( (&std::get<0>(a_out.output_ports()) == &src), "src not bound to output port 0 in composite_node a_out");
467 
468     if(hidden) {
469         a_in.add_nodes(f, seq, que);
470         a_out.add_nodes(src);
471     } else {
472         a_in.add_visible_nodes(f, seq, que);
473         a_out.add_visible_nodes(src);
474     }
475 
476     tbb::flow::make_edge(a_out, a_in);
477     tbb::flow::make_edge(f, seq);
478     tbb::flow::make_edge(seq, que);
479     src.activate();
480     g.wait_for_all();
481 
482     for(int i = 1; i<finish/step; ++i) {
483         que.try_get(num);
484         CHECK_MESSAGE( (num == 4*i - 3), "number does not match position in sequence");
485     }
486     g.wait_for_all();
487 }
488 
489 //! Test single node inside composite nodes
490 //! \brief \ref error_guessing
491 TEST_CASE("Tiny tests"){
492     test_tiny(false);
493     test_tiny(true);
494 }
495 
496 //! Test basic adders in composite node
497 //! \brief \ref error_guessing
498 TEST_CASE("Adder tests"){
499     test_adder(false);
500     test_adder(true);
501 }
502 
503 //! Test nested adders in composite node
504 //! \brief \ref error_guessing
505 TEST_CASE("Nested adder tests"){
506     test_nested_adder(true);
507     test_nested_adder(false);
508 }
509 
510 //! Test returning a subset of inputs
511 //! \brief \ref error_guessing
512 TEST_CASE("Prefix test"){
513     test_prefix(false);
514     test_prefix(true);
515 }
516 
517 //! Test input-only composite node
518 //! \brief \ref error_guessing \ref boundary
519 TEST_CASE("Input-only composite"){
520     input_only_output_only_composite(true);
521     input_only_output_only_composite(false);
522 }
523 
524