| #ifndef CAFFE2_OPERATORS_MAP_OPS_H_ | |
| #define CAFFE2_OPERATORS_MAP_OPS_H_ | |
| #include "caffe2/core/blob_serialization.h" | |
| #include "caffe2/core/context.h" | |
| #include "caffe2/core/operator.h" | |
| #include <c10/util/irange.h> | |
| #include <algorithm> | |
| #include <iterator> | |
| #include <string> | |
| #include <typeinfo> | |
| #include <unordered_map> | |
| #include <utility> | |
| #include <vector> | |
| namespace caffe2 { | |
| template <typename T> | |
| struct TypeNameTraits { | |
| static constexpr const char* name = "unknown"; | |
| }; | |
| template <> | |
| struct TypeNameTraits<int64_t> { | |
| static constexpr const char* name = "int64_t"; | |
| }; | |
| template <> | |
| struct TypeNameTraits<int32_t> { | |
| static constexpr const char* name = "int32_t"; | |
| }; | |
| template <typename KEY_T, typename VALUE_T> | |
| struct MapTypeTraits { | |
| using MapType = std::unordered_map<KEY_T, VALUE_T>; | |
| static string MapTypeName() { | |
| return string("(std::unordered_map<") + TypeNameTraits<KEY_T>::name + ", " + | |
| TypeNameTraits<VALUE_T>::name + ">)"; | |
| } | |
| }; | |
| using MapType64To64 = MapTypeTraits<int64_t, int64_t>::MapType; | |
| using MapType64To32 = MapTypeTraits<int64_t, int32_t>::MapType; | |
| using MapType32To32 = MapTypeTraits<int32_t, int32_t>::MapType; | |
| using MapType32To64 = MapTypeTraits<int32_t, int64_t>::MapType; | |
| template <class Context> | |
| class CreateMapOp final : public Operator<Context> { | |
| public: | |
| USE_OPERATOR_CONTEXT_FUNCTIONS; | |
| template <class... Args> | |
| explicit CreateMapOp(Args&&... args) | |
| : Operator<Context>(std::forward<Args>(args)...) {} | |
| ~CreateMapOp() {} | |
| bool RunOnDevice() override { | |
| TensorProto::DataType key_dtype = static_cast<TensorProto::DataType>( | |
| this->template GetSingleArgument<int>( | |
| "key_dtype", TensorProto_DataType_INT32)); | |
| return DispatchHelper<TensorTypes<int32_t, int64_t>>::call( | |
| this, DataTypeToTypeMeta(key_dtype)); | |
| } | |
| template <typename KEY_T> | |
| bool DoRunWithType() { | |
| TensorProto::DataType value_dtype = static_cast<TensorProto::DataType>( | |
| this->template GetSingleArgument<int>( | |
| "value_dtype", TensorProto_DataType_INT32)); | |
| return DispatchHelper< | |
| TensorTypes2<int32_t, int64_t, GenericTensorImplementation>, | |
| KEY_T>::call(this, DataTypeToTypeMeta(value_dtype)); | |
| } | |
| template <typename KEY_T, typename VALUE_T> | |
| bool DoRunWithType2() { | |
| // clear to make sure the map is empty | |
| this->template Output<typename MapTypeTraits<KEY_T, VALUE_T>::MapType>(MAP) | |
| ->clear(); | |
| return true; | |
| } | |
| template <typename KEY_T> | |
| bool DoRunWithOtherType2() { | |
| TensorProto::DataType value_dtype = static_cast<TensorProto::DataType>( | |
| this->template GetSingleArgument<int>( | |
| "value_dtype", TensorProto_DataType_INT32)); | |
| CAFFE_THROW( | |
| "CreateMap is not implemented on value tensor of type ", | |
| DataTypeToTypeMeta(value_dtype).name(), | |
| "consider adding it as a type in the DispatchHelper list"); | |
| } | |
| OUTPUT_TAGS(MAP); | |
| }; | |
| template <class Context> | |
| class KeyValueToMapOp final : public Operator<Context> { | |
| public: | |
| USE_OPERATOR_CONTEXT_FUNCTIONS; | |
| template <class... Args> | |
| explicit KeyValueToMapOp(Args&&... args) | |
| : Operator<Context>(std::forward<Args>(args)...) {} | |
| ~KeyValueToMapOp() {} | |
| bool RunOnDevice() override { | |
| return DispatchHelper<TensorTypes<int32_t, int64_t>>::call( | |
| this, Input(KEYS)); | |
| } | |
| template <typename KEY_T> | |
| bool DoRunWithType() { | |
| return DispatchHelper< | |
| TensorTypes2<int32_t, int64_t, GenericTensorImplementation>, | |
| KEY_T>::call(this, Input(VALUES)); | |
| } | |
| template <typename KEY_T, typename VALUE_T> | |
| bool DoRunWithType2() { | |
| using MapType = typename MapTypeTraits<KEY_T, VALUE_T>::MapType; | |
| const auto& key_input = Input(KEYS); | |
| const auto& value_input = Input(VALUES); | |
| CAFFE_ENFORCE_EQ(key_input.numel(), value_input.numel()); | |
| auto* key_data = key_input.template data<KEY_T>(); | |
| auto* value_data = value_input.template data<VALUE_T>(); | |
| auto* map_data = this->template Output<MapType>(MAP); | |
| for (const auto i : c10::irange(key_input.numel())) { | |
| map_data->emplace(key_data[i], value_data[i]); | |
| } | |
| return true; | |
| } | |
| template <typename KEY_T> | |
| bool DoRunWithOtherType2() { | |
| CAFFE_THROW( | |
| "KeyValueToMap is not implemented on value tensor of type ", | |
| Input(VALUES).dtype().name(), | |
| "consider adding it as a type in the DispatchHelper list"); | |
| } | |
| INPUT_TAGS(KEYS, VALUES); | |
| OUTPUT_TAGS(MAP); | |
| }; | |
| template <class Context> | |
| class MapToKeyValueOp final : public Operator<Context> { | |
| public: | |
| USE_OPERATOR_CONTEXT_FUNCTIONS; | |
| template <class... Args> | |
| explicit MapToKeyValueOp(Args&&... args) | |
| : Operator<Context>(std::forward<Args>(args)...) {} | |
| ~MapToKeyValueOp() {} | |
| bool RunOnDevice() override { | |
| return DispatchHelper<TensorTypes< | |
| MapType64To64, | |
| MapType64To32, | |
| MapType32To32, | |
| MapType32To64>>::call(this, OperatorBase::InputBlob(MAP)); | |
| } | |
| template <typename MAP_T> | |
| bool DoRunWithType() { | |
| using key_type = typename MAP_T::key_type; | |
| using mapped_type = typename MAP_T::mapped_type; | |
| auto& map_data = this->template Input<MAP_T>(MAP); | |
| auto* key_output = Output( | |
| KEYS, {static_cast<int64_t>(map_data.size())}, at::dtype<key_type>()); | |
| auto* value_output = Output( | |
| VALUES, | |
| {static_cast<int64_t>(map_data.size())}, | |
| at::dtype<mapped_type>()); | |
| auto* key_data = key_output->template mutable_data<key_type>(); | |
| auto* value_data = value_output->template mutable_data<mapped_type>(); | |
| for (const auto& it : map_data) { | |
| *key_data = it.first; | |
| *value_data = it.second; | |
| key_data++; | |
| value_data++; | |
| } | |
| return true; | |
| } | |
| INPUT_TAGS(MAP); | |
| OUTPUT_TAGS(KEYS, VALUES); | |
| }; | |
| template <typename KEY_T, typename VALUE_T> | |
| class MapSerializer : public BlobSerializerBase { | |
| public: | |
| using MapType = typename MapTypeTraits<KEY_T, VALUE_T>::MapType; | |
| void Serialize( | |
| const void* pointer, | |
| TypeMeta typeMeta, | |
| const string& name, | |
| BlobSerializerBase::SerializationAcceptor acceptor) override { | |
| CAFFE_ENFORCE(typeMeta.Match<MapType>()); | |
| const MapType& map_data = *static_cast<const MapType*>(pointer); | |
| int64_t sz = map_data.size(); | |
| Tensor key_tensor(CPU); | |
| key_tensor.Resize(sz); | |
| Tensor value_tensor(CPU); | |
| value_tensor.Resize(sz); | |
| auto* key_data = key_tensor.mutable_data<KEY_T>(); | |
| auto* value_data = value_tensor.mutable_data<VALUE_T>(); | |
| for (const auto& it : map_data) { | |
| *key_data = it.first; | |
| *value_data = it.second; | |
| key_data++; | |
| value_data++; | |
| } | |
| TensorProtos tensor_protos; | |
| TensorSerializer ser; | |
| ser.Serialize( | |
| key_tensor, name, tensor_protos.add_protos(), 0, key_tensor.numel()); | |
| ser.Serialize( | |
| value_tensor, | |
| name, | |
| tensor_protos.add_protos(), | |
| 0, | |
| value_tensor.numel()); | |
| BlobProto blob_proto; | |
| blob_proto.set_name(name); | |
| blob_proto.set_type(MapTypeTraits<KEY_T, VALUE_T>::MapTypeName()); | |
| blob_proto.set_content(SerializeAsString_EnforceCheck(tensor_protos)); | |
| acceptor(name, SerializeBlobProtoAsString_EnforceCheck(blob_proto)); | |
| } | |
| }; | |
| template <typename KEY_T, typename VALUE_T> | |
| class MapDeserializer : public BlobDeserializerBase { | |
| public: | |
| using MapType = typename MapTypeTraits<KEY_T, VALUE_T>::MapType; | |
| void Deserialize(const BlobProto& proto, Blob* blob) override { | |
| TensorProtos tensor_protos; | |
| CAFFE_ENFORCE( | |
| tensor_protos.ParseFromString(proto.content()), | |
| "Fail to parse TensorProtos"); | |
| TensorDeserializer deser; | |
| Tensor key_tensor = deser.Deserialize(tensor_protos.protos(0)); | |
| Tensor value_tensor = deser.Deserialize(tensor_protos.protos(1)); | |
| auto* key_data = key_tensor.data<KEY_T>(); | |
| auto* value_data = value_tensor.data<VALUE_T>(); | |
| auto* map_ptr = blob->template GetMutable<MapType>(); | |
| for (const auto i : c10::irange(key_tensor.numel())) { | |
| map_ptr->emplace(key_data[i], value_data[i]); | |
| } | |
| } | |
| }; | |
| } // namespace caffe2 | |
| #endif // CAFFE2_OPERATORS_MAP_OPS_H_ |