File: rosen_34.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 (243 lines) | stat: -rw-r--r-- 7,309 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
// 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
// ----------------------------------------------------------------------------

/*
{xrst_begin rosen_34.cpp}
{xrst_spell
   rclr
}

rosen_34: Example and Test
##########################

Define
:math:`X : \B{R} \rightarrow \B{R}^n` by

.. math::

   X_i (t) =  t^{i+1}

for :math:`i = 1 , \ldots , n-1`.
It follows that

.. math::

   \begin{array}{rclr}
   X_i(0)     & = & 0                             & {\rm for \; all \;} i \\
   X_i ' (t)  & = & 1                             & {\rm if \;} i = 0      \\
   X_i '(t)   & = & (i+1) t^i = (i+1) X_{i-1} (t) & {\rm if \;} i > 0
   \end{array}

The example tests Rosen34 using the relations above:

Operation Sequence
******************
The :ref:`rosen34-name` method for solving ODE's requires the inversion
of a system of linear equations.
This indices used for pivoting may change with different values
for :math:`t` and :math:`x`.
This example checks the comparison operators.
If some of the comparisons change,
it makes a new recording of the function with the pivots for the current
:math:`t` and :math:`x`.
Note that one could skip this step and always use the same pivot.
This would not be as numerically stable,
but it would still solve the equations
(so long as none of the pivot elements are zero).

{xrst_literal
   // BEGIN C++
   // END C++
}

{xrst_end rosen_34.cpp}
*/
// BEGIN C++

# include <cppad/cppad.hpp>        // For automatic differentiation

namespace {
   class Fun {
   private:
      const bool           use_x_;
      CppAD::ADFun<double> ode_ind_;
      CppAD::ADFun<double> ode_dep_;
   public:
      // constructor
      Fun(bool use_x) : use_x_(use_x)
      { }

      // compute f(t, x) both for double and AD<double>
      template <class Scalar>
      void Ode(
         const Scalar                   &t,
         const CPPAD_TESTVECTOR(Scalar) &x,
         CPPAD_TESTVECTOR(Scalar)       &f)
      {  size_t n  = x.size();
         Scalar ti(1);
         f[0]   = Scalar(1);
         for(size_t i = 1; i < n; i++)
         {  ti *= t;
            if( use_x_ )
               f[i] = Scalar(i+1) * x[i-1];
            else
               f[i] = Scalar(i+1) * ti;
         }
      }

      // compute partial of f(t, x) w.r.t. t using AD
      void Ode_ind(
         const double                   &t,
         const CPPAD_TESTVECTOR(double) &x,
         CPPAD_TESTVECTOR(double)       &f_t)
      {  using namespace CppAD;

         size_t n                = x.size();
         bool   ode_ind_defined  = ode_ind_.size_var() != 0;
         //
         CPPAD_TESTVECTOR(double) t_vec(1);
         t_vec[0] = t;
         //
         bool retape = true;
         if( ode_ind_defined )
         {  // check if any comparison operators have a different result
            ode_ind_.new_dynamic(x);
            ode_ind_.Forward(0, t_vec);
            retape = ode_ind_.compare_change_number() > 0;
         }
         if( retape )
         {  // record function that evaluates f(t, x)
            // with t as independent variable and x as dynamcic parameter
            CPPAD_TESTVECTOR(AD<double>) at(1);
            CPPAD_TESTVECTOR(AD<double>) ax(n);
            CPPAD_TESTVECTOR(AD<double>) af(n);

            // set argument values
            at[0] = t;
            size_t i;
            for(i = 0; i < n; i++)
               ax[i] = x[i];

            // declare independent variables and dynamic parameters
            size_t abort_op_index = 0;
            bool   record_compare = false;
            Independent(at, abort_op_index, record_compare, ax);

            // compute f(t, x)
            this->Ode(at[0], ax, af);

            // define AD function object
            ode_ind_.Dependent(at, af);

            // store result in ode_ind_ so can be re-used
            assert( ode_ind_.size_var() != 0 );
         }
         // special case where new_dynamic not yet set
         if( ! ode_ind_defined )
            ode_ind_.new_dynamic(x);
         // compute partial of f w.r.t t
         f_t = ode_ind_.Jacobian(t_vec); // partial f(t, x) w.r.t. t
      }

      // compute partial of f(t, x) w.r.t. x using AD
      void Ode_dep(
         const double                   &t,
         const CPPAD_TESTVECTOR(double) &x,
         CPPAD_TESTVECTOR(double)       &f_x)
      {  using namespace CppAD;

         size_t n                = x.size();
         bool   ode_dep_defined  = ode_dep_.size_var() != 0;
         //
         CPPAD_TESTVECTOR(double) t_vec(1), dx(n), df(n);
         t_vec[0] = t;
         //
         bool retape = true;
         if( ode_dep_defined )
         {  // check if any comparison operators have a different result
            ode_dep_.new_dynamic(t_vec);
            ode_dep_.Forward(0, x);
            retape = ode_dep_.compare_change_number() > 0;
         }
         if( retape )
         {  // record function that evaluates f(t, x)
            // with x as independent variable and t as dynamcic parameter
            CPPAD_TESTVECTOR(AD<double>) at(1);
            CPPAD_TESTVECTOR(AD<double>) ax(n);
            CPPAD_TESTVECTOR(AD<double>) af(n);

            // set argument values
            at[0] = t;
            for(size_t i = 0; i < n; i++)
               ax[i] = x[i];

            // declare independent variables
            size_t abort_op_index = 0;
            bool   record_compare = false;
            Independent(ax, abort_op_index, record_compare, at);

            // compute f(t, x)
            this->Ode(at[0], ax, af);

            // define AD function object
            ode_dep_.Dependent(ax, af);

            // store result in ode_dep_ so can be re-used
            assert( ode_ind_.size_var() != 0 );
         }
         // special case where new_dynamic not yet set
         if( ! ode_dep_defined )
            ode_dep_.new_dynamic(t_vec);
         // compute partial of f w.r.t x
         f_x = ode_dep_.Jacobian(x); // partial f(t, x) w.r.t. x
      }


   };
}

bool rosen_34(void)
{  bool ok = true;     // initial return value

   using CppAD::NearEqual;
   double eps99 = 99.0 * std::numeric_limits<double>::epsilon();

   size_t  n = 4;      // number components in X(t) and order of method
   size_t  M = 2;      // number of Rosen34 steps in [ti, tf]
   double ti = 0.;     // initial time
   double tf = 2.;     // final time

   // xi = X(0)
   CPPAD_TESTVECTOR(double) xi(n);
   for(size_t i = 0; i <n; i++)
      xi[i] = 0.;

   for(size_t use_x = 0; use_x < 2; use_x++)
   {  // function object depends on value of use_x
      Fun F(use_x > 0);

      // compute Rosen34 approximation for X(tf)
      CPPAD_TESTVECTOR(double) xf(n), e(n);
      xf = CppAD::Rosen34(F, M, ti, tf, xi, e);

      double check = tf;
      for(size_t i = 0; i < n; i++)
      {  // check that error is always positive
         ok    &= (e[i] >= 0.);
         // 4th order method is exact for i < 4
         if( i < 4 ) ok &=
            NearEqual(xf[i], check, eps99, eps99);
         // 3rd order method is exact for i < 3
         if( i < 3 )
            ok &= (e[i] <= eps99);

         // check value for next i
         check *= tf;
      }
   }
   return ok;
}

// END C++