| #include "caffe2/operators/tile_op.h" |
| |
| namespace caffe2 { |
| |
| REGISTER_CPU_OPERATOR(Tile, TileOp<CPUContext>); |
| REGISTER_CPU_OPERATOR(TileGradient, TileGradientOp<float, CPUContext>); |
| |
| OPERATOR_SCHEMA(Tile) |
| .NumInputs(1, 3) |
| .NumOutputs(1) |
| .TensorInferenceFunction( |
| [](const OperatorDef& def, const vector<TensorShape>& in) { |
| vector<TensorShape> out(1); |
| out[0] = TensorShape(in[0]); |
| ArgumentHelper helper(def); |
| |
| auto tiles = helper.GetSingleArgument<int32_t>("tiles", 1); |
| auto axis = helper.GetSingleArgument<int32_t>("axis", 0); |
| if (in.size() > 1) { |
| // Tile or axis is specified as input; we can't determine |
| // the size |
| out[0].set_unknown_shape(true); |
| } else { |
| const auto canonical_axis = |
| canonical_axis_index_(axis, out[0].dims().size()); |
| out[0].set_dims( |
| canonical_axis, out[0].dims().Get(canonical_axis) * tiles); |
| } |
| return out; |
| }) |
| .SetDoc(R"DOC( |
| Constructs a tensor by tiling a given tensor along a specified axis. This operation creates a new tensor by replicating the input tensor a number of times specified by the `tiles` argument along the `axis` dimension. The output tensor's `axis` dimension has $(X.dims(axis) * tiles)$ elements. |
| |
| Github Links: |
| - https://github.com/pytorch/pytorch/blob/master/caffe2/operators/tile_op.cc |
| |
| <details> |
| |
| <summary> <b>Example</b> </summary> |
| |
| **Code** |
| |
| ``` |
| |
| workspace.ResetWorkspace() |
| |
| op = core.CreateOperator( |
| "Tile", |
| ["X", "tiles", "axis"], |
| ["Y"] |
| ) |
| |
| workspace.FeedBlob("X", np.random.randint(10, size=(5,5))) |
| workspace.FeedBlob("tiles", np.array([5]).astype(np.int32)) |
| workspace.FeedBlob("axis", np.array([1]).astype(np.int32)) |
| print("X:", workspace.FetchBlob("X")) |
| workspace.RunOperatorOnce(op) |
| print("Y:", workspace.FetchBlob("Y")) |
| |
| ``` |
| |
| **Result** |
| |
| ``` |
| |
| X: |
| [[9 1 7 1 3] |
| [2 3 6 2 5] |
| [0 9 2 6 4] |
| [5 8 1 5 9] |
| [2 0 1 3 7]] |
| Y: |
| [[9 1 7 1 3 9 1 7 1 3 9 1 7 1 3 9 1 7 1 3 9 1 7 1 3] |
| [2 3 6 2 5 2 3 6 2 5 2 3 6 2 5 2 3 6 2 5 2 3 6 2 5] |
| [0 9 2 6 4 0 9 2 6 4 0 9 2 6 4 0 9 2 6 4 0 9 2 6 4] |
| [5 8 1 5 9 5 8 1 5 9 5 8 1 5 9 5 8 1 5 9 5 8 1 5 9] |
| [2 0 1 3 7 2 0 1 3 7 2 0 1 3 7 2 0 1 3 7 2 0 1 3 7]] |
| |
| ``` |
| |
| </details> |
| |
| )DOC") |
| .Arg("tiles", "(*int*): number of replicas") |
| .Arg("axis", "(*int*): axis to replicate along") |
| .Input(0, "X", "(*Tensor*): input tensor") |
| .Input( |
| 1, |
| "tiles", |
| "(*Tensor`<int>`*): [OPTIONAL] number of replicas (overrides `tiles` argument)") |
| .Input( |
| 2, |
| "axis", |
| "(*Tensor`<int>`*): [OPTIONAL] axis to replicate along (overrides `axis` argument)") |
| .Output(0, "Y", "(*Tensor*): output tensor") |
| .InheritOnnxSchema(); |
| |
| OPERATOR_SCHEMA(TileGradient).NumInputs(1, 3).NumOutputs(1); |
| |
| class GetTileGradient : public GradientMakerBase { |
| using GradientMakerBase::GradientMakerBase; |
| vector<OperatorDef> GetGradientDefs() override { |
| // Check whether the tiles/axis information was |
| // passed through input arguments |
| vector<std::string> g_inputs({GO(0)}); |
| if (Def().input_size() > 1) { |
| g_inputs.push_back(I(1)); |
| } |
| if (Def().input_size() > 2) { |
| g_inputs.push_back(I(2)); |
| } |
| return SingleGradientDef( |
| "TileGradient", "", g_inputs, vector<string>{GI(0)}); |
| } |
| }; |
| |
| REGISTER_GRADIENT(Tile, GetTileGradient); |
| |
| } // namespace caffe2 |