Function Objects

O2scl

Lambda functions and std::mem_fn

Functions are passed to numerical routines using template-based function classes, sometimes called “functors”. O2scl classes which accept functions as parameters generally default to types built upon std::function. If the user would like to use Boost function objects instead, these may also be used, simply by specifying the Boost function type in the template parameter.

Some template aliases are defined to save typing of the function types, e.g.

  • funct : One function of one variable (used in one-dimensional solver and minimizer classes, derivative classes, integration classes, etc.)

  • funct_ld : One function of one variable using long double

  • multi_funct : One function of several variables (used in minimizer and integration classes)

  • mm_funct: \(n\) functions of \(n\) variables (used in solver classes)

  • grad_funct: gradient function for minimizer classes

  • ode_funct: \(n\) derivatives as a function of \(n\) function values and the value of the independent variable

  • ode_jac_funct: Jacobian function for ODE classes

  • ode_it_funct: Function to specify ODEs for iterative solution

  • jac_funct : Jacobian function for solver and fitting classes

  • fit_funct: Fit function

  • ool_hfunct: Hessian matrix function for constrained minimization

First function object example

The example below demonstrates how C++11 function objects can be used with the root_brent_gsl solver.

/* Example: ex_lambda.cpp
   -------------------------------------------------------------------
   Demonstrate how to use standard library and lambda function objects
   with O2scl.
 
*/
#include <iostream>
#include <functional>
#include <o2scl/root_brent_gsl.h>
#include <o2scl/test_mgr.h>

using namespace std;
using namespace o2scl;

// A global function
double gfn(double x) {
  return sin(x)-0.1;
}

// A global function with a parameter
double gfn_param(double x, double a) {
  return sin(x)-a;
}

class a_class {
public:
  // A member function
  double mfn(double x) {
    return sin(x)-0.1;
  }
  // A member function with a parameter
  double mfn_param(double x, double a) {
    return sin(x)-a;
  }
  // A member function with a const and non-const reference parameter
  double mfn_param_ref(double x, const double &a, double &b) {
    b=x*x;
    return sin(x)-a;
  }
};

int main(void) {
  test_mgr t;
  t.set_output_level(2);

  cout.setf(ios::scientific);

  // The O2scl solver. Note that we use the same solver for 
  // all the examples below.
  root_brent_gsl<std::function<double(double)> > grb;

  // For the initial bracket 
  double a, b;

  // With a global function
  {
    a=-0.9, b=0.9;
    std::function<double(double)> f=gfn;
    grb.solve_bkt(a,b,f);
    t.test_rel(a,asin(0.1),1.0e-12,"Global function");
  }

  // O2scl defines 'funct' as std::function<double(double)>, so this
  // shorter notation also works. 
  {
    a=-0.9, b=0.9;
    funct f=gfn;
    grb.solve_bkt(a,b,f);
    t.test_rel(a,asin(0.1),1.0e-12,"Global function (take 2)");
  }

  // With a global function
  {
    a=-0.9, b=0.9;
    funct f=std::bind(gfn_param,std::placeholders::_1,0.1);
    grb.solve_bkt(a,b,f);
    t.test_rel(a,asin(0.1),1.0e-12,"Global function with parameter");
  }

  // With a member function
  {
    a=-0.9, b=0.9;
    a_class ac;
    funct f=std::bind(std::mem_fn<double(double)>(&a_class::mfn),
		      ac,std::placeholders::_1);
    grb.solve_bkt(a,b,f);
    t.test_rel(a,asin(0.1),1.0e-12,"Member function");
  }

  // With a member function which has a fixed parameter
  {
    a=-0.9, b=0.9;
    a_class ac;
    funct f=std::bind(std::mem_fn<double(double,double)>
		      (&a_class::mfn_param),
		      ac,std::placeholders::_1,0.1);
    grb.solve_bkt(a,b,f);
    t.test_rel(a,asin(0.1),1.0e-12,"Member function with parameter");
  }

  // Inline specification of the function
  {
    a=-0.9, b=0.9;
    funct f=[](double x) -> double { double z=sin(x)-0.1; return z; };
    grb.solve_bkt(a,b,f);
    t.test_rel(a,asin(0.1),1.0e-12,"Inline 1");
  }

  // A bit of a shorter notation 
  {
    a=-0.9, b=0.9;
    funct f=[](double x){ return sin(x)-0.1; };
    grb.solve_bkt(a,b,f);
    t.test_rel(a,asin(0.1),1.0e-12,"Inline 2");
    // Show copy construction works
    a=-0.9, b=0.9;
    funct f2=f;
    grb.solve_bkt(a,b,f2);
    t.test_rel(a,asin(0.1),1.0e-12,"Inline 3");
  }

  // With a member function and reference parameters. Note that
  // we use std::cref for the const reference and std::ref
  // for the non-const reference
  {
    a=-0.9, b=0.9;
    a_class ac;
    double param1=0.1, param2;
    funct f=std::bind(std::mem_fn<double(double,const double &,double &)>
		      (&a_class::mfn_param_ref),
		      ac,std::placeholders::_1,std::cref(param1),
		      std::ref(param2));
    grb.solve_bkt(a,b,f);
    t.test_rel(a,asin(0.1),1.0e-12,"Member function with references");
    // The last function call isn't always at the final root!
    t.test_rel(param2,0.01,1.0e-2,"Reference 2");
  }

  t.report();
  return 0;
}

General comments about function objects

The C++ standard library functors employ copy construction at various types, so one must be careful about the types involved in creating the functor. Generally, all classes should have constructors and structs should be avoided because they can cause difficulties with default copy construction.

There is a small overhead associated with the indirection: a “user class” accesses the function class which then calls function which was specified in the constructor of the function class. In many problems, the overhead associated with the indirection is small. Some of this overhead can always be avoided by inheriting directly from the function class and thus the user class will make a direct virtual function call. To eliminate the overhead entirely, one can specify a new type for the template parameter in the user class.

Second function object example

This example shows how to provide functions to O2scl classes by solving the equation

\[\left\{ 1+\frac{1}{p_2} \sin \left[ 50 \left( x-p_1 \right) \right] \right\} \tan^{-1} \left[ 4 \left( x-p_1 \right) \right] = 0\]

Where \(p_1 = 0.01\) and \(p_2 = 1.1\). The parameter \(p_1\) is stored as member data for the class, and the parameter \(p_2\) is an argument to the member function.

The image below shows how the solver progresses to the solution of the example function.

alt text
/* Example: ex_fptr.cpp
   -------------------------------------------------------------------
   This gives an example of the how member functions and external
   parameters are supplied to numerical routines. In this case, a
   member function with two parameters is passed to the root_brent_gsl
   class, which solves the equation. One of the parameters is member
   data, and the other is specified using the extra parameter argument
   to the function.
*/

#include <fstream>
#include <o2scl/funct.h>
#include <o2scl/root_brent_gsl.h>
#include <o2scl/test_mgr.h>

using namespace std;
using namespace o2scl;

class my_class {

private:

  double parameter;

public:
  
  void set_parameter() { parameter=0.01; }
  
  // A function demonstrating the different ways of implementing
  // function parameters
  double function_to_solve(double x, double &p) {
    return atan((x-parameter)*4)*(1.0+sin((x-parameter)*50.0)/p);
  }
  
};

// This header contains the code for write_file()
#include "ex_fptr.h"

int main(void) {
  
  cout.setf(ios::scientific);
  
  test_mgr t;
  // Only print something out if one of the tests fails
  t.set_output_level(1);

  // The solver, specifying the type of the parameter (double)
  // and the function type (funct<double>)
  root_brent_gsl<> solver;

  my_class c;
  c.set_parameter();
  
  double p=1.1;

  // This is the code that allows specification of class member
  // functions as functions to solve. This approach avoids the use of
  // static variables and functions and multiple inheritance at the
  // expense of a little overhead. We need to provide the address of
  // an instantiated object and the address of the member function.
  funct function=std::bind(std::mem_fn<double(double,double &)>
			   (&my_class::function_to_solve),
			   &c,std::placeholders::_1,std::ref(p));
  
  double x1=-1;
  double x2=2;
  
  // The value verbose=1 prints out iteration information
  // and verbose=2 requires a keypress between iterations.
  solver.verbose=1;
  solver.solve_bkt(x1,x2,function);

  // This is actually a somewhat difficult function to solve because
  // of the sinusoidal behavior.
  cout << "Solution: " << x1 
       << " Function value: " << c.function_to_solve(x1,p) << endl;

  // Write the function being solved to a file (see source code 
  // in examples directory for details)
  write_file(x1);

  // Obtain and summarize test results
  t.test_abs(c.function_to_solve(x1,p),0.0,1.0e-10,"ex_fptr");
  t.report();

  return 0;
}

Function typedefs

typedef std::function<double(double)> o2scl::funct

One-dimensional function typedef in src/base/funct.h.

typedef std::function<long double(long double)> o2scl::funct_ld

One-dimensional function typedef in src/base/funct.h.

typedef std::function<double(size_t, const boost::numeric::ublas::vector<double>&)> o2scl::multi_funct

Multi-dimensional function typedef in src/base/multi_funct.h.

typedef std::function<int(size_t, const boost::numeric::ublas::vector<double>&, boost::numeric::ublas::vector<double>&)> o2scl::mm_funct

Array of multi-dimensional functions typedef in src/base/mm_funct.h.

typedef std::function<int(size_t, boost::numeric::ublas::vector<double>&, boost::numeric::ublas::vector<double>&)> o2scl::grad_funct

Array of multi-dimensional functions typedef in src/min/mmin.h.

typedef std::function<int(double, size_t, const boost::numeric::ublas::vector<double>&, boost::numeric::ublas::vector<double>&)> o2scl::ode_funct

Ordinary differential equation function in src/ode/ode_funct.h.

typedef std::function<int(double, size_t, const boost::numeric::ublas::vector<double>&, boost::numeric::ublas::matrix<double>&, boost::numeric::ublas::vector<double>&)> o2scl::ode_jac_funct

Functor for ODE Jacobians in src/ode/ode_jac_funct.h.

typedef std::function<double(size_t, double, boost::numeric::ublas::matrix_row<boost::numeric::ublas::matrix<double>>&)> o2scl::ode_it_funct

Function for iterative solving of ODEs.

typedef std::function<int(size_t, boost::numeric::ublas::vector<double>&, size_t, boost::numeric::ublas::vector<double>&, boost::numeric::ublas::matrix<double>&)> o2scl::jac_funct

Jacobian function (not necessarily square) in src/root/jacobian.h.

typedef std::function<double(size_t, const boost::numeric::ublas::vector<double>&, double)> o2scl::fit_funct

Array of multi-dimensional functions typedef (C++11 version) in src/fit/fit_base.h.

typedef std::function<int(size_t, const boost::numeric::ublas::vector<double>&, const boost::numeric::ublas::vector<double>&, boost::numeric::ublas::vector<double>&)> o2scl::ool_hfunct

Hessian product function for o2scl::mmin_constr.