File: vec_ad.cpp

package info (click to toggle)
cppad 2026.00.00.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 11,584 kB
  • sloc: cpp: 112,960; sh: 6,146; ansic: 179; python: 71; sed: 12; makefile: 10
file content (633 lines) | stat: -rw-r--r-- 15,880 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
// SPDX-FileCopyrightText: Bradley M. Bell <bradbell@seanet.com>
// SPDX-FileContributor: 2003-24 Bradley M. Bell
// ----------------------------------------------------------------------------

/*
Old examples now only used for validation testing
*/
// BEGIN C++

# include <cppad/cppad.hpp>
# include <cmath>
# include <limits>
# include <cppad/speed/uniform_01.hpp>


namespace { // Begin empty namespace

void myhandler(
   bool known       ,
   int  line        ,
   const char *file ,
   const char *exp  ,
   const char *msg  )
{  // error handler must not return, so throw an exception
   throw msg;
}

bool test_comp_assign(void)
{  bool ok = true;

   // replace the default CppAD error handler
   CppAD::ErrorHandler info(myhandler);


   // create a VecAD vector
   CppAD::VecAD<double> v(1);

   // assign its element a value
   v[0] = 1.0;

# ifndef NDEBUG
   // use try / catch because error haandler throws an exception
   try {
      // set ok to false unless catch block is executed
      ok = false;

      // attempt to use a compound assignment operator
      // with a reference to a VecAD object
      CppAD::AD<double> x = 0.0;
      v[x] += 1.0;
   }
   catch (const char* msg)
   {  std::string check =
         "Can't use VecAD<Base>::reference on left side of +=";
      ok = msg == check;
   }
# endif

   return ok;
}

bool VecADTestOne(void)
{  bool ok = true;
   using namespace CppAD;
   using CppAD::sin;
   using CppAD::cos;
   double eps99 = 99.0 * std::numeric_limits<double>::epsilon();


   size_t      n = 3;
   AD<double>  N(n);

   AD<double>  a;
   size_t      i;

   // create the array
   CppAD::VecAD<double> V(n);

   // check assignment from double (while not taping)
   for(i = 0; i < n; i++)
      V[i] = double(n - i);

   // check assignment from an AD<double> (while not taping)
   for(i = 0; i < n; i++)
      V[i] = 2. * V[i];

   // check array values (while not taping)
   for(i = 0; i < n; i++)
      ok &= ( V[i] == 2. * double(n - i) );

   // independent variable
   CPPAD_TESTVECTOR(AD<double>) X(1);
   X[0] = double(n - 1);
   Independent(X);

   // check assignment from double during taping
   a = -1.;
   for(i = 0; i < n; i++)
   {  a += 1.;
      V[a] = double(n - i);
   }

   // check assignment from AD<double> during taping
   a = -1.;
   for(i = 0; i < n; i++)
   {  a += 1.;
      V[a] = sin( X[0] ) * V[a];
   }

   // dependent variable
   CPPAD_TESTVECTOR(AD<double>) Z(1);
   Z[0] = V[ X[0] ];

   // create f: X -> Z
   ADFun<double> f(X, Z);
   CPPAD_TESTVECTOR(double)  x( f.Domain() );
   CPPAD_TESTVECTOR(double) dx( f.Domain() );
   CPPAD_TESTVECTOR(double)  z( f.Range() );
   CPPAD_TESTVECTOR(double) dz( f.Range() );

   double vx;
   for(i = 0; i < n; i++)
   {  // check that the indexing operation was taped
      x[0] = double(i);
      z    = f.Forward(0, x);
      vx   = double(n - i);
      ok  &= NearEqual(z[0], sin(x[0]) * vx, eps99, eps99);

      // note that derivative of v[x] w.r.t. x is zero
      dx[0] = 1.;
      dz    = f.Forward(1, dx);
      ok   &= NearEqual(dz[0], cos(x[0]) * vx, eps99, eps99);

      // reverse mode calculation of same value
      dz[0] = 1.;
      dx    = f.Reverse(1, dz);
      ok   &= NearEqual(dx[0], cos(x[0]) * vx, eps99, eps99);
   }


   return ok;
}

// create the discrete function AD<double> Floor(const AD<double> &X)
double Floor(const double &x)
{  return std::floor(x); }
CPPAD_DISCRETE_FUNCTION(double, Floor)

bool VecADTestTwo(void)
{  bool ok = true;
   using namespace CppAD;
   double eps99 = 99.0 * std::numeric_limits<double>::epsilon();

   double pi    = 4. * CppAD::atan(1.);
   size_t nx    = 10;                             // number of x grid point
   double xLow  = 0;                              // minimum value for x
   double xUp   = 2 * pi;                         // maximum value for x
   double xStep = (xUp - xLow) / double(nx - 1);  // step size in x
   double xCur;                                   // current value for x

   // fill in the data vector on a uniform grid
   VecAD<double> Data(nx);
   size_t i;
   for(i = 0; i < nx; i++)
   {  xCur = xLow + double(i) * xStep;
      // use size_t indexing of Data while not taping
      Data[i] = CppAD::sin(xCur);
   }

   // declare independent variable
   CPPAD_TESTVECTOR(AD<double>) X(1);
   X[0] = 2.;
   Independent(X);

   // compute index corresponding to current value of X[0]
   AD<double> I = X[0] / xStep;
   AD<double> Ifloor = Floor(I);

   // make sure Ifloor >= 0  (during taping)
   AD<double> Zero(0);
   Ifloor = CondExpLt(Ifloor, Zero, Zero, Ifloor);

   // make sure Ifloor <= nx - 2 (during taping)
   AD<double> Nxminus2(nx - 2);
   Ifloor = CondExpGt(Ifloor, Nxminus2, Nxminus2, Ifloor);

   // Iceil is Ifloor + 1
   AD<double> Iceil  = Ifloor + 1.;

   // linear interpolate Data
   CPPAD_TESTVECTOR(AD<double>) Y(1);
   Y[0] = Data[Ifloor] + (I - Ifloor) * (Data[Iceil] - Data[Ifloor]);

   // create f: X -> Y that linearly interpolates the data vector
   ADFun<double> f(X, Y);

   // evaluate the linear interpolant at the mid point for first interval
   CPPAD_TESTVECTOR(double)  x(1);
   CPPAD_TESTVECTOR(double)  y(1);
   x[0] = xStep / 2.;
   y    = f.Forward(0, x);
   ok  &= NearEqual(y[0], (Data[0] + Data[1])/2., eps99, eps99);

   // evaluate the derivative with respect to x
   CPPAD_TESTVECTOR(double) dx(1);
   CPPAD_TESTVECTOR(double) dy(1);
   dx[0] = 1.;
   dy    = f.Forward(1, dx);
   ok   &= NearEqual(dy[0], (Data[1] - Data[0]) / xStep, eps99, eps99);

   return ok;
}

bool SecondOrderReverse(void)
{  // Bradley M. Bell 2009-07-06
   // Reverse mode for LdpOp was only modifying the highest order partial
   // This test demonstrated the bug
   bool ok = true;
   using CppAD::AD;
   using CppAD::NearEqual;
   double eps = 10. * std::numeric_limits<double>::epsilon();

   size_t n = 1;
   CPPAD_TESTVECTOR(AD<double>) X(n);
   X[0] = 2.;
   CppAD::Independent(X);

   size_t m = 2;
   CPPAD_TESTVECTOR(AD<double>) Y(m);

   // The LdpOp instruction corresponds to operations with VecAD vectors.
   CppAD::VecAD<double> Z(2);
   AD<double> zero = 0;
   Z[zero] = X[0] + 1;

   // The LdvOp instruction corresponds to the index being a variable.
   AD<double> one = X[0] - 1; // one in a variable here
   Z[one]  = X[0] + 1.;


   // Compute a function where the second order partial for y
   // depends on the first order partials for z
   // This will use the LdpOp instruction because the index
   // access to z is the parameter zero.
   Y[0] = Z[zero] * Z[zero];
   Y[1] = Z[one]  * Z[one];

   CppAD::ADFun<double> f(X, Y);

   // first order forward
   CPPAD_TESTVECTOR(double) dx(n);
   size_t p = 1;
   dx[0]    = 1.;
   f.Forward(p, dx);

   // Test LdpOp
   // second order reverse (test exp_if_true case)
   CPPAD_TESTVECTOR(double) w(m), dw(2 * n);
   w[0] = 1.;
   w[1] = 0.;
   p    = 2;
   dw = f.Reverse(p, w);

   // check first derivative in dw
   double check = 2. * (Value( X[0] ) + 1.);
   ok &= NearEqual(dw[0], check, eps, eps);

   // check second derivative in dw
   check = 2.;
   ok &= NearEqual(dw[1], check, eps, eps);

   // Test LdvOp
   // second order reverse (test exp_if_true case)
   w[0] = 0.;
   w[1] = 1.;
   p    = 2;
   dw = f.Reverse(p, w);

   // check first derivative in dw
   check = 2. * (Value( X[0] ) + 1.);
   ok &= NearEqual(dw[0], check, eps, eps);

   // check second derivative in dw
   check = 2.;
   ok &= NearEqual(dw[1], check, eps, eps);

   return ok;
}

/*
get_test_function
Function that uses all the VecAD operators; i.e.
StppOp, StpvOp, StvpOp, StvvOp, LdOpOp, LdvOp

The funcntion v(x) is defined by
v_0(x) = 6.0
v_1(x) = x_2
v_2(x) = if int(x_0) == 2 then      x_3
         else if int(x_1) == 2 then 5.0
         else                       2.0
v_3(x) = if int(x_0) == 3 then      x_3
         else if int(x_1) == 3 then 5.0
         else                       3.0

The function y(x) is defined by
y_i(x) = if i < 4 then v_i(x) * v_i(x)
         else v_k(x) * v_k(x) where k = int(x_4)
*/
CppAD::ADFun<double> get_test_function(void)
{  //
   // AD, VecAD
   using CppAD::AD;
   //
   // ax
   CPPAD_TESTVECTOR( AD<double> ) ax(5);
   for(size_t i = 0; i < 5; ++i)
      ax[i] = 2.0;
   CppAD::Independent(ax);
   //
   // av (parameter)
   CppAD::VecAD<double> av(4);
   for(size_t i = 0; i < 4; ++i)
      av[i] = double(i);
   //
   // av (variable)
   AD<double> azero(0);
   AD<double> aone(1);
   av[ ax[1] ] = 5.0;    // StvpOp
   av[ ax[0] ] = ax[3];  // StvvOp
   av[azero]   = 6.0;    // StppOp
   av[aone]    = ax[2];  // StpvOp
   //
   // ay
   CPPAD_TESTVECTOR( AD<double> ) ay(5);
   for(size_t i = 0; i < 4; ++i)
   {  AD<double> ai(i);
      ay[i] = av[ai] * av[ai]; // LdpOp
   }
   ay[4] = av[ ax[4] ] * av[ ax[4] ];   // LdvOp
   //
   // test_function
   return CppAD::ADFun<double>(ax, ay);
}
//
// get_test_function_x
CPPAD_TESTVECTOR(double) get_test_function_x(void)
{  //
   // seed
   using std::chrono::seconds;
   seconds sec = std::chrono::duration_cast<seconds>(
      std::chrono::system_clock::now().time_since_epoch()
   );
   unsigned int seed = static_cast<unsigned int>( sec.count() );
   //
   // uniform_01
   CppAD::uniform_01(seed);
   //
   // x
   CPPAD_TESTVECTOR(double) x(5);
   CppAD::uniform_01(x.size(), x);
   for(size_t i = 0; i < 5; ++i)
      x[i] = 3.999999 * x[i];
   //
   return x;
}
//
// forward_zero_test_function
bool forward_zero_test_function(
   CppAD::ADFun<double>&     fun ,
   CPPAD_TESTVECTOR(double)& x   )
{  //
   // ok
   bool ok = true;
   //
   // eps99
   double eps99 = std::numeric_limits<double>::epsilon();
   //
   // y
   CPPAD_TESTVECTOR(double) y(5);
   y = fun.Forward(0, x);
   //
   // v
   CPPAD_TESTVECTOR(double) v(4);
   v[0] = 6.0;
   v[1] = x[2];
   // v[2]
   if( int(x[0]) == 2 )
      v[2] = x[3];
   else if( int(x[1]) == 2 )
      v[2] = 5.0;
   else
      v[2] = 2.0;
   // v[3]
   if( int(x[0]) == 3 )
      v[3] = x[3];
   else if( int(x[1]) == 3 )
      v[3] = 5.0;
   else
      v[3] = 3.0;
   //
   // ok
   for(size_t i = 0; i < 4; ++i)
      ok &= CppAD::NearEqual(y[i], v[i] * v[i], eps99, eps99);
   size_t k = size_t(x[4]);
   ok &= CppAD::NearEqual(y[4], v[k] * v[k], eps99, eps99);
   //
   return ok;
}

// forward_one_test_function
bool forward_one_test_function(
   CppAD::ADFun<double>&     fun ,
   CPPAD_TESTVECTOR(double)& x   )
{  //
   // ok
   bool ok = true;
   //
   // eps99
   double eps99 = std::numeric_limits<double>::epsilon();
   //
   // fun
   fun.Forward(0, x);
   //
   // dx, dy
   CPPAD_TESTVECTOR(double) dx(5), dy(5);
   for(size_t j = 0; j < 5; ++j)
   {  // dx
      for(size_t k = 0; k < 5; ++k)
         dx[k] = 0.0;
      dx[j] = 1.0;
      //
      // dy
      dy = fun.Forward(1, dx);
      //
      // ok
      // for dy_i / dx_j for i = 0, ..., 3
      for(size_t i = 0; i < 4; ++i)
      {  if( j == 0 || j == 1 )
            ok &= dy[i] == 0.0;
         else if( j == 2 )
         {  if( i == 1 )
               ok &= CppAD::NearEqual(dy[i], 2.0 * x[2], eps99, eps99);
            else
               ok &= dy[i] == 0.0;
         }
         else if( j == 3 )
         {  if( size_t(x[0]) == i )
            {  if( i == 2 || i == 3 )
                  ok &= CppAD::NearEqual(dy[i], 2.0 * x[3], eps99, eps99);
               else
                  ok &= dy[i] == 0.0;
            }
         }
         else
         {   assert( j == 4 );
             ok &= dy[i] == 0.0;
         }
      }
      //
      // ok
      // for dy_4 / dx_j
      if( j == 0 || j == 1 || j == 4 )
            ok &= dy[4] == 0.0;
      else
      {  size_t k = size_t( x[4] );
         if( k == 1 && j == 2 )
            ok &= CppAD::NearEqual(dy[4], 2.0 * x[2], eps99, eps99);
         else if( (k == 2 || k == 3) && j == 3 && size_t(x[0]) == k )
            ok &= CppAD::NearEqual(dy[4], 2.0 * x[3], eps99, eps99);
         else
            ok &= dy[4] == 0.0;
      }
   }
   //
   return ok;
}

bool jac_sparsity_test_function(CppAD::ADFun<double>& fun)
{  //
   // ok
   bool ok = true;
   //
   // size_vector
   typedef CppAD::vector<size_t> size_vector;
   //
   // sparse_rc
   typedef CppAD::sparse_rc<size_vector> sparse_rc;
   //
   // dependency
   for(bool dependency : {false, true})
   {  //
      // pattern_check
      size_t nr = 5, nc = 5, nnz = 0;
      sparse_rc pattern_check(nr, nc, nnz);
      for(size_t i = 0; i < nr; ++i)
      {  // values in v depend on x_2 and x_3
         pattern_check.push_back(i, 2);
         pattern_check.push_back(i, 3);
         if( dependency )
         {  // indices in v depend on x_0 and x_1
            pattern_check.push_back(i, 0);
            pattern_check.push_back(i, 1);
            if( i == 4 )
            {  // y_4 depends on x_4 as an index in v
               pattern_check.push_back(i, 4);
            }
         }
      }
      //
      // pattern_eye
      nr = 5, nc = 5, nnz = 5;
      sparse_rc pattern_eye(nr, nc, nnz);
      for(size_t k = 0; k < nr; ++k)
         pattern_eye.set(k, k, k);
      //
      // pattern_jac
      sparse_rc pattern_jac;
      bool transpose     = false;
      bool internal_bool = false;
      fun.for_jac_sparsity(
         pattern_eye, transpose, dependency, internal_bool, pattern_jac
      );
      //
      // ok
      ok &= pattern_jac == pattern_check;
      //
      // pattern_jac
      fun.rev_jac_sparsity(
         pattern_eye, transpose, dependency, internal_bool, pattern_jac
      );
      //
      // ok
      ok &= pattern_jac == pattern_check;
   }
   //
   return ok;
}

bool hes_sparsity_test_function(CppAD::ADFun<double>& fun)
{  //
   // ok
   bool ok = true;
   //
   // size_vector
   typedef CppAD::vector<size_t> size_vector;
   //
   // sparse_rc
   typedef CppAD::sparse_rc<size_vector> sparse_rc;
   //
   // pattern_check
   size_t nr = 5, nc = 5, nnz = 0;
   sparse_rc pattern_check(nr, nc, nnz);
   for(int r : {2, 3} )
   {  for(int c : {2, 3} )
         pattern_check.push_back(size_t(r), size_t(c));
   }
   //
   // pattern_eye
   nr = 5, nc = 5, nnz = 5;
   sparse_rc pattern_eye(nr, nc, nnz);
   for(size_t k = 0; k < nr; ++k)
      pattern_eye.set(k, k, k);
   //
   // pattern_jac
   sparse_rc pattern_jac;
   bool dependency    = false;
   bool transpose     = false;
   bool internal_bool = false;
   fun.for_jac_sparsity(
      pattern_eye, transpose, dependency, internal_bool, pattern_jac
   );
   //
   // select_domain
   CPPAD_TESTVECTOR(bool) select_domain(5);
   for(size_t i = 0; i < 5; ++i)
      select_domain[i] = true;
   //
   // select_range
   CPPAD_TESTVECTOR(bool) select_range(5);
   for(size_t i = 0; i < 5; ++i)
      select_range[i] = false;
   //
   // pattern_hes
   sparse_rc pattern_hes;
   //
   // i
   for(size_t i = 0; i < 5; ++i)
   {  //
      // pattern_hes
      select_range[i] = true;
      fun.rev_hes_sparsity(
         select_range, transpose, internal_bool, pattern_hes
      );
      select_range[i] = false;
      //
      // ok
      ok &= pattern_hes == pattern_check;
      //
      // pattern_hes
      select_range[i] = true;
      fun.for_hes_sparsity(
         select_domain, select_range, internal_bool, pattern_hes
      );
      select_range[i] = false;
      //
      // ok
      ok &= pattern_hes == pattern_check;
   }
   //
   return ok;
}

} // END empty namespace

bool VecAD(void)
{  bool ok = true;
   ok &= test_comp_assign();
   ok &= VecADTestOne();
   ok &= VecADTestTwo();
   ok &= SecondOrderReverse();
   //
   // fun, x
   CppAD::ADFun<double>     fun = get_test_function();
   CPPAD_TESTVECTOR(double) x   = get_test_function_x();
   //
   ok &= forward_zero_test_function(fun, x);
   ok &= forward_one_test_function(fun, x);
   ok &= jac_sparsity_test_function(fun);
   ok &= hes_sparsity_test_function(fun);
   //
   return ok;
}