How to use "integrate_1d"

Hi,

I would like to use the recently added “integrate_1d” function in stan (I am using Rstan and have only limited background in using C++). However, I have some trouble in understanding how to use this function. I would have expected something like

integrate_1d(f,a,b),

wherein a and b denote the limits of integration and wherein f denotes a
previously defined function. However, from the manual I gather the following:
"The integrate_1dfunction requires

  • its first argument to refer to a function wth signature(real, real, real[],real[], int[]) : real,
  • the remaining six arguments are assignable to types real,real,real[],real[],andint[], and
  • the fourth and fifth arguments must be expressions not containing any variablesnot originating in the data or transformed data blocks."

I do not understand this specification. What is the meaning of the arguments in the signature(real, real, real[],real[], int[])?

Perhaps a small toy example would settle my questions.
Thanks for your help!

See here: 9.4 Argument Types and Qualifiers | Stan Reference Manual

Thank you very much. But as far as I noticed, the manual speaks about matrices/arrays/int-variables. What I have difficulties with is not the formal type specification, but to know how the arguments in the signature relate to the concrete problem of implementing the integral of a function, e.g… which argument(s) represent the bounds of integration? Where and how do I reference the function which has to be integrated?

Ah, I see. A few minutes ago, I tried to find that information myself, but had no luck. It’s not even in the Stan Functions reference, which is where it should be.

It is given more detail in the Math library API mc-stan.org/math I just wasn’t sure if the information there necessarily helps @pasjor.

Unfortunately, the API documentation doesn’t help much. The catch is that the mapping of C++ arguments to Stan arguments is not one-to-one. Stan functions obviously do not have arguments of the std::ostream& type, and since the last argument in the Stan function integrate_1d is of type int[], it obviously does not correspond to the last argument in the C++ function integrate_1d, which is of type double.

1 Like

My thoughts exactly.

integrate_1d is a functional that behaves in the same was as integrate_ode and algebra_solver with the target functor defined in the functions block. Consequently you can follow the pattern for ordinary differential equations and algebraic systems that are well documented in the manual.

The basic structure for the algebraic solver, for example, is given by

functions {
  vector objective_function(vector y, vector theta, real[] x_r, int[] x_i) {
    // compute objective function and return output values
  }
}

transformed data {
  vector[1] y_guess;
  vector[1] theta;
  vector[1] y;
  real x_r[0];
  int x_i[0];

  y = algebra_solver(objective_function, y_guess, theta, x_r, x_i);
}

The biggest change you’ll have to make is the exact signature of the function defined in the functions block to match that required by the integrate_1d functional.

1 Like

Here’s the function signature of integrate_ode_rk45:

real[ , ] integrate_ode_rk45(function ode, real[] initial_state, real initial_time, real[] times, real[] theta, real[] x_r, int[] x_i)

Here’s the apparent function signature of integrate_1d:

real integrate_1d(function foo, real, real, real[], real[], int[])

Given how different these function signatures are, it is clear that integrate_1d does not behave like integrate_ode except in the broadest strokes.

Thank you all very much!

I will examine the integrate_ode function closer. This could solve my problem. However, as I also gather from some of the objections, the signature of the integrate_1d function is somewhat different. It was also helpful to look at the link with the underlying C++ code and documentation. However, I understand that there is still a difference from the C++ signature to the stan-signature. I hope that I can cope with the necessary translation from one language into the other.

I think example code of a small application of the integrate_1d function (e.g. computing the integral of 2*x+1 over the interval from -1 to +1) would be most helpful in gaining a quick understanding of the function.

For what it’s worth, in my personal opinion, if a function is poorly documented, that may be a sign that it isn’t ready for prime time. It certainly means that if something goes wrong when using the function, you will have more difficulty debugging.

2 Likes

@jjramsey thanks, you’re right that integrate_1d() isn’t documented properly. I tried to find it in the functions reference and I couldn’t either. I’m not sure how it got into the language without documentation. That shouldn’t happen.

I just opened an issue on GitHub:

2 Likes

just added in missing documentation that got lost during conversion from latex to bookdown - here’s what yinz need to know:

3 Likes

@mitzimorris Thanks for tracking that down

also added back in this:

3 Likes

Many thanks - this is very helpful!

Trying to compile this example (in pystan) leads to an error:

  real normal_density(real x,          // Function argument
                    real xc,         // Complement of function argument
                                     //  on the domain (defined later)
                    real[] theta,    // parameters
                    real[] x_r,      // data (real)
                    int[] x_i) {     // data (integer)
  real mu = theta[1];
  real sigma = theta[2];

  return 1 / (sqrt(2 * pi()) * sigma) * exp(-0.5 * ((x - mu) / sigma)^2);
}
  
  
}

data {
  int N;
  real y[N];
}

transformed data {
  real x_r[0];
  int x_i[0];
}

parameters {
  real mu;
  real<lower = 0.0> sigma;
  real left_limit;
}

model {
  mu ~ normal(0, 1);
  sigma ~ normal(0, 1);
  left_limit ~ normal(0, 1);
  target += normal_lpdf(y | mu, sigma);
  target += log(integrate_1d(normal_density,
                             left_limit,
                             positive_infinity(),
                             { mu, sigma }, x_r, x_i));
}```

The error:

ValueError: Failed to parse Stan model 'anon_model_8185db55041ace7ae18bc15d93d6605e'. Error message:
SYNTAX ERROR, MESSAGE(S) FROM PARSER:
 error in 'example_int1d.stan' at line 41, column 52
  -------------------------------------------------
    39:                              left_limit,
    40:                              positive_infinity(),
    41:                              { mu, sigma }, x_r, x_i));
                                                           ^
    42: }
  -------------------------------------------------

PARSER EXPECTED: ","

Thoughts?

call to integrate_1d is missing relative tolerance argument - this works:

target += log(integrate_1d(normal_density,
                             left_limit,
                             right_limit,
                             theta, x_r, x_i, 0.01));

I’m looking into the discrepancy between the documentation, which indicates rel_tol is optional, and above behavoir, which show that it isn’t.

3 Likes