Boost.Proto:Proto变换是否有可能评估矩阵的混合表达式?矢量?

时间:2015-10-02 05:56:12

标签: c++ dsl expression-templates boost-proto

现在我正在尝试教g ++编译器线性代数,以便g ++可以重写像(matrix * vector)(index)这样的表达式作为评估表达式的循环。基本上这是我期望作为系列中的the last article的下一篇文章" Expressive C++"。最后一篇文章解释了如何制作用于添加向量的EDSL,以便我编写另一个EDSL用于将矩阵乘以向量。

但是当我自己的矩阵和向量类的Proto域名作为第一个宏参数传递时,无法编译BOOST_PROTO_DEFINE_OPERATORS宏。

所以我想知道Proto变换是否有可能评估矩阵和矢量对象的混合表达式。似乎没有可以编译的示例代码,以及Proto用户中的"Adapting Existing Types to Proto"中的示例代码'指南1.57.0是不完整的,虽然它是关于如何使现有的矩阵和矢量类型适应Proto。

我不知所措。你能给我一些建议或提示吗?

这是我的源代码。如果您建议我如何解决问题,我将非常感激:

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

namespace mpl = boost::mpl;
namespace proto = boost::proto;

namespace LinAlg {
    class Vector;
    class Matrix;

    // Functor for lazy evaluation
    struct ElmOfMatVecMult;

    // The grammar for an expression like ( matrix * vector )(index)
    struct MatVecMultElmGrammar : proto::or_<
        proto::when< proto::multiplies< Matrix, Vector>,
                    proto::_make_function( ElmOfMatVecMult,
                                            proto::_left, proto::_right,
                                            proto::_state) >
    > {};

    // The grammar for a vector expression
    // ( Now I consider just one form : matrix * vector . )
    struct VecExprGrammar : proto::or_<
        proto::when< proto::function< MatVecMultElmGrammar, proto::_>,
                    MatVecMultElmGrammar( proto::_left, proto::_right) >,
        proto::multiplies< Matrix, Vector>
    > {};

    template<typename E> struct Expr;

    // The above grammar is associated with this domain.
    struct Domain
        : proto::domain<proto::generator<Expr>, VecExprGrammar>
    {};

    // A wrapper template for linear algebraic expressions
    // including matrices and vectors
    template<typename ExprType>
    struct Expr
        : proto::extends<ExprType, Expr<ExprType>, Domain>
    {
        explicit Expr(const ExprType& e)
            : proto::extends<ExprType, Expr<ExprType>, Domain>(e)
        {}
    };

    // Testing if data in an heap array can be a vector object
    class Vector {
        private:
            int sz;
            double* data;

    public:
        template <typename Sig> struct result;

        template <typename This, typename T>
        struct result< This(T) > { typedef double type; };

        typedef double result_type;

        explicit Vector(int sz_ = 1, double iniVal = 0.0) :
            sz( sz_), data( new double[sz] ) {
            for (int i = 0; i < sz; i++) data[i] = iniVal;
            std::cout << "Created" << std::endl;
        }
        Vector(const Vector& vec) :
            sz( vec.sz), data( new double[sz] ) {
            for (int i = 0; i < sz; i++) data[i] = vec.data[i];
            std::cout << "Copied! " << std::endl;
        }

        ~Vector() {
            delete [] data;
            std::cout << "Deleted" << std::endl;
        }

        // accessing to an element of this vector
        double& operator()(int i) { return data[i]; }
        const double& operator()(int i) const { return data[i]; }
    };


    // Testing if data in an heap array can be a matrix object
    class Matrix
    {
    private:
        int rowSz, colSz;
        double* data;
        double** m;

    public:
        template <typename Signature> struct result;

        template <typename This, typename T>
        struct result< This(T,T) > { typedef double type; };

        explicit Matrix(int rowSize = 1, int columnSize =1,
                    double iniVal = 0.0) :
            rowSz( rowSize), colSz(columnSize),
            data( new double[rowSz*colSz] ), m( new double*[rowSz])
        {
            for (int i = 0; i < rowSz; i++) m[i] = data + i*colSz;
            for (int ri = 0; ri < rowSz; ri++)
                for (int ci = 0; ci < colSz; ci++) m[ri][ci] = iniVal;
            std::cout << "Created" << std::endl;
        }

        Matrix(const Matrix& mat) :
            rowSz( mat.rowSz), colSz( mat.colSz),
            data( new double[rowSz*colSz] ), m( new double*[rowSz])
        {
            for (int i = 0; i < rowSz; i++) m[i] = data + i*colSz;
                for (int ri = 0; ri < rowSz; ri++)
                    for (int ci = 0; ci < colSz; ci++)
                        m[ri][ci] = mat.m[ri][ci];
                std::cout << "Copied! " << std::endl;
        }

        ~Matrix()
        {
            delete [] m;
            delete [] data;
            std::cout << "Deleted" << std::endl;
        }

        int rowSize() const { return rowSz; }
        int columnSize() const { return colSz; }

        // accesing to a vector element
        double& operator()(int ri, int ci) { return m[ri][ci]; }
        const double& operator()(int ri, int ci) const { return m[ri][ci]; }
    };

    // An expression like ( matrix * vector )(index) is transformed
    // into the loop for calculating the dot product between
    // the vector and matrix.
    struct ElmOfMatVecMult
    {
        double operator()( Matrix const& mat, Vector const& vec,
                        int index) const
        {
            double elm = 0.0;
            for (int ci =0;  ci < mat.columnSize(); ci++)
                elm += mat(index, ci) * vec(ci);
            return elm;
        }
    };

    // Define a trait for detecting linear algebraic terminals, to be used
    // by the BOOST_PROTO_DEFINE_OPERATORS macro below.
    template<typename> struct IsExpr  : mpl::false_ {};
    template<> struct IsExpr< Vector> : mpl::true_  {};
    template<> struct IsExpr< Matrix> : mpl::true_  {};

    // This defines all the overloads to make expressions involving
    // Vector and Matrix objects to build Proto's expression templates.
    BOOST_PROTO_DEFINE_OPERATORS(IsExpr, Domain)
}

int main()
{
    using namespace LinAlg;

    proto::_default<> trans;

    Matrix mat( 3, 3);
    Vector vec1(3);

    mat(0,0) = 1.00; mat(0,1) = 1.01; mat(0,2) = 1.02;
    mat(1,0) = 1.10; mat(1,1) = 1.11; mat(1,2) = 1.12;
    mat(2,0) = 1.20; mat(2,1) = 1.21; mat(2,2) = 1.22;

    vec1(0) = 1.0;
    vec1(1) = 2.0;
    vec1(2) = 3.0;

    proto::display_expr( ( mat * vec1)(2) );
    proto::display_expr( VecExprGrammar()( ( mat * vec1)(2) );
    double vecElm = trans( VecExprGrammar()( ( mat * vec1)(2) );

    return 0;
}

1 个答案:

答案 0 :(得分:1)

我以某种方式确认我的问题的答案是'是'。让我的源代码工作的技巧是定义一个活动语法,用于将{* 1}}对象替换为矩阵*向量,并将MatVecMult定义为MatVecMult对象,以便创建一个惰性求值函数,proto::callable。当使用LazyMatVecMult调用此LazyMatVecMult实例时,它会计算矩阵的索引行和向量的点积。

(很抱歉我自己回答了我的问题。但我认为我并不是唯一一个偶然发现如何为矢量和矩阵表达式制作基于Proto的EDSL的人。)

此源代码已确认可与g ++ 4.8.4一起使用:

operator()(int index)