mirror of
https://github.com/pytorch/pytorch.git
synced 2025-10-20 21:14:14 +08:00
Summary: Pull Request resolved: https://github.com/pytorch/pytorch/pull/70248 Modified loops in files under fbsource/fbcode/caffe2/ from the format ``` for(TYPE var=x0;var<x_max;x++) ``` to the format ``` for(const auto var: irange(xmax)) ``` This was achieved by running r-barnes's loop upgrader script (D28874212) with some modification to exclude all files under /torch/jit and a number of reversions or unused variable suppression warnings added by hand. Test Plan: Sandcastle Reviewed By: malfet Differential Revision: D32813863 fbshipit-source-id: 527244b4a2b220fdfe7f17dee3599603f492a2ca
183 lines
6.1 KiB
C++
183 lines
6.1 KiB
C++
#pragma once
|
|
|
|
#include "caffe2/core/operator.h"
|
|
#include "c10/util/irange.h"
|
|
|
|
#include <algorithm>
|
|
#include <cmath>
|
|
#include <limits>
|
|
|
|
namespace caffe2 {
|
|
|
|
template <class Context>
|
|
class SelfBinningHistogramOp final : public Operator<Context> {
|
|
public:
|
|
USE_OPERATOR_CONTEXT_FUNCTIONS;
|
|
template <class... Args>
|
|
explicit SelfBinningHistogramOp(Args&&... args)
|
|
: Operator<Context>(std::forward<Args>(args)...),
|
|
num_bins_(this->template GetSingleArgument<int>("num_bins", 0)),
|
|
num_edges_(num_bins_ + 1),
|
|
bin_spacing_(this->template GetSingleArgument<std::string>(
|
|
"bin_spacing",
|
|
"linear")),
|
|
logspace_start_(this->template GetSingleArgument<float>("logspace_start", 1e-24)),
|
|
abs_(this->template GetSingleArgument<bool>("abs", false))
|
|
{
|
|
CAFFE_ENFORCE_GE(
|
|
num_bins_, 1, "Number of bins must be greater than or equal to 1.");
|
|
CAFFE_ENFORCE(
|
|
bin_spacing_ == "linear" || bin_spacing_ == "logarithmic",
|
|
"Bin spacing can be one of 'linear' or 'logarithmic'."
|
|
);
|
|
CAFFE_ENFORCE_GT(
|
|
logspace_start_, 0,
|
|
"Logarithmic spacing base is a multiplier and is expected to be >1.");
|
|
}
|
|
|
|
bool RunOnDevice() override {
|
|
return DispatchHelper<TensorTypes<float, double>>::call(this, Input(0));
|
|
}
|
|
|
|
template <typename T>
|
|
bool DoRunWithType() {
|
|
CheckInputs();
|
|
|
|
// Scale the range so that the last count is always 0.
|
|
const T RANGE_SCALING = 1.0001;
|
|
|
|
const auto* histogram_values = Output(HISTOGRAM_VALUES);
|
|
histogram_values->Resize(num_edges_);
|
|
auto* histogram_values_data = histogram_values->template mutable_data<T>();
|
|
const auto* histogram_counts = Output(HISTOGRAM_COUNTS);
|
|
histogram_counts->Resize(num_edges_);
|
|
auto* histogram_counts_data =
|
|
histogram_counts->template mutable_data<int64_t>();
|
|
|
|
// Calculate the max and min.
|
|
bool first_seen = false;
|
|
// 0 initialization is arbitrary here to suppress linter warnings.
|
|
// The actual initialization check happens through the first_seen variable.
|
|
T max = 0;
|
|
T min = 0;
|
|
int64_t total_count = 0;
|
|
for (const auto input_idx : c10::irange(InputSize())) {
|
|
const auto& x = Input(input_idx);
|
|
const int64_t N = x.numel();
|
|
total_count += N;
|
|
const auto* x_data = x.template data<T>();
|
|
for (const auto data_idx : c10::irange(N)) {
|
|
const T val = this->abs_ ? std::abs(x_data[data_idx]) : x_data[data_idx];
|
|
if (!first_seen) {
|
|
max = val;
|
|
min = val;
|
|
first_seen = true;
|
|
} else {
|
|
max = std::max(val, max);
|
|
min = std::min(val, min);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!first_seen) {
|
|
math::Set<T, Context>(num_edges_, 0, histogram_values_data, &context_);
|
|
math::Set<int64_t, Context>(
|
|
num_edges_, 0, histogram_counts_data, &context_);
|
|
return true;
|
|
}
|
|
|
|
CAFFE_ENFORCE(min <= max, "Incorrect min-max computation min=", min, " max=", max);
|
|
T scaled_max = 0; // this is set in both branches
|
|
if (bin_spacing_ == "linear") {
|
|
// Let's scale the range so that the last count is 0.
|
|
scaled_max = min + (max - min) * RANGE_SCALING;
|
|
T scaled_range = (scaled_max - min);
|
|
// Avoid underflow by calculating advancement through multiplication.
|
|
for (const auto i : c10::irange(num_edges_)) {
|
|
T advancement_ratio = T(i) / num_bins_;
|
|
histogram_values_data[i] = min + advancement_ratio * scaled_range;
|
|
}
|
|
} else if (bin_spacing_ == "logarithmic") {
|
|
// First, we need to sanitize the range.
|
|
if (min < logspace_start_) {
|
|
min = logspace_start_;
|
|
}
|
|
if (max < logspace_start_) {
|
|
max = logspace_start_;
|
|
}
|
|
T linear_range = max - min;
|
|
linear_range = linear_range * RANGE_SCALING;
|
|
scaled_max = min + linear_range;
|
|
// Calculate base interval using geometric sum.
|
|
// Simply: multiplier = exp((log(max) - log(min))/N)
|
|
// Avoid underflow by delaying division and exp.
|
|
T log_multiplier_numerator =log(scaled_max) - log(min);
|
|
// Avoid underflow by:
|
|
// - Calculating each advancement separately for each i.
|
|
for (const auto i : c10::irange(num_edges_)) {
|
|
T advancement_ratio = T(i)/num_bins_;
|
|
histogram_values_data[i] = min * exp(log_multiplier_numerator * advancement_ratio);
|
|
}
|
|
}
|
|
|
|
math::Set<int64_t, Context>(
|
|
num_edges_, 0, histogram_counts_data, &context_);
|
|
if (histogram_values_data[num_edges_-1] <= max) {
|
|
// In cases of min&max being equal (or any unexpected numerical underflow) we
|
|
// may not have a final edge larger than the max.
|
|
histogram_values_data[num_edges_-1] = scaled_max;
|
|
histogram_counts_data[0] = total_count;
|
|
}
|
|
else {
|
|
for (const auto input_idx : c10::irange(InputSize())) {
|
|
const auto& x = Input(input_idx);
|
|
const int64_t N = x.numel();
|
|
const auto* x_data = x.template data<T>();
|
|
for (const auto data_idx : c10::irange(N)) {
|
|
const T val = this->abs_ ? std::abs(x_data[data_idx]) : x_data[data_idx];
|
|
const auto bisection_it = std::upper_bound(
|
|
histogram_values_data,
|
|
histogram_values_data + num_edges_,
|
|
val);
|
|
const int bisection_idx = bisection_it - histogram_values_data;
|
|
if (bisection_idx > 0 && bisection_idx < num_edges_) {
|
|
histogram_counts_data[bisection_idx - 1]++;
|
|
}
|
|
if (bisection_idx == 0) {
|
|
histogram_counts_data[0]++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
protected:
|
|
OUTPUT_TAGS(HISTOGRAM_VALUES, HISTOGRAM_COUNTS);
|
|
|
|
private:
|
|
int num_bins_;
|
|
int num_edges_;
|
|
std::string bin_spacing_;
|
|
float logspace_start_;
|
|
bool abs_; // automatically apply abs() on the input values
|
|
|
|
void CheckInputs() {
|
|
const auto& input_zero = Input(0);
|
|
for (const auto i : c10::irange(1, InputSize())) {
|
|
CAFFE_ENFORCE_EQ(
|
|
Input(i).dtype(),
|
|
input_zero.dtype(),
|
|
"All inputs must have the same type; expected ",
|
|
input_zero.dtype().name(),
|
|
" but got ",
|
|
Input(i).dtype().name(),
|
|
" for input ",
|
|
i);
|
|
}
|
|
}
|
|
};
|
|
|
|
} // namespace caffe2
|