blob: 1fec9c18823373a32542da2f23815b72df691720 [file] [log] [blame]
#include "caffe2/operators/transpose_op.h"
namespace caffe2 {
REGISTER_CPU_OPERATOR(Transpose, TransposeOp<CPUContext>);
OPERATOR_SCHEMA(Transpose)
.NumInputs(1)
.NumOutputs(1)
.TensorInferenceFunction([](const OperatorDef& def,
const vector<TensorShape>& in) {
ArgumentHelper helper(def);
vector<int> axes = helper.GetRepeatedArgument<int>("axes");
vector<TensorShape> out(1);
out[0].set_data_type(in[0].data_type());
if (axes.empty()) {
for (auto axis = in [0].dims().rbegin(); axis != in[0].dims().rend();
++axis) {
out[0].add_dims(*axis);
}
} else {
auto tensor_size = in[0].dims().size();
auto valid_axes =
std::all_of(axes.begin(), axes.end(), [&tensor_size](int& axis) {
return axis >= 0 && axis < tensor_size;
});
CAFFE_ENFORCE(valid_axes, "Axes argument passed in had invalid values");
CAFFE_ENFORCE(
// NOLINTNEXTLINE(clang-diagnostic-sign-compare)
axes.size() == tensor_size,
"Axes argument passed in had the incorrect size");
// NOLINTNEXTLINE(modernize-loop-convert)
for (auto axis = axes.begin(); axis != axes.end(); ++axis) {
out[0].add_dims(in[0].dims().Get(*axis));
}
}
return out;
})
.SetDoc(R"DOC(
Transpose the input tensor by permuting the axes of the input according
to the `axes` argument. Similar to numpy's
[transpose](https://docs.scipy.org/doc/numpy/reference/generated/numpy.transpose.html)
function.
For example, when axes=(1, 0, 2), given an input tensor of shape
(1, 2, 3), the output shape will be (2, 1, 3).
Github Links:
- https://github.com/pytorch/pytorch/blob/main/caffe2/operators/transpose_op.cc
<details>
<summary> <b>Example</b> </summary>
**Code**
```
workspace.ResetWorkspace()
op = core.CreateOperator(
"Transpose",
["X"],
["Y"],
axes=(0,3,1,2)
)
x = np.random.rand(1,32,32,3)
workspace.FeedBlob("X", x)
print("X.shape (NHWC order):", workspace.FetchBlob("X").shape)
workspace.RunOperatorOnce(op)
print("Y.shape (NCHW order):", workspace.FetchBlob("Y").shape)
```
**Result**
```
X.shape (NHWC order): (1, 32, 32, 3)
Y.shape (NCHW order): (1, 3, 32, 32)
```
</details>
)DOC")
.Arg(
"axes",
"*(type: Tuple(int))* Order to permute axes of input tensor. Reverses "
"the dimensions by default.")
.Input(0, "X", "*(type: Tensor)* Input tensor.")
.Output(0, "Y", "*(type: Tensor)* Transposed output.")
.InheritOnnxSchema();
class GetTransposeGradient : public GradientMakerBase {
using GradientMakerBase::GradientMakerBase;
// We will create our own arguments.
bool CopyArguments() const override {
return false;
}
vector<OperatorDef> GetGradientDefs() override {
auto ops = SingleGradientDef(
"Transpose", "", vector<string>{GO(0)}, vector<string>{GI(0)});
ops[0].mutable_arg()->CopyFrom(Def().arg());
if (ArgumentHelper::HasArgument(Def(), "axes")) {
// If axes is specified, we will need to figure out the inverse index.
const Argument& old_axes = GetArgument(Def(), "axes");
const int axes_size = old_axes.ints_size();
Argument* new_arg = GetMutableArgument("axes", false, &ops[0]);
for (int i = 0; i < axes_size; ++i) {
new_arg->set_ints(old_axes.ints(i), i);
}
}
return ops;
}
};
REGISTER_GRADIENT(Transpose, GetTransposeGradient);
} // namespace caffe2