![]() |
Eigen-unsupported
3.3.3
|
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 // Copyright (C) 2013 Christian Seiler <christian@iwakd.de> 00006 // 00007 // This Source Code Form is subject to the terms of the Mozilla 00008 // Public License v. 2.0. If a copy of the MPL was not distributed 00009 // with this file, You can obtain one at http://mozilla.org/MPL/2.0/. 00010 00011 #ifndef EIGEN_CXX11_TENSOR_TENSOR_H 00012 #define EIGEN_CXX11_TENSOR_TENSOR_H 00013 00014 namespace Eigen { 00015 00062 template<typename Scalar_, int NumIndices_, int Options_, typename IndexType_> 00063 class Tensor : public TensorBase<Tensor<Scalar_, NumIndices_, Options_, IndexType_> > 00064 { 00065 public: 00066 typedef Tensor<Scalar_, NumIndices_, Options_, IndexType_> Self; 00067 typedef TensorBase<Tensor<Scalar_, NumIndices_, Options_, IndexType_> > Base; 00068 typedef typename Eigen::internal::nested<Self>::type Nested; 00069 typedef typename internal::traits<Self>::StorageKind StorageKind; 00070 typedef typename internal::traits<Self>::Index Index; 00071 typedef Scalar_ Scalar; 00072 typedef typename NumTraits<Scalar>::Real RealScalar; 00073 typedef typename Base::CoeffReturnType CoeffReturnType; 00074 00075 enum { 00076 IsAligned = bool(EIGEN_MAX_ALIGN_BYTES>0) & !(Options_&DontAlign), 00077 Layout = Options_ & RowMajor ? RowMajor : ColMajor, 00078 CoordAccess = true, 00079 RawAccess = true 00080 }; 00081 00082 static const int Options = Options_; 00083 static const int NumIndices = NumIndices_; 00084 typedef DSizes<Index, NumIndices_> Dimensions; 00085 00086 protected: 00087 TensorStorage<Scalar, Dimensions, Options> m_storage; 00088 00089 #ifdef EIGEN_HAS_SFINAE 00090 template<typename CustomIndices> 00091 struct isOfNormalIndex{ 00092 static const bool is_array = internal::is_base_of<array<Index, NumIndices>, CustomIndices>::value; 00093 static const bool is_int = NumTraits<CustomIndices>::IsInteger; 00094 static const bool value = is_array | is_int; 00095 }; 00096 #endif 00097 00098 public: 00099 // Metadata 00100 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Index rank() const { return NumIndices; } 00101 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Index dimension(std::size_t n) const { return m_storage.dimensions()[n]; } 00102 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE const Dimensions& dimensions() const { return m_storage.dimensions(); } 00103 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Index size() const { return m_storage.size(); } 00104 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Scalar *data() { return m_storage.data(); } 00105 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE const Scalar *data() const { return m_storage.data(); } 00106 00107 // This makes EIGEN_INITIALIZE_COEFFS_IF_THAT_OPTION_IS_ENABLED 00108 // work, because that uses base().coeffRef() - and we don't yet 00109 // implement a similar class hierarchy 00110 inline Self& base() { return *this; } 00111 inline const Self& base() const { return *this; } 00112 00113 #if EIGEN_HAS_VARIADIC_TEMPLATES 00114 template<typename... IndexTypes> 00115 EIGEN_DEVICE_FUNC inline const Scalar& coeff(Index firstIndex, Index secondIndex, IndexTypes... otherIndices) const 00116 { 00117 // The number of indices used to access a tensor coefficient must be equal to the rank of the tensor. 00118 EIGEN_STATIC_ASSERT(sizeof...(otherIndices) + 2 == NumIndices, YOU_MADE_A_PROGRAMMING_MISTAKE) 00119 return coeff(array<Index, NumIndices>{{firstIndex, secondIndex, otherIndices...}}); 00120 } 00121 #endif 00122 00123 // normal indices 00124 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE const Scalar& coeff(const array<Index, NumIndices>& indices) const 00125 { 00126 eigen_internal_assert(checkIndexRange(indices)); 00127 return m_storage.data()[linearizedIndex(indices)]; 00128 } 00129 00130 // custom indices 00131 #ifdef EIGEN_HAS_SFINAE 00132 template<typename CustomIndices, 00133 EIGEN_SFINAE_ENABLE_IF( !(isOfNormalIndex<CustomIndices>::value) ) 00134 > 00135 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE const Scalar& coeff(CustomIndices& indices) const 00136 { 00137 return coeff(internal::customIndices2Array<Index,NumIndices>(indices)); 00138 } 00139 #endif 00140 00141 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE const Scalar& coeff() const 00142 { 00143 EIGEN_STATIC_ASSERT(NumIndices == 0, YOU_MADE_A_PROGRAMMING_MISTAKE); 00144 return m_storage.data()[0]; 00145 } 00146 00147 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE const Scalar& coeff(Index index) const 00148 { 00149 eigen_internal_assert(index >= 0 && index < size()); 00150 return m_storage.data()[index]; 00151 } 00152 00153 #if EIGEN_HAS_VARIADIC_TEMPLATES 00154 template<typename... IndexTypes> 00155 inline Scalar& coeffRef(Index firstIndex, Index secondIndex, IndexTypes... otherIndices) 00156 { 00157 // The number of indices used to access a tensor coefficient must be equal to the rank of the tensor. 00158 EIGEN_STATIC_ASSERT(sizeof...(otherIndices) + 2 == NumIndices, YOU_MADE_A_PROGRAMMING_MISTAKE) 00159 return coeffRef(array<Index, NumIndices>{{firstIndex, secondIndex, otherIndices...}}); 00160 } 00161 #endif 00162 00163 // normal indices 00164 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Scalar& coeffRef(const array<Index, NumIndices>& indices) 00165 { 00166 eigen_internal_assert(checkIndexRange(indices)); 00167 return m_storage.data()[linearizedIndex(indices)]; 00168 } 00169 00170 // custom indices 00171 #ifdef EIGEN_HAS_SFINAE 00172 template<typename CustomIndices, 00173 EIGEN_SFINAE_ENABLE_IF( !(isOfNormalIndex<CustomIndices>::value) ) 00174 > 00175 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Scalar& coeffRef(CustomIndices& indices) 00176 { 00177 return coeffRef(internal::customIndices2Array<Index,NumIndices>(indices)); 00178 } 00179 #endif 00180 00181 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Scalar& coeffRef() 00182 { 00183 EIGEN_STATIC_ASSERT(NumIndices == 0, YOU_MADE_A_PROGRAMMING_MISTAKE); 00184 return m_storage.data()[0]; 00185 } 00186 00187 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Scalar& coeffRef(Index index) 00188 { 00189 eigen_internal_assert(index >= 0 && index < size()); 00190 return m_storage.data()[index]; 00191 } 00192 00193 #if EIGEN_HAS_VARIADIC_TEMPLATES 00194 template<typename... IndexTypes> 00195 inline const Scalar& operator()(Index firstIndex, Index secondIndex, IndexTypes... otherIndices) const 00196 { 00197 // The number of indices used to access a tensor coefficient must be equal to the rank of the tensor. 00198 EIGEN_STATIC_ASSERT(sizeof...(otherIndices) + 2 == NumIndices, YOU_MADE_A_PROGRAMMING_MISTAKE) 00199 return this->operator()(array<Index, NumIndices>{{firstIndex, secondIndex, otherIndices...}}); 00200 } 00201 #else 00202 EIGEN_DEVICE_FUNC 00203 EIGEN_STRONG_INLINE const Scalar& operator()(Index i0, Index i1) const 00204 { 00205 return coeff(array<Index, 2>(i0, i1)); 00206 } 00207 EIGEN_DEVICE_FUNC 00208 EIGEN_STRONG_INLINE const Scalar& operator()(Index i0, Index i1, Index i2) const 00209 { 00210 return coeff(array<Index, 3>(i0, i1, i2)); 00211 } 00212 EIGEN_DEVICE_FUNC 00213 EIGEN_STRONG_INLINE const Scalar& operator()(Index i0, Index i1, Index i2, Index i3) const 00214 { 00215 return coeff(array<Index, 4>(i0, i1, i2, i3)); 00216 } 00217 EIGEN_DEVICE_FUNC 00218 EIGEN_STRONG_INLINE const Scalar& operator()(Index i0, Index i1, Index i2, Index i3, Index i4) const 00219 { 00220 return coeff(array<Index, 5>(i0, i1, i2, i3, i4)); 00221 } 00222 #endif 00223 00224 // custom indices 00225 #ifdef EIGEN_HAS_SFINAE 00226 template<typename CustomIndices, 00227 EIGEN_SFINAE_ENABLE_IF( !(isOfNormalIndex<CustomIndices>::value) ) 00228 > 00229 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE const Scalar& operator()(CustomIndices& indices) const 00230 { 00231 return coeff(internal::customIndices2Array<Index,NumIndices>(indices)); 00232 } 00233 #endif 00234 00235 // normal indices 00236 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE const Scalar& operator()(const array<Index, NumIndices>& indices) const 00237 { 00238 return coeff(indices); 00239 } 00240 00241 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE const Scalar& operator()(Index index) const 00242 { 00243 eigen_internal_assert(index >= 0 && index < size()); 00244 return coeff(index); 00245 } 00246 00247 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE const Scalar& operator()() const 00248 { 00249 EIGEN_STATIC_ASSERT(NumIndices == 0, YOU_MADE_A_PROGRAMMING_MISTAKE); 00250 return coeff(); 00251 } 00252 00253 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE const Scalar& operator[](Index index) const 00254 { 00255 // The bracket operator is only for vectors, use the parenthesis operator instead. 00256 EIGEN_STATIC_ASSERT(NumIndices == 1, YOU_MADE_A_PROGRAMMING_MISTAKE); 00257 return coeff(index); 00258 } 00259 00260 #if EIGEN_HAS_VARIADIC_TEMPLATES 00261 template<typename... IndexTypes> 00262 inline Scalar& operator()(Index firstIndex, Index secondIndex, IndexTypes... otherIndices) 00263 { 00264 // The number of indices used to access a tensor coefficient must be equal to the rank of the tensor. 00265 EIGEN_STATIC_ASSERT(sizeof...(otherIndices) + 2 == NumIndices, YOU_MADE_A_PROGRAMMING_MISTAKE) 00266 return operator()(array<Index, NumIndices>{{firstIndex, secondIndex, otherIndices...}}); 00267 } 00268 #else 00269 EIGEN_DEVICE_FUNC 00270 EIGEN_STRONG_INLINE Scalar& operator()(Index i0, Index i1) 00271 { 00272 return coeffRef(array<Index, 2>(i0, i1)); 00273 } 00274 EIGEN_DEVICE_FUNC 00275 EIGEN_STRONG_INLINE Scalar& operator()(Index i0, Index i1, Index i2) 00276 { 00277 return coeffRef(array<Index, 3>(i0, i1, i2)); 00278 } 00279 EIGEN_DEVICE_FUNC 00280 EIGEN_STRONG_INLINE Scalar& operator()(Index i0, Index i1, Index i2, Index i3) 00281 { 00282 return coeffRef(array<Index, 4>(i0, i1, i2, i3)); 00283 } 00284 EIGEN_DEVICE_FUNC 00285 EIGEN_STRONG_INLINE Scalar& operator()(Index i0, Index i1, Index i2, Index i3, Index i4) 00286 { 00287 return coeffRef(array<Index, 5>(i0, i1, i2, i3, i4)); 00288 } 00289 #endif 00290 00291 // normal indices 00292 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Scalar& operator()(const array<Index, NumIndices>& indices) 00293 { 00294 return coeffRef(indices); 00295 } 00296 00297 // custom indices 00298 #ifdef EIGEN_HAS_SFINAE 00299 template<typename CustomIndices, 00300 EIGEN_SFINAE_ENABLE_IF( !(isOfNormalIndex<CustomIndices>::value) ) 00301 > 00302 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Scalar& operator()(CustomIndices& indices) 00303 { 00304 return coeffRef(internal::customIndices2Array<Index,NumIndices>(indices)); 00305 } 00306 #endif 00307 00308 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Scalar& operator()(Index index) 00309 { 00310 eigen_assert(index >= 0 && index < size()); 00311 return coeffRef(index); 00312 } 00313 00314 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Scalar& operator()() 00315 { 00316 EIGEN_STATIC_ASSERT(NumIndices == 0, YOU_MADE_A_PROGRAMMING_MISTAKE); 00317 return coeffRef(); 00318 } 00319 00320 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Scalar& operator[](Index index) 00321 { 00322 // The bracket operator is only for vectors, use the parenthesis operator instead 00323 EIGEN_STATIC_ASSERT(NumIndices == 1, YOU_MADE_A_PROGRAMMING_MISTAKE) 00324 return coeffRef(index); 00325 } 00326 00327 EIGEN_DEVICE_FUNC 00328 EIGEN_STRONG_INLINE Tensor() 00329 : m_storage() 00330 { 00331 } 00332 00333 EIGEN_DEVICE_FUNC 00334 EIGEN_STRONG_INLINE Tensor(const Self& other) 00335 : m_storage(other.m_storage) 00336 { 00337 } 00338 00339 #if EIGEN_HAS_VARIADIC_TEMPLATES 00340 template<typename... IndexTypes> 00341 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Tensor(Index firstDimension, IndexTypes... otherDimensions) 00342 : m_storage(firstDimension, otherDimensions...) 00343 { 00344 // The number of dimensions used to construct a tensor must be equal to the rank of the tensor. 00345 EIGEN_STATIC_ASSERT(sizeof...(otherDimensions) + 1 == NumIndices, YOU_MADE_A_PROGRAMMING_MISTAKE) 00346 } 00347 #else 00348 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE explicit Tensor(Index dim1) 00349 : m_storage(dim1, array<Index, 1>(dim1)) 00350 { 00351 EIGEN_STATIC_ASSERT(1 == NumIndices, YOU_MADE_A_PROGRAMMING_MISTAKE) 00352 } 00353 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Tensor(Index dim1, Index dim2) 00354 : m_storage(dim1*dim2, array<Index, 2>(dim1, dim2)) 00355 { 00356 EIGEN_STATIC_ASSERT(2 == NumIndices, YOU_MADE_A_PROGRAMMING_MISTAKE) 00357 } 00358 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Tensor(Index dim1, Index dim2, Index dim3) 00359 : m_storage(dim1*dim2*dim3, array<Index, 3>(dim1, dim2, dim3)) 00360 { 00361 EIGEN_STATIC_ASSERT(3 == NumIndices, YOU_MADE_A_PROGRAMMING_MISTAKE) 00362 } 00363 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Tensor(Index dim1, Index dim2, Index dim3, Index dim4) 00364 : m_storage(dim1*dim2*dim3*dim4, array<Index, 4>(dim1, dim2, dim3, dim4)) 00365 { 00366 EIGEN_STATIC_ASSERT(4 == NumIndices, YOU_MADE_A_PROGRAMMING_MISTAKE) 00367 } 00368 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Tensor(Index dim1, Index dim2, Index dim3, Index dim4, Index dim5) 00369 : m_storage(dim1*dim2*dim3*dim4*dim5, array<Index, 5>(dim1, dim2, dim3, dim4, dim5)) 00370 { 00371 EIGEN_STATIC_ASSERT(5 == NumIndices, YOU_MADE_A_PROGRAMMING_MISTAKE) 00372 } 00373 #endif 00374 00376 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE explicit Tensor(const array<Index, NumIndices>& dimensions) 00377 : m_storage(internal::array_prod(dimensions), dimensions) 00378 { 00379 EIGEN_INITIALIZE_COEFFS_IF_THAT_OPTION_IS_ENABLED 00380 } 00381 00382 template<typename OtherDerived> 00383 EIGEN_DEVICE_FUNC 00384 EIGEN_STRONG_INLINE Tensor(const TensorBase<OtherDerived, ReadOnlyAccessors>& other) 00385 { 00386 typedef TensorAssignOp<Tensor, const OtherDerived> Assign; 00387 Assign assign(*this, other.derived()); 00388 resize(TensorEvaluator<const Assign, DefaultDevice>(assign, DefaultDevice()).dimensions()); 00389 internal::TensorExecutor<const Assign, DefaultDevice>::run(assign, DefaultDevice()); 00390 } 00391 template<typename OtherDerived> 00392 EIGEN_DEVICE_FUNC 00393 EIGEN_STRONG_INLINE Tensor(const TensorBase<OtherDerived, WriteAccessors>& other) 00394 { 00395 typedef TensorAssignOp<Tensor, const OtherDerived> Assign; 00396 Assign assign(*this, other.derived()); 00397 resize(TensorEvaluator<const Assign, DefaultDevice>(assign, DefaultDevice()).dimensions()); 00398 internal::TensorExecutor<const Assign, DefaultDevice>::run(assign, DefaultDevice()); 00399 } 00400 00401 EIGEN_DEVICE_FUNC 00402 EIGEN_STRONG_INLINE Tensor& operator=(const Tensor& other) 00403 { 00404 typedef TensorAssignOp<Tensor, const Tensor> Assign; 00405 Assign assign(*this, other); 00406 resize(TensorEvaluator<const Assign, DefaultDevice>(assign, DefaultDevice()).dimensions()); 00407 internal::TensorExecutor<const Assign, DefaultDevice>::run(assign, DefaultDevice()); 00408 return *this; 00409 } 00410 template<typename OtherDerived> 00411 EIGEN_DEVICE_FUNC 00412 EIGEN_STRONG_INLINE Tensor& operator=(const OtherDerived& other) 00413 { 00414 typedef TensorAssignOp<Tensor, const OtherDerived> Assign; 00415 Assign assign(*this, other); 00416 resize(TensorEvaluator<const Assign, DefaultDevice>(assign, DefaultDevice()).dimensions()); 00417 internal::TensorExecutor<const Assign, DefaultDevice>::run(assign, DefaultDevice()); 00418 return *this; 00419 } 00420 00421 #if EIGEN_HAS_VARIADIC_TEMPLATES 00422 template<typename... IndexTypes> EIGEN_DEVICE_FUNC 00423 void resize(Index firstDimension, IndexTypes... otherDimensions) 00424 { 00425 // The number of dimensions used to resize a tensor must be equal to the rank of the tensor. 00426 EIGEN_STATIC_ASSERT(sizeof...(otherDimensions) + 1 == NumIndices, YOU_MADE_A_PROGRAMMING_MISTAKE) 00427 resize(array<Index, NumIndices>{{firstDimension, otherDimensions...}}); 00428 } 00429 #endif 00430 00432 EIGEN_DEVICE_FUNC void resize(const array<Index, NumIndices>& dimensions) 00433 { 00434 int i; 00435 Index size = Index(1); 00436 for (i = 0; i < NumIndices; i++) { 00437 internal::check_rows_cols_for_overflow<Dynamic>::run(size, dimensions[i]); 00438 size *= dimensions[i]; 00439 } 00440 #ifdef EIGEN_INITIALIZE_COEFFS 00441 bool size_changed = size != this->size(); 00442 m_storage.resize(size, dimensions); 00443 if(size_changed) EIGEN_INITIALIZE_COEFFS_IF_THAT_OPTION_IS_ENABLED 00444 #else 00445 m_storage.resize(size, dimensions); 00446 #endif 00447 } 00448 00449 // Why this overload, DSizes is derived from array ??? // 00450 EIGEN_DEVICE_FUNC void resize(const DSizes<Index, NumIndices>& dimensions) { 00451 array<Index, NumIndices> dims; 00452 for (int i = 0; i < NumIndices; ++i) { 00453 dims[i] = dimensions[i]; 00454 } 00455 resize(dims); 00456 } 00457 00458 EIGEN_DEVICE_FUNC 00459 void resize() 00460 { 00461 EIGEN_STATIC_ASSERT(NumIndices == 0, YOU_MADE_A_PROGRAMMING_MISTAKE); 00462 // Nothing to do: rank 0 tensors have fixed size 00463 } 00464 00466 #ifdef EIGEN_HAS_SFINAE 00467 template<typename CustomDimension, 00468 EIGEN_SFINAE_ENABLE_IF( !(isOfNormalIndex<CustomDimension>::value) ) 00469 > 00470 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void resize(CustomDimension& dimensions) 00471 { 00472 resize(internal::customIndices2Array<Index,NumIndices>(dimensions)); 00473 } 00474 #endif 00475 00476 #ifndef EIGEN_EMULATE_CXX11_META_H 00477 template <typename std::ptrdiff_t... Indices> 00478 EIGEN_DEVICE_FUNC 00479 void resize(const Sizes<Indices...>& dimensions) { 00480 array<Index, NumIndices> dims; 00481 for (int i = 0; i < NumIndices; ++i) { 00482 dims[i] = static_cast<Index>(dimensions[i]); 00483 } 00484 resize(dims); 00485 } 00486 #else 00487 template <std::size_t V1, std::size_t V2, std::size_t V3, std::size_t V4, std::size_t V5> 00488 EIGEN_DEVICE_FUNC 00489 void resize(const Sizes<V1, V2, V3, V4, V5>& dimensions) { 00490 array<Index, NumIndices> dims; 00491 for (int i = 0; i < NumIndices; ++i) { 00492 dims[i] = static_cast<Index>(dimensions[i]); 00493 } 00494 resize(dims); 00495 } 00496 #endif 00497 00498 protected: 00499 00500 bool checkIndexRange(const array<Index, NumIndices>& indices) const 00501 { 00502 using internal::array_apply_and_reduce; 00503 using internal::array_zip_and_reduce; 00504 using internal::greater_equal_zero_op; 00505 using internal::logical_and_op; 00506 using internal::lesser_op; 00507 00508 return 00509 // check whether the indices are all >= 0 00510 array_apply_and_reduce<logical_and_op, greater_equal_zero_op>(indices) && 00511 // check whether the indices fit in the dimensions 00512 array_zip_and_reduce<logical_and_op, lesser_op>(indices, m_storage.dimensions()); 00513 } 00514 00515 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Index linearizedIndex(const array<Index, NumIndices>& indices) const 00516 { 00517 if (Options&RowMajor) { 00518 return m_storage.dimensions().IndexOfRowMajor(indices); 00519 } else { 00520 return m_storage.dimensions().IndexOfColMajor(indices); 00521 } 00522 } 00523 }; 00524 00525 } // end namespace Eigen 00526 00527 #endif // EIGEN_CXX11_TENSOR_TENSOR_H