| 127 | } |
| 128 | |
| 129 | Maybe<Tensor> MakeLocalTensorFromData(PyObject* data, const Optional<Symbol<DType>>& dtype, |
| 130 | const Optional<Symbol<Device>>& device, |
| 131 | const bool requires_grad, const bool pin_memory) { |
| 132 | bool is_bfloat16_dtype = dtype ? JUST(dtype)->data_type() == DataType::kBFloat16 : false; |
| 133 | bool is_cuda_device = device ? JUST(device)->enum_type() == DeviceType::kCUDA : false; |
| 134 | if (is_bfloat16_dtype && is_cuda_device) { |
| 135 | #if CUDA_VERSION < 11000 |
| 136 | return Error::RuntimeError() |
| 137 | << "Cannot create a bfloat16 tensor on gpu under cuda version: 11000"; |
| 138 | #endif // CUDA_VERSION >= 11000 |
| 139 | } |
| 140 | PyArray_Descr* np_dtype = |
| 141 | dtype.has_value() && !is_bfloat16_dtype |
| 142 | ? PyArray_DescrFromType(JUST(numpy::OFDataTypeToNumpyType(JUST(dtype)->data_type()))) |
| 143 | : nullptr; |
| 144 | // NPY_ARRAY_DEFAULT is NPY_ARRAY_C_CONTIGUOUS | NPY_ARRAY_BEHAVED, so the |
| 145 | // array with NPY_ARRAY_DEFAULT flag is C-style contiguous. |
| 146 | // NPY_ARRAY_FORCECAST is needed otherwise there will a segfault. |
| 147 | // |
| 148 | // Even though PyArray_FromAny can cast the input array to the desired dtype |
| 149 | // if `dtype` argument is set, it fails to handle the following case: |
| 150 | // >> x = [flow.tensor([1, 2])] * 3 <-- x is a list of flow.Tensor |
| 151 | // >> y = flow.tensor(x, dtype=flow.float32) <-- returns nullptr |
| 152 | // However, the following case without `dtype` argument works well: |
| 153 | // >> x = [flow.tensor([1, 2])] * 3 |
| 154 | // >> y = flow.tensor(x) |
| 155 | // So we cast the input array to the desired dtype manually. |
| 156 | PyArrayObject* _array = reinterpret_cast<PyArrayObject*>( |
| 157 | PyArray_FromAny(data, nullptr, 0, 0, |
| 158 | NPY_ARRAY_DEFAULT | NPY_ARRAY_ENSURECOPY | NPY_ARRAY_FORCECAST, nullptr)); |
| 159 | if (!_array) { |
| 160 | return Error::RuntimeError() << "Can not convert input data to a new numpy array."; |
| 161 | } |
| 162 | // PyArray_FromArray steals a reference to np_dtype object, so no need to decref it. |
| 163 | PyObject* array = PyArray_FromArray( |
| 164 | _array, np_dtype, NPY_ARRAY_DEFAULT | NPY_ARRAY_ENSURECOPY | NPY_ARRAY_FORCECAST); |
| 165 | Py_DECREF(_array); |
| 166 | auto* np_arr = reinterpret_cast<PyArrayObject*>(array); |
| 167 | const npy_intp* dims_ptr = PyArray_SHAPE(np_arr); |
| 168 | const Shape shape(DimVector(dims_ptr, dims_ptr + PyArray_NDIM(np_arr))); |
| 169 | DataType np_data_type = JUST(numpy::GetOFDataTypeFromNpArray(np_arr)); |
| 170 | |
| 171 | Symbol<Device> device_; |
| 172 | if (device) { |
| 173 | device_ = JUST(device); |
| 174 | } else { |
| 175 | device_ = JUST(Device::New("cpu")); |
| 176 | } |
| 177 | std::shared_ptr<Tensor> tensor = |
| 178 | JUST(functional::Empty(shape, JUST(DType::Get(np_data_type)), device_, |
| 179 | /*requires_grad=*/false, /*pin_memory=*/pin_memory)); |
| 180 | if (device_->enum_type() != DeviceType::kMeta) { |
| 181 | JUST(CopyLocalTensorFromUntypedArray(tensor, array)); |
| 182 | } |
| 183 | |
| 184 | Py_DECREF(array); |
| 185 | if (dtype && JUST(dtype)->data_type() != np_data_type) { |
| 186 | tensor = JUST(functional::To(tensor, JUST(dtype), false)); |
no test coverage detected