blob: 52f6c10d1bf5fe6345e10710045e9913c6fdc373 [file] [log] [blame]
#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