C++ обвивка за алгоритъм за намиране на корен на GSL с производно

И така, докато съм доста щастлив да намеря много отговори в Stack Overflow, реших, че е време да задам въпрос сам.
Опитвам се да използвам алгоритъм за намиране на корен с производни. В съответствие с GSL трябва да дефинирам функцията и нейната производна предварително. Но се чудя дали това може да стане по-елегантно с обвивка.

Преди известно време намерих много удобен шаблон (GSL C++ wrapper) което работи добре за една функция, напр. интегрирам и го използвам интензивно.

Сега се чудя дали този подход може да бъде разширен, за да осигури две функции за GSL, а именно самата функция и нейното производно.

Редактиране: Решение

template <typename F, typename G>
class gsl_root_deriv : public gsl_function_fdf
{
private:
    const F&    _f;
    const G&    _df;

    static double invoke_f(double x, void* params){
        return static_cast<gsl_root_deriv*>(params) -> _f(x);
    }

    static double invoke_df(double x, void* params){
        return static_cast<gsl_root_deriv*>(params) -> _df(x);
    }

    static void     invoke_fdf(double x, void* params, double* f, double* df){
        (*f)    = static_cast<gsl_root_deriv*>(params)  ->  _f(x);
        (*df)   = static_cast<gsl_root_deriv*>(params)  ->  _df(x);
    }

public:
    gsl_root_deriv(const F& f_init, const G& df_init)
    : _f(f_init), _df(df_init)
    {
        f               = &gsl_root_deriv::invoke_f;
        df          = &gsl_root_deriv::invoke_df;
        fdf         = &gsl_root_deriv::invoke_fdf;
        params  = this;
    }
};

И минимален пример, който прилича на примерът от GSL:

#include <iostream>
#include <gsl/gsl_errno.h>
#include <gsl/gsl_roots.h>
#include <memory>

#include "gsl_root_deriv.h"

int main()
{
auto f = [](double x) -> double { return 0.25 * x*x - 1.0; };
auto df = [](double x) -> double { return 0.5 * x; };

gsl_root_deriv<decltype(f),decltype(df)> Fp(f,df);

int status(0), iter(0), max_iter(100);

const gsl_root_fdfsolver_type* T( gsl_root_fdfsolver_newton);

std::unique_ptr<gsl_root_fdfsolver,void(*)(gsl_root_fdfsolver*)>
    s(gsl_root_fdfsolver_alloc(T),gsl_root_fdfsolver_free);

double x_0(0.0), x(5.0);

gsl_root_fdfsolver_set( s.get(), &Fp, x );

do
{
    iter++;
    std::cout << "Iteration " << iter << std::endl;
    status = gsl_root_fdfsolver_iterate( s.get() );
    x_0 = x;
    x = gsl_root_fdfsolver_root( s.get() );
    status = gsl_root_test_delta( x, x_0, 0.0, 1.0e-3 );
} while( status == GSL_CONTINUE && iter < max_iter );

std::cout << "Converged to root " << x << std::endl;

return 0;
}

Сърдечни поздрави,
-- Клаус


person Community    schedule 06.07.2014    source източник
comment
Ако ви хареса толкова много, можете да гласувате за този отговор :p   -  person Vivian Miranda    schedule 07.07.2014
comment
Нуждаете се от втори параметър на шаблона. Забравих за това!   -  person Vivian Miranda    schedule 07.07.2014
comment
Още нещо. Ако имате по-ефективен начин за изчисляване на fdf (f и df едновременно), можете да добавите трети аргумент в конструктора. Тук току-що показах основно изпълнение.   -  person Vivian Miranda    schedule 07.07.2014
comment
Честно казано, не схващам идеята за коментара ви. Освен това трябва да призная, че не разбирам причината, поради която е необходим втори аргумент на шаблона. Имам предвид, че и f, и df (от структурата gsl_function_fdf) са указател към функции, връщащи двойно, следователно те са от един и същи „тип“ или не? Така защо едно име F не е достатъчно? Не се колебайте да отговорите „време е да прочета книга“ ;) Поздрави --   -  person    schedule 07.07.2014
comment
GSL пита третата fdf функция, в случай че потребителят има оптимизиран начин за изчисляване на f и df едновременно. Ако имате по-добра реализация в този случай, може би си струва да добавите трета функция в конструктора.   -  person Vivian Miranda    schedule 07.07.2014
comment
Добър въпрос за C++ е да попитате @stackoverflow защо е необходим вторият параметър на шаблона. И аз се обърках.   -  person Vivian Miranda    schedule 07.07.2014


