Data Types and Declarations
This chapter covers the data types for expressions in Stan. Every variable used in a Stan program must have a declared data type. Only values of that type will be assignable to the variable (except for temporary states of transformed data and transformed parameter values). This follows the convention of programming languages like C++, not the conventions of scripting languages like Python or statistical languages such as R or BUGS.
The motivation for strong, static typing is threefold.
- Strong typing forces the programmer’s intent to be declared with the variable, making programs easier to comprehend and hence easier to debug and maintain.
- Strong typing allows programming errors relative to the declared intent to be caught sooner (at compile time) rather than later (at run time). The Stan compiler (called through an interface such as CmdStan, RStan, or PyStan) will flag any type errors and indicate the offending expressions quickly when the program is compiled.
- Constrained types will catch runtime data, initialization, and intermediate value errors as soon as they occur rather than allowing them to propagate and potentially pollute final results.
Strong typing disallows assigning the same variable to objects of different types at different points in the program or in different invocations of the program.
Overview of data types
Arguments for built-in and user-defined functions and local variables are required to be basic data types, meaning an unconstrained scalar, vector, or matrix type, or an array of such.
Passing arguments to functions in Stan works just like assignment to basic types. Stan functions are only specified for the basic data types of their arguments, including array dimensionality, but not for sizes or constraints. Of course, functions often check constraints as part of their behavior.
Primitive types
Stan provides two primitive data types, real
for continuous values and int
for integer values. These are both considered scalar types.
Complex types
Stan provides a complex number data type complex
, where a complex number contains both a real and an imaginary component, both of which are of type real
. Complex types are considered scalar types.
Vector and matrix types
Stan provides three real-valued matrix data types, vector
for column vectors, row_vector
for row vectors, and matrix
for matrices.
Stan also provides three complex-valued matrix data types, complex_vector
for column vectors, complex_row_vector
for row vectors, and complex_matrix
for matrices.
Array types
Any type (including the constrained types discussed in the next section) can be made into an array type by declaring array arguments. For example,
array[10] real x;
array[6, 7] matrix[3, 3] m;
array[12, 8, 15] complex z;
declares x
to be a one-dimensional array of size 10 containing real values, declares m
to be a two-dimensional array of size \(6 \times 7\) containing values that are \(3 \times 3\) matrices, and declares z
to be a \(12 \times 8 \times 15\) array of complex numbers.
Prior to 2.26 Stan models used a different syntax which has since been removed. See the Removed Features chapter for more details.
Tuple types
For any sequence of types, Stan provides a tuple data type. For example,
tuple(real, array[5] int) xi;
declares xi
to be a tuple holding two values, the first of which is of type type real
and the second of which a 5-dimensional array of type int
.
Constrained data types
Declarations of variables other than local variables may be provided with constraints. These constraints are not part of the underlying data type for a variable, but determine error checking in the transformed data, transformed parameter, and generated quantities block, and the transform from unconstrained to constrained space in the parameters block.
All of the basic data types other than complex
may be given lower and upper bounds using syntax such as
int<lower=1> N;
real<upper=0> log_p;
vector<lower=-1, upper=1>[3] rho;
There are also special data types for structured vectors and matrices. There are four constrained vector data types, simplex
for unit simplexes, unit_vector
for unit-length vectors, ordered
for ordered vectors of scalars and positive_ordered
for vectors of positive ordered scalars. There are specialized matrix data types corr_matrix
and cov_matrix
for correlation matrices (symmetric, positive definite, unit diagonal) and covariance matrices (symmetric, positive definite). The type cholesky_factor_cov
is for Cholesky factors of covariance matrices (lower triangular, positive diagonal, product with own transpose is a covariance matrix). The type cholesky_factor_corr
is for Cholesky factors of correlation matrices (lower triangular, positive diagonal, unit-length rows).
Constraints provide error checking for variables defined in the data
, transformed data
, transformed parameters
, and generated quantities
blocks. Constraints are critical for variables declared in the parameters
block, where they determine the transformation from constrained variables (those satisfying the declared constraint) to unconstrained variables (those ranging over all of \(\mathbb{R}^n\)).
It is worth calling out the most important aspect of constrained data types:
The model must have support (non-zero density, equivalently finite log density) at parameter values that satisfy the declared constraints.
If this condition is violated with parameter values that satisfy declared constraints but do not have finite log density, then the samplers and optimizers may have any of a number of pathologies including just getting stuck, failure to initialize, excessive Metropolis rejection, or biased draws due to inability to explore the tails of the distribution.
Primitive numerical data types
Unfortunately, the lovely mathematical abstraction of integers and real numbers is only partially supported by finite-precision computer arithmetic.
Integers
Stan uses 32-bit (4-byte) integers for all of its integer representations. The maximum value that can be represented as an integer is \(2^{31}-1\); the minimum value is \(-(2^{31})\).
When integers overflow, their value is determined by the underlying architecture. On most, their values wrap, but this cannot be guaranteed. Thus it is up to the Stan programmer to make sure the integer values in their programs stay in range. In particular, every intermediate expression must have an integer value that is in range.
Integer arithmetic works in the expected way for addition, subtraction, and multiplication, but truncates the result of division (see the Stan Functions Reference integer-valued arithmetic operators section for more information).
Reals
Stan uses 64-bit (8-byte) floating point representations of real numbers. Stan roughly1 follows the IEEE 754 standard for floating-point computation. The range of a 64-bit number is roughly \(\pm 2^{1022}\), which is slightly larger than \(\pm 10^{307}\). It is a good idea to stay well away from such extreme values in Stan models as they are prone to cause overflow.
64-bit floating point representations have roughly 15 decimal digits of accuracy. But when they are combined, the result often has less accuracy. In some cases, the difference in accuracy between two operands and their result is large.
There are three special real values used to represent (1) not-a-number value for error conditions, (2) positive infinity for overflow, and (3) negative infinity for overflow. The behavior of these special numbers follows standard IEEE 754 behavior.
Not-a-number
The not-a-number value propagates. If an argument to a real-valued function is not-a-number, it either rejects (an exception in the underlying C++) or returns not-a-number itself. For boolean-valued comparison operators, if one of the arguments is not-a-number, the return value is always zero (i.e., false).
Infinite values
Positive infinity is greater than all numbers other than itself and not-a-number; negative infinity is similarly smaller. Adding an infinite value to a finite value returns the infinite value. Dividing a finite number by an infinite value returns zero; dividing an infinite number by a finite number returns the infinite number of appropriate sign. Dividing a finite number by zero returns positive infinity. Dividing two infinite numbers produces a not-a-number value as does subtracting two infinite numbers. Some functions are sensitive to infinite values; for example, the exponential function returns zero if given negative infinity and positive infinity if given positive infinity. Often the gradients will break down when values are infinite, making these boundary conditions less useful than they may appear at first.
Promoting integers to reals
Stan automatically promotes integer values to real values if necessary, but does not automatically demote real values to integers. For very large integers, this will cause a rounding error to fewer significant digits in the floating point representation than in the integer representation.
Unlike in C++, real values are never demoted to integers. Therefore, real values may only be assigned to real variables. Integer values may be assigned to either integer variables or real variables. Internally, the integer representation is cast to a floating-point representation. This operation is not without overhead and should thus be avoided where possible.
Complex numerical data type
The complex
data type is a scalar, but unlike real
and int
types, it contains two components, a real and imaginary component, both of which are of type real
. That is, the real and imaginary components of a complex number are 64-bit, IEEE 754-complaint floating point numbers.
Constructing and accessing complex numbers
Imaginary literals are written in mathematical notation using a numeral followed by the suffix i
. For example, the following example constructs a complex number \(2 - 1.3i\) and assigns it to the variable z
.
complex z = 2 - 1.3i;
real re = get_real(z); // re has value 2.0
real im = get_imag(z); // im has value -1.3
The getter functions then extract the real and imaginary components of z
and assign them to re
and im
respectively.
The function to_complex
constructs a complex number from its real and imaginary components. The functional form needs to be used whenever the components are not literal numerals, as in the following example.
vector[K] re;
vector[K] im;
// ...
for (k in 1:K) {
complex z = to_complex(re[k], im[k]);
// ...
}
Promoting real to complex
Expressions of type real
may be assigned to variables of type complex
. For example, the following is a valid sequence of Stan statements.
real x = 5.0;
complex z = x; // get_real(z) == 5.0, get_imag(z) == 0
The real number assigned to a complex number determine’s the complex number’s real component, with the imaginary component set to zero.
Assignability is transitive, so that expressions of type int
may also be assigned to variables of type complex
, as in the following example.
int n = 2;
complex z = n;
Function arguments also support promotion of integer or real typed expressions to type complex
.
Scalar data types and variable declarations
All variables used in a Stan program must have an explicitly declared data type. The form of a declaration includes the type and the name of a variable. This section covers scalar types, namely integer, real, and complex. The next section covers vector and matrix types, and the following section array types.
Unconstrained integer
Unconstrained integers are declared using the int
keyword. For example, the variable N
is declared to be an integer as follows.
int N;
Constrained integer
Integer data types may be constrained to allow values only in a specified interval by providing a lower bound, an upper bound, or both. For instance, to declare N
to be a positive integer, use the following.
int<lower=1> N;
This illustrates that the bounds are inclusive for integers.
To declare an integer variable cond
to take only binary values, that is zero or one, a lower and upper bound must be provided, as in the following example.
int<lower=0, upper=1> cond;
Unconstrained real
Unconstrained real variables are declared using the keyword real
. The following example declares theta
to be an unconstrained continuous value.
real theta;
Unconstrained complex
Unconstrained complex numbers are declared using the keyword complex
. The following example declares z
to be an unconstrained complex variable.
complex z;
Constrained real
Real variables may be bounded using the same syntax as integers. In theory (that is, with arbitrary-precision arithmetic), the bounds on real values would be exclusive. Unfortunately, finite-precision arithmetic rounding errors will often lead to values on the boundaries, so they are allowed in Stan.
The variable sigma
may be declared to be non-negative as follows.
real<lower=0> sigma;
The following declares the variable x
to be less than or equal to \(-1\).
real<upper=-1> x;
To ensure rho
takes on values between \(-1\) and \(1\), use the following declaration.
real<lower=-1, upper=1> rho;
Infinite constraints
Lower bounds that are negative infinity or upper bounds that are positive infinity are ignored. Stan provides constants positive_infinity()
and negative_infinity()
which may be used for this purpose, or they may be supplied as data.
Affinely transformed real
Real variables may be declared on a space that has been transformed using an affine transformation \(x\mapsto \mu + \sigma * x\) with offset \(\mu\) and (positive) multiplier \(\sigma\), using a syntax similar to that for bounds. While these transforms do not change the asymptotic sampling behaviour of the resulting Stan program (in a sense, the model the program implements), they can be useful for making the sampling process more efficient by transforming the geometry of the problem to a more natural multiplier and to a more natural offset for the sampling process, for instance by facilitating a non-centered parameterisation. While these affine transformation declarations do not impose a hard constraint on variables, they behave like the bounds constraints in many ways and could perhaps be viewed as acting as a sort of soft constraint.
The variable x
may be declared to have offset \(1\) as follows.
real<offset=1> x;
Similarly, it can be declared to have multiplier \(2\) as follows.
real<multiplier=2> x;
Finally, we can combine both declarations to declare a variable with offset \(1\) and multiplier \(2\).
real<offset=1, multiplier=2> x;
As an example, we can give x
a normal distribution with non-centered parameterization as follows.
parameters {
real<offset=mu, multiplier=sigma> x;
}model {
x ~ normal(mu, sigma); }
Recall that the centered parameterization is achieved with the code
parameters {
real x;
}model {
x ~ normal(mu, sigma); }
or equivalently
parameters {
real<offset=0, multiplier=1> x;
}model {
x ~ normal(mu, sigma); }
Expressions as bounds and offset/multiplier
Bounds (and offset and multiplier) for integer or real variables may be arbitrary expressions. The only requirement is that they only include variables that have been declared (though not necessarily defined) before the declaration. array[N] row_vector[D] x; If the bounds themselves are parameters, the behind-the-scenes variable transform accounts for them in the log Jacobian.
For example, it is acceptable to have the following declarations.
data {
real lb;
}parameters {
real<lower=lb> phi;
}
This declares a real-valued parameter phi
to take values greater than the value of the real-valued data variable lb
. Constraints may be arbitrary expressions, but must be of type int
for integer variables and of type real
for real variables (including constraints on vectors, row vectors, and matrices). Variables used in constraints can be any variable that has been defined at the point the constraint is used. For instance,
data {
int<lower=1> N;
array[N] real y;
}parameters {
real<lower=min(y), upper=max(y)> phi;
}
This declares a positive integer data variable N
, an array y
of real-valued data of length N
, and then a parameter ranging between the minimum and maximum value of y
. As shown in the example code, the functions min()
and max()
may be applied to containers such as arrays.
A more subtle case involves declarations of parameters or transformed parameters based on parameters declared previously. For example, the following program will work as intended.
parameters {
real a;
real<lower=a> b; // enforces a < b
}transformed parameters {
real c;
real<lower=c> d;
c = a;
d = b; }
The parameters instance works because all parameters are defined externally before the block is executed. The transformed parameters case works even though c
isn’t defined at the point it is used, because constraints on transformed parameters are only validated at the end of the block. Data variables work like parameter variables, whereas transformed data and generated quantity variables work like transformed parameter variables.
Declaring optional variables
A variable may be declared with a size that depends on a boolean constant. For example, consider the definition of alpha
in the following program fragment.
data {
int<lower=0, upper=1> include_alpha;
// ...
}parameters {
vector[include_alpha ? N : 0] alpha;
// ...
}
If include_alpha
is true, the model will include the vector alpha
; if the flag is false, the model will not include alpha
(technically, it will include alpha
of size 0, which means it won’t contain any values and won’t be included in any output).
This technique is not just useful for containers. If the value of N
is set to 1, then the vector alpha
will contain a single element and thus alpha[1]
behaves like an optional scalar, the existence of which is controlled by include_alpha
.
This coding pattern allows a single Stan program to define different models based on the data provided as input. This strategy is used extensively in the implementation of the RStanArm package.
Vector and matrix data types
Stan provides three types of container objects: arrays, vectors, and matrices. Vectors and matrices are more limited kinds of data structures than arrays. Vectors are intrinsically one-dimensional collections of real or complex values, whereas matrices are intrinsically two dimensional. Vectors, matrices, and arrays are not assignable to one another, even if their dimensions are identical. A \(3 \times 4\) matrix is a different kind of object in Stan than a \(3 \times 4\) array.
The intention of using matrix types is to call out their usage in the code. There are three situations in Stan where only vectors and matrices may be used,
- matrix arithmetic operations (e.g., matrix multiplication)
- linear algebra functions (e.g., eigenvalues and determinants), and
- multivariate function parameters and outcomes (e.g., multivariate normal distribution arguments).
Vectors and matrices cannot be typed to return integer values. They are restricted to real
and complex
values.
For constructing vectors and matrices in Stan, see Vector, Matrix, and Array Expressions.
Indexing from 1
Vectors and matrices, as well as arrays, are indexed starting from one (1) in Stan. This follows the convention in statistics and linear algebra as well as their implementations in the statistical software packages R, MATLAB, BUGS, and JAGS. General computer programming languages, on the other hand, such as C++ and Python, index arrays starting from zero.
Vectors
Vectors in Stan are column vectors; see below for information on row vectors. Vectors are declared with a size (i.e., a dimensionality). For example, a 3-dimensional real vector is declared with the keyword vector
, as follows.
vector[3] u;
Vectors may also be declared with constraints, as in the following declaration of a 3-vector of non-negative values.
vector<lower=0>[3] u;
Similarly, they may be declared with a offset and/or multiplier, as in the following example
vector<offset=42, multiplier=3>[3] u;
Complex vectors
Like real vectors, complex vectors are column vectors and are declared with a size. For example, a 3-dimensional complex vector is declared with the keyword complex_vector
, as follows.
complex_vector[3] v;
Complex vector declarations do not support any constraints.
Unit simplexes
A unit simplex is a vector with non-negative values whose entries sum to 1. For instance, \([0.2,0.3,0.4,0.1]^{\top}\) is a unit 4-simplex. Unit simplexes are most often used as parameters in categorical or multinomial distributions, and they are also the sampled variate in a Dirichlet distribution. Simplexes are declared with their full dimensionality. For instance, theta
is declared to be a unit \(5\)-simplex by
simplex[5] theta;
Unit simplexes are implemented as vectors and may be assigned to other vectors and vice-versa. Simplex variables, like other constrained variables, are validated to ensure they contain simplex values; for simplexes, this is only done up to a statically specified accuracy threshold \(\epsilon\) to account for errors arising from floating-point imprecision.
In high dimensional problems, simplexes may require smaller step sizes in the inference algorithms in order to remain stable; this can be achieved through higher target acceptance rates for samplers and longer warmup periods, tighter tolerances for optimization with more iterations, and in either case, with less dispersed parameter initialization or custom initialization if there are informative priors for some parameters.
Unit vectors
A unit vector is a vector with a norm of one. For instance, \([0.5,
0.5, 0.5, 0.5]^{\top}\) is a unit 4-vector. Unit vectors are sometimes used in directional statistics. Unit vectors are declared with their full dimensionality. For instance, theta
is declared to be a unit \(5\)-vector by
unit_vector[5] theta;
Unit vectors are implemented as vectors and may be assigned to other vectors and vice-versa. Unit vector variables, like other constrained variables, are validated to ensure that they are indeed unit length; for unit vectors, this is only done up to a statically specified accuracy threshold \(\epsilon\) to account for errors arising from floating-point imprecision.
Ordered vectors
An ordered vector type in Stan represents a vector whose entries are sorted in ascending order. For instance, \((-1.3,2.7,2.71)^{\top}\) is an ordered 3-vector. Ordered vectors are most often employed as cut points in ordered logistic regression models (see section).
The variable c
is declared as an ordered 5-vector by
ordered[5] c;
After their declaration, ordered vectors, like unit simplexes, may be assigned to other vectors and other vectors may be assigned to them. Constraints will be checked after executing the block in which the variables were declared.
Positive, ordered vectors
There is also a positive, ordered vector type which operates similarly to ordered vectors, but all entries are constrained to be positive. For instance, \((2,3.7,4,12.9)\) is a positive, ordered 4-vector.
The variable d
is declared as a positive, ordered 5-vector by
positive_ordered[5] d;
Like ordered vectors, after their declaration, positive ordered vectors may be assigned to other vectors and other vectors may be assigned to them. Constraints will be checked after executing the block in which the variables were declared.
Row vectors
Row vectors are declared with the keyword row_vector
. Like (column) vectors, they are declared with a size. For example, a 1093-dimensional row vector u
would be declared as
row_vector[1093] u;
Constraints are declared as for vectors, as in the following example of a 10-vector with values between -1 and 1.
row_vector<lower=-1, upper=1>[10] u;
Offset and multiplier are also similar as for the following 3-row-vector with offset -42 and multiplier 3.
row_vector<offset=-42, multiplier=3>[3] u;
Row vectors may not be assigned to column vectors, nor may column vectors be assigned to row vectors. If assignments are required, they may be accommodated through the transposition operator.
Complex row vectors
Complex row vectors are declared with the keyword complex_row_vector
and given a size in basic declarations. For example, a 12-dimensional complex row vector v
would be declared as
complex_row_vector[12] v;
Complex row vectors do not allow constraints.
Matrices
Matrices are declared with the keyword matrix
along with a number of rows and number of columns. For example,
matrix[3, 3] A;
matrix[M, N] B;
declares A
to be a \(3 \times 3\) matrix and B
to be a \(M
\times N\) matrix. For the second declaration to be well formed, the variables M
and N
must be declared as integers in either the data or transformed data block and before the matrix declaration.
Matrices may also be declared with constraints, as in this (\(3 \times 4\)) matrix of non-positive values.
matrix<upper=0>[3, 4] B;
Similarly, matrices can be declared to have a set offset and/or multiplier, as in this matrix with multiplier 5.
matrix<multiplier=5>[3, 4] B;
Assigning to rows of a matrix
Rows of a matrix can be assigned by indexing the left-hand side of an assignment statement. For example, this is possible.
matrix[M, N] a;
row_vector[N] b;
// ...
1] = b; a[
This copies the values from row vector b
to a[1]
, which is the first row of the matrix a
. If the number of columns in a
is not the same as the size of b
, a run-time error is raised; the number of columns of a
is N
, which is also the number of columns of b
.
Assignment works by copying values in Stan. That means any subsequent assignment to a[1]
does not affect b
, nor does an assignment to b
affect a
.
Complex matrices
Complex matrices are declared with the keyword complex_matrix
and a number of rows and columns. For example,
complex_matrix[3, 3] C;
Complex matrices do not allow constraints.
Covariance matrices
Matrix variables may be constrained to represent covariance matrices. A matrix is a covariance matrix if it is symmetric and positive definite. Like correlation matrices, covariance matrices only need a single dimension in their declaration. For instance,
cov_matrix[K] Omega;
declares Omega
to be a \(K \times K\) covariance matrix, where \(K\) is the value of the data variable K
.
Correlation matrices
Matrix variables may be constrained to represent correlation matrices. A matrix is a correlation matrix if it is symmetric and positive definite, has entries between \(-1\) and \(1\), and has a unit diagonal. Because correlation matrices are square, only one dimension needs to be declared. For example,
corr_matrix[3] Sigma;
declares Sigma
to be a \(3 \times 3\) correlation matrix.
Correlation matrices may be assigned to other matrices, including unconstrained matrices, if their dimensions match, and vice-versa.
Cholesky factors of covariance matrices
Matrix variables may be constrained to represent the Cholesky factors of a covariance matrix. This is often more convenient or more efficient than representing covariance matrices directly.
A Cholesky factor \(L\) is an \(M \times N\) lower-triangular matrix (if \(m < n\) then \(L[m, n] =0\)) with a strictly positive diagonal (\(L[k, k] > 0\)) and \(M \geq N\). If \(L\) is a Cholesky factor, then \(\Sigma = L \, L^{\top}\) is a covariance matrix (i.e., it is positive definite). The mapping between positive definite matrices and their Cholesky factors is bijective—every covariance matrix has a unique Cholesky factorization.
The typical case of a square Cholesky factor may be declared with a single dimension,
cholesky_factor_cov[4] L;
Cholesky factors of positive semi-definite matrices
In general, two dimensions may be declared, with the above being equal to cholesky_factor_cov[4, 4]
. The type cholesky_factor_cov[M, N]
may be used for the general \(M \times N\) case to produce positive semi-definite matrices of rank \(M\).
Cholesky factors of correlation matrices
Matrix variables may be constrained to represent the Cholesky factors of a correlation matrix.
A Cholesky factor for a correlation matrix \(L\) is a \(K \times K\) lower-triangular matrix with positive diagonal entries and rows that are of length 1 (i.e., \(\sum_{n=1}^K L_{m,n}^2 = 1\)). If \(L\) is a Cholesky factor for a correlation matrix, then \(L\,L^{\top}\) is a correlation matrix (i.e., symmetric positive definite with a unit diagonal).
To declare the variable L
to be a K
by K
Cholesky factor of a correlation matrix, the following code may be used.
cholesky_factor_corr[K] L;
Assigning constrained variables
Constrained variables of all types may be assigned to other variables of the same unconstrained type and vice-versa. Matching is interpreted strictly as having the same basic type and number of array dimensions. Constraints are not considered, but basic data types are. For instance, a variable declared to be real<lower=0, upper=1>
could be assigned to a variable declared as real
and vice-versa. Similarly, a variable declared as matrix[3, 3]
may be assigned to a variable declared as cov_matrix[3]
or cholesky_factor_cov[3]
, and vice-versa.
Checks are carried out at the end of each relevant block of statements to ensure constraints are enforced. This includes run-time size checks. The Stan compiler isn’t able to catch the fact that an attempt may be made to assign a matrix of one dimensionality to a matrix of mismatching dimensionality.
Promoting real to complex matrixes
Real-valued vectors, row vectors and matrices may be assigned to complex-valued vectors, row vectors and matrices, respectively. For example, the following is legal.
vector[N] v = ...;
complex_vector[N] u = 2 * v;
Row vectors and matrices work the same way.
Expressions as size declarations
Variables may be declared with sizes given by expressions. Such expressions are constrained to only contain data or transformed data variables. This ensures that all sizes are determined once the data is read in and transformed data variables defined by their statements. For example, the following is legal.
data {
int<lower=0> N_observed, N_missing;
// ...
transformed parameters {
vector[N_observed + N_missing] y;
// ...
Accessing vector and matrix elements
If v
is a column vector or row vector, then v[2]
is the second element in the vector. If m
is a matrix, then m[2, 3]
is the value in the second row and third column.
Providing a matrix with a single index returns the specified row. For instance, if m
is a matrix, then m[2]
is the second row. This allows Stan blocks such as
matrix[M, N] m;
row_vector[N] v;
real x;
// ...
2];
v = m[3]; // x == m[2][3] == m[2, 3] x = v[
The type of m[2]
is row_vector
because it is the second row of m
. Thus it is possible to write m[2][3]
instead of m[2, 3]
to access the third element in the second row. When given a choice, the form m[2, 3]
is preferred.
Complex versions work the same way,
complex_matrix[M, N] m = ...;
complex_row_vector[N] u = m[3];
complex_vector[M] v = m[ , 2];
Array index style
The form m[2, 3]
is more efficient because it does not require the creation and use of an intermediate expression template for m[2]
. In later versions, explicit calls to m[2][3]
may be optimized to be as efficient as m[2, 3]
by the Stan compiler.
Size declaration restrictions
An integer expression is used to pick out the sizes of vectors, matrices, and arrays. For instance, we can declare a vector of size M + N
using
vector[M + N] y;
Any integer-denoting expression may be used for the size declaration, providing all variables involved are either data, transformed data, or local variables. That is, expressions used for size declarations may not include parameters or transformed parameters or generated quantities.
Array data types
Stan supports arrays of arbitrary dimension. The values in an array can be any type, so that arrays may contain values that are simple reals or integers, vectors, matrices, or other arrays. Arrays are the only way to store sequences of integers, and some functions in Stan, such as discrete distributions, require integer arguments.
A two-dimensional array is just an array of arrays, both conceptually and in terms of current implementation. When an index is supplied to an array, it returns the value at that index. When more than one index is supplied, this indexing operation is chained. For example, if a
is a two-dimensional array, then a[m, n]
is just a convenient shorthand for a[m][n]
.
Vectors, matrices, and arrays are not assignable to one another, even if their dimensions are identical.
For constructing arrays in Stan, see Vector, Matrix, and Array Expressions.
Declaring array variables
Arrays are declared with the keyword array
followed by the dimensions enclosed in square brackets, the element type, and the name of the variable.
The variable n
is declared as an array of five integers as follows.
array[5] int n;
A two-dimensional array of complex values with three rows and four columns is declared as follows.
array[3, 4] complex a;
A three-dimensional array z
of positive reals with five rows, four columns, and two shelves can be declared as follows.
array[5, 4, 2] real<lower=0> z;
Arrays may also be declared to contain vectors. For example,
array[3] vector[7] mu;
declares mu
to be an array of size 3 containing vectors with 7 elements. Arrays may also contain matrices. The example
array[15, 12] complex_matrix[7, 2] mu;
declares a 15 by 12 array of \(7 \times 2\) complex matrices. Any of the constrained types may also be used in arrays, as in the declaration
array[2, 3, 4] cholesky_factor_cov[5, 6] mu;
of a \(2 \times 3 \times 4\) array of \(5 \times 6\) Cholesky factors of covariance matrices.
Accessing array elements and subarrays
If x
is a 1-dimensional array of length 5, then x[1]
is the first element in the array and x[5]
is the last. For a \(3
\times 4\) array y
of two dimensions, y[1, 1]
is the first element and y[3, 4]
the last element. For a three-dimensional array z
, the first element is z[1, 1, 1]
, and so on.
Subarrays of arrays may be accessed by providing fewer than the full number of indexes. For example, suppose y
is a two-dimensional array with three rows and four columns. Then y[3]
is one-dimensional array of length four. This means that y[3][1]
may be used instead of y[3, 1]
to access the value of the first column of the third row of y
. The form y[3, 1]
is the preferred form (see note in this chapter).
Assigning
Subarrays may be manipulated and assigned just like any other variables. Similar to the behavior of matrices, Stan allows blocks such as
array[9, 10, 11] real w;
array[10, 11] real x;
array[11] real y;
real z;
// ...
5];
x = w[4]; // y == w[5][4] == w[5, 4]
y = x[3]; // z == w[5][4][3] == w[5, 4, 3] z = y[
Complex-valued arrays work the same way.
Arrays of matrices and vectors
Arrays of vectors and matrices are accessed in the same way as arrays of doubles. Consider the following vector and scalar declarations.
array[3, 4] vector[5] a;
array[4] vector[5] b;
vector[5] c;
real x;
With these declarations, the following assignments are legal.
1]; // result is array of vectors
b = a[1, 3]; // result is vector
c = a[3]; // same result as above
c = b[1, 3, 5]; // result is scalar
x = a[3, 5]; // same result as above
x = b[5]; // same result as above x = c[
Row vectors and other derived vector types (simplex and ordered) behave the same way in terms of indexing.
Consider the following matrix, vector and scalar declarations.
array[3, 4] matrix[6, 5] d;
array[4] matrix[6, 5] e;
matrix[6, 5] f;
row_vector[5] g;
real x;
With these declarations, the following definitions are legal.
1]; // result is array of matrices
e = d[1, 3]; // result is matrix
f = d[3]; // same result as above
f = e[1, 3, 2]; // result is row vector
g = d[3, 2]; // same result as above
g = e[2]; // same result as above
g = f[1, 3, 5, 2]; // result is scalar
x = d[3, 5, 2]; // same result as above
x = e[5, 2]; // same result as above
x = f[2]; // same result as above x = g[
As shown, the result f[2]
of supplying a single index to a matrix is the indexed row, here row 2 of matrix f
.
Partial array assignment
Subarrays of arrays may be assigned by indexing on the left-hand side of an assignment statement. For example, the following is legal.
array[I, J, K] real x;
array[J, K] real y;
array[K] real z;
// ...
1] = y;
x[1, 1] = z; x[
The sizes must match. Here, x[1]
is a J
by K
array, as is y
.
Partial array assignment also works for arrays of matrices, vectors, and row vectors.
Mixing array, vector, and matrix types
Arrays, row vectors, column vectors and matrices are not interchangeable in Stan. Thus a variable of any one of these fundamental types is not assignable to any of the others, nor may it be used as an argument where the other is required (use as arguments follows the assignment rules).
Mixing vectors and arrays
For example, vectors cannot be assigned to arrays or vice-versa.
array[4] real a;
vector[4] b;
row_vector[4] c;
// ...
// illegal assignment of vector to array
a = b; // illegal assignment of array to vector
b = a; // illegal assignment of row vector to array
a = c; // illegal assignment of array to row vector c = a;
Mixing row and column vectors
It is not even legal to assign row vectors to column vectors or vice versa.
vector[4] b;
row_vector[4] c;
// ...
// illegal assignment of row vector to column vector
b = c; // illegal assignment of column vector to row vector c = b;
Mixing matrices and arrays
The same holds for matrices, where 2-dimensional arrays may not be assigned to matrices or vice-versa.
array[3, 4] real a;
matrix[3, 4] b;
// ...
// illegal assignment of matrix to array
a = b; // illegal assignment of array to matrix b = a;
Mixing matrices and vectors
A \(1 \times N\) matrix cannot be assigned a row vector or vice versa.
matrix[1, 4] a;
row_vector[4] b;
// ...
// illegal assignment of row vector to matrix
a = b; // illegal assignment of matrix to row vector b = a;
Similarly, an \(M \times 1\) matrix may not be assigned to a column vector.
matrix[4, 1] a;
vector[4] b;
// ...
// illegal assignment of column vector to matrix
a = b; // illegal assignment of matrix to column vector b = a;
Size declaration restrictions
An integer expression is used to pick out the sizes of arrays. The same restrictions as for vector and matrix sizes apply, namely that the size is declared with an integer-denoting expression that does not contain any parameters, transformed parameters, or generated quantities.
Size zero arrays
If any of an array’s dimensions is size zero, the entire array will be of size zero. That is, if we declare
array[3, 0] real a;
then the resulting size of a
is zero and querying any of its dimensions at run time will result in the value zero. Declared as above, a[1]
will be a size-zero one-dimensional array. For comparison, declaring
array[0, 3] real b;
also produces an array with an overall size of zero, but in this case, there is no way to index legally into b
, because b[0]
is undefined. The array will behave at run time as if it’s a \(0 \times
0\) array. For example, the result of to_matrix(b)
will be a \(0 \times 0\) matrix, not a \(0 \times 3\) matrix.
Tuple data type
Stan supports tuples of arbitrary size. The values in a tuple can be of arbitrary type, but the component types must be declared along with the declaration of the tuple. Tuples can be manipulated as a whole, or their elements may be accessed and set individually.
Declaring tuple variables
Tuples are declared with the keyword tuple
followed by a parenthesized sequence of types, which determine the types of the respective tuple entries. For example, a tuple with three elements may be declared as
tuple(int, vector[3], complex) abc;
Tuples must have at least two entries, so the following declarations are illegal.
tuple() nil; // ILLEGAL
tuple(int) n; // ILLEGAL
Tuples can be assigned as a whole if their elements can be assigned individually. For example, a
can be assigned to b
in the following example because int
can be promoted to complex
.
tuple(int, real) a;
...tuple(complex, real) b = a;
Tuple types may have elements which are declared as tuples, such as the following example.
tuple(int, tuple(real, complex)) x;
In this case, it would probably be simpler to use a 3-tuple type, tuple(int, real, complex)
.
Tuples can be declared with constraints anywhere that ordinary variables can (i.e., as top-level block variables). That means any context in which it is legal to have a declaration
real<lower=0> sigma;
real<lower=0, upper=1> theta;
it is legal to have a tuple with constraints such as
tuple(real<lower=0>, real<lower=0, upper=1>) sigma_theta;
Accessing tuple elements
Tuple elements may be accessed directly. For example, with our declaration of abc
from the last section, Stan uses abc.1
for the first element, abc.2
for the second, and abc.3
for the third. These numbers must be integer literals (i.e., they cannot be variables), and must be within the size of the number of elements of tuples. The types of elements are as declared, so that abc.1
is of type int
, abc.2
of type vector[3]
and abc.3
of type complex
.
Assigning tuple elements
Tuple elements can be assigned individually, allowing, e.g.,
tuple(int, real) ab;
.1 = 123;
ab.2 = 12.9; ab
As with other assignments, promotions will happen if necessary (of int
to real
and of real
to complex
, along with the corresponding container type promotions).
Unpacking assignment of tuples
For convenience of using values stored in tuples, Stan supports “unpacking” (or “destructuring”) of tuples in an assignment statement.
Given a tuple t
of type tuple(T1, ..., Tn)
and a sequence of assignable expressions of types v1
, …, vn
, where each vi
has a type which is assignable from type Ti
, individual elements of the tuple may be assigned to the corresponding variables in the sequence by the statement
/*...*/, vn) = t; (v1,
Note that the above parenthesis are required, unlike in some other languages with similar features (e.g., Python).
These unpacking assignments can be nested if the tuple on the right hand side contains nested tuples.
For example, if T
is a tuple of type tuple(int, (real, real), complex)
, then the program
int i;
real x, y;
complex z;
(i, (x, y), z) = T;
Assigns the result of T.1
to i
, the result of T.2.1
to x
, the result of T.2.2
to y
, and the result of T.3
to z
.
The left hand side must match in size the tuple on the right. Additionally, the same variable may not appear more than once in the left hand side of an unpacking assignment.
Variable types vs. constraints and sizes
The type information associated with a variable only contains the underlying type and dimensionality of the variable.
Type information excludes sizes
The size associated with a given variable is not part of its data type. For example, declaring a variable using
array[3] real a;
declares the variable a
to be an array. The fact that it was declared to have size 3 is part of its declaration, but not part of its underlying type.
When are sizes checked?
Sizes are determined dynamically (at run time) and thus cannot be type-checked statically when the program is compiled. As a result, any conformance error on size will raise a run-time error. For example, trying to assign an array of size 5 to an array of size 6 will cause a run-time error. Similarly, multiplying an \(N \times M\) by a \(J \times K\) matrix will raise a run-time error if \(M \neq J\).
Type information excludes constraints
Like sizes, constraints are not treated as part of a variable’s type in Stan when it comes to the compile-time check of operations it may participate in. Anywhere Stan accepts a matrix as an argument, it will syntactically accept a correlation matrix or covariance matrix or Cholesky factor. Thus a covariance matrix may be assigned to a matrix and vice-versa.
Similarly, a bounded real may be assigned to an unconstrained real and vice-versa.
When are function argument constraints checked?
For arguments to functions, constraints are sometimes, but not always checked when the function is called. Exclusions include C++ standard library functions. All probability functions and cumulative distribution functions check that their arguments are appropriate at run time as the function is called.
When are declared variable constraints checked?
For data variables, constraints are checked after the variable is read from a data file or other source. For transformed data variables, the check is done after the statements in the transformed data block have executed. Thus it is legal for intermediate values of variables to not satisfy declared constraints.
For parameters, constraints are enforced by the transform applied and do not need to be checked. For transformed parameters, the check is done after the statements in the transformed parameter block have executed.
For all blocks defining variables (transformed data, transformed parameters, generated quantities), real values are initialized to NaN
and integer values are initialized to the smallest legal integer (i.e., a large absolute value negative number).
For generated quantities, constraints are enforced after the statements in the generated quantities block have executed.
Type naming notation
In order to refer to data types, it is convenient to have a way to refer to them. The type naming notation outlined in this section is not part of the Stan programming language, but rather a convention adopted in this document to enable a concise description of a type.
Because size information is not part of a data type, data types will be written without size information. For instance, array[] real
is the type of one-dimensional array of reals and matrix
is the type of matrices. The three-dimensional integer array type is written as array[,,] int
, indicating the number slots available for indexing. Similarly, array[,] vector
is the type of a two-dimensional array of vectors.
Variable declaration
Variables in Stan are declared by giving a type and a name. For example
int N;
vector[N] y;
array[5] matrix[3, 4] A;
declares a variable N
that is an integer, a variable y
that is a vector of length N
(the previously declared variable), and a variable A
, which is a length-5 array where each element is a 3 by 4 matrix.
The size of top-level variables in the parameters
, transformed parameters
, and generated quantities
must remain constant across all iterations, therefore only data variables can be used in top-level size declarations.
// illegal and will be flagged by the compiler:
generated quantities {
int N = 10;
array[N] int foo;
Depending on where the variable is declared in the Stan program, it either must or cannot have size information, and constraints are either optional or not allowed.
// valid block variables, but not locals or function parameters
vector<lower=0>[N] u;
// valid as a block or local variable, but not a function parameter
array[3] int is;
// function parameters exclude sizes and cannot be constrained
void pretty_print_tri_lower(matrix x) { ... }
Top-level variables can have constraints and must include sizes for their types, as in the above examples. Local variables, like those defined inside loops or local blocks cannot be constrained, but still include sizes. Finally, variables declared as function parameters are not constrained types and exclude sizes.
In the following table, the leftmost column is a list of the unconstrained and undimensioned basic types; these are used as function return types and argument types. The middle column is of unconstrained types with dimensions; these are used as local variable types. The variables M
and N
indicate number of columns and rows, respectively. The variable K
is used for square matrices, i.e., K
denotes both the number of rows and columns. The rightmost column lists the corresponding constrained types. An expression of any right-hand column type may be assigned to its corresponding left-hand column basic type. At runtime, dimensions are checked for consistency for all variables; containers of any sizes may be assigned to function arguments. The constrained matrix types cov_matrix[K]
, corr_matrix[K]
, cholesky_factor_cov[K]
, and cholesky_factor_corr[K]
are only assignable to matrices of dimensions matrix[K, K]
types.
Function Argument (unsized) |
(unconstrained) |
|
---|---|---|
int |
int |
int |
int<lower=L> |
||
int<upper=U> |
||
int<lower=L, upper=U> |
||
int<offset=O> |
||
int<multiplier=M> |
||
int<offset=O, multiplier=M> |
||
real |
real |
real |
real<lower=L> |
||
real<upper=U> |
||
real<lower=L, upper=U> |
||
real<offset=O> |
||
real<multiplier=M> |
||
real<offset=O, multiplier=M> |
||
complex |
complex |
complex |
vector |
vector[N] |
vector[N] |
vector[N]<lower=L> |
||
vector[N]<upper=U> |
||
vector[N]<lower=L, upper=U> |
||
vector[N]<offset=O> |
||
vector[N]<multiplier=M> |
||
vector[N]<offset=O, multiplier=M> |
||
ordered[N] |
||
positive_ordered[N] |
||
simplex[N] |
||
unit_vector[N] |
||
row_vector |
row_vector[N] |
row_vector[N] |
row_vector[N]<lower=L> |
||
row_vector[N]<upper=U> |
||
row_vector[N]<lower=L, upper=U> |
||
row_vector[N]<offset=O> |
||
row_vector[N]<multiplier=M> |
||
row_vector[N]<offset=O, multiplier=M> |
||
matrix |
matrix[M, N] |
matrix[M, N] |
matrix[M, N]<lower=L> |
||
matrix[M, N]<upper=U> |
||
matrix[M, N]<lower=L, uppers=U> |
||
matrix[M, N]<offset=O> |
||
matrix[M, N]<multiplier=M> |
||
matrix[M, N]<offset=O, multiplier=M> |
||
matrix[K, K] |
corr_matrix[K] |
|
matrix[K, K] |
cov_matrix[K] |
|
matrix[K, K] |
cholesky_factor_corr[K] |
|
matrix[K, K] |
cholesky_factor_cov[K] |
|
complex_vector |
complex_vector[M] |
complex_vector[M] |
complex_row_vector |
complex_row_vector[N] |
complex_row_vector[N] |
complex_matrix |
complex_matrix[M, N] |
complex_matrix[M,N] |
array[] vector |
array[M] vector[N] |
array[M] vector[N] |
array[M] vector[N]<lower=L> |
||
array[M] vector[N]<upper=U> |
||
array[M] vector[N]<lower=L, upper=U> |
||
array[M] vector[N]<offset=O> |
||
array[M] vector[N]<multiplier=M> |
||
array[M] vector[N]<offset=O, multiplier=M> |
||
array[M] ordered[N] |
||
array[M] positive_ordered[N] |
||
array[M] simplex[N] |
||
array[M] unit_vector[N] |
Additional array types follow the same basic template as the final example in the table and can contain any of the previous types. The unsized version of arrays with more than one dimension is specified by using commas, e.g. array[ , ]
is a 2-D array.
For more on how function arguments and return types are declared, consult the User’s Guide chapter on functions.
Compound variable declaration and definition
Stan allows assignable variables to be declared and defined in a single statement. Assignable variables are
- local variables, and
- variables declared in the transformed data, transformed parameters, or generated quantities blocks.
For example, the statement
int N = 5;
declares the variable N
to be an integer scalar type and at the same time defines it to be the value of the expression 5
.
Assignment typing
The type of the expression on the right-hand side of the assignment must be assignable to the type of the variable being declared. For example, it is legal to have
real sum = 0;
even though 0
is of type int
and sum
is of type real
, because integer-typed scalar expressions can be assigned to real-valued scalar variables. In all other cases, the type of the expression on the right-hand side of the assignment must be identical to the type of the variable being declared.
Variables of any type may have values assigned to them. For example,
matrix[3, 2] a = b;
declares a \(3 \times 2\) matrix variable a
and assigns a copy of the value of b
to the variable a
. The variable b
must be of type matrix
for the statement to be well formed. For the code to execute successfully, b
must be the same shape as a
, but this cannot be validated until run time. Because a copy is assigned, subsequent changes to a
do not affect b
and subsequent changes to b
do not affect a
.
Right-hand side expressions
The right-hand side may be any expression which has a type which is assignable to the variable being declared. For example,
matrix[3, 2] a = 0.5 * (b + c);
assigns the matrix variable a
to half of the sum of b
and c
. The only requirement on b
and c
is that the expression b + c
be of type matrix
. For example, b
could be of type matrix
and c
of type real
, because adding a matrix to a scalar produces a matrix, and the multiplying by a scalar produces another matrix.
Similarly,
complex z = 2 + 3i;
assigns the the complex number \(2 + 3i\) to the complex scalar z
. The right-hand side expression can be a call to a user defined function, allowing general algorithms to be applied that might not be otherwise expressible as simple expressions (e.g., iterative or recursive algorithms).
Scope within expressions
Any variable that is in scope and any function that is available in the block in which the compound declaration and definition appears may be used in the expression on the right-hand side of the compound declaration and definition statement.
Declaring multiple variables at once
Stan will interpret multiple comma-separated variable names following a single type as declaring multiple new variables. This is available for all variable declarations in all blocks.
Types for multiple declarations
The code:
real x, y;
is equivalent to
real x;
real y;
As a result, all declarations on the same line must be of the same type.
Combining with other features
The ability to declare multiple variables can be combined with assignments whenever a declare-define is valid, as documented in the section introducing compound declarations and definitions :
real x = 3, y = 5.6;
Constrained data types can also be declared together, so long as the constraint for each variable is the same:
real<lower=0> x, y;
Footnotes
Stan compiles integers to
int
and reals todouble
types in C++. Precise details of rounding will depend on the compiler and hardware architecture on which the code is run.↩︎