CppADCodeGen 2.4.3
A C++ Algorithmic Differentiation Package with Source Code Generation
Loading...
Searching...
No Matches
loop_free_model.hpp
1#ifndef CPPAD_CG_LOOP_FREE_MODEL_INCLUDED
2#define CPPAD_CG_LOOP_FREE_MODEL_INCLUDED
3/* --------------------------------------------------------------------------
4 * CppADCodeGen: C++ Algorithmic Differentiation with Source Code Generation:
5 * Copyright (C) 2013 Ciengis
6 * Copyright (C) 2020 Joao Leal
7 *
8 * CppADCodeGen is distributed under multiple licenses:
9 *
10 * - Eclipse Public License Version 1.0 (EPL1), and
11 * - GNU General Public License Version 3 (GPL3).
12 *
13 * EPL1 terms and conditions can be found in the file "epl-v10.txt", while
14 * terms and conditions for the GPL3 can be found in the file "gpl3.txt".
15 * ----------------------------------------------------------------------------
16 * Author: Joao Leal
17 */
18
19namespace CppAD {
20namespace cg {
21
28template <class Base>
30public:
32 using Arg = Argument<Base>;
33 using VectorSet = std::vector<std::set<size_t> >;
34protected:
42 std::vector<size_t> dependentIndexes_;
43 std::map<size_t, size_t> dependentOrig2Local;
48 bool jacSparsity_;
59 // whether or not the hessian sparsities have been evaluated
60 bool hessSparsity_;
61public:
62
70 const std::vector<size_t>& dependentOrigIndexes) :
71 fun_(fun),
72 dependentIndexes_(dependentOrigIndexes),
73 jacSparsity_(false),
74 hessSparsity_(false) {
75 CPPADCG_ASSERT_KNOWN(fun != nullptr, "fun cannot be null")
76 CPPADCG_ASSERT_KNOWN(dependentOrigIndexes.size() <= fun->Range(), "invalid size")
77
78 for (size_t il = 0; il < dependentIndexes_.size(); il++)
79 dependentOrig2Local[dependentIndexes_[il]] = il;
80 }
81
82 LoopFreeModel(const LoopFreeModel<Base>&) = delete;
83 LoopFreeModel& operator=(const LoopFreeModel<Base>&) = delete;
84
85 inline ADFun<CGB>& getTape() const {
86 return *fun_;
87 }
88
89 inline size_t getTapeDependentCount() const {
90 return fun_->Range();
91 }
92
93 inline size_t getTemporaryDependentCount() const {
94 return fun_->Range() - dependentIndexes_.size();
95 }
96
97 inline size_t getTapeIndependentCount() const {
98 return fun_->Domain();
99 }
100
105 inline const std::vector<size_t>& getOrigDependentIndexes() const {
106 return dependentIndexes_;
107 }
108
109 inline size_t getLocalDependentIndex(size_t origI) const {
110 return dependentOrig2Local.at(origI);
111 }
112
113 inline void evalJacobianSparsity() {
114 if (!jacSparsity_) {
115 jacTapeSparsity_ = jacobianSparsitySet<VectorSet, CGB>(*fun_);
116 jacSparsity_ = true;
117 }
118 }
119
120 inline const VectorSet& getJacobianSparsity() const {
121 return jacTapeSparsity_;
122 }
123
124 inline void evalHessianSparsity() {
125 if (!hessSparsity_) {
126 size_t mo = dependentIndexes_.size();
127 size_t m = fun_->Range();
128 size_t n = fun_->Domain();
129
130 // hessian for the original equations
131 std::set<size_t> eqs;
132 if (mo != 0) {
133 for (size_t i = 0; i < mo; i++)
134 eqs.insert(eqs.end(), i);
135
136 hessTapeOrigEqSparsity_ = hessianSparsitySet<std::vector<std::set<size_t> >, CGB>(*fun_, eqs);
137 }
138
139 // hessian for the temporary variable equations
140 if (m != mo) {
141 eqs.clear();
142 for (size_t i = mo; i < m; i++)
143 eqs.insert(eqs.end(), i);
144 hessTapeTempSparsity_ = hessianSparsitySet<std::vector<std::set<size_t> >, CGB>(*fun_, eqs);
145 } else {
146 hessTapeTempSparsity_.resize(n);
147 }
148
149 hessSparsity_ = true;
150 }
151 }
152
153 inline const VectorSet& getHessianTempEqsSparsity() const {
154 CPPADCG_ASSERT_UNKNOWN(hessSparsity_)
156 }
157
158 inline const VectorSet& getHessianOrigEqsSparsity() const {
159 CPPADCG_ASSERT_UNKNOWN(hessSparsity_)
161 }
162
174 const std::set<size_t>& iterations,
175 size_t iterCount,
176 const CG<Base>& value,
177 IndexOperationNode<Base>& iterationIndexOp) {
178 using namespace std;
179
180 if (iterations.size() == iterCount) {
181 // present in all iterations
182
183 return value;
184
185 } else {
186
187 // no point in creating branches where both branch sides are zero
188 if (value.isIdenticalZero())
189 return value;
190
195 OperationNode<Base>* tmpDclVar = handler.makeNode(CGOpCode::TmpDcl);
196 Argument<Base> tmpArg(*tmpDclVar);
197
198 set<size_t> usedIter;
199 OperationNode<Base>* cond = loops::createIndexConditionExpressionOp<Base>(handler, iterations, usedIter, iterCount - 1, iterationIndexOp);
200
201 // if
202 OperationNode<Base>* ifStart = handler.makeNode(CGOpCode::StartIf, *cond);
203
204 OperationNode<Base>* tmpAssign1 = handler.makeNode(CGOpCode::LoopIndexedTmp,{tmpArg, asArgument(value)});
205 OperationNode<Base>* ifAssign = handler.makeNode(CGOpCode::CondResult,{*ifStart, *tmpAssign1});
206
207 // else
208 OperationNode<Base>* elseStart = handler.makeNode(CGOpCode::Else,{*ifStart, *ifAssign});
209
210 OperationNode<Base>* tmpAssign2 = handler.makeNode(CGOpCode::LoopIndexedTmp,{tmpArg, Base(0)});
211 OperationNode<Base>* elseAssign = handler.makeNode(CGOpCode::CondResult,{*elseStart, *tmpAssign2});
212
213 // end if
214 OperationNode<Base>* endIf = handler.makeNode(CGOpCode::EndIf,{*elseStart, *elseAssign});
215
216 //
217 OperationNode<Base>* tmpVar = handler.makeNode(CGOpCode::Tmp,{tmpArg, *endIf});
218 return CG<Base>(*tmpVar);
219 }
220
221 }
222
235 inline std::map<size_t, std::map<size_t, CGB> > calculateJacobianHessianUsedByLoops(CodeHandler<Base>& handler,
236 std::map<LoopModel<Base>*, loops::HessianWithLoopsInfo<Base> >& loopHessInfo,
237 const std::vector<CGB>& x,
238 std::vector<CGB>& temps,
239 const VectorSet& noLoopEvalJacSparsity,
240 bool individualColoring) {
241 using namespace std;
242 using namespace CppAD::cg::loops;
243
244 CPPADCG_ASSERT_UNKNOWN(hessSparsity_) // check that the sparsities have been evaluated
245
246 size_t mo = dependentIndexes_.size();
247 size_t m = getTapeDependentCount();
248 size_t n = fun_->Domain();
249
250 std::vector<std::vector<CGB> > vwNoLoop(loopHessInfo.size());
251 std::vector<map<size_t, map<size_t, CGB> > > vhessNoLoop(loopHessInfo.size());
252
256 std::vector<std::set<size_t> > noLoopEvalHessTempsSparsity(n);
257
258 for (const auto& itLoop2Info : loopHessInfo) {
259 const HessianWithLoopsInfo<Base>& info = itLoop2Info.second;
260
261 addMatrixSparsity(info.noLoopEvalHessTempsSparsity, noLoopEvalHessTempsSparsity);
262 }
263 std::vector<size_t> hesRow, hesCol;
264 generateSparsityIndexes(noLoopEvalHessTempsSparsity, hesRow, hesCol);
265
266 size_t l = 0;
267 for (const auto& itLoop2Info : loopHessInfo) {
268 LoopModel<Base>* loop = itLoop2Info.first;
269 const HessianWithLoopsInfo<Base>& info = itLoop2Info.second;
270
271 const std::vector<IterEquationGroup<Base> >& eqGroups = loop->getEquationsGroups();
272 size_t nIterations = loop->getIterationCount();
273 size_t nEqGroups = eqGroups.size();
274
275 std::vector<CGB>& wNoLoop = vwNoLoop[l];
276 wNoLoop.resize(m);
277 for (size_t inl = 0; inl < mo; inl++) {
278 wNoLoop[inl] = Base(0);
279 }
280
281 for (size_t inl = mo; inl < m; inl++) {
282 size_t k = inl - mo;
283 const LoopPosition* posK = loop->getTempIndepIndexes(k);
284
285 if (posK != nullptr) {
286
287 for (size_t g = 0; g < nEqGroups; g++) {
288 const IterEquationGroup<Base>& group = eqGroups[g];
289
290 CGB v = Base(0);
291
292 for (size_t tapeI : group.tapeI) {
293 const map<size_t, CGB>& row = info.dyiDzk[tapeI];
294 typename map<size_t, CGB>::const_iterator itCol = row.find(posK->tape);
295 if (itCol != row.end()) {
296 const CGB& dydz = itCol->second;
297 v += dydz * info.w[tapeI];
298 }
299 }
300
304 v = createConditionalOperation(handler,
305 group.iterations,
306 nIterations,
307 v,
308 *info.iterationIndexOp);
309
310 wNoLoop[inl] += v;
311 }
312
313 }
314 }
315
316 l++;
317 }
318
319 std::vector<map<size_t, CGB> > dyDx;
320 generateLoopForJacHes(*fun_, x, vwNoLoop, temps,
321 getJacobianSparsity(),
322 noLoopEvalJacSparsity,
323 dyDx,
325 noLoopEvalHessTempsSparsity,
326 vhessNoLoop,
327 individualColoring);
328
329 // save Jacobian
330 map<size_t, map<size_t, CGB> > dzDx;
331 for (size_t inl = mo; inl < m; inl++) {
332 // dz_k/dx_v (for temporary variable)
333 size_t k = inl - mo;
334 dzDx[k] = dyDx[inl];
335 }
336
337 // save Hessian
338 l = 0;
339 for (auto& itLoop2Info : loopHessInfo) {
340 HessianWithLoopsInfo<Base>& info = itLoop2Info.second;
341 info.dzDxx = vhessNoLoop[l];
342 l++;
343 }
344
345 return dzDx;
346 }
347
348 inline void calculateHessian4OrignalEquations(const std::vector<CGB>& x,
349 const std::vector<CGB>& w,
350 const VectorSet& noLoopEvalHessSparsity,
351 const std::vector<std::map<size_t, std::set<size_t> > >& noLoopEvalHessLocations,
352 std::vector<CGB>& hess) {
353 using namespace std;
354 using namespace CppAD::cg::loops;
355
356 CPPADCG_ASSERT_UNKNOWN(hessSparsity_) // check that the sparsities have been evaluated
357
358 std::vector<CGB> wNoLoop(getTapeDependentCount());
359 std::vector<CGB> hessNoLoop;
360
364 std::vector<size_t> row, col;
365 generateSparsityIndexes(noLoopEvalHessSparsity, row, col);
366
367 if (!row.empty()) {
368 hessNoLoop.resize(row.size());
369
370 for (size_t inl = 0; inl < dependentIndexes_.size(); inl++) {
371 wNoLoop[inl] = w[dependentIndexes_[inl]];
372 }
373
374 CppAD::sparse_hessian_work work; // temporary structure for CPPAD
375 // "cppad.symmetric" may have missing values for functions using
376 // atomic functions which only provide half of the elements
377 // (some values could be zeroed)
378 work.color_method = "cppad.general";
379 fun_->SparseHessian(x, wNoLoop, hessTapeOrigEqSparsity_, row, col, hessNoLoop, work);
380
381 // save non-indexed hessian elements
382 for (size_t el = 0; el < row.size(); el++) {
383 size_t j1 = row[el];
384 size_t j2 = col[el];
385 const set<size_t>& locations = noLoopEvalHessLocations[j1].at(j2);
386 for (size_t itE : locations)
387 hess[itE] = hessNoLoop[el];
388 }
389 }
390 }
391
392 virtual ~LoopFreeModel() {
393 delete fun_;
394 }
395
396};
397
398} // END cg namespace
399} // END CppAD namespace
400
401#endif
std::set< size_t > tapeI
equations indexes in tape of the loop model
std::set< size_t > iterations
iterations which only have these equations defined
const std::vector< size_t > & getOrigDependentIndexes() const
std::map< size_t, std::map< size_t, CGB > > calculateJacobianHessianUsedByLoops(CodeHandler< Base > &handler, std::map< LoopModel< Base > *, loops::HessianWithLoopsInfo< Base > > &loopHessInfo, const std::vector< CGB > &x, std::vector< CGB > &temps, const VectorSet &noLoopEvalJacSparsity, bool individualColoring)
std::vector< size_t > dependentIndexes_
LoopFreeModel(ADFun< CGB > *fun, const std::vector< size_t > &dependentOrigIndexes)
ADFun< CGB > *const fun_
void calculateHessian4OrignalEquations(const std::vector< CGB > &x, const std::vector< CGB > &w, const VectorSet &noLoopEvalHessSparsity, const std::vector< std::map< size_t, std::set< size_t > > > &noLoopEvalHessLocations, std::vector< CGB > &hess)
CG< Base > createConditionalOperation(CodeHandler< Base > &handler, const std::set< size_t > &iterations, size_t iterCount, const CG< Base > &value, IndexOperationNode< Base > &iterationIndexOp)
const std::vector< IterEquationGroup< Base > > & getEquationsGroups() const
const size_t getIterationCount() const
const LoopPosition * getTempIndepIndexes(size_t k) const
std::vector< std::map< size_t, CG< Base > > > dyiDzk