Отговори (1)


Ще трябва да направите някои модификации

Gsl fdf структура е следната

struct gsl_function_fdf_struct 
{
  double (* f) (double x, void * params);
  double (* df) (double x, void * params);
  void (* fdf) (double x, void * params, double * f, double * df);
  void * params;
};

typedef struct gsl_function_fdf_struct gsl_function_fdf ;

Ако сте разбрали какво всъщност прави обвивката, тогава ще видите, че обобщението е доста лесно

class gsl_function_fdf_pp : public gsl_function_fdf
{
   public:
   gsl_function_fdf_pp
   (
     std::function<double(double)> const& kf, 
     std::function<double(double)> const& kdf
   ) : _f(kf), _df(kdf)
   {
      f   = &gsl_function_fdf_pp::invoke;
      df  = &gsl_function_fdf_pp::invoke2;
      fdf = &gsl_function_fdf_pp::invoke3;
      params=this;
   }     
   private:
   std::function<double(double)> _f;
   std::function<double(double)> _df;

   static double invoke(double x, void *params) 
   {
     return static_cast<gsl_function_fdf_pp*>(params)->_f(x);
   }

   static double invoke2(double x, void *params) 
   {
     return static_cast<gsl_function_fdf_pp*>(params)->_df(x);
   }

   static void invoke3(double x, void * params, double* f, double* df)
   {
     (*f)  = static_cast<gsl_function_fdf_pp*>(params)->_f(x);
     (*df) = static_cast<gsl_function_fdf_pp*>(params)->_df(x);
   }
};

Версията на шаблона.

template< typename F, typename U >  class gsl_function_fdf_pp : public gsl_function_fdf 
{
  public:
  gsl_function_fdf_pp(const F& kf, const U& kdf) : _f(kf), _df(kdf)
  {
    f   = &gsl_function_fdf_pp::invoke;
    df  = &gsl_function_fdf_pp::invoke2;
    fdf = &gsl_function_fdf_pp::invoke3;
    params=this;
  }
  private:
  const F& _f;
  const U& _df;

  static double invoke(double x, void *params) 
  {
    return static_cast<gsl_function_fdf_pp*>(params)->_f(x);
  }

  static double invoke2(double x, void *params) 
  {
    return static_cast<gsl_function_fdf_pp*>(params)->_df(x);
  }

  static void invoke3(double x, void * params, double* f, double* df)
  {
    (*f)  = static_cast<gsl_function_fdf_pp*>(params)->_f(x);
    (*df) = static_cast<gsl_function_fdf_pp*>(params)->_df(x);
  }
}; 

EDIT2: След коригиране на 2 малки проблема, този пример работи

int main()
{
  auto f  = [](double x)->double{ return x*x; };
  auto df = [](double x)->double{ return 2.0 * x; };

  gsl_function_fdf_pp<decltype(f),decltype(df)> Fp(f,df);

  return 0;
}
person Vivian Miranda    schedule 07.07.2014
comment
Уважаеми Винисиус, много ви благодаря за отговора. Разрових се в директорията gsl и успешно намерих структурата gsl_function_fdf в math.h. Предполагам, че разбрах идеята на класа (шаблона) досега: Вие извличате наследство от структурата gsl_function_fdf и го модифицирате според C++ интерфейса. Поради наследяване обектът gsl_function_fdf_pp is е обект gsl_function_fdf и gsl може да се справи с него. Единственото нещо, което не разбирам напълно: Защо хвърляте показалеца на params? Бихте ли разяснили това? С уважение, Клаус - person ; 07.07.2014
comment
Тъй като статичните членски функции не могат да сочат към нестатични членски указатели (като например указателя this!). Така че ние използваме params void указателя, за да посочим текущото моментно време на класа! - person Vivian Miranda; 07.07.2014
comment
Благодаря. Но шаблонът не работи, моля, вижте минималния пример в редакцията на първия ми пост. Поздрави - person ; 07.07.2014
comment
Имаше правописна грешка в името на конструктора и забравих да добавя втори параметър на шаблона - person Vivian Miranda; 07.07.2014
comment
Добре, мисля, че добавихте запетая твърде много. ;) За разбирането - person ; 07.07.2014