1

For a physics related calculation, I need to evaluate numerically a four dimensional integral that depends on several parameters that need to be varied; such that they can't be globally defined. I am using the Cuba package which provides the following function:

Cuhre(NDIM, NCOMP, Integrand , USERDATA, NVEC,EPSREL, EPSABS, VERBOSE | LAST,MINEVAL,
 MAXEVAL, KEY,STATEFILE, SPIN,&nregions, &neval, &fail, integral, error, prob);

The argument "Integrand" is a function that is expected to be defined in the following fashion:

int Integrand(const int *ndim, const cubareal xx[],const int *ncomp,
cubareal ff[], void *userdata) ;

My problem is that I would like to integrate a function that depends on continuous parameters that I would like to vary within the program. What I mean by this is that my integrand function depends on several extra parameters, say a,b,c.

I would then like to define something like the Integrand function locally, i.e. in a scope where the parameters a, b and c are fixed, so that it will have the correct form to be passed as an argument to the Cuhre function.

I then thought that I could perhaps put everything into a class such as:

 class integrate{
  private: // parameters

  public:
//constructor
int Integrand(...){
  // calculation involving the parameters 
}

  };

and define the function as a method for that class. This however didn't work because I can't pass non-static object methods to a function, and, making the method static would also force me to make the parameters static which means I would not be able to initialise them within the program.

Is there any workaround to this?

Jack
  • 247
  • 3
  • 10
  • 2
    Please describe what you're attempting to do _not_ in terms of ways that don't work. – Lightness Races in Orbit Apr 22 '16 at 13:45
  • If I undestood you well you could pass the pointer to the method: `int (integrate::*method)(...)` and the instance of the `integrate integr;` class to make it run on your passed instance as follows `(integr.*method)(...);` – W.F. Apr 22 '16 at 13:47
  • 1
    your definition of the function reminds me a bit of FORTRAN, c++ has no `integer` but its called `int`... – 463035818_is_not_an_ai Apr 22 '16 at 13:51
  • @tobi303: sorry indeed, that is not how the function is defined in C++, that is just what is asked in the manual, I'll modify with the actual code – Jack Apr 22 '16 at 13:54
  • 5
    The userdata parameter allows you to pass around a structure with your parameters. – Alexandre C. Apr 22 '16 at 13:57
  • @AlexandreC. Thanks for pointing that out to me, I had completely misunderstood the point of that variable. – Jack Apr 22 '16 at 14:09
  • The userdata argument will be passed around untouched by the integration routine. This is standard practice in C when accepting function pointers to allow for one such extra argument. – Alexandre C. Apr 22 '16 at 14:14

2 Answers2

1

If I understand this properly, if you don't want to use userdata, you may be able to use a closure:

// local parameter variables
T1 param1 = ...;
T2 param2 = ...;
T3 param3 = ...;

// capture params in a lambda expression
auto myIntegrand = [param1, param2, param3](const int *ndim, const cubareal xx[], const int *ncomp, cubareal ff[], void *userdata) {
    stuff(param1);
    stuff(param2);
    stuff(param3);
};

// can now pass around myIntegrand

This is just a shortcut of making a class which has those parameters as member variables, and passing around a pointer to the class instance's method.

Careful with whether you want to capture by value (may be expensive copying) or by reference (may become invalid & blow up your program).

Claudiu
  • 224,032
  • 165
  • 485
  • 680
1

[Edit: as you misunderstood the userdata pointer, as I see from the comments, let me show you how to use it:]

struct Parameters
{
    int a, b, c; // TODO: appropriate defaults
};

int integrand
(
    const int* ndim, const cubareal xx[],
    const int* ncomp, cubareal ff[],
    void* userdata
)
{
    Parameters* p = reinterpret_cast<Parameters*>(userdata);
    /* calculations using p->a, p->b, p->c */
}

int main(int argc, char* argv[])
{
    Parameters p;
    /* fill in parameters appropriately, e. g. parsing command line parameters */
    Cuhre(/* other parameters */, &integrand, &p, /* remaining parameters */);
}

My previous solution remains as an alternative...

.cpp file:

namespace
{
    int g_a = 0; // appropriate default values...
    int g_b = 0;
    int g_c = 0;
};

void setIntegrationParameters (int a, int b, int c)
{
    g_a = a;
    g_b = b;
    g_c = c;
}

int integrand
(
    const int* ndim, const cubareal xx[],
    const int* ncomp, cubareal ff[],
    void* userdata
)
{
    /* calculations using g_a, g_b, g_c */
}

.h file:

void setIntegrationParameters (int a, int b, int c);
int integrand
(
    const int* ndim, const cubareal xx[],
    const int* ncomp, cubareal ff[],
    void* userdata
);

And from within your main program, you can e. g. parse command line parameters to calculate a, b, c and set them as parameters for the integration via setIntegrationParameters.

(You could do exactly the same with C code, of course then using C-Casts and static global variables instead of anonymous namespace...)

Aconcagua
  • 24,880
  • 4
  • 34
  • 59