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