| #ifndef TH_GENERIC_FILE |
| #define TH_GENERIC_FILE "generic/Tensor.cpp" |
| #else |
| |
| extern PyObject *THPTensorClass; |
| PyObject * THPTensor_(newObject)(THTensor *ptr) |
| { |
| // TODO: error checking |
| PyObject *args = PyTuple_New(0); |
| PyObject *kwargs = Py_BuildValue("{s:K}", "cdata", (unsigned long long) ptr); |
| PyObject *instance = PyObject_Call(THPTensorClass, args, kwargs); |
| Py_DECREF(args); |
| Py_DECREF(kwargs); |
| return instance; |
| } |
| |
| bool THPTensor_(IsSubclass)(PyObject *tensor) |
| { |
| return PyObject_IsSubclass((PyObject*)Py_TYPE(tensor), (PyObject*)&THPTensorType); |
| } |
| |
| static void THPTensor_(dealloc)(THPTensor* self) |
| { |
| THTensor_(free)(LIBRARY_STATE self->cdata); |
| Py_TYPE(self)->tp_free((PyObject*)self); |
| } |
| |
| static PyObject * THPTensor_(pynew)(PyTypeObject *type, PyObject *args, PyObject *kwargs) |
| { |
| HANDLE_TH_ERRORS |
| PyObject *cdata_arg = NULL; // keyword-only arg - cdata pointer value |
| THLongStorage *sizes_arg = NULL; // a storage with sizes for a new tensor |
| THTensor *tensor_arg = NULL; // a tensor to be viewed on |
| // TODO: constructor from storage |
| PyObject *iterable_arg = NULL; // an iterable, with new tensor contents |
| std::vector<size_t> iterator_lengths; // a queue storing lengths of iterables at each depth |
| bool args_ok = true; |
| |
| if (kwargs && PyDict_Size(kwargs) == 1) { |
| cdata_arg = PyDict_GetItemString(kwargs, "cdata"); |
| args_ok = cdata_arg != NULL; |
| } else if (args && PyTuple_Size(args) == 1) { |
| PyObject *arg = PyTuple_GET_ITEM(args, 0); |
| if (THPTensor_(IsSubclass)(arg)) { |
| tensor_arg = ((THPTensor*)arg)->cdata; |
| } else if (THPLongStorage_IsSubclass(arg)) { |
| sizes_arg = ((THPLongStorage*)arg)->cdata; |
| } else if (THPUtils_checkLong(arg)) { |
| sizes_arg = THPUtils_getLongStorage(args); |
| args_ok = sizes_arg != nullptr; |
| } else { |
| iterable_arg = arg; |
| Py_INCREF(arg); |
| THPObjectPtr item = arg; |
| THPObjectPtr iter; |
| while ((iter = PyObject_GetIter(item)) != nullptr) { |
| Py_ssize_t length = PyObject_Length(item); |
| iterator_lengths.push_back(length); |
| // TODO length == 0 is an error too |
| if (length == -1) { |
| // TODO: error |
| return NULL; |
| } |
| if (length > 0) { |
| item = PyIter_Next(iter); |
| if (item == nullptr) { |
| // TODO: set error |
| return NULL; |
| } |
| } else { |
| break; |
| } |
| } |
| if (iterator_lengths.size() > 1) { |
| for (auto length: iterator_lengths) { |
| if (length <= 0) { |
| // TODO: error message |
| THPUtils_setError("invalid size"); |
| return NULL; |
| } |
| } |
| } |
| args_ok = iterator_lengths.size() > 0; |
| // We have accumulated some errors along the way. |
| // Since we did all checking and ignored only the non-important |
| // ones it's safe to clear them here. |
| PyErr_Clear(); |
| } |
| } else if (args && PyTuple_Size(args) > 0) { |
| sizes_arg = THPUtils_getLongStorage(args); |
| args_ok = sizes_arg != nullptr; |
| } |
| |
| if (!args_ok) { |
| // TODO: nice error mossage |
| THPUtils_setError("invalid arguments"); |
| return NULL; |
| } |
| |
| THPTensorPtr self = (THPTensor *)type->tp_alloc(type, 0); |
| if (self != nullptr) { |
| if (cdata_arg) { |
| self->cdata = (THTensor*)PyLong_AsVoidPtr(cdata_arg); |
| } else if (sizes_arg) { |
| self->cdata = THTensor_(newWithSize)(LIBRARY_STATE sizes_arg, nullptr); |
| } else if (tensor_arg) { |
| self->cdata = THTensor_(newWithTensor)(LIBRARY_STATE tensor_arg); |
| } else if (iterable_arg && iterator_lengths.size() == 1 && iterator_lengths[0] == 0) { |
| self->cdata = THTensor_(new)(LIBRARY_STATE_NOARGS); |
| } else if (iterable_arg) { |
| size_t iter_depth = iterator_lengths.size(); |
| std::stack<THPObjectPtr> iterator_stack; |
| std::vector<size_t> items_processed(iter_depth); |
| Py_INCREF(iterable_arg); |
| THPObjectPtr item = iterable_arg; |
| PyObject *iter; |
| while (iterator_stack.size() != iter_depth) { |
| iter = PyObject_GetIter(item); |
| if (!iter) { |
| THPUtils_setError("inconsistent iterator depth"); |
| return NULL; |
| } |
| iterator_stack.emplace(iter); |
| item = PyIter_Next(iter); |
| if (item == nullptr) { |
| THPUtils_setError("error or empty iter"); |
| return NULL; |
| } |
| } |
| THLongStoragePtr sizes = THLongStorage_newWithSize(iter_depth); |
| long *sizes_data = sizes->data; |
| for (size_t s: iterator_lengths) { |
| *sizes_data++ = s; |
| } |
| THTensorPtr tensor = THTensor_(newWithSize)(LIBRARY_STATE sizes, NULL); |
| |
| // TODO CUDA |
| #ifndef THC_GENERIC_FILE |
| #define SET_ITEM if (!THPUtils_(parseReal)(item, data++)) return NULL |
| real *data = tensor->storage->data; |
| #else |
| #define SET_ITEM if (!THPUtils_(parseReal)(item, &item_value)) return NULL; THStorage_(set)(LIBRARY_STATE storage, item_nr++, item_value) |
| real item_value; |
| size_t item_nr = 0; |
| THStorage *storage = tensor->storage; |
| #endif |
| SET_ITEM; |
| items_processed[iter_depth-1]++; |
| |
| while (!iterator_stack.empty()) { |
| PyObject *iter = iterator_stack.top().get(); |
| // Parse items |
| if (iterator_stack.size() == iter_depth) { |
| while ((item = PyIter_Next(iter))) { |
| SET_ITEM; |
| items_processed[iter_depth-1]++; |
| } |
| if (items_processed[iter_depth-1] != iterator_lengths[iter_depth-1]) { |
| THPUtils_setError("inconsistent size"); |
| return NULL; |
| } |
| iterator_stack.pop(); // this deallocates the iter |
| // Iterate on lower depths |
| } else { |
| item = PyIter_Next(iter); |
| if (item == nullptr) { |
| if (PyErr_Occurred()) |
| return NULL; |
| if (items_processed[iterator_stack.size()-1]) { |
| THPUtils_setError("inconsistent size"); |
| return NULL; |
| } |
| iterator_stack.pop(); // this deallocates the iter |
| } else { |
| PyObject *new_iter = PyObject_GetIter(item); |
| if (!new_iter) { |
| THPUtils_setError("non-iterable item"); |
| return NULL; |
| } |
| items_processed[iterator_stack.size()] = 0; |
| iterator_stack.emplace(new_iter); |
| } |
| } |
| } |
| self->cdata = tensor.release(); |
| } else { |
| self->cdata = THTensor_(new)(LIBRARY_STATE_NOARGS); |
| } |
| |
| if (self->cdata == NULL) |
| return NULL; |
| } |
| return (PyObject *)self.release(); |
| END_HANDLE_TH_ERRORS |
| } |
| |
| #define INDEX_LONG(DIM, IDX_VARIABLE, TENSOR_VARIABLE, CASE_1D, CASE_MD) \ |
| long idx; \ |
| THPUtils_getLong(IDX_VARIABLE, &idx); \ |
| long dimsize = THTensor_(size)(LIBRARY_STATE TENSOR_VARIABLE, DIM); \ |
| idx = (idx < 0) ? dimsize + idx : idx; \ |
| \ |
| THArgCheck(dimsize > 0, 1, "empty tensor"); \ |
| THArgCheck(idx >= 0 && idx < dimsize, 2, "out of range"); \ |
| \ |
| if(THTensor_(nDimension)(LIBRARY_STATE TENSOR_VARIABLE) == 1) { \ |
| CASE_1D; \ |
| } else { \ |
| CASE_MD; \ |
| } |
| |
| #define GET_OFFSET(t, idx) \ |
| t->storageOffset + t->stride[0] * idx; |
| |
| static bool THPTensor_(_index)(THPTensor *self, PyObject *index, |
| THTensor * &tresult, THStorage * &sresult, long &storage_offset) |
| { |
| tresult = NULL; |
| sresult = NULL; |
| try { |
| // Indexing with an integer |
| if(PyLong_Check(index) || PyInt_Check(index)) { |
| THTensor *self_t = self->cdata; |
| INDEX_LONG(0, index, self_t, |
| // 1D tensor |
| sresult = self_t->storage; |
| storage_offset = GET_OFFSET(self_t, idx), |
| // >1D tensor |
| tresult = THTensor_(newWithTensor)(LIBRARY_STATE self_t); |
| THTensor_(select)(LIBRARY_STATE tresult, NULL, 0, idx) |
| ) |
| // Indexing with a single element tuple |
| } else if (PyTuple_Check(index) && |
| PyTuple_Size(index) == 1 && |
| (PyLong_Check(PyTuple_GET_ITEM(index, 0)) |
| || PyInt_Check(PyTuple_GET_ITEM(index, 0)))) { |
| PyObject *index_obj = PyTuple_GET_ITEM(index, 0); |
| tresult = THTensor_(newWithTensor)(LIBRARY_STATE self->cdata); |
| INDEX_LONG(0, index_obj, tresult, |
| THTensor_(narrow)(LIBRARY_STATE tresult, NULL, 0, idx, 1), |
| THTensor_(narrow)(LIBRARY_STATE tresult, NULL, 0, idx, 1) |
| ) |
| // Indexing with a slice |
| } else if (PySlice_Check(index)) { |
| tresult = THTensor_(newWithTensor)(LIBRARY_STATE self->cdata); |
| Py_ssize_t start, end, length; |
| if (!THPUtils_(parseSlice)(index, THTensor_(size)(LIBRARY_STATE tresult, 0), &start, &end, &length)) |
| return false; |
| THTensor_(narrow)(LIBRARY_STATE tresult, NULL, 0, start, length); |
| // Indexing multiple dimensions |
| } else if(PyTuple_Check(index)) { |
| THArgCheck(PyTuple_Size(index) <= THTensor_(nDimension)(LIBRARY_STATE self->cdata), 2, |
| "Indexing too many dimensions"); |
| tresult = THTensor_(newWithTensor)(LIBRARY_STATE self->cdata); |
| int t_dim = 0; |
| |
| for(int dim = 0; dim < PyTuple_Size(index); dim++) { |
| PyObject *dimidx = PyTuple_GET_ITEM(index, dim); |
| if(THPUtils_checkLong(dimidx)) { |
| INDEX_LONG(t_dim, dimidx, tresult, |
| // 1D tensor |
| sresult = tresult->storage; |
| storage_offset = GET_OFFSET(tresult, idx); |
| THTensor_(free)(LIBRARY_STATE tresult); |
| tresult = NULL; |
| return true, |
| // >1D tensor |
| THTensor_(select)(LIBRARY_STATE tresult, NULL, t_dim, idx) |
| ) |
| } else if (PyTuple_Check(dimidx)) { |
| long length = 1; |
| if (PyTuple_Size(dimidx) == 0 || PyTuple_Size(dimidx) > 2 || !THPUtils_checkLong(PyTuple_GET_ITEM(dimidx, 0))) { |
| PyErr_SetString(PyExc_RuntimeError, "Expected one or two integers"); |
| return false; |
| } |
| PyObject *index_obj = PyTuple_GET_ITEM(dimidx, 0); |
| if (PyTuple_Size(dimidx) == 2) { |
| long idx; |
| if (!THPUtils_checkLong(PyTuple_GET_ITEM(dimidx, 1))) { |
| THPUtils_setError("Expected one or two intetegers"); |
| return false; |
| } |
| THPUtils_getLong(index_obj, &idx); |
| THPUtils_getLong(PyTuple_GET_ITEM(dimidx, 1), &length); |
| length -= idx; |
| } |
| INDEX_LONG(t_dim, index_obj, tresult, |
| THTensor_(narrow)(LIBRARY_STATE tresult, NULL, t_dim++, idx, length), |
| THTensor_(narrow)(LIBRARY_STATE tresult, NULL, t_dim++, idx, length) |
| ) |
| } else if (PySlice_Check(dimidx)) { |
| Py_ssize_t start, end, length; |
| if (!THPUtils_(parseSlice)(dimidx, THTensor_(size)(LIBRARY_STATE tresult, t_dim), &start, &end, &length)) |
| return false; |
| THTensor_(narrow)(LIBRARY_STATE tresult, NULL, t_dim++, start, length); |
| } else { |
| PyErr_SetString(PyExc_RuntimeError, "Slicing with an unsupported type"); |
| return false; |
| } |
| } |
| } |
| return true; |
| } catch(...) { |
| THTensor_(free)(LIBRARY_STATE tresult); |
| throw; |
| } |
| } |
| #undef INDEX_LONG |
| #undef GET_PTR_1D |
| |
| static PyObject * THPTensor_(getValue)(THPTensor *self, PyObject *index) |
| { |
| HANDLE_TH_ERRORS |
| #ifndef THC_GENERIC_FILE |
| if(THPByteTensor_IsSubclass(index)) { |
| THTensor *t = THTensor_(new)(LIBRARY_STATE_NOARGS); |
| THTensor_(maskedSelect)(LIBRARY_STATE t, self->cdata, ((THPByteTensor*)index)->cdata); |
| return THPTensor_(newObject)(t); |
| #elif defined(THC_REAL_IS_FLOAT) |
| if(THPTensor_(IsSubclass)(index)) { |
| THTensor *t = THTensor_(new)(LIBRARY_STATE_NOARGS); |
| THTensor_(maskedSelect)(LIBRARY_STATE t, self->cdata, ((THPTensor*)index)->cdata); |
| return THPTensor_(newObject)(t); |
| #else |
| if (false) { |
| #endif |
| } else { |
| THTensor *tresult; // TODO: free on error |
| THStorage *sresult; |
| long storage_offset; |
| if (!THPTensor_(_index)(self, index, tresult, sresult, storage_offset)) |
| return NULL; |
| if (tresult) |
| return THPTensor_(newObject)(tresult); |
| if (sresult) |
| return THPUtils_(newReal)(THStorage_(get)(LIBRARY_STATE sresult, storage_offset)); |
| char err_string[512]; |
| snprintf (err_string, 512, |
| "%s %s", "Unknown exception in THPTensor_(getValue). Index type is: ", |
| index->ob_type->tp_name); |
| PyErr_SetString(PyExc_RuntimeError, err_string); |
| return NULL; |
| } |
| END_HANDLE_TH_ERRORS |
| } |
| |
| //extern PyObject * THPTensor_(copy)(THPTensor *self, PyObject *other); |
| int THPTensor_(setValue)(THPTensor *self, PyObject *index, PyObject *value) |
| { |
| HANDLE_TH_ERRORS |
| #if !defined(THC_GENERIC_FILE) || defined(THC_REAL_IS_FLOAT) |
| #ifdef THC_REAL_IS_FLOAT |
| if (THPTensor_(IsSubclass)(index)) { |
| THPTensor *mask = (THPTensor*)index; |
| #else |
| if (THPByteTensor_IsSubclass(index)) { |
| THPByteTensor *mask = (THPByteTensor*)index; |
| #endif |
| if (THPUtils_(checkReal)(value)) { |
| real v; |
| if (!THPUtils_(parseReal)(value, &v)) |
| return -1; |
| THTensor_(maskedFill)(LIBRARY_STATE self->cdata, mask->cdata, v); |
| } else if (THPTensor_(IsSubclass)(value)) { |
| THTensor_(maskedCopy)(LIBRARY_STATE self->cdata, mask->cdata, ((THPTensor*)value)->cdata); |
| } else { |
| THError("number or Tensor expected"); |
| } |
| #else |
| if (false) { |
| #endif |
| } else { |
| THTensor *tresult; |
| THStorage *sresult; |
| long storage_offset; |
| real v; |
| if (!THPTensor_(_index)(self, index, tresult, sresult, storage_offset)) |
| return -1; |
| |
| THTensorPtr tresult_ptr = tresult; |
| if (sresult) { |
| if (!THPUtils_(parseReal)(value, &v)) |
| return -1; |
| THStorage_(set)(LIBRARY_STATE sresult, storage_offset, v); |
| } else if (tresult) { |
| if (THPUtils_(checkReal)(value)) { |
| if (!THPUtils_(parseReal)(value, &v)) |
| return -1; |
| THTensor_(fill)(LIBRARY_STATE tresult, v); |
| } else { |
| // TODO: try to do this without creating a temporary object |
| THPTensorPtr tmp = (THPTensor*)THPTensor_(newObject)(tresult_ptr.get()); |
| tresult_ptr.release(); |
| if (!THPModule_tensorCopy((PyObject*)tmp.get(), value)) |
| return -1; |
| } |
| } else { |
| // TODO: error message |
| THPUtils_setError("error"); |
| return -1; |
| } |
| } |
| return 0; |
| END_HANDLE_TH_ERRORS_RET(-1) |
| } |
| |
| static PyMappingMethods THPTensor_(mappingmethods) = { |
| NULL, |
| (binaryfunc)THPTensor_(getValue), |
| (objobjargproc)THPTensor_(setValue) |
| }; |
| |
| // TODO: implement equality |
| PyTypeObject THPTensorType = { |
| PyVarObject_HEAD_INIT(NULL, 0) |
| "torch._C." THPTensorBaseStr, /* tp_name */ |
| sizeof(THPTensor), /* tp_basicsize */ |
| 0, /* tp_itemsize */ |
| (destructor)THPTensor_(dealloc), /* tp_dealloc */ |
| 0, /* tp_print */ |
| 0, /* tp_getattr */ |
| 0, /* tp_setattr */ |
| 0, /* tp_reserved */ |
| 0, /* tp_repr */ |
| 0, /* tp_as_number */ |
| 0, /* tp_as_sequence */ |
| &THPTensor_(mappingmethods), /* tp_as_mapping */ |
| 0, /* tp_hash */ |
| 0, /* tp_call */ |
| 0, /* tp_str */ |
| 0, /* tp_getattro */ |
| 0, /* tp_setattro */ |
| 0, /* tp_as_buffer */ |
| Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ |
| NULL, /* tp_doc */ |
| 0, /* tp_traverse */ |
| 0, /* tp_clear */ |
| 0, /* tp_richcompare */ |
| 0, /* tp_weaklistoffset */ |
| 0, /* tp_iter */ |
| 0, /* tp_iternext */ |
| 0, /* will be assigned in init */ /* tp_methods */ |
| 0, /* will be assigned in init */ /* tp_members */ |
| 0, /* tp_getset */ |
| 0, /* tp_base */ |
| 0, /* tp_dict */ |
| 0, /* tp_descr_get */ |
| 0, /* tp_descr_set */ |
| 0, /* tp_dictoffset */ |
| 0, /* tp_init */ |
| 0, /* tp_alloc */ |
| THPTensor_(pynew), /* tp_new */ |
| }; |
| |
| static struct PyMemberDef THPTensor_(members)[] = { |
| {(char*)"_cdata", T_ULONGLONG, offsetof(THPTensor, cdata), READONLY, NULL}, |
| {NULL} |
| }; |
| |
| #include "TensorMethods.cpp" |
| |
| typedef struct { |
| PyObject_HEAD |
| } THPTensorStateless; |
| |
| PyTypeObject THPTensorStatelessType = { |
| PyVarObject_HEAD_INIT(NULL, 0) |
| "torch._C." THPTensorBaseStr ".stateless", /* tp_name */ |
| sizeof(THPTensorStateless), /* tp_basicsize */ |
| 0, /* tp_itemsize */ |
| 0, /* tp_dealloc */ |
| 0, /* tp_print */ |
| 0, /* tp_getattr */ |
| 0, /* tp_setattr */ |
| 0, /* tp_reserved / tp_compare */ |
| 0, /* tp_repr */ |
| 0, /* tp_as_number */ |
| 0, /* tp_as_sequence */ |
| 0, /* tp_as_mapping */ |
| 0, /* tp_hash */ |
| 0, /* tp_call */ |
| 0, /* tp_str */ |
| 0, /* tp_getattro */ |
| 0, /* tp_setattro */ |
| 0, /* tp_as_buffer */ |
| Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ |
| NULL, /* tp_doc */ |
| 0, /* tp_traverse */ |
| 0, /* tp_clear */ |
| 0, /* tp_richcompare */ |
| 0, /* tp_weaklistoffset */ |
| 0, /* tp_iter */ |
| 0, /* tp_iternext */ |
| THPTensorStatelessMethods, /* tp_methods */ |
| 0, /* tp_members */ |
| 0, /* tp_getset */ |
| 0, /* tp_base */ |
| 0, /* tp_dict */ |
| 0, /* tp_descr_get */ |
| 0, /* tp_descr_set */ |
| 0, /* tp_dictoffset */ |
| 0, /* tp_init */ |
| 0, /* tp_alloc */ |
| 0, /* tp_new */ |
| 0, /* tp_free */ |
| 0, /* tp_is_gc */ |
| 0, /* tp_bases */ |
| 0, /* tp_mro */ |
| 0, /* tp_cache */ |
| 0, /* tp_subclasses */ |
| 0, /* tp_weaklist */ |
| }; |
| |
| bool THPTensor_(init)(PyObject *module) |
| { |
| THPTensorType.tp_methods = THPTensor_(methods); |
| THPTensorType.tp_members = THPTensor_(members); |
| if (PyType_Ready(&THPTensorType) < 0) |
| return false; |
| THPTensorStatelessType.tp_new = PyType_GenericNew; |
| if (PyType_Ready(&THPTensorStatelessType) < 0) |
| return false; |
| |
| PyModule_AddObject(module, THPTensorBaseStr, (PyObject *)&THPTensorType); |
| return true; |
| } |
| |
| #endif |