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
360 lines
9.6 KiB
C++
360 lines
9.6 KiB
C++
# ifndef CPPAD_EXAMPLE_BASE_ADOLC_HPP
|
|
# define CPPAD_EXAMPLE_BASE_ADOLC_HPP
|
|
/* --------------------------------------------------------------------------
|
|
CppAD: C++ Algorithmic Differentiation: Copyright (C) 2003-20 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.
|
|
---------------------------------------------------------------------------- */
|
|
/*
|
|
$begin base_adolc.hpp$$
|
|
$spell
|
|
stringstream
|
|
struct
|
|
string
|
|
setprecision
|
|
str
|
|
valgrind
|
|
azmul
|
|
expm1
|
|
atanh
|
|
acosh
|
|
asinh
|
|
erf
|
|
erfc
|
|
ifndef
|
|
define
|
|
endif
|
|
Rel
|
|
codassign
|
|
eps
|
|
std
|
|
abs_geq
|
|
fabs
|
|
cppad.hpp
|
|
undef
|
|
Lt
|
|
Le
|
|
Eq
|
|
Ge
|
|
Gt
|
|
namespace
|
|
cassert
|
|
condassign
|
|
hpp
|
|
bool
|
|
const
|
|
Adolc
|
|
adouble
|
|
CondExpOp
|
|
inline
|
|
enum
|
|
CppAD
|
|
pow
|
|
acos
|
|
asin
|
|
atan
|
|
cos
|
|
cosh
|
|
exp
|
|
sqrt
|
|
atrig
|
|
$$
|
|
|
|
|
|
$section Enable use of AD<Base> where Base is Adolc's adouble Type$$
|
|
|
|
$head Syntax$$
|
|
$codei%# include <cppad/example/base_adolc.hpp>
|
|
%$$
|
|
$children%
|
|
example/general/mul_level_adolc.cpp
|
|
%$$
|
|
|
|
$head Example$$
|
|
The file $cref mul_level_adolc.cpp$$ contains an example use of
|
|
Adolc's $code adouble$$ type for a CppAD $icode Base$$ type.
|
|
The file $cref mul_level_adolc_ode.cpp$$ contains a more realistic
|
|
(and complex) example.
|
|
|
|
$head Include Files$$
|
|
This file $code base_adolc.hpp$$ requires $code adouble$$ to be defined.
|
|
In addition, it is included before $code <cppad/cppad.hpp>$$,
|
|
but it needs to include parts of CppAD that are used by this file.
|
|
This is done with the following include commands:
|
|
$srccode%cpp% */
|
|
# include <adolc/adolc.h>
|
|
# include <cppad/base_require.hpp>
|
|
/* %$$
|
|
|
|
$head CondExpOp$$
|
|
The type $code adouble$$ supports a conditional assignment function
|
|
with the syntax
|
|
$codei%
|
|
condassign(%a%, %b%, %c%, %d%)
|
|
%$$
|
|
which evaluates to
|
|
$codei%
|
|
%a% = (%b% > 0) ? %c% : %d%;
|
|
%$$
|
|
This enables one to include conditionals in the recording of
|
|
$code adouble$$ operations and later evaluation for different
|
|
values of the independent variables
|
|
(in the same spirit as the CppAD $cref CondExp$$ function).
|
|
$srccode%cpp% */
|
|
namespace CppAD {
|
|
inline adouble CondExpOp(
|
|
enum CppAD::CompareOp cop ,
|
|
const adouble &left ,
|
|
const adouble &right ,
|
|
const adouble &trueCase ,
|
|
const adouble &falseCase )
|
|
{ adouble result;
|
|
switch( cop )
|
|
{
|
|
case CompareLt: // left < right
|
|
condassign(result, right - left, trueCase, falseCase);
|
|
break;
|
|
|
|
case CompareLe: // left <= right
|
|
condassign(result, left - right, falseCase, trueCase);
|
|
break;
|
|
|
|
case CompareEq: // left == right
|
|
condassign(result, left - right, falseCase, trueCase);
|
|
condassign(result, right - left, falseCase, result);
|
|
break;
|
|
|
|
case CompareGe: // left >= right
|
|
condassign(result, right - left, falseCase, trueCase);
|
|
break;
|
|
|
|
case CompareGt: // left > right
|
|
condassign(result, left - right, trueCase, falseCase);
|
|
break;
|
|
default:
|
|
CppAD::ErrorHandler::Call(
|
|
true , __LINE__ , __FILE__ ,
|
|
"CppAD::CondExp",
|
|
"Error: for unknown reason."
|
|
);
|
|
result = trueCase;
|
|
}
|
|
return result;
|
|
}
|
|
}
|
|
/* %$$
|
|
|
|
$head CondExpRel$$
|
|
The $cref/CPPAD_COND_EXP_REL/base_cond_exp/CondExpRel/$$ macro invocation
|
|
$srccode%cpp% */
|
|
namespace CppAD {
|
|
CPPAD_COND_EXP_REL(adouble)
|
|
}
|
|
/* %$$
|
|
|
|
$head EqualOpSeq$$
|
|
The Adolc user interface does not specify a way to determine if
|
|
two $code adouble$$ variables correspond to the same operations sequence.
|
|
Make $code EqualOpSeq$$ an error if it gets used:
|
|
$srccode%cpp% */
|
|
namespace CppAD {
|
|
inline bool EqualOpSeq(const adouble &x, const adouble &y)
|
|
{ CppAD::ErrorHandler::Call(
|
|
true , __LINE__ , __FILE__ ,
|
|
"CppAD::EqualOpSeq(x, y)",
|
|
"Error: adouble does not support EqualOpSeq."
|
|
);
|
|
return false;
|
|
}
|
|
}
|
|
/* %$$
|
|
|
|
$head Identical$$
|
|
The Adolc user interface does not specify a way to determine if an
|
|
$code adouble$$ depends on the independent variables.
|
|
To be safe (but slow) return $code false$$ in all the cases below.
|
|
$srccode%cpp% */
|
|
namespace CppAD {
|
|
inline bool IdenticalCon(const adouble &x)
|
|
{ return false; }
|
|
inline bool IdenticalZero(const adouble &x)
|
|
{ return false; }
|
|
inline bool IdenticalOne(const adouble &x)
|
|
{ return false; }
|
|
inline bool IdenticalEqualCon(const adouble &x, const adouble &y)
|
|
{ return false; }
|
|
}
|
|
/* %$$
|
|
|
|
$head Integer$$
|
|
$srccode%cpp% */
|
|
inline int Integer(const adouble &x)
|
|
{ return static_cast<int>( x.getValue() ); }
|
|
/* %$$
|
|
|
|
$head azmul$$
|
|
$srccode%cpp% */
|
|
namespace CppAD {
|
|
CPPAD_AZMUL( adouble )
|
|
}
|
|
/* %$$
|
|
|
|
$head Ordered$$
|
|
$srccode%cpp% */
|
|
namespace CppAD {
|
|
inline bool GreaterThanZero(const adouble &x)
|
|
{ return (x > 0); }
|
|
inline bool GreaterThanOrZero(const adouble &x)
|
|
{ return (x >= 0); }
|
|
inline bool LessThanZero(const adouble &x)
|
|
{ return (x < 0); }
|
|
inline bool LessThanOrZero(const adouble &x)
|
|
{ return (x <= 0); }
|
|
inline bool abs_geq(const adouble& x, const adouble& y)
|
|
{ return fabs(x) >= fabs(y); }
|
|
}
|
|
/* %$$
|
|
|
|
$head Unary Standard Math$$
|
|
The following $cref/required/base_require/$$ functions
|
|
are defined by the Adolc package for the $code adouble$$ base case:
|
|
$pre
|
|
$$
|
|
$code acos$$,
|
|
$code acosh$$,
|
|
$code asin$$,
|
|
$code asinh$$,
|
|
$code atan$$,
|
|
$code atanh$$,
|
|
$code cos$$,
|
|
$code cosh$$,
|
|
$code erf$$,
|
|
$code exp$$,
|
|
$code fabs$$,
|
|
$code log$$,
|
|
$code sin$$,
|
|
$code sinh$$,
|
|
$code sqrt$$,
|
|
$code tan$$.
|
|
|
|
$head erfc$$
|
|
If you provide $code --enable-atrig-erf$$ on the configure command line,
|
|
the adolc package supports all the c++11 math functions except
|
|
$code erfc$$, $code expm1$$, and $code log1p$$.
|
|
For the reason, we make using $code erfc$$ an error:
|
|
$srccode%cpp% */
|
|
namespace CppAD {
|
|
# define CPPAD_BASE_ADOLC_NO_SUPPORT(fun) \
|
|
inline adouble fun(const adouble& x) \
|
|
{ CPPAD_ASSERT_KNOWN( \
|
|
false, \
|
|
#fun ": adolc does not support this function" \
|
|
); \
|
|
return 0.0; \
|
|
}
|
|
CPPAD_BASE_ADOLC_NO_SUPPORT(erfc)
|
|
CPPAD_BASE_ADOLC_NO_SUPPORT(expm1)
|
|
CPPAD_BASE_ADOLC_NO_SUPPORT(log1p)
|
|
# undef CPPAD_BASE_ADOLC_NO_SUPPORT
|
|
}
|
|
/* %$$
|
|
|
|
$head sign$$
|
|
This $cref/required/base_require/$$ function is defined using the
|
|
$code codassign$$ function so that its $code adouble$$ operation sequence
|
|
does not depend on the value of $icode x$$.
|
|
$srccode%cpp% */
|
|
namespace CppAD {
|
|
inline adouble sign(const adouble& x)
|
|
{ adouble s_plus, s_minus, half(.5);
|
|
// set s_plus to sign(x)/2, except for case x == 0, s_plus = -.5
|
|
condassign(s_plus, +x, -half, +half);
|
|
// set s_minus to -sign(x)/2, except for case x == 0, s_minus = -.5
|
|
condassign(s_minus, -x, -half, +half);
|
|
// set s to sign(x)
|
|
return s_plus - s_minus;
|
|
}
|
|
}
|
|
/* %$$
|
|
|
|
$head abs$$
|
|
This $cref/required/base_require/$$ function uses the adolc $code fabs$$
|
|
function:
|
|
$srccode%cpp% */
|
|
namespace CppAD {
|
|
inline adouble abs(const adouble& x)
|
|
{ return fabs(x); }
|
|
}
|
|
/* %$$
|
|
|
|
$head pow$$
|
|
This $cref/required/base_require/$$ function
|
|
is defined by the Adolc package for the $code adouble$$ base case.
|
|
|
|
$head numeric_limits$$
|
|
The following defines the CppAD $cref numeric_limits$$
|
|
for the type $code adouble$$:
|
|
$srccode%cpp% */
|
|
namespace CppAD {
|
|
CPPAD_NUMERIC_LIMITS(double, adouble)
|
|
}
|
|
/* %$$
|
|
|
|
$head to_string$$
|
|
The following defines the CppAD $cref to_string$$ function
|
|
for the type $code adouble$$:
|
|
$srccode%cpp% */
|
|
namespace CppAD {
|
|
template <> struct to_string_struct<adouble>
|
|
{ std::string operator()(const adouble& x)
|
|
{ std::stringstream os;
|
|
int n_digits = 1 + std::numeric_limits<double>::digits10;
|
|
os << std::setprecision(n_digits);
|
|
os << x.value();
|
|
return os.str();
|
|
}
|
|
};
|
|
}
|
|
/* %$$
|
|
|
|
$head hash_code$$
|
|
It appears that an $code adouble$$ object can have fields
|
|
that are not initialized.
|
|
This results in a $code valgrind$$ error when these fields are used by the
|
|
$cref/default/base_hash/Default/$$ hashing function.
|
|
For this reason, the $code adouble$$ class overrides the default definition.
|
|
$srccode|cpp| */
|
|
namespace CppAD {
|
|
inline unsigned short hash_code(const adouble& x)
|
|
{ unsigned short code = 0;
|
|
double value = x.value();
|
|
if( value == 0.0 )
|
|
return code;
|
|
double log_x = std::log( fabs( value ) );
|
|
// assume log( std::numeric_limits<double>::max() ) is near 700
|
|
code = static_cast<unsigned short>(
|
|
(CPPAD_HASH_TABLE_SIZE / 700 + 1) * log_x
|
|
);
|
|
code = code % CPPAD_HASH_TABLE_SIZE;
|
|
return code;
|
|
}
|
|
}
|
|
/* |$$
|
|
Note that after the hash codes match, the
|
|
$cref/Identical/base_adolc.hpp/Identical/$$ function will be used
|
|
to make sure two values are the same and one can replace the other.
|
|
A more sophisticated implementation of the $code Identical$$ function
|
|
would detect which $code adouble$$ values depend on the
|
|
$code adouble$$ independent variables (and hence can change).
|
|
|
|
|
|
$end
|
|
*/
|
|
# endif
|