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;
real x1[5];
real x2[4, 7];
...
// declare some variables for results
real y0;
real y1[5];
real y2[4, 7];
...
// 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,
matrix[17, 93] u[12];
matrix[17, 93] z[12];
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,
int n1[23];
real z1[23];
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;
real x10[5];
real x11[5];
real x20[4, 7];
real x21[4, 7];
...
// declare some variables for results
real y0;
real y1[5];
real y2[4, 7];
...
// 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,
matrix[17, 93] u[12];
matrix[17, 93] z[12];
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;
int xi[5];
int xii[5, 5];
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;
int xi[7];
// 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);