xref: /aosp_15_r20/external/tensorflow/tensorflow/python/lib/core/py_seq_tensor.cc (revision b6fb3261f9314811a0f4371741dbb8839866f948)
1 /* Copyright 2017 The TensorFlow Authors. All Rights Reserved.
2 
3 Licensed under the Apache License, Version 2.0 (the "License");
4 you may not use this file except in compliance with the License.
5 You may obtain a copy of the License at
6 
7     http://www.apache.org/licenses/LICENSE-2.0
8 
9 Unless required by applicable law or agreed to in writing, software
10 distributed under the License is distributed on an "AS IS" BASIS,
11 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 See the License for the specific language governing permissions and
13 limitations under the License.
14 ==============================================================================*/
15 
16 #include "tensorflow/python/lib/core/py_seq_tensor.h"
17 
18 #include "tensorflow/c/eager/tfe_context_internal.h"
19 #include "tensorflow/c/eager/tfe_tensorhandle_internal.h"
20 #include "tensorflow/c/tensor_interface.h"
21 #include "tensorflow/c/tf_tensor_internal.h"
22 #include "tensorflow/core/framework/tensor.h"
23 #include "tensorflow/core/framework/types.h"
24 #include "tensorflow/core/lib/core/errors.h"
25 #include "tensorflow/core/lib/strings/str_util.h"
26 #include "tensorflow/core/platform/errors.h"
27 #include "tensorflow/core/platform/macros.h"
28 #include "tensorflow/core/platform/tstring.h"
29 #include "tensorflow/core/platform/types.h"
30 #include "tensorflow/python/lib/core/ndarray_tensor.h"
31 #include "tensorflow/python/lib/core/ndarray_tensor_bridge.h"
32 #include "tensorflow/python/lib/core/numpy.h"
33 #include "tensorflow/python/lib/core/py_util.h"
34 #include "tensorflow/python/lib/core/safe_ptr.h"
35 
36 namespace tensorflow {
37 namespace {
38 
PyIsInstance(PyObject * obj,PyTypeObject * t)39 inline bool PyIsInstance(PyObject* obj, PyTypeObject* t) {
40   return PyObject_IsInstance(obj, reinterpret_cast<PyObject*>(t));
41 }
42 
PyType(PyObject * obj)43 inline PyObject* PyType(PyObject* obj) {
44   return reinterpret_cast<PyObject*>(obj->ob_type);
45 }
46 
IsPyString(PyObject * obj)47 bool IsPyString(PyObject* obj) {
48   return PyBytes_Check(obj) || PyUnicode_Check(obj);
49 }
50 
IsPyInt(PyObject * obj)51 bool IsPyInt(PyObject* obj) {
52 #if PY_MAJOR_VERSION >= 3
53   return PyLong_Check(obj) ||
54          PyIsInstance(obj, &PyIntegerArrType_Type);  // NumPy integers
55 #else
56   return PyInt_Check(obj) || PyLong_Check(obj) ||
57          PyIsInstance(obj, &PyIntegerArrType_Type);  // NumPy integers
58 #endif
59 }
60 
IsPyDouble(PyObject * obj)61 bool IsPyDouble(PyObject* obj) {
62   return PyIsInstance(obj, &PyDoubleArrType_Type);  // NumPy double type.
63 }
64 
IsNumpyHalf(PyObject * obj)65 bool IsNumpyHalf(PyObject* obj) {
66   return PyIsInstance(obj, &PyHalfArrType_Type);
67 }
68 
IsPyFloat(PyObject * obj)69 bool IsPyFloat(PyObject* obj) {
70   return PyFloat_Check(obj) ||
71          PyIsInstance(obj, &PyFloatingArrType_Type);  // NumPy float types
72 }
73 
74 struct ConverterState {
75   // The inferred tensor shape.
76   gtl::InlinedVector<int64_t, 4> inferred_shape;
77 
78   // The inferred tensor data type.
79   DataType inferred_dtype;
80 
81   // The following fields are used by ZeroDimArrayToScalar.
82   // We cache the last result of the check for a zero dimensional array: the
83   // function is called many times in a conversion, and most of the time is
84   // to check for the same type. This cache can reduce the conversion time by
85   // about 25%.
86   PyTypeObject* last_zerodim_type;
87   bool last_zerodim_check;
88 
ConverterStatetensorflow::__anonfc08bad90111::ConverterState89   ConverterState() : inferred_dtype(DT_INVALID), last_zerodim_type(nullptr) {}
90 };
91 
92 // If the input is a zero dimensional PyArray return it converted to a scalar.
93 // Otherwise return the input and increment its reference count.
94 // Users must Py_DECREF the output of this method.
ZeroDimArrayToScalar(PyObject * obj,ConverterState * state)95 PyObject* ZeroDimArrayToScalar(PyObject* obj, ConverterState* state) {
96   auto type = Py_TYPE(obj);
97   auto pyarray_obj = reinterpret_cast<PyArrayObject*>(obj);
98   if (type != state->last_zerodim_type) {
99     state->last_zerodim_type = type;
100     state->last_zerodim_check =
101         PyObject_TypeCheck(obj, &PyArray_Type) &&
102         !PyObject_TypeCheck(obj, &PyGenericArrType_Type);
103   }
104 
105   if (state->last_zerodim_check && PyArray_NDIM(pyarray_obj) == 0) {
106     obj = PyArray_ToScalar(PyArray_DATA(pyarray_obj), pyarray_obj);
107   } else {
108     Py_INCREF(obj);
109   }
110   return obj;
111 }
112 
113 // Sets *elem to a NEW reference to an element in seq on success.
114 // REQUIRES: PySequence_Check(seq) && PySequence_Length(seq) > 0.
SampleElementFromSequence(PyObject * seq,PyObject ** elem)115 Status SampleElementFromSequence(PyObject* seq, PyObject** elem) {
116   *elem = PySequence_GetItem(seq, 0);
117   if (*elem != nullptr) return OkStatus();
118   // seq may implement the sequence protocol (i.e., implement __getitem__)
119   // but may legitimately not have a 0-th element (__getitem__(self, 0)
120   // raises a KeyError). For example:
121   // seq = pandas.Series([0, 1, 2], index=[2, 4, 6])
122   //
123   // We don't actually care for the element at key 0, any element will do
124   // for inferring the element types. All elements are expected to
125   // have the same type, and this will be validated when converting
126   // to an EagerTensor.
127   PyErr_Clear();
128   Safe_PyObjectPtr iter(PyObject_GetIter(seq));
129   if (PyErr_Occurred()) {
130     return errors::InvalidArgument("Cannot infer dtype of a ",
131                                    Py_TYPE(seq)->tp_name,
132                                    " object: ", PyExceptionFetch());
133   }
134   *elem = PyIter_Next(iter.get());
135   if (PyErr_Occurred()) {
136     return errors::InvalidArgument(
137         "Cannot infer dtype of a ", Py_TYPE(seq)->tp_name,
138         " object, as iter(<object>).next() failed: ", PyExceptionFetch());
139   }
140   if (*elem == nullptr) {
141     return errors::InvalidArgument("Cannot infer dtype of a ",
142                                    Py_TYPE(seq)->tp_name,
143                                    " object since it is an empty sequence");
144   }
145   return OkStatus();
146 }
147 
148 tstring PyRepr(PyObject* obj);
149 bool IsPyDimension(PyObject* obj);
150 
InferShapeAndType(PyObject * obj,ConverterState * state)151 Status InferShapeAndType(PyObject* obj, ConverterState* state) {
152   std::vector<Safe_PyObjectPtr> refs_to_clean;
153   while (true) {
154     // Convert any zero dimensional numpy arrays to scalars first of all.
155     // We also have to make sure a reference to the safe_obj is kept.
156     obj = ZeroDimArrayToScalar(obj, state);
157     refs_to_clean.push_back(make_safe(obj));
158     // We test strings first, in case a string is considered a sequence.
159     if (IsPyString(obj)) {
160       state->inferred_dtype = DT_STRING;
161     } else if (PySequence_Check(obj)) {
162       auto length = PySequence_Length(obj);
163       if (length > 0) {
164         state->inferred_shape.push_back(length);
165         PyObject* elem = nullptr;
166         TF_RETURN_IF_ERROR(SampleElementFromSequence(obj, &elem));
167         obj = elem;
168         refs_to_clean.push_back(make_safe(obj));
169         continue;
170       } else if (length == 0) {
171         state->inferred_shape.push_back(length);
172         state->inferred_dtype = DT_INVALID;  // Invalid dtype for empty tensors.
173       } else {
174         // The sequence does not have a valid length (PySequence_Length < 0).
175         if (PyErr_Occurred()) {
176           // PySequence_Length failed and set an exception. Fetch the message
177           // and convert it to a failed status.
178           return errors::InvalidArgument(PyExceptionFetch());
179         } else {
180           // This is almost certainly dead code: PySequence_Length failed but
181           // did not set an exception.
182           return errors::InvalidArgument(
183               "Attempted to convert an invalid sequence to a Tensor.");
184         }
185       }
186     } else if (IsPyDouble(obj)) {
187       state->inferred_dtype = DT_DOUBLE;
188     } else if (IsNumpyHalf(obj)) {
189       state->inferred_dtype = DT_HALF;
190     } else if (IsPyFloat(obj)) {
191       state->inferred_dtype = DT_FLOAT;
192     } else if (PyBool_Check(obj) || PyIsInstance(obj, &PyBoolArrType_Type)) {
193       // Have to test for bool before int, since IsInt(True/False) == true.
194       state->inferred_dtype = DT_BOOL;
195     } else if (IsPyInt(obj)) {
196       state->inferred_dtype = DT_INT64;
197     } else if (IsPyDimension(obj)) {
198       state->inferred_dtype = DT_INT64;
199     } else if (PyComplex_Check(obj) ||
200                PyIsInstance(obj, &PyComplexFloatingArrType_Type)) {  // NumPy
201       state->inferred_dtype = DT_COMPLEX128;
202     } else {
203       return errors::InvalidArgument("Attempt to convert a value (",
204                                      PyRepr(obj),
205                                      ") with an unsupported type (",
206                                      PyRepr(PyType(obj)), ") to a Tensor.");
207     }
208     return OkStatus();
209   }
210 }
211 
212 // Error messages
213 
214 const char ErrorConverting[] =
215     "Error while converting Python sequence to Tensor.";
216 const char ErrorRectangular[] =
217     "Can't convert non-rectangular Python sequence to Tensor.";
218 const char ErrorMixedTypes[] =
219     "Can't convert Python sequence with mixed types to Tensor.";
220 const char ErrorOutOfRange[] =
221     "Can't convert Python sequence with out-of-range integer to Tensor.";
222 const char ErrorOutOfRangeDouble[] =
223     "Can't convert Python sequence with a value out of range for a "
224     "double-precision float.";
225 const char ErrorConvertingUnicodeString[] =
226     "Error converting unicode string while converting Python sequence to "
227     "Tensor.";
228 const char ErrorFoundInt64[] =
229     "Can't convert Python sequence with out-of-range integer to int32 Tensor.";
230 const char ErrorFoundFloat[] =
231     "Can't convert Python sequence with floating point values to integer "
232     "Tensor.";
233 
234 // Defines a converter that recursively converts an object into
235 // an array of type T using the conversion function defined by the
236 // traits class in a ConvertScalar function.
237 //
238 // Note that these helper functions require shape.dims() >= 1.
239 template <class T>
240 struct ConverterTraits {
241   static const tensorflow::DataType kTypeEnum;
242   static const char* ConvertScalar(PyObject* v, T* out);
243 };
244 
245 template <class T>
246 struct Converter {
Helpertensorflow::__anonfc08bad90111::Converter247   static const char* Helper(PyObject* obj, int depth, ConverterState* state,
248                             T** buf) {
249     if (TF_PREDICT_FALSE(obj == nullptr)) {
250       return ErrorConverting;
251     }
252 
253     Safe_PyObjectPtr seq = make_safe(PySequence_Fast(obj, ""));
254     if (TF_PREDICT_FALSE(seq == nullptr)) return ErrorRectangular;
255 
256     const int64_t s = state->inferred_shape[depth];
257     if (TF_PREDICT_FALSE(s != PySequence_Fast_GET_SIZE(seq.get()))) {
258       return ErrorRectangular;
259     }
260 
261     if (state->inferred_shape.size() - depth > 1) {
262       /* Iterate over outer dim, and recursively convert each element. */
263       for (int64_t i = 0; i < s; ++i) {
264         const char* error = Helper(PySequence_Fast_GET_ITEM(seq.get(), i),
265                                    depth + 1, state, buf);
266         if (TF_PREDICT_FALSE(error != nullptr)) return error;
267       }
268     } else {
269       PyObject** l = PySequence_Fast_ITEMS(seq.get());
270       for (int64_t i = 0; i < s; ++i) {
271         auto scalar = ZeroDimArrayToScalar(l[i], state);
272         const char* error = ConverterTraits<T>::ConvertScalar(scalar, *buf);
273         Py_DECREF(scalar);
274         if (TF_PREDICT_FALSE(error != nullptr)) return error;
275         ++*buf;
276       }
277     }
278     return nullptr;
279   }
280 
Converttensorflow::__anonfc08bad90111::Converter281   static Status Convert(TFE_Context* ctx, PyObject* obj, ConverterState* state,
282                         TFE_TensorHandle** h, const char** error) {
283     // TODO(josh11b): Allocator & attributes
284     AbstractTensorInterface* t;
285     if (state->inferred_shape.empty()) { /* Scalar case */
286       T value;
287       auto scalar = ZeroDimArrayToScalar(obj, state);
288       *error = ConverterTraits<T>::ConvertScalar(scalar, &value);
289       Py_DECREF(scalar);
290       if (*error != nullptr) return errors::InvalidArgument(*error);
291       t = ConverterTraits<T>::CreateScalar(ctx, value);
292       if (t == nullptr) {
293         return errors::Internal("Cannot create tensor.");
294       }
295     } else {
296       t = ConverterTraits<T>::CreateTensor(ctx, state->inferred_shape);
297       if (t == nullptr) {
298         return errors::Internal("Cannot create tensor.");
299       }
300       if (t->NumElements() > 0) {
301         T* buf = static_cast<T*>(t->Data());
302         *error = Helper(obj, 0, state, &buf);
303         if (*error != nullptr) {
304           t->Release();
305           return errors::InvalidArgument(*error);
306         }
307       }
308     }
309     *h = tensorflow::wrap(tensorflow::unwrap(ctx)->CreateLocalHandle(t));
310     t->Release();
311     return OkStatus();
312   }
313 };
314 
315 // Int support
316 
317 template <>
318 struct ConverterTraits<int64_t> {
CreateScalartensorflow::__anonfc08bad90111::ConverterTraits319   static AbstractTensorInterface* CreateScalar(TFE_Context* ctx,
320                                                int64_t value) {
321     return tensorflow::unwrap(ctx)->CreateInt64Scalar(value);
322   }
323 
CreateTensortensorflow::__anonfc08bad90111::ConverterTraits324   static AbstractTensorInterface* CreateTensor(
325       TFE_Context* ctx, absl::Span<const int64_t> dim_sizes) {
326     return tensorflow::unwrap(ctx)->CreateTensor(DT_INT64, dim_sizes);
327   }
328 
ConvertScalartensorflow::__anonfc08bad90111::ConverterTraits329   static const char* ConvertScalar(PyObject* v, int64_t* out) {
330 #if PY_MAJOR_VERSION < 3
331     if (TF_PREDICT_TRUE(PyInt_Check(v))) {
332       *out = PyInt_AS_LONG(v);
333       return nullptr;
334     }
335 #endif
336     if (TF_PREDICT_TRUE(PyLong_Check(v) || IsPyDimension(v))) {
337       int overflow = 0;
338       // Have to use LongLong for 64 bits, since long is 32 bits on Windows.
339       *out = PyLong_AsLongLongAndOverflow(v, &overflow);
340       if (TF_PREDICT_FALSE(overflow)) return ErrorOutOfRange;
341       return nullptr;
342     }
343     if (PyIsInstance(v, &PyIntegerArrType_Type)) {  // NumPy integers
344 #if PY_MAJOR_VERSION < 3
345       Safe_PyObjectPtr as_int = make_safe(PyNumber_Int(v));
346 #else
347       Safe_PyObjectPtr as_int = make_safe(PyNumber_Long(v));
348 #endif
349       return ConvertScalar(as_int.get(), out);
350     }
351     if (IsPyFloat(v)) return ErrorFoundFloat;
352     return ErrorMixedTypes;
353   }
354 };
355 
356 typedef Converter<int64_t> Int64Converter;
357 
358 template <>
359 struct ConverterTraits<uint64> {
CreateScalartensorflow::__anonfc08bad90111::ConverterTraits360   static AbstractTensorInterface* CreateScalar(TFE_Context* ctx, uint64 value) {
361     return tensorflow::unwrap(ctx)->CreateUint64Scalar(value);
362   }
363 
CreateTensortensorflow::__anonfc08bad90111::ConverterTraits364   static AbstractTensorInterface* CreateTensor(
365       TFE_Context* ctx, absl::Span<const int64_t> dim_sizes) {
366     return tensorflow::unwrap(ctx)->CreateTensor(DT_UINT64, dim_sizes);
367   }
368 
ConvertScalartensorflow::__anonfc08bad90111::ConverterTraits369   static const char* ConvertScalar(PyObject* v, uint64* out) {
370 #if PY_MAJOR_VERSION < 3
371     if (TF_PREDICT_TRUE(PyInt_Check(v))) {
372       *out = PyInt_AsUnsignedLongLongMask(v);
373       return nullptr;
374     }
375 #endif
376     if (TF_PREDICT_TRUE(PyLong_Check(v) || IsPyDimension(v))) {
377       *out = PyLong_AsUnsignedLongLong(v);
378       return nullptr;
379     }
380     if (PyIsInstance(v, &PyIntegerArrType_Type)) {  // NumPy integers
381 #if PY_MAJOR_VERSION < 3
382       Safe_PyObjectPtr as_int = make_safe(PyNumber_Int(v));
383 #else
384       Safe_PyObjectPtr as_int = make_safe(PyNumber_Long(v));
385 #endif
386       return ConvertScalar(as_int.get(), out);
387     }
388     if (IsPyFloat(v)) return ErrorFoundFloat;
389     return ErrorMixedTypes;
390   }
391 };
392 
393 typedef Converter<uint64> UInt64Converter;
394 
395 template <>
396 struct ConverterTraits<int32> {
CreateScalartensorflow::__anonfc08bad90111::ConverterTraits397   static AbstractTensorInterface* CreateScalar(TFE_Context* ctx,
398                                                int32_t value) {
399     return tensorflow::unwrap(ctx)->CreateInt32Scalar(value);
400   }
401 
CreateTensortensorflow::__anonfc08bad90111::ConverterTraits402   static AbstractTensorInterface* CreateTensor(
403       TFE_Context* ctx, absl::Span<const int64_t> dim_sizes) {
404     return tensorflow::unwrap(ctx)->CreateTensor(DT_INT32, dim_sizes);
405   }
406 
ConvertScalartensorflow::__anonfc08bad90111::ConverterTraits407   static const char* ConvertScalar(PyObject* v, int32* out) {
408     int64_t i;
409 #if PY_MAJOR_VERSION < 3
410     if (TF_PREDICT_TRUE(PyInt_Check(v))) {
411       i = PyInt_AS_LONG(v);
412     } else
413 #endif
414         if (PyLong_Check(v) || IsPyDimension(v)) {
415       int overflow = 0;
416       // Have to use LongLong for 64 bits, since long is 32 bits on Windows.
417       i = PyLong_AsLongLongAndOverflow(v, &overflow);
418       if (TF_PREDICT_FALSE(overflow)) return ErrorOutOfRange;
419     } else if (PyIsInstance(v, &PyIntegerArrType_Type)) {  // NumPy integers
420 #if PY_MAJOR_VERSION < 3
421       Safe_PyObjectPtr as_int = make_safe(PyNumber_Int(v));
422 #else
423       Safe_PyObjectPtr as_int = make_safe(PyNumber_Long(v));
424 #endif
425       return ConvertScalar(as_int.get(), out);
426     } else if (IsPyFloat(v)) {
427       return ErrorFoundFloat;
428     } else {
429       return ErrorMixedTypes;
430     }
431     *out = static_cast<uint32>(static_cast<uint64>(i));
432     // Check for 32-bit overflow.
433     if (TF_PREDICT_FALSE(i != *out)) return ErrorFoundInt64;
434     return nullptr;
435   }
436 };
437 
438 typedef Converter<int32> Int32Converter;
439 
440 // Floating-point support
441 
442 // Returns `true` if `out` overflows when converted from `as_double`.
443 template <class T>
CheckForOverflow(double as_double,T * out)444 static inline bool CheckForOverflow(double as_double, T* out) {
445   return (sizeof(T) < sizeof(double) && std::isinf(*out) &&
446           std::isfinite(as_double));
447 }
448 
449 // There is no `std::isinf` that takes `Eigen::half` as argument but Eigen
450 // provides `Eigen::numext::isinf` instead.
451 template <>
CheckForOverflow(double as_double,Eigen::half * out)452 inline bool CheckForOverflow<Eigen::half>(double as_double, Eigen::half* out) {
453   return (Eigen::numext::isinf(*out) && std::isfinite(as_double));
454 }
455 
456 template <class T>
ConvertOneFloat(PyObject * v,T * out)457 static const char* ConvertOneFloat(PyObject* v, T* out) {
458   if (PyErr_Occurred()) {
459     return nullptr;
460   }
461   if (TF_PREDICT_TRUE(PyFloat_Check(v))) {
462     const double as_double = PyFloat_AS_DOUBLE(v);
463     *out = static_cast<T>(as_double);
464     // Check for overflow
465     if (TF_PREDICT_FALSE(CheckForOverflow<T>(as_double, out))) {
466       return ErrorOutOfRangeDouble;
467     }
468     return nullptr;
469   }
470 #if PY_MAJOR_VERSION < 3
471   if (PyInt_Check(v)) {
472     *out = static_cast<T>(PyInt_AS_LONG(v));
473     return nullptr;
474   }
475 #endif
476   if (PyLong_Check(v)) {
477     *out = static_cast<T>(PyLong_AsDouble(v));
478     if (PyErr_Occurred()) return ErrorOutOfRangeDouble;
479     return nullptr;
480   }
481   if (PyIsInstance(v, &PyFloatingArrType_Type)) {  // NumPy float types
482     Safe_PyObjectPtr as_float = make_safe(PyNumber_Float(v));
483     if (PyErr_Occurred()) {
484       return nullptr;
485     }
486     return ConvertOneFloat<T>(as_float.get(), out);
487   }
488   if (PyIsInstance(v, &PyIntegerArrType_Type)) {  // NumPy integers
489 #if PY_MAJOR_VERSION < 3
490     Safe_PyObjectPtr as_int = make_safe(PyNumber_Int(v));
491 #else
492     Safe_PyObjectPtr as_int = make_safe(PyNumber_Long(v));
493 #endif
494     if (PyErr_Occurred()) {
495       return nullptr;
496     }
497     return ConvertOneFloat<T>(as_int.get(), out);
498   }
499   return ErrorMixedTypes;
500 }
501 
502 template <>
503 struct ConverterTraits<float> {
CreateScalartensorflow::__anonfc08bad90111::ConverterTraits504   static AbstractTensorInterface* CreateScalar(TFE_Context* ctx, float value) {
505     return tensorflow::unwrap(ctx)->CreateFloatScalar(value);
506   }
507 
CreateTensortensorflow::__anonfc08bad90111::ConverterTraits508   static AbstractTensorInterface* CreateTensor(
509       TFE_Context* ctx, absl::Span<const int64_t> dim_sizes) {
510     return tensorflow::unwrap(ctx)->CreateTensor(DT_FLOAT, dim_sizes);
511   }
512 
ConvertScalartensorflow::__anonfc08bad90111::ConverterTraits513   static const char* ConvertScalar(PyObject* v, float* out) {
514     return ConvertOneFloat<float>(v, out);
515   }
516 };
517 
518 template <>
519 struct ConverterTraits<double> {
CreateScalartensorflow::__anonfc08bad90111::ConverterTraits520   static AbstractTensorInterface* CreateScalar(TFE_Context* ctx, double value) {
521     return tensorflow::unwrap(ctx)->CreateDoubleScalar(value);
522   }
523 
CreateTensortensorflow::__anonfc08bad90111::ConverterTraits524   static AbstractTensorInterface* CreateTensor(
525       TFE_Context* ctx, absl::Span<const int64_t> dim_sizes) {
526     return tensorflow::unwrap(ctx)->CreateTensor(DT_DOUBLE, dim_sizes);
527   }
528 
ConvertScalartensorflow::__anonfc08bad90111::ConverterTraits529   static const char* ConvertScalar(PyObject* v, double* out) {
530     return ConvertOneFloat<double>(v, out);
531   }
532 };
533 
534 typedef Converter<double> DoubleConverter;
535 typedef Converter<float> FloatConverter;
536 
537 template <>
538 struct ConverterTraits<Eigen::half> {
CreateScalartensorflow::__anonfc08bad90111::ConverterTraits539   static AbstractTensorInterface* CreateScalar(TFE_Context* ctx,
540                                                Eigen::half value) {
541     return tensorflow::unwrap(ctx)->CreateHalfScalar(value);
542   }
543 
CreateTensortensorflow::__anonfc08bad90111::ConverterTraits544   static AbstractTensorInterface* CreateTensor(
545       TFE_Context* ctx, absl::Span<const int64_t> dim_sizes) {
546     return tensorflow::unwrap(ctx)->CreateTensor(DT_HALF, dim_sizes);
547   }
548 
ConvertScalartensorflow::__anonfc08bad90111::ConverterTraits549   static const char* ConvertScalar(PyObject* v, Eigen::half* out) {
550     return ConvertOneFloat<Eigen::half>(v, out);
551   }
552 };
553 
554 typedef Converter<Eigen::half> NumpyHalfConverter;
555 
556 // String support
557 
558 template <>
559 struct ConverterTraits<tstring> {
CreateScalartensorflow::__anonfc08bad90111::ConverterTraits560   static AbstractTensorInterface* CreateScalar(TFE_Context* ctx,
561                                                tstring value) {
562     return tensorflow::unwrap(ctx)->CreateStringScalar(value);
563   }
564 
CreateTensortensorflow::__anonfc08bad90111::ConverterTraits565   static AbstractTensorInterface* CreateTensor(
566       TFE_Context* ctx, absl::Span<const int64_t> dim_sizes) {
567     return tensorflow::unwrap(ctx)->CreateTensor(DT_STRING, dim_sizes);
568   }
569 
ConvertScalartensorflow::__anonfc08bad90111::ConverterTraits570   static const char* ConvertScalar(PyObject* v, tstring* out) {
571     if (PyBytes_Check(v)) {
572       out->assign(PyBytes_AS_STRING(v), PyBytes_GET_SIZE(v));
573       return nullptr;
574     }
575     if (PyUnicode_Check(v)) {
576 #if PY_MAJOR_VERSION >= 3
577       Py_ssize_t size;
578       const char* str = PyUnicode_AsUTF8AndSize(v, &size);
579       if (str == nullptr) return ErrorConvertingUnicodeString;
580       out->assign(str, size);
581       return nullptr;
582 #else
583       PyObject* py_str = PyUnicode_AsUTF8String(v);
584       if (py_str == nullptr) return ErrorConvertingUnicodeString;
585       out->assign(PyBytes_AS_STRING(py_str), PyBytes_GET_SIZE(py_str));
586       Py_DECREF(py_str);
587       return nullptr;
588 #endif
589     }
590     return ErrorMixedTypes;
591   }
592 };
593 
594 typedef Converter<tstring> StringConverter;
595 
596 // Converts Python object `c` that should hold a Python string into a
597 // C++ string in *out.  Returns nullptr on success, or a message on error.
598 // Defined below, but forward declared here for use in PyRepr.
PyRepr(PyObject * obj)599 tstring PyRepr(PyObject* obj) {
600   if (obj == nullptr) {
601     return "<null>";
602   }
603   Safe_PyObjectPtr repr_obj = make_safe(PyObject_Repr(obj));
604   if (repr_obj) {
605     tstring repr_str;
606     if (ConverterTraits<tstring>::ConvertScalar(repr_obj.get(), &repr_str) ==
607         nullptr) {
608       return repr_str;
609     }
610   }
611   return "<error computing repr()>";
612 }
613 
IsPyDimension(PyObject * obj)614 bool IsPyDimension(PyObject* obj) {
615   const char* tp_name = obj->ob_type->tp_name;
616   if (strcmp(tp_name, "Dimension") != 0) return false;
617   bool ret = str_util::EndsWith(
618       PyRepr(PyType(obj)),
619       "tensorflow.python.framework.tensor_shape.Dimension'>");
620   return ret;
621 }
622 
623 // Complex support
624 
625 template <>
626 struct ConverterTraits<complex128> {
CreateScalartensorflow::__anonfc08bad90111::ConverterTraits627   static AbstractTensorInterface* CreateScalar(TFE_Context* ctx,
628                                                complex128 value) {
629     return tensorflow::unwrap(ctx)->CreateComplex128Scalar(value);
630   }
631 
CreateTensortensorflow::__anonfc08bad90111::ConverterTraits632   static AbstractTensorInterface* CreateTensor(
633       TFE_Context* ctx, absl::Span<const int64_t> dim_sizes) {
634     return tensorflow::unwrap(ctx)->CreateTensor(DT_COMPLEX128, dim_sizes);
635   }
636 
ConvertScalartensorflow::__anonfc08bad90111::ConverterTraits637   static const char* ConvertScalar(PyObject* v, complex128* out) {
638     if (PyComplex_Check(v)) {
639       *out = complex128(PyComplex_RealAsDouble(v), PyComplex_ImagAsDouble(v));
640       return nullptr;
641     } else if (PyIsInstance(v, &PyComplexFloatingArrType_Type)) {  // NumPy
642       auto as_complex = PyComplex_AsCComplex(v);
643       *out = complex128(as_complex.real, as_complex.imag);
644       return nullptr;
645     }
646     double as_double;
647     auto error = ConvertOneFloat<double>(v, &as_double);
648     if (error != nullptr) return error;
649     *out = complex128(as_double, 0.0);
650     return nullptr;
651   }
652 };
653 
654 typedef Converter<complex128> Complex128Converter;
655 
656 // Bool support
657 
658 template <>
659 struct ConverterTraits<bool> {
CreateScalartensorflow::__anonfc08bad90111::ConverterTraits660   static AbstractTensorInterface* CreateScalar(TFE_Context* ctx, bool value) {
661     return tensorflow::unwrap(ctx)->CreateBoolScalar(value);
662   }
663 
CreateTensortensorflow::__anonfc08bad90111::ConverterTraits664   static AbstractTensorInterface* CreateTensor(
665       TFE_Context* ctx, absl::Span<const int64_t> dim_sizes) {
666     return tensorflow::unwrap(ctx)->CreateTensor(DT_BOOL, dim_sizes);
667   }
668 
ConvertScalartensorflow::__anonfc08bad90111::ConverterTraits669   static const char* ConvertScalar(PyObject* v, bool* out) {
670     if (v == Py_True) {
671       *out = true;
672     } else if (v == Py_False) {
673       *out = false;
674     } else if (PyIsInstance(v, &PyBoolArrType_Type)) {  // NumPy
675       *out = PyObject_IsTrue(v);
676     } else {
677       return ErrorMixedTypes;
678     }
679     return nullptr;
680   }
681 };
682 
683 typedef Converter<bool> BoolConverter;
684 
685 // Convert a Python numpy.ndarray object to a TFE_TensorHandle.
686 // The two may share underlying storage so changes to one may reflect in the
687 // other.
NumpyToTFE_TensorHandle(TFE_Context * ctx,PyObject * obj)688 TFE_TensorHandle* NumpyToTFE_TensorHandle(TFE_Context* ctx, PyObject* obj) {
689   Safe_TF_TensorPtr tf_tensor = make_safe(static_cast<TF_Tensor*>(nullptr));
690   Status status = tensorflow::NdarrayToTensor(ctx, obj, &tf_tensor);
691 
692   if (TF_PREDICT_FALSE(!status.ok())) {
693     PyErr_SetString(PyExc_ValueError,
694                     tensorflow::strings::StrCat(
695                         "Failed to convert a NumPy array to a Tensor (",
696                         status.error_message(), ").")
697                         .c_str());
698     return nullptr;
699   }
700 
701   return tensorflow::wrap(
702       tensorflow::unwrap(ctx)->CreateLocalHandle(tf_tensor->tensor));
703 }
704 
705 }  // namespace
706 
707 // TODO(b/147743551): This function handles enough conversions to justify
708 // promoting to something like PyObjectToTensorHandle.
709 // TODO(b/147828820): Handle Tensors properly.
PySeqToTFE_TensorHandle(TFE_Context * ctx,PyObject * obj,DataType dtype)710 TFE_TensorHandle* PySeqToTFE_TensorHandle(TFE_Context* ctx, PyObject* obj,
711                                           DataType dtype) {
712   // Shortcut: __array__ objects (such as Pandas data frames).
713   // These objects are efficiently handled by Numpy. We transform them into
714   // Numpy arrays and handle them in the Numpy case below. Note that Tensors
715   // implement the __array__ function, and will be handled in this shortcut.
716   Safe_PyObjectPtr array =
717       make_safe(PyArray_FromArrayAttr(obj, nullptr, nullptr));
718   if (array == nullptr) {
719     return nullptr;
720   }
721   if (array.get() == Py_NotImplemented) {
722     // The Py_NotImplemented returned from PyArray_FromArrayAttr is not
723     // Py_INCREF'ed, so we don't want the Safe_PyObjectPtr to Py_DECREF it.
724     array.release();
725   } else {
726     // PyArray_FromArrayAttr ensures that `array` is a PyArrayObject, so all
727     // we have to do is replace `obj` with it and continue.
728     obj = array.get();
729   }
730 
731   // Shortcut: Numpy arrays.
732   if (PyArray_Check(obj)) {
733     int desired_np_dtype = -1;
734     if (dtype != tensorflow::DT_INVALID) {
735       if (!tensorflow::TF_DataType_to_PyArray_TYPE(
736                static_cast<TF_DataType>(dtype), &desired_np_dtype)
737                .ok()) {
738         PyErr_SetString(
739             PyExc_TypeError,
740             tensorflow::strings::StrCat("Invalid dtype argument value ", dtype)
741                 .c_str());
742         return nullptr;
743       }
744     }
745 
746     PyArrayObject* array = reinterpret_cast<PyArrayObject*>(obj);
747     int array_dtype = PyArray_TYPE(array);
748 
749     Safe_PyObjectPtr safe_value(nullptr);
750     // Use Numpy to convert between types if needed.
751     if ((desired_np_dtype >= 0 && desired_np_dtype != array_dtype) ||
752         !PyArray_ISCARRAY(array)) {
753       int new_dtype = desired_np_dtype >= 0 ? desired_np_dtype : array_dtype;
754       safe_value = tensorflow::make_safe(
755           PyArray_FromAny(obj, PyArray_DescrFromType(new_dtype), 0, 0,
756                           NPY_ARRAY_CARRAY_RO | NPY_ARRAY_FORCECAST, nullptr));
757       if (PyErr_Occurred()) return nullptr;
758       if (safe_value == nullptr) {
759         PyErr_SetString(PyExc_ValueError, "Error while casting a numpy value");
760       }
761       obj = safe_value.get();
762     }
763     return NumpyToTFE_TensorHandle(ctx, obj);
764   }
765 
766   ConverterState state;
767   Status status = InferShapeAndType(obj, &state);
768   if (!status.ok()) {
769     PyErr_SetString(PyExc_ValueError, status.error_message().c_str());
770     return nullptr;
771   }
772   DataType requested_dtype = DT_INVALID;
773   if (dtype != DT_INVALID) {
774     requested_dtype = dtype;
775   }
776 
777   // NOTE(josh11b): If don't successfully convert to the requested type,
778   // we just try instead to create a tensor of the inferred type and
779   // let the caller convert it to the requested type using a cast
780   // operation.
781   const char* error = nullptr;
782   TFE_TensorHandle* handle = nullptr;
783   status = errors::Unimplemented("Missing Python -> Tensor conversion for ",
784                                  DataTypeString(state.inferred_dtype));
785   switch (requested_dtype) {
786     case DT_FLOAT:
787       status = FloatConverter::Convert(ctx, obj, &state, &handle, &error);
788       break;
789 
790     case DT_DOUBLE:
791       status = DoubleConverter::Convert(ctx, obj, &state, &handle, &error);
792       break;
793 
794     case DT_HALF:
795       status = NumpyHalfConverter::Convert(ctx, obj, &state, &handle, &error);
796       break;
797 
798     case DT_INT64:
799       status = Int64Converter::Convert(ctx, obj, &state, &handle, &error);
800       break;
801 
802     case DT_INT32:
803       status = Int32Converter::Convert(ctx, obj, &state, &handle, &error);
804       break;
805 
806     case DT_UINT64:
807       status = UInt64Converter::Convert(ctx, obj, &state, &handle, &error);
808       break;
809 
810     case DT_COMPLEX128:
811       status = Complex128Converter::Convert(ctx, obj, &state, &handle, &error);
812       break;
813 
814     case DT_STRING:
815       status = StringConverter::Convert(ctx, obj, &state, &handle, &error);
816       break;
817 
818     case DT_BOOL:
819       status = BoolConverter::Convert(ctx, obj, &state, &handle, &error);
820       break;
821 
822     default:
823       break;
824   }
825   if (status.ok()) return handle;
826 
827   switch (state.inferred_dtype) {
828     case DT_FLOAT:
829       // TODO(josh11b): Handle mixed floats and complex numbers?
830       if (requested_dtype == DT_INVALID) {
831         // TensorFlow uses float32s to represent floating point numbers
832         // by default (for space and speed over using doubles).
833         status = FloatConverter::Convert(ctx, obj, &state, &handle, &error);
834       } else {
835         // We are going to do a cast to the user's requested dtype
836         // after this.  We use doubles for this intermediate result so
837         // we don't lose precision that might be representable in the
838         // final type.
839         status = DoubleConverter::Convert(ctx, obj, &state, &handle, &error);
840       }
841       break;
842 
843     case DT_DOUBLE:
844       status = DoubleConverter::Convert(ctx, obj, &state, &handle, &error);
845       break;
846 
847     case DT_HALF:
848       status = NumpyHalfConverter::Convert(ctx, obj, &state, &handle, &error);
849       break;
850 
851     case DT_INT64:
852       if (requested_dtype == DT_INVALID) {
853         status = Int32Converter::Convert(ctx, obj, &state, &handle, &error);
854         if (error == ErrorFoundInt64) {
855           status = Int64Converter::Convert(ctx, obj, &state, &handle, &error);
856         }
857         if (error == ErrorFoundFloat) {
858           status = FloatConverter::Convert(ctx, obj, &state, &handle, &error);
859         }
860         // TODO(josh11b): May also want to fall back to using doubles if
861         // error == ErrorOutOfRange?
862       } else {
863         status = Int64Converter::Convert(ctx, obj, &state, &handle, &error);
864         if (error == ErrorFoundFloat) {
865           status = DoubleConverter::Convert(ctx, obj, &state, &handle, &error);
866         }
867       }
868       break;
869 
870     case DT_STRING:
871       status = StringConverter::Convert(ctx, obj, &state, &handle, &error);
872       break;
873 
874     case DT_COMPLEX128:
875       status = Complex128Converter::Convert(ctx, obj, &state, &handle, &error);
876       break;
877 
878     case DT_BOOL:
879       status = BoolConverter::Convert(ctx, obj, &state, &handle, &error);
880       break;
881 
882     case DT_INVALID:  // Only occurs for empty tensors.
883     {
884       AbstractTensorInterface* t = tensorflow::unwrap(ctx)->CreateTensor(
885           requested_dtype == DT_INVALID ? DT_FLOAT : requested_dtype,
886           state.inferred_shape);
887       auto* result =
888           tensorflow::wrap(tensorflow::unwrap(ctx)->CreateLocalHandle(t));
889       t->Release();
890       return result;
891     }
892 
893     default:
894       break;
895   }
896 
897   if (!status.ok()) {
898     PyErr_SetString(PyExc_ValueError, status.error_message().c_str());
899     return nullptr;
900   }
901 
902   return handle;
903 }
904 
905 }  // namespace tensorflow
906