|  | #pragma once | 
|  |  | 
|  | #include "caffe2/core/operator.h" | 
|  | #include "caffe2/utils/eigen_utils.h" | 
|  | #include "caffe2/utils/math.h" | 
|  |  | 
|  | namespace caffe2 { | 
|  |  | 
|  | template <typename T, class Context> | 
|  | class EnsureClippedOp final : public Operator<Context> { | 
|  | public: | 
|  | USE_OPERATOR_CONTEXT_FUNCTIONS; | 
|  |  | 
|  | template <class... Args> | 
|  | explicit EnsureClippedOp(Args&&... args) | 
|  | : Operator<Context>(std::forward<Args>(args)...), | 
|  | min_(std::numeric_limits<T>::lowest()), | 
|  | max_(std::numeric_limits<T>::max()) { | 
|  | if (HasArgument("min")) { | 
|  | min_ = static_cast<T>(this->template GetSingleArgument<float>("min", 0)); | 
|  | } | 
|  | if (HasArgument("max")) { | 
|  | max_ = static_cast<T>(this->template GetSingleArgument<float>("max", 0)); | 
|  | } | 
|  | } | 
|  |  | 
|  | bool RunOnDevice() override { | 
|  | if (InputSize() > INDICES) { | 
|  | // spares gradient, selective checking clipping | 
|  | CAFFE_ENFORCE_EQ( | 
|  | Input(PARAM).size_from_dim(1), | 
|  | Input(GRAD).size_from_dim(Input(INDICES).dim())); | 
|  | return DispatchHelper<TensorTypes<int32_t, int64_t>>::call( | 
|  | this, Input(INDICES)); | 
|  | } else { | 
|  | auto& X = Input(PARAM); | 
|  |  | 
|  | auto* Y = Output(OUTPUT_PARAM, X.sizes(), at::dtype<float>()); | 
|  | EigenVectorMap<float>(Y->template mutable_data<float>(), Y->numel()) = | 
|  | ConstEigenVectorMap<float>(X.template data<float>(), X.numel()) | 
|  | .cwiseMax(min_) | 
|  | .cwiseMin(max_); | 
|  | return true; | 
|  | } | 
|  | } | 
|  |  | 
|  | template <typename SIndex> | 
|  | bool DoRunWithType(); | 
|  |  | 
|  | protected: | 
|  | T min_; | 
|  | T max_; | 
|  | INPUT_TAGS(PARAM, INDICES, GRAD); | 
|  | OUTPUT_TAGS(OUTPUT_PARAM); | 
|  | }; | 
|  |  | 
|  | } // namespace caffe2 |