we need an autodiff library at some point (or we need to roll our own but I do not think that makes sense). CppAD is well tested and header only and easy to include. It is also Liscene compatible with GPL v3.0. Here we bring it in as a dependency
448 lines
17 KiB
C++
448 lines
17 KiB
C++
# ifndef CPPAD_LOCAL_OPTIMIZE_GET_DYN_PREVIOUS_HPP
|
|
# define CPPAD_LOCAL_OPTIMIZE_GET_DYN_PREVIOUS_HPP
|
|
/* --------------------------------------------------------------------------
|
|
CppAD: C++ Algorithmic Differentiation: Copyright (C) 2003-19 Bradley M. Bell
|
|
|
|
CppAD is distributed under the terms of the
|
|
Eclipse Public License Version 2.0.
|
|
|
|
This Source Code may also be made available under the following
|
|
Secondary License when the conditions for such availability set forth
|
|
in the Eclipse Public License, Version 2.0 are satisfied:
|
|
GNU General Public License, Version 2.0 or later.
|
|
---------------------------------------------------------------------------- */
|
|
/*!
|
|
\file get_cexp_info.hpp
|
|
Create operator information tables
|
|
*/
|
|
|
|
# include <cppad/local/optimize/match_op.hpp>
|
|
# include <cppad/local/optimize/usage.hpp>
|
|
# include <cppad/local/optimize/hash_code.hpp>
|
|
|
|
// BEGIN_CPPAD_LOCAL_OPTIMIZE_NAMESPACE
|
|
namespace CppAD { namespace local { namespace optimize {
|
|
|
|
/*!
|
|
mapping from a dynamic parameter index to its arguments
|
|
|
|
\param i_dyn
|
|
is the dynamic parameter index
|
|
|
|
\param dyn_ind2par_ind
|
|
is the mapping from dynamic parameter index to parameter index
|
|
(size is number of dynamic parameters).
|
|
|
|
\param dyn_par_is
|
|
i-th element is true (false) if i-th parameter is (is not) dynamic
|
|
(size is number of parameters).
|
|
|
|
\param dyn_arg_offset
|
|
j-th element is the offset in dyn_par_arg of the first argument for j-th
|
|
dynamic parameter's operator (size is number of dynamic parameters).
|
|
This is only defined for dynamic parameters indices less than or equal i_dyn.
|
|
|
|
\param dyn_par_arg
|
|
it the vector of arguments for all the dynamic parameter operators.
|
|
This is only defined for dynamic parameters indices less than or equal i_dyn.
|
|
|
|
\param par_ind2dyn_ind
|
|
is the mapping from parameter index to dynamic parameter index
|
|
(size is number of parameters). This is only defined for parameter
|
|
indices less than or equal the parameter index corresponding to i_dyn.
|
|
|
|
\param dyn_previous
|
|
is the mapping from dynamic parameter index to previous dynamic parameter
|
|
that can be used as a replacement (size is number of dynamic parameters).
|
|
This is only defined for dynamic parameters indices less than or equal i_dyn.
|
|
|
|
\param arg_match
|
|
Size of this vector must be number of arguments for operator for i_dyn.
|
|
The input value of its elements does not matter.
|
|
Upn return it containts the parameter indices for the arguments
|
|
to use when matching this operator
|
|
Arguments that are dynamic prarameters, and have previous matches,
|
|
have been replaced by their previous matches.
|
|
*/
|
|
inline void dyn_arg_match(
|
|
size_t i_dyn ,
|
|
const pod_vector<addr_t>& dyn_ind2par_ind ,
|
|
const pod_vector<bool> & dyn_par_is ,
|
|
const pod_vector<addr_t>& dyn_arg_offset ,
|
|
const pod_vector<addr_t>& dyn_par_arg ,
|
|
const pod_vector<addr_t>& par_ind2dyn_ind ,
|
|
const pod_vector<addr_t>& dyn_previous ,
|
|
pod_vector<addr_t>& arg_match )
|
|
{
|
|
// number of dynamic parameters
|
|
addr_t num_dynamic_par = addr_t( dyn_ind2par_ind.size() );
|
|
//
|
|
// check some assumptions
|
|
CPPAD_ASSERT_UNKNOWN( size_t( num_dynamic_par ) == dyn_arg_offset.size() );
|
|
CPPAD_ASSERT_UNKNOWN( size_t( num_dynamic_par ) == dyn_previous.size() );
|
|
CPPAD_ASSERT_UNKNOWN( dyn_par_is.size() == par_ind2dyn_ind.size() );
|
|
//
|
|
// number of arguments for this operator
|
|
addr_t n_arg = addr_t( arg_match.size() );
|
|
//
|
|
// index in dyn_par_arg of first argument for this operator
|
|
addr_t i_arg = dyn_arg_offset[i_dyn];
|
|
//
|
|
// loop over arguments for this operator
|
|
for(addr_t j = 0; j < n_arg; ++j)
|
|
{ // parameter index for this argument
|
|
addr_t j_par = dyn_par_arg[i_arg + j];
|
|
CPPAD_ASSERT_UNKNOWN( j_par < dyn_ind2par_ind[i_dyn] );
|
|
//
|
|
// map dynamic parameters arguments to previous matches
|
|
if( dyn_par_is[j_par] )
|
|
{ addr_t j_dyn = par_ind2dyn_ind[j_par];
|
|
if( dyn_previous[j_dyn] != num_dynamic_par )
|
|
{ CPPAD_ASSERT_UNKNOWN( dyn_previous[j_dyn] < j_dyn );
|
|
// previous dynamic parameter
|
|
j_dyn = dyn_previous[j_dyn];
|
|
// correspoding parameter
|
|
j_par = dyn_ind2par_ind[j_dyn];
|
|
}
|
|
}
|
|
arg_match[j] = j_par;
|
|
}
|
|
return;
|
|
}
|
|
|
|
/*!
|
|
Get mapping from each dynamic parameter to a previous dynamic parameter
|
|
that can be used to replace it (if one exists).
|
|
|
|
\tparam Base
|
|
base type for the operator; i.e., this operation was recorded
|
|
using AD< Base > and computations by this routine are done using type
|
|
Base.
|
|
|
|
\param play
|
|
This is the old operation sequence.
|
|
|
|
\param random_itr
|
|
This is a random iterator for the old operation sequence.
|
|
|
|
\param par_usage
|
|
The size of this vector is the number of parameters in the
|
|
operation sequence.i.e., play->nun_var_rec().
|
|
It is the usage counting previous operator optimization of operators.
|
|
|
|
\param dyn_previous
|
|
The input size of this vector must be zero.
|
|
Upon return it has size equal to the number of dynamic parameters in the
|
|
operation sequence; i.e., num_dyn = play->num_dynamic_par().
|
|
Let k = dyn_parvious[j]. If k == num_dyn, no replacement was found for the
|
|
j-th dynamic parameter. If k != num_dyn, the k-th dynamic parameter can be
|
|
used in place of the j-th dynamic parameter, k < j, dyn_previous[k] != num_dyn,
|
|
par_usage[dyn_ind2par_ind[k]] == true.
|
|
*/
|
|
|
|
template <class Addr, class Base>
|
|
void get_dyn_previous(
|
|
const player<Base>* play ,
|
|
const play::const_random_iterator<Addr>& random_itr ,
|
|
pod_vector<bool>& par_usage ,
|
|
pod_vector<addr_t>& dyn_previous )
|
|
{
|
|
// number of parameters in the recording
|
|
size_t num_par = play->num_par_rec();
|
|
|
|
// number of dynamic parameters in the recording
|
|
size_t num_dynamic_par = play->num_dynamic_par();
|
|
|
|
// number of independent dynamic parameters in the recording
|
|
size_t num_dynamic_ind = play->num_dynamic_ind();
|
|
|
|
// check some assumptions
|
|
CPPAD_ASSERT_UNKNOWN( dyn_previous.size() == 0 );
|
|
CPPAD_ASSERT_UNKNOWN( par_usage.size() == num_par );
|
|
CPPAD_ASSERT_UNKNOWN( num_dynamic_par <= num_par );
|
|
CPPAD_ASSERT_UNKNOWN( num_dynamic_ind <= num_dynamic_par );
|
|
CPPAD_ASSERT_UNKNOWN( num_arg_dyn( ind_dyn ) == 0 );
|
|
|
|
// dynamic parameter information
|
|
dyn_previous.resize( num_dynamic_par );
|
|
const pod_vector<addr_t>& dyn_ind2par_ind( play->dyn_ind2par_ind() );
|
|
const pod_vector<bool>& dyn_par_is( play->dyn_par_is() );
|
|
const pod_vector<opcode_t>& dyn_par_op( play->dyn_par_op() );
|
|
const pod_vector<addr_t>& dyn_par_arg( play->dyn_par_arg() );
|
|
|
|
// mapping from parameter index to dynamic parameter index
|
|
// only defined when dyn_par_is is true
|
|
pod_vector<addr_t> par_ind2dyn_ind(num_par);
|
|
|
|
// mapping from dynamic parameter index to first argument index
|
|
pod_vector<addr_t> dyn_arg_offset(num_dynamic_par);
|
|
|
|
// ----------------------------------------------------------------------
|
|
// compute dyn_previous
|
|
// ----------------------------------------------------------------------
|
|
sparse::list_setvec hash_table_dyn;
|
|
hash_table_dyn.resize(CPPAD_HASH_TABLE_SIZE, num_dynamic_par);
|
|
//
|
|
// Initialize in dyn_par_arg
|
|
// (independent dynamic parameters do not have any arguments)
|
|
size_t i_arg = 0;
|
|
//
|
|
// independent dynamic parameters
|
|
for(size_t i_dyn = 0; i_dyn < num_dynamic_ind; ++i_dyn)
|
|
{ // parameter index
|
|
size_t i_par = size_t( dyn_ind2par_ind[i_dyn] );
|
|
// dynamic parameter index is one greater because phantom parameter
|
|
// at index 0 is not dynamic
|
|
CPPAD_ASSERT_UNKNOWN( i_par == i_dyn + 1 );
|
|
// mapping from parameter index to dynamic parameter index
|
|
par_ind2dyn_ind[i_par] = addr_t( i_dyn );
|
|
// never get optimized out
|
|
dyn_previous[i_dyn] = addr_t( num_dynamic_par );
|
|
}
|
|
//
|
|
// other dynamic parameters
|
|
for(size_t i_dyn = num_dynamic_ind; i_dyn < num_dynamic_par; ++i_dyn)
|
|
{ // Initialize previous for this dynamic parameter. This is only
|
|
// defined for dynamic parameter indices less than or equal i_dyn
|
|
dyn_previous[i_dyn] = addr_t( num_dynamic_par );
|
|
//
|
|
// mapping from dynamic parameter index to argument offset
|
|
// is only defined for j_dyn <= i_dyn
|
|
dyn_arg_offset[i_dyn] = addr_t( i_arg );
|
|
//
|
|
// parameter index for this dynamic parameter
|
|
size_t i_par = size_t( dyn_ind2par_ind[i_dyn] );
|
|
//
|
|
// mapping from parameter indices to dynamic parameter indices
|
|
// is only defined when dyn_par_is[i_par] is true and for parameter
|
|
// indices less than or equal i_par
|
|
CPPAD_ASSERT_UNKNOWN( dyn_par_is[i_par] );
|
|
par_ind2dyn_ind[i_par] = addr_t( i_dyn );
|
|
//
|
|
// operator for this dynamic parameter
|
|
op_code_dyn op = op_code_dyn( dyn_par_op[i_dyn] );
|
|
//
|
|
// temporary used below and decaled here to reduce memory allocation
|
|
pod_vector<addr_t> arg_match;
|
|
//
|
|
// temporaries used below and decaled here to reduce indentation level
|
|
bool match;
|
|
size_t code;
|
|
size_t count;
|
|
//
|
|
// check for a previous match for i_dyn
|
|
if( par_usage[i_par] ) switch( op )
|
|
{
|
|
// ---------------------------------------------------------------
|
|
// unary operators
|
|
case abs_dyn:
|
|
case acos_dyn:
|
|
case acosh_dyn:
|
|
case asin_dyn:
|
|
case asinh_dyn:
|
|
case atan_dyn:
|
|
case atanh_dyn:
|
|
case cos_dyn:
|
|
case cosh_dyn:
|
|
case erf_dyn:
|
|
case erfc_dyn:
|
|
case exp_dyn:
|
|
case expm1_dyn:
|
|
case fabs_dyn:
|
|
case log_dyn:
|
|
case log1p_dyn:
|
|
case sign_dyn:
|
|
case sin_dyn:
|
|
case sinh_dyn:
|
|
case sqrt_dyn:
|
|
case tan_dyn:
|
|
case tanh_dyn:
|
|
CPPAD_ASSERT_UNKNOWN( num_arg_dyn(op) == 1);
|
|
CPPAD_ASSERT_UNKNOWN( dyn_par_is[i_par] );
|
|
{ size_t num_arg = 1;
|
|
arg_match.resize(num_arg);
|
|
dyn_arg_match(
|
|
i_dyn,
|
|
dyn_ind2par_ind,
|
|
dyn_par_is,
|
|
dyn_arg_offset,
|
|
dyn_par_arg,
|
|
par_ind2dyn_ind,
|
|
dyn_previous,
|
|
arg_match
|
|
);
|
|
opcode_t op_t = opcode_t(op);
|
|
code = optimize_hash_code(
|
|
op_t, num_arg, arg_match.data()
|
|
);
|
|
//
|
|
// iterator for the set with this hash code
|
|
sparse::list_setvec_const_iterator itr(hash_table_dyn, code);
|
|
//
|
|
// check for a match
|
|
count = 0;
|
|
match = false;
|
|
while( ! match && *itr != num_dynamic_par )
|
|
{ ++count;
|
|
//
|
|
// candidate for current dynamic parameter
|
|
size_t k_dyn = *itr;
|
|
CPPAD_ASSERT_UNKNOWN( k_dyn < i_dyn );
|
|
//
|
|
// argument offset for the candidate
|
|
addr_t k_arg = dyn_arg_offset[k_dyn];
|
|
//
|
|
match = op_t == dyn_par_op[k_dyn];
|
|
match &= arg_match[0] == dyn_par_arg[k_arg + 0];
|
|
if( ! match )
|
|
++itr;
|
|
}
|
|
if( match )
|
|
{ size_t k_dyn = *itr;
|
|
CPPAD_ASSERT_UNKNOWN( k_dyn < i_dyn );
|
|
dyn_previous[i_dyn] = addr_t( k_dyn );
|
|
}
|
|
else
|
|
{ CPPAD_ASSERT_UNKNOWN( count < 11 );
|
|
if( count == 10 )
|
|
{ // restart list for this hash code
|
|
hash_table_dyn.clear(code);
|
|
}
|
|
// Add this entry to hash table.
|
|
// Not using post_element becasue we need to iterate for
|
|
// this code before adding another element for this code.
|
|
hash_table_dyn.add_element(code, i_dyn);
|
|
}
|
|
}
|
|
break;
|
|
|
|
// ---------------------------------------------------------------
|
|
// binary operators
|
|
case add_dyn:
|
|
case div_dyn:
|
|
case mul_dyn:
|
|
case pow_dyn:
|
|
case sub_dyn:
|
|
case zmul_dyn:
|
|
CPPAD_ASSERT_UNKNOWN( num_arg_dyn(op) == 2);
|
|
CPPAD_ASSERT_UNKNOWN( dyn_par_is[i_par] );
|
|
match = false;
|
|
{ size_t num_arg = 2;
|
|
arg_match.resize(num_arg);
|
|
dyn_arg_match(
|
|
i_dyn,
|
|
dyn_ind2par_ind,
|
|
dyn_par_is,
|
|
dyn_arg_offset,
|
|
dyn_par_arg ,
|
|
par_ind2dyn_ind,
|
|
dyn_previous,
|
|
arg_match
|
|
);
|
|
opcode_t op_t = opcode_t(op);
|
|
code = optimize_hash_code(
|
|
op_t, num_arg, arg_match.data()
|
|
);
|
|
//
|
|
// iterator for the set with this hash code
|
|
sparse::list_setvec_const_iterator itr(hash_table_dyn, code);
|
|
//
|
|
// check for a match
|
|
count = 0;
|
|
while( ! match && *itr != num_dynamic_par )
|
|
{ ++count;
|
|
//
|
|
// candidate for current dynamic parameter
|
|
size_t k_dyn = *itr;
|
|
CPPAD_ASSERT_UNKNOWN( k_dyn < i_dyn );
|
|
//
|
|
// argument offset for the candidate
|
|
addr_t k_arg = dyn_arg_offset[k_dyn];
|
|
//
|
|
match = op_t == dyn_par_op[k_dyn];
|
|
match &= arg_match[0] == dyn_par_arg[k_arg + 0];
|
|
match &= arg_match[1] == dyn_par_arg[k_arg + 1];
|
|
if( ! match )
|
|
++itr;
|
|
}
|
|
if( match )
|
|
{ size_t k_dyn = *itr;
|
|
CPPAD_ASSERT_UNKNOWN( k_dyn < i_dyn );
|
|
dyn_previous[i_dyn] = addr_t( k_dyn );
|
|
}
|
|
}
|
|
if( (! match) & ( (op == add_dyn) | (op == mul_dyn) ) )
|
|
{ size_t num_arg = 2;
|
|
std::swap( arg_match[0], arg_match[1] );
|
|
opcode_t op_t = opcode_t(op);
|
|
size_t code_swp = optimize_hash_code(
|
|
op_t, num_arg, arg_match.data()
|
|
);
|
|
//
|
|
// iterator for the set with this hash code
|
|
sparse::list_setvec_const_iterator itr(hash_table_dyn, code_swp);
|
|
//
|
|
// check for a match
|
|
while( ! match && *itr != num_dynamic_par )
|
|
{ //
|
|
// candidate for current dynamic parameter
|
|
size_t k_dyn = *itr;
|
|
CPPAD_ASSERT_UNKNOWN( k_dyn < i_dyn );
|
|
//
|
|
// argument offset for the candidate
|
|
addr_t k_arg = dyn_arg_offset[k_dyn];
|
|
//
|
|
match = op_t == dyn_par_op[k_dyn];
|
|
match &= arg_match[0] == dyn_par_arg[k_arg + 0];
|
|
match &= arg_match[1] == dyn_par_arg[k_arg + 1];
|
|
if( ! match )
|
|
++itr;
|
|
}
|
|
if( match )
|
|
{ size_t k_dyn = *itr;
|
|
CPPAD_ASSERT_UNKNOWN( k_dyn < i_dyn );
|
|
dyn_previous[i_dyn] = addr_t( k_dyn );
|
|
}
|
|
}
|
|
if( ! match )
|
|
{ CPPAD_ASSERT_UNKNOWN( count < 11 );
|
|
if( count == 10 )
|
|
{ // restart list for this hash code
|
|
hash_table_dyn.clear(code);
|
|
}
|
|
// Add the entry to hash table
|
|
// Not using post_element becasue we need to iterate for
|
|
// this code before adding another element for this code.
|
|
hash_table_dyn.add_element(code, i_dyn);
|
|
}
|
|
|
|
// --------------------------------------------------------------
|
|
// skipping these cases for now
|
|
case dis_dyn:
|
|
case cond_exp_dyn:
|
|
case atom_dyn:
|
|
case result_dyn:
|
|
break;
|
|
|
|
|
|
// --------------------------------------------------------------
|
|
// should be no other cases; e.g., no ind_dyn or number_dyn.
|
|
default:
|
|
CPPAD_ASSERT_UNKNOWN(false);
|
|
break;
|
|
}
|
|
i_arg += num_arg_dyn(op);
|
|
if( op == atom_dyn )
|
|
{ CPPAD_ASSERT_UNKNOWN( num_arg_dyn(op) == 0 );
|
|
size_t n = size_t( dyn_par_arg[i_arg + 1] );
|
|
size_t m = size_t( dyn_par_arg[i_arg + 2] );
|
|
size_t n_arg = 5 + n + m;
|
|
i_arg += n_arg;
|
|
}
|
|
}
|
|
}
|
|
|
|
} } } // END_CPPAD_LOCAL_OPTIMIZE_NAMESPACE
|
|
|
|
# endif
|