1 # ifndef CPPAD_LOCAL_OPTIMIZE_RECORD_CSUM_HPP
2 # define CPPAD_LOCAL_OPTIMIZE_RECORD_CSUM_HPP
3 /* --------------------------------------------------------------------------
4 CppAD: C++ Algorithmic Differentiation: Copyright (C) 2003-17 Bradley M. Bell
5
6 CppAD is distributed under multiple licenses. This distribution is under
7 the terms of the
8 Eclipse Public License Version 1.0.
9
10 A copy of this license is included in the COPYING file of this distribution.
11 Please visit http://www.coin-or.org/CppAD/ for information on other licenses.
12 -------------------------------------------------------------------------- */
13 /*!
14 \file record_csum.hpp
15 Recording a cummulative cummulative summation.
16 */
17 # include <cppad/local/optimize/old2new.hpp>
18
19 // BEGIN_CPPAD_LOCAL_OPTIMIZE_NAMESPACE
20 namespace CppAD { namespace local { namespace optimize {
21 /*!
22 Recording a cummulative cummulative summation.
23
24 \param play
25 player object corresponding to the old recroding.
26
27 \param opt_op_info
28 mapping from old index to operator index to operator information
29
30 \param old2new
31 mapping from old operator index to information about the new recording.
32
33 \param current
34 is the index in the old operation sequence for
35 the variable corresponding to the result for the current operator.
36 We use the notation i_op = play->var2op(current).
37 It follows that NumRes( opt_op_info[i_op].op ) > 0.
38 If 0 < j_op < i_op, either opt_op_info[j_op].usage == csum_usage,
39 opt_op_info[j_op].usage = no_usage, or old2new[j_op].new_var != 0.
40
41 \param rec
42 is the object that will record the new operations.
43
44 \return
45 is the operator and variable indices in the new operation sequence.
46
47 \param work
48 Is temporary work space. On input and output,
49 work.op_stack, work.add_stack, and work.sub_stack, are all empty.
50 These stacks are passed in so that they are created once
51 and then be reused with calls to record_csum.
52
53 \par Assumptions
54 opt_op_info[i_o].op
55 must be one of AddpvOp, AddvvOp, SubpvOp, SubvpOp, SubvvOp.
56 opt_op_info[i_op].usage != no_usage and ! opt_op_info[i_op].usage == csum_usage.
57 Furthermore opt_op_info[j_op].usage == csum_usage is true from some
58 j_op that corresponds to a variable that is an argument to
59 opt_op_info[i_op].
60 */
61
62 template <class Base>
record_csum(const player<Base> * play,const vector<struct_opt_op_info> & opt_op_info,const CppAD::vector<struct struct_old2new> & old2new,size_t current,recorder<Base> * rec,struct_csum_stacks & work)63 struct_size_pair record_csum(
64 const player<Base>* play ,
65 const vector<struct_opt_op_info>& opt_op_info ,
66 const CppAD::vector<struct struct_old2new>& old2new ,
67 size_t current ,
68 recorder<Base>* rec ,
69 // local information passed so stacks need not be allocated for every call
70 struct_csum_stacks& work )
71 {
72 # ifndef NDEBUG
73 // number of parameters corresponding to the old operation sequence.
74 size_t npar = play->num_par_rec();
75 # endif
76
77 // vector of length npar containing the parameters the old operation
78 // sequence; i.e., given a parameter index i < npar, the corresponding
79 // parameter value is par[i].
80 const Base* par = play->GetPar();
81
82 // check assumption about work space
83 CPPAD_ASSERT_UNKNOWN( work.op_stack.empty() );
84 CPPAD_ASSERT_UNKNOWN( work.add_stack.empty() );
85 CPPAD_ASSERT_UNKNOWN( work.sub_stack.empty() );
86 //
87 size_t i_op = play->var2op(current);
88 CPPAD_ASSERT_UNKNOWN( ! ( opt_op_info[i_op].usage == csum_usage ) );
89 //
90 // information corresponding to the root node in the cummulative summation
91 struct struct_csum_variable var;
92 size_t not_used;
93 play->get_op_info(i_op, var.op, var.arg, not_used);
94 var.add = true; // was parrent operator positive or negative
95 //
96 // initialize stack as containing this one operator
97 work.op_stack.push( var );
98 //
99 // initialize sum of parameter values as zero
100 Base sum_par(0);
101 //
102 # ifndef NDEBUG
103 bool ok = false;
104 if( var.op == SubvpOp )
105 ok = opt_op_info[ play->var2op(var.arg[0]) ].usage == csum_usage;
106 if( var.op == AddpvOp || var.op == SubpvOp )
107 ok = opt_op_info[ play->var2op(var.arg[1]) ].usage == csum_usage;
108 if( var.op == AddvvOp || var.op == SubvvOp )
109 { ok = opt_op_info[ play->var2op(var.arg[0]) ].usage == csum_usage;
110 ok |= opt_op_info[ play->var2op(var.arg[1]) ].usage == csum_usage;
111 }
112 CPPAD_ASSERT_UNKNOWN( ok );
113 # endif
114 //
115 // while there are operators left on the stack
116 while( ! work.op_stack.empty() )
117 { // get this summation operator
118 var = work.op_stack.top();
119 work.op_stack.pop();
120 OpCode op = var.op;
121 const addr_t* arg = var.arg;
122 bool add = var.add;
123 //
124 // process first argument to this operator
125 switch(op)
126 { // cases where first argument is a parameter
127 case AddpvOp:
128 case SubpvOp:
129 CPPAD_ASSERT_UNKNOWN( size_t(arg[0]) < npar );
130 // first argument has same sign as parent node
131 if( add )
132 sum_par += par[arg[0]];
133 else sum_par -= par[arg[0]];
134 break;
135
136 // cases where first argument is a variable
137 case AddvvOp:
138 case SubvpOp:
139 case SubvvOp:
140 //
141 // check if the first argument has csum usage
142 if( opt_op_info[play->var2op(arg[0])].usage == csum_usage )
143 { CPPAD_ASSERT_UNKNOWN(
144 size_t( old2new[ play->var2op(arg[0]) ].new_var) == 0
145 );
146 // push the operator corresponding to the first argument
147 size_t i_op_tmp = play->var2op(arg[0]);
148 play->get_op_info(i_op_tmp, var.op, var.arg, not_used);
149 // first argument has same sign as parent node
150 var.add = add;
151 work.op_stack.push( var );
152 }
153 else
154 { // there are no nodes below this one
155 CPPAD_ASSERT_UNKNOWN( size_t(arg[0]) < current );
156 if( add )
157 work.add_stack.push(arg[0]);
158 else work.sub_stack.push(arg[0]);
159 }
160 break;
161
162 default:
163 CPPAD_ASSERT_UNKNOWN(false);
164 }
165 // process second argument to this operator
166 switch(op)
167 { // cases where second argument is a parameter
168 case SubvpOp:
169 CPPAD_ASSERT_UNKNOWN( size_t(arg[1]) < npar );
170 // second argument has opposite sign of parent node
171 if( add )
172 sum_par -= par[arg[1]];
173 else sum_par += par[arg[1]];
174 break;
175
176 // cases where second argument is a variable and has opposite sign
177 case SubvvOp:
178 case SubpvOp:
179 add = ! add;
180
181 // cases where second argument is a variable and has same sign
182 case AddvvOp:
183 case AddpvOp:
184 // check if the second argument has csum usage
185 if( opt_op_info[play->var2op(arg[1])].usage == csum_usage )
186 { CPPAD_ASSERT_UNKNOWN(
187 size_t( old2new[ play->var2op(arg[1]) ].new_var) == 0
188 );
189 // push the operator corresoponding to the second arugment
190 size_t i_op_tmp = play->var2op(arg[1]);
191 play->get_op_info(i_op_tmp, var.op, var.arg, not_used);
192 var.add = add;
193 work.op_stack.push( var );
194 }
195 else
196 { // there are no nodes below this one
197 CPPAD_ASSERT_UNKNOWN( size_t(arg[1]) < current );
198 if( add )
199 work.add_stack.push(arg[1]);
200 else work.sub_stack.push(arg[1]);
201 }
202 break;
203
204 default:
205 CPPAD_ASSERT_UNKNOWN(false);
206 }
207 }
208 // number of variables to add in this cummulative sum operator
209 size_t n_add = work.add_stack.size();
210 // number of variables to subtract in this cummulative sum operator
211 size_t n_sub = work.sub_stack.size();
212 //
213 CPPAD_ASSERT_UNKNOWN(
214 std::numeric_limits<addr_t>::max() >= n_add + n_sub
215 );
216 //
217 rec->PutArg( addr_t(n_add) ); // arg[0]
218 rec->PutArg( addr_t(n_sub) ); // arg[1]
219 addr_t new_arg = rec->PutPar(sum_par);
220 rec->PutArg(new_arg); // arg[2]
221 // addition arguments
222 for(size_t i = 0; i < n_add; i++)
223 { CPPAD_ASSERT_UNKNOWN( ! work.add_stack.empty() );
224 size_t old_arg = work.add_stack.top();
225 new_arg = old2new[ play->var2op(old_arg) ].new_var;
226 CPPAD_ASSERT_UNKNOWN( 0 < new_arg && size_t(new_arg) < current );
227 rec->PutArg(new_arg); // arg[3+i]
228 work.add_stack.pop();
229 }
230 // subtraction arguments
231 for(size_t i = 0; i < n_sub; i++)
232 { CPPAD_ASSERT_UNKNOWN( ! work.sub_stack.empty() );
233 size_t old_arg = work.sub_stack.top();
234 new_arg = old2new[ play->var2op(old_arg) ].new_var;
235 CPPAD_ASSERT_UNKNOWN( 0 < new_arg && size_t(new_arg) < current );
236 rec->PutArg(new_arg); // arg[3 + arg[0] + i]
237 work.sub_stack.pop();
238 }
239 // number of additions plus number of subtractions
240 rec->PutArg( addr_t(n_add + n_sub) ); // arg[3 + arg[0] + arg[1]]
241 //
242 // return value
243 struct_size_pair ret;
244 ret.i_op = rec->num_op_rec();
245 ret.i_var = rec->PutOp(CSumOp);
246 //
247 return ret;
248 }
249
250 } } } // END_CPPAD_LOCAL_OPTIMIZE_NAMESPACE
251
252
253 # endif
254