TensorPadding.h
00001 // This file is part of Eigen, a lightweight C++ template library
00002 // for linear algebra.
00003 //
00004 // Copyright (C) 2014 Benoit Steiner <benoit.steiner.goog@gmail.com>
00005 //
00006 // This Source Code Form is subject to the terms of the Mozilla
00007 // Public License v. 2.0. If a copy of the MPL was not distributed
00008 // with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
00009 
00010 #ifndef EIGEN_CXX11_TENSOR_TENSOR_PADDING_H
00011 #define EIGEN_CXX11_TENSOR_TENSOR_PADDING_H
00012 
00013 namespace Eigen {
00014 
00022 namespace internal {
00023 template<typename PaddingDimensions, typename XprType>
00024 struct traits<TensorPaddingOp<PaddingDimensions, XprType> > : public traits<XprType>
00025 {
00026   typedef typename XprType::Scalar Scalar;
00027   typedef traits<XprType> XprTraits;
00028   typedef typename XprTraits::StorageKind StorageKind;
00029   typedef typename XprTraits::Index Index;
00030   typedef typename XprType::Nested Nested;
00031   typedef typename remove_reference<Nested>::type _Nested;
00032   static const int NumDimensions = XprTraits::NumDimensions;
00033   static const int Layout = XprTraits::Layout;
00034 };
00035 
00036 template<typename PaddingDimensions, typename XprType>
00037 struct eval<TensorPaddingOp<PaddingDimensions, XprType>, Eigen::Dense>
00038 {
00039   typedef const TensorPaddingOp<PaddingDimensions, XprType>& type;
00040 };
00041 
00042 template<typename PaddingDimensions, typename XprType>
00043 struct nested<TensorPaddingOp<PaddingDimensions, XprType>, 1, typename eval<TensorPaddingOp<PaddingDimensions, XprType> >::type>
00044 {
00045   typedef TensorPaddingOp<PaddingDimensions, XprType> type;
00046 };
00047 
00048 }  // end namespace internal
00049 
00050 
00051 
00052 template<typename PaddingDimensions, typename XprType>
00053 class TensorPaddingOp : public TensorBase<TensorPaddingOp<PaddingDimensions, XprType>, ReadOnlyAccessors>
00054 {
00055   public:
00056   typedef typename Eigen::internal::traits<TensorPaddingOp>::Scalar Scalar;
00057   typedef typename Eigen::NumTraits<Scalar>::Real RealScalar;
00058   typedef typename XprType::CoeffReturnType CoeffReturnType;
00059   typedef typename Eigen::internal::nested<TensorPaddingOp>::type Nested;
00060   typedef typename Eigen::internal::traits<TensorPaddingOp>::StorageKind StorageKind;
00061   typedef typename Eigen::internal::traits<TensorPaddingOp>::Index Index;
00062 
00063   EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE TensorPaddingOp(const XprType& expr, const PaddingDimensions& padding_dims, const Scalar padding_value)
00064       : m_xpr(expr), m_padding_dims(padding_dims), m_padding_value(padding_value) {}
00065 
00066     EIGEN_DEVICE_FUNC
00067     const PaddingDimensions& padding() const { return m_padding_dims; }
00068     EIGEN_DEVICE_FUNC
00069     Scalar padding_value() const { return m_padding_value; }
00070 
00071     EIGEN_DEVICE_FUNC
00072     const typename internal::remove_all<typename XprType::Nested>::type&
00073     expression() const { return m_xpr; }
00074 
00075   protected:
00076     typename XprType::Nested m_xpr;
00077     const PaddingDimensions m_padding_dims;
00078     const Scalar m_padding_value;
00079 };
00080 
00081 
00082 // Eval as rvalue
00083 template<typename PaddingDimensions, typename ArgType, typename Device>
00084 struct TensorEvaluator<const TensorPaddingOp<PaddingDimensions, ArgType>, Device>
00085 {
00086   typedef TensorPaddingOp<PaddingDimensions, ArgType> XprType;
00087   typedef typename XprType::Index Index;
00088   static const int NumDims = internal::array_size<PaddingDimensions>::value;
00089   typedef DSizes<Index, NumDims> Dimensions;
00090   typedef typename XprType::Scalar Scalar;
00091   typedef typename XprType::CoeffReturnType CoeffReturnType;
00092   typedef typename PacketType<CoeffReturnType, Device>::type PacketReturnType;
00093   static const int PacketSize = internal::unpacket_traits<PacketReturnType>::size;
00094 
00095   enum {
00096     IsAligned = true,
00097     PacketAccess = TensorEvaluator<ArgType, Device>::PacketAccess,
00098     Layout = TensorEvaluator<ArgType, Device>::Layout,
00099     CoordAccess = true,
00100     RawAccess = false
00101   };
00102 
00103   EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE TensorEvaluator(const XprType& op, const Device& device)
00104       : m_impl(op.expression(), device), m_padding(op.padding()), m_paddingValue(op.padding_value())
00105   {
00106     // The padding op doesn't change the rank of the tensor. Directly padding a scalar would lead
00107     // to a vector, which doesn't make sense. Instead one should reshape the scalar into a vector
00108     // of 1 element first and then pad.
00109     EIGEN_STATIC_ASSERT((NumDims > 0), YOU_MADE_A_PROGRAMMING_MISTAKE);
00110 
00111     // Compute dimensions
00112     m_dimensions = m_impl.dimensions();
00113     for (int i = 0; i < NumDims; ++i) {
00114       m_dimensions[i] += m_padding[i].first + m_padding[i].second;
00115     }
00116     const typename TensorEvaluator<ArgType, Device>::Dimensions& input_dims = m_impl.dimensions();
00117     if (static_cast<int>(Layout) == static_cast<int>(ColMajor)) {
00118       m_inputStrides[0] = 1;
00119       m_outputStrides[0] = 1;
00120       for (int i = 1; i < NumDims; ++i) {
00121         m_inputStrides[i] = m_inputStrides[i-1] * input_dims[i-1];
00122         m_outputStrides[i] = m_outputStrides[i-1] * m_dimensions[i-1];
00123       }
00124       m_outputStrides[NumDims] = m_outputStrides[NumDims-1] * m_dimensions[NumDims-1];
00125     } else {
00126       m_inputStrides[NumDims - 1] = 1;
00127       m_outputStrides[NumDims] = 1;
00128       for (int i = NumDims - 2; i >= 0; --i) {
00129         m_inputStrides[i] = m_inputStrides[i+1] * input_dims[i+1];
00130         m_outputStrides[i+1] = m_outputStrides[i+2] * m_dimensions[i+1];
00131       }
00132       m_outputStrides[0] = m_outputStrides[1] * m_dimensions[0];
00133     }
00134   }
00135 
00136   EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE const Dimensions& dimensions() const { return m_dimensions; }
00137 
00138   EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE bool evalSubExprsIfNeeded(Scalar*) {
00139     m_impl.evalSubExprsIfNeeded(NULL);
00140     return true;
00141   }
00142   EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void cleanup() {
00143     m_impl.cleanup();
00144   }
00145 
00146   EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE CoeffReturnType coeff(Index index) const
00147   {
00148     eigen_assert(index < dimensions().TotalSize());
00149     Index inputIndex = 0;
00150     if (static_cast<int>(Layout) == static_cast<int>(ColMajor)) {
00151       for (int i = NumDims - 1; i > 0; --i) {
00152         const Index idx = index / m_outputStrides[i];
00153         if (isPaddingAtIndexForDim(idx, i)) {
00154           return m_paddingValue;
00155         }
00156         inputIndex += (idx - m_padding[i].first) * m_inputStrides[i];
00157         index -= idx * m_outputStrides[i];
00158       }
00159       if (isPaddingAtIndexForDim(index, 0)) {
00160         return m_paddingValue;
00161       }
00162       inputIndex += (index - m_padding[0].first);
00163     } else {
00164       for (int i = 0; i < NumDims - 1; ++i) {
00165         const Index idx = index / m_outputStrides[i+1];
00166         if (isPaddingAtIndexForDim(idx, i)) {
00167           return m_paddingValue;
00168         }
00169         inputIndex += (idx - m_padding[i].first) * m_inputStrides[i];
00170         index -= idx * m_outputStrides[i+1];
00171       }
00172       if (isPaddingAtIndexForDim(index, NumDims-1)) {
00173         return m_paddingValue;
00174       }
00175       inputIndex += (index - m_padding[NumDims-1].first);
00176     }
00177     return m_impl.coeff(inputIndex);
00178   }
00179 
00180   template<int LoadMode>
00181   EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE PacketReturnType packet(Index index) const
00182   {
00183     if (static_cast<int>(Layout) == static_cast<int>(ColMajor)) {
00184       return packetColMajor(index);
00185     }
00186     return packetRowMajor(index);
00187   }
00188 
00189   EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE TensorOpCost costPerCoeff(bool vectorized) const {
00190     TensorOpCost cost = m_impl.costPerCoeff(vectorized);
00191     if (static_cast<int>(Layout) == static_cast<int>(ColMajor)) {
00192       for (int i = 0; i < NumDims; ++i)
00193         updateCostPerDimension(cost, i, i == 0);
00194     } else {
00195       for (int i = NumDims - 1; i >= 0; --i)
00196         updateCostPerDimension(cost, i, i == NumDims - 1);
00197     }
00198     return cost;
00199   }
00200 
00201   EIGEN_DEVICE_FUNC Scalar* data() const { return NULL; }
00202 
00203  private:
00204   EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE bool isPaddingAtIndexForDim(
00205       Index index, int dim_index) const {
00206 #if defined(EIGEN_HAS_INDEX_LIST)
00207     return (!internal::index_pair_first_statically_eq<PaddingDimensions>(dim_index, 0) &&
00208             index < m_padding[dim_index].first) ||
00209         (!internal::index_pair_second_statically_eq<PaddingDimensions>(dim_index, 0) &&
00210          index >= m_dimensions[dim_index] - m_padding[dim_index].second);
00211 #else
00212     return (index < m_padding[dim_index].first) ||
00213            (index >= m_dimensions[dim_index] - m_padding[dim_index].second);
00214 #endif
00215   }
00216 
00217   EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE bool isLeftPaddingCompileTimeZero(
00218       int dim_index) const {
00219 #if defined(EIGEN_HAS_INDEX_LIST)
00220     return internal::index_pair_first_statically_eq<PaddingDimensions>(dim_index, 0);
00221 #else
00222     EIGEN_UNUSED_VARIABLE(dim_index);
00223     return false;
00224 #endif
00225   }
00226 
00227   EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE bool isRightPaddingCompileTimeZero(
00228       int dim_index) const {
00229 #if defined(EIGEN_HAS_INDEX_LIST)
00230     return internal::index_pair_second_statically_eq<PaddingDimensions>(dim_index, 0);
00231 #else
00232     EIGEN_UNUSED_VARIABLE(dim_index);
00233     return false;
00234 #endif
00235   }
00236 
00237 
00238   void updateCostPerDimension(TensorOpCost& cost, int i, bool first) const {
00239     const double in = static_cast<double>(m_impl.dimensions()[i]);
00240     const double out = in + m_padding[i].first + m_padding[i].second;
00241     if (out == 0)
00242       return;
00243     const double reduction = in / out;
00244     cost *= reduction;
00245     if (first) {
00246       cost += TensorOpCost(0, 0, 2 * TensorOpCost::AddCost<Index>() +
00247                     reduction * (1 * TensorOpCost::AddCost<Index>()));
00248     } else {
00249       cost += TensorOpCost(0, 0, 2 * TensorOpCost::AddCost<Index>() +
00250                                  2 * TensorOpCost::MulCost<Index>() +
00251                     reduction * (2 * TensorOpCost::MulCost<Index>() +
00252                                  1 * TensorOpCost::DivCost<Index>()));
00253     }
00254   }
00255 
00256  protected:
00257 
00258   EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE PacketReturnType packetColMajor(Index index) const
00259   {
00260     EIGEN_STATIC_ASSERT((PacketSize > 1), YOU_MADE_A_PROGRAMMING_MISTAKE)
00261     eigen_assert(index+PacketSize-1 < dimensions().TotalSize());
00262 
00263     const Index initialIndex = index;
00264     Index inputIndex = 0;
00265     for (int i = NumDims - 1; i > 0; --i) {
00266       const Index first = index;
00267       const Index last = index + PacketSize - 1;
00268       const Index lastPaddedLeft = m_padding[i].first * m_outputStrides[i];
00269       const Index firstPaddedRight = (m_dimensions[i] - m_padding[i].second) * m_outputStrides[i];
00270       const Index lastPaddedRight = m_outputStrides[i+1];
00271 
00272       if (!isLeftPaddingCompileTimeZero(i) && last < lastPaddedLeft) {
00273         // all the coefficient are in the padding zone.
00274         return internal::pset1<PacketReturnType>(m_paddingValue);
00275       }
00276       else if (!isRightPaddingCompileTimeZero(i) && first >= firstPaddedRight && last < lastPaddedRight) {
00277         // all the coefficient are in the padding zone.
00278         return internal::pset1<PacketReturnType>(m_paddingValue);
00279       }
00280       else if ((isLeftPaddingCompileTimeZero(i) && isRightPaddingCompileTimeZero(i)) || (first >= lastPaddedLeft && last < firstPaddedRight)) {
00281         // all the coefficient are between the 2 padding zones.
00282         const Index idx = index / m_outputStrides[i];
00283         inputIndex += (idx - m_padding[i].first) * m_inputStrides[i];
00284         index -= idx * m_outputStrides[i];
00285       }
00286       else {
00287         // Every other case
00288         return packetWithPossibleZero(initialIndex);
00289       }
00290     }
00291 
00292     const Index last = index + PacketSize - 1;
00293     const Index first = index;
00294     const Index lastPaddedLeft = m_padding[0].first;
00295     const Index firstPaddedRight = (m_dimensions[0] - m_padding[0].second);
00296     const Index lastPaddedRight = m_outputStrides[1];
00297 
00298     if (!isLeftPaddingCompileTimeZero(0) && last < lastPaddedLeft) {
00299       // all the coefficient are in the padding zone.
00300       return internal::pset1<PacketReturnType>(m_paddingValue);
00301     }
00302     else if (!isRightPaddingCompileTimeZero(0) && first >= firstPaddedRight && last < lastPaddedRight) {
00303       // all the coefficient are in the padding zone.
00304       return internal::pset1<PacketReturnType>(m_paddingValue);
00305     }
00306     else if ((isLeftPaddingCompileTimeZero(0) && isRightPaddingCompileTimeZero(0)) || (first >= lastPaddedLeft && last < firstPaddedRight)) {
00307       // all the coefficient are between the 2 padding zones.
00308       inputIndex += (index - m_padding[0].first);
00309       return m_impl.template packet<Unaligned>(inputIndex);
00310     }
00311     // Every other case
00312     return packetWithPossibleZero(initialIndex);
00313   }
00314 
00315   EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE PacketReturnType packetRowMajor(Index index) const
00316   {
00317     EIGEN_STATIC_ASSERT((PacketSize > 1), YOU_MADE_A_PROGRAMMING_MISTAKE)
00318     eigen_assert(index+PacketSize-1 < dimensions().TotalSize());
00319 
00320     const Index initialIndex = index;
00321     Index inputIndex = 0;
00322 
00323     for (int i = 0; i < NumDims - 1; ++i) {
00324       const Index first = index;
00325       const Index last = index + PacketSize - 1;
00326       const Index lastPaddedLeft = m_padding[i].first * m_outputStrides[i+1];
00327       const Index firstPaddedRight = (m_dimensions[i] - m_padding[i].second) * m_outputStrides[i+1];
00328       const Index lastPaddedRight = m_outputStrides[i];
00329 
00330       if (!isLeftPaddingCompileTimeZero(i) && last < lastPaddedLeft) {
00331         // all the coefficient are in the padding zone.
00332         return internal::pset1<PacketReturnType>(m_paddingValue);
00333       }
00334       else if (!isRightPaddingCompileTimeZero(i) && first >= firstPaddedRight && last < lastPaddedRight) {
00335         // all the coefficient are in the padding zone.
00336         return internal::pset1<PacketReturnType>(m_paddingValue);
00337       }
00338       else if ((isLeftPaddingCompileTimeZero(i) && isRightPaddingCompileTimeZero(i)) || (first >= lastPaddedLeft && last < firstPaddedRight)) {
00339         // all the coefficient are between the 2 padding zones.
00340         const Index idx = index / m_outputStrides[i+1];
00341         inputIndex += (idx - m_padding[i].first) * m_inputStrides[i];
00342         index -= idx * m_outputStrides[i+1];
00343       }
00344       else {
00345         // Every other case
00346         return packetWithPossibleZero(initialIndex);
00347       }
00348     }
00349 
00350     const Index last = index + PacketSize - 1;
00351     const Index first = index;
00352     const Index lastPaddedLeft = m_padding[NumDims-1].first;
00353     const Index firstPaddedRight = (m_dimensions[NumDims-1] - m_padding[NumDims-1].second);
00354     const Index lastPaddedRight = m_outputStrides[NumDims-1];
00355 
00356     if (!isLeftPaddingCompileTimeZero(NumDims-1) && last < lastPaddedLeft) {
00357       // all the coefficient are in the padding zone.
00358       return internal::pset1<PacketReturnType>(m_paddingValue);
00359     }
00360     else if (!isRightPaddingCompileTimeZero(NumDims-1) && first >= firstPaddedRight && last < lastPaddedRight) {
00361       // all the coefficient are in the padding zone.
00362       return internal::pset1<PacketReturnType>(m_paddingValue);
00363     }
00364     else if ((isLeftPaddingCompileTimeZero(NumDims-1) && isRightPaddingCompileTimeZero(NumDims-1)) || (first >= lastPaddedLeft && last < firstPaddedRight)) {
00365       // all the coefficient are between the 2 padding zones.
00366       inputIndex += (index - m_padding[NumDims-1].first);
00367       return m_impl.template packet<Unaligned>(inputIndex);
00368     }
00369     // Every other case
00370     return packetWithPossibleZero(initialIndex);
00371   }
00372 
00373   EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE PacketReturnType packetWithPossibleZero(Index index) const
00374   {
00375     EIGEN_ALIGN_MAX typename internal::remove_const<CoeffReturnType>::type values[PacketSize];
00376     for (int i = 0; i < PacketSize; ++i) {
00377       values[i] = coeff(index+i);
00378     }
00379     PacketReturnType rslt = internal::pload<PacketReturnType>(values);
00380     return rslt;
00381   }
00382 
00383   Dimensions m_dimensions;
00384   array<Index, NumDims+1> m_outputStrides;
00385   array<Index, NumDims> m_inputStrides;
00386   TensorEvaluator<ArgType, Device> m_impl;
00387   PaddingDimensions m_padding;
00388 
00389   Scalar m_paddingValue;
00390 };
00391 
00392 
00393 
00394 
00395 } // end namespace Eigen
00396 
00397 #endif // EIGEN_CXX11_TENSOR_TENSOR_PADDING_H
 All Classes Functions Variables Typedefs Enumerator