//===========================================================================
/*!
*
*
* \brief AbstractObjectiveFunction
*
*
* \author T.Voss, T. Glasmachers, O.Krause
* \date 2010-2011
*
*
* \par Copyright 1995-2017 Shark Development Team
*
*
* This file is part of Shark.
*
*
* Shark is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Shark is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Shark. If not, see .
*
*/
//===========================================================================
#ifndef SHARK_OBJECTIVEFUNCTIONS_ABSTRACTOBJECTIVEFUNCTION_H
#define SHARK_OBJECTIVEFUNCTIONS_ABSTRACTOBJECTIVEFUNCTION_H
#include
#include
#include
#include
#include
namespace shark {
/// \brief Super class of all objective functions for optimization and learning.
/// \par
/// The AbstractObjectiveFunction template class is the most general
/// interface for a function to be minimized by an
/// optimizer. It subsumes many more specialized classes,
/// ranging from classical test problems in evolutionary algorithms to
/// data-dependent objective functions in supervised learning. This
/// interface allows all general purpose optimization procedures to be
/// used as model training algorithms in a learning task, with
/// applications ranging from training of neural networks to direct
/// policy search in reinforcement learning.
///
/// AbstractObjectiveFunction offers a rich interface to support
/// different types of optimizers. Since not every objective function meets
/// every requirement, a flag system exists which tells the optimizer
/// which features are available. These are:
/// HAS_VALUE: The function can be evaluated. If not set, evalDerivative returns a meaningless
/// value (for example std::numeric_limits::quiet_nan());
/// HAS_FIRST_DERIVATIVE: evalDerivative can be called for the FirstOrderDerivative.
/// The Derivative is defined and as exact as possible;
/// HAS_SECOND_DERIVATIVE: evalDerivative can be called for the second derivative.
/// IS_CONSTRAINED_FEATURE: The function has constraints and isFeasible might return false;
/// CAN_PROPOSE_STARTING_POINT: the function can return a possibly randomized starting point;
/// CAN_PROVIDE_CLOSEST_FEASIBLE: if the function is constrained, closest feasible can be
/// called to construct a feasible point.
///
/// In the single objective case, the shark convention is to return a double value, while in
/// Multi objective optimization a RealVector is returned with an entry for every objective.
/// Moreoever, derivatives in the single objective case are RealVectors, while they are
/// RealMatrix in the multi-objective case (i.e. the jacobian of the function).
///
/// Calling the derivatives, proposeStartingPoint or closestFeasible when the flags are not set
/// will throw an exception.
/// The features can be queried using the method features() as in
/// if(!(f.features()&Function::HAS_VALUE))
/// \tparam PointType The search space the function is defined upon.
/// \tparam ResultT The objective space the function is defined upon.
template
class AbstractObjectiveFunction : public INameable{
public:
typedef PointType SearchPointType;
typedef ResultT ResultType;
//if the result type is not an arithmetic type, we assume it is a vector-type->multi objective optimization
typedef typename boost::mpl::if_<
std::is_arithmetic,
SearchPointType,
RealMatrix
>::type FirstOrderDerivative;
struct SecondOrderDerivative {
FirstOrderDerivative gradient;
RealMatrix hessian;
};
/// \brief List of features that are supported by an implementation.
enum Feature {
HAS_VALUE = 1, ///< The function can be evaluated and evalDerivative returns a meaningless value (for example std::numeric_limits::quiet_nan()).
HAS_FIRST_DERIVATIVE = 2, ///< The method evalDerivative is implemented for the first derivative and returns a sensible value.
HAS_SECOND_DERIVATIVE = 4, ///< The method evalDerivative is implemented for the second derivative and returns a sensible value.
CAN_PROPOSE_STARTING_POINT = 8, ///< The function can propose a sensible starting point to search algorithms.
IS_CONSTRAINED_FEATURE = 16, ///< The objective function is constrained.
HAS_CONSTRAINT_HANDLER = 32, ///< The constraints are governed by a constraint handler which can be queried by getConstraintHandler()
CAN_PROVIDE_CLOSEST_FEASIBLE = 64, ///< If the function is constrained, the method closestFeasible is implemented and returns a "repaired" solution.
IS_THREAD_SAFE = 128, ///< can eval or evalDerivative be called in parallel?
IS_NOISY = 256 ///< The function value is perturbed by some kind of noise
};
/// This statement declares the member m_features. See Core/Flags.h for details.
SHARK_FEATURE_INTERFACE;
/// \brief returns whether this function can calculate it's function value
bool hasValue()const{
return m_features & HAS_VALUE;
}
/// \brief returns whether this function can calculate the first derivative
bool hasFirstDerivative()const{
return m_features & HAS_FIRST_DERIVATIVE;
}
/// \brief returns whether this function can calculate the second derivative
bool hasSecondDerivative()const{
return m_features & HAS_SECOND_DERIVATIVE;
}
/// \brief returns whether this function can propose a starting point.
bool canProposeStartingPoint()const{
return m_features & CAN_PROPOSE_STARTING_POINT;
}
/// \brief returns whether this function can return
bool isConstrained()const{
return m_features & IS_CONSTRAINED_FEATURE;
}
/// \brief returns whether this function can return
bool hasConstraintHandler()const{
return m_features & HAS_CONSTRAINT_HANDLER;
}
/// \brief Returns whether this function can calculate thee closest feasible to an infeasible point.
bool canProvideClosestFeasible()const{
return m_features & CAN_PROVIDE_CLOSEST_FEASIBLE;
}
/// \brief Returns true, when the function can be usd in parallel threads.
bool isThreadSafe()const{
return m_features & IS_THREAD_SAFE;
}
/// \brief Returns true, when the function can be usd in parallel threads.
bool isNoisy()const{
return m_features & IS_NOISY;
}
/// \brief Default ctor.
AbstractObjectiveFunction():m_evaluationCounter(0), mep_rng(&random::globalRng){
m_features |=HAS_VALUE;
}
/// \brief Virtual destructor
virtual ~AbstractObjectiveFunction() {}
virtual void init() {
m_evaluationCounter=0;
}
///\brief Sets the Rng used by the objective function.
///
/// Objective functions need random numbers for different tasks,
/// e.g. to provide a first starting point or for example
/// mini batch learning where batches are chosen randomly.
/// By default, shark::random::globalRng is used.
/// In a multi-threaded environment this might not be safe as
/// the Rng is not thread safe. In this case, every thread should use its
/// own Rng.
void setRng(random::rng_type* rng){
mep_rng = rng;
}
/// \brief Accesses the number of variables
virtual std::size_t numberOfVariables() const=0;
virtual bool hasScalableDimensionality()const{
return false;
}
/// \brief Adjusts the number of variables if the function is scalable.
/// \param [in] numberOfVariables The new dimension.
virtual void setNumberOfVariables( std::size_t numberOfVariables ){
throw SHARKEXCEPTION("dimensionality of function is not scalable");
}
virtual std::size_t numberOfObjectives() const{
return 1;
}
virtual bool hasScalableObjectives()const{
return false;
}
/// \brief Adjusts the number of objectives if the function is scalable.
/// \param numberOfObjectives The new number of objectives to optimize for.
virtual void setNumberOfObjectives( std::size_t numberOfObjectives ){
throw SHARKEXCEPTION("dimensionality of function is not scaleable");
}
/// \brief Accesses the evaluation counter of the function.
std::size_t evaluationCounter() const {
return m_evaluationCounter;
}
/// \brief Returns the constraint handler of the function if it has one.
///
/// If the function does not offer a constraint handler, an exception is thrown.
AbstractConstraintHandler const& getConstraintHandler()const{
SHARK_RUNTIME_CHECK(m_constraintHandler, "Objective Function does not have an constraint handler!");
return *m_constraintHandler;
}
/// \brief Tests whether a point in SearchSpace is feasible, e.g., whether the constraints are fulfilled.
/// \param [in] input The point to be tested for feasibility.
/// \return true if the point is feasible, false otherwise.
virtual bool isFeasible( const SearchPointType & input) const {
if(hasConstraintHandler()) return getConstraintHandler().isFeasible(input);
SHARK_RUNTIME_CHECK(!isConstrained(), "Not overwritten, even though function is constrained");
return true;
}
/// \brief If supported, the supplied point is repaired such that it satisfies all of the function's constraints.
///
/// \param [in,out] input The point to be repaired.
///
/// \throws FeatureNotAvailableException in the default implementation.
virtual void closestFeasible( SearchPointType & input ) const {
if(!isConstrained()) return;
if(hasConstraintHandler()) return getConstraintHandler().closestFeasible(input);
SHARK_FEATURE_EXCEPTION(CAN_PROVIDE_CLOSEST_FEASIBLE);
}
/// \brief Proposes a starting point in the feasible search space of the function.
///
/// \return The generated starting point.
/// \throws FeatureNotAvailableException in the default implementation
/// and if a function does not support this feature.
virtual SearchPointType proposeStartingPoint()const {
if(hasConstraintHandler()&& getConstraintHandler().canGenerateRandomPoint()){
SearchPointType startingPoint;
getConstraintHandler().generateRandomPoint(*mep_rng, startingPoint);
return startingPoint;
}
else{
SHARK_FEATURE_EXCEPTION(CAN_PROPOSE_STARTING_POINT);
}
}
/// \brief Evaluates the objective function for the supplied argument.
/// \param [in] input The argument for which the function shall be evaluated.
/// \return The result of evaluating the function for the supplied argument.
/// \throws FeatureNotAvailableException in the default implementation
/// and if a function does not support this feature.
virtual ResultType eval( SearchPointType const& input )const {
SHARK_FEATURE_EXCEPTION(HAS_VALUE);
}
/// \brief Evaluates the function. Useful together with STL-Algorithms like std::transform.
ResultType operator()( SearchPointType const& input ) const {
return eval(input);
}
/// \brief Evaluates the objective function and calculates its gradient.
/// \param [in] input The argument to eval the function for.
/// \param [out] derivative The derivate is placed here.
/// \return The result of evaluating the function for the supplied argument.
/// \throws FeatureNotAvailableException in the default implementation
/// and if a function does not support this feature.
virtual ResultType evalDerivative( SearchPointType const& input, FirstOrderDerivative & derivative )const {
SHARK_FEATURE_EXCEPTION(HAS_FIRST_DERIVATIVE);
}
/// \brief Evaluates the objective function and calculates its gradient.
/// \param [in] input The argument to eval the function for.
/// \param [out] derivative The derivate and the Hessian are placed here.
/// \return The result of evaluating the function for the supplied argument.
/// \throws FeatureNotAvailableException in the default implementation
/// and if a function does not support this feature.
virtual ResultType evalDerivative( SearchPointType const& input, SecondOrderDerivative & derivative )const {
SHARK_FEATURE_EXCEPTION(HAS_SECOND_DERIVATIVE);
}
protected:
mutable std::size_t m_evaluationCounter; ///< Evaluation counter, default value: 0.
AbstractConstraintHandler const* m_constraintHandler;
random::rng_type* mep_rng;
/// \brief helper function which is called to announce the presence of an constraint handler.
///
/// This function quries the propabilities of the handler and sts up the flags accordingly
void announceConstraintHandler(AbstractConstraintHandler const* handler){
SHARK_RUNTIME_CHECK(handler, "[AbstractObjectiveFunction::AnnounceConstraintHandler] Handler is not allowed to be NULL");
m_constraintHandler = handler;
m_features |= IS_CONSTRAINED_FEATURE;
m_features |= HAS_CONSTRAINT_HANDLER;
if(handler->canGenerateRandomPoint())
m_features |=CAN_PROPOSE_STARTING_POINT;
if(handler->canProvideClosestFeasible())
m_features |= CAN_PROVIDE_CLOSEST_FEASIBLE;
}
};
typedef AbstractObjectiveFunction< RealVector, double > SingleObjectiveFunction;
typedef AbstractObjectiveFunction< RealVector, RealVector > MultiObjectiveFunction;
}
#endif