1 // This file is part of Eigen, a lightweight C++ template library 2 // for linear algebra. 3 // 4 // Copyright (C) 2014 Benoit Steiner <benoit.steiner.goog@gmail.com> 5 // 6 // This Source Code Form is subject to the terms of the Mozilla 7 // Public License v. 2.0. If a copy of the MPL was not distributed 8 // with this file, You can obtain one at http://mozilla.org/MPL/2.0/. 9 10 #ifndef EIGEN_CXX11_TENSOR_TENSOR_PADDING_H 11 #define EIGEN_CXX11_TENSOR_TENSOR_PADDING_H 12 13 namespace Eigen { 14 15 /** \class TensorPadding 16 * \ingroup CXX11_Tensor_Module 17 * 18 * \brief Tensor padding class. 19 * At the moment only padding with a constant value is supported. 20 * 21 */ 22 namespace internal { 23 template<typename PaddingDimensions, typename XprType> 24 struct traits<TensorPaddingOp<PaddingDimensions, XprType> > : public traits<XprType> 25 { 26 typedef typename XprType::Scalar Scalar; 27 typedef traits<XprType> XprTraits; 28 typedef typename XprTraits::StorageKind StorageKind; 29 typedef typename XprTraits::Index Index; 30 typedef typename XprType::Nested Nested; 31 typedef typename remove_reference<Nested>::type _Nested; 32 static const int NumDimensions = XprTraits::NumDimensions; 33 static const int Layout = XprTraits::Layout; 34 }; 35 36 template<typename PaddingDimensions, typename XprType> 37 struct eval<TensorPaddingOp<PaddingDimensions, XprType>, Eigen::Dense> 38 { 39 typedef const TensorPaddingOp<PaddingDimensions, XprType>& type; 40 }; 41 42 template<typename PaddingDimensions, typename XprType> 43 struct nested<TensorPaddingOp<PaddingDimensions, XprType>, 1, typename eval<TensorPaddingOp<PaddingDimensions, XprType> >::type> 44 { 45 typedef TensorPaddingOp<PaddingDimensions, XprType> type; 46 }; 47 48 } // end namespace internal 49 50 51 52 template<typename PaddingDimensions, typename XprType> 53 class TensorPaddingOp : public TensorBase<TensorPaddingOp<PaddingDimensions, XprType>, ReadOnlyAccessors> 54 { 55 public: 56 typedef typename Eigen::internal::traits<TensorPaddingOp>::Scalar Scalar; 57 typedef typename Eigen::NumTraits<Scalar>::Real RealScalar; 58 typedef typename XprType::CoeffReturnType CoeffReturnType; 59 typedef typename Eigen::internal::nested<TensorPaddingOp>::type Nested; 60 typedef typename Eigen::internal::traits<TensorPaddingOp>::StorageKind StorageKind; 61 typedef typename Eigen::internal::traits<TensorPaddingOp>::Index Index; 62 63 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE TensorPaddingOp(const XprType& expr, const PaddingDimensions& padding_dims, const Scalar padding_value) 64 : m_xpr(expr), m_padding_dims(padding_dims), m_padding_value(padding_value) {} 65 66 EIGEN_DEVICE_FUNC 67 const PaddingDimensions& padding() const { return m_padding_dims; } 68 EIGEN_DEVICE_FUNC 69 Scalar padding_value() const { return m_padding_value; } 70 71 EIGEN_DEVICE_FUNC 72 const typename internal::remove_all<typename XprType::Nested>::type& 73 expression() const { return m_xpr; } 74 75 protected: 76 typename XprType::Nested m_xpr; 77 const PaddingDimensions m_padding_dims; 78 const Scalar m_padding_value; 79 }; 80 81 82 // Eval as rvalue 83 template<typename PaddingDimensions, typename ArgType, typename Device> 84 struct TensorEvaluator<const TensorPaddingOp<PaddingDimensions, ArgType>, Device> 85 { 86 typedef TensorPaddingOp<PaddingDimensions, ArgType> XprType; 87 typedef typename XprType::Index Index; 88 static const int NumDims = internal::array_size<PaddingDimensions>::value; 89 typedef DSizes<Index, NumDims> Dimensions; 90 typedef typename XprType::Scalar Scalar; 91 typedef typename XprType::CoeffReturnType CoeffReturnType; 92 typedef typename PacketType<CoeffReturnType, Device>::type PacketReturnType; 93 static const int PacketSize = internal::unpacket_traits<PacketReturnType>::size; 94 95 enum { 96 IsAligned = true, 97 PacketAccess = TensorEvaluator<ArgType, Device>::PacketAccess, 98 Layout = TensorEvaluator<ArgType, Device>::Layout, 99 CoordAccess = true, 100 RawAccess = false 101 }; 102 103 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE TensorEvaluator(const XprType& op, const Device& device) 104 : m_impl(op.expression(), device), m_padding(op.padding()), m_paddingValue(op.padding_value()) 105 { 106 // The padding op doesn't change the rank of the tensor. Directly padding a scalar would lead 107 // to a vector, which doesn't make sense. Instead one should reshape the scalar into a vector 108 // of 1 element first and then pad. 109 EIGEN_STATIC_ASSERT((NumDims > 0), YOU_MADE_A_PROGRAMMING_MISTAKE); 110 111 // Compute dimensions 112 m_dimensions = m_impl.dimensions(); 113 for (int i = 0; i < NumDims; ++i) { 114 m_dimensions[i] += m_padding[i].first + m_padding[i].second; 115 } 116 const typename TensorEvaluator<ArgType, Device>::Dimensions& input_dims = m_impl.dimensions(); 117 if (static_cast<int>(Layout) == static_cast<int>(ColMajor)) { 118 m_inputStrides[0] = 1; 119 m_outputStrides[0] = 1; 120 for (int i = 1; i < NumDims; ++i) { 121 m_inputStrides[i] = m_inputStrides[i-1] * input_dims[i-1]; 122 m_outputStrides[i] = m_outputStrides[i-1] * m_dimensions[i-1]; 123 } 124 m_outputStrides[NumDims] = m_outputStrides[NumDims-1] * m_dimensions[NumDims-1]; 125 } else { 126 m_inputStrides[NumDims - 1] = 1; 127 m_outputStrides[NumDims] = 1; 128 for (int i = NumDims - 2; i >= 0; --i) { 129 m_inputStrides[i] = m_inputStrides[i+1] * input_dims[i+1]; 130 m_outputStrides[i+1] = m_outputStrides[i+2] * m_dimensions[i+1]; 131 } 132 m_outputStrides[0] = m_outputStrides[1] * m_dimensions[0]; 133 } 134 } 135 136 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE const Dimensions& dimensions() const { return m_dimensions; } 137 138 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE bool evalSubExprsIfNeeded(Scalar*) { 139 m_impl.evalSubExprsIfNeeded(NULL); 140 return true; 141 } 142 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void cleanup() { 143 m_impl.cleanup(); 144 } 145 146 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE CoeffReturnType coeff(Index index) const 147 { 148 eigen_assert(index < dimensions().TotalSize()); 149 Index inputIndex = 0; 150 if (static_cast<int>(Layout) == static_cast<int>(ColMajor)) { 151 for (int i = NumDims - 1; i > 0; --i) { 152 const Index idx = index / m_outputStrides[i]; 153 if (isPaddingAtIndexForDim(idx, i)) { 154 return m_paddingValue; 155 } 156 inputIndex += (idx - m_padding[i].first) * m_inputStrides[i]; 157 index -= idx * m_outputStrides[i]; 158 } 159 if (isPaddingAtIndexForDim(index, 0)) { 160 return m_paddingValue; 161 } 162 inputIndex += (index - m_padding[0].first); 163 } else { 164 for (int i = 0; i < NumDims - 1; ++i) { 165 const Index idx = index / m_outputStrides[i+1]; 166 if (isPaddingAtIndexForDim(idx, i)) { 167 return m_paddingValue; 168 } 169 inputIndex += (idx - m_padding[i].first) * m_inputStrides[i]; 170 index -= idx * m_outputStrides[i+1]; 171 } 172 if (isPaddingAtIndexForDim(index, NumDims-1)) { 173 return m_paddingValue; 174 } 175 inputIndex += (index - m_padding[NumDims-1].first); 176 } 177 return m_impl.coeff(inputIndex); 178 } 179 180 template<int LoadMode> 181 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE PacketReturnType packet(Index index) const 182 { 183 if (static_cast<int>(Layout) == static_cast<int>(ColMajor)) { 184 return packetColMajor(index); 185 } 186 return packetRowMajor(index); 187 } 188 189 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE TensorOpCost costPerCoeff(bool vectorized) const { 190 TensorOpCost cost = m_impl.costPerCoeff(vectorized); 191 if (static_cast<int>(Layout) == static_cast<int>(ColMajor)) { 192 for (int i = 0; i < NumDims; ++i) 193 updateCostPerDimension(cost, i, i == 0); 194 } else { 195 for (int i = NumDims - 1; i >= 0; --i) 196 updateCostPerDimension(cost, i, i == NumDims - 1); 197 } 198 return cost; 199 } 200 201 EIGEN_DEVICE_FUNC Scalar* data() const { return NULL; } 202 203 private: 204 EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE bool isPaddingAtIndexForDim( 205 Index index, int dim_index) const { 206 #if defined(EIGEN_HAS_INDEX_LIST) 207 return (!internal::index_pair_first_statically_eq<PaddingDimensions>(dim_index, 0) && 208 index < m_padding[dim_index].first) || 209 (!internal::index_pair_second_statically_eq<PaddingDimensions>(dim_index, 0) && 210 index >= m_dimensions[dim_index] - m_padding[dim_index].second); 211 #else 212 return (index < m_padding[dim_index].first) || 213 (index >= m_dimensions[dim_index] - m_padding[dim_index].second); 214 #endif 215 } 216 217 EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE bool isLeftPaddingCompileTimeZero( 218 int dim_index) const { 219 #if defined(EIGEN_HAS_INDEX_LIST) 220 return internal::index_pair_first_statically_eq<PaddingDimensions>(dim_index, 0); 221 #else 222 EIGEN_UNUSED_VARIABLE(dim_index); 223 return false; 224 #endif 225 } 226 227 EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE bool isRightPaddingCompileTimeZero( 228 int dim_index) const { 229 #if defined(EIGEN_HAS_INDEX_LIST) 230 return internal::index_pair_second_statically_eq<PaddingDimensions>(dim_index, 0); 231 #else 232 EIGEN_UNUSED_VARIABLE(dim_index); 233 return false; 234 #endif 235 } 236 237 238 void updateCostPerDimension(TensorOpCost& cost, int i, bool first) const { 239 const double in = static_cast<double>(m_impl.dimensions()[i]); 240 const double out = in + m_padding[i].first + m_padding[i].second; 241 if (out == 0) 242 return; 243 const double reduction = in / out; 244 cost *= reduction; 245 if (first) { 246 cost += TensorOpCost(0, 0, 2 * TensorOpCost::AddCost<Index>() + 247 reduction * (1 * TensorOpCost::AddCost<Index>())); 248 } else { 249 cost += TensorOpCost(0, 0, 2 * TensorOpCost::AddCost<Index>() + 250 2 * TensorOpCost::MulCost<Index>() + 251 reduction * (2 * TensorOpCost::MulCost<Index>() + 252 1 * TensorOpCost::DivCost<Index>())); 253 } 254 } 255 256 protected: 257 258 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE PacketReturnType packetColMajor(Index index) const 259 { 260 EIGEN_STATIC_ASSERT((PacketSize > 1), YOU_MADE_A_PROGRAMMING_MISTAKE) 261 eigen_assert(index+PacketSize-1 < dimensions().TotalSize()); 262 263 const Index initialIndex = index; 264 Index inputIndex = 0; 265 for (int i = NumDims - 1; i > 0; --i) { 266 const Index first = index; 267 const Index last = index + PacketSize - 1; 268 const Index lastPaddedLeft = m_padding[i].first * m_outputStrides[i]; 269 const Index firstPaddedRight = (m_dimensions[i] - m_padding[i].second) * m_outputStrides[i]; 270 const Index lastPaddedRight = m_outputStrides[i+1]; 271 272 if (!isLeftPaddingCompileTimeZero(i) && last < lastPaddedLeft) { 273 // all the coefficient are in the padding zone. 274 return internal::pset1<PacketReturnType>(m_paddingValue); 275 } 276 else if (!isRightPaddingCompileTimeZero(i) && first >= firstPaddedRight && last < lastPaddedRight) { 277 // all the coefficient are in the padding zone. 278 return internal::pset1<PacketReturnType>(m_paddingValue); 279 } 280 else if ((isLeftPaddingCompileTimeZero(i) && isRightPaddingCompileTimeZero(i)) || (first >= lastPaddedLeft && last < firstPaddedRight)) { 281 // all the coefficient are between the 2 padding zones. 282 const Index idx = index / m_outputStrides[i]; 283 inputIndex += (idx - m_padding[i].first) * m_inputStrides[i]; 284 index -= idx * m_outputStrides[i]; 285 } 286 else { 287 // Every other case 288 return packetWithPossibleZero(initialIndex); 289 } 290 } 291 292 const Index last = index + PacketSize - 1; 293 const Index first = index; 294 const Index lastPaddedLeft = m_padding[0].first; 295 const Index firstPaddedRight = (m_dimensions[0] - m_padding[0].second); 296 const Index lastPaddedRight = m_outputStrides[1]; 297 298 if (!isLeftPaddingCompileTimeZero(0) && last < lastPaddedLeft) { 299 // all the coefficient are in the padding zone. 300 return internal::pset1<PacketReturnType>(m_paddingValue); 301 } 302 else if (!isRightPaddingCompileTimeZero(0) && first >= firstPaddedRight && last < lastPaddedRight) { 303 // all the coefficient are in the padding zone. 304 return internal::pset1<PacketReturnType>(m_paddingValue); 305 } 306 else if ((isLeftPaddingCompileTimeZero(0) && isRightPaddingCompileTimeZero(0)) || (first >= lastPaddedLeft && last < firstPaddedRight)) { 307 // all the coefficient are between the 2 padding zones. 308 inputIndex += (index - m_padding[0].first); 309 return m_impl.template packet<Unaligned>(inputIndex); 310 } 311 // Every other case 312 return packetWithPossibleZero(initialIndex); 313 } 314 315 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE PacketReturnType packetRowMajor(Index index) const 316 { 317 EIGEN_STATIC_ASSERT((PacketSize > 1), YOU_MADE_A_PROGRAMMING_MISTAKE) 318 eigen_assert(index+PacketSize-1 < dimensions().TotalSize()); 319 320 const Index initialIndex = index; 321 Index inputIndex = 0; 322 323 for (int i = 0; i < NumDims - 1; ++i) { 324 const Index first = index; 325 const Index last = index + PacketSize - 1; 326 const Index lastPaddedLeft = m_padding[i].first * m_outputStrides[i+1]; 327 const Index firstPaddedRight = (m_dimensions[i] - m_padding[i].second) * m_outputStrides[i+1]; 328 const Index lastPaddedRight = m_outputStrides[i]; 329 330 if (!isLeftPaddingCompileTimeZero(i) && last < lastPaddedLeft) { 331 // all the coefficient are in the padding zone. 332 return internal::pset1<PacketReturnType>(m_paddingValue); 333 } 334 else if (!isRightPaddingCompileTimeZero(i) && first >= firstPaddedRight && last < lastPaddedRight) { 335 // all the coefficient are in the padding zone. 336 return internal::pset1<PacketReturnType>(m_paddingValue); 337 } 338 else if ((isLeftPaddingCompileTimeZero(i) && isRightPaddingCompileTimeZero(i)) || (first >= lastPaddedLeft && last < firstPaddedRight)) { 339 // all the coefficient are between the 2 padding zones. 340 const Index idx = index / m_outputStrides[i+1]; 341 inputIndex += (idx - m_padding[i].first) * m_inputStrides[i]; 342 index -= idx * m_outputStrides[i+1]; 343 } 344 else { 345 // Every other case 346 return packetWithPossibleZero(initialIndex); 347 } 348 } 349 350 const Index last = index + PacketSize - 1; 351 const Index first = index; 352 const Index lastPaddedLeft = m_padding[NumDims-1].first; 353 const Index firstPaddedRight = (m_dimensions[NumDims-1] - m_padding[NumDims-1].second); 354 const Index lastPaddedRight = m_outputStrides[NumDims-1]; 355 356 if (!isLeftPaddingCompileTimeZero(NumDims-1) && last < lastPaddedLeft) { 357 // all the coefficient are in the padding zone. 358 return internal::pset1<PacketReturnType>(m_paddingValue); 359 } 360 else if (!isRightPaddingCompileTimeZero(NumDims-1) && first >= firstPaddedRight && last < lastPaddedRight) { 361 // all the coefficient are in the padding zone. 362 return internal::pset1<PacketReturnType>(m_paddingValue); 363 } 364 else if ((isLeftPaddingCompileTimeZero(NumDims-1) && isRightPaddingCompileTimeZero(NumDims-1)) || (first >= lastPaddedLeft && last < firstPaddedRight)) { 365 // all the coefficient are between the 2 padding zones. 366 inputIndex += (index - m_padding[NumDims-1].first); 367 return m_impl.template packet<Unaligned>(inputIndex); 368 } 369 // Every other case 370 return packetWithPossibleZero(initialIndex); 371 } 372 373 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE PacketReturnType packetWithPossibleZero(Index index) const 374 { 375 EIGEN_ALIGN_MAX typename internal::remove_const<CoeffReturnType>::type values[PacketSize]; 376 for (int i = 0; i < PacketSize; ++i) { 377 values[i] = coeff(index+i); 378 } 379 PacketReturnType rslt = internal::pload<PacketReturnType>(values); 380 return rslt; 381 } 382 383 Dimensions m_dimensions; 384 array<Index, NumDims+1> m_outputStrides; 385 array<Index, NumDims> m_inputStrides; 386 TensorEvaluator<ArgType, Device> m_impl; 387 PaddingDimensions m_padding; 388 389 Scalar m_paddingValue; 390 }; 391 392 393 394 395 } // end namespace Eigen 396 397 #endif // EIGEN_CXX11_TENSOR_TENSOR_PADDING_H 398