2013-05-30 4 views
2

J'ai un algorithme d'optimisation continue qui prend une fonction Oracle comme paramètre de modèle. La classe d'optimisation spécifique est définie comme:Fonction globale et pointeur de fonction de membre non statique

template<class Space, class Solution, class Oracle> 
class ConjugateGradient : public ContinuousOptimizerInterface<Space, Solution, Oracle> { 
public: 

    // Returns the optimal solution found for a given search space 
    virtual const Solution& search(const Space& space, Oracle f); 
}; 

et dans le cadre de la mise en œuvre de la recherche J'ai appels à la fonction oracle:

template<class Space, class Solution, class Oracle> 
inline const Solution& ConjugateGradient<Space, Solution, Oracle>::search(const Space& space, Oracle f) { 
    // ... 
    // get function value and gradient at X 
    double fx; 
    Solution dfx; 
    tie(fx, dfx) = f(X); 
    // .. 
} 

Un exemple simple en utilisant une fonction quadratique globale:

typedef tuple<double, VectorXd> (*oracle_f)(const VectorXd&); 
static tuple<double, VectorXd> f(const VectorXd& X) { 
    double f = pow((4.0-X(0)), 2) + 10.0; 
    VectorXd df = 2.0*X - VectorXd::Ones(X.rows())*8.0; 
    return make_tuple(f, df); 
} 

// ... 
ConjugateGradient<TestSpace, VectorXd, oracle_f> optimizer; 
VectorXd optimal = optimizer.search(TestSpace(), f); 

Cela fonctionne bien, mais maintenant je dois être en mesure de passer une fonction membre non statique d'une classe en tant que fonction Oracle à l'algorithme ConjugateGradient. Comment dois-je modifier la déclaration de modèle ConjugateGradient et l'implémentation de la fonction oracle pour qu'elle soit une fonction globale ou une fonction de membre non statique? Une approche alternative serait de créer une fonction d'enveloppement globale et d'utiliser varargs pour passer des arguments à la fonction d'encapsulation, mais c'est moche et pas de type sécurisé.

MISE À JOUR: Cet exemple utilise l'idée de liaison de la réponse ci-dessous mais en utilisant boost :: bind au lieu de std :: bind car je ne suis pas sur C++ 11 et std :: bind n'est disponible que pour C++ 11.

#include <Eigen/Dense> 
#include <boost/tuple/tuple.hpp> 
#include <boost/function.hpp> 
#include <boost/bind.hpp> 

using namespace Eigen; 
using namespace boost; 

// the class is parameterized with the appropriate minimizer strategy 
enum Minimizer { kNormalEquations, kConjugateGradient }; 

template <Minimizer M = kNormalEquations> 
class SomeANN : AnnInterface { 
private: 
    // ... 
public: 
    // define the oracle function type and member function 
    typedef tuple<double, VectorXd> (SomeANN::*oracle_f)(const VectorXd&); 
    tuple<double, VectorXd> f(const VectorXd& theta); 
}; 

template <> 
inline tuple<double, VectorXd> SomeANN<kConjugateGradient>::f(const VectorXd& theta) { 
    double f = 0.0; 
    VectorXd df; 
    return make_tuple(f, df); 
} 

// ridge solver using conjugate gradient 
template <> 
inline void SomeANN<kConjugateGradient>::ridge_solve(const VectorXd& Y) { 
    ConjugateGradient<BeginSpace, VectorXd, SomeANN::oracle_f> optimizer; 
    // ... 
    optimizer.search(BeginSpace(Y.rows()), boost::bind(&SomeANN::f, this, _1)); 
} 

puis-je obtenir l'erreur:

some_ann.h:163:84: error: no matching function for call to 'ConjugateGradient<BeginSpace, Eigen::Matrix<double, -0x00000000000000001, 1>, boost::tuples::tuple<double, Eigen::Matrix<double, -0x00000000000000001, 1> > (SomeANN<(Minimizer)1u>::*)(const Eigen::Matrix<double, -0x00000000000000001, 1>&)>::search(BeginSpace, boost::_bi::bind_t<boost::tuples::tuple<double, Eigen::Matrix<double, -0x00000000000000001, 1> >, boost::_mfi::mf1<boost::tuples::tuple<double, Eigen::Matrix<double, -0x00000000000000001, 1> >, SomeANN<(Minimizer)1u>, const Eigen::Matrix<double, -0x00000000000000001, 1>&>, boost::_bi::list2<boost::_bi::value<SomeANN<(Minimizer)1u>*>, boost::arg<1> > >)' 
conjugate_gradient.h:67:2: error: must use '.*' or '->*' to call pointer-to-member function in 'f (...)', e.g. '(... ->* f) (...)' 
conjugate_gradient.h:84:3: error: must use '.*' or '->*' to call pointer-to-member function in 'f (...)', e.g. '(... ->* f) (...)' 
conjugate_gradient.h:111:5: error: must use '.*' or '->*' to call pointer-to-member function in 'f (...)', e.g. '(... ->* f) (...)' 
conjugate_gradient.h:153:4: error: must use '.*' or '->*' to call pointer-to-member function in 'f (...)', e.g. '(... ->* f) (...)' 
make[2]: *** [CMakeFiles/some_ann_library.dir/main/cpp/some_ann.cc.o] Error 1 
make[1]: *** [CMakeFiles/some_ann_library.dir/all] Error 2 
make: *** [all] Error 2 

Répondre

3

Ne pas modifier la recherche, passez une expression de liaison:

auto& solution = cg.search(space, std::bind(
    &MyType::member_function, &myInstance, std::placeholders::_1)); 
+0

bind fonctionnera très bien – count0

+0

note d'un côté est que pour cette Pour travailler j'ai besoin de C11 et je ne le fais pas actuellement. Je suis sur GNU MacPorts gcc46 4.6.3_9 –

+0

4.6 -std = C++ 0x mode n'a pas std :: bind? –

Questions connexes