// Copyright 2020 Google LLC
//
// This source code is licensed under the BSD-style license found in the
// LICENSE file in the root directory of this source tree.

#include <xnnpack.h>

#include "subgraph-tester.h"
#include <gtest/gtest.h>

namespace xnnpack {

TEST(SUBGRAPH_NCHW, single_conv) {
  auto tester = SubgraphTester(4);
  tester
    .AddDynamicTensorF32({1, 256, 256, 3}, 0)
    .AddStaticTensorF32({32, 3, 3, 3}, TensorType::kDense, 1)
    .AddStaticTensorF32({32}, TensorType::kDense, 2)
    .AddDynamicTensorF32({1, 128, 128, 32}, 3)
    .AddConvolution2D(
        ConvolutionParams{
          Padding{1, 1, 1, 1},
          Kernel{3, 3},
          Subsampling{2, 2},
          Dilation{1, 1},
          /*groups=*/ 1,
          /*group_input_channels=*/ 3,
          /*group_output_channels=*/ 32,
        }, 0, 1, 2, 3)
    .Optimize()
    .RewriteForNchw();

  ASSERT_EQ(tester.GetLayout(0), xnn_layout_type_nhwc);
  ASSERT_EQ(tester.GetLayout(3), xnn_layout_type_nhwc);
}

TEST(SUBGRAPH_NCHW, single_conv_and_global_average_pooling) {
  auto tester = SubgraphTester(5);
  tester
    .AddDynamicTensorF32({1, 256, 256, 3}, 0)
    .AddStaticTensorF32({32, 3, 3, 3}, TensorType::kDense, 1)
    .AddStaticTensorF32({32}, TensorType::kDense, 2)
    .AddDynamicTensorF32({1, 128, 128, 32}, 3)
    .AddOutputTensorF32({32}, 4)
    .AddConvolution2D(
        ConvolutionParams{
          Padding{1, 1, 1, 1},
          Kernel{3, 3},
          Subsampling{2, 2},
          Dilation{1, 1},
          /*groups=*/ 1,
          /*group_input_channels=*/ 3,
          /*group_output_channels=*/ 32,
        }, 0, 1, 2, 3)
    .AddGlobalAveragePooling(3, 4)
    .Optimize()
    .RewriteForNchw();

  ASSERT_EQ(tester.GetLayout(0), xnn_layout_type_nhwc);
  ASSERT_EQ(tester.GetLayout(3), xnn_layout_type_nhwc);
  ASSERT_EQ(tester.GetLayout(4), xnn_layout_type_nhwc);
}

TEST(SUBGRAPH_NCHW, pixelwise_conv_sandwich) {
  auto tester = SubgraphTester(8);
  tester
    .AddDynamicTensorF32({1, 256, 256, 3}, 0)
    .AddStaticTensorF32({8, 3, 3, 3}, TensorType::kDense, 1)
    .AddStaticTensorF32({8}, TensorType::kDense, 2)
    .AddDynamicTensorF32({1, 128, 128, 8}, 3)
    .AddStaticTensorF32({4, 1, 1, 8}, TensorType::kSparse, 4)
    .AddStaticTensorF32({4}, TensorType::kDense, 5)
    .AddDynamicTensorF32({1, 128, 128, 4}, 6)
    .AddOutputTensorF32({1, 4}, 7)
    .AddConvolution2D(
        ConvolutionParams{
          Padding{1, 1, 1, 1},
          Kernel{3, 3},
          Subsampling{2, 2},
          Dilation{1, 1},
          /*groups=*/ 1,
          /*group_input_channels=*/ 3,
          /*group_output_channels=*/ 8
        }, 0, 1, 2, 3)
    .AddConvolution2D(
        ConvolutionParams{
          Padding{0, 0, 0, 0},
          Kernel{1, 1},
          Subsampling{1, 1},
          Dilation{1, 1},
          /*groups=*/ 1,
          /*group_input_channels=*/ 8,
          /*group_output_channels=*/ 4
        }, 3, 4, 5, 6)
    .AddGlobalAveragePooling(6, 7)
    .Optimize()
    .RewriteForNchw();

  ASSERT_EQ(tester.GetLayout(0), xnn_layout_type_nhwc);
  ASSERT_EQ(tester.GetLayout(3), xnn_layout_type_nchw);
  ASSERT_EQ(tester.GetLayout(6), xnn_layout_type_nchw);
  ASSERT_EQ(tester.GetLayout(7), xnn_layout_type_nhwc);
}

TEST(SUBGRAPH_NCHW, bottleneck) {
  auto tester = SubgraphTester(15);
  tester
    .AddDynamicTensorF32({1, 256, 256, 3}, 0)
    .AddStaticTensorF32({8, 3, 3, 3}, TensorType::kDense, 1)
    .AddStaticTensorF32({8}, TensorType::kDense, 2)
    .AddDynamicTensorF32({1, 128, 128, 8}, 3)
    .AddStaticTensorF32({4, 1, 1, 8}, TensorType::kSparse, 4)
    .AddStaticTensorF32({4}, TensorType::kDense, 5)
    .AddDynamicTensorF32({1, 128, 128, 4}, 6)
    .AddStaticTensorF32({1, 3, 3, 4}, TensorType::kDense, 7)
    .AddStaticTensorF32({4}, TensorType::kDense, 8)
    .AddDynamicTensorF32({1, 128, 128, 4}, 9)
    .AddStaticTensorF32({8, 1, 1, 4}, TensorType::kSparse, 10)
    .AddStaticTensorF32({8}, TensorType::kDense, 11)
    .AddDynamicTensorF32({1, 128, 128, 8}, 12)
    .AddDynamicTensorF32({1, 128, 128, 8}, 13)
    .AddDynamicTensorF32({1, 128, 128, 8}, 13)
    .AddOutputTensorF32({1, 8}, 14)
    .AddConvolution2D(
        ConvolutionParams{
          Padding{1, 1, 1, 1},
          Kernel{3, 3},
          Subsampling{2, 2},
          Dilation{1, 1},
          /*groups=*/ 1,
          /*group_input_channels=*/ 3,
          /*group_output_channels=*/ 8
        }, 0, 1, 2, 3)
    .AddConvolution2D(
        ConvolutionParams{
          Padding{0, 0, 0, 0},
          Kernel{1, 1},
          Subsampling{1, 1},
          Dilation{1, 1},
          /*groups=*/ 1,
          /*group_input_channels=*/ 8,
          /*group_output_channels=*/ 4
        }, 3, 4, 5, 6)
    .AddDepthwiseConvolution2D(
        DepthwiseConvolutionParams{
          Padding{1, 1, 1, 1},
          Kernel{3, 3},
          Subsampling{1, 1},
          Dilation{1, 1},
          /*depth_multiplier=*/ 1,
          /*input_channels=*/ 4
        }, 6, 7, 8, 9)
    .AddConvolution2D(
        ConvolutionParams{
          Padding{0, 0, 0, 0},
          Kernel{1, 1},
          Subsampling{1, 1},
          Dilation{1, 1},
          /*groups=*/ 1,
          /*group_input_channels=*/ 8,
          /*group_output_channels=*/ 4
        }, 9, 10, 11, 12)
    .AddAddition(3, 12, 13)
    .AddGlobalAveragePooling(13, 14)
    .Optimize()
    .RewriteForNchw();

  ASSERT_EQ(tester.GetLayout(0), xnn_layout_type_nhwc);
  ASSERT_EQ(tester.GetLayout(3), xnn_layout_type_nchw);
  ASSERT_EQ(tester.GetLayout(6), xnn_layout_type_nchw);
  ASSERT_EQ(tester.GetLayout(9), xnn_layout_type_nchw);
  ASSERT_EQ(tester.GetLayout(12), xnn_layout_type_nchw);
  ASSERT_EQ(tester.GetLayout(13), xnn_layout_type_nchw);
  ASSERT_EQ(tester.GetLayout(14), xnn_layout_type_nhwc);
}

}  // namespace xnnpack
