1 # ifndef CPPAD_CORE_SUBGRAPH_REVERSE_HPP
2 # define CPPAD_CORE_SUBGRAPH_REVERSE_HPP
3 /* --------------------------------------------------------------------------
4 CppAD: C++ Algorithmic Differentiation: Copyright (C) 2003-19 Bradley M. Bell
5 
6 CppAD is distributed under the terms of the
7              Eclipse Public License Version 2.0.
8 
9 This Source Code may also be made available under the following
10 Secondary License when the conditions for such availability set forth
11 in the Eclipse Public License, Version 2.0 are satisfied:
12       GNU General Public License, Version 2.0 or later.
13 ---------------------------------------------------------------------------- */
14 /*
15 $begin subgraph_reverse$$
16 $spell
17     resize
18     subgraph
19     Subgraphs
20     dw
21     Taylor
22     Bool
23     const
24 $$
25 
26 $section Reverse Mode Using Subgraphs$$
27 
28 $head Syntax$$
29 $icode%f%.subgraph_reverse(%select_domain%)
30 %$$
31 $icode%f%.subgraph_reverse(%q%, %ell%, %col%, %dw%)
32 %$$
33 $icode%f%.clear_subgraph()
34 %$$
35 
36 $head Purpose$$
37 We use $latex F : \B{R}^n \rightarrow \B{R}^m$$ to denote the
38 $cref/AD function/glossary/AD Function/$$ corresponding to $icode f$$.
39 Reverse mode computes the derivative of the $cref Forward$$ mode
40 $cref/Taylor coefficients/glossary/Taylor Coefficient/$$
41 with respect to the domain variable $latex x$$.
42 
43 $head Notation$$
44 We use the reverse mode
45 $cref/notation/reverse_any/Notation/$$ with the following change:
46 the vector
47 $cref/w^(k)/reverse_any/Notation/w^(k)/$$ is defined
48 $latex \[
49 w_i^{(k)} = \left\{ \begin{array}{ll}
50     1 & {\rm if} \; k = q-1 \; \R{and} \; i = \ell
51     \\
52     0       & {\rm otherwise}
53 \end{array} \right.
54 \] $$
55 
56 $head BaseVector$$
57 The type $icode BaseVector$$ must be a $cref SimpleVector$$ class with
58 $cref/elements of type/SimpleVector/Elements of Specified Type/$$
59 $icode Base$$.
60 The routine $cref CheckSimpleVector$$ will generate an error message
61 if this is not the case.
62 
63 $head BoolVector$$
64 The type $icode BoolVector$$ is a $cref SimpleVector$$ class with
65 $cref/elements of type/SimpleVector/Elements of Specified Type/$$
66 $code bool$$.
67 
68 $head SizeVector$$
69 The type $icode SizeVector$$ is a $cref SimpleVector$$ class with
70 $cref/elements of type/SimpleVector/Elements of Specified Type/$$
71 $code size_t$$.
72 
73 $head select_domain$$
74 The argument $icode select_domain$$ has prototype
75 $codei%
76     const %BoolVector%& %select_domain%
77 %$$
78 It has size $latex n$$ and specifies which independent variables
79 to include in future $code subgraph_reverse$$ calculations.
80 If $icode%select_domain%[%j%]%$$ is false,
81 it is assumed that $latex u^{(k)}_j = 0$$ for $latex k > 0$$; i.e.,
82 the $th j$$ component of the Taylor coefficient for $latex x$$,
83 with order greater that zero, are zero; see
84 $cref/u^(k)/reverse_any/Notation/u^(k)/$$.
85 
86 $head q$$
87 The argument $icode q$$ has prototype
88 $codei%
89     size_t %q%
90 %$$
91 and specifies the number of Taylor coefficient orders to be differentiated.
92 
93 $head ell$$
94 The argument $icode ell$$ has prototype
95 $codei%
96     size_t %ell%
97 %$$
98 and specifies the dependent variable index that we are computing
99 the derivatives for; i.e. $latex \ell$$.
100 This index can only be used once per, and after, a call that selects
101 the independent variables using $icode select_domain$$.
102 
103 $head col$$
104 This argument $icode col$$ has prototype
105 $codei%
106     %SizeVector% %col%
107 %$$
108 The input size and value of its elements do not matter.
109 The $icode%col%.resize%$$ member function is used to change its size
110 to the number the number of possible non-zero derivative components.
111 For each $icode c$$,
112 $codei%
113     %select_domain%[ %col%[%c%] ] == true
114     %col%[%c%+1] >= %col%[%c%]
115 %$$
116 and the derivative with respect to the $th j$$ independent
117 variable is possibly non-zero where
118 $icode%j% = %col%[%c%]%$$.
119 
120 $head dw$$
121 The argument $icode dw$$ has prototype
122 $codei%
123     %Vector% %dw%
124 %$$
125 Its input size and value does not matter.
126 Upon return,
127 it is a vector with size $latex n \times q$$.
128 For $latex c = 0 , \ldots , %col%.size()-1$$,
129 and $latex k = 0, \ldots , q-1$$,
130 $latex \[
131     dw[ j * q + k ] = W^{(1)} ( x )_{j,k}
132 \] $$
133 is the derivative of the specified Taylor coefficients w.r.t the $th j$$
134 independent variable where $icode%j% = %col%[%c%]%$$.
135 Note that this corresponds to the $cref reverse_any$$ convention when
136 $cref/w/reverse_any/w/$$ has size $icode%m% * %q%$$.
137 
138 $head clear_subgraph$$
139 Calling this routine will free memory that holds
140 information between calls to subgraph calculations so that
141 it does not need to be recalculated.
142 (This memory is automatically freed when $icode f$$ is deleted.)
143 You cannot free this memory between calls that select the domain
144 and corresponding calls that compute reverse mode derivatives.
145 Some of this information is also used by $cref subgraph_sparsity$$.
146 
147 $head Example$$
148 $children%
149     example/sparse/subgraph_reverse.cpp
150 %$$
151 The file
152 $cref subgraph_reverse.cpp$$
153 contains an example and test of this operation.
154 
155 $end
156 */
157 namespace CppAD { // BEGIN_CPPAD_NAMESPACE
158 /*!
159 \file subgraph_reverse.hpp
160 Compute derivatvies using reverse mode and subgraphs.
161 */
162 
163 /// clear all subgraph information
164 template <class Base, class RecBase>
clear_subgraph(void)165 void ADFun<Base,RecBase>::clear_subgraph(void)
166 {   play_.clear_random();
167     subgraph_info_.clear();
168     subgraph_partial_.clear();
169 }
170 
171 /*!
172 Initialize reverse mode derivative computation on subgraphs.
173 
174 \param select_domain
175 is a vector with size equal to the dimension of the domain for this function.
176 Only derivatives w.r.t. the components that are true will be computed.
177 
178 \par subgraph_info_.map_user_op()
179 If the input size of this vector is zero,
180 its value for this player (play_) is computed.
181 
182 \par subgraph_info.in_subgraph_
183 This vector is initialized for a reverse mode computation on subgraphs.
184 
185 \par subgraph_info.select_domain()
186 This vector is set equal to the select_domain argument.
187 
188 \par subgraph_info.process_range()
189 This vector is initialized to have size Range() and its elements are false.
190 */
191 
192 template <class Base, class RecBase>
193 template <class BoolVector>
subgraph_reverse(const BoolVector & select_domain)194 void ADFun<Base,RecBase>::subgraph_reverse( const BoolVector& select_domain )
195 {   using local::pod_vector;
196     //
197     CPPAD_ASSERT_UNKNOWN(
198         dep_taddr_.size() == subgraph_info_.n_dep()
199     );
200     CPPAD_ASSERT_UNKNOWN(
201         size_t( select_domain.size() ) == subgraph_info_.n_ind()
202     );
203 
204     // map_user_op
205     if( subgraph_info_.map_user_op().size() == 0 )
206         subgraph_info_.set_map_user_op(&play_);
207     else
208     {   CPPAD_ASSERT_UNKNOWN( subgraph_info_.check_map_user_op(&play_) );
209     }
210     CPPAD_ASSERT_UNKNOWN(
211         subgraph_info_.map_user_op().size() == play_.num_op_rec()
212     );
213 
214     // initialize for reverse mode subgraph computations
215     switch( play_.address_type() )
216     {
217         case local::play::unsigned_short_enum:
218         subgraph_info_.init_rev<unsigned short>(&play_, select_domain);
219         break;
220 
221         case local::play::unsigned_int_enum:
222         subgraph_info_.init_rev<unsigned int>(&play_, select_domain);
223         break;
224 
225         case local::play::size_t_enum:
226         subgraph_info_.init_rev<size_t>(&play_, select_domain);
227         break;
228 
229         default:
230         CPPAD_ASSERT_UNKNOWN(false);
231     }
232     CPPAD_ASSERT_UNKNOWN(
233         subgraph_info_.in_subgraph().size() == play_.num_op_rec()
234     );
235 
236     return;
237 }
238 
239 
240 /*!
241 Use reverse mode to compute derivative of Taylor coefficients on a subgraph.
242 
243 The function
244 \f$ X : {\bf R} \times {\bf R}^{n \times q} \rightarrow {\bf R} \f$
245 is defined by
246 \f[
247 X(t , u) = \sum_{k=0}^{q-1} u^{(k)} t^k
248 \f]
249 The function
250 \f$ Y : {\bf R} \times {\bf R}^{n \times q} \rightarrow {\bf R} \f$
251 is defined by
252 \f[
253 Y(t , u) = F[ X(t, u) ]
254 \f]
255 The function
256 \f$ W : {\bf R}^{n \times q} \rightarrow {\bf R} \f$ is defined by
257 \f[
258 W(u) = \sum_{k=0}^{q-1} ( w^{(k)} )^{\rm T}
259 \frac{1}{k !} \frac{ \partial^k } { t^k } Y(0, u)
260 \f]
261 
262 \param q
263 is the number of Taylor coefficient we are differentiating.
264 
265 \param ell
266 is the component of the range that is selected for differentiation.
267 
268 \param col
269 is the set of indices j = col[c] where the return value is defined.
270 If an index j is not in col, then either its derivative is zero,
271 or it is not in select_domain.
272 
273 \param dw
274 Is a vector \f$ dw \f$ such that
275 for j = col[c],
276 \f$ k = 0 , \ldots , q-1 \f$
277 \f[
278     dw[ j * q + k ] = W^{(1)} ( x )_{j,k}
279 \f]
280 where the matrix \f$ x \f$ is the value for \f$ u \f$
281 that corresponding to the forward mode Taylor coefficients
282 for the independent variables as specified by previous calls to Forward.
283 
284 \par subgraph_info.process_range()
285 The element process_range[ell] is set to true by this operation.
286 
287 \par subgraph_info.in_subgraph_
288 some of the elements of this vector are set to have value ell
289 (so it can not longer be used to determine the subgraph corresponding to
290 the ell-th dependent variable).
291 */
292 template <class Base, class RecBase>
293 template <class Addr, class BaseVector, class SizeVector>
subgraph_reverse_helper(size_t q,size_t ell,SizeVector & col,BaseVector & dw)294 void ADFun<Base,RecBase>::subgraph_reverse_helper(
295     size_t      q   ,
296     size_t      ell ,
297     SizeVector& col ,
298     BaseVector& dw  )
299 {   using local::pod_vector;
300     // used to identify the RecBase type in calls to sweeps
301     RecBase not_used_rec_base(0.0);
302     //
303     // get a random iterator for this player
304     play_.template setup_random<Addr>();
305     typename local::play::const_random_iterator<Addr> random_itr =
306         play_.template get_random<Addr>();
307 
308     // check BaseVector is Simple Vector class with Base type elements
309     CheckSimpleVector<Base, BaseVector>();
310     CPPAD_ASSERT_KNOWN(
311         q > 0,
312         "The second argument to Reverse must be greater than zero."
313     );
314     CPPAD_ASSERT_KNOWN(
315         num_order_taylor_ >= q,
316         "Less than q Taylor coefficients are currently stored"
317         " in this ADFun object."
318     );
319     CPPAD_ASSERT_KNOWN(
320         num_direction_taylor_ == 1,
321         "reverse mode for Forward(q, r, xq) with more than one direction"
322         "\n(r > 1) is not yet supported."
323     );
324     CPPAD_ASSERT_KNOWN(
325         ell < dep_taddr_.size(),
326         "dependent variable index in to large for this function"
327     );
328     CPPAD_ASSERT_KNOWN(
329         subgraph_info_.process_range()[ell] == false,
330         "This dependent variable index has already been processed\n"
331         "after the previous subgraph_reverse(select_domain)."
332     );
333 
334     // subgraph of operators connected to dependent variable ell
335     pod_vector<addr_t> subgraph;
336     subgraph_info_.get_rev(
337         random_itr, dep_taddr_, addr_t(ell), subgraph
338     );
339 
340     // Add all the atomic function call operators
341     // for calls that have first operator in the subgraph
342     local::subgraph::entire_call(random_itr, subgraph);
343 
344     // First add the BeginOp and EndOp to the subgraph and then sort it
345     // sort the subgraph
346     addr_t i_op_begin_op = 0;
347     addr_t i_op_end_op   = addr_t( play_.num_op_rec() - 1);
348     subgraph.push_back(i_op_begin_op);
349     subgraph.push_back(i_op_end_op);
350     std::sort( subgraph.data(), subgraph.data() + subgraph.size() );
351     CPPAD_ASSERT_UNKNOWN( subgraph[0] == i_op_begin_op );
352     CPPAD_ASSERT_UNKNOWN( subgraph[subgraph.size()-1] == i_op_end_op );
353     /*
354     // Use this printout for debugging
355     std::cout << "{ ";
356     for(size_t k = 0; k < subgraph.size(); k++)
357     {   if( k > 0 )
358             std::cout << ", ";
359         std::cout << subgraph[k];
360     }
361     std::cout << "}\n";
362     */
363 
364     // initialize subgraph_partial_ matrix to zero on subgraph
365     Base zero(0);
366     subgraph_partial_.resize(num_var_tape_ * q);
367     for(size_t k = 0; k < subgraph.size(); ++k)
368     {
369         size_t               i_op = size_t( subgraph[k] );
370         local::OpCode        op;
371         const addr_t*        arg;
372         size_t               i_var;
373         random_itr.op_info(i_op, op, arg, i_var);
374         if( NumRes(op) == 0 )
375         {   CPPAD_ASSERT_UNKNOWN(
376                 op == local::AFunOp  ||
377                 op == local::FunapOp ||
378                 op == local::FunavOp ||
379                 op == local::FunrpOp ||
380                 op == local::EndOp
381             );
382         }
383         else if( op != local::BeginOp )
384         {   CPPAD_ASSERT_UNKNOWN( i_var >= NumRes(op) );
385             size_t j_var = i_var + 1 - NumRes(op);
386             for(size_t i = j_var; i <= i_var; ++i)
387             {   for(size_t j = 0; j < q; ++j)
388                     subgraph_partial_[i * q + j] = zero;
389             }
390         }
391     }
392 
393     // set partial to one for component we are differentiating
394     subgraph_partial_[ dep_taddr_[ell] * q + q - 1] = Base(1);
395 
396     // evaluate the derivatives
397     CPPAD_ASSERT_UNKNOWN( cskip_op_.size() == play_.num_op_rec() );
398     CPPAD_ASSERT_UNKNOWN( load_op2var_.size()  == play_.num_var_load_rec() );
399     size_t n = Domain();
400     //
401     local::play::const_subgraph_iterator<Addr> subgraph_itr =
402         play_.end_subgraph(random_itr, &subgraph);
403     //
404     local::sweep::reverse(
405         q - 1,
406         n,
407         num_var_tape_,
408         &play_,
409         cap_order_taylor_,
410         taylor_.data(),
411         q,
412         subgraph_partial_.data(),
413         cskip_op_.data(),
414         load_op2var_,
415         subgraph_itr,
416         not_used_rec_base
417     );
418 
419     // number of non-zero in return value
420     size_t col_size       = 0;
421     size_t subgraph_index = 0;
422     CPPAD_ASSERT_UNKNOWN( subgraph[subgraph_index] == 0 );
423     // Skip BeginOp
424     ++subgraph_index;
425     while( subgraph_index < subgraph.size() )
426     {   // check for InvOp
427         if( subgraph[subgraph_index] > addr_t(n) )
428             subgraph_index = subgraph.size();
429         else
430         {   ++col_size;
431             ++subgraph_index;
432         }
433     }
434     col.resize(col_size);
435 
436     // return the derivative values
437     dw.resize(n * q);
438     for(size_t c = 0; c < col_size; ++c)
439     {   size_t i_op = size_t( subgraph[c + 1] );
440         CPPAD_ASSERT_UNKNOWN( play_.GetOp(i_op) == local::InvOp );
441         //
442         size_t j = i_op - 1;
443         CPPAD_ASSERT_UNKNOWN( i_op == random_itr.var2op( ind_taddr_[j] ) );
444         //
445         // return paritial for this independent variable
446         col[c] = j;
447         for(size_t k = 0; k < q; k++)
448             dw[j * q + k ] = subgraph_partial_[ind_taddr_[j] * q + k];
449     }
450     //
451     CPPAD_ASSERT_KNOWN( ! ( hasnan(dw) && check_for_nan_ ) ,
452         "f.subgraph_reverse(dw, q, ell): dw has a nan,\n"
453         "but none of f's Taylor coefficents are nan."
454     );
455     //
456     return;
457 }
458 /*!
459 \copydoc subgraph_reverse_helper
460 
461 */
462 template <class Base, class RecBase>
463 template <class BaseVector, class SizeVector>
subgraph_reverse(size_t q,size_t ell,SizeVector & col,BaseVector & dw)464 void ADFun<Base,RecBase>::subgraph_reverse(
465     size_t      q   ,
466     size_t      ell ,
467     SizeVector& col ,
468     BaseVector& dw  )
469 {   using local::pod_vector;
470     //
471     // call proper version of helper function
472     switch( play_.address_type() )
473     {
474         case local::play::unsigned_short_enum:
475         subgraph_reverse_helper<unsigned short>(q, ell, col, dw);
476         break;
477 
478         case local::play::unsigned_int_enum:
479         subgraph_reverse_helper<unsigned int>(q, ell, col, dw);
480         break;
481 
482         case local::play::size_t_enum:
483         subgraph_reverse_helper<size_t>(q, ell, col, dw);
484         break;
485 
486         default:
487         CPPAD_ASSERT_UNKNOWN(false);
488     }
489     //
490     return;
491 }
492 
493 } // END_CPPAD_NAMESPACE
494 # endif
495