#include "caffe2/operators/tile_op.h" namespace caffe2 { REGISTER_CPU_OPERATOR(Tile, TileOp); REGISTER_CPU_OPERATOR(TileGradient, TileGradientOp); OPERATOR_SCHEMA(Tile) .NumInputs(1, 3) .NumOutputs(1) .TensorInferenceFunction( [](const OperatorDef& def, const vector& in) { vector out(1); out[0] = TensorShape(in[0]); ArgumentHelper helper(def); auto tiles = helper.GetSingleArgument("tiles", 1); auto axis = helper.GetSingleArgument("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
Example **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]] ```
)DOC") .Arg("tiles", "(*int*): number of replicas") .Arg("axis", "(*int*): axis to replicate along") .Input(0, "X", "(*Tensor*): input tensor") .Input( 1, "tiles", "(*Tensor``*): [OPTIONAL] number of replicas (overrides `tiles` argument)") .Input( 2, "axis", "(*Tensor``*): [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 GetGradientDefs() override { // Check whether the tiles/axis information was // passed through input arguments vector 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{GI(0)}); } }; REGISTER_GRADIENT(Tile, GetTileGradient); } // namespace caffe2