Files
sunnypilot/external/cppad/include/cppad/local/rev_jac_sweep.hpp
T
2020-01-17 10:33:21 -08:00

759 lines
19 KiB
C++

# ifndef CPPAD_LOCAL_REV_JAC_SWEEP_HPP
# define CPPAD_LOCAL_REV_JAC_SWEEP_HPP
/* --------------------------------------------------------------------------
CppAD: C++ Algorithmic Differentiation: Copyright (C) 2003-17 Bradley M. Bell
CppAD is distributed under multiple licenses. This distribution is under
the terms of the
Eclipse Public License Version 1.0.
A copy of this license is included in the COPYING file of this distribution.
Please visit http://www.coin-or.org/CppAD/ for information on other licenses.
-------------------------------------------------------------------------- */
namespace CppAD { namespace local { // BEGIN_CPPAD_LOCAL_NAMESPACE
/*!
\file rev_jac_sweep.hpp
Compute Reverse mode Jacobian sparsity patterns.
*/
/*!
\def CPPAD_REV_JAC_SWEEP_TRACE
This value is either zero or one.
Zero is the normal operational value.
If it is one, a trace of every rev_jac_sweep computation is printed.
*/
# define CPPAD_REV_JAC_SWEEP_TRACE 0
/*!
Given the sparsity pattern for the dependent variables,
RevJacSweep computes the sparsity pattern for all the independent variables.
\tparam Base
base type for the operator; i.e., this operation sequence was recorded
using AD< \a Base > and computations by this routine are done using type
\a Base.
\tparam Vector_set
is the type used for vectors of sets. It can be either
sparse_pack or sparse_list.
\param dependency
Are the derivatives with respect to left and right of the expression below
considered to be non-zero:
\code
CondExpRel(left, right, if_true, if_false)
\endcode
This is used by the optimizer to obtain the correct dependency relations.
\param n
is the number of independent variables on the tape.
\param numvar
is the total number of variables on the tape; i.e.,
\a play->num_var_rec().
This is also the number of rows in the entire sparsity pattern \a RevJac.
\param play
The information stored in \a play
is a recording of the operations corresponding to a function
\f[
F : {\bf R}^n \rightarrow {\bf R}^m
\f]
where \f$ n \f$ is the number of independent variables
and \f$ m \f$ is the number of dependent variables.
The object \a play is effectly constant.
It is not declared const because while playing back the tape
the object \a play holds information about the current location
with in the tape and this changes during playback.
\param var_sparsity
For i = 0 , ... , \a numvar - 1,
(all the variables on the tape)
the forward Jacobian sparsity pattern for variable i
corresponds to the set with index i in \a var_sparsity.
\b
\b
\b Input:
For i = 0 , ... , \a numvar - 1,
the forward Jacobian sparsity pattern for variable i is an input
if i corresponds to a dependent variable.
Otherwise the sparsity patten is empty.
\n
\n
\b Output: For j = 1 , ... , \a n,
the sparsity pattern for the dependent variable with index (j-1)
is given by the set with index index j in \a var_sparsity.
*/
template <class Base, class Vector_set>
void RevJacSweep(
bool dependency,
size_t n,
size_t numvar,
local::player<Base>* play,
Vector_set& var_sparsity
)
{
OpCode op;
size_t i_op;
size_t i_var;
const addr_t* arg = CPPAD_NULL;
size_t i, j, k;
// length of the parameter vector (used by CppAD assert macros)
const size_t num_par = play->num_par_rec();
// check numvar argument
CPPAD_ASSERT_UNKNOWN( numvar > 0 );
CPPAD_ASSERT_UNKNOWN( play->num_var_rec() == numvar );
CPPAD_ASSERT_UNKNOWN( var_sparsity.n_set() == numvar );
// upper limit (exclusive) for elements in the set
size_t limit = var_sparsity.end();
// vecad_sparsity contains a sparsity pattern for each VecAD object.
// vecad_ind maps a VecAD index (beginning of the VecAD object)
// to the index of the corresponding set in vecad_sparsity.
size_t num_vecad_ind = play->num_vec_ind_rec();
size_t num_vecad_vec = play->num_vecad_vec_rec();
Vector_set vecad_sparsity;
vecad_sparsity.resize(num_vecad_vec, limit);
pod_vector<size_t> vecad_ind;
if( num_vecad_vec > 0 )
{ size_t length;
vecad_ind.extend(num_vecad_ind);
j = 0;
for(i = 0; i < num_vecad_vec; i++)
{ // length of this VecAD
length = play->GetVecInd(j);
// set to proper index for this VecAD
vecad_ind[j] = i;
for(k = 1; k <= length; k++)
vecad_ind[j+k] = num_vecad_vec; // invalid index
// start of next VecAD
j += length + 1;
}
CPPAD_ASSERT_UNKNOWN( j == play->num_vec_ind_rec() );
}
// ----------------------------------------------------------------------
// user's atomic op calculator
atomic_base<Base>* user_atom = CPPAD_NULL; // user's atomic op calculator
//
// work space used by UserOp.
vector<Base> user_x; // parameters in x as integers
vector<size_t> user_ix; // variable indices for argument vector
vector<size_t> user_iy; // variable indices for result vector
//
// information set by forward_user (initialization to avoid warnings)
size_t user_old=0, user_m=0, user_n=0, user_i=0, user_j=0;
// information set by forward_user (necessary initialization)
enum_user_state user_state = end_user; // proper initialization
// ----------------------------------------------------------------------
//
// pointer to the beginning of the parameter vector
// (used by atomic functions
const Base* parameter = CPPAD_NULL;
if( num_par > 0 )
parameter = play->GetPar();
//
// Initialize
play->reverse_start(op, arg, i_op, i_var);
CPPAD_ASSERT_UNKNOWN( op == EndOp );
# if CPPAD_REV_JAC_SWEEP_TRACE
std::cout << std::endl;
CppAD::vectorBool z_value(limit);
# endif
bool more_operators = true;
while(more_operators)
{ bool flag; // temporary for use in switch cases
//
// next op
play->reverse_next(op, arg, i_op, i_var);
# ifndef NDEBUG
if( i_op <= n )
{ CPPAD_ASSERT_UNKNOWN((op == InvOp) | (op == BeginOp));
}
else CPPAD_ASSERT_UNKNOWN((op != InvOp) & (op != BeginOp));
# endif
// rest of information depends on the case
switch( op )
{
case AbsOp:
CPPAD_ASSERT_NARG_NRES(op, 1, 1);
reverse_sparse_jacobian_unary_op(
i_var, arg[0], var_sparsity
);
break;
// -------------------------------------------------
case AddvvOp:
CPPAD_ASSERT_NARG_NRES(op, 2, 1);
reverse_sparse_jacobian_binary_op(
i_var, arg, var_sparsity
);
break;
// -------------------------------------------------
case AddpvOp:
CPPAD_ASSERT_NARG_NRES(op, 2, 1);
reverse_sparse_jacobian_unary_op(
i_var, arg[1], var_sparsity
);
break;
// -------------------------------------------------
case AcosOp:
// sqrt(1 - x * x), acos(x)
CPPAD_ASSERT_NARG_NRES(op, 1, 2);
reverse_sparse_jacobian_unary_op(
i_var, arg[0], var_sparsity
);
break;
// -------------------------------------------------
# if CPPAD_USE_CPLUSPLUS_2011
case AcoshOp:
// sqrt(x * x - 1), acosh(x)
CPPAD_ASSERT_NARG_NRES(op, 1, 2);
reverse_sparse_jacobian_unary_op(
i_var, arg[0], var_sparsity
);
break;
# endif
// -------------------------------------------------
case AsinOp:
// sqrt(1 - x * x), asin(x)
CPPAD_ASSERT_NARG_NRES(op, 1, 2);
reverse_sparse_jacobian_unary_op(
i_var, arg[0], var_sparsity
);
break;
// -------------------------------------------------
# if CPPAD_USE_CPLUSPLUS_2011
case AsinhOp:
// sqrt(1 + x * x), asinh(x)
CPPAD_ASSERT_NARG_NRES(op, 1, 2);
reverse_sparse_jacobian_unary_op(
i_var, arg[0], var_sparsity
);
break;
# endif
// -------------------------------------------------
case AtanOp:
// 1 + x * x, atan(x)
CPPAD_ASSERT_NARG_NRES(op, 1, 2);
reverse_sparse_jacobian_unary_op(
i_var, arg[0], var_sparsity
);
break;
// -------------------------------------------------
# if CPPAD_USE_CPLUSPLUS_2011
case AtanhOp:
// 1 - x * x, atanh(x)
CPPAD_ASSERT_NARG_NRES(op, 1, 2);
reverse_sparse_jacobian_unary_op(
i_var, arg[0], var_sparsity
);
break;
# endif
// -------------------------------------------------
case BeginOp:
CPPAD_ASSERT_NARG_NRES(op, 1, 1);
more_operators = false;
break;
// -------------------------------------------------
case CSkipOp:
// CSkipOp has a variable number of arguments and
// reverse_next thinks it one has one argument.
// We must inform reverse_next of this special case.
play->reverse_cskip(op, arg, i_op, i_var);
break;
// -------------------------------------------------
case CSumOp:
// CSumOp has a variable number of arguments and
// reverse_next thinks it one has one argument.
// We must inform reverse_next of this special case.
play->reverse_csum(op, arg, i_op, i_var);
reverse_sparse_jacobian_csum_op(
i_var, arg, var_sparsity
);
break;
// -------------------------------------------------
case CExpOp:
reverse_sparse_jacobian_cond_op(
dependency, i_var, arg, num_par, var_sparsity
);
break;
// ---------------------------------------------------
case CosOp:
// sin(x), cos(x)
CPPAD_ASSERT_NARG_NRES(op, 1, 2);
reverse_sparse_jacobian_unary_op(
i_var, arg[0], var_sparsity
);
break;
// ---------------------------------------------------
case CoshOp:
// sinh(x), cosh(x)
CPPAD_ASSERT_NARG_NRES(op, 1, 2);
reverse_sparse_jacobian_unary_op(
i_var, arg[0], var_sparsity
);
break;
// -------------------------------------------------
case DisOp:
CPPAD_ASSERT_NARG_NRES(op, 2, 1);
// derivative is identically zero but dependency is not
if( dependency ) reverse_sparse_jacobian_unary_op(
i_var, arg[1], var_sparsity
);
break;
// -------------------------------------------------
case DivvvOp:
CPPAD_ASSERT_NARG_NRES(op, 2, 1);
reverse_sparse_jacobian_binary_op(
i_var, arg, var_sparsity
);
break;
// -------------------------------------------------
case DivpvOp:
CPPAD_ASSERT_NARG_NRES(op, 2, 1);
reverse_sparse_jacobian_unary_op(
i_var, arg[1], var_sparsity
);
break;
// -------------------------------------------------
case DivvpOp:
CPPAD_ASSERT_NARG_NRES(op, 2, 1);
reverse_sparse_jacobian_unary_op(
i_var, arg[0], var_sparsity
);
break;
// -------------------------------------------------
case ErfOp:
// arg[1] is always the parameter 0
// arg[0] is always the parameter 2 / sqrt(pi)
CPPAD_ASSERT_NARG_NRES(op, 3, 5);
reverse_sparse_jacobian_unary_op(
i_var, arg[0], var_sparsity
);
break;
// -------------------------------------------------
case ExpOp:
CPPAD_ASSERT_NARG_NRES(op, 1, 1);
reverse_sparse_jacobian_unary_op(
i_var, arg[0], var_sparsity
);
break;
// -------------------------------------------------
# if CPPAD_USE_CPLUSPLUS_2011
case Expm1Op:
CPPAD_ASSERT_NARG_NRES(op, 1, 1);
reverse_sparse_jacobian_unary_op(
i_var, arg[0], var_sparsity
);
break;
# endif
// -------------------------------------------------
case InvOp:
CPPAD_ASSERT_NARG_NRES(op, 0, 1);
break;
// -------------------------------------------------
case LdpOp:
reverse_sparse_jacobian_load_op(
dependency,
op,
i_var,
arg,
num_vecad_ind,
vecad_ind.data(),
var_sparsity,
vecad_sparsity
);
break;
// -------------------------------------------------
case LdvOp:
reverse_sparse_jacobian_load_op(
dependency,
op,
i_var,
arg,
num_vecad_ind,
vecad_ind.data(),
var_sparsity,
vecad_sparsity
);
break;
// -------------------------------------------------
case EqpvOp:
case EqvvOp:
case LtpvOp:
case LtvpOp:
case LtvvOp:
case LepvOp:
case LevpOp:
case LevvOp:
case NepvOp:
case NevvOp:
CPPAD_ASSERT_NARG_NRES(op, 2, 0);
break;
// -------------------------------------------------
case LogOp:
CPPAD_ASSERT_NARG_NRES(op, 1, 1);
reverse_sparse_jacobian_unary_op(
i_var, arg[0], var_sparsity
);
break;
// -------------------------------------------------
# if CPPAD_USE_CPLUSPLUS_2011
case Log1pOp:
CPPAD_ASSERT_NARG_NRES(op, 1, 1);
reverse_sparse_jacobian_unary_op(
i_var, arg[0], var_sparsity
);
break;
# endif
// -------------------------------------------------
case MulpvOp:
CPPAD_ASSERT_NARG_NRES(op, 2, 1);
reverse_sparse_jacobian_unary_op(
i_var, arg[1], var_sparsity
);
break;
// -------------------------------------------------
case MulvvOp:
CPPAD_ASSERT_NARG_NRES(op, 2, 1);
reverse_sparse_jacobian_binary_op(
i_var, arg, var_sparsity
);
break;
// -------------------------------------------------
case ParOp:
CPPAD_ASSERT_NARG_NRES(op, 1, 1);
break;
// -------------------------------------------------
case PowvpOp:
reverse_sparse_jacobian_unary_op(
i_var, arg[0], var_sparsity
);
break;
// -------------------------------------------------
case PowpvOp:
CPPAD_ASSERT_NARG_NRES(op, 2, 3);
reverse_sparse_jacobian_unary_op(
i_var, arg[1], var_sparsity
);
break;
// -------------------------------------------------
case PowvvOp:
CPPAD_ASSERT_NARG_NRES(op, 2, 3);
reverse_sparse_jacobian_binary_op(
i_var, arg, var_sparsity
);
break;
// -------------------------------------------------
case PriOp:
CPPAD_ASSERT_NARG_NRES(op, 5, 0);
break;
// -------------------------------------------------
case SignOp:
CPPAD_ASSERT_NARG_NRES(op, 1, 1);
// derivative is identically zero but dependency is not
if( dependency ) reverse_sparse_jacobian_unary_op(
i_var, arg[0], var_sparsity
);
break;
// -------------------------------------------------
case SinOp:
// cos(x), sin(x)
CPPAD_ASSERT_NARG_NRES(op, 1, 2);
reverse_sparse_jacobian_unary_op(
i_var, arg[0], var_sparsity
);
break;
// -------------------------------------------------
case SinhOp:
// cosh(x), sinh(x)
CPPAD_ASSERT_NARG_NRES(op, 1, 2);
reverse_sparse_jacobian_unary_op(
i_var, arg[0], var_sparsity
);
break;
// -------------------------------------------------
case SqrtOp:
CPPAD_ASSERT_NARG_NRES(op, 1, 1);
reverse_sparse_jacobian_unary_op(
i_var, arg[0], var_sparsity
);
break;
// -------------------------------------------------
case StppOp:
// does not affect sparsity or dependency when both are parameters
CPPAD_ASSERT_NARG_NRES(op, 3, 0);
break;
// -------------------------------------------------
case StpvOp:
reverse_sparse_jacobian_store_op(
dependency,
op,
arg,
num_vecad_ind,
vecad_ind.data(),
var_sparsity,
vecad_sparsity
);
break;
// -------------------------------------------------
case StvpOp:
CPPAD_ASSERT_NARG_NRES(op, 3, 0);
// storing a parameter only affects dependency
reverse_sparse_jacobian_store_op(
dependency,
op,
arg,
num_vecad_ind,
vecad_ind.data(),
var_sparsity,
vecad_sparsity
);
break;
// -------------------------------------------------
case StvvOp:
reverse_sparse_jacobian_store_op(
dependency,
op,
arg,
num_vecad_ind,
vecad_ind.data(),
var_sparsity,
vecad_sparsity
);
break;
// -------------------------------------------------
case SubvvOp:
CPPAD_ASSERT_NARG_NRES(op, 2, 1);
reverse_sparse_jacobian_binary_op(
i_var, arg, var_sparsity
);
break;
// -------------------------------------------------
case SubpvOp:
CPPAD_ASSERT_NARG_NRES(op, 2, 1);
reverse_sparse_jacobian_unary_op(
i_var, arg[1], var_sparsity
);
break;
// -------------------------------------------------
case SubvpOp:
CPPAD_ASSERT_NARG_NRES(op, 2, 1);
reverse_sparse_jacobian_unary_op(
i_var, arg[0], var_sparsity
);
break;
// -------------------------------------------------
case TanOp:
// tan(x)^2, tan(x)
CPPAD_ASSERT_NARG_NRES(op, 1, 2);
reverse_sparse_jacobian_unary_op(
i_var, arg[0], var_sparsity
);
break;
// -------------------------------------------------
case TanhOp:
// tanh(x)^2, tanh(x)
CPPAD_ASSERT_NARG_NRES(op, 1, 2);
reverse_sparse_jacobian_unary_op(
i_var, arg[0], var_sparsity
);
break;
// -------------------------------------------------
case UserOp:
CPPAD_ASSERT_UNKNOWN(
user_state == start_user || user_state == end_user
);
flag = user_state == end_user;
user_atom = play->reverse_user(op, user_state,
user_old, user_m, user_n, user_i, user_j
);
if( flag )
{ // start of user atomic operation sequence
user_x.resize( user_n );
user_ix.resize( user_n );
user_iy.resize( user_m );
}
else
{ // end of users atomic operation sequence
user_atom->set_old(user_old);
user_atom->rev_sparse_jac(
user_x, user_ix, user_iy, var_sparsity
);
}
break;
case UsrapOp:
CPPAD_ASSERT_UNKNOWN( size_t(arg[0]) < num_par );
play->reverse_user(op, user_state,
user_old, user_m, user_n, user_i, user_j
);
// argument parameter value
user_x[user_j] = parameter[arg[0]];
// special variable index used for parameters
user_ix[user_j] = 0;
//
break;
case UsravOp:
CPPAD_ASSERT_UNKNOWN( size_t(arg[0]) <= i_var );
CPPAD_ASSERT_UNKNOWN( 0 < arg[0] );
play->reverse_user(op, user_state,
user_old, user_m, user_n, user_i, user_j
);
// argument variables not available during sparsity calculations
user_x[user_j] = CppAD::numeric_limits<Base>::quiet_NaN();
// variable index for this argument
user_ix[user_j] = arg[0];
break;
case UsrrpOp:
// parameter result in an atomic operation sequence
CPPAD_ASSERT_UNKNOWN( size_t(arg[0]) < num_par );
play->reverse_user(op, user_state,
user_old, user_m, user_n, user_i, user_j
);
// special variable index used for parameters
user_iy[user_i] = 0;
break;
case UsrrvOp:
play->reverse_user(op, user_state,
user_old, user_m, user_n, user_i, user_j
);
// variable index for this result
user_iy[user_i] = i_var;
break;
// -------------------------------------------------
case ZmulpvOp:
CPPAD_ASSERT_NARG_NRES(op, 2, 1);
reverse_sparse_jacobian_unary_op(
i_var, arg[1], var_sparsity
);
break;
// -------------------------------------------------
case ZmulvpOp:
CPPAD_ASSERT_NARG_NRES(op, 2, 1);
reverse_sparse_jacobian_unary_op(
i_var, arg[0], var_sparsity
);
break;
// -------------------------------------------------
case ZmulvvOp:
CPPAD_ASSERT_NARG_NRES(op, 2, 1);
reverse_sparse_jacobian_binary_op(
i_var, arg, var_sparsity
);
break;
// -------------------------------------------------
default:
CPPAD_ASSERT_UNKNOWN(0);
}
# if CPPAD_REV_JAC_SWEEP_TRACE
for(j = 0; j < limit; j++)
z_value[j] = false;
typename Vector_set::const_iterator itr(var_sparsity, i_var);
j = *itr;
while( j < limit )
{ z_value[j] = true;
j = *(++itr);
}
printOp(
std::cout,
play,
i_op,
i_var,
op,
arg
);
// Note that sparsity for UsrrvOp are computed before call to
// atomic function so no need to delay printing (as in forward mode)
if( NumRes(op) > 0 && op != BeginOp ) printOpResult(
std::cout,
0,
(CppAD::vectorBool *) CPPAD_NULL,
1,
&z_value
);
std::cout << std::endl;
}
std::cout << std::endl;
# else
}
# endif
// values corresponding to BeginOp
CPPAD_ASSERT_UNKNOWN( i_op == 0 );
CPPAD_ASSERT_UNKNOWN( i_var == 0 );
return;
}
} } // END_CPPAD_LOCAL_NAMESPACE
// preprocessor symbols that are local to this file
# undef CPPAD_REV_JAC_SWEEP_TRACE
# undef CPPAD_ATOMIC_CALL
# endif