| #include "caffe2/operators/partition_ops.h" |
| |
| namespace caffe2 { |
| namespace { |
| |
| REGISTER_CPU_OPERATOR(Partition, PartitionOp); |
| REGISTER_CPU_OPERATOR(LengthsPartition, LengthsPartitionOp); |
| REGISTER_CPU_OPERATOR(GatherByKey, GatherByKeyOp); |
| |
| OPERATOR_SCHEMA(GatherByKey) |
| .NumInputs(2, INT_MAX) |
| .NumOutputs(1) |
| .SetDoc(R"DOC( |
| Inverse operation of Partition. |
| |
| Takes the original, full 'keys' tensor followed by sharded value tensors, |
| and returns the full value tensor, combined using the same hash used in |
| Partition. |
| )DOC") |
| .Input( |
| 0, |
| "keys", |
| "The first input is the full keys tensor" |
| " (same as the first input of Partition).") |
| .Input( |
| 1, |
| "sharded_values", |
| "Subsequented inputs are sharded values tensors.") |
| .Output(0, "values", "Reconstructed values tensor."); |
| |
| OPERATOR_SCHEMA(Partition) |
| .NumInputsOutputs([](int in, int out) { |
| return in > 0 && out > 0 && out % in == 0; |
| }) |
| .SetDoc(R"DOC( |
| Splits the input int tensor into multiple ones according to the first tensor. |
| |
| Takes the first input and partitions it to shards according to the remainder of |
| values modulo the number of partitions. It requires that the first tensor is of |
| integral type. The number of partitions is derived as (num_output / num_input). |
| |
| If additional inputs are present they must have the same shape as the first |
| input, optionally with extra trailing dimensions. They will be partitioned |
| accordingly to the first input. |
| |
| Optional arg 'pack_first_input' transforms the first tensor values as |
| X_ij / num_partitions. |
| |
| Outputs are ordered as |
| X_0_part_0, X_1_part_0, ..., X_N-1_part_0, X_0_part_1, ..., X_N-1_part_K-1 |
| )DOC") |
| .Arg( |
| "pack_first_input", |
| "(int, default 0) If set, the operator transforms " |
| "the first tensor values as floor(X_ij / num_partitions)") |
| .Input( |
| 0, |
| "input", |
| "Input tensor containing data to be partitioned. The " |
| "number of input tensors might be greater than 1 but must have the " |
| "same shape as the previous tensors.") |
| .Output( |
| 0, |
| "partitions", |
| "Output Partitions. The number of output tensors has to be a " |
| "multiple of the number of input tensors."); |
| |
| OPERATOR_SCHEMA(LengthsPartition) |
| .NumInputsOutputs([](int in, int out) { |
| return in >= 2 && out > 0 && out % in == 0; |
| }) |
| .SetDoc(R"DOC( |
| LengthsPartition splits the input int tensor into multiple ones according to the |
| second tensor. The first dimension is expected to be the tensor that describes |
| lengths of the elements. |
| |
| Takes the second input and partitions it to shards according to the remainder of |
| values modulo the number of partitions. It requires the second tensor to be |
| a 1D-tensor of the integral type. The first tensor should be 1D-tensor of int32 |
| that would represent the lengths of the elements in the input. The number of |
| partitions is derived as (num_output / num_input). |
| |
| If additional inputs are present they must have the same shape as the first |
| input, optionally with extra trailing dimensions. They will be partitioned |
| accordingly to the first input. |
| |
| Optional arg 'pack_first_input' transforms the first tensor values as |
| X_ij / num_partitions. |
| |
| Outputs are ordered as |
| X_0_part_0, X_1_part_0, ..., X_N-1_part_0, X_0_part_1, ..., X_N-1_part_K-1 |
| )DOC") |
| .Arg( |
| "pack_first_input", |
| "(int, default 0) If set, the operator transforms " |
| "the first tensor values as floor(X_ij / num_partitions)") |
| .Input( |
| 0, |
| "input", |
| "Input tensor containing data to be partitioned. The " |
| "number of input tensors might be greater than 1 but must have the " |
| "same shape as the previous tensors.") |
| .Output( |
| 0, |
| "partitions", |
| "Output Partitions. The number of output tensors has to be a " |
| "multiple of the number of input tensors."); |
| |
| namespace { |
| |
| class GetGatherByKeyGradient : public GradientMakerBase { |
| using GradientMakerBase::GradientMakerBase; |
| std::vector<OperatorDef> GetGradientDefs() override { |
| ArgumentHelper argsHelper(def_); |
| auto pack_first_input = |
| argsHelper.GetSingleArgument<int>("pack_first_input", 0); |
| |
| Argument packArg = MakeArgument<int>("pack_first_input", pack_first_input); |
| if (g_output_[0].IsDense()) { |
| std::vector<std::string> inputs; |
| // NOLINTNEXTLINE(clang-diagnostic-sign-compare) |
| for (int i = 1; i < g_input_.size(); ++i) { |
| inputs.push_back("_" + GI(i) + "_keys"); |
| inputs.push_back(GI(i)); |
| } |
| return SingleGradientDef( |
| "Partition", |
| "", |
| std::vector<std::string>{I(0), GO(0)}, |
| inputs, |
| std::vector<Argument>{packArg}); |
| } else { |
| std::vector<std::string> inputs; |
| // NOLINTNEXTLINE(clang-diagnostic-sign-compare) |
| for (int i = 1; i < g_input_.size(); ++i) { |
| inputs.push_back("_" + GI_I(i) + "_keys"); |
| inputs.push_back(GI_I(i)); |
| inputs.push_back(GI_V(i)); |
| } |
| return SingleGradientDef( |
| "Partition", |
| "", |
| std::vector<std::string>{I(0), GO_I(0), GO_V(0)}, |
| inputs, |
| std::vector<Argument>{packArg}); |
| } |
| } |
| }; |
| |
| } // namespace |
| |
| // This should actually have gradient, but for now nothing uses it. |
| // Because gradient computation right now is not input/output aware it can't be |
| // GRADIENT_NOT_IMPLEMENTEDYET |
| NO_GRADIENT(Partition); |
| NO_GRADIENT(LengthsPartition); |
| REGISTER_GRADIENT(GatherByKey, GetGatherByKeyGradient); |
| } // namespace |
| } // namespace caffe2 |