template <typename T1, int R1, int C1, typename T2, int R2, int C2>
inline
const std::pair<Eigen::Matrix<T1,R1,C1>,
Eigen::Matrix<T2,R2,C2> >
tuple(const Eigen::Matrix<T1,R1,C1>& A, const Eigen::Matrix<T2,R2,C2>& B) {
return std::make_pair(A,B);
}
y ~ poisson_log_log(tuple(X,beta));
// PoissonLog(n|X,beta) [n >= 0] = Poisson(n|exp(X*beta))
template <bool propto,
typename T_n, typename T_X, int N, int K, typename T_beta>
typename return_type<T_X,T_beta>::type
poisson_log_log(const T_n& n, const std::pair<Eigen::Matrix<T_X,N,K>,
Eigen::Matrix<T_beta,K,1> >& Xbeta) {
typedef typename stan::partials_return_type<T_n,T_X,T_beta>::type
T_partials_return;
static const char* function("stan::prob::poisson_log_log");
// check if any vectors are zero length
if (!(stan::length(n) && N))
return 0.0;
// set up return value accumulator
T_partials_return logp(0.0);
// FIXME: implement
return logp;
}
I don't understand why you're suggesting using
a pair rather than two separate arguments.
And by "user", do you mean a client of the C++ API
or an end user of RStan?
And which doc are we talking about --- the API doc in
doxygen? We've been very inconsistent with that, but
I'm trying to add proper doc to all my commits now.
> lookup(dpois)
StanFunction Arguments ReturnType Page
443 poisson_log (ints n, reals lambda) real 342
444 poisson_log_log (ints n, reals alpha) real 342
445 poisson_log ~ real 342
447 poisson ~ real 341
> lookup(dpois)
StanFunction Arguments ReturnType Page
443 poisson_log (ints n, reals lambda) real 342
444 poisson_log_log (ints n, reals alpha) real 342
??? poisson_log_log (ints n, pair Xbeta) real 342
445 poisson_log ~ real 342
447 poisson ~ real 341
But my biggest worry (despite being the one who suggested
to Rob this would be a good idea) is that it's not
general enough. For example, it's not going to work
for hierarchical/multilevel models unless we pad out X lme4 style,
because there's more index fiddling beyond a simple product.
It's not super-general, but it might be general enough for Rob's sponsor. I think the likelihood of many lme4 style models would be better implemented in Stan as a loop over grouped data, in which case they could use a construction like this for each group.
data {
int<lower=2> J;
int<lower=1> N[J];
int<lower=1> K;
matrix[sum(N),K] X;
vector[sum(N)] y;
}
transformed data {
vector y_j[J]; // parses as std::vector<Eigen::Matrix<T,Eigen::Dynamic,1> > y_j(J);
matrix X_j[J]; // parses as std::vector<Eigen::Matrix<T,Eigen::Dynamic,Eigen::Dynamic> > X_j(J);
int mark; // where T is double in transformed data / generated quantities and var otherwise
mark <- 1;
for (j in 1:J) {
X_j[j] <- block(X, mark, 1, N[j], K);
y_j[j] <- segment(y, mark, N[j]);
mark <- mark + N[j];
}
}
...
model {
for(j in 1:J) likelihood_lp(theta[j], y_j[j], X_j[j]);
}
The main reason the sizing is all there is for error checking so
that we don't segfault at runtime.
#include <vector>
#include <Eigen/Dense>
int main() {
std::vector<Eigen::MatrixXd> x(2);
Eigen::MatrixXd y = x[0] + x[1];
return y.rows();
}
For what you want to do, I'd suggest ... to do the work in the model:
block_view(X, mark, 1, N[j], K); // returns an N[j] by K Eigen::Map that points to the mark,1 element of X
So I would suggest something like
normal_lm(y, x, beta, sigma);
which would be equal to
normal(y, x * beta, sigma);
But I think that only works for (matrix x, vector beta),
or were you imagining also having (row_vector x, vector beta)?
What would happen to the other arguments and vectorization?
> But it would be good to create an idiom that was common across many of the distributions that are used for GLMs.
OK, that's some motivation. But then we'd need to
introduce pair() into the language to use it, right? I probably
misunderstood what you were suggesting.
Calling it lookup rather than stan_lookup may
be a problem with more namespace clashes. Will we be able to do
lookup(poisson), too? And maybe "translate" would be better to take
R functions to Stan functions?
Maybe we should work out some kind of extensions package?
> If someone tried to multiply x[0] by an actual matrix, they would get Stan's non-conformability error, if someone tried to access an element of x[0], they would get Stan's out of bounds error, etc.
Oh, I think you're right --- I think we check the actual size,
not the declared size, because they're the same. At least in
assignment.
> Oh, I think you're right --- I think we check the actual size,
> not the declared size, because they're the same. At least in
> assignment.
But right now it won't work because everything gets sized when
it's defined along with being declared.
model {
matrix[0,0] X[2];
vector[4] ones;
ones[1] <- 1;
ones[2] <- 1;
ones[3] <- 1;
ones[4] <- 1;
X[1] <- diag_matrix(ones);
theta ~ normal(0,1);
}
/**
* Copy the right-hand side's value to the left-hand side
* variable.
*
* The <code>assign()</code> function is overloaded. This
* instance will be called for arguments that are both
* <code>Eigen::Matrix</code> types and whose shapes match. The
* shapes are specified in the row and column template parameters.
*
* @tparam LHS Type of left-hand side matrix elements.
* @tparam RHS Type of right-hand side matrix elements.
* @tparam R Row shape of both matrices.
* @tparam C Column shape of both mtarices.
* @param x Left-hand side matrix.
* @param y Right-hand side matrix.
* @throw std::invalid_argument if sizes do not match.
*/
template <typename LHS, typename RHS, int R, int C>
inline void
assign(Eigen::Matrix<LHS,R,C>& x,
const Eigen::Matrix<RHS,R,C>& y) {
if(x.rows() == 0 && x.cols() == 0) {
x = y;
return ;
}
stan::math::check_matching_dims("assign",
"x", x,
"y", y);
for (int i = 0; i < x.size(); ++i)
assign(x(i),y(i));
}
matrix X[2];
// parse to vector<Eigen::Matrix<T__,Eigen::Dynamic,Eigen::Dynamic> > X(2);
// i.e. no fill value after the 2
matrix[0,0] X[2];
> Iff so, then I really think we should do this in the language. Being able to, e.g., return a std::vector of two Eigen things where the first was eigenvectors and the second was eigenvalues would be much less dumb than calling eigenvectors_sym() and eigenvalues_sym() separately and similarly for the other matrix decompositions.
I'm not sure how we'd do that because the eigenvectors form a matrix
and the eigenvalues a vector.
#include <vector>
#include <Eigen/Dense>
int main() {
std::vector<Eigen::Matrixd> > x(2);
Eigen::MatrixXd m(2,2);
m(0,0) = 3;
m(1,0) = 2.5;
m(0,1) = -1;
m(1,1) = m(1,0) + m(0,1);
x[0] = m;
x[1] = m.col(1);
return 0;
}
> And Seth / Aki could get going on this idea of representing a Kronecker product of p covariance matrices of different orders as a cov_matrix KP[p].
This I know from nothing.
transformed data {
vector[4] ones;
ones[1] <- 1;
ones[2] <- 1;
ones[3] <- 1;
ones[4] <- 1;
}
parameters {
real theta;
}
model {
matrix[4,4] X[2];
X[1] <- diag_matrix(ones);
theta ~ normal(0,1);
}
parameters {
real theta;
}
model {
matrix[-1,-1] X[2];
theta ~ normal(0,1);
}
Let me work on turning some of these into issues, then we
can continue the discussion there.