#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