Prev Next rc_sparsity.cpp

@(@\newcommand{\W}[1]{ \; #1 \; } \newcommand{\R}[1]{ {\rm #1} } \newcommand{\B}[1]{ {\bf #1} } \newcommand{\D}[2]{ \frac{\partial #1}{\partial #2} } \newcommand{\DD}[3]{ \frac{\partial^2 #1}{\partial #2 \partial #3} } \newcommand{\Dpow}[2]{ \frac{\partial^{#1}}{\partial {#2}^{#1}} } \newcommand{\dpow}[2]{ \frac{ {\rm d}^{#1}}{{\rm d}\, {#2}^{#1}} }@)@
Preferred Sparsity Patterns: Row and Column Indices: Example and Test

Purpose
This example show how to use row and column index sparsity patterns sparse_rc to compute sparse Jacobians and Hessians. This became the preferred way to represent sparsity on 2017-02-09 .
# include <cppad/cppad.hpp>
namespace {
     using CppAD::sparse_rc;
     using CppAD::sparse_rcv;
     using CppAD::NearEqual;
     //
     typedef CPPAD_TESTVECTOR(bool)                b_vector;
     typedef CPPAD_TESTVECTOR(size_t)              s_vector;
     typedef CPPAD_TESTVECTOR(double)              d_vector;
     typedef CPPAD_TESTVECTOR( CppAD::AD<double> ) a_vector;
     //
     double eps99 = 99.0 * std::numeric_limits<double>::epsilon();
     // -----------------------------------------------------------------------
     // function f(x) that we are computing sparse results for
     // -----------------------------------------------------------------------
     a_vector fun(const a_vector& x)
     {     size_t n  = x.size();
          a_vector ret(n + 1);
          for(size_t i = 0; i < n; i++)
          {     size_t j = (i + 1) % n;
               ret[i]     = x[i] * x[i] * x[j];
          }
          ret[n] = 0.0;
          return ret;
     }
     // -----------------------------------------------------------------------
     // Jacobian
     // -----------------------------------------------------------------------
     bool check_jac(
          const d_vector&                       x      ,
          const sparse_rcv<s_vector, d_vector>& subset )
     {     bool ok  = true;
          size_t n = x.size();
          //
          ok &= subset.nnz() == 2 * n;
          const s_vector& row( subset.row() );
          const s_vector& col( subset.col() );
          const d_vector& val( subset.val() );
          s_vector row_major = subset.row_major();
          for(size_t i = 0; i < n; i++)
          {     size_t j = (i + 1) % n;
               size_t k = 2 * i;
               //
               ok &= row[ row_major[k] ]   == i;
               ok &= row[ row_major[k+1] ] == i;
               //
               size_t ck  = col[ row_major[k] ];
               size_t ckp = col[ row_major[k+1] ];
               double vk  = val[ row_major[k] ];
               double vkp = val[ row_major[k+1] ];
               //
               // put diagonal element first
               if( j < i )
               {     std::swap(ck, ckp);
                    std::swap(vk, vkp);
               }
               // diagonal element
               ok &= ck == i;
               ok &= NearEqual( vk, 2.0 * x[i] * x[j], eps99, eps99 );
               // off diagonal element
               ok &= ckp == j;
               ok &= NearEqual( vkp, x[i] * x[i], eps99, eps99 );
          }
          return ok;
     }
     // Use forward mode for Jacobian and sparsity pattern
     bool forward_jac(CppAD::ADFun<double>& f)
     {     bool ok = true;
          size_t n = f.Domain();
          //
          // sparsity pattern for identity matrix
          sparse_rc<s_vector> pattern_in(n, n, n);
          for(size_t k = 0; k < n; k++)
               pattern_in.set(k, k, k);
          //
          // sparsity pattern for Jacobian
          bool transpose     = false;
          bool dependency    = false;
          bool internal_bool = false;
          sparse_rc<s_vector> pattern_out;
          f.for_jac_sparsity(
               pattern_in, transpose, dependency, internal_bool, pattern_out
          );
          //
          // compute entire Jacobian
          size_t                         group_max = 1;
          std::string                    coloring  = "cppad";
          sparse_rcv<s_vector, d_vector> subset( pattern_out );
          CppAD::sparse_jac_work         work;
          d_vector x(n);
          for(size_t j = 0; j < n; j++)
               x[j] = double(j + 2);
          size_t n_sweep = f.sparse_jac_for(
               group_max, x, subset, pattern_out, coloring, work
          );
          //
          // check Jacobian
          ok &= check_jac(x, subset);
          ok &= n_sweep == 2;
          //
          return ok;
     }
     // Use reverse mode for Jacobian and sparsity pattern
     bool reverse_jac(CppAD::ADFun<double>& f)
     {     bool ok = true;
          size_t n = f.Domain();
          size_t m = f.Range();
          //
          // sparsity pattern for identity matrix
          sparse_rc<s_vector> pattern_in(m, m, m);
          for(size_t k = 0; k < m; k++)
               pattern_in.set(k, k, k);
          //
          // sparsity pattern for Jacobian
          bool transpose     = false;
          bool dependency    = false;
          bool internal_bool = false;
          sparse_rc<s_vector> pattern_out;
          f.rev_jac_sparsity(
               pattern_in, transpose, dependency, internal_bool, pattern_out
          );
          //
          // compute entire Jacobian
          std::string                    coloring  = "cppad";
          sparse_rcv<s_vector, d_vector> subset( pattern_out );
          CppAD::sparse_jac_work         work;
          d_vector x(n);
          for(size_t j = 0; j < n; j++)
               x[j] = double(j + 2);
          size_t n_sweep = f.sparse_jac_rev(
               x, subset, pattern_out, coloring, work
          );
          //
          // check Jacobian
          ok &= check_jac(x, subset);
          ok &= n_sweep == 2;
          //
          return ok;
     }
     // ------------------------------------------------------------------------
     // Hessian
     // ------------------------------------------------------------------------
     bool check_hes(
          size_t                                i      ,
          const d_vector&                       x      ,
          const sparse_rcv<s_vector, d_vector>& subset )
     {     bool ok  = true;
          size_t n = x.size();
          size_t j = (i + 1) % n;
          //
          ok &= subset.nnz() == 3;
          const s_vector& row( subset.row() );
          const s_vector& col( subset.col() );
          const d_vector& val( subset.val() );
          s_vector row_major = subset.row_major();
          //
          double v0 = val[ row_major[0] ];
          double v1 = val[ row_major[1] ];
          double v2 = val[ row_major[2] ];
          if( j < i )
          {     ok &= row[ row_major[0] ] == j;
               ok &= col[ row_major[0] ] == i;
               ok &= NearEqual( v0, 2.0 * x[i], eps99, eps99 );
               //
               ok &= row[ row_major[1] ] == i;
               ok &= col[ row_major[1] ] == j;
               ok &= NearEqual( v1, 2.0 * x[i], eps99, eps99 );
               //
               ok &= row[ row_major[2] ] == i;
               ok &= col[ row_major[2] ] == i;
               ok &= NearEqual( v2, 2.0 * x[j], eps99, eps99 );
          }
          else
          {     ok &= row[ row_major[0] ] == i;
               ok &= col[ row_major[0] ] == i;
               ok &= NearEqual( v0, 2.0 * x[j], eps99, eps99 );
               //
               ok &= row[ row_major[1] ] == i;
               ok &= col[ row_major[1] ] == j;
               ok &= NearEqual( v1, 2.0 * x[i], eps99, eps99 );
               //
               ok &= row[ row_major[2] ] == j;
               ok &= col[ row_major[2] ] == i;
               ok &= NearEqual( v2, 2.0 * x[i], eps99, eps99 );
          }
          return ok;
     }
     // Use forward mode for Hessian and sparsity pattern
     bool forward_hes(CppAD::ADFun<double>& f)
     {     bool ok = true;
          size_t n = f.Domain();
          size_t m = f.Range();
          //
          b_vector select_domain(n);
          for(size_t j = 0; j < n; j++)
               select_domain[j] = true;
          sparse_rc<s_vector> pattern_out;
          //
          for(size_t i = 0; i < m; i++)
          {     // select i-th component of range
               b_vector select_range(m);
               d_vector w(m);
               for(size_t k = 0; k < m; k++)
               {     select_range[k] = k == i;
                    w[k] = 0.0;
                    if( k == i )
                         w[k] = 1.0;
               }
               //
               bool internal_bool = false;
               f.for_hes_sparsity(
                    select_domain, select_range, internal_bool, pattern_out
               );
               //
               // compute Hessian for i-th component function
               std::string                    coloring  = "cppad.symmetric";
               sparse_rcv<s_vector, d_vector> subset( pattern_out );
               CppAD::sparse_hes_work         work;
               d_vector x(n);
               for(size_t j = 0; j < n; j++)
                    x[j] = double(j + 2);
               size_t n_sweep = f.sparse_hes(
                    x, w, subset, pattern_out, coloring, work
               );
               //
               // check Hessian
               if( i == n )
                    ok &= subset.nnz() == 0;
               else
               {     ok &= check_hes(i, x, subset);
                    ok &= n_sweep == 1;
               }
          }
          return ok;
     }
     // Use reverse mode for Hessian and sparsity pattern
     bool reverse_hes(CppAD::ADFun<double>& f)
     {     bool ok = true;
          size_t n = f.Domain();
          size_t m = f.Range();
          //
          // n by n identity matrix
          sparse_rc<s_vector> pattern_in(n, n, n);
          for(size_t j = 0; j < n; j++)
               pattern_in.set(j, j, j);
          //
          bool transpose     = false;
          bool dependency    = false;
          bool internal_bool = true;
          sparse_rc<s_vector> pattern_out;
          //
          f.for_jac_sparsity(
               pattern_in, transpose, dependency, internal_bool, pattern_out
          );
          //
          for(size_t i = 0; i < m; i++)
          {     // select i-th component of range
               b_vector select_range(m);
               d_vector w(m);
               for(size_t k = 0; k < m; k++)
               {     select_range[k] = k == i;
                    w[k] = 0.0;
                    if( k == i )
                         w[k] = 1.0;
               }
               //
               f.rev_hes_sparsity(
                    select_range, transpose, internal_bool, pattern_out
               );
               //
               // compute Hessian for i-th component function
               std::string                    coloring  = "cppad.symmetric";
               sparse_rcv<s_vector, d_vector> subset( pattern_out );
               CppAD::sparse_hes_work         work;
               d_vector x(n);
               for(size_t j = 0; j < n; j++)
                    x[j] = double(j + 2);
               size_t n_sweep = f.sparse_hes(
                    x, w, subset, pattern_out, coloring, work
               );
               //
               // check Hessian
               if( i == n )
                    ok &= subset.nnz() == 0;
               else
               {     ok &= check_hes(i, x, subset);
                    ok &= n_sweep == 1;
               }
          }
          return ok;
     }
}
// driver for all of the cases above
bool rc_sparsity(void)
{     bool ok = true;
     //
     // record the funcion
     size_t n = 20;
     size_t m = n + 1;
     a_vector x(n), y(m);
     for(size_t j = 0; j < n; j++)
          x[j] = CppAD::AD<double>(j+1);
     CppAD::Independent(x);
     y = fun(x);
     CppAD::ADFun<double> f(x, y);
     //
     // run the example / tests
     ok &= forward_jac(f);
     ok &= reverse_jac(f);
     ok &= forward_hes(f);
     ok &= reverse_hes(f);
     //
     return ok;
}

Input File: example/sparse/rc_sparsity.cpp