| // Note(jiayq): the import_array function is done inside | |
| // caffe2_python.cc. Read | |
| // http://docs.scipy.org/doc/numpy-1.10.1/reference/c-api.array.html#miscellaneous | |
| // for more details. | |
| #define NO_IMPORT_ARRAY | |
| #include "pybind_state.h" | |
| #include <pybind11/pybind11.h> | |
| #include <pybind11/stl.h> | |
| #include "caffe2/ideep/operators/operator_fallback_ideep.h" | |
| #include <caffe2/ideep/ideep_utils.h> | |
| namespace caffe2 { | |
| namespace python { | |
| USE_IDEEP_DEF_ALIASES(); | |
| class IDeepFetcher; | |
| class IDeepFeeder; | |
| REGISTER_IDEEP_OPERATOR(Python, IDEEPFallbackOp<PythonOp<CPUContext, false>>); | |
| REGISTER_BLOB_FETCHER((TypeMeta::Id<itensor>()), IDeepFetcher); | |
| REGISTER_BLOB_FEEDER(IDEEP, IDeepFeeder); | |
| class IDeepFetcher : public BlobFetcherBase { | |
| TypeMeta type_transform(const itensor &atensor) { | |
| switch (atensor.get_data_type()) { | |
| case itensor::data_type::f32: | |
| return TypeMeta::Make<float>(); | |
| case itensor::data_type::s32: | |
| return TypeMeta::Make<int>(); | |
| case itensor::data_type::s8: | |
| return TypeMeta::Make<int8_t>(); | |
| case itensor::data_type::u8: | |
| return TypeMeta::Make<uint8_t>(); | |
| default: | |
| // Should we throw exception? | |
| return TypeMeta(); | |
| } | |
| } | |
| public: | |
| pybind11::object Fetch(const Blob &blob) override { | |
| try { | |
| return FetchTensor(blob.Get<itensor>(), true).obj; | |
| } catch (ideep::error &e) { | |
| LOG(ERROR) << "IDEEP error: " << e.message; | |
| throw; | |
| } | |
| } | |
| FetchedBlob FetchTensor(const itensor &atensor, bool force_copy) { | |
| #ifdef USE_NUMPY | |
| FetchedBlob result; | |
| CAFFE_ENFORCE((atensor.ndims() != 0) && | |
| (atensor.get_nelems() == 0 || | |
| atensor.get_data_handle() != nullptr), | |
| "Trying to fetch uninitialized tensor"); | |
| // NOTE: Only support float so far. | |
| const int numpy_type = NPY_FLOAT; | |
| CAFFE_ENFORCE( | |
| numpy_type != -1, | |
| "Unsupported ideep memory data type? This usually should not happen " | |
| "since ideep memory usually only do float and double."); | |
| itensor::dims dims = atensor.get_public_format_dims(); | |
| std::vector<npy_intp> npy_dims(dims.begin(), dims.end()); | |
| result.copied = force_copy || atensor.need_reorder(); | |
| void *outPtr; | |
| if (result.copied) { | |
| result.obj = py::reinterpret_steal<py::object>( | |
| PyArray_SimpleNew(atensor.ndims(), npy_dims.data(), numpy_type)); | |
| outPtr = static_cast<void *>( | |
| PyArray_DATA(reinterpret_cast<PyArrayObject *>(result.obj.ptr()))); | |
| } else { | |
| outPtr = atensor.get_data_handle(); | |
| result.obj = py::reinterpret_steal<py::object>(PyArray_SimpleNewFromData( | |
| atensor.ndims(), npy_dims.data(), numpy_type, outPtr)); | |
| } | |
| if (numpy_type == NPY_OBJECT) { | |
| CAFFE_THROW("We don't support strings."); | |
| } | |
| if (result.copied) { | |
| atensor.to_public(outPtr); | |
| } | |
| return result; | |
| #else | |
| CAFFE_THROW("Caffe2 was compiled without NumPy support."); | |
| #endif // USE_NUMPY | |
| } | |
| }; | |
| class IDeepFeeder : public BlobFeederBase { | |
| itensor::data_type type_transform(const TypeMeta meta) { | |
| if (meta == TypeMeta::Make<float>()) | |
| return itensor::data_type::f32; | |
| else if (meta == TypeMeta::Make<int>()) | |
| return itensor::data_type::s32; | |
| else if (meta == TypeMeta::Make<int8_t>()) | |
| return itensor::data_type::s8; | |
| else if (meta == TypeMeta::Make<uint8_t>()) | |
| return itensor::data_type::u8; | |
| else | |
| return itensor::data_type::undef; | |
| } | |
| public: | |
| void FeedTensor( | |
| const DeviceOption &option, | |
| PyArrayObject *original_array, | |
| itensor *tensor) { | |
| #ifdef USE_NUMPY | |
| PyArrayObject *array = PyArray_GETCONTIGUOUS(original_array); | |
| auto g = MakeGuard([&]() { Py_XDECREF(array); }); | |
| const auto npy_type = PyArray_TYPE(array); | |
| const TypeMeta meta = NumpyTypeToCaffe(npy_type); | |
| CAFFE_ENFORCE_NE( | |
| meta, | |
| ScalarType::Undefined, | |
| "This numpy data type is not supported: ", | |
| PyArray_TYPE(array), "."); | |
| int ndim = PyArray_NDIM(array); | |
| npy_intp *npy_dims = PyArray_DIMS(array); | |
| itensor::dims adims; | |
| for (int i = 0; i < ndim; i++) { | |
| adims.push_back(static_cast<itensor::dims::value_type>(npy_dims[i])); | |
| } | |
| switch (npy_type) { | |
| case NPY_OBJECT: | |
| case NPY_UNICODE: | |
| CAFFE_THROW("IDeep doesn't support string"); | |
| break; | |
| default: | |
| auto type = type_transform(meta); | |
| if (tensor->get_dims() != adims || type != tensor->get_data_type()) { | |
| tensor->resize(adims, type); | |
| } | |
| tensor->feed_from(adims, type, | |
| static_cast<void *>(PyArray_DATA(array))); | |
| } | |
| #else | |
| CAFFE_THROW("Caffe2 was compiled without NumPy support."); | |
| #endif // USE_NUMPY | |
| } | |
| bool ZeroDim(PyArrayObject *array) { | |
| #ifdef USE_NUMPY | |
| int ndim = PyArray_NDIM(array); | |
| return ndim == 0; | |
| #else | |
| CAFFE_THROW("Caffe2 was compiled without NumPy support."); | |
| #endif | |
| } | |
| void Feed( | |
| const DeviceOption& option, | |
| PyArrayObject* original_array, | |
| Blob* blob, | |
| bool in_place) override { | |
| #ifdef USE_NUMPY | |
| try { | |
| PyArrayObject *array = PyArray_GETCONTIGUOUS(original_array); | |
| auto g = MakeGuard([&]() { Py_XDECREF(array); }); | |
| const auto npy_type = PyArray_TYPE(array); | |
| const TypeMeta meta = NumpyTypeToCaffe(npy_type); | |
| // TODO: if necessary, use dispatcher. | |
| if ((in_place && blob->IsType<itensor>()) | |
| || (meta.Match<float>() && !ZeroDim(original_array))) { | |
| FeedTensor(option, original_array, blob->GetMutable<itensor>()); | |
| } else { | |
| DeviceOption cpu_option(option); | |
| cpu_option.set_device_type(DeviceTypeProto::PROTO_CPU); | |
| TensorFeeder<CPUContext> cpu_tensor_feeder; | |
| if (in_place) { | |
| cpu_tensor_feeder.FeedTensor( | |
| cpu_option, | |
| original_array, | |
| BlobGetMutableTensor(blob, OptionToDevice(cpu_option).type()), | |
| true); | |
| } else { | |
| blob->Reset<Tensor>(new Tensor( | |
| cpu_tensor_feeder.FeedTensor(cpu_option, original_array))); | |
| } | |
| } | |
| } catch (ideep::error &e) { | |
| LOG(ERROR) << "IDEEP error: " << e.message; | |
| throw; | |
| } | |
| #else | |
| CAFFE_THROW("Caffe2 was compiled without NumPy support."); | |
| #endif | |
| } | |
| }; | |
| } // namespace python | |
| } // namespace caffe2 |