pytorch/caffe2/python/pybind_state.h
Yangqing Jia aa4d07d3c4 bugfix for Windows, esp. VS 2017
Summary:
aaronmarkham this solves your Windows build issue. Basically:

(1) VS 2017 does not have CUDA support yet, and we will be waiting on NVidia to do so.

(2) VS 2015 and 2017 need different cmake generator strings.

This PR shows how to determine those and also updates appveyor to do contbuild guard for the following 3 settings:
- VS2015 without cuda
- VS2017 without cuda
- VS2015 with cuda
Closes https://github.com/caffe2/caffe2/pull/210

Differential Revision: D4745007

Pulled By: Yangqing

fbshipit-source-id: 50952552843abd0eb6f4145d9f132daeee3a6794
2017-03-21 05:17:59 -07:00

239 lines
7.0 KiB
C++

#pragma once
#include <unordered_map>
#include "caffe2/core/context.h"
#include "caffe2/core/init.h"
#include "caffe2/core/logging.h"
#include "caffe2/core/net.h"
#include "caffe2/core/operator.h"
#include "caffe2/core/scope_guard.h"
#include "caffe2/core/tensor.h"
#include "caffe2/core/types.h"
#include "caffe2/core/workspace.h"
#include "caffe2/proto/caffe2.pb.h"
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include <Python.h>
#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
#define PY_ARRAY_UNIQUE_SYMBOL caffe2_python_ARRAY_API
#include <numpy/arrayobject.h>
// Temporary solution for numpy < 1.7 versions: old macro, no promises.
// You're strongly advised to upgrade to >= 1.7.
#ifndef NPY_ARRAY_C_CONTIGUOUS
#define NPY_ARRAY_C_CONTIGUOUS NPY_C_CONTIGUOUS
#define PyArray_SetBaseObject(arr, x) (PyArray_BASE(arr) = (x))
#endif
namespace caffe2 {
namespace python {
namespace py = pybind11;
// Add methods common to both CPU and GPU mode.
void addGlobalMethods(pybind11::module& m);
// Expose Workspace, Net, Blob
void addObjectMethods(pybind11::module& m);
class BlobFetcherBase {
public:
struct FetchedBlob {
pybind11::object obj;
bool copied;
};
virtual ~BlobFetcherBase();
virtual pybind11::object Fetch(const Blob& blob) = 0;
};
class BlobFeederBase {
public:
virtual ~BlobFeederBase();
virtual void
Feed(const DeviceOption& option, PyArrayObject* array, Blob* blob) = 0;
};
CAFFE_DECLARE_TYPED_REGISTRY(BlobFetcherRegistry, CaffeTypeId, BlobFetcherBase);
#define REGISTER_BLOB_FETCHER(id, ...) \
CAFFE_REGISTER_TYPED_CLASS(BlobFetcherRegistry, id, __VA_ARGS__)
inline unique_ptr<BlobFetcherBase> CreateFetcher(CaffeTypeId id) {
return BlobFetcherRegistry()->Create(id);
}
CAFFE_DECLARE_TYPED_REGISTRY(BlobFeederRegistry, int, BlobFeederBase);
#define REGISTER_BLOB_FEEDER(device_type, ...) \
CAFFE_REGISTER_TYPED_CLASS(BlobFeederRegistry, device_type, __VA_ARGS__)
inline unique_ptr<BlobFeederBase> CreateFeeder(int device_type) {
return BlobFeederRegistry()->Create(device_type);
}
static_assert(
sizeof(int) == sizeof(int32_t),
"We make an assumption that int is always int32 for numpy "
"type mapping.");
int CaffeToNumpyType(const TypeMeta& meta);
const TypeMeta& NumpyTypeToCaffe(int numpy_type);
template <class Context>
class TensorFetcher : public BlobFetcherBase {
public:
pybind11::object Fetch(const Blob& blob) override {
return FetchTensor(blob.Get<Tensor<Context>>(), true).obj;
}
bool NeedsCopy(const TypeMeta& meta) const {
return !std::is_same<Context, CPUContext>::value ||
CaffeToNumpyType(meta) == NPY_OBJECT;
}
FetchedBlob FetchTensor(const Tensor<Context>& tensor, bool force_copy) {
FetchedBlob result;
CAFFE_ENFORCE_GE(tensor.size(), 0, "Trying to fetch unitilized tensor");
const int numpy_type = CaffeToNumpyType(tensor.meta());
CAFFE_ENFORCE(
numpy_type != -1,
"This tensor's data type is not supported: ",
tensor.meta().name(),
".");
std::vector<npy_intp> npy_dims;
for (const auto dim : tensor.dims()) {
npy_dims.push_back(dim);
}
result.copied = force_copy || NeedsCopy(tensor.meta());
void* outPtr;
if (result.copied) {
result.obj = pybind11::object(
PyArray_SimpleNew(tensor.ndim(), npy_dims.data(), numpy_type),
/* borrowed */ false);
outPtr = static_cast<void*>(
PyArray_DATA(reinterpret_cast<PyArrayObject*>(result.obj.ptr())));
} else {
outPtr = const_cast<Tensor<Context>&>(tensor).raw_mutable_data();
result.obj = pybind11::object(
PyArray_SimpleNewFromData(
tensor.ndim(), npy_dims.data(), numpy_type, outPtr),
/* borrowed */ false);
}
if (numpy_type == NPY_OBJECT) {
PyObject** outObj = reinterpret_cast<PyObject**>(outPtr);
auto* str = tensor.template data<std::string>();
for (int i = 0; i < tensor.size(); ++i) {
outObj[i] = PyBytes_FromStringAndSize(str->data(), str->size());
str++;
// cleanup on failure
if (outObj[i] == nullptr) {
for (int j = 0; j < i; ++j) {
Py_DECREF(outObj[j]);
}
CAFFE_THROW("Failed to allocate string for ndarray of strings.");
}
}
return result;
}
if (result.copied) {
Context context;
context.template CopyBytes<Context, CPUContext>(
tensor.nbytes(), tensor.raw_data(), outPtr);
context.FinishDeviceComputation();
}
return result;
}
};
template <class Context>
class TensorFeeder : public BlobFeederBase {
public:
void FeedTensor(
const DeviceOption& option,
PyArrayObject* original_array,
Tensor<Context>* tensor) {
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(
meta.id() != 0,
"This numpy data type is not supported: ",
PyArray_TYPE(array),
".");
Context context(option);
context.SwitchToDevice();
// numpy requires long int as its dims.
int ndim = PyArray_NDIM(array);
npy_intp* npy_dims = PyArray_DIMS(array);
std::vector<TIndex> dims;
for (int i = 0; i < ndim; ++i) {
dims.push_back(npy_dims[i]);
}
tensor->Resize(dims);
// Now, copy the data to the tensor.
switch (npy_type) {
case NPY_OBJECT: {
PyObject** input = reinterpret_cast<PyObject**>(PyArray_DATA(array));
auto* outPtr = tensor->template mutable_data<std::string>();
for (int i = 0; i < tensor->size(); ++i) {
char* str;
Py_ssize_t strSize;
CAFFE_ENFORCE(
PyBytes_AsStringAndSize(input[i], &str, &strSize) != -1,
"Unsupported python object type passed into ndarray.");
outPtr[i] = std::string(str, strSize);
}
} break;
default:
context.template CopyBytes<CPUContext, Context>(
tensor->size() * meta.itemsize(),
static_cast<void*>(PyArray_DATA(array)),
tensor->raw_mutable_data(meta));
}
context.FinishDeviceComputation();
}
virtual void
Feed(const DeviceOption& option, PyArrayObject* original_array, Blob* blob) {
FeedTensor(option, original_array, blob->GetMutable<Tensor<Context>>());
}
};
namespace python_detail {
struct Func;
}
class PythonOpBase : public Operator<CPUContext> {
public:
PythonOpBase(const OperatorDef& operator_def, Workspace* ws)
: Operator(operator_def, ws), ws_(ws) {}
bool RunOnDevice() override final;
protected:
virtual const python_detail::Func& getFunc() = 0;
Workspace* ws_;
};
class PythonOp final : public PythonOpBase {
public:
using PythonOpBase::PythonOpBase;
protected:
const python_detail::Func& getFunc() override;
};
class PythonGradientOp final : public PythonOpBase {
public:
using PythonOpBase::PythonOpBase;
protected:
const python_detail::Func& getFunc() override;
};
} // namespace python
} // namespace caffe2