[proto] Using proto with expressions containing matrices from the EIgen library
Hi all, I am trying to use proto for the evaluation of expressions involved in a Finite Element Method. The idea is that developers can write expressions that resemble the mathematical formulation of the problem, but that get processed efficiently to use matrix operations on fixed-size matrices. The matrix library that is used as backend is Eigen (http://eigen.tuxfamily.org). In my first attempt, I used proto::eval with a context that understands some custom terminals and functions, and delegates other stuff (like the math operators) to the proto default context. Unfortunately, this approach works only on expressions involving at most 2 matrices, since in more complex cases Eigen constructs its own nested expression templates and these may refer to their operands by reference. The operands may be temporaries, however, (as generated by a nested call to proto::eval), so the program either crashes or gives the wrong result. In a release compile, everything works, presumably because all the nested proto calls get inlined. In my second attempt, I tried to use an object transform, hoping the temporaries would persist until the evaluation happens. The code looks like this: templatetypename Left, typename Right struct MultExpr { typedef typename Eigen::ProductReturnTypeLeft, Right::Type type; }; struct EvaluateExpr : boost::proto::or_ boost::proto::when boost::proto::multipliesboost::proto::_, boost::proto::_, MultExpr EvaluateExpr(boost::proto::_left), EvaluateExpr(boost::proto::_right) (EvaluateExpr(boost::proto::_left), EvaluateExpr(boost::proto::_right)) , boost::proto::when boost::proto::_, ContextEvaluator {}; An example of the expression to evaluate: transpose(mapped_gradient(nodes)) * transpose(jacobian_adjoint(nodes)) * jacobian_adjoint(nodes) where nodes is a terminal that can be evaluated using a context. In the EvaluateExpr grammar, ContextEvaluator is a primitive transform that simply delegates evaluation to proto::eval, using the data argument as context. The context stores the matrices involved, so any references to matrices returned are safe to use. The Eigen product expression is now constructed using object transform MultExpr, but it turns out to exhibit the same problem with temporaries. Does anyone see a solution to this? At the moment I am thinking of having the EvaluateExpr transform build a fusion::cons list to store all the intermediate expressions, and refer to those by reference in subsequent expressions, but I'm not sure how to pull this off yet, or if there is a simpler way. I have also asked the question on the Eigen forum, together with a small sample that exposes the temporary variable problem without using proto: http://forum.kde.org/viewtopic.php?f=74t=91119 Kind regards, -- Bart ___ proto mailing list proto@lists.boost.org http://lists.boost.org/mailman/listinfo.cgi/proto
Re: [proto] Using proto with expressions containing matrices from the EIgen library
That's a tough one :/ Main problem is probably the fact you can't control when/Where eigen do his bidding. Best shot is to externally make eigen temporary proto terminals, write a grammar that disable operators onthem and then write a transform dealing with the composite E.T AST. ___ proto mailing list proto@lists.boost.org http://lists.boost.org/mailman/listinfo.cgi/proto
Re: [proto] Using proto with expressions containing matrices from the EIgen library
On Wed, Oct 27, 2010 at 6:57 PM, Eric Niebler e...@boostpro.com wrote: Are you saying that proto::eval is generating temporary matrices? I don't understand why, but there's always a way to get Proto to store intermediate nodes by value instead of by reference. Without knowing what you're doing, I can't say more. No, but the return value of the operator() in the eval structs in the context (in my first attempt) or the object created by the object transform is sometimes stored by reference in the Eigen expression templates. I'll give more details on what exactly I need below, based on the sample I posted to the Eigen forum. Can ContextEvaluator ever return a reference to a temporary object? Is No, never. Eigen::ProductReturnTypeLeft, Right::Type ever a reference? Does that type hold Left and Right by reference? Yes, sometimes, and that is exactly the problem :) It deduces the need for by-reference storage if it would otherwise need to do an expensive copy, so it is crucial that this system can do its job in order to preserve the performance of Eigen. Looking at that code, I strongly suspect that Eigen::ProductReturnTypeLeft, Right::Type is a type that holds Right by reference. In fact, I'm 99% sure it does. Print the name of the resulting type of Eigen::ProductReturnTypeLeft, Right::Type and see if that's the case. If so, you've found your bug. Can you see why? Yup, it took me a while to figure out, but the purpose of the sample I posted to Eigen is indeed to show this: the result of multb is stored by reference in the result of multa, and this only gets evaluated on the call to operator, so it tries to access a dangling reference to the multb result which disappeared. In proto, multb becomes operator() of an eval struct in a context, or the object constructed using an object transform. So to rephrase my question: let's say I want to evaluate proto expression _cout A * B * C. To do this, I traverse the tree and the following should happen: 1. B * C gets evaluated first, into an (Eigen) expression template that stores a reference to B and C. This is OK, since B and C are not temporaries. 2. A*(result of B*C) gets evaluated, which may produce something that stores (result of B*C) by reference 3. The final expression result is output using , and at that point the Eigen expression templates execute, expecting that all the referred variables still exist. So how can I make sure the (result of B*C) gets stored somewhere? If I can do that, then I can use this stored data to construct the A*(result of B*C) step, and it's safe even if it is done by reference. For more complex expressions, all the steps in the tree would need to be stored like this. Once the call to operator finishes, this temporary tree can be discarded. The problem I have here appears to be general to expression template matrix libraries, before Eigen we used our own matrix lib, and it exhibited the same problem. Kind regards, -- Bart ___ proto mailing list proto@lists.boost.org http://lists.boost.org/mailman/listinfo.cgi/proto
Re: [proto] Using proto with expressions containing matrices from the EIgen library
On 10/27/2010 12:57 PM, Bart Janssens wrote: So to rephrase my question: let's say I want to evaluate proto expression _cout A * B * C. To do this, I traverse the tree and the following should happen: 1. B * C gets evaluated first, into an (Eigen) expression template that stores a reference to B and C. This is OK, since B and C are not temporaries. Yes. 2. A*(result of B*C) gets evaluated, which may produce something that stores (result of B*C) by reference Yes. 3. The final expression result is output using , and at that point the Eigen expression templates execute, expecting that all the referred variables still exist. Yes. And they do because all the temporary objects that have been created live until the end of the full expression, which includes the output expression. So how can I make sure the (result of B*C) gets stored somewhere? (result of B*C) is a temporary object (X) that holds B and C by reference. A*(B*C) is a temporary object (Y) that holds the temporary object (X) by reference. This is all kosher. However, that's not what your example was doing. Your example was RETURNING the equivalent of A*(B*C) from a function. NOT GOOD. The temporary object (X) dies at the end of the full expression in which it was created. That's the return statement. By the time you try to traverse the expression to evaluate it, (X) is dead and buried. If I can do that, then I can use this stored data to construct the A*(result of B*C) step, and it's safe even if it is done by reference. You should be asking yourself why you're trying to return expression templates from a function. If you really need to do that, then you can't go returning references to temporary objects. For more complex expressions, all the steps in the tree would need to be stored like this. Once the call to operator finishes, this temporary tree can be discarded. The problem I have here appears to be general to expression template matrix libraries, before Eigen we used our own matrix lib, and it exhibited the same problem. Right. This problem has nothing at all to do with Proto. -- Eric Niebler BoostPro Computing http://www.boostpro.com ___ proto mailing list proto@lists.boost.org http://lists.boost.org/mailman/listinfo.cgi/proto