# Issues with indexing and performing mathematical operations on three-dimensional array

I am trying to operate on a three-dimensional array and perform a simple mathematical formula by selecting and indexing a single vector from this 80x131x296 matrix. I declare my three dimensional matrix as below

``````  int dists[nSubjects, maxTrials, 296];
``````

And I attempt to perform an operation using the a vector of 296 elements as below:

`````` v = v + ( A[s] ./ ( 1 + dists[s, t, ] ) ) * pe;
``````

However, for this I receive the error “No matches for: Available argument signatures for operator +: Expression is ill formed.”

I am not sure why Stan seems unhappy with me using an indexed vector of the three dimensional array in the formula. Stan remains unhappy even if I attempt to assign this indexed vector to a one-dimensional vector of 296 elements, as below, despite this seeming to be the way that the stan manual recommends indexing from three dimensional arrays.

`````` curDists = dists[s,t,];
``````

The only way that I can get Stan to accept this formula and allow the indexing is for me to (inefficeintly) iterate through all 296 elements of dists and assign them to the curDists vector one-by-one as follows:

``````      for(i in 1:296){
curDists[i] = dists[s,t,i];
}
v = v + ( A[s] ./ ( 1 + curDists ) ) * pe;
``````

Stan is finally happy and allows me to index and perform the formula once I assign them one-by-one. This is very inefficient though and I would like to vectorize. Does anyone know why I am having these issues? Why I cannot index a single vector of 296 elements from the 3-dimensional array? Why I need to assign the elements one-by-one to the vector of 296, rather than just assigning the vector all at once? etc.

My full Stan code is below:

``````data {
int<lower=1> nSubjects;
int<lower = 1> nArms; //number of bandit arms
int<lower = 1> maxTrials; //number of trials
int<lower = 1> nTrials[nSubjects]; //number of trials per sub
int<lower = 0, upper = 296> cueChoice[nSubjects, maxTrials]; // which cue chosen
int<lower = 0, upper = 2> sideChoice[nSubjects,maxTrials]; //index of which side chosen
real reward[nSubjects,maxTrials]; //outcome of bandit arm pull
int<lower = 0> leftOption[nSubjects, maxTrials]; // left option
int<lower = 0> rightOption[nSubjects, maxTrials]; // right option

//int<lower = 0> dists[nSubjects, maxTrials, 296];
int dists[nSubjects, maxTrials, 296];
}

transformed data {
vector[296] initV;  // initial values for V
initV = rep_vector(0.5, 296);
}

parameters {
// Declare all parameters as vectors for vectorizing
// Hyper(group)-parameters
vector[2] mu_pr;
vector<lower=0>[2] sigma;

// Subject-level raw parameters (for Matt trick)
vector[nSubjects] A_pr;    // learning rate
vector[nSubjects] tau_pr;  // inverse temperature
}

transformed parameters {
// subject-level parameters
vector<lower=0, upper=1>[nSubjects] A;
vector<lower=0, upper=10>[nSubjects] tau;

for (i in 1:nSubjects) {
A[i]   = Phi_approx(mu_pr[1]  + sigma[1]  * A_pr[i]);
tau[i] = Phi_approx(mu_pr[2] + sigma[2] * tau_pr[i]) * 10;
}
}

model {
// Hyperparameters
mu_pr  ~ normal(0, 1);
sigma ~ normal(0, 0.2);

// individual parameters
A_pr   ~ normal(0, 1);
tau_pr ~ normal(0, 1);

for (s in 1:nSubjects) {
vector[296] v;
real pe;
vector[2] w;
vector[296] curDists;
vector[296] Avec;
v = initV;

for (t in 1:nTrials[s]) {
w[1] = v[leftOption[s,t]];
w[2] = v[rightOption[s,t]];

sideChoice[s,t] ~ categorical_logit( tau[s] * w );
pe = reward[s,t] - v[cueChoice[s,t]];

for(i in 1:296){
curDists[i] = dists[s,t,i];
}
v = v + ( A[s] ./ ( 1 + curDists ) ) * pe;
//v = v + ( A[s] ./ ( 1 + dists[s,t,] ) ) * pe;

}
}
}

generated quantities {
// For group level parameters
real<lower=0, upper=1> mu_A;
real<lower=0, upper=10> mu_tau;

// For log likelihood calculation
real log_lik[nSubjects];

// For posterior predictive check
real y_pred[nSubjects, maxTrials];

// Set all posterior predictions to 0 (avoids NULL values)
for (i in 1:nSubjects) {
for (t in 1:maxTrials) {
y_pred[i, t] = -1;
}
}

mu_A   = Phi_approx(mu_pr[1]);
mu_tau = Phi_approx(mu_pr[2]) * 10;

{ // local section, this saves time and space

for (s in 1:nSubjects) {
vector[296] v;
real pe;
vector[2] w;
vector[296] curDists;
v = initV;

log_lik[s] = 0;

for (t in 1:(nTrials[s])) {
w[1] = v[leftOption[s,t]];
w[2] = v[rightOption[s,t]];

// compute log likelihood of current trial
log_lik[s] += categorical_logit_lpmf(sideChoice[s, t] | tau[s] * w);

// generate posterior prediction for current trial
y_pred[s, t] = categorical_rng(softmax(tau[s] * w));

// prediction error
pe = reward[s, t] - v[cueChoice[s, t]];

for(i in 1:296){
curDists[i] = dists[s,t,i];
}

// value updating (learning)
v += ( A[s] ./ ( 1 + curDists ) ) * pe;
//v += (A[s]/ ( 1 + dists[s, t, 1:2] ) ) * pe;
}

}

}
}
``````

I believe the issue is not due to your indexing, but in in the addition:

``````1 + dists[s, t, ]
``````

There is not a valid signature for:

``````int + int[]
``````

And so the parser errors.

Additionally, you’re mixing different container types, as `v` and `A[s]` are both `vector` and `dists[s,t,]` is an `int[]`, which is also not a valid operation.

If you want to use `dists` in operations with vectors, then you’ll need to declare it as an array of vectors instead:

``````vector[296] dists[nSubjects, maxTrials];
}
``````

Thank you so much! It looks like this worked. Quick clarification question, you said there is no valid signature for int + int[]. I was wondering why this is the case? For example, at this link, it does indicate that vectors and not compatible with arrays and vice versa. But shouldn’t int be compatible with int since they are of the same type/class? What am I misssing, in terms of why they are not compatible with one another?

1 Like

It’s because stan doesn’t do automatic broadcasting nor recycling. So it’s not about whether int is compatible with int, but whether scalar is compatible with array.

There are some cases where Stan code is “vectorized”, so you can use scalars together with vectors (instead of arrays). But whether this is supported varies between different functions/operators. Also note that some functions/operators don’t interprete vectors as a container of unrelated elements, but in their vector-mathematics sense.