3.1 Vectorization of real-valued functions

Although listed in this chapter, many of Stan’s built-in functions are vectorized so that they may be applied to any argument type. The vectorized form of these functions is not any faster than writing an explicit loop that iterates over the elements applying the function—it’s just easier to read and write and less error prone.

3.1.1 Unary function vectorization

Many of Stan’s unary functions can be applied to any argument type. For example, the exponential function, exp, can be applied to real arguments or arrays of real arguments. Other than for integer arguments, the result type is the same as the argument type, including dimensionality and size. Integer arguments are first promoted to real values, but the result will still have the same dimensionality and size as the argument.

3.1.1.1 Real and real array arguments

When applied to a simple real value, the result is a real value. When applied to arrays, vectorized functions like exp() are defined elementwise. For example,

 // declare some variables for arguments
 real x0;
 array[5] real x1;
 array[4, 7] real x2;
 // ...
 // declare some variables for results
 real y0;
 array[5] real y1;
 array[4, 7] real y2;
 // ...
 // calculate and assign results
 y0 = exp(x0);
 y1 = exp(x1);
 y2 = exp(x2);

When exp is applied to an array, it applies elementwise. For example, the statement above,

 y2 = exp(x2);

produces the same result for y2 as the explicit loop

for (i in 1:4) {
  for (j in 1:7) {
    y2[i, j] = exp(x2[i, j]);
  }
}

3.1.1.2 Vector and matrix arguments

Vectorized functions also apply elementwise to vectors and matrices. For example,

 vector[5] xv;
 row_vector[7] xrv;
 matrix[10, 20] xm;
 
 vector[5] yv;
 row_vector[7] yrv;
 matrix[10, 20] ym;
 
 yv = exp(xv);
 yrv = exp(xrv);
 ym = exp(xm);

Arrays of vectors and matrices work the same way. For example,

 array[12] matrix[17, 93] u;
 
 array[12] matrix[17, 93] z;
 
 z = exp(u);

After this has been executed, z[i, j, k] will be equal to exp(u[i, j, k]).

3.1.1.3 Integer and integer array arguments

Integer arguments are promoted to real values in vectorized unary functions. Thus if n is of type int, exp(n) is of type real. Arrays work the same way, so that if n2 is a one dimensional array of integers, then exp(n2) will be a one-dimensional array of reals with the same number of elements as n2. For example,

 array[23] int n1;
 array[23] real z1;
 z1 = exp(n1);

It would be illegal to try to assign exp(n1) to an array of integers; the return type is a real array.

3.1.2 Binary function vectorization

Like the unary functions, many of Stan’s binary functions have been vectorized, and can be applied elementwise to combinations of both scalars or container types.

3.1.2.1 Scalar and scalar array arguments

When applied to two scalar values, the result is a scalar value. When applied to two arrays, or combination of a scalar value and an array, vectorized functions like pow() are defined elementwise. For example,

 // declare some variables for arguments
 real x00;
 real x01;
 array[5] real x10;
 array[5]real x11;
 array[4, 7] real x20;
 array[4, 7] real x21;
 // ...
 // declare some variables for results
 real y0;
 array[5] real y1;
 array[4, 7] real y2;
 // ...
 // calculate and assign results
 y0 = pow(x00, x01);
 y1 = pow(x10, x11);
 y2 = pow(x20, x21);

When pow is applied to two arrays, it applies elementwise. For example, the statement above,

 y2 = pow(x20, x21);

produces the same result for y2 as the explicit loop

for (i in 1:4) {
  for (j in 1:7) {
    y2[i, j] = pow(x20[i, j], x21[i, j]);
  }
}

Alternatively, if a combination of an array and a scalar are provided, the scalar value is broadcast to be applied to each value of the array. For example, the following statement:

y2 = pow(x20, x00);

produces the same result for y2 as the explicit loop:

for (i in 1:4) {
  for (j in 1:7) {
    y2[i, j] = pow(x20[i, j], x00);
  }
}

3.1.2.2 Vector and matrix arguments

Vectorized binary functions also apply elementwise to vectors and matrices, and to combinations of these with scalar values. For example,

 real x00;
 vector[5] xv00;
 vector[5] xv01;
 row_vector[7] xrv;
 matrix[10, 20] xm;
 
 vector[5] yv;
 row_vector[7] yrv;
 matrix[10, 20] ym;
 
 yv = pow(xv00, xv01);
 yrv = pow(xrv, x00);
 ym = pow(x00, xm);

Arrays of vectors and matrices work the same way. For example,

 array[12] matrix[17, 93] u;
 
 array[12] matrix[17, 93] z;
 
 z = pow(u, x00);

After this has been executed, z[i, j, k] will be equal to pow(u[i, j, k], x00).

3.1.2.3 Input & return types

Vectorised binary functions require that both inputs, unless one is a real, be containers of the same type and size. For example, the following statements are legal:

 vector[5] xv;
 row_vector[7] xrv;
 matrix[10, 20] xm;
 
 vector[5] yv = pow(xv, xv)
 row_vector[7] yrv = pow(xrv, xrv)
 matrix[10, 20] = pow(xm, xm)

But the following statements are not:

 vector[5] xv;
 vector[7] xv2;
 row_vector[5] xrv;
 
 // Cannot mix different types
 vector[5] yv = pow(xv, xrv)

 // Cannot mix different sizes of the same type
 vector[5] yv = pow(xv, xv2)

While the vectorized binary functions generally require the same input types, the only exception to this is for binary functions that require one input to be an integer and the other to be a real (e.g., bessel_first_kind). For these functions, one argument can be a container of any type while the other can be an integer array, as long as the dimensions of both are the same. For example, the following statements are legal:

 vector[5] xv;
 matrix[5, 5] xm;
 array[5] int xi;
 array[5, 5] int xii;
 
 vector[5] yv = bessel_first_kind(xi, xv);
 matrix[5, 5] ym = bessel_first_kind(xii, xm);

Whereas these are not:

 vector[5] xv;
 matrix[5, 5] xm;
 array[7] int xi;

 // Dimensions of containers do not match
 vector[5] yv = bessel_first_kind(xi, xv);

 // Function requires first argument be an integer type
 matrix[5, 5] ym = bessel_first_kind(xm, xm);