From b6f5ee242c99f4e1dab2f9c406188b3ca33380a5 Mon Sep 17 00:00:00 2001 From: Junyuan Xie Date: Sun, 13 Dec 2015 13:06:23 -0800 Subject: [PATCH 1/3] tensordot --- mshadow/extension.h | 2 + mshadow/extension/tensor_dot.h | 109 ++++++++++++++++++++++++++++ mshadow/extension/tensor_dot_grad.h | 104 ++++++++++++++++++++++++++ 3 files changed, 215 insertions(+) create mode 100644 mshadow/extension/tensor_dot.h create mode 100644 mshadow/extension/tensor_dot_grad.h diff --git a/mshadow/extension.h b/mshadow/extension.h index 7f821068834d..e45bc83856f1 100644 --- a/mshadow/extension.h +++ b/mshadow/extension.h @@ -28,5 +28,7 @@ #include "./extension/slice.h" #include "./extension/take.h" #include "./extension/take_grad.h" +#include "./extension/tensor_dot.h" +#include "./extension/tensor_dot_grad.h" #include "./extension/spatial_upsampling_nearest.h" #endif // MSHADOW_EXTENSION_H_ diff --git a/mshadow/extension/tensor_dot.h b/mshadow/extension/tensor_dot.h new file mode 100644 index 000000000000..b0418f87206f --- /dev/null +++ b/mshadow/extension/tensor_dot.h @@ -0,0 +1,109 @@ +/*! + * Copyright (c) 2015 by Contributors + * \file tensor_dot.h + * \brief + * \author Junyuan Xie +*/ +#ifndef MSHADOW_EXTENSION_TENSOR_DOT_H_ +#define MSHADOW_EXTENSION_TENSOR_DOT_H_ + +#include "../extension.h" + +namespace mshadow { +namespace expr { + +/*! \brief Do dot product long the second dimension of two + * 3 dimensional tensors. Output a dimensional tensor + * \tparam LhsExp type of left expression + * \tparam RhsExp type of right expression + * \tparam DType data type + */ +template +struct TensorDotExp: public Exp, + DType, type::kChainer> { + /*! \brief left oprand */ + const LhsExp &lhs_; + /*! \brief right oprand */ + const RhsExp &rhs_; + /*! \brief shape of output*/ + Shape<2> shape_; + /*! \brief size of middle dimension */ + index_t size_; + /*! constructor */ + TensorDotExp(const LhsExp &lhs, const RhsExp &rhs) + : lhs_(lhs), rhs_(rhs) { + Shape<3> lshape = ShapeCheck<3, LhsExp>::Check(lhs_); + Shape<3> rshape = ShapeCheck<3, RhsExp>::Check(rhs_); + CHECK_EQ(lshape, rshape) << "Shape of two oprand must be the same."; + this->shape_ = Shape2(lshape[0], lshape[2]); + this->size_ = lshape[1]; + } +}; // struct TensorDotExp + +/*! + * \brief pooling subregion results together + * \param lhs left oprand + * \param rhs right oprand + * \tparam LhsExp left expression + * \tparam RhsExp right expression + * \tparam DType the content data type + */ +template +inline TensorDotExp +tensor_dot(const Exp &lhs, + const Exp &rhs) { + TypeCheckPass::kDim == 3> + ::Error_Expression_Does_Not_Meet_Dimension_Req(); + TypeCheckPass::kDim == 3> + ::Error_Expression_Does_Not_Meet_Dimension_Req(); + return TensorDotExp(lhs.self(), rhs.self()); +} +//---------------------- +// Execution plan +//---------------------- +template +struct Plan, DType> { + public: + explicit Plan(const TensorDotExp &e) + : lhs_(MakePlan(e.lhs_)), rhs_(MakePlan(e.rhs_)), size_(e.size_) {} + MSHADOW_XINLINE DType Eval(index_t i, index_t j) const { + DType res = 0; + for (index_t k = 0; k < size_; ++k) { + res += lhs_.Eval(i+k*size_, j) * + rhs_.Eval(i+k*size_, j); + } + return res; + } + + private: + Plan lhs_; + Plan rhs_; + const index_t size_; +}; + +template +inline Plan, DType> +MakePlan(const TensorDotExp &exp) { + return Plan, DType>(exp); +} + +template +struct ShapeCheck > { + inline static Shape + Check(const TensorDotExp &t) { + CHECK(dim == 2) + << "TakeExp only support 2D output"; + ShapeCheck<3, LhsExp>::Check(t.lhs_); + ShapeCheck<3, RhsExp>::Check(t.rhs_); + return t.shape_; + } +}; + +template +struct ExpInfo > { + static const int kDim = 2; + static const int kDevMask = ExpInfo::kDevMask; +}; +} //namespace expr +} //namespace mshadow +#endif // MSHADOW_EXTENSION_TENSOR_DOT_H_ diff --git a/mshadow/extension/tensor_dot_grad.h b/mshadow/extension/tensor_dot_grad.h new file mode 100644 index 000000000000..7f28c89f13c1 --- /dev/null +++ b/mshadow/extension/tensor_dot_grad.h @@ -0,0 +1,104 @@ +/*! + * Copyright (c) 2015 by Contributors + * \file tensor_dot.h + * \brief + * \author Junyuan Xie +*/ +#ifndef MSHADOW_EXTENSION_TENSOR_DOT_GRAD_H_ +#define MSHADOW_EXTENSION_TENSOR_DOT_GRAD_H_ + +#include "../extension.h" + +namespace mshadow { +namespace expr { + +/*! \brief Backward for tensor dot + * \tparam DataExp type of left expression + * \tparam TopExp type of right expression + * \tparam DType data type + */ +template +struct TensorDotGradExp: public Exp, + DType, type::kChainer> { + /*! \brief data oprand */ + const DataExp &data_; + /*! \brief top grad oprand */ + const TopExp &top_; + /*! \brief shape of output */ + Shape<3> shape_; + /*! \brief size of middle dimension */ + index_t size_; + /*! constructor */ + TensorDotGradExp(const DataExp &data, const TopExp &top) + : data_(data), top_(top) { + Shape<3> dshape = ShapeCheck<3, DataExp>::Check(data_); + Shape<2> tshape = ShapeCheck<2, TopExp>::Check(top_); + CHECK_EQ(dshape[0], tshape[0]) << "Shape of two oprand doesn't match"; + CHECK_EQ(dshape[2], tshape[1]) << "Shape of two oprand doesn't match"; + this->shape_ = dshape; + this->size_ = dshape[1]; + } +}; // struct TensorDotGradExp + +/*! + * \brief pooling subregion results together + * \param data data oprand + * \param top top grad oprand + * \tparam DataExp left expression + * \tparam TopExp right expression + * \tparam DType the content data type + */ +template +inline TensorDotGradExp +tensor_dot_grad(const Exp &data, + const Exp &top) { + TypeCheckPass::kDim == 3> + ::Error_Expression_Does_Not_Meet_Dimension_Req(); + TypeCheckPass::kDim == 2> + ::Error_Expression_Does_Not_Meet_Dimension_Req(); + return TensorDotGradExp(data.self(), top.self()); +} +//---------------------- +// Execution plan +//---------------------- +template +struct Plan, DType> { + public: + explicit Plan(const TensorDotGradExp &e) + : data_(MakePlan(e.data_)), top_(MakePlan(e.top_)), size_(e.size_) {} + MSHADOW_XINLINE DType Eval(index_t i, index_t j) const { + return data_.Eval(i, j) * top_.Eval(i/size_, j); + } + + private: + Plan data_; + Plan top_; + const index_t size_; +}; + +template +inline Plan, DType> +MakePlan(const TensorDotGradExp &exp) { + return Plan, DType>(exp); +} + +template +struct ShapeCheck > { + inline static Shape + Check(const TensorDotGradExp &t) { + CHECK(dim == 3) + << "TakeExp only support 3D output"; + ShapeCheck<3, DataExp>::Check(t.data_); + ShapeCheck<2, TopExp>::Check(t.top_); + return t.shape_; + } +}; + +template +struct ExpInfo > { + static const int kDim = 3; + static const int kDevMask = ExpInfo::kDevMask; +}; +} //namespace expr +} //namespace mshadow +#endif // MSHADOW_EXTENSION_TENSOR_DOT_GRAD_H_ From 697d5b28c4eae7506c921c9fe896e458a8be4ad4 Mon Sep 17 00:00:00 2001 From: Junyuan Xie Date: Sun, 13 Dec 2015 15:42:04 -0800 Subject: [PATCH 2/3] broadcast and reduce with axis --- mshadow/extension.h | 4 +- mshadow/extension/broadcast_with_axis.h | 89 +++++++++++++++++++ mshadow/extension/reduce_with_axis.h | 94 ++++++++++++++++++++ mshadow/extension/tensor_dot.h | 109 ------------------------ mshadow/extension/tensor_dot_grad.h | 104 ---------------------- 5 files changed, 185 insertions(+), 215 deletions(-) create mode 100644 mshadow/extension/broadcast_with_axis.h create mode 100644 mshadow/extension/reduce_with_axis.h delete mode 100644 mshadow/extension/tensor_dot.h delete mode 100644 mshadow/extension/tensor_dot_grad.h diff --git a/mshadow/extension.h b/mshadow/extension.h index e45bc83856f1..01e11f1ba9c0 100644 --- a/mshadow/extension.h +++ b/mshadow/extension.h @@ -28,7 +28,7 @@ #include "./extension/slice.h" #include "./extension/take.h" #include "./extension/take_grad.h" -#include "./extension/tensor_dot.h" -#include "./extension/tensor_dot_grad.h" +#include "./extension/reduce_with_axis.h" +#include "./extension/broadcast_with_axis.h" #include "./extension/spatial_upsampling_nearest.h" #endif // MSHADOW_EXTENSION_H_ diff --git a/mshadow/extension/broadcast_with_axis.h b/mshadow/extension/broadcast_with_axis.h new file mode 100644 index 000000000000..88359844bf32 --- /dev/null +++ b/mshadow/extension/broadcast_with_axis.h @@ -0,0 +1,89 @@ +/*! + * Copyright (c) 2015 by Contributors + * \file tensor_dot.h + * \brief + * \author Junyuan Xie +*/ +#ifndef MSHADOW_EXTENSION_BROADCAST_WITH_AXIS_H_ +#define MSHADOW_EXTENSION_BROADCAST_WITH_AXIS_H_ + +#include "../extension.h" + +namespace mshadow { +namespace expr { + +/*! \brief Backward for tensor dot + * \tparam DataExp type of left expression + * \tparam TopExp type of right expression + * \tparam DType data type + */ +template +struct BroadcastWithAxisExp: + public MakeTensorExp, + SrcExp, srcdim+1, DType> { + /*! \brief data oprand */ + const SrcExp &src_; + /*! \brief size of middle dimension */ + index_t leading_; + /*! \brief size of middle dimension */ + index_t trailing_; + /*! \brief size of middle dimension */ + index_t size_; + /*! \brief size of middle dimension */ + index_t last_; + /*! constructor */ + BroadcastWithAxisExp(const SrcExp &src, const index_t size) + : src_(src), size_(size) { + CHECK(srcdim > axis) << "broadcast axis out of bound"; + Shape src_shape = ShapeCheck::Check(src_); + this->leading_ = 1; + for (index_t i = 0; i <= axis; ++i) { + this->leading_ *= src_shape[i]; + this->shape_[i] = src_shape[i]; + } + this->shape_[axis+1] = size_; + this->trailing_ = 1; + for (index_t i = axis+1; i < srcdim; ++i) { + this->trailing_ *= src_shape[i]; + this->shape_[i+1] = src_shape[i]; + } + this->last_ = src_shape[srcdim-1]; + } +}; // struct BroadcastWithAxisExp + +/*! + * \brief pooling subregion results together + * \param data data oprand + * \param top top grad oprand + * \tparam DataExp left expression + * \tparam TopExp right expression + * \tparam DType the content data type + */ +template +inline BroadcastWithAxisExp::kDim> +broadcast_with_axis(const Exp &src, const index_t size) { + return BroadcastWithAxisExp::kDim>(src.self(), size); +} +//---------------------- +// Execution plan +//---------------------- +template +struct Plan, DType> { + public: + explicit Plan(const BroadcastWithAxisExp &e) + : src_(MakePlan(e.src_)), leading_(e.leading_), + trailing_(e.trailing_), size_(e.size_), last_(e.last_) {} + MSHADOW_XINLINE DType Eval(index_t i, index_t j) const { + index_t x = (i*last_+j)/trailing_/size_; + index_t y = (i*last_+j)%trailing_; + index_t z = x*trailing_ + y; + return src_.Eval(z/last_, z%last_); + } + + private: + Plan src_; + const index_t leading_, trailing_, size_, last_; +}; +} //namespace expr +} //namespace mshadow +#endif // MSHADOW_EXTENSION_BROADCAST_WITH_AXIS_H_ diff --git a/mshadow/extension/reduce_with_axis.h b/mshadow/extension/reduce_with_axis.h new file mode 100644 index 000000000000..fcca083c6fd8 --- /dev/null +++ b/mshadow/extension/reduce_with_axis.h @@ -0,0 +1,94 @@ +/*! + * Copyright (c) 2015 by Contributors + * \file reduce_with_axis.h + * \brief + * \author Junyuan Xie +*/ +#ifndef MSHADOW_EXTENSION_REDUCE_WITH_AXIS_H_ +#define MSHADOW_EXTENSION_REDUCE_WITH_AXIS_H_ + +#include "../extension.h" + +namespace mshadow { +namespace expr { + +/*! \brief reduce out the dimension of src labeled by axis. + * \tparam Reducer type of reducer + * \tparam SrcExp type of source expression + * \tparam DType data type + */ +template +struct ReduceWithAxisExp: + public MakeTensorExp, + SrcExp, srcdim-1, DType> { + /*! \brief source oprand */ + const SrcExp &src_; + /*! \brief size of leading dimensions */ + index_t leading_; + /*! \brief size of trailing dimensions */ + index_t trailing_; + /*! \brief size of axis dimension */ + index_t size_; + /*! \brief size of last src dimension */ + index_t last_; + /*! constructor */ + ReduceWithAxisExp(const SrcExp &src) + : src_(src) { + CHECK(srcdim > axis) << "reduce axis out of bound"; + Shape src_shape = ShapeCheck::Check(src_); + this->leading_ = 1; + for (index_t i = 0; i < axis; ++i) { + this->leading_ *= src_shape[i]; + this->shape_[i] = src_shape[i]; + } + this->size_ = src_shape[axis]; + this->trailing_ = 1; + for (index_t i = axis + 1; i < srcdim; ++i) { + this->trailing_ *= src_shape[i]; + this->shape_[i-1] = src_shape[i]; + } + this->last_ = src_shape[srcdim-1]; + } +}; // struct ReduceWithAxisExp + +/*! + * \brief pooling subregion results together + * \param lhs left oprand + * \param rhs right oprand + * \tparam LhsExp left expression + * \tparam RhsExp right expression + * \tparam DType the content data type + */ +template +inline ReduceWithAxisExp::kDim> +reduce_with_axis(const Exp &src) { + return ReduceWithAxisExp::kDim>(src.self()); +} +//---------------------- +// Execution plan +//---------------------- +template +struct Plan, DType> { + public: + explicit Plan(const ReduceWithAxisExp &e) + : src_(MakePlan(e.src_)), leading_(e.leading_), trailing_(e.trailing_), + size_(e.size_), last_(e.last_) {} + MSHADOW_XINLINE DType Eval(index_t i, index_t j) const { + index_t x = (i*last_ + j)/trailing_; + index_t y = (i*last_ + j)%trailing_; + + DType res; Reducer::SetInitValue(res); + for (index_t k = 0; k < size_; ++k) { + index_t z = (x*size_+k)*trailing_+y; + Reducer::Reduce(res, src_.Eval(z/last_, z%last_)); + } + return res; + } + + private: + Plan src_; + const index_t leading_, trailing_, size_, last_; +}; +} //namespace expr +} //namespace mshadow +#endif // MSHADOW_EXTENSION_REDUCE_WITH_AXIS_H_ diff --git a/mshadow/extension/tensor_dot.h b/mshadow/extension/tensor_dot.h deleted file mode 100644 index b0418f87206f..000000000000 --- a/mshadow/extension/tensor_dot.h +++ /dev/null @@ -1,109 +0,0 @@ -/*! - * Copyright (c) 2015 by Contributors - * \file tensor_dot.h - * \brief - * \author Junyuan Xie -*/ -#ifndef MSHADOW_EXTENSION_TENSOR_DOT_H_ -#define MSHADOW_EXTENSION_TENSOR_DOT_H_ - -#include "../extension.h" - -namespace mshadow { -namespace expr { - -/*! \brief Do dot product long the second dimension of two - * 3 dimensional tensors. Output a dimensional tensor - * \tparam LhsExp type of left expression - * \tparam RhsExp type of right expression - * \tparam DType data type - */ -template -struct TensorDotExp: public Exp, - DType, type::kChainer> { - /*! \brief left oprand */ - const LhsExp &lhs_; - /*! \brief right oprand */ - const RhsExp &rhs_; - /*! \brief shape of output*/ - Shape<2> shape_; - /*! \brief size of middle dimension */ - index_t size_; - /*! constructor */ - TensorDotExp(const LhsExp &lhs, const RhsExp &rhs) - : lhs_(lhs), rhs_(rhs) { - Shape<3> lshape = ShapeCheck<3, LhsExp>::Check(lhs_); - Shape<3> rshape = ShapeCheck<3, RhsExp>::Check(rhs_); - CHECK_EQ(lshape, rshape) << "Shape of two oprand must be the same."; - this->shape_ = Shape2(lshape[0], lshape[2]); - this->size_ = lshape[1]; - } -}; // struct TensorDotExp - -/*! - * \brief pooling subregion results together - * \param lhs left oprand - * \param rhs right oprand - * \tparam LhsExp left expression - * \tparam RhsExp right expression - * \tparam DType the content data type - */ -template -inline TensorDotExp -tensor_dot(const Exp &lhs, - const Exp &rhs) { - TypeCheckPass::kDim == 3> - ::Error_Expression_Does_Not_Meet_Dimension_Req(); - TypeCheckPass::kDim == 3> - ::Error_Expression_Does_Not_Meet_Dimension_Req(); - return TensorDotExp(lhs.self(), rhs.self()); -} -//---------------------- -// Execution plan -//---------------------- -template -struct Plan, DType> { - public: - explicit Plan(const TensorDotExp &e) - : lhs_(MakePlan(e.lhs_)), rhs_(MakePlan(e.rhs_)), size_(e.size_) {} - MSHADOW_XINLINE DType Eval(index_t i, index_t j) const { - DType res = 0; - for (index_t k = 0; k < size_; ++k) { - res += lhs_.Eval(i+k*size_, j) * - rhs_.Eval(i+k*size_, j); - } - return res; - } - - private: - Plan lhs_; - Plan rhs_; - const index_t size_; -}; - -template -inline Plan, DType> -MakePlan(const TensorDotExp &exp) { - return Plan, DType>(exp); -} - -template -struct ShapeCheck > { - inline static Shape - Check(const TensorDotExp &t) { - CHECK(dim == 2) - << "TakeExp only support 2D output"; - ShapeCheck<3, LhsExp>::Check(t.lhs_); - ShapeCheck<3, RhsExp>::Check(t.rhs_); - return t.shape_; - } -}; - -template -struct ExpInfo > { - static const int kDim = 2; - static const int kDevMask = ExpInfo::kDevMask; -}; -} //namespace expr -} //namespace mshadow -#endif // MSHADOW_EXTENSION_TENSOR_DOT_H_ diff --git a/mshadow/extension/tensor_dot_grad.h b/mshadow/extension/tensor_dot_grad.h deleted file mode 100644 index 7f28c89f13c1..000000000000 --- a/mshadow/extension/tensor_dot_grad.h +++ /dev/null @@ -1,104 +0,0 @@ -/*! - * Copyright (c) 2015 by Contributors - * \file tensor_dot.h - * \brief - * \author Junyuan Xie -*/ -#ifndef MSHADOW_EXTENSION_TENSOR_DOT_GRAD_H_ -#define MSHADOW_EXTENSION_TENSOR_DOT_GRAD_H_ - -#include "../extension.h" - -namespace mshadow { -namespace expr { - -/*! \brief Backward for tensor dot - * \tparam DataExp type of left expression - * \tparam TopExp type of right expression - * \tparam DType data type - */ -template -struct TensorDotGradExp: public Exp, - DType, type::kChainer> { - /*! \brief data oprand */ - const DataExp &data_; - /*! \brief top grad oprand */ - const TopExp &top_; - /*! \brief shape of output */ - Shape<3> shape_; - /*! \brief size of middle dimension */ - index_t size_; - /*! constructor */ - TensorDotGradExp(const DataExp &data, const TopExp &top) - : data_(data), top_(top) { - Shape<3> dshape = ShapeCheck<3, DataExp>::Check(data_); - Shape<2> tshape = ShapeCheck<2, TopExp>::Check(top_); - CHECK_EQ(dshape[0], tshape[0]) << "Shape of two oprand doesn't match"; - CHECK_EQ(dshape[2], tshape[1]) << "Shape of two oprand doesn't match"; - this->shape_ = dshape; - this->size_ = dshape[1]; - } -}; // struct TensorDotGradExp - -/*! - * \brief pooling subregion results together - * \param data data oprand - * \param top top grad oprand - * \tparam DataExp left expression - * \tparam TopExp right expression - * \tparam DType the content data type - */ -template -inline TensorDotGradExp -tensor_dot_grad(const Exp &data, - const Exp &top) { - TypeCheckPass::kDim == 3> - ::Error_Expression_Does_Not_Meet_Dimension_Req(); - TypeCheckPass::kDim == 2> - ::Error_Expression_Does_Not_Meet_Dimension_Req(); - return TensorDotGradExp(data.self(), top.self()); -} -//---------------------- -// Execution plan -//---------------------- -template -struct Plan, DType> { - public: - explicit Plan(const TensorDotGradExp &e) - : data_(MakePlan(e.data_)), top_(MakePlan(e.top_)), size_(e.size_) {} - MSHADOW_XINLINE DType Eval(index_t i, index_t j) const { - return data_.Eval(i, j) * top_.Eval(i/size_, j); - } - - private: - Plan data_; - Plan top_; - const index_t size_; -}; - -template -inline Plan, DType> -MakePlan(const TensorDotGradExp &exp) { - return Plan, DType>(exp); -} - -template -struct ShapeCheck > { - inline static Shape - Check(const TensorDotGradExp &t) { - CHECK(dim == 3) - << "TakeExp only support 3D output"; - ShapeCheck<3, DataExp>::Check(t.data_); - ShapeCheck<2, TopExp>::Check(t.top_); - return t.shape_; - } -}; - -template -struct ExpInfo > { - static const int kDim = 3; - static const int kDevMask = ExpInfo::kDevMask; -}; -} //namespace expr -} //namespace mshadow -#endif // MSHADOW_EXTENSION_TENSOR_DOT_GRAD_H_ From 629d09ff93232f73dbd58d71eba55b24830cfc1e Mon Sep 17 00:00:00 2001 From: Junyuan Xie Date: Sun, 13 Dec 2015 15:53:08 -0800 Subject: [PATCH 3/3] lint --- mshadow/extension/broadcast_with_axis.h | 4 ++-- mshadow/extension/reduce_with_axis.h | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/mshadow/extension/broadcast_with_axis.h b/mshadow/extension/broadcast_with_axis.h index 88359844bf32..c10b907d2efe 100644 --- a/mshadow/extension/broadcast_with_axis.h +++ b/mshadow/extension/broadcast_with_axis.h @@ -84,6 +84,6 @@ struct Plan, DType> { Plan src_; const index_t leading_, trailing_, size_, last_; }; -} //namespace expr -} //namespace mshadow +} // namespace expr +} // namespace mshadow #endif // MSHADOW_EXTENSION_BROADCAST_WITH_AXIS_H_ diff --git a/mshadow/extension/reduce_with_axis.h b/mshadow/extension/reduce_with_axis.h index fcca083c6fd8..b1c090ea7fa0 100644 --- a/mshadow/extension/reduce_with_axis.h +++ b/mshadow/extension/reduce_with_axis.h @@ -32,7 +32,7 @@ struct ReduceWithAxisExp: /*! \brief size of last src dimension */ index_t last_; /*! constructor */ - ReduceWithAxisExp(const SrcExp &src) + explicit ReduceWithAxisExp(const SrcExp &src) : src_(src) { CHECK(srcdim > axis) << "reduce axis out of bound"; Shape src_shape = ShapeCheck::Check(src_); @@ -89,6 +89,6 @@ struct Plan, DType> { Plan src_; const index_t leading_, trailing_, size_, last_; }; -} //namespace expr -} //namespace mshadow +} // namespace expr +} // namespace mshadow #endif // MSHADOW_EXTENSION_REDUCE_WITH_AXIS_H_