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