1 /* --------------------------------------------------------------------------
2 CppAD: C++ Algorithmic Differentiation: Copyright (C) 2003-20 Bradley M. Bell
3 
4 CppAD is distributed under the terms of the
5              Eclipse Public License Version 2.0.
6 
7 This Source Code may also be made available under the following
8 Secondary License when the conditions for such availability set forth
9 in the Eclipse Public License, Version 2.0 are satisfied:
10       GNU General Public License, Version 2.0 or later.
11 ---------------------------------------------------------------------------- */
12 /*
13 $begin graph_atom_op.cpp$$
14 $spell
15     atom
16     Json
17 $$
18 
19 $section C++ AD Graph Atomic Functions: Example and Test$$
20 
21 $head Source Code$$
22 $srcthisfile%0%// BEGIN C++%// END C++%1%$$
23 
24 $end
25 */
26 // BEGIN C++
27 # include <cppad/cppad.hpp>
28 
atom_op(void)29 bool atom_op(void)
30 {   bool ok = true;
31     using std::string;
32     // -----------------------------------------------------------------------
33     // Define f_0 (x_0, x_1; p) = x_1 + p_0 * x_0
34     //
35     // This function does not have an atomic function operator
36     // node_1 : p[0]
37     // node_2 : x[0]
38     // node_3 : x[1]
39     // node_4 : p[0] * x[0]
40     // node_5 : x[1] + p[0] * x[0]
41     // y[0]   = x[1] + p[0] * x[0]
42     //
43     // C++ graph object
44     CppAD::cpp_graph graph_obj;
45     //
46     // operator being used
47     CppAD::graph::graph_op_enum op_enum;
48     //
49     // set scalars
50     graph_obj.function_name_set("f(x; p)");
51     size_t n_dynamic_ind = 1;
52     graph_obj.n_dynamic_ind_set(n_dynamic_ind);
53     size_t n_variable_ind = 2;
54     graph_obj.n_variable_ind_set(n_variable_ind);
55     //
56     // node_4 : p[0] * x[0]
57     op_enum = CppAD::graph::mul_graph_op;
58     graph_obj.operator_vec_push_back(op_enum);
59     graph_obj.operator_arg_push_back(1);
60     graph_obj.operator_arg_push_back(2);
61     //
62     // node_5 : x[1] + p[0] * x[0]
63     op_enum = CppAD::graph::add_graph_op;
64     graph_obj.operator_vec_push_back(op_enum);
65     graph_obj.operator_arg_push_back(3);
66     graph_obj.operator_arg_push_back(4);
67     //
68     // y[0]   = x[1] + p[0] * x[0]
69     graph_obj.dependent_vec_push_back(5);
70     //
71     // f(x, p) = x_1 + p_0 * x_0
72     CppAD::ADFun<float> f;
73     f.from_graph(graph_obj);
74     //
75     ok &= f.Domain() == 2;
76     ok &= f.Range() == 1;
77     ok &= f.size_dyn_ind() == 1;
78     //
79     // A ckhpoint_two function with name f(x; p) is derived from
80     // an atomic_three fucntion with the same name.
81     bool internal_bool    = false;
82     bool use_hes_sparsity = false;
83     bool use_base2ad      = false;
84     bool use_in_parallel  = false;
85     CppAD::chkpoint_two<float> chk_f(f, "f(x; p)",
86         internal_bool, use_hes_sparsity, use_base2ad, use_in_parallel
87     );
88     // -----------------------------------------------------------------------
89     // g (u_0, u_1; p, q) = f(u_0 + q_0, u_1 + q_1, p)
90     //                    = u_1 + q_1 + p_0 * ( u_0 + q_0 )
91     //
92     // This function has an atomic function operator with name f(x; p)
93     // node_1 : q[0]
94     // node_2 : q[1]
95     // node_3 : u[0]
96     // node_4 : u[1]
97     // node_5 : u[0] + q[0]
98     // node_6 : u[1] + q[1]
99     // node_7 : f( u[0] + q[0], u[1] + q[1]; p)
100     // y[0]   = u[1] + q[1] + p[0] * (u[0]  + q[0])
101     //
102     graph_obj.initialize();
103     //
104     graph_obj.function_name_set("g(u; p, q)");
105     n_dynamic_ind = 2;
106     graph_obj.n_dynamic_ind_set(n_dynamic_ind);
107     n_variable_ind = 2;
108     graph_obj.n_variable_ind_set(n_variable_ind);
109     //
110     // node_5 : u[0] + q[0]
111     op_enum = CppAD::graph::add_graph_op;
112     graph_obj.operator_vec_push_back(op_enum);
113     graph_obj.operator_arg_push_back(3);
114     graph_obj.operator_arg_push_back(1);
115     //
116     // node_6 : u[1] + q[1]
117     graph_obj.operator_vec_push_back(op_enum);
118     graph_obj.operator_arg_push_back(4);
119     graph_obj.operator_arg_push_back(2);
120     //
121     // node_7 : f( u[0] + q[0], u[1] + q[1]; p)
122     //
123     // name_index, n_result, n_arg come before first_node
124     size_t name_index = graph_obj.atomic_name_vec_size();
125     graph_obj.atomic_name_vec_push_back("f(x; p)");
126     //
127     op_enum = CppAD::graph::atom_graph_op;
128     graph_obj.operator_vec_push_back(op_enum);
129     graph_obj.operator_arg_push_back(name_index);  // name_index
130     graph_obj.operator_arg_push_back(1);           // n_result
131     graph_obj.operator_arg_push_back(2);           // n_node_arg
132     graph_obj.operator_arg_push_back(5);           // first node arg
133     graph_obj.operator_arg_push_back(6);           // second node arg
134     //
135     // y[0]   = u[1] + q[1] + p[0] * (u[0]  + q[0])
136     graph_obj.dependent_vec_push_back(7);
137     // ------------------------------------------------------------------------
138     CppAD::ADFun<float> g;
139     g.from_graph(graph_obj);
140     // ------------------------------------------------------------------------
141     ok &= g.Domain() == 2;
142     ok &= g.Range() == 1;
143     ok &= g.size_dyn_ind() == 2;
144     //
145     // set p in g(u; p, q)
146     CPPAD_TESTVECTOR(float) p(1);
147     p[0] = 2.0;
148     chk_f.new_dynamic(p);
149     //
150     // set q in g(u; p, q)
151     CPPAD_TESTVECTOR(float) q(2);
152     q[0] = 3.0;
153     q[1] = 4.0;
154     g.new_dynamic(q);
155     //
156     // evalute g(u; p, q)
157     CPPAD_TESTVECTOR(float) u(2), y(1);
158     u[0] = 5.0;
159     u[1] = 6.0;
160     y    = g.Forward(0, u);
161     //
162     // check value
163     ok &= y[0] == u[1] + q[1] + p[0] * (u[0]  + q[0]);
164     // ------------------------------------------------------------------------
165     g.to_graph(graph_obj);
166     g.from_graph(graph_obj);
167     // ------------------------------------------------------------------------
168     ok &= g.Domain() == 2;
169     ok &= g.Range() == 1;
170     ok &= g.size_dyn_ind() == 2;
171     //
172     // set p in g(u; p, q)
173     p[0] = 3.0;
174     chk_f.new_dynamic(p);
175     //
176     // set q in g(u; p, q)
177     q[0] = 4.0;
178     q[1] = 5.0;
179     g.new_dynamic(q);
180     //
181     // evalute g(u; p, q)
182     u[0] = 6.0;
183     u[1] = 7.0;
184     y    = g.Forward(0, u);
185     //
186     // check value
187     ok &= y[0] == u[1] + q[1] + p[0] * (u[0]  + q[0]);
188     // ------------------------------------------------------------------------
189     return ok;
190 }
191 // END C++
192