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