|  | #include "caffe2/operators/ensure_clipped_op.h" | 
|  | #include "caffe2/core/tensor.h" | 
|  |  | 
|  | namespace caffe2 { | 
|  |  | 
|  | template <> | 
|  | template <typename SIndex> | 
|  | bool EnsureClippedOp<float, CPUContext>::DoRunWithType() { | 
|  | Output(OUTPUT_PARAM)->ResizeLike(Input(PARAM)); | 
|  | const auto* indices = Input(INDICES).template data<SIndex>(); | 
|  | const auto* paramIn = Input(PARAM).template data<float>(); | 
|  | auto* paramOut = Output(OUTPUT_PARAM)->template mutable_data<float>(); | 
|  | CAFFE_ENFORCE_EQ(paramIn, paramOut); | 
|  | // n: number of sparse embeddings to be normalized | 
|  | auto n = Input(INDICES).numel(); | 
|  | if (n == 0) { | 
|  | return true; | 
|  | } | 
|  | // embedding length, e.g. 32, 64, 128 | 
|  | auto block_size = Input(GRAD).numel() / n; | 
|  | for (int i = 0; i < n; ++i) { | 
|  | auto idx = indices[i]; | 
|  | auto offsetIdx = idx * block_size; | 
|  | EigenVectorMap<float>(paramOut + offsetIdx, block_size) = | 
|  | ConstEigenVectorMap<float>(paramIn + offsetIdx, block_size) | 
|  | .cwiseMax(min_) | 
|  | .cwiseMin(max_); | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | REGISTER_CPU_OPERATOR(EnsureClipped, EnsureClippedOp<float, CPUContext>); | 
|  | OPERATOR_SCHEMA(EnsureClipped) | 
|  | .NumInputs(1, 3) | 
|  | .NumOutputs(1) | 
|  | .Input(0, "param", "Parameters to be normalized") | 
|  | .Input(1, "indices", "Sparse indices, only needed for sparse param") | 
|  | .Input(2, "grad", "Gradient computed, only needed for sparse param") | 
|  | .Output(0, "output_param", "param ensured to be clipped within range") | 
|  | .AllowInplace({{0, 0}}) | 
|  | .SetDoc(R"DOC( | 
|  | Given a tensor, apply clip after gradient is applied; when the param is sparse as | 
|  | indicated by valid indices and grad, in-place is required | 
|  | )DOC"); | 
|  |  | 
|  | SHOULD_NOT_DO_GRADIENT(EnsureClipped); | 
|  | } // namespace caffe2 |