xref: /aosp_15_r20/external/pytorch/torch/csrc/utils/python_numbers.h (revision da0073e96a02ea20f0ac840b70461e3646d07c45)
1  #pragma once
2  
3  #include <c10/core/Device.h>
4  #include <torch/csrc/Exceptions.h>
5  #include <torch/csrc/jit/frontend/tracer.h>
6  #include <torch/csrc/python_headers.h>
7  #include <torch/csrc/utils/object_ptr.h>
8  #include <torch/csrc/utils/tensor_numpy.h>
9  #include <cstdint>
10  #include <limits>
11  #include <stdexcept>
12  
13  // largest integer that can be represented consecutively in a double
14  const int64_t DOUBLE_INT_MAX = 9007199254740992;
15  
THPUtils_packDeviceIndex(c10::DeviceIndex value)16  inline PyObject* THPUtils_packDeviceIndex(c10::DeviceIndex value) {
17    return PyLong_FromLong(value);
18  }
19  
THPUtils_packInt32(int32_t value)20  inline PyObject* THPUtils_packInt32(int32_t value) {
21    return PyLong_FromLong(value);
22  }
23  
THPUtils_packInt64(int64_t value)24  inline PyObject* THPUtils_packInt64(int64_t value) {
25    return PyLong_FromLongLong(value);
26  }
27  
THPUtils_packUInt32(uint32_t value)28  inline PyObject* THPUtils_packUInt32(uint32_t value) {
29    return PyLong_FromUnsignedLong(value);
30  }
31  
THPUtils_packUInt64(uint64_t value)32  inline PyObject* THPUtils_packUInt64(uint64_t value) {
33    return PyLong_FromUnsignedLongLong(value);
34  }
35  
THPUtils_packDoubleAsInt(double value)36  inline PyObject* THPUtils_packDoubleAsInt(double value) {
37    return PyLong_FromDouble(value);
38  }
39  
THPUtils_checkLongExact(PyObject * obj)40  inline bool THPUtils_checkLongExact(PyObject* obj) {
41    return PyLong_CheckExact(obj) && !PyBool_Check(obj);
42  }
43  
THPUtils_checkLong(PyObject * obj)44  inline bool THPUtils_checkLong(PyObject* obj) {
45    // Fast path
46    if (THPUtils_checkLongExact(obj)) {
47      return true;
48    }
49  
50  #ifdef USE_NUMPY
51    if (torch::utils::is_numpy_int(obj)) {
52      return true;
53    }
54  #endif
55  
56    return PyLong_Check(obj) && !PyBool_Check(obj);
57  }
58  
THPUtils_unpackInt(PyObject * obj)59  inline int32_t THPUtils_unpackInt(PyObject* obj) {
60    int overflow = 0;
61    long value = PyLong_AsLongAndOverflow(obj, &overflow);
62    if (value == -1 && PyErr_Occurred()) {
63      throw python_error();
64    }
65    if (overflow != 0) {
66      throw std::runtime_error("Overflow when unpacking long");
67    }
68    if (value > std::numeric_limits<int32_t>::max() ||
69        value < std::numeric_limits<int32_t>::min()) {
70      throw std::runtime_error("Overflow when unpacking long");
71    }
72    return (int32_t)value;
73  }
74  
THPUtils_unpackLong(PyObject * obj)75  inline int64_t THPUtils_unpackLong(PyObject* obj) {
76    int overflow = 0;
77    long long value = PyLong_AsLongLongAndOverflow(obj, &overflow);
78    if (value == -1 && PyErr_Occurred()) {
79      throw python_error();
80    }
81    if (overflow != 0) {
82      throw std::runtime_error("Overflow when unpacking long");
83    }
84    return (int64_t)value;
85  }
86  
THPUtils_unpackUInt32(PyObject * obj)87  inline uint32_t THPUtils_unpackUInt32(PyObject* obj) {
88    unsigned long value = PyLong_AsUnsignedLong(obj);
89    if (PyErr_Occurred()) {
90      throw python_error();
91    }
92    if (value > std::numeric_limits<uint32_t>::max()) {
93      throw std::runtime_error("Overflow when unpacking unsigned long");
94    }
95    return (uint32_t)value;
96  }
97  
THPUtils_unpackUInt64(PyObject * obj)98  inline uint64_t THPUtils_unpackUInt64(PyObject* obj) {
99    unsigned long long value = PyLong_AsUnsignedLongLong(obj);
100    if (PyErr_Occurred()) {
101      throw python_error();
102    }
103    return (uint64_t)value;
104  }
105  
106  bool THPUtils_checkIndex(PyObject* obj);
107  
THPUtils_unpackIndex(PyObject * obj)108  inline int64_t THPUtils_unpackIndex(PyObject* obj) {
109    if (!THPUtils_checkLong(obj)) {
110      auto index = THPObjectPtr(PyNumber_Index(obj));
111      if (index == nullptr) {
112        throw python_error();
113      }
114      // NB: This needs to be called before `index` goes out of scope and the
115      // underlying object's refcount is decremented
116      return THPUtils_unpackLong(index.get());
117    }
118    return THPUtils_unpackLong(obj);
119  }
120  
THPUtils_unpackBool(PyObject * obj)121  inline bool THPUtils_unpackBool(PyObject* obj) {
122    if (obj == Py_True) {
123      return true;
124    } else if (obj == Py_False) {
125      return false;
126    } else {
127      throw std::runtime_error("couldn't convert python object to boolean");
128    }
129  }
130  
THPUtils_checkBool(PyObject * obj)131  inline bool THPUtils_checkBool(PyObject* obj) {
132  #ifdef USE_NUMPY
133    if (torch::utils::is_numpy_bool(obj)) {
134      return true;
135    }
136  #endif
137    return PyBool_Check(obj);
138  }
139  
THPUtils_checkDouble(PyObject * obj)140  inline bool THPUtils_checkDouble(PyObject* obj) {
141  #ifdef USE_NUMPY
142    if (torch::utils::is_numpy_scalar(obj)) {
143      return true;
144    }
145  #endif
146    return PyFloat_Check(obj) || PyLong_Check(obj);
147  }
148  
THPUtils_unpackDouble(PyObject * obj)149  inline double THPUtils_unpackDouble(PyObject* obj) {
150    if (PyFloat_Check(obj)) {
151      return PyFloat_AS_DOUBLE(obj);
152    }
153    double value = PyFloat_AsDouble(obj);
154    if (value == -1 && PyErr_Occurred()) {
155      throw python_error();
156    }
157    return value;
158  }
159  
THPUtils_unpackComplexDouble(PyObject * obj)160  inline c10::complex<double> THPUtils_unpackComplexDouble(PyObject* obj) {
161    Py_complex value = PyComplex_AsCComplex(obj);
162    if (value.real == -1.0 && PyErr_Occurred()) {
163      throw python_error();
164    }
165  
166    return c10::complex<double>(value.real, value.imag);
167  }
168  
THPUtils_unpackNumberAsBool(PyObject * obj)169  inline bool THPUtils_unpackNumberAsBool(PyObject* obj) {
170    if (PyFloat_Check(obj)) {
171      return (bool)PyFloat_AS_DOUBLE(obj);
172    }
173  
174    if (PyComplex_Check(obj)) {
175      double real_val = PyComplex_RealAsDouble(obj);
176      double imag_val = PyComplex_ImagAsDouble(obj);
177      return !(real_val == 0 && imag_val == 0);
178    }
179  
180    // NOLINTNEXTLINE(cppcoreguidelines-init-variables)
181    int overflow;
182    long long value = PyLong_AsLongLongAndOverflow(obj, &overflow);
183    if (value == -1 && PyErr_Occurred()) {
184      throw python_error();
185    }
186    // No need to check overflow, because when overflow occured, it should
187    // return true in order to keep the same behavior of numpy.
188    return (bool)value;
189  }
190  
THPUtils_unpackDeviceIndex(PyObject * obj)191  inline c10::DeviceIndex THPUtils_unpackDeviceIndex(PyObject* obj) {
192    int overflow = 0;
193    long value = PyLong_AsLongAndOverflow(obj, &overflow);
194    if (value == -1 && PyErr_Occurred()) {
195      throw python_error();
196    }
197    if (overflow != 0) {
198      throw std::runtime_error("Overflow when unpacking DeviceIndex");
199    }
200    if (value > std::numeric_limits<c10::DeviceIndex>::max() ||
201        value < std::numeric_limits<c10::DeviceIndex>::min()) {
202      throw std::runtime_error("Overflow when unpacking DeviceIndex");
203    }
204    return (c10::DeviceIndex)value;
205  }
206