Boost logo

Boost Users :

Subject: Re: [Boost-users] proto for array expressions
From: Eric Niebler (eric_at_[hidden])
Date: 2008-12-31 19:21:21


Sorry for the delay, the holidays, you know ...

Daniel Oberhoff wrote:
> On 2008-12-18 23:46:40 +0100, Eric Niebler <eric_at_[hidden]>
> said:
>
>> Daniel Oberhoff wrote:
>>>
>>> Now with proto I was wondering how hard it would be to build an et
>>> engine for array math. Basically I am interested in general tensor
>>> notation, and the resulting expressions should be iteratable
>>> efficiently. think (advanced example):
>>>
>>> a_ij = b_i * (c_j - sum(d_ijk, k = [i-1,1])
>>
>> I don't understand your notation. What are a_ij et. al.?
>
> Sorry, the underscripts where meant to be indices. So a is a 2d array, b
> and c are 1d arrays, and d is a 3d array. The statement says: to
> calculate a at a given index pair i,j (say 0,0) you substitute for i,j
> on the right. And the sum sums over the third index of the 3d array d.
> The notation further says that the bounds for the summation depend on
> what you substitute for i. So to fill the array a like that you need to
> substitute all values in the domian (say both indices run from 0 to 99
> for a 100x100 array), and for efficiency it would be best if the right
> hand side would result in an iterator that substitutes subsequent all
> values in series.

OK, the syntax for your tensor expressions needs work, but I think what
you're shooting for is certainly doable. How about a syntax like this:

     a[i][j] = b[i] * ( c[j] - sum( (d[i][j][k], k = (i-1,1)) ) )

This is a valid C++ expression, and I think there could be enough type
information in it to make it do something useful. Here is a simple
program that makes the above well-formed, and also defines a very
rudimentary the grammar for tensor expressions, with transforms that
calculate the expression's dimensionality. That's important because you
don't want to assign a 2-D array to a 1-D array, for instance.

     #include <iostream>
     #include <boost/mpl/next_prior.hpp>
     #include <boost/proto/proto.hpp>

     namespace mpl = boost::mpl;
     namespace proto = boost::proto;
     namespace fusion = boost::fusion;
     using proto::_;

     // The array implementation
     template<typename T, typename Dim>
     struct array_
     {
         typedef T value_type;
         typedef Dim dimension_type;

         friend std::ostream &operator <<(std::ostream &sout, array_)
         {
             return sout << "array<" << typeid(T).name() << "," <<
Dim::value << ">";
         }
     };

     // array<int, 1> is a 1-D array
     // array<int, 2> is a 2-D array
     // array<int, 3> is a 3-D array
     template<typename T, int Dim>
     struct array
       : proto::extends<
             typename proto::terminal<array_<T, mpl::int_<Dim> > >::type
           , array<T, Dim>
>
     {
         array() {}
     };

     template<typename Dim>
     struct index
     {
         typedef Dim dimension_type;
         friend std::ostream &operator <<(std::ostream &sout, index)
         {
             return sout << "index<" << Dim::value << ">";
         }
     };

     template<typename T>
     struct dimension_of
     {
         typedef typename T::dimension_type type;
     };

     proto::terminal<index<mpl::int_<1> > >::type const i = {{}};
     proto::terminal<index<mpl::int_<2> > >::type const j = {{}};
     proto::terminal<index<mpl::int_<3> > >::type const k = {{}};

     // The grammar for valid tensor expressions, with transforms
     // that calculate the dimension of the expression
     struct dimension
       : proto::or_<
             proto::when<
                 proto::terminal<array_<_, _> >
               , dimension_of<proto::_value>()
>
           , proto::when<
                 proto::terminal<_>
               , mpl::int_<0>()
>
           , proto::when<
                 proto::subscript<dimension, _>
               , mpl::prior<dimension(proto::_left)>()
>
           , proto::when<
                 proto::unary_expr<_, dimension>
               , dimension(proto::_child)
>
           , proto::when<
                 proto::and_<
                     proto::binary_expr<_, dimension, dimension>
                   , proto::if_<mpl::equal_to<dimension(proto::_left),
dimension(proto::_right)>()>
>
               , dimension(proto::_left)
>
>
     {};

     struct sum_
     {
         friend std::ostream &operator <<(std::ostream &sout, sum_)
         {
             return sout << "sum_";
         }
     };

     proto::terminal<sum_>::type const sum = {{}};

     template<typename Expr>
     void test_expr(Expr const &expr)
     {
         proto::display_expr(expr);
         BOOST_MPL_ASSERT((proto::matches<Expr, dimension>));
         std::cout << "dimension = " << dimension()(expr) << std::endl;
     }

     int main()
     {
         array<int, 2> a; // a 2-D array
         array<int, 1> b; // 1-D arrays ...
         array<int, 1> c;
         array<int, 3> d; // a 3-D array

         test_expr(
             a[i][j] = b[i] * ( c[j] - sum( (d[i][j][k], k = (i-1,1)) ) )
         );
     }

HTH,

-- 
Eric Niebler
BoostPro Computing
http://www.boostpro.com

Boost-users list run by williamkempf at hotmail.com, kalb at libertysoft.com, bjorn.karlsson at readsoft.com, gregod at cs.rpi.edu, wekempf at cox.net