pytorch/test/cpp/tensorexpr/test_conv.cpp
Nikita Shulga 4cb534f92e Make PyTorch code-base clang-tidy compliant (#56892)
Summary:
This is an automatic change generated by the following script:
```
#!/usr/bin/env python3
from subprocess import check_output, check_call
import os

def get_compiled_files_list():
    import json
    with open("build/compile_commands.json") as f:
        data = json.load(f)
    files = [os.path.relpath(node['file']) for node in data]
    for idx, fname in enumerate(files):
        if fname.startswith('build/') and fname.endswith('.DEFAULT.cpp'):
            files[idx] = fname[len('build/'):-len('.DEFAULT.cpp')]
    return files

def run_clang_tidy(fname):
    check_call(["python3", "tools/clang_tidy.py", "-c", "build", "-x", fname,"-s"])
    changes = check_output(["git", "ls-files", "-m"])
    if len(changes) == 0:
        return
    check_call(["git", "commit","--all", "-m", f"NOLINT stubs for {fname}"])

def main():
    git_files = check_output(["git", "ls-files"]).decode("ascii").split("\n")
    compiled_files = get_compiled_files_list()
    for idx, fname in enumerate(git_files):
        if fname not in compiled_files:
            continue
        if fname.startswith("caffe2/contrib/aten/"):
            continue
        print(f"[{idx}/{len(git_files)}] Processing {fname}")
        run_clang_tidy(fname)

if __name__ == "__main__":
    main()
```

Pull Request resolved: https://github.com/pytorch/pytorch/pull/56892

Reviewed By: H-Huang

Differential Revision: D27991944

Pulled By: malfet

fbshipit-source-id: 5415e1eb2c1b34319a4f03024bfaa087007d7179
2021-04-28 14:10:25 -07:00

133 lines
4.3 KiB
C++

#include <gtest/gtest.h>
#include <torch/csrc/jit/tensorexpr/ir_simplifier.h>
#include <torch/csrc/jit/tensorexpr/llvm_codegen.h>
#include <torch/csrc/jit/tensorexpr/loopnest.h>
#include <torch/csrc/jit/tensorexpr/operators/conv2d.h>
#include <torch/csrc/jit/tensorexpr/tensor.h>
#include <torch/torch.h>
namespace torch {
namespace jit {
namespace te = torch::jit::tensorexpr;
namespace F = torch::nn::functional;
// Generate test data with few bits of precision, to minimize error
// accumulation from floating-point reordering.
static at::Tensor genTestData(c10::IntArrayRef args) {
// NOLINTNEXTLINE(cppcoreguidelines-avoid-magic-numbers)
return at::trunc(at::randn(args) * 256.0f) / 256.0f;
}
#ifdef TORCH_ENABLE_LLVM
TEST(Conv, DepthwiseConv2D) {
te::KernelScope kernel_scope;
constexpr int N = 1, C = 72, H = 56, W = 56;
constexpr int K = 72, R = 3, S = 3;
constexpr int kPad = 1, kStride = 2, kGroups = C;
constexpr int CperG = C / kGroups;
te::Placeholder input("input", te::kFloat, {N, C, H, W});
te::Placeholder weight("weight", te::kFloat, {K, CperG, R, S});
te::Placeholder bias("bias", te::kFloat, {K});
te::Tensor* output = te::conv2d_depthwise(
input.handle(), weight.handle(), bias.handle(), kStride, kPad, kGroups);
te::LoopNest loop({output});
loop.simplify();
loop.prepareForCodegen();
te::LLVMCodeGen cg(loop.root_stmt(), {input, weight, bias, output});
auto it = genTestData({N, C, H, W});
auto wt = genTestData({K, CperG, R, S});
auto bt = genTestData({K});
auto ref = at::conv2d(it, wt, bt, kStride, kPad, /*dilation=*/1, kGroups);
auto ot = at::zeros_like(ref);
cg.call(
{it.data_ptr<float>(),
wt.data_ptr<float>(),
bt.data_ptr<float>(),
ot.data_ptr<float>()});
ASSERT_TRUE(at::allclose(ref, ot));
}
#endif
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
TEST(Conv, Conv2D) {
te::KernelScope kernel_scope;
// Input dimensions.
constexpr int N = 1;
constexpr int C = 3;
constexpr int H = 11;
constexpr int W = 11;
// Filter dimensions.
constexpr int K = 8;
constexpr int R = 3;
constexpr int S = 3;
// Output dims.
constexpr int OH = H - R + 1;
constexpr int OW = W - S + 1;
// Compute reference result.
at::Tensor input = torch::randn({N, C, H, W});
at::Tensor filter = torch::randn({K, C, R, S});
at::Tensor ref = F::conv2d(input, filter);
// Double check the output size is as expected.
ASSERT_EQ(ref.size(0), N);
ASSERT_EQ(ref.size(1), K);
ASSERT_EQ(ref.size(2), OH);
ASSERT_EQ(ref.size(3), OW);
te::Placeholder inputB(te::BufHandle("input", {N, C, H, W}, te::kFloat));
te::Placeholder filterB(te::BufHandle("filter", {K, C, R, S}, te::kFloat));
te::Tensor* conv = te::Reduce(
"conv",
{{N, "n"}, {K, "k"}, {OH, "oh"}, {OW, "ow"}},
te::Sum(),
// FIXME: We have to use a `std::vector` parameter here and then unpack
// it, because we don't have an overload allowing for an arbitrary number
// of ExprHandle/VarHandle parameters.
[&](const std::vector<te::VarHandle>& v) {
auto const& n = v[0];
auto const& k = v[1];
auto const& oh = v[2];
auto const& ow = v[3];
auto const& c = v[4];
// NOLINTNEXTLINE(cppcoreguidelines-avoid-magic-numbers)
auto const& r = v[5];
// NOLINTNEXTLINE(cppcoreguidelines-avoid-magic-numbers)
auto const& s = v[6];
// FIXME: We have to use `call` and construct a `std::vector` here
// because the `operator()` overload is only specialized for a small
// number of arguments.
return inputB.load(n, c, oh + r, ow + s) * filterB.load(k, c, r, s);
},
// FIXME: If you forget one of the reduction dims, you get a segfault.
// Could that be caught by a verifier?
{{C, "c"}, {R, "r"}, {S, "s"}});
// FIXME: It'd be nice to have a single header that pulls in things like
// LoopNest, IRSimplifier, etc.
te::LoopNest loop({conv});
loop.prepareForCodegen();
te::Stmt* s = loop.root_stmt();
s = te::IRSimplifier::simplify(s);
at::Tensor result = at::empty_like(ref);
te::SimpleIREvaluator cg(s, {inputB, filterB, conv});
cg.call(
{input.data_ptr<float>(),
filter.data_ptr<float>(),
result.data_ptr<float>()});
ASSERT_TRUE(at::allclose(ref, result, 1e-3, 1e-3));
}
} // namespace jit
} // namespace torch