Prev Next cppad_sparse_hessian.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}} }@)@This is cppad-20221105 documentation. Here is a link to its current documentation .
Cppad Speed: Sparse Hessian

Specifications
See link_sparse_hessian .

Implementation
# include <cppad/cppad.hpp>
# include <cppad/speed/uniform_01.hpp>
# include <cppad/speed/sparse_hes_fun.hpp>

// Note that CppAD uses global_option["memory"] at the main program level
# include <map>
extern std::map<std::string, bool> global_option;
// see comments in main program for this external
extern size_t global_cppad_thread_alloc_inuse;

namespace {
    // typedefs
    using CppAD::vector;
    typedef CppAD::AD<double>                     a1double;
    typedef CppAD::AD<a1double>                   a2double;
    typedef vector<bool>                          b_vector;
    typedef vector<size_t>                        s_vector;
    typedef vector<double>                        d_vector;
    typedef vector<a1double>                      a1vector;
    typedef vector<a2double>                      a2vector;
    typedef CppAD::sparse_rc<s_vector>            sparsity_pattern;
    typedef CppAD::sparse_rcv<s_vector, d_vector> sparse_matrix;
    // ------------------------------------------------------------------------
    void create_fun(
        const d_vector&             x        ,
        const s_vector&             row      ,
        const s_vector&             col      ,
        CppAD::ADFun<double>&       fun      )
    {
        // initialize a1double version of independent variables
        size_t n = x.size();
        a1vector a1x(n);
        for(size_t j = 0; j < n; j++)
            a1x[j] = x[j];
        //
        // optimization options
        std::string optimize_options =
            "no_conditional_skip no_compare_op no_print_for_op";
        //
        // order of derivative in sparse_hes_fun
        size_t order = 0;
        //
        // do not even record comparison operators
        size_t abort_op_index = 0;
        bool record_compare   = false;
        //
        if( ! global_option["hes2jac"] )
        {
            // declare independent variables
            Independent(a1x, abort_op_index, record_compare);
            //
            // AD computation of y
            a1vector a1y(1);
            CppAD::sparse_hes_fun<a1double>(n, a1x, row, col, order, a1y);
            //
            // create function object f : X -> Y
            fun.Dependent(a1x, a1y);
            //
            if( global_option["optimize"] )
                fun.optimize(optimize_options);
            //
            // skip comparison operators
            fun.compare_change_count(0);
            //
            // fun corresonds to f(x)
            return;
        }
        // declare independent variables for f(x)
        a2vector a2x(n);
        for(size_t j = 0; j < n; j++)
            a2x[j] = a1x[j];
        Independent(a2x, abort_op_index, record_compare);
        //
        // a2double computation of y
        a2vector a2y(1);
        CppAD::sparse_hes_fun<a2double>(n, a2x, row, col, order, a2y);
        //
        // create function object corresponding to y = f(x)
        CppAD::ADFun<a1double> a1f;
        a1f.Dependent(a2x, a2y);
        //
        // declare independent variables for g(x)
        Independent(a1x, abort_op_index, record_compare);
        //
        // a1double computation of z
        a1vector a1w(1), a1z(n);
        a1w[0] = 1.0;
        a1f.Forward(0, a1x);
        a1z = a1f.Reverse(1, a1w);
        //
        // create function object z = g(x) = f'(x)
        fun.Dependent(a1x, a1z);
        //
        if( global_option["optimize"] )
            fun.optimize(optimize_options);
        //
        // skip comparison operators
        fun.compare_change_count(0);
        //
        // fun corresonds to g(x)
        return;
    }
    // ------------------------------------------------------------------------
    void calc_sparsity(
        sparsity_pattern&      sparsity ,
        CppAD::ADFun<double>&  fun      )
    {
        size_t n = fun.Domain();
        size_t m = fun.Range();
        //
        bool transpose     = false;
        //
        if( global_option["subsparsity"] )
        {   CPPAD_ASSERT_UNKNOWN( global_option["hes2jac"] )
            CPPAD_ASSERT_UNKNOWN( n == m );
            b_vector select_domain(n), select_range(m);
            for(size_t j = 0; j < n; ++j)
                select_domain[j] = true;
            for(size_t i = 0; i < m; ++i)
                select_range[i] = true;
            //
            // fun corresponds to g(x)
            fun.subgraph_sparsity(
                select_domain, select_range, transpose, sparsity
            );
            return;
        }
        bool dependency    = false;
        bool reverse       = global_option["revsparsity"];
        bool internal_bool = global_option["boolsparsity"];
        //
        if( ! global_option["hes2jac"] )
        {   // fun corresponds to f(x)
            //
            CPPAD_ASSERT_UNKNOWN( m == 1 );
            //
            b_vector select_range(m);
            select_range[0] = true;
            //
            if( reverse )
            {   sparsity_pattern identity;
                identity.resize(n, n, n);
                for(size_t k = 0; k < n; k++)
                    identity.set(k, k, k);
                fun.for_jac_sparsity(
                    identity, transpose, dependency, internal_bool, sparsity
                );
                fun.rev_hes_sparsity(
                    select_range, transpose, internal_bool, sparsity
                );
            }
            else
            {   b_vector select_domain(n);
                for(size_t j = 0; j < n; j++)
                    select_domain[j] = true;
                fun.for_hes_sparsity(
                    select_domain, select_range, internal_bool, sparsity
                );
            }
            return;
        }
        // fun correspnds to g(x)
        CPPAD_ASSERT_UNKNOWN( m == n );
        //
        // sparsity pattern for identity matrix
        sparsity_pattern eye;
        eye.resize(n, n, n);
        for(size_t k = 0; k < n; k++)
            eye.set(k, k, k);
        //
        if( reverse )
        {   fun.rev_jac_sparsity(
                eye, transpose, dependency, internal_bool, sparsity
            );
        }
        else
        {   fun.for_jac_sparsity(
                eye, transpose, dependency, internal_bool, sparsity
            );
        }
        return;
    }
    // ------------------------------------------------------------------------
    size_t calc_hessian(
        d_vector&               hessian  ,
        const d_vector&         x        ,
        sparse_matrix&          subset   ,
        const sparsity_pattern& sparsity ,
        CppAD::sparse_jac_work& jac_work ,
        CppAD::sparse_hes_work& hes_work ,
        CppAD::ADFun<double>&   fun      )
    {   size_t n_color;
        //
        if( ! global_option["hes2jac"] )
        {   // fun corresponds to f(x)
            //
            // coloring method
            std::string coloring = "cppad";
            if( global_option["colpack"] )
                coloring = "colpack";
            if( global_option["symmetric"] )
                coloring += ".symmetric";
            else
                coloring += ".general";
            //
            // only one function component
            d_vector w(1);
            w[0] = 1.0;
            //
            // compute hessian
            n_color = fun.sparse_hes(
                x, w, subset, sparsity, coloring, hes_work
            );
        }
        else
        {   // fun corresponds to g(x)
            //
            if( global_option["subgraph"] )
            {   fun.subgraph_jac_rev(x, subset);
                n_color = 0;
            }
            else
            {
                //
                // coloring method
                std::string coloring = "cppad";
# if CPPAD_HAS_COLPACK
                if( global_option["colpack"] )
                    coloring = "colpack";
# endif
                size_t group_max = 1;
                n_color = fun.sparse_jac_for(
                    group_max, x, subset, sparsity, coloring, jac_work
                );
            }
        }
        // return result
        const d_vector& val( subset.val() );
        size_t nnz = subset.nnz();
        for(size_t k = 0; k < nnz; k++)
            hessian[k] = val[k];
        //
        return n_color;
    }
}

bool link_sparse_hessian(
    size_t                           size     ,
    size_t                           repeat   ,
    const CppAD::vector<size_t>&     row      ,
    const CppAD::vector<size_t>&     col      ,
    CppAD::vector<double>&           x        ,
    CppAD::vector<double>&           hessian  ,
    size_t&                          n_color  )
{   global_cppad_thread_alloc_inuse = 0;

    // --------------------------------------------------------------------
    // check global options
    const char* valid[] = {
        "memory", "onetape", "optimize", "hes2jac", "subgraph",
        "boolsparsity", "revsparsity", "symmetric"
# if CPPAD_HAS_COLPACK
        , "colpack"
# else
        , "subsparsity"
# endif
    };
    size_t n_valid = sizeof(valid) / sizeof(valid[0]);
    typedef std::map<std::string, bool>::iterator iterator;
    //
    for(iterator itr=global_option.begin(); itr!=global_option.end(); ++itr)
    {   if( itr->second )
        {   bool ok = false;
            for(size_t i = 0; i < n_valid; i++)
                ok |= itr->first == valid[i];
            if( ! ok )
                return false;
        }
    }
    if( global_option["subsparsity"] )
    {   if( global_option["boolsparsity"] || global_option["revsparsity"] )
            return false;
        if( ! global_option["hes2jac"] )
            return false;
    }
    if( global_option["subgraph"] )
    {   if( ! global_option["hes2jac"] )
            return false;
    }
# if ! CPPAD_HAS_COLPACK
    if( global_option["colpack"] )
        return false;
# endif
    // -----------------------------------------------------------------------
    // setup
    size_t n = size;          // number of independent variables
    CppAD::ADFun<double> fun; // AD function object used to calculate Hessian
    //
    // declare sparsity pattern
    sparsity_pattern sparsity;
    //
    // declare subset where Hessian is evaluated
    sparsity_pattern subset_pattern;
    size_t nr  = n;
    size_t nc  = n;
    size_t nnz = row.size();
    subset_pattern.resize(nr, nc, nnz);
    for(size_t k = 0; k < nnz; k++)
        subset_pattern.set(k, row[k], col[k]);
    sparse_matrix subset( subset_pattern );
    //
    // structures that holds some of the work done by sparse_jac, sparse_hes
    CppAD::sparse_jac_work jac_work;
    CppAD::sparse_hes_work hes_work;

    // -----------------------------------------------------------------------
    if( ! global_option["onetape"] ) while(repeat--)
    {   // choose a value for x
        CppAD::uniform_01(n, x);
        //
        // create f(x)
        create_fun(x, row, col, fun);
        //
        // calculate the sparsity pattern for Hessian of f(x)
        calc_sparsity(sparsity, fun);
        //
        // calculate the Hessian at this x
        jac_work.clear(); // wihtout work from previous calculation
        hes_work.clear();
        n_color = calc_hessian(
            hessian, x, subset, sparsity, jac_work, hes_work, fun
        );
    }
    else
    {   // choose a value for x
        CppAD::uniform_01(n, x);
        //
        // create f(x)
        create_fun(x, row, col, fun);
        //
        // calculate the sparsity pattern for Hessian of f(x)
        calc_sparsity(sparsity, fun);
        //
        while(repeat--)
        {   // choose a value for x
            CppAD::uniform_01(n, x);
            //
            // calculate this Hessian at this x
            n_color = calc_hessian(
                hessian, x, subset, sparsity, jac_work, hes_work, fun
            );
        }
    }
    size_t thread                   = CppAD::thread_alloc::thread_num();
    global_cppad_thread_alloc_inuse = CppAD::thread_alloc::inuse(thread);
    return true;
}

Input File: speed/cppad/sparse_hessian.cpp