-------------------------------------------
~/stan>git branch -a -v
* master 60e8c4e Fixes so that new version of
multi_normal_log passes unit tests.
remotes/origin/HEAD -> origin/master
remotes/origin/master 60e8c4e Fixes so that new version of
multi_normal_log passes unit tests.
~/stan>git status
# On branch master
nothing to commit (working directory clean)
-----------------------------
Could this be related to the work in R we did on
a branch on my machine at work? I don't know that
there's much in rstan/rstan other than boilerplate
generated by rcpp.
I wrote a bunch of extra functions for the distributions
to get rid of the C++-11 features. Can you see a diff
for any of these files?
- Bob
stan/lib/Eigen/src/Core/CwiseBinaryOp.h
you'll find the macro
EIGEN_CHECK_BINARY_COMPATIBILITY
which is what's throwing our error:
YOU_MIXED_DIFFERENT_NUMERIC_TYPES_...
The doc above says you can't mix different types.
But they do have a special case for complex inside
the first EIGEN_STATIC_ASSERT.
When they say "vectorization", I think they're
referring to their fixed size optimizations
and things like GPU optimizations (some of which
only work on 32-bit float, I think).
You can see the template specializations for the product
type in the following file starting at line 119:
Core/Product.h
For instance, gemm is vectorized floating point operations.
I'm pretty sure this determines the expression template
return type for a product and is responsible for figuring
out whether to actually multiply or use something like
gemm vectorization. Note that Large=2 and Small=3 are just
enum values separated from 1, and all Dynamic dimensions
are Large given the categorization template metaprogram
up at line 62 of Prouct.h.
This all plays out in
Core/products/GeneralMatrixMatrix.h
where they actually define the access operators that eventually
resolve the expression templates.
Every time I dive into this stuff I get scared and back off.
- Bob
If you look at line 109 of
stan/lib/Eigen/src/Core/CwiseBinaryOp.h
you'll find the macro
EIGEN_CHECK_BINARY_COMPATIBILITY
which is what's throwing our error:
YOU_MIXED_DIFFERENT_NUMERIC_TYPES_...
The doc above says you can't mix different types.
But they do have a special case for complex inside
the first EIGEN_STATIC_ASSERT.
When they say "vectorization", I think they're
referring to their fixed size optimizations
and things like GPU optimizations (some of which
only work on 32-bit float, I think).
You can see the template specializations for the product
type in the following file starting at line 119:
Core/Product.h
For instance, gemm is vectorized floating point operations.
I'm pretty sure this determines the expression template
return type for a product and is responsible for figuring
out whether to actually multiply or use something like
gemm vectorization. Note that Large=2 and Small=3 are just
enum values separated from 1, and all Dynamic dimensions
are Large given the categorization template metaprogram
up at line 62 of Prouct.h.
This all plays out in
Core/products/GeneralMatrixMatrix.h
where they actually define the access operators that eventually
resolve the expression templates.
Every time I dive into this stuff I get scared and back off.
I believe vectorization probably more aptly refers to SIMD instructions (think SSE or, if your memory is that good, MMX) which work on both 32bit and 64bit platforms with both floats and doubles. However, they are only relevant for basic numeric instructions and loosing vectorization for the double OP vari cases doesn't really matter at this point.
On 3/12/12 12:33 PM, Marcus Brubaker wrote:
> On Mon, Mar 12, 2012 at 2:05 AM, Bob Carpenter <ca...@alias-i.com
> <mailto:ca...@alias-i.com>> wrote:
> I believe vectorization probably more aptly refers to SIMD instructions
> (think SSE or, if your memory is that good, MMX) which work on both
> 32bit and 64bit platforms with both floats and doubles.
That makes sense.
> However, they
> are only relevant for basic numeric instructions and loosing
> vectorization for the double OP vari cases doesn't really matter at this
> point.
There's also all kinds of parallelism directives tied in on
a platform-specific basis at this low level.
The code's pretty dense to work through.
>
> You can see the template specializations for the product
> type in the following file starting at line 119:
>
> Core/Product.h
>
> For instance, gemm is vectorized floating point operations.
> I'm pretty sure this determines the expression template
> return type for a product and is responsible for figuring
> out whether to actually multiply or use something like
> gemm vectorization. Note that Large=2 and Small=3 are just
> enum values separated from 1, and all Dynamic dimensions
> are Large given the categorization template metaprogram
> up at line 62 of Prouct.h.
>
> This all plays out in
>
> Core/products/__GeneralMatrixMatrix.h
>
> where they actually define the access operators that eventually
> resolve the expression templates.
>
> Every time I dive into this stuff I get scared and back off.
>
>
> It seems like the limiting factor here is really that we're defining a
> var scalar type and constructing an Eigen::Matrix<var> when Eigen really
> doesn't support non-basic types (except for complex) for entries.
Just to be clear. Eigen's fine with our var type, just
not with mixing var and double types.
> One way around this is might be to define a matrix variant of the vari
> class. E.G., something like:
>
> template<int Rows, int Cols>
> class vari_matrix : public chainable {
> const Eigen::Matrix<double,Rows,Cols> val_;
> ...
> }
This is the first thing I tried to design, but no luck.
There are several problems with this specific suggestion.
One, we almost certainly want Rows and Cols to be runtime,
not compile time. Two, we need a similarly shaped adjoint.
So let's assume we really have this:
class vari_matrix : public chainable {
Eigen::matrix<double,Dynamic,Dynamic> val_;
Eigen::matrix<double,Dynamic,Dynamic> adj_;
}
Now let's suppose we define the operators to work as we
want, so that we have:
var& vari_matrix::operator()(int m, int n);
and wrap a var_matrix around it:
class var_matrix {
vari_matrix* vmi_;
}
So when you look at this, you realize maybe we could just template
it all:
template <typename Val>
class vari {
Val val_;
Val adj_;
}
template <typename Val>
class var {
vari<Val>* vi_;
}
We have to construct that var using a new vari given val_(m,n)
and adj_(m,n), which is going to be a real drag. But this should
really be a reference, so that we could do
vari_matrix x;
x(m,n) = y;
I never figured out how to do that without a whole lot of work
on a new kind of var that acts like a view back into the matrix
and remains very inefficient.
Then how do we string together expressions like:
(x - y) * Sigma * (x - y)'
and make sure that they get evaluated with the proper expression
templates AND propagate the right derivatives? We'd have to
build up our own expressions, I think.
So what I did is just went with Matrix<var,Dynamic,Dynamic> for
matrices, wrote our own matrix ops, and just punted on expressions.
I wish to continue to emphasize that the savings from partially
evaluating the derivatives in the var-based ops will dwarf any
savings from getting the expression templates working within Eigen.
That only solves one of our problems, and even if we do it, makes
evaluating the derivatives actually harder, as we have to start
specializing Eigen's template expressions for Scalar = var.
The really slow part is the promotion of double to var, but
that can all pretty much be eliminated by just implementing our
own basic matrix ops. The only mixed ops are the very simple
ones.
At that point, we'd basically be leaning on Eigen for the linear
algebra and the basic data types, but not for the basic matrix
operations other than get/set.
> This would allow for fast (and vectorized) implementations of things
> like multiply(matrix_d,matrix_v). However, this seems likely to be a
> rather large design change and would end up being much messier than if
> Eigen just allowed (non-vectorized) mixed-type operations.
Right. But how do we get the derivatives? I think we still have
to write all of the propagation ourselves as extensions of vari_matrix
as we did for other ops by extending vari.
- Bob
On 3/12/12 12:33 PM, Marcus Brubaker wrote:
There's also all kinds of parallelism directives tied in on
However, they
are only relevant for basic numeric instructions and loosing
vectorization for the double OP vari cases doesn't really matter at this
point.
a platform-specific basis at this low level.
The code's pretty dense to work through.
It seems like the limiting factor here is really that we're defining a
var scalar type and constructing an Eigen::Matrix<var> when Eigen really
doesn't support non-basic types (except for complex) for entries.
Just to be clear. Eigen's fine with our var type, just
not with mixing var and double types.
This is the first thing I tried to design, but no luck.
One way around this is might be to define a matrix variant of the vari
class. E.G., something like:
template<int Rows, int Cols>
class vari_matrix : public chainable {
const Eigen::Matrix<double,Rows,Cols> val_;
...
}
There are several problems with this specific suggestion.
One, we almost certainly want Rows and Cols to be runtime,
not compile time.
Then how do we string together expressions like:
(x - y) * Sigma * (x - y)'
and make sure that they get evaluated with the proper expression
templates AND propagate the right derivatives? We'd have to
build up our own expressions, I think.
I wish to continue to emphasize that the savings from partially
evaluating the derivatives in the var-based ops will dwarf any
savings from getting the expression templates working within Eigen.
That only solves one of our problems, and even if we do it, makes
evaluating the derivatives actually harder, as we have to start
specializing Eigen's template expressions for Scalar = var.
The really slow part is the promotion of double to var, but
that can all pretty much be eliminated by just implementing our
own basic matrix ops. The only mixed ops are the very simple
ones.
Right. But how do we get the derivatives? I think we still have
This would allow for fast (and vectorized) implementations of things
like multiply(matrix_d,matrix_v). However, this seems likely to be a
rather large design change and would end up being much messier than if
Eigen just allowed (non-vectorized) mixed-type operations.
to write all of the propagation ourselves as extensions of vari_matrix
as we did for other ops by extending vari.
On 3/12/12 3:12 PM, Marcus Brubaker wrote:
> On Mon, Mar 12, 2012 at 1:47 PM, Bob Carpenter <ca...@alias-i.com
> <mailto:ca...@alias-i.com>> wrote:
>
>
>
> On 3/12/12 12:33 PM, Marcus Brubaker wrote:
>
> One way around this is might be to define a matrix variant of
> the vari
> class. E.G., something like:
>
> template<int Rows, int Cols>
> class vari_matrix : public chainable {
> const Eigen::Matrix<double,Rows,__Cols> val_;
> ...
> }
>
>
> This is the first thing I tried to design, but no luck.
>
> There are several problems with this specific suggestion.
> One, we almost certainly want Rows and Cols to be runtime,
> not compile time.
>
>
> If Eigen::Matrix was used as the base then Rows and Cols could be
> Dynamic right?
No, Eigen::Matrix has half a dozen or so template parameters,
the first of which is the type, and the second two of which are
rows and columns, which can either be fixed values or Dynamic.
matrix_d : Matrix<double,Dynamic,Dynamic>
vector_d : Matrix<double,Dynamic,1>
row_vector_d : Matrix<double,1,Dynamic>
When you provide 1 as a fixed column or row size, Eigen uses a
template specialization to implement all the vector operations
like dot().
We could get away with a templated Val. But it makes
matrix indexing by row/col very expensive because we'd need
a vari<double> that referenced a position in the matrix.
We could implement the vari<double> view into vari<Matrix>
as an extension of vari with a no-op chain(), but it's very
heavy in terms of alloc, virtual no-op calls during auto-dif,
and access during use.
>
> Then how do we string together expressions like:
>
> (x - y) * Sigma * (x - y)'
>
> and make sure that they get evaluated with the proper expression
> templates AND propagate the right derivatives? We'd have to
> build up our own expressions, I think.
>
>
>
> That's true. The one advantage that would have is allowing handling
> things like inverse(A) better by exploiting that dinv(A) = -inv(A) * dA
> * inv(A) as opposed to pushing them through an LU factorization and
> similarly for determinant(A)/log_determinant(A). On the whole, probably
> not worth the trouble.
We can implement our own expressions.
And it's precisiely the kind of handling of derivatives
that this is going to make very hard.
> I wish to continue to emphasize that the savings from partially
> evaluating the derivatives in the var-based ops will dwarf any
> savings from getting the expression templates working within Eigen.
> That only solves one of our problems, and even if we do it, makes
> evaluating the derivatives actually harder, as we have to start
> specializing Eigen's template expressions for Scalar = var.
>
> The really slow part is the promotion of double to var, but
> that can all pretty much be eliminated by just implementing our
> own basic matrix ops. The only mixed ops are the very simple
> ones.
>
>
> I think there are two independent motivating factors here, at least from
> my perspective. One is performance (e.g., working to avoid unneeded
> promotion to vars) and the other is simplicity of code (e.g., allowing
> us to simply write A<var> OP B<double> as opposed to reimplementing
> every op explicitly). That getting mixed types working would allow the
> templated expressions to potentially improve performance is really a bonus.
I completely agree that the syntax would be nice.
We can get rid of the promotion through better implementations
of multiply(), add(), etc.
The problem is that even if we implement every OP explicitly,
it's not enough. We have to implement all of the expression
templates created by those ops. When you multiply matrices in
Eigen, or multiply a scalar by a matrix, or add two matrices,
you get an expression template, not a matrix. And they're all
different expression templates depending on the op. It's only
when you copy or assign that you get everything written out to
a matrix. So let's say I implement:
Matrix<var,Dynamic,Dynamic>
operator*(Matrix<var,Dynamic,Dynamic>,
Matrix<double,Dynamic,Dynamic>);
Then I can write:
Matrix<var,Dynamic,Dynamic> A(3,3);
Matrix<double,Dynamic,Dynamic> B(3,3);
Matrix<var,Dynamic,Dynamic> C(3,3);
...
C = A * B; // OK
C = A.transpose() * B; // FAIL
C = (A * A) * B; // FAIL
The problem is that A.transpose() is not of type
Matrix<var,Dynamic,Dynamic>, nor is (A * A). They
each have their own expression template type that's
only related to the base type through the curiously
recursive template pattern. So you need to keep
implementing for all of these expression teplates.
This is what's going to be required to change Eigen to
support mixed operations internally.
I'm forcing a write to Matrix after every op in Stan's
matrix lib. So you could use Stan's matrix transpose() to
do
C = multiply(transpose(A),B);
or
C = multiply(multiply(A,B),C);
The cost for auto-dif is going to dwarf the savings from
expression templates anyway, which is why I did it this way.
In Stan's own language, you get the expressions back, but
they're implemented with the explicit-copy Stan implementations.
It's not impossible to do both right, but just getting the mixed
ops working is only the beginning. If they're done like complex,
we'll need way more derivative-specific stuff before they're
more efficient than the non-expression-templated way I have it now
with derivatives pushed through.
>
> This would allow for fast (and vectorized) implementations of things
> like multiply(matrix_d,matrix_v). However, this seems likely to
> be a
> rather large design change and would end up being much messier
> than if
> Eigen just allowed (non-vectorized) mixed-type operations.
>
>
> Right. But how do we get the derivatives? I think we still have
> to write all of the propagation ourselves as extensions of vari_matrix
> as we did for other ops by extending vari.
>
>
>
> Yeah, that would likely be necessary. You might be able to avoid some
> duplication by treating scalars as 1x1 matrices but it would certainly
> be a big change and not a priority right now if, as you've alluded to
> elsewhere, the goal is to get something mostly fully featured out there
> for people to start using.
But Eigen doesn't implement scalars as 1x1 matrices. I don't
know if it specializes Matrix<T,1,1> for added efficiency in the
really small case. Probably.
- Bob
implementing for all of these expression templates.
C = (A * A).eval() * B; // FAIL?
BenUsing eval() should work, assuming
internal::eval<Derived>::type
evaluates to the right kind of plain-old matrix.
A matrix constructor or static cast would also work,
but wouldn't have the clean ref passing that eval()
seems to have in case of a no-op.
This is essentially what all of our plain-old functions
in matrix are doing by assigning to an Eigen::Matrix
return type.
- Bob
On 3/12/12 11:10 PM, Ben Goodrich wrote:
Using eval() should work, assuminginternal::eval<Derived>::type
evaluates to the right kind of plain-old matrix.