strided_slice_op.cc 10.8 KB
Newer Older
W
wangchaochaohu 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
/* Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License. */

#include "paddle/fluid/operators/strided_slice_op.h"
#include <algorithm>
#include <memory>
18
#include <string>
W
wangchaochaohu 已提交
19
#include <vector>
20
#include "paddle/fluid/operators/slice_op.h"
W
wangchaochaohu 已提交
21 22 23 24 25 26 27 28 29 30

namespace paddle {
namespace operators {

using Tensor = framework::Tensor;

class StridedSliceOp : public framework::OperatorWithKernel {
 public:
  using framework::OperatorWithKernel::OperatorWithKernel;

31
  void InferShape(framework::InferShapeContext *ctx) const override {
W
wangchaochaohu 已提交
32 33 34 35 36 37 38 39 40 41 42 43
    PADDLE_ENFORCE_EQ(ctx->HasInput("Input"), true,
                      "Input (Input) of slice op should not be null.");
    PADDLE_ENFORCE_EQ(ctx->HasOutput("Out"), true,
                      "Output (Out) of slice op should not be null.");

    auto in_dims = ctx->GetInputDim("Input");
    PADDLE_ENFORCE_LT(in_dims.size(), 7,
                      "The rank of input should be less than 7.");
    auto starts = ctx->Attrs().Get<std::vector<int>>("starts");
    auto ends = ctx->Attrs().Get<std::vector<int>>("ends");
    auto strides = ctx->Attrs().Get<std::vector<int>>("strides");
    auto axes = ctx->Attrs().Get<std::vector<int>>("axes");
44
    auto infer_flags = ctx->Attrs().Get<std::vector<int>>("infer_flags");
W
wangchaochaohu 已提交
45

46 47 48
    auto starts_size = starts.size();
    auto ends_size = ends.size();
    auto strides_size = strides.size();
W
wangchaochaohu 已提交
49

50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
    if (ctx->HasInputs("StartsTensorList")) {
      auto StartsTensorList = ctx->Inputs("StartsTensorList");
      PADDLE_ENFORCE_GT(StartsTensorList.size(), 0,
                        "StartsTensorList size can't be zero");
      starts_size = StartsTensorList.size();
    }
    if (ctx->HasInputs("EndsTensorList")) {
      auto EndsTensorList = ctx->Inputs("EndsTensorList");
      PADDLE_ENFORCE_GT(EndsTensorList.size(), 0,
                        "EndsTensorList size can't be zero");
      ends_size = EndsTensorList.size();
    }
    if (ctx->HasInputs("StridesTensorList")) {
      auto StridesTensorList = ctx->Inputs("StridesTensorList");
      PADDLE_ENFORCE_GT(StridesTensorList.size(), 0,
                        "StridesTensorList size can't be zero");
      strides_size = StridesTensorList.size();
    }

    auto tensor_input = false;
    if (ctx->HasInput("EndsTensor") || ctx->HasInput("StartsTensor") ||
        ctx->HasInput("StridesTensor")) {
      tensor_input = true;
    }
    if (ctx->HasInput("EndsTensor") == false) {
      PADDLE_ENFORCE_EQ(ends_size, axes.size(),
                        "The size of ends must be equal to the size of axes.");
    }
    if (ctx->HasInput("StartsTensor") == false) {
      PADDLE_ENFORCE_EQ(
          starts_size, axes.size(),
          "The size of starts must be equal to the size of axes.");
    }
    if (ctx->HasInput("StridesTensor") == false) {
      PADDLE_ENFORCE_EQ(
          strides_size, axes.size(),
          "The size of strides must be equal to the size of axes.");
    }
W
wangchaochaohu 已提交
88 89
    // we need to analysis strided slice op is valid for
    // the parameter that we get from python front
90 91 92 93
    std::vector<int> out_dims_vector(in_dims.size(), -1);
    if (!tensor_input) {
      StridedSliceOutDims(starts, ends, strides, axes, infer_flags, in_dims,
                          out_dims_vector.data(), axes.size(), true);
W
wangchaochaohu 已提交
94 95 96 97 98 99 100 101 102
    }
    framework::DDim out_dims(framework::make_ddim(out_dims_vector));

    ctx->SetOutputDim("Out", out_dims);
    ctx->ShareLoD("Input", /*->*/ "Out");
  }

 protected:
  framework::OpKernelType GetExpectedKernelType(
103
      const framework::ExecutionContext &ctx) const override {
W
wangchaochaohu 已提交
104 105 106
    return framework::OpKernelType(ctx.Input<Tensor>("Input")->type(),
                                   ctx.Input<Tensor>("Input")->place());
  }
107 108 109 110 111 112 113 114 115 116 117 118 119 120
  framework::OpKernelType GetKernelTypeForVar(
      const std::string &var_name, const Tensor &tensor,
      const framework::OpKernelType &expected_kernel_type) const override {
    if (var_name == "StartsTensor" || var_name == "EndsTensor" ||
        var_name == "StridesTensor") {
      return expected_kernel_type;
    }
    if (var_name == "StartsTensorList" || var_name == "EndsTensorList" ||
        var_name == "StridesTensorList") {
      return expected_kernel_type;
    }
    return framework::OpKernelType(expected_kernel_type.data_type_,
                                   tensor.place(), tensor.layout());
  }
W
wangchaochaohu 已提交
121 122 123 124 125 126
};

class StridedSliceOpMaker : public framework::OpProtoAndCheckerMaker {
 public:
  void Make() override {
    AddInput("Input", "Tensor of data to extract slices from.");
127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165
    AddOutput("Out", "Strided Sliced data tensor.");

    AddInput("StartsTensor",
             "(Tensor<int32>, optional) If provided, slice will use this."
             "It has the highest priority of StartsTensor, StartsTensorList "
             "and attr(starts).")
        .AsDispensable();
    AddInput("EndsTensor",
             "(Tensor<int32>, optional) If provided, slice will use this."
             "It has the highest priority of EndsTensor, EndsTensorList and "
             "attr(ends).")
        .AsDispensable();
    AddInput(
        "StridesTensor",
        "(Tensor<int32>, optional) If provided, slice will use this."
        "It has the highest priority of StridesTensor, StridesTensorList and "
        "attr(ends).")
        .AsDispensable();
    AddInput(
        "StartsTensorList",
        "(vector<Tensor<int32>>, optional) If provided, slice will use this."
        "The shape of the tensor in vector MUST BE [1]."
        "It has higher priority compare with attr(starts).")
        .AsDuplicable()
        .AsDispensable();
    AddInput(
        "EndsTensorList",
        "(vector<Tensor<int32>>, optional) If provided, slice will use this."
        "The shape of the tensor in vector MUST BE [1]."
        "It has higher priority compare with attr(ends).")
        .AsDuplicable()
        .AsDispensable();
    AddInput(
        "StridesTensorList",
        "(vector<Tensor<int32>>, optional) If provided, slice will use this."
        "The shape of the tensor in vector MUST BE [1]."
        "It has higher priority compare with attr(strides).")
        .AsDuplicable()
        .AsDispensable();
W
wangchaochaohu 已提交
166
    AddAttr<std::vector<int>>(
167
        "axes", "(list<int>) Axes that `starts` and `ends` apply to.");
W
wangchaochaohu 已提交
168
    AddAttr<std::vector<int>>(
169 170
        "starts", "(list<int>) Start indices for the strided slice start.")
        .SetDefault({});
W
wangchaochaohu 已提交
171
    AddAttr<std::vector<int>>("ends",
172 173
                              "(list<int>) End indices the tensor slice end")
        .SetDefault({});
W
wangchaochaohu 已提交
174
    AddAttr<std::vector<int>>(
175 176 177 178 179
        "strides", "(list<int> Stride step from the start to the end)")
        .SetDefault({});
    AddAttr<std::vector<int>>(
        "infer_flags", "(list<int>) Flags of inferring dims in attributes.")
        .SetDefault({});
W
wangchaochaohu 已提交
180 181 182 183 184 185 186 187 188 189 190 191 192 193 194
    AddComment(R"DOC(
Strided Slice Operator.
Instead of calling this op directly most users will want to use the
NumPy-style slicing syntax.
For Example:
data = fluid.layers.fill_constant(shape=[3, 3], value=0, dtype='int64')
y = fluid.layers.strided_slice(data, [0, 1], [1,0], [2, 3], [1, 1])
)DOC");
  }
};

class StridedSliceOpGrad : public framework::OperatorWithKernel {
 public:
  using framework::OperatorWithKernel::OperatorWithKernel;

195
  void InferShape(framework::InferShapeContext *ctx) const override {
W
wangchaochaohu 已提交
196 197 198 199 200 201 202 203 204 205 206
    PADDLE_ENFORCE_EQ(ctx->HasInput("Input"), true, "Input should not be null");
    PADDLE_ENFORCE_EQ(ctx->HasInput(framework::GradVarName("Out")), true,
                      "Input(Out@GRAD) should not be null");
    auto x_dims = ctx->GetInputDim("Input");
    auto x_grad_name = framework::GradVarName("Input");
    if (ctx->HasOutput(x_grad_name)) {
      ctx->SetOutputDim(x_grad_name, x_dims);
    }
  }

  framework::OpKernelType GetExpectedKernelType(
207
      const framework::ExecutionContext &ctx) const override {
W
wangchaochaohu 已提交
208 209 210 211
    return framework::OpKernelType(
        ctx.Input<framework::Tensor>(framework::GradVarName("Out"))->type(),
        ctx.GetPlace());
  }
212 213 214 215 216 217 218 219 220 221 222 223
  framework::OpKernelType GetKernelTypeForVar(
      const std::string &var_name, const Tensor &tensor,
      const framework::OpKernelType &expected_kernel_type) const override {
    if (var_name == "StartsTensor" || var_name == "EndsTensor") {
      return expected_kernel_type;
    }
    if (var_name == "StartsTensorList" || var_name == "EndsTensorList") {
      return expected_kernel_type;
    }
    return framework::OpKernelType(expected_kernel_type.data_type_,
                                   tensor.place(), tensor.layout());
  }
W
wangchaochaohu 已提交
224 225 226 227 228 229 230 231
};

class StridedSliceOpGradMaker : public framework::SingleGradOpDescMaker {
 public:
  using framework::SingleGradOpDescMaker::SingleGradOpDescMaker;

 protected:
  std::unique_ptr<framework::OpDesc> Apply() const override {
232
    auto *bind = new framework::OpDesc();
W
wangchaochaohu 已提交
233 234
    bind->SetInput(framework::GradVarName("Out"), OutputGrad("Out"));
    bind->SetInput("Input", Input("Input"));
235 236 237 238 239 240
    bind->SetInput("StartsTensor", Input("StartsTensor"));
    bind->SetInput("EndsTensor", Input("EndsTensor"));
    bind->SetInput("StridesTensor", Input("StridesTensor"));
    bind->SetInput("StartsTensorList", Input("StartsTensorList"));
    bind->SetInput("EndsTensorList", Input("EndsTensorList"));
    bind->SetInput("StridesTensorList", Input("StridesTensorList"));
W
wangchaochaohu 已提交
241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272
    bind->SetOutput(framework::GradVarName("Input"), InputGrad("Input"));
    bind->SetAttrMap(Attrs());
    bind->SetType("strided_slice_grad");
    return std::unique_ptr<framework::OpDesc>(bind);
  }
};

DECLARE_NO_NEED_BUFFER_VARS_INFERENCE(
    StridedSliceOpGradNoNeedBufferVarsInference, "Input");

}  // namespace operators
}  // namespace paddle

namespace ops = paddle::operators;
REGISTER_OPERATOR(strided_slice, ops::StridedSliceOp, ops::StridedSliceOpMaker,
                  ops::StridedSliceOpGradMaker);
REGISTER_OPERATOR(strided_slice_grad, ops::StridedSliceOpGrad,
                  ops::StridedSliceOpGradNoNeedBufferVarsInference);

REGISTER_OP_CPU_KERNEL(
    strided_slice,
    ops::StridedSliceKernel<paddle::platform::CPUDeviceContext, int>,
    ops::StridedSliceKernel<paddle::platform::CPUDeviceContext, int64_t>,
    ops::StridedSliceKernel<paddle::platform::CPUDeviceContext, float>,
    ops::StridedSliceKernel<paddle::platform::CPUDeviceContext, double>);

REGISTER_OP_CPU_KERNEL(
    strided_slice_grad,
    ops::StridedSliceGradKernel<paddle::platform::CPUDeviceContext, int>,
    ops::StridedSliceGradKernel<paddle::platform::CPUDeviceContext, int64_t>,
    ops::StridedSliceGradKernel<paddle::platform::CPUDeviceContext, float>,
    ops::StridedSliceGradKernel<paddle::platform::CPUDeviceContext, double>